Add mergeRequestsCsvExport GraphQL mutation

What does this MR do and why?

Adds an experimental GraphQL mutation, mergeRequestsCsvExport (milestone 19.6), that takes a project path and the same filter arguments as the project mergeRequests connection, then enqueues the existing CSV export. Nothing changes for users yet.

This is the backend for fixing the merge request list "Export as CSV" ignoring filters. The list writes filters like merge_user and target_branches[] to the URL, but the REST export only accepts the finder names (merge_user_username, target_branch), so those filters are dropped. !261946 (merged) switches the export modal to this mutation behind a feature flag, and !261956 removes the REST export after the rollout.

Merge request stack

These merge requests fix #583992 together. Merge them in this order; same-numbered MRs are independent.

Order MR What Depends on
1 !262241 (merged) Extract the merge request filter arguments into a shared concern (no schema change) None
1 !262242 (merged) Accept the subscribed filter as a string in IssuableFinder None
2 !261924 (merged) (this MR) Add the mergeRequestsCsvExport GraphQL mutation !262241 (merged), !262242 (merged)
3 !261946 (merged) Switch the MR list export to the mutation behind merge_requests_csv_export_graphql !261924 (merged)
4 !261956 Remove the REST export and the flag after the rollout in #633656 (not before 2026-11-16) !261946 (merged)
Implementation details
  • Stacked on !262241 (merged), which extracts MergeRequests::SharedFilterArguments. This mutation includes that concern, so it accepts the list's filters and translates them to finder params the same way.
  • Depends on !262242 (merged) (IssuableFinder#by_subscribed accepting the string form job arguments arrive in), so the subscribed filter survives the worker.
  • Follows the workItemsCsvExport pattern and enqueues IssuableExportCsvWorker.
  • The project is exposed as resource_parent, so SearchArguments sets attempt_project_search_optimizations like the list resolver does. Without it, search terms under 3 characters were dropped or matched exactly, and the export returned different MRs from the list.
  • Authorization: authorize :read_merge_request and authorize_granular_token permissions: :read_merge_request, boundary_type: :project. This matches what the REST export requires today. Anonymous users can't run any mutation (GlobalPolicy prevents execute_graphql_mutation), and the spec covers that.
  • Schema: additions only.

Verification:

  • spec/requests/api/graphql/mutations/merge_requests/csv/export_spec.rb: filter mapping to finder names, closedAfter validation, search parity with the list for UI, fix ab and Refactor (the first two fail without the resource_parent change), non-member and anonymous access errors, and the granular token shared example.
  • ee/spec/requests/api/graphql/mutations/merge_requests/csv/export_spec.rb: approver.
  • Customer workaround (posted on the issue): add merge_user_username and target_branch params to the URL before exporting.

References

Screenshots or screen recordings

Not applicable: backend only.

How to set up and validate locally

  1. Run the request specs:
    bundle exec rspec spec/requests/api/graphql/mutations/merge_requests/csv/export_spec.rb
    bundle exec rspec ee/spec/requests/api/graphql/mutations/merge_requests/csv/export_spec.rb
  2. Or run mutation { mergeRequestsCsvExport(input: { projectPath: "group/project", targetBranches: ["main"] }) { message errors } } in GraphiQL and confirm message is returned with no 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.

🤖 Generated with Claude Code

Edited by Kai Armstrong

Merge request reports

Loading
Loading