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: true

Why?

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 SequentialCursorPaginator class (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/hasPreviousPage across 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 InventoryDashboard to use the new sequential paginator class when the security_inventory_pagination feature 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 },
        }
    • replaces the "Load More" button with a GlKeysetPagination controlled by the paginator's hasNextPage and hasPreviousPage
    • uses the paginator's getNextCombinedPage and getPreviousCombinedPage in the component's handleNextPage and handlePreviousPage
    • uses the paginator's refetch in the component's handleRefetch
    • scrolls to top on page changes
  • it maintains backward compatibility with existing "Load more" pagination when the feature flag is disabled

References

Screenshots or screen recordings

Before (or feature flag disabled) After (with feature flag enabled)

How to set up and validate locally

  1. git checkout mfluharty-security-inventory-pagination-attempt-two
  2. have a GitLab Ultimate license
  3. enable https://gdk.test:3443/rails/features/security_inventory_pagination
  4. 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)

  1. in the sidebar, choose Secure => Security inventory
  2. 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.

Edited by Miranda Fluharty

Merge request reports

Loading