Include granular_scopes in personal access token create and rotate API responses

Summary

Granular personal access tokens have scopes stored as granular_scopes, but several create and rotate endpoints do not return them. The caller sees granular: true and scopes: ["granular"] and cannot confirm which scopes were set or copied. GET /personal_access_tokens, GET /personal_access_tokens/:id and POST /personal_access_tokens/:id/rotate already return them.

Affected endpoints

  • POST /user/personal_access_tokens - lib/api/users.rb:1893. Accepts granular_scopes (exactly_one_of scopes/granular_scopes) and creates the token with create_granular_token, but presents it with PersonalAccessTokenWithToken and no options. The response omits the scopes the caller just set.
  • POST /personal_access_tokens/self/rotate - lib/api/personal_access_tokens/self_rotation.rb:41. RotateService copies granular scopes to the new token (app/services/personal_access_tokens/rotate_service.rb:66-68), and a granular token can call this route (rotate_personal_access_token permission). The response omits granular_scopes. The sibling POST /personal_access_tokens/:id/rotate already returns them.
  • POST /groups/:id/manage/personal_access_tokens/:pat_id/rotate - ee/lib/api/manage/groups.rb:134. Presents the rotated token the same way, without options.

Cause

API::Entities::PersonalAccessToken (lib/api/entities/personal_access_token.rb:25-27) exposes granular_scopes only when token.granular? && options[:with_granular_scopes]. PersonalAccessTokenWithToken inherits from it. The endpoints above present without the option, so the field is never rendered.

Verified on master by rendering the entity for a granular token in a development console: no granular_scopes key without the option, present with it.

Proposed fix

Follow the pattern used by GET /personal_access_tokens, GET /personal_access_tokens/:id and POST /personal_access_tokens/:id/rotate (lib/api/personal_access_tokens.rb:44,69,103, added by !243754 (merged)):

  • Preload granular_scopes: :namespace.
  • Present with with_granular_scopes: true and project_ids_by_namespace_id: project_ids_by_namespace_id_for([token]) (helper in lib/api/helpers/personal_access_tokens_helpers.rb).

!259764 (merged) applies the same pattern to GET /personal_access_tokens/self and is a good model, including specs.

The change is additive: the field only appears for granular tokens. Update the matching API docs page for each endpoint.

Acceptance criteria

  • The three endpoints return granular_scopes for granular tokens.
  • Legacy token responses are unchanged and run no granular_scopes query.
  • A project-scoped granular scope resolves project_id.
  • An N+1 spec covers the new presentation.
  • API docs are updated for each endpoint.

References