Validate pipeline event flow trigger filter fields

What does this MR do and why?

Addresses #597413

Trigger filters for pipeline_hooks events that use unsupported fields or invalid values save cleanly but silently never match any event, so a typo in a filter means the flow never runs and the user gets no feedback.

This MR validates pipeline_hooks filter rules against an allowlist of fields and values, so an invalid filter now fails with a clear validation error at create and update time. It mirrors the approach taken for merge_request/work_item action values in !244173 (merged):

  • New ALLOWED_FILTER_FIELDS allowlist on Ai::FlowTrigger:
    • object_attributes.status: running, success, failed, canceled (matches the statuses offered in the UI)
    • object_attributes.source: any Enums::Ci::Pipeline.sources key
    • merge_request.title: any value
  • Unknown fields inside pipeline_hooks filters are rejected; values are checked per field.
  • The existing collect_action_values traversal is reimplemented on a shared collect_leaf_rules helper (behavior-preserving; the pre-existing specs from !244173 (merged) pass unchanged).
  • Errors surface through the existing ServiceResponse → GraphQL mutation errors path; no service, GraphQL, or frontend changes.
  • The filter format and supported pipeline event fields/values are documented on the Triggers page.

Behind the new feature flag validate_flow_trigger_filter_fields (gitlab_com_derisk, default off, project actor) so that updates to pre-existing records with now-invalid filters are not broken before a staged rollout.

Other event types are unaffected: event types without an entry in ALLOWED_FILTER_FIELDS are skipped entirely, and the merge_request/work_item action validation stays independently gated behind validate_flow_trigger_filter_actions.

How to set up and validate locally

  1. Enable the feature flag in the rails console:

    project = Project.find_by_full_path('<project full path>')
    Feature.enable(:validate_flow_trigger_filter_fields, project)
  2. Build an invalid trigger in the console:

    trigger = project.ai_flow_triggers.new(
      description: 'test',
      config_path: '.gitlab/duo/flows/test.yaml',
      user: User.find_by(username: '<service account username>'),
      event_types: [Ai::FlowTrigger::EVENT_TYPES[:pipeline_hooks]],
      filter: { 'pipeline_hooks' => { 'rules' => [
        { 'field' => 'object_attributes.status', 'operator' => 'eq', 'value' => 'passed' }] } }
    )
    
    trigger.valid? # => false
    trigger.errors.full_messages
    # => ["Filter contains invalid object_attributes.status values for pipeline_hooks: passed.
    #      Allowed values are: running, success, failed, canceled"]
    
    trigger.filter = { 'pipeline_hooks' => { 'rules' => [
      { 'field' => 'object_attributes.ref', 'operator' => 'eq', 'value' => 'main' }] } }
    trigger.valid? # => false
    trigger.errors.full_messages
    # => ["Filter contains invalid fields for pipeline_hooks: object_attributes.ref.
    #      Allowed fields are: object_attributes.status, object_attributes.source, merge_request.title"]
    
    Feature.disable(:validate_flow_trigger_filter_fields, project)
    trigger.valid? # => true (validation skipped when the flag is off)
  3. Or via GraphQL: send aiFlowTriggerCreate/aiFlowTriggerUpdate with a filter like the above and confirm the message is returned in the mutation errors array. Valid filters (for example {"pipeline_hooks": {"rules": [{"field": "object_attributes.status", "operator": "in", "value": ["failed", "canceled"]}]}}) save without errors.

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.

References

Edited by Tiger Watson

Merge request reports

Loading
Loading