Add work_item_type_names filter to the Work Items REST API

Summary

Add a work_item_type_names filter to the Work Items REST API list endpoint, so clients can filter by work item type NAME (including custom types) rather than only by integer type id.

This is a sibling to #605888 (closed) (formalizing work_item_type_ids). That one gives clients an id-based filter; this one adds the convenient name-based filter on top.

Why this is useful

Today the only way to filter by a specific (custom) work item type is work_item_type_ids, which takes integer ids. That forces every client to first resolve a human-readable type name (e.g. "Activated by default") to its numeric id before it can filter, which means an extra lookup round-trip and client-side plumbing.

Type names are what humans and automation configs actually speak. A concrete driver: the gitlab-triage gem is adding a work_items policy type on this REST API (epic gitlab-org#22402). Its triage rules are written in YAML by humans, e.g. filter for work items of type "Bug". Without a name filter, the gem has to build and maintain a name-to-id resolver and do a preliminary lookup on every run. A name filter removes that entirely, and the same convenience applies to any external integration or script.

Names are safe to filter on here: work item types are managed at the root namespace level (organization / top-level group), so type names cannot collide within a namespace tree. There is no ambiguity to resolve.

Scope

  • REST API only for now. GraphQL can wire this up later cheaply (see below); it is intentionally out of scope for this issue.
  • Additive. Does not change or replace work_item_type_ids (which the frontend depends on and #605888 (closed) is formalizing).

Background: how type filtering works today (important, read before implementing)

  • work_item_type_ids is a pure passthrough: the finder does where(work_item_type_id: ids). Nothing resolves specific type identity. GraphQL even pre-resolves its GID input to integers before the finder (prepare: in shared_filter_arguments.rb).
  • The base-type types/issue_types filter resolves names, but only for the six system base types, not specific/custom types.
  • So resolving a specific type NAME to its id does not exist anywhere yet. This issue adds it, in the shared finder, so all current and future consumers can reuse it.

Implementation checklist

1. Resolution in the shared finder

  • In app/finders/issues/issue_types_filter.rb, add a by_work_item_type_names filter method and call it from #filter (the method that currently chains by_work_item_type_ids then by_issue_types). Adding the method without adding it to the filter chain is a no-op, so wire it in.
  • Resolve names to ids using the type Provider, scoped to the finder's parent: WorkItems::TypesFramework::Provider.new(parent). parent is available on the filter via Issuables::BaseFilter (it is params.parent, i.e. project || group). The Provider walks to the root ancestor, so a Project or Group parent both resolve custom types correctly.
  • Resolve all requested names in a single pass (not one find per name). Provider#find_by_name exists but is exact-match and O(n) per call; prefer resolving against the Provider's full type list once, matching case-insensitively (the intended vocabulary is case-insensitive, e.g. "bug" matches "Bug"). Map matched names to their ids, then feed the existing with_work_item_type_ids scope (app/models/issue.rb).
  • Unknown name behavior: an unknown name matches nothing (consistent with how an unknown id behaves today). Do NOT raise. If every requested name is unknown, the result set is empty.
  • Negated path: add by_negated_work_item_type_names in app/finders/issues_finder.rb alongside the existing by_negated_work_item_type_ids (around lines 164, 255-260), resolving names the same way and feeding without_work_item_type_ids.

2. REST exposure (the only API surface in this issue)

  • Declare work_item_type_names, type: Array[String], in lib/api/helpers/work_items/list_params.rb in the top-level filter block AND the not block (mirror where label_name / the base-type types are declared).
  • Confirm the param reaches the finder. API::Helpers::WorkItemsFilterParams#transform builds finder params from the raw params hash and carries resource_parent, so a declared work_item_type_names key flows through. Verify the finder reads params[:work_item_type_names] (and not_params[:work_item_type_names] for negation) - add the reader if needed. Do not assume it auto-wires; trace it end to end.
  • Combining work_item_type_names with work_item_type_ids or base-type types is an AND. This is acceptable; no mutual-exclusivity needed. (Contrast: types + work_item_type_ids is a known silent-AND footgun tracked in #605888 (closed), but that is out of scope here.)

3. Explicitly OUT of scope (follow-up)

  • The GraphQL argument :work_item_type_names in app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb. The resolution branch will already exist in the finder, so exposing it on GraphQL later is roughly an argument declaration plus a spec. Do not add it in this issue.
  • No changes to work_item_type_ids (frontend depends on it; formalization is #605888 (closed)).

Acceptance criteria

  • GET /projects/:id/-/work_items?work_item_type_names[]=Bug returns only work items of type "Bug", including when "Bug" is a namespace-defined custom type.
  • Case-insensitive: work_item_type_names[]=bug matches type "Bug".
  • Multiple names OR together: work_item_type_names[]=Bug&work_item_type_names[]=Incident returns both.
  • not[work_item_type_names][]=Bug excludes work items of type "Bug".
  • An unknown type name matches nothing (no error); all-unknown yields an empty result set.
  • Works on both project and group list endpoints.

Testing

  • Finder specs (ee/spec/finders/issues_finder_spec.rb, since custom types are EE): cover work_item_type_names and the negated variant, using a custom type created with the :work_item_custom_type factory (create(:work_item_custom_type, namespace: root_namespace)), asserting a custom-type name resolves and filters correctly. Test through the finder's public interface, not private methods.
  • REST request specs: add a work_item_type_names context to the shared example spec/support/shared_examples/api/work_items_filter_shared_examples.rb (where the types context lives, around lines 58-65). It is already exercised by both the project and group list specs in spec/requests/api/work_items_spec.rb (via it_behaves_like 'work item listing filters'), so covering it there gives both surfaces for free. Include the not block and a custom type.

Size

Small. Roughly 40-70 lines of production code (finder branches plus REST param declarations) and 60-80 lines of tests. No database migration, no model change. Add a changelog trailer.

Edited by 🤖 GitLab Bot 🤖