Frontend dropdown for security policy eligible projects
Summary
Updates the policy scope "specific projects" dropdown to use the new securityPolicyEligibleProjects resolver, replacing the multi-query approach that only showed descendant projects.
Part 2 of 2 for #593858
Stacking note: This MR contains all changes (backend + frontend) so it can be tested end-to-end independently. After !231293 (merged) is merged and this branch is rebased, only frontend changes will remain.
Changes
- New GraphQL query:
get_spp_eligible_projects.query.graphql— single query usingsecurityPolicyEligibleProjectswith cursor-based pagination - Refactored dropdown:
LinkedGroupsProjectsDropdown— replaces multi-query approach (fetch linked groups → fetch projects per group) with a single query. Removes client-side aggregation/deduplication. Uses proper cursor-based pagination instead of hardcodedfirst: 20 - Updated selector:
ScopeProjectSelector— passes SPPfullPathto the refactored dropdown - Removed:
get_spp_linked_groups_projects.query.graphql(dead code after refactor) - Tests: Frontend specs for dropdown and selector
Demo
| Description | UI |
|---|---|
| Before | |
| After |
What this fixes
| Before | After |
|---|---|
| Only descendant projects visible | All projects under root ancestors of linked groups |
Hardcoded first: 20 limit |
Proper cursor-based pagination |
| N+1-like client round-trips (1 query per linked group) | Single server-side query |
| Client-side deduplication needed | Server handles deduplication |
MR Dependencies
MR1 (Backend) !231293 ──sequential──▶ This MR (Frontend)How to manually test
This walks through the exact hierarchy from the issue's root-cause example so you can directly observe the projects that were hidden by the bug.
1. Set up the namespace hierarchy
In your GDK, create this structure:
topgroup/
├── subgroup-a/ ← we will link the SPP to this subgroup
│ ├── project-1 (project inside the linked subgroup)
│ └── child-group/
│ └── project-2 (descendant of the linked subgroup)
├── subgroup-b/ ← sibling subgroup, NOT linked to the SPP
│ └── project-3 (sibling-of-linked-group project)
├── project-4 (parent-level project, directly under topgroup)
└── security-policies-project ← will be assigned as the SPPStep-by-step:
- Create top group:
topgroup - Create subgroups:
topgroup/subgroup-aandtopgroup/subgroup-b - Create nested subgroup:
topgroup/subgroup-a/child-group - Create the projects above. The exact contents don't matter — only the paths.
2. Link the SPP to subgroup-a only
- Navigate to
topgroup/subgroup-a→ Secure → Policies. - Click Edit policy project and link
topgroup/security-policies-project.
Only
subgroup-ais linked. Neithertopgroupnorsubgroup-bhas an SPP assigned. This is the configuration that exposes the bug.
3. Open the policy editor
- On
topgroup/subgroup-a→ Secure → Policies, click New policy. - Pick any policy type (Scan execution / Merge request approval / Pipeline execution / Vulnerability management — they all share the dropdown).
- Fill in required fields (name, rule, action) — content doesn't matter.
- Scroll to Policy scope → switch to specific projects (or "all projects in this group" with the exception type set to "except specific projects").
- Open the projects dropdown.
4. Compare what you see in the dropdown
| Project | Before (bug) | After (this MR) |
|---|---|---|
project-1 (inside linked subgroup-a) |
||
project-2 (in child-group, descendant of linked subgroup) |
||
project-3 (in sibling subgroup-b, not linked to SPP) |
||
project-4 (parent level, directly under topgroup) |
||
security-policies-project (the SPP itself) |
Additional improvements you should observe in the dropdown:
- Pagination: if more than 20 eligible projects exist, scrolling loads more pages. (Before: hard-capped at 20 regardless of how many were eligible.)
- Search: typing in the search box filters server-side across all eligible projects — not just the first 20.
- Footer count: the footer reflects the count of loaded projects.
5. Verify it works across all policy types
Repeat step 3 for each remaining policy type (Merge request approval, Pipeline execution, Vulnerability management). The dropdown should behave identically — the fix lives in the shared LinkedGroupsProjectsDropdown.
6. GraphQL verification (optional)
In /-/graphql-explorer, run:
query {
project(fullPath: "topgroup/security-policies-project") {
securityPolicyEligibleProjects(search: "") {
nodes { id name fullPath }
pageInfo { hasNextPage endCursor }
}
}
}Expected nodes: project-1, project-2, project-3, project-4, and security-policies-project. These are all non-archived projects under the root ancestor (topgroup) of the SPP's linked groups that the current user has access to.
Test plan
- Projects from sibling groups are visible in the dropdown
- Projects from parent groups are visible in the dropdown
- Backward compatibility: existing project selection still works
- Pagination works correctly (no hardcoded
first: 20limit) - Search works via server-side filtering
- Empty state when no eligible projects exist
- Works for all policy types (Scan Execution, Merge Request Approval, Pipeline Execution, Vulnerability Management)
Closes #593858