Add upsertFlatUserCap GraphQL mutation
What does this MR do and why?
Adds the upsertFlatUserCap GraphQL mutation to set the default per-user
budget cap for a subscription. Takes flatUserCap (Float, >= 0) and
flatUserCapEnabled (Boolean), proxies to CDot's upsertBudgetCapSubscription.
This mutation must be called before upsertUserBudgetCapOverrides (MR4) —
CDot requires a subscription budget cap record to exist before per-user
overrides can be created.
Gated behind the budget_caps_graphql_api feature flag.
Related to https://gitlab.com/gitlab-org/gitlab/-/issues/595491
Stacked MR
| # | MR | Description | Status |
|---|---|---|---|
| 1 | !230214 (merged) | Foundation — feature flag + client + shared concern | Merged |
| 2 | !230218 (merged) | Query — budget caps read API (BudgetCapsType, UserOverrideType, PORO) |
Merged |
| 3 | This MR | Mutation — upsert flat user cap | You are here |
| 4 | !230222 (merged) | Mutation — upsert user budget cap overrides | In review |
| 5 | !230226 (closed) | Enable feature flag by default (draft, blocked by 3-4) | Draft |
How to set up and validate locally
Prerequisites: GDK running with CDot on localhost:5000. Use existing group saastestgroup.
-
Enable the feature flag
Feature.enable(:budget_caps_graphql_api) -
SaaS — Owner sets flat user cap
Log in as owner of
saastestgroup. Open GraphiQL at http://localhost:3000/-/graphql-explorer:mutation { upsertFlatUserCap(input: { namespacePath: "saastestgroup" flatUserCap: 25.0 flatUserCapEnabled: true }) { flatUserCap flatUserCapEnabled errors } }Expected:
flatUserCap: 25.0,flatUserCapEnabled: true, empty errors. -
Self-Managed — Instance admin sets flat user cap
Log in as admin. Admin mode must be enabled — toggle admin mode from user settings (click avatar → Preferences → enable Admin Mode), or use a Personal Access Token (PAT) with
apiscope which bypasses admin mode automatically viasessionless_bypass_admin_mode!.Omit
namespacePathfor the SM path:mutation { upsertFlatUserCap(input: { flatUserCap: 100.0 flatUserCapEnabled: true }) { flatUserCap flatUserCapEnabled errors } }Expected:
flatUserCap: 100.0,flatUserCapEnabled: true, empty errors.Note: On Self-Managed, the
adminpolicy condition inBasePolicyrequires bothuser.admin?and an active admin mode session (Gitlab::Auth::CurrentUserMode#admin_mode?). Without admin mode enabled, the mutation returns a permission error. API requests authenticated with a PAT bypass this requirement automatically. -
Validation — Negative value rejected
mutation { upsertFlatUserCap(input: { namespacePath: "saastestgroup" flatUserCap: -5.0 flatUserCapEnabled: true }) { flatUserCap flatUserCapEnabled errors } }Expected: error
"flatUserCap must be greater than or equal to 0" -
Unauthorized — non-owner (SaaS)
Log in as a non-owner of
saastestgroup, run mutation from step 2.Expected:
"The resource that you are attempting to access does not exist or you don't have permission to perform this action" -
Feature flag disabled
Feature.disable(:budget_caps_graphql_api)Run mutation from step 2 again.
Expected: error
"budget_caps_graphql_api feature flag is disabled" -
Verify MR4 overrides work after flat cap is set
Re-enable the feature flag, then run
upsertUserBudgetCapOverrides(MR4). CDot requires a subscription budget cap record to exist before per-user overrides can be created — step 2 creates that record.mutation { upsertUserBudgetCapOverrides(input: { namespacePath: "saastestgroup" overrides: [ { userId: "gid://gitlab/User/1", cap: 15.0, enabled: true } ] }) { userOverrides { user { id username } cap capEnabled } errors } }Expected: updated overrides with resolved User objects.
MR acceptance checklist
These checklists encourage us to confirm any changes have been thoroughly considered.
- This MR is behind a feature flag (
budget_caps_graphql_api) - Request specs covering SaaS, self-managed, authorization, validation, and CDot error propagation