Add activate Artifact Registry mutation

What does this MR do and why?

This MR adds Mutations::ArtifactRegistry::Activate, the GraphQL mutation an organization owner's setup page calls to provision the organization's Artifact Registry. This is monolith S10, Step 5.

The mutation takes the organization (from request context) and the requested slug only. The billing anchor is derived server-side from the organization's single top-level group, so a permanent value never reaches a form field or a mutation argument.

It extends the shared Artifact Registry mutation base Mutations::ArtifactRegistry::Base (the same base the repository mutations use). The base supplies the feature-flag gate, the organization from context, the RendersErrors error mapping, and the standard errors payload array. Activate overrides the base availability gate to raise the membership floor from read_artifact_registry to update_organization, because claiming a slug is an owner-only write. That ability implies read, so it is the only gate the mutation needs. The check is re-run server-side on every request. With the artifact_registry_ui flag off it raises a top-level resource-not-available error before any client call.

To avoid duplicating the resolve flow, it implements the base resolve_artifact_registry template method rather than re-implementing the whole resolve wrapper, errors-array management, or flag gate. One small enabling change to the base class: its payload errors array is now a memoized private method instead of a local, so a subclass can append a recoverable service refusal. This is backward compatible; the repository mutations that also extend the base are unaffected.

The mutation invokes ArtifactRegistry::ProvisionNamespaceService (already on master) and returns the provisioned mapping's resolved registry (slug, status, creation time) in its registry payload field. A refused slug and the zero-or-several-top-level-groups refusal are recoverable: they populate the payload errors and leave the form editable rather than raising. An unavailable service is re-raised as service-unavailable through the shared error mapping.

This change is behind the existing artifact_registry_ui flag, which is dark and disabled by default. Because the flag is dark there is no changelog and the schema text needs no i18n.

Not yet functional end to end: ServiceCredential is the S02 fail-closed seam and returns a nil token until the auth path wires a real provider. Until then a live activation fails closed with a service-unavailable error before reaching Artifact Registry, so the mutation is inert on a real instance and is exercised only through doubled-client specs. This matches the stubbed-client state of the other Artifact Registry slices.

Rebase note: the branch was rebased onto master after Steps 3 and 4 merged, so it now carries only the Step 5 commits and targets master directly.

How to set up and validate locally

This is a dark, off-by-default change with no user-visible behavior yet, so it is validated by the spec suite and a schema-wiring script.

Run this with bundle exec rails runner:

mutation = Mutations::ArtifactRegistry::Activate

abort("FAIL: not a BaseMutation") unless mutation < ::Mutations::BaseMutation
abort("FAIL: does not extend the shared AR base") unless mutation < ::Mutations::ArtifactRegistry::Base
puts "OK: extends Mutations::ArtifactRegistry::Base"

args = mutation.arguments.keys
abort("FAIL: expected a slug argument, got #{args}") unless args.include?('slug') && (args - ['slug', 'clientMutationId']).empty?
puts "OK: single argument 'slug' (billing anchor derived server-side)"

fields = mutation.fields.keys.sort
abort("FAIL: expected registry + errors fields, got #{fields}") unless fields == %w[clientMutationId errors registry]
puts "OK: exposes registry payload field beside errors"

registered = GitlabSchema.mutation.fields.key?('artifactRegistryActivate')
abort("FAIL: mutation not registered on the schema") unless registered
puts "OK: registered as artifactRegistryActivate"

puts "ALL CHECKS PASSED"

It prints "ALL CHECKS PASSED".

Automated specs and results:

  • RuboCop clean on the changed files (activate.rb, base.rb).
  • Activate mutation request spec: bundle exec rspec ee/spec/requests/api/graphql/mutations/artifact_registry/activate_spec.rb -> 13 examples, 0 failures. It covers: an end-to-end provision that writes the mapping row and returns the resolved registry; a taken slug (409), a syntactically invalid slug, no top-level group, and several top-level groups, each asserting its distinct refusal message with no row and no client call; an already-activated organization resolving idempotently to its existing slug; a 503 raising service-unavailable with the message preserved; a provision that succeeds while the follow-up status read fails, raising rather than leaking a resolution-failure marker into the field; a member holding only the read ability and a non-member, each a top-level access error with no client call; and the flag off, resource-not-available with no client call.
  • Repository mutation specs: bundle exec rspec ee/spec/requests/api/graphql/mutations/artifact_registry/repositories/ -> 21 examples, 0 failures, confirming the base change is backward compatible.
  • The authorization and flag guards were mutation-tested: reverting update_organization to read_artifact_registry fails the read-ability and non-member specs, and removing the flag gate fails the flag-off spec.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

References

Edited by Narendran

Merge request reports

Loading
Loading