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 operator is now an enum, so IN instead of "in" (same for match).
  • 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 pipelineHooks instead of pipeline_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

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::FlowTrigger persists the expected filter hash:

Ai::FlowTrigger.last.filter
  • Confirm passing both triggerFilter and triggerConditions returns 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 }
  }
}
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 }
  }
}
Edited by Keeyan Nejad

Merge request reports

Loading
Loading