Use next/prev pagination in inventory (modular)
What does this MR do and why?
Use next/prev pagination in inventory (modular)
Add a sequential cursor paginator class to vue_shared
Import and use it in the security inventory
if the security_inventory_pagination feature flag is enabled
Changelog: changed
EE: trueWhy?
The security inventory page is intended to be an overview of the state of security tools across an entire top-level group, so every item must be in the list. Statuses for projects within subgroups are summarized in the subgroup rows, and statuses for projects that are direct descendants of the top-level group are displayed in more detail.
Currently we have only a "Load more" button at the bottom that loads and appends an inconsistent number of items to the list. We want to limit the number of items displayed at a time to improves performance, and we want to make it more clear that the "select all" action selects the items that are visible at the time (up to the limit of 100 items).
How?
This MR implements next/previous pagination for the security inventory by introducing a new SequentialCursorPaginator class that combines multiple GraphQL resources into a single paginated view.
- it creates a
SequentialCursorPaginatorclass (app/assets/javascripts/vue_shared/utils/sequential_cursor_pagination.js), which:- manages cursor-based pagination across multiple GraphQL resources
- fetches items sequentially from each resource until a full page is collected
- tracks which resource the current page starts and ends on (
resourceStartIndex,resourceEndIndex) - caches pageInfo for each resource to determine
hasNextPage/hasPreviousPageacross all resources - supports any number of resources, and supports skipping resources conditionally (e.g., skip subgroups when searching)
- it uses the split GraphQL queries from Reduce GraphQL query complexity for security in... (!223950 - merged)
- it updates the
InventoryDashboardto use the new sequential paginator class when thesecurity_inventory_paginationfeature flag is enabled:- initializes a paginator with two resources (subgroups, then projects)
- each resource object includes a query and some options to guide the paginator how to read the resource's items and pageInfo from the query
// example: subgroups resource { query: SubgroupsAndProjectsQuery, // skip this resource if this function returns true skip: () => this.hasSearch, // use these pagination variable names when we get pages from this resource first: 'subgroupsFirst', last: 'subgroupsLast', after: 'subgroupsAfter', before: 'subgroupsBefore', // how to read nodes and pageInfo from the resource's query result getNodes: (result) => result.data.group.descendantGroups.nodes, getPageInfo: (result) => result.data.group.descendantGroups.pageInfo, // use these variables every time we use this resource's query baseVariables: { projectsFirst: 0 }, }
- each resource object includes a query and some options to guide the paginator how to read the resource's items and pageInfo from the query
- replaces the "Load More" button with a
GlKeysetPaginationcontrolled by the paginator'shasNextPageandhasPreviousPage - uses the paginator's
getNextCombinedPageandgetPreviousCombinedPagein the component'shandleNextPageandhandlePreviousPage - uses the paginator's
refetchin the component'shandleRefetch - scrolls to top on page changes
- initializes a paginator with two resources (subgroups, then projects)
- it maintains backward compatibility with existing "Load more" pagination when the feature flag is disabled
References
- issue: [Frontend] Combine more than one type of item i... (#591670)
- first frontend attempt: Add Pagination to Security Inventory Page (!189193 - merged) (now reverted)
Screenshots or screen recordings
| Before (or feature flag disabled) | After (with feature flag enabled) |
|---|---|
How to set up and validate locally
git checkout mfluharty-security-inventory-pagination-attempt-two- have a GitLab Ultimate license
- enable https://gdk.test:3443/rails/features/security_inventory_pagination
- go to a group - specifically, this should be a group that has at least 20 direct descendants (subgroups and projects) where you have at least the maintainer role
To populate some test data (assuming you're @root in GDK) you can run this in bin/rails c with whatever numbers you want:
root = User.find_by(username: 'root')
group = FactoryBot.create(:group)
group.add_member(root, :maintainer)
25.times { |i| FactoryBot.create(:group, name: "Subgroup #{i}", parent: group) }
25.times { |i| FactoryBot.create(:project, name: "Project #{i}", namespace: group) }... and then navigate to the group.web_url (https://gdk.test:3443/groups/group1, if it's the same as what my GDK did)
- in the sidebar, choose
Secure=>Security inventory - test out the pagination
- it should show all subgroups before all projects
- the pages should be distinct sets of items (no overlap) that are the same when you return back to them
- no items should be skipped over
- the "select all" action should toggle the selection of the currently visible items
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.