Include granular_scopes in service account personal access token rotate API responses

Summary

Rotating a service account personal access token returns the new token without its granular_scopes. A service account can hold a fine-grained token, so the response should say which scopes the token has.

Today the rotate response shows granular: true and scopes: ["granular"], but not the scopes themselves.

This covers reporting only. Whether service account routes can create fine-grained tokens is discussed separately in #573355.

Affected endpoints

  • POST /groups/:id/service_accounts/:user_id/personal_access_tokens/:token_id/rotate (lib/api/group_service_accounts.rb:342)
  • POST /projects/:id/service_accounts/:user_id/personal_access_tokens/:token_id/rotate (lib/api/project_service_accounts.rb:316)

Cause

Both routes rotate through PersonalAccessTokens::RotateService. It copies the granular scopes of the old token to the new token.

Both routes then present the new token with API::Entities::PersonalAccessTokenWithToken and no options. That entity inherits from API::Entities::PersonalAccessToken. The parent entity exposes granular_scopes only when the token is granular and options[:with_granular_scopes] is passed.

Proposed fix

Do the same as !260276 (merged) did for the other rotate routes. Present the new token with **granular_scopes_options_for([new_token]).

The helper is in lib/api/helpers/personal_access_tokens_helpers.rb. Both API classes already include API::Helpers::PersonalAccessTokensHelpers.

For a token that is not granular, the helper returns no options and runs no query. Legacy token responses stay the same.

Update doc/api/service_accounts.md in the sections "Rotate a personal access token for a group service account" and "Rotate a personal access token for a project service account". Add a history item and a note, as !260276 (merged) did.

Acceptance criteria

  • Both routes return granular_scopes for a granular token.
  • 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.
  • Docs are updated for both routes.

References