Fix scoped label wildcards not persisting in saved views
What does this MR do and why?
Saved Views silently drop scoped label wildcard filters (e.g. label = workflow::*). Saving a view with this filter succeeds with no error, but the filter is gone on reload.
WorkItems::SavedViews::FilterNormalizerService only recognized the literal strings Any/None as label wildcards. A scoped wildcard like workflow::* fell through to find_label_ids, which does an exact-title lookup via LabelsFinder — workflow::* never matches a real label title, so it resolved to label_ids: [] and was saved as an empty filter with no warning.
The scoped-wildcard resolution needed here already exists and is used correctly by the live work items list filter (EE::Issuables::LabelFilter#find_label_ids_uncached, LIKE-based). Saved Views persistence just never reused it.
How this MR fixes it
Extends the EE override of label_wildcard? (ee/app/services/ee/work_items/saved_views/filter_normalizer_service.rb) to also recognize key::* patterns when scoped_labels is licensed. This routes the wildcard through the existing label_wildcard_id pass-through mechanism already used for Any/None — the pattern is stored as a literal string rather than resolved to IDs at save time, and gets correctly re-expanded via the existing LIKE-based logic when the saved view is actually loaded and queried. No changes needed on the read/sanitize side — that's already an unconditional pass-through for label_wildcard_id → label_name.
Screenshots (Before vs After)
How to set up and validate locally
Requires a license with the scoped_labels feature (Premium/Ultimate).
- In a group with a licensed
scoped_labelsfeature, create a couple of scoped labels, e.g.workflow::in dev,workflow::in review. - Navigate to the group or project's Work Items list.
- Apply a scoped label wildcard filter:
label = workflow::*. - Save the view.
- Refresh the page or open the view in a new tab.
- Before this fix: the label filter is silently dropped on reload.
- After this fix: the saved view retains
workflow::*and applies it on load, matching all labels under that scope.
Also verifiable directly against the service:
result = WorkItems::SavedViews::FilterNormalizerService.new(
filter_data: { label_name: ['workflow::*'] },
container: project, # or group, with scoped_labels licensed
current_user: user
).execute
result.payload[:label_wildcard_id] # => "workflow::*" (previously label_ids: [])- Confirm the added specs pass:
bundle exec rspec ee/spec/services/ee/work_items/saved_views/filter_normalizer_service_spec.rb spec/services/work_items/saved_views/filter_sanitizer_service_spec.rb

