Artifact registry: Add the artifact table to the repository detail page

What does this MR do and why?

Fills the repository detail page's main column with the artifacts the repository holds. Until now the page rendered its header over an empty two-column grid.

This change is behind the feature flag :artifact_registry_ui (dark, default_enabled: false).

This is monolith/S05 Step 8, and the first AR slice to add a nested connection to the client-side Apollo local-state layer, so it introduces the artifact types the sidebar, empty state, and pager steps extend.

The table's shape follows the repository's format

Artifact Registry answers artifacts per format family (ADR-009): the container formats share one set of OCI Distribution Spec endpoints and return images, Maven and npm return packages, and a request whose format does not match the repository is a 404 the client maps to nil. So a repository carries one connection and null for the other, and that null is what the page reads to decide which table to render — no format switch in the query, which could not work anyway, since the format arrives in the same result.

The columns are what the contract can fill, and no more

The design draws five columns. Artifact Registry's list responses carry three fields:

contract type fields
ContainerImage id, name, last_downloaded_at
MavenPackage id, group_id, artifact_id, last_downloaded_at
NpmPackage id, name, scope, versions_count, tags_count, last_downloaded_at

Size, when an artifact was published, and what published it are absent from every one of them. Those are the Phase 2 columns gated on an AR contract extension, so they arrive with the extension that backs them rather than as cells with nothing to put in them. What ships:

format columns a row reads
DOCKER, OCI Image payment-service
MAVEN Package com.company.payment:core
NPM Package · Versions @company/payment-core

The name is the address a user would paste into a client, which is why Maven renders coordinates and npm renders its scope. An unscoped npm package renders its bare name rather than a name behind an empty scope.

Also deferred, all drawn in the prototype: the per-row copy and kebab, the artifact filter bar, and the sortable header. Rows are non-navigable — an artifact has no view of its own until monolith/S06.

Changes

  • repositories/detail/artifacts_table.vue (new) — a GlTable following repositories/list/repositories_table.vue's conventions. RegistryList was the other candidate and is the wrong one: it renders a card list with bulk-delete affordances.
  • graphql/typedefs.graphqlArtifactRegistryImage, ArtifactRegistryMavenPackage, ArtifactRegistryNpmPackage, a ArtifactRegistryPackage union over the two package types, and the two connections. A union rather than one element type with nullable fields, so each format's fields stay non-null instead of being set in alternation.
  • graphql/cache_config.js — exports possibleTypes for the union. Without it the cache cannot match the inline fragments and every Maven and npm cell renders blank. createDefaultClient merges it over the generated graphql_shared/possible_types.json.
  • graphql/mock_resolvers.js — nested images/packages resolvers keyed on the parent's format. Artifacts are derived from the repository rather than stored, because nothing in the browser publishes one.
  • repositories/detail/repository_detail.vue — renders the table, picks the connection, and reports a connection-only failure inside the table region so the header and sidebar stay.

No pageInfo on either connection. It arrives with the pager as pageInfo: PageInfo! referencing the schema's type — redeclaring it broke the Apollo project loader once already (c5f2822bc245).

Screenshots or screen recordings

One repository per format, each on its own detail page. Docker and OCI share the container family's Image column; Maven renders coordinates; npm is the only format whose list response carries a counter, so it is the only one with a second column.

Docker Maven
fmt-docker fmt-maven
npm OCI
fmt-npm fmt-oci

Two things visible above are not from this change: the empty space to the right of each table is the sidebar, which is the next step, and OCI renders a letter avatar rather than a logo because its logo waits on trademark approval.

How to set up and validate locally

  1. Enable the flag: Feature.enable(:artifact_registry_ui) in rails c.
  2. Visit /o/<organization>/-/artifact_registry/acme/repositoriesacme is Organizations::ArtifactRegistry::STUB_SLUG, and the controller renders 404 for any other slug.
  3. Create a repository through New repository → Hosted repository, picking the format you want to see.
  4. Back on the list, click the repository name. This has to be an in-SPA click: the local resolvers hold their store in memory, so a full page load empties it and the detail page renders not-found.
  5. Confirm the table renders that format's columns. Repeat per format — Docker and OCI share Image, Maven shows coordinates, npm adds Versions.

Verification performed

  • jest ee/spec/frontend/packages_and_registries/artifact_registry — 22 suites, 366 examples, green.
  • ESLint, Prettier clean on the touched files.
  • Driven in a browser against a GDK, one repository per format. Read back from the DOM rather than eyeballed:
    • DOCKER['Image'], rows payment-service, api-gateway, auth-service
    • OCI['Image'], same rows
    • MAVEN['Package'], rows com.company.payment:core, :api, :gateway
    • NPM['Package', 'Versions'], rows @company/payment-core 5, @company/payment-api 2, design-tokens 12 npm rendering at all is the check that possibleTypes is wired: an unmatched union fragment would have produced empty cells. Zero console errors throughout.

Notes for review

  • An empty repository gets a plain "This repository has no artifacts." line rather than bare column headers. The illustrated empty state that offers setup instructions is its own later step; this is a placeholder it replaces, and a small deliberate step beyond what the step was scoped to.
  • The artifact table has no busy state of its own. The plan called for a table skeleton, but the merged scaffold step deliberately withholds both page regions until the read resolves, and its specs pin that. One page-level loading affordance covers it. The pager step can add a busy state when paging makes one reachable.
  • hasArtifactsError reads a null connection, not a GraphQL error. That is what the contract produces (a failed or mismatched artifact list resolves nil) and it avoids an errorPolicy change that would alter how the page handles a failed repository read.

MR acceptance checklist

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

References

Edited by Zack Cuddy

Merge request reports

Loading
Loading