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 } }