Draft: Add npmMetadata field to Artifact Registry version type (monolith/S06 plan: 8/22)
What does this MR do and why?
Step 8 of the monolith/S06 "version detail" plan (work item #623231): the GraphQL npm version metadata field for GitLab Artifact Registry. It targets master directly (the Step 5 single-version field it built on has merged).
What it adds:
- An
npmMetadatafield on the version element type (ArtifactRegistryVersion), returning a newArtifactRegistryNpmVersionMetadatatype. The field reads the already-loaded version value object, so it issues no Artifact Registry request; the detail type inherits it through subclassing. ArtifactRegistryNpmVersionMetadataexposesdescription(pulled from the stored package.json) andpackageJson, the filtered package.json object the Manifest tab renders verbatim.packageJsonis a GraphQL JSON scalar because the object is free-form, publisher-authored content with no fixed shape. The stored copy is what Artifact Registry kept after publish-time allow-list filtering (it drops READMEs, licenseText, contributors, and lifecycle-hook fields, and caps the size), not the document the publisher sent.descriptionis guarded: a non-String stored value resolves null rather than a stringified Hash or Array, matching how the value object guards the container.
The npmMetadata field is nullable: a Maven version, a version published before the projection lands, or one whose stored object is absent or malformed all resolve null, so the Manifest tab renders its empty state rather than an error.
Behind the shared artifact_registry_ui feature flag, dark. Schema text only, so no changelog and no user-facing i18n. The GraphQL reference docs, introspection schema, and possible_types.json are regenerated.
References
- Work item: #623231
- Plan: https://gitlab.com/gitlab-org/ops/artifact-registry/-/blob/main/docs/plans/monolith/2026-08-18-version-detail.md (Step 8)
- Spec: https://gitlab.com/gitlab-org/ops/artifact-registry/-/blob/main/docs/specs/monolith/S06-version-detail.md (npm metadata)
How to set up and validate locally
The field is a pure read over the version value object and the metadata type, so it validates without an Artifact Registry request. Run in rails runner (or paste into rails console):
type = ::Types::ArtifactRegistry::NpmVersionMetadataType
# 1. npm version -> the stored metadata hash is returned
version = ::ArtifactRegistry::Version.new(
'id' => '1', 'version' => '1.0.0',
'npm_metadata' => { 'description' => 'A demo package', 'name' => '@acme/demo' }
)
raise 'expected metadata hash' unless version.npm_metadata == { 'description' => 'A demo package', 'name' => '@acme/demo' }
# 2. description leaf: a String is returned as-is
desc = type.send(:new, version.npm_metadata, {}).description
raise "expected description, got #{desc.inspect}" unless desc == 'A demo package'
# 3. description leaf: a non-String is coerced to null
bad = type.send(:new, { 'description' => { 'nested' => 'x' } }, {}).description
raise "expected nil for non-String description, got #{bad.inspect}" unless bad.nil?
# 4. packageJson returns the whole object verbatim
pj = type.send(:new, version.npm_metadata, {}).package_json
raise "expected raw object, got #{pj.inspect}" unless pj == { 'description' => 'A demo package', 'name' => '@acme/demo' }
# 5. a Maven version (no npm_metadata) -> the field resolves null
maven = ::ArtifactRegistry::Version.new('id' => '2', 'version' => '2.0.0')
raise 'expected nil npm_metadata for Maven' unless maven.npm_metadata.nil?
# 6. a non-Hash stored value -> null (fail-safe)
malformed = ::ArtifactRegistry::Version.new('npm_metadata' => 'oops')
raise 'expected nil for non-Hash metadata' unless malformed.npm_metadata.nil?
puts 'OK: npmMetadata path validated (description guard, packageJson passthrough, Maven/malformed null)'Expected output: OK: npmMetadata path validated (description guard, packageJson passthrough, Maven/malformed null).
Suggested labels
~"type::feature" ~backend ~frontend ~"Category:Artifact Registry" ~"group::container registry"
Screenshots or screen recordings
N/A. Schema text only, behind a dark feature flag, with no rendered UI change in this MR.