Add artifact registry GraphQL mutation foundation and repository read
What does this MR do and why?
Adds the GraphQL foundation for Artifact Registry repository CRUD (monolith/S07 Step 1): a base mutation, the single-repository read resolver, and the description/settings fields on the shared ArtifactRegistryRepository type. This is the seam the create/update/delete mutations build on.
- Base mutation (
Mutations::ArtifactRegistry::Base): a template that resolves the organization fromcontext[:current_organization], gates on theartifact_registry_uiflag and the organization'sread_artifact_registryability, acquires the request-memoized client, and wraps the call in the S03 error rendering. Concrete mutations implement#resolve_artifact_registryand return their payload fields; the base supplies the standarderrorsarray, so a mutation cannot ship an ungated write or leak a client error by forgetting to wrap the call. A flag-off or non-member request raises a top-levelResourceNotAvailablebefore any client call. Artifact Registry remains the authoritative per-operation authorizer; the Rails gate here is a membership floor, not a per-operation pre-check. Each concrete mutation declares its ownauthorize_granular_token skip_reason: :external_service_authorizes. - Single-repository read (
Resolvers::ArtifactRegistry::RepositoryResolver): resolves one repository by the organization slug and name, returningnullon a read-404 (edit prefill). - Shared client acquisition (
ArtifactRegistry::AcquiresClientconcern): the flag check, client acquisition, and slug forwarding shared by the base resolver and the base mutation; each supplies the organization through#artifact_registry_organization. - Type fields: adds
descriptionand the polymorphicsettingsobject to the sharedArtifactRegistryRepositorytype.settingsis non-null: the client defaults it to an empty object, so it is always present. - Frontend typedefs: removes the now server-backed
descriptionfield and theOrganization.artifactRegistryRepositoryread from the Vue app's client-side Apollo typedefs (graphql/typedefs.graphql). The merged Vue write UI declared these as local@clientextensions while there was no backend; now that the schema provides them, keeping the local declarations would collide during the Apollo schema build. The write flows still run on the local mock resolvers through the remaining@clientoperations.
The shared ArtifactRegistryRepository type and its enums are owned by monolith/S04, merged in !248690 (merged). This MR only adds the two experiment fields to that type and the single-read field on the organization; it introduces neither the type nor the enums.
All fields and arguments are experiment (milestone 19.3), behind the dark artifact_registry_ui flag, so there is no user-facing change and no changelog.
References
- Spec:
docs/specs/monolith/S07-repository-crud.md(Approved), GraphQL surface - Plan:
docs/plans/monolith/2026-07-17-repository-crud.md(merged), Step 1 - Shared type and enums dependency: !248690 (merged) (monolith/S04), merged
- Follow-up: #609504 (closed) — type
settingswith a union/interface before GA - ADR-014 (frontend to Artifact Registry), ADR-021 (authorization)
Screenshots or screen recordings
N/A. Schema only, behind a dark flag; the one frontend change removes now-redundant client typedefs and has no visible effect.
How to set up and validate locally
- Point the client at a local Artifact Registry service and enable the flag (adjust the token stub as needed for your setup):
org = Organizations::Organization.first Gitlab.config.artifact_registry['api_url'] = 'http://127.0.0.1:8090' Feature.enable(:artifact_registry_ui, org) - Query the single-repository read for an existing repository:
query = <<~GQL query($id: OrganizationsOrganizationID!, $name: String!) { organization(id: $id) { artifactRegistryRepository(name: $name) { name description settings } } } GQL user = org.organization_users.first.user pp GitlabSchema.execute(query, variables: { 'id' => org.to_global_id.to_s, 'name' => 'my-repo' }, context: { current_user: user, current_organization: org }).to_h - Confirm a missing repository resolves to
null(pass a name that does not exist), and that with the flag off the field resolves tonullwith no client call.
Unit and request specs cover the base mutation gate, the resolver read paths, and the two added type fields:
bundle exec rspec \
ee/spec/graphql/mutations/artifact_registry/base_spec.rb \
ee/spec/graphql/resolvers/artifact_registry/repository_resolver_spec.rb \
ee/spec/graphql/types/artifact_registry/repository_type_spec.rb \
ee/spec/requests/api/graphql/organizations/artifact_registry_repository_spec.rbMR acceptance checklist
Evaluate this MR against the MR acceptance checklist.