Add per-user daily usage method to SubscriptionUsageClient
## Problem
Part of &22817 (self-scoped GitLab Credits usage view), split out of gitlab-org/gitlab#604722. The self-scoped dashboard lives behind the `user_gitlab_credits_dashboard` feature flag.
`currentUser.creditsUsage` — the self-scoped GraphQL field resolved by `Resolvers::GitlabSubscriptions::User::CreditsUsageResolver` — exposes `creditsUsed`, `products`, `usedFlowTypes`, and `blockedStatus`, but no `dailyUsage` series.
The `dailyUsage` fields that already exist are cross-user and unusable for a self-view:
- `GitlabSubscriptionUsage.dailyUsage` is subscription-wide.
- `usersUsage.dailyUsage` is an all-consumers aggregate.
Deriving the series from the per-user `events` connection is not viable either: that resolver is capped at `max_page_size: 20`, so a month of activity would need dozens of round trips.
Until the field exists, the dashboard chart in `ee/app/assets/javascripts/usage_quotas/user_credits_dashboard/` has no real series to read. The frontend cutover is tracked separately in gitlab-org/gitlab#627199, which is blocked by this issue.
The upstream dependency is now in place. gitlab-org/gitlab#605985 added a per-user `dailyUsage` field to CustomersDot, implemented in gitlab-org/customers-gitlab-com!17037. It accepts only a `flowTypes` filter, always returns a date-ascending series, and returns `null` unless exactly one user is requested:
```graphql
usersUsage {
users(userIds: [$userId]) {
dailyUsage(flowTypes: $flowTypes) {
date
creditsUsed
}
}
}
```
## Proposal
Expose the upstream field through the backend, up to and including `currentUser.creditsUsage.dailyUsage`.
### 1. API client
In `ee/lib/gitlab/subscription_portal/subscription_usage_client.rb`:
- Add `GET_USER_DAILY_USAGE_QUERY`, selecting `usersUsage.users(userIds:).dailyUsage(flowTypes:)`.
- Add `get_daily_usage_for_user_id(user_id, flow_types: nil)`.
- Memoize with `strong_memoize_with(:get_daily_usage_for_user_id, user_id, flow_types)`, matching sibling methods such as `get_used_flow_types_for_user_id`.
- Return `{ success: true, dailyUsage: [...] }`. Read the series via `response.dig(:data, :subscription, :gitlabCreditsUsage, :usersUsage, :users).to_a.first`, so a missing user yields `dailyUsage: nil` rather than raising.
- Guard forward compatibility with the existing `undefined_field_error?(response, 'dailyUsage')` helper, the same pattern used for `sessionCreditsUsed` and `entityType`. When CustomersDot has not yet deployed the field, log a warning via `Gitlab::AppLogger` and return `{ success: false, reason: :undefined_field, dailyUsage: nil }` instead of raising to Sentry.
### 2. Domain object
In `ee/lib/gitlab_subscriptions/subscriptions_usage/user/credits_usage.rb` (`GitlabSubscriptions::SubscriptionsUsage::User::CreditsUsage`):
- Add a `daily_usage` method calling `subscription_usage_client.get_daily_usage_for_user_id(user.id, flow_types: flow_types)`.
- Return `[]` unless the response succeeded; otherwise map rows into structs.
- Reuse the existing `::GitlabSubscriptions::SubscriptionUsage::DailyUsage` struct (`date`, `credits_used`, `declarative_policy_subject`), the same way this class already reuses `::GitlabSubscriptions::SubscriptionUsage::Product` and `::GitlabSubscriptions::SubscriptionUsage::ProductFlowType`.
- Set `declarative_policy_subject` to `declarative_policy_subject` (the `user`), so the authorization subject is the user and not the subscription.
- Memoize with `strong_memoize_attr`, matching `used_flow_types`, `products`, and `blocked_status` in the same class.
### 3. GraphQL type
- Add `ee/app/graphql/types/gitlab_subscriptions/user/daily_usage_type.rb`.
- A self-safe type is required: `Types::GitlabSubscriptions::SubscriptionUsage::DailyUsageType` authorizes `read_subscription_usage`, which a Guest self-viewer does not hold. The new type authorizes `read_user` and declares `authorize_granular_token skip_reason: :parent_authorizes`.
- This mirrors how `Types::GitlabSubscriptions::User::ProductType` was added alongside `Types::GitlabSubscriptions::SubscriptionUsage::ProductType`.
- Fields: `date` (`GraphQL::Types::ISO8601Date`, non-null) and `creditsUsed` (`GraphQL::Types::Float`, non-null).
- Add a `daily_usage` field to `ee/app/graphql/types/gitlab_subscriptions/user/credits_usage_type.rb`, typed `[User::DailyUsageType]`, `null: true`.
- No field arguments. Flow-type filtering already comes from the resolver's existing `flowTypes` argument, which the domain object threads through.
### 4. Generated artefacts
- Regenerate `doc/api/graphql/reference/_index.md` and `public/-/graphql/introspection_result.json`.
### 5. Specs
- `ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb` — request variables for both self-managed and GitLab.com, the `flow_types` filter, the empty-`users` case, memoization, and the undefined-field guard.
- `ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user/credits_usage_spec.rb`
- `ee/spec/graphql/types/gitlab_subscriptions/user/daily_usage_type_spec.rb`
- `ee/spec/graphql/types/gitlab_subscriptions/user/credits_usage_type_spec.rb`
- `ee/spec/requests/api/graphql/gitlab_subscriptions/user/credits_usage_spec.rb`
## Files
API client:
- `ee/lib/gitlab/subscription_portal/subscription_usage_client.rb`
Domain object:
- `ee/lib/gitlab_subscriptions/subscriptions_usage/user/credits_usage.rb`
GraphQL:
- `ee/app/graphql/types/gitlab_subscriptions/user/daily_usage_type.rb` (new)
- `ee/app/graphql/types/gitlab_subscriptions/user/credits_usage_type.rb`
Generated:
- `doc/api/graphql/reference/_index.md`
- `public/-/graphql/introspection_result.json`
Specs:
- `ee/spec/lib/gitlab/subscription_portal/subscription_usage_client_spec.rb`
- `ee/spec/lib/gitlab_subscriptions/subscriptions_usage/user/credits_usage_spec.rb`
- `ee/spec/graphql/types/gitlab_subscriptions/user/daily_usage_type_spec.rb`
- `ee/spec/graphql/types/gitlab_subscriptions/user/credits_usage_type_spec.rb`
- `ee/spec/requests/api/graphql/gitlab_subscriptions/user/credits_usage_spec.rb`
## Out of scope
- **Frontend.** Removing the mocked `dailyUsage` from `buildMockSelfCreditsUsage` and pointing the dashboard chart at the real field is tracked in gitlab-org/gitlab#627199. This issue is backend only.
- **`sort` and `limit` arguments.** The original proposal for this issue included them, but the upstream CustomersDot field accepts only `flowTypes` and always returns a date-ascending series, so forwarding `sort` or `limit` would be a GraphQL error. The subscription-wide `GitlabSubscriptionUsage.dailyUsage` keeps its `sort` and `limit` arguments and is unaffected. If per-user sorting or limiting is needed later, it belongs either in the caller or in a follow-up CustomersDot change.
- **Per-user `dailyAverage`.** CustomersDot does not expose it on the per-user type.
- **Owner/Admin subscription dashboard.** No change to its existing behaviour or authorization.
## Acceptance criteria
- `currentUser.creditsUsage.dailyUsage` returns the current user's own daily series for the given namespace and date range, filtered by the resolver's `flowTypes` argument.
- The series is never another user's and never the subscription-wide figure.
- A Guest self-viewer can read the field, because the type authorizes `read_user` rather than `read_subscription_usage`.
- When CustomersDot has not yet deployed the per-user field, the client logs a warning and the field degrades to an empty series rather than raising.
issue
GitLab AI Context
Project: gitlab-org/gitlab
Instance: https://gitlab.com
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/CONTRIBUTING.md — contribution guidelines
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/README.md — project overview and setup
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/AGENTS.md — AI agent instructions
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/CLAUDE.md — Claude Code instructions
Repository: https://gitlab.com/gitlab-org/gitlab
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD