Support more edge-cases in inheritance enablement
What does this MR do and why?
This MR builds on !256094 (merged) to add some edge-case support for group inheritance enablement within ItemConsumers::CreateService
- Prevent a project from enabling an item that it is inheriting
- Replace a direct group enablement with an inherited one, reusing the service account, and removing all of the direct project enablements.
- Prevent foundational flows from being group inheritance enabled (this is handled separately in https://gitlab.com/gitlab-org/gitlab/-/work_items/627525)
Note that for 2, we don't clean up the project memberships in the way that ItemConsumer::DestroyService does because we want to keep the project memberships in tact. See !256297 (comment 3855788200).
References
Group inheritance (BE): Services and mutations (#627517 - closed)
How to set up and validate locally
In rails console enable the feature flag:
Feature.enable(:ai_catalog_group_inherited_consumers)Create a flow in the project (Automate > Flows > New flow), ensure you set its visibility to public. Don't enable it at project-level though.
Find the item's Global ID as you'll use it in the mutations:
item = Ai::Catalog::Item.last
item.to_global_id.to_s # => "gid://gitlab/Ai::Catalog::Item/<id>"The frontend doesn't create group enablements yet, so use GraphQL (/-/graphql-explorer) for the group-level scenarios.
Create an inheritance enablement. (Note triggerTypes are available only for custom flows, and external agents, omit if enabling a custom agent).
mutation {
aiCatalogItemConsumerCreate(input: {
itemId: "gid://gitlab/Ai::Catalog::Item/<item-id>",
target: { groupId: "gid://gitlab/Group/<top-level-group-id>" },
consumerKind: INHERITANCE,
triggerTypes: ["mention"]
}) {
errors
itemConsumer { id consumerKind group { name } serviceAccount { username } }
}
}Expect no errors.
Verify the group-scoped item consumer was created successfully in the rails console:
Ai::Catalog::ItemConsumer.inheritance.lastProject can't enable an item it inherits
With the inheritance record above, attempt to enable the same item for a project in the group.
mutation {
aiCatalogItemConsumerCreate(input: {
itemId: "gid://gitlab/Ai::Catalog::Item/<item-id>",
target: { projectId: "gid://gitlab/Project/<descendant-project-id>" },
triggerTypes: ["mention"]
}) {
errors
itemConsumer { id consumerKind group { name } serviceAccount { username } }
}
}You should get an error response similar to below:
{
"data": {
"aiCatalogItemConsumerCreate": {
"errors": [
"This agent or flow is already inherited from the group and can't be enabled separately"
],
"itemConsumer": null
}
},
"correlationId": "01M36123HH84KF7X5RWVQ6VY9G"
}Replacing a direct group enablement with an inheritance enablement
For this test, you should work with a custom flow, so we can test service account states.
Create a new custom flow. Ensure it is public. Have the project enable it (can be done in the UI).
This auto-creates a direct group consumer record (for the top-level group) plus the project consumer record.
We can check the state in a rails console:
old_direct_group_consumer = Ai::Catalog::ItemConsumer.direct.where.not(group_id: nil).last
item_id = old_direct_group_consumer.ai_catalog_item_id
service_account_id = old_direct_group_consumer.active_service_account.id
Ai::Catalog::ItemConsumer.for_item(item_id) # Should see two direct consumer records, one for the top-level group and one for the projectNow run the GraphQL mutation to enable the item through group inheritance (replace <item-id> with the new item ID):
mutation {
aiCatalogItemConsumerCreate(input: {
itemId: "gid://gitlab/Ai::Catalog::Item/<item-id>",
target: { groupId: "gid://gitlab/Group/<top-level-group-id>" },
consumerKind: INHERITANCE,
triggerTypes: ["mention"]
}) {
errors
itemConsumer { id consumerKind group { name } serviceAccount { username } }
}
}You should get a success response.
Verify the replace has happened:
Ai::Catalog::ItemConsumer.for_item(item_id) # Should only list the single group inheritance record
Ai::Catalog::ItemConsumer.inheritance.last.service_account_id == service_account_id # => true (the service account is reused)Now disable the flag:
Feature.disable(:ai_catalog_group_inherited_consumers)Attempting to enable should have the mutation respond with something like:
{
"data": {
"aiCatalogItemConsumerCreate": {
"errors": [
"Turning on agents and flows for a group and all its subgroups is not available"
],
"itemConsumer": null
}
},
"correlationId": "01M360TME65CTPFJFEH0K3RRJZ"
}Test that normal AI catalog enablement features work as normal:
- Enabling custom items at project-level in the UI
- Enabling foundational flows
- Enabling foundational agents
- Enabling foundational external agents at project-level in the UI
Database SQL performance of the destroy
See !256297 (comment 3889112789).
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #627517 (closed)