Add triggerConditions to AI catalog consumer mutations
What does this MR do and why?
The triggerFilter argument on aiCatalogItemConsumerCreate and aiCatalogItemConsumerBulkCreate currently uses the JSON type, so clients have no way to introspect the schema of a valid filter.
This MR adds a new, fully-typed triggerConditions argument (input objects mirroring the filter.json schema) alongside the existing triggerFilter, which is now deprecated.
The typed input keeps the same top-level shape as the old JSON, with one field per known event type. The resolver converts the input objects back into the existing hash shape, so the model's JSON-schema validation remains the source of truth.
There are a few changes:
- The
operatoris now an enum, soINinstead of"in"(same formatch). - Nested rules required a
"type": "group"key/value. Now this can be omitted, since the BE works it out. The FE didn't support nested rules though, so this shouldn't cause any problems. - Event keys are camel cased. So
pipelineHooksinstead ofpipeline_hooks
Alternate proposal with a schema difference
My original implementation, resulted in a slight change to the schema (shown below). The difference being that the actual rules is wrapped in a new "rule" key. The same goes for nested rules in a "group" key.
The reason for this is that the GraphQL resolver can't dynamically resolve the type, when it could be either a rule, or a group nesting. This format is easier for GraphQL to deal with. The data stored in the end is the same.
That solution would have been a bit less ambiguous for the consumer, and had better type checking. But it would result in a change in the schema. Instead I use a special type that resolves whether the user passed a rule, or rule group, at runtime. This keeps the schema identical, but makes it a bit harder to reason with. I think in the interest of not breaking the existing schema, it's worth the tradeoff.
What this MR ships
"rules": [
{ "field": "object_attributes.status", "operator": "IN", "value": ["success"] }
]Change in the rejected alternate proposal
"rules": [
{ "rule": { "field": "object_attributes.status", "operator": "IN", "value": ["success"] } }
]References
- Work item: #602625 (closed)
How to set up and validate locally
-
Run the mutation with the new filter:
mutation { aiCatalogItemConsumerCreate(input: { itemId: "gid://gitlab/Ai::Catalog::Item/<ITEM_ID>", target: { projectId: "gid://gitlab/Project/<PROJECT_ID>" }, triggerTypes: ["pipeline_hooks"], triggerConditions: { pipelineHooks: { match: ALL, rules: [ { field: "object_attributes.status", operator: IN, value: ["success", "failed"] } ] } } }) { errors } } -
Confirm the created
Ai::FlowTriggerpersists the expectedfilterhash:
Ai::FlowTrigger.last.filter- Confirm passing both
triggerFilterandtriggerConditionsreturns a mutually-exclusive error:
mutation {
aiCatalogItemConsumerCreate(input: {
target: { projectId: "gid://gitlab/Project/1" }
itemId: "gid://gitlab/Ai::Catalog::Item/1"
triggerTypes: ["pipeline_hooks"]
triggerFilter: {
pipeline_hooks: {
type: "group"
match: "all"
rules: [
{ field: "object_attributes.status", operator: "in", value: ["success", "failed"] }
]
}
}
triggerConditions: {
pipelineHooks: {
match: ALL
rules: [
{ field: "object_attributes.status", operator: IN, value: ["success", "failed"] }
]
}
}
}) {
errors
itemConsumer { id }
}
}-
Delete the consumer from http://gdk.test:8080/alligator/primary/-/automate/flows (so we can enable it again)
-
Create a consumer with nested rules:
mutation {
aiCatalogItemConsumerCreate(input: {
target: { projectId: "gid://gitlab/Project/<project>" }
itemId: "gid://gitlab/Ai::Catalog::Item/1"
triggerTypes: ["pipeline_hooks"]
triggerConditions: {
pipelineHooks: {
match: ALL
rules: [
{ field: "object_attributes.status", operator: IN, value: ["success", "failed"] }
{
match: ANY
rules: [
{ field: "object_attributes.ref", operator: EQ, value: "main" }
{
match: ALL
rules: [
{ field: "object_attributes.source", operator: EQ, value: "push" }
{ field: "project.path_with_namespace", operator: CONTAINS, value: "gitlab-org/" }
]
}
]
}
]
}
}
}) {
errors
itemConsumer { id }
}
}