Add includeNonBillable to the subscription usage events API

What does this MR do and why?

The user drill-down on the GitLab Credits dashboard lists billable events only, so once the user's total credits used card counts non-billable usage (!265553 (merged)), the event log adds up to less than the card, and promotional usage leaves no trace there.

CustomersDot now takes an opt-in includeNonBillable argument on events and usedFlowTypes and reports billable and skipReason per event (https://gitlab.com/gitlab-org/customers-gitlab-com/-/merge_requests/17693). This MR is the backend half of consuming it:

  • GraphQL: GitlabSubscriptionUsageUser.events and .usedFlowTypes take includeNonBillable: Boolean = false. GitlabSubscriptionUsageUserEvent gets billable: Boolean and skipReason: String, both null unless the argument is set. Without the argument every query is unchanged, so existing API consumers see no change.
  • Client: new GET_USER_EVENTS_WITH_NON_BILLABLE_QUERY and GET_USED_FLOW_TYPES_WITH_NON_BILLABLE_QUERY, used only when the argument is set. While CustomersDot hasn't deployed the argument (or the two event fields), the client retries with the billable-only query, the same fallback pattern as entityType and nonBillableCreditsUsed.
  • Complexity: CustomersDot caps license-key (self-managed) requests at a query complexity of 200. GitLab's events query sits at 171; the two new fields over a 20-row page add 40. The opt-in query therefore leaves out the deprecated eventType (superseded by flowType, https://gitlab.com/gitlab-org/gitlab/-/work_items/581215), landing at 191. GitLab's eventType field is documented as null when includeNonBillable is true. Measured with GraphQL::Analysis::QueryComplexity against the CustomersDot schema.
  • Specs, GraphQL docs and introspection schema.

Frontend (badge on non-billable rows, filter including non-billable flows) is in !265821 (merged), stacked on this branch. Split on purpose: GitLab.com deploys are rolling, and this change adds a GraphQL argument, which @gl_introduced can't cover at all.

This is change G2 of the plan in #633154 (comment 4099142904).

References

Screenshots or screen recordings

Screenshot_2026-10-29_at_08.37.01

How to set up and validate locally

  1. Check out https://gitlab.com/gitlab-org/customers-gitlab-com/-/merge_requests/17693 in your local CustomersDot. Without it, the client falls back to the billable-only queries and billable/skipReason are null.

  2. Set up GDK in SaaS mode with that CustomersDot, as in "How to set up and validate locally" in !260725 (merged).

  3. In CustomersDot, seed billable and non-billable Orbit usage for one user and enrich:

    bin/populate_usage_data -r 20 --realm SaaS --root-namespace-id <id> --user-id <user_id> --feature-qualified-name orbit_mcp --team-member --skip-reason promo
    bin/populate_usage_data -r 20 --realm SaaS --root-namespace-id <id> --user-id <user_id> --feature-qualified-name orbit_rest
  4. In /-/graphql-explorer, as a group owner:

    query {
      subscriptionUsage(namespacePath: "<group-path>") {
        usersUsage {
          users(username: "<username>") {
            nodes {
              usedFlowTypes { id }
              allFlowTypes: usedFlowTypes(includeNonBillable: true) { id }
              events(first: 20) { nodes { flowType billable skipReason } }
              allEvents: events(includeNonBillable: true, first: 20) { nodes { flowType eventType billable skipReason } }
            }
          }
        }
      }
    }
  5. events and usedFlowTypes are unchanged (billable only, billable/skipReason null). allEvents interleaves orbit_mcp rows with billable: false, skipReason: "promo" and eventType: null; allFlowTypes adds orbit_mcp.

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 Sheldon Led

Merge request reports

Loading
Loading