Reduce GraphQL query complexity for security inventory query
What does this MR do and why?
Splits the security inventory's monolithic GraphQL query into two focused queries to reduce complexity below the authenticated limit of 250.
The subgroups_and_projects.query.graphql query had a calculated complexity of ~329 (reported as 340 in the issue), exceeding AUTHENTICATED_MAX_COMPLEXITY (250). This was flagged by the complexity tests added in !213762 (merged). While the query worked in practice because the frontend passes first: 20 (complexity is calculated against the worst-case max_page_size of 100), it was at risk of breaking if fields were added or larger page sizes used.
The original query used @skip(if: $hasSearch) and @include(if: $hasSearch) directives to make the browse path (descendantGroups + projects) and the search path (namespaceSecurityProjects) mutually exclusive. This MR makes that separation explicit by splitting them into two separate .graphql files, each with significantly lower complexity.
Implementation details
-
subgroups_and_projects.query.graphql(browse mode): Stripped down to only fetchdescendantGroupsandprojectsunder a group. Removed$search,$hasSearch,$namespaceId, filter variables,@skip/@includedirectives, and the entirenamespaceSecurityProjectsblock. -
namespace_security_projects.query.graphql(search mode, new file): Fetches security-filtered projects via thenamespaceSecurityProjectsresolver plus a minimalgroup { id }fetch. Contains all search/filter variables. -
inventory_dashboard.vue:- Replaced the single
subgroupItemsApollo smart query with two:subgroupItems(skipped when searching) andsearchResults(skipped when not searching). - Split
fetchSubgroupsAndProjects()intofetchSubgroupsAndProjects()(browse) andfetchSecurityProjects()(search). - Split
loadMoreProjects()intoloadMoreBrowseProjects()andloadMoreSearchProjects(). - Added
refetchData()that delegates to the active query. - Updated
isLoadingto check both smart queries.
- Replaced the single
-
Test files (9 files): Extracted shared mock data into reusable
mockProjects/mockSubgroupsarrays. AddednamespaceSecurityProjectsResponsemock andcreateSearchResponse/createPaginatedSearchHandlerhelpers. Updated 7 spec files to use the new direct exports. Registered both query handlers in the Apollo mock provider.
How to set up and validate locally
- Navigate to a group's Secure > Security inventory page.
- Browse mode: Verify subgroups and projects load. Click "Load more" to test pagination for both subgroups and projects.
- Search mode: Enter a search term or apply filters (analyzer status, vulnerability counts, security attributes). Verify filtered projects load and paginate correctly.
- Transition: Switch between browse and search modes (clear the search bar) and confirm data updates correctly.
- Verify sidebar navigation (clicking subgroups) still works.
To verify complexity reduction in a Rails console:
%w[subgroups_and_projects namespace_security_projects].each do |name|
Gitlab::Graphql::Queries.all
.select { |q| q.file.include?("#{name}.query.graphql") }
.each { |q| puts "#{q.file} complexity: #{q.complexity(GitlabSchema)}" }
end
puts "MAX: #{GitlabSchema::AUTHENTICATED_MAX_COMPLEXITY}"
ee/app/assets/javascripts/security_inventory/graphql/subgroups_and_projects.query.graphql complexity: 197
ee/app/assets/javascripts/security_inventory/graphql/namespace_security_projects.query.graphql complexity: 140
MAX COMPLEXITY: 250
MR acceptance checklist
- Tests added and updated (319 tests across 25 suites pass)
- No new N+1 queries introduced (query split only, no resolver changes)
- ESLint, Prettier, and circular dependency checks pass
- Pipeline passes
- No breaking changes to user-facing behavior
Related issues
Closes #584297
References
- !213762 (merged) - Added GraphQL query complexity tests that identified this issue
- #581051 (closed) - Static analysis for GraphQL query complexity
- #580576 (closed) - Original regression where a GraphQL query exceeded complexity limits
Changelog: fixed