diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 5577fa57ae3c2b211f27bc0074522c1846904b2b..19ad93d6f53f7794712bd19513512bf3e50ae5ed 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -23766,6 +23766,23 @@ An AI catalog agent. #### Fields with arguments +##### `AiCatalogAgent.configurationForProject` + +{{< details >}} +**Introduced** in GitLab 18.6. +**Status**: Experiment. +{{< /details >}} + +Item configuration for the given project. This field can only be resolved at most 20 times in any single request. + +Returns [`AiCatalogItemConsumer`](#aicatalogitemconsumer). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `projectId` | [`ProjectID!`](#projectid) | Global ID of the project to return the item configuration of. | + ##### `AiCatalogAgent.latestVersion` Latest version of the item. @@ -23831,6 +23848,23 @@ An AI catalog flow. #### Fields with arguments +##### `AiCatalogFlow.configurationForProject` + +{{< details >}} +**Introduced** in GitLab 18.6. +**Status**: Experiment. +{{< /details >}} + +Item configuration for the given project. This field can only be resolved at most 20 times in any single request. + +Returns [`AiCatalogItemConsumer`](#aicatalogitemconsumer). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `projectId` | [`ProjectID!`](#projectid) | Global ID of the project to return the item configuration of. | + ##### `AiCatalogFlow.latestVersion` Latest version of the item. @@ -23877,6 +23911,7 @@ An AI catalog item configuration. | Name | Type | Description | | ---- | ---- | ----------- | +| `enabled` | [`Boolean`](#boolean) | Indicates if the configuration item is enabled. | | `group` | [`Group`](#group) | Group in which the catalog item is configured. | | `id` | [`ID!`](#id) | ID of the configuration item. | | `item` | [`AiCatalogItem`](#aicatalogitem) | Configuration catalog item. | @@ -23916,6 +23951,23 @@ An AI catalog third party flow. #### Fields with arguments +##### `AiCatalogThirdPartyFlow.configurationForProject` + +{{< details >}} +**Introduced** in GitLab 18.6. +**Status**: Experiment. +{{< /details >}} + +Item configuration for the given project. This field can only be resolved at most 20 times in any single request. + +Returns [`AiCatalogItemConsumer`](#aicatalogitemconsumer). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `projectId` | [`ProjectID!`](#projectid) | Global ID of the project to return the item configuration of. | + ##### `AiCatalogThirdPartyFlow.latestVersion` Latest version of the item. @@ -53738,6 +53790,23 @@ Implementations: ##### Fields with arguments +###### `AiCatalogItem.configurationForProject` + +{{< details >}} +**Introduced** in GitLab 18.6. +**Status**: Experiment. +{{< /details >}} + +Item configuration for the given project. This field can only be resolved at most 20 times in any single request. + +Returns [`AiCatalogItemConsumer`](#aicatalogitemconsumer). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `projectId` | [`ProjectID!`](#projectid) | Global ID of the project to return the item configuration of. | + ###### `AiCatalogItem.latestVersion` Latest version of the item. diff --git a/ee/app/graphql/types/ai/catalog/item_consumer_type.rb b/ee/app/graphql/types/ai/catalog/item_consumer_type.rb index ced05aca9d089fae940ce8b5101ea7f7c1c28476..021be6e199105974442948eb0e267d921be5a0e6 100644 --- a/ee/app/graphql/types/ai/catalog/item_consumer_type.rb +++ b/ee/app/graphql/types/ai/catalog/item_consumer_type.rb @@ -10,6 +10,9 @@ class ItemConsumerType < ::Types::BaseObject connection_type_class ::Types::CountableConnectionType + field :enabled, GraphQL::Types::Boolean, + null: true, + description: 'Indicates if the configuration item is enabled.' field :group, ::Types::GroupType, null: true, description: 'Group in which the catalog item is configured.' diff --git a/ee/app/graphql/types/ai/catalog/item_interface.rb b/ee/app/graphql/types/ai/catalog/item_interface.rb index 2906756f877d1781b87b8303cf769ca26a8cb62d..5e7dda33e093fe33d8b6058b7443efd056c64f71 100644 --- a/ee/app/graphql/types/ai/catalog/item_interface.rb +++ b/ee/app/graphql/types/ai/catalog/item_interface.rb @@ -42,6 +42,16 @@ module ItemInterface argument :released, ::GraphQL::Types::Boolean, required: false, description: 'Return the latest released version.' end + field :configuration_for_project, ::Types::Ai::Catalog::ItemConsumerType, + null: true, + experiment: { milestone: '18.6' }, + description: 'Item configuration for the given project. ' \ + 'This field can only be resolved at most 20 times in any single request.' do + argument :project_id, ::Types::GlobalIDType[::Project], required: true, + description: 'Global ID of the project to return the item configuration of.' + + extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 20) + end orphan_types ::Types::Ai::Catalog::AgentType orphan_types ::Types::Ai::Catalog::FlowType @@ -63,6 +73,10 @@ def latest_version(released: nil) end end + def configuration_for_project(project_id:) + object.consumers.for_projects(project_id.model_id).first + end + def self.resolve_type(item, _context) RESOLVE_TYPES[item.item_type.to_sym] or raise "Unknown catalog item type: #{item.item_type}" # rubocop:disable Style/AndOr -- Syntax error when || is used end diff --git a/ee/app/models/ai/catalog/item_consumer.rb b/ee/app/models/ai/catalog/item_consumer.rb index b1721134d73dacb6ec435a7a6a68a21462af8dc7..798b83f787aafb4634739494d44c3f427cf5a52e 100644 --- a/ee/app/models/ai/catalog/item_consumer.rb +++ b/ee/app/models/ai/catalog/item_consumer.rb @@ -28,6 +28,7 @@ class ItemConsumer < ApplicationRecord belongs_to :project scope :not_for_projects, ->(project) { where.not(project: project) } + scope :for_projects, ->(project) { where(project: project) } scope :for_item, ->(item_id) { where(ai_catalog_item_id: item_id) } scope :with_item_type, ->(item_type) { joins(:item).where(item: { item_type: item_type }) } diff --git a/ee/spec/graphql/types/ai/catalog/item_consumer_type_spec.rb b/ee/spec/graphql/types/ai/catalog/item_consumer_type_spec.rb index 5fb7e87129b80b1b2cbd9d1b79b8fb5550de38be..d417f4f191826cd24e6561331daf8205c020aaa5 100644 --- a/ee/spec/graphql/types/ai/catalog/item_consumer_type_spec.rb +++ b/ee/spec/graphql/types/ai/catalog/item_consumer_type_spec.rb @@ -11,6 +11,7 @@ expected_fields = %w[ group id + enabled item organization pinned_version_prefix diff --git a/ee/spec/graphql/types/ai/catalog/item_interface_spec.rb b/ee/spec/graphql/types/ai/catalog/item_interface_spec.rb index 1cb8e2d61c7ea11b5bf0899b97949d35eb9da0c0..977c7a011f9ff7be4fc1decb6bda035453259ac0 100644 --- a/ee/spec/graphql/types/ai/catalog/item_interface_spec.rb +++ b/ee/spec/graphql/types/ai/catalog/item_interface_spec.rb @@ -10,6 +10,7 @@ it 'has the expected fields' do expected_fields = %w[ created_at + configuration_for_project description id item_type diff --git a/ee/spec/models/ai/catalog/item_consumer_spec.rb b/ee/spec/models/ai/catalog/item_consumer_spec.rb index b456d9763940e16d737ac61c5aeae38384183230..9555a5a9b94c799a0e49a00afe2fc9d931668092 100644 --- a/ee/spec/models/ai/catalog/item_consumer_spec.rb +++ b/ee/spec/models/ai/catalog/item_consumer_spec.rb @@ -172,6 +172,21 @@ end describe 'scopes' do + describe '.for_projects' do + it 'includes records that belong to the given projects' do + included_projects = create_list(:project, 2) + included_item_consumers = included_projects.map do |project| + create(:ai_catalog_item_consumer, project: project) + end + + create(:ai_catalog_item_consumer, project: create(:project)) + + expect(described_class.for_projects(included_projects)).to match_array( + included_item_consumers + ) + end + end + describe '.not_for_projects' do it 'excludes records that belong to the given projects' do excluded_projects = create_list(:project, 2) diff --git a/ee/spec/requests/api/graphql/ai/catalog/item_spec.rb b/ee/spec/requests/api/graphql/ai/catalog/item_spec.rb index c1454baf1a2a85e4d4ccbe19a2d646dfcddbd3c2..604a34da59a9c2691ae56ae0ed513a234f86fe56 100644 --- a/ee/spec/requests/api/graphql/ai/catalog/item_spec.rb +++ b/ee/spec/requests/api/graphql/ai/catalog/item_spec.rb @@ -6,7 +6,9 @@ include Ai::Catalog::TestHelpers include GraphqlHelpers - let_it_be(:project) { create(:project) } + let_it_be(:reporter_user) { create(:user) } + let_it_be(:developer_user) { create(:user) } + let_it_be(:project) { create(:project, reporters: reporter_user, developers: developer_user) } let_it_be_with_reload(:catalog_item) { create(:ai_catalog_item, project: project, public: true) } let(:latest_version) { catalog_item.latest_version } @@ -143,17 +145,13 @@ let_it_be(:catalog_item) { create(:ai_catalog_item, project: project) } context 'when developer' do - let(:current_user) do - create(:user).tap { |user| project.add_developer(user) } - end + let(:current_user) { developer_user } it_behaves_like 'a successful query' end context 'when reporter' do - let(:current_user) do - create(:user).tap { |user| project.add_reporter(user) } - end + let(:current_user) { reporter_user } it_behaves_like 'an unsuccessful query' end @@ -210,6 +208,42 @@ end end + describe 'configurationForProject field' do + let_it_be(:item_consumer) { create(:ai_catalog_item_consumer, item: catalog_item, project: project) } + + let(:data) { graphql_data_at(:ai_catalog_item, :configuration_for_project) } + + let(:query) do + <<~GRAPHQL + query { + aiCatalogItem(id: "#{params[:id]}") { + configurationForProject(projectId: "#{project.to_global_id}") { + id + enabled + } + } + } + GRAPHQL + end + + context 'when the user does not have permission' do + let(:current_user) { reporter_user } + + it_behaves_like 'an unsuccessful query' + end + + context 'when the user has permission' do + let(:current_user) { developer_user } + + it 'returns the item configuration' do + post_graphql(query, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(data).to match(a_graphql_entity_for(item_consumer, :enabled)) + end + end + end + context 'when item is a flow' do let_it_be(:flow) { create(:ai_catalog_flow, project: project, public: true) } let(:params) { { id: flow.to_global_id } }