Add GraphQL single-version field to Artifact Registry (monolith/S06 plan: 5/22)

What does this MR do and why?

Step 5 of the monolith/S06 "version detail" plan (work item #623231). It adds the GraphQL single-version field and the foundations later steps build on.

Add a version(id:, artifactId:) field on Types::ArtifactRegistry::RepositoryDetailsType, resolved by a new VersionResolver and returning the version element type (VersionType via the ArtifactRegistryVersionDetails detail type). The single-version read returns the same ArtifactRegistryVersion shape the versions connection does; the detail type carries the child fields later steps mount (files connection, statistics), which stay off the versions list connection so a per-row fan-out cannot happen.

Add a VersionPresenter that pairs the version value object with the repository and organization it was read through, so the later child fields can reach the slug, repository name/format, and client.

The resolver holds the repository format ahead of the client call (a docker/oci repository resolves null with no request), resolves null on 404 (existence-hiding), and applies an artifact-pairing check: it resolves null when the version's package_id names a different package. The pairing check fails open when Artifact Registry omits package_id, because rejecting an absent key would null every version on a deployment predating the serialization patch.

Add sizeBytes (nullable BigInt) to the version element type; it is null for a Maven version until Artifact Registry serializes the column, and on a remote repository.

Review feedback addressed

  • Coerce ArtifactRegistry::Version#package_id to a String so a JSON-number package_id from Artifact Registry compares correctly against the coerced GraphQL ID argument (previously a numeric package_id would fail the pairing check and null every version).
  • Coerce ArtifactRegistry::Version#size to an Integer so a numeric-string or float from Artifact Registry cannot render a non-numeric sizeBytes through BigInt; a non-numeric value reads nil.
  • Cover sizeBytes on the versions connection path in the request spec (it lives on the shared element type, so it is live on the list too).
  • Add type/nullability/argument/resolver assertions for the new version field in the repository details type spec.
  • Make the mock-resolver spec's parent-answered-fields exemption earned, by asserting the version resolver returns each exempted field on its object.
  • Restore the load-bearing note on VersionType that a bare version has no policy, so dropping the authorization skip would raise.

References

Screenshots or screen recordings

N/A. Schema text only, behind the dark artifact_registry_ui feature flag, with no rendered UI change in this MR.

Danger's multiversion-compatibility warning is not a concern here: the frontend change is typedefs-only and the version field stays @client, so nothing new is asked of the server in this MR.

How to set up and validate locally

Two parts: a rails runner script that asserts the value-object coercion (the headline review fix), and the spec commands.

Save the following script and run it with bundle exec rails runner <file> (observed output: OK: package_id and size coercion behave as expected).

# Validates the review-feedback fixes on ArtifactRegistry::Version coercion:
#   - package_id (JSON number) coerces to String so the resolver pairing check compares correctly
#   - size (numeric string / non-numeric) coerces to Integer or nil so BigInt renders numerically
numeric_pkg = ArtifactRegistry::Version.new('id' => 'v1', 'package_id' => 123)
raise "package_id not coerced to String" unless numeric_pkg.package_id == '123'

absent_pkg = ArtifactRegistry::Version.new('id' => 'v1')
raise "absent package_id should be nil (fail-open)" unless absent_pkg.package_id.nil?

int_size = ArtifactRegistry::Version.new('id' => 'v1', 'size' => 4_294_967_296)
raise "integer size should pass through" unless int_size.size == 4_294_967_296

str_size = ArtifactRegistry::Version.new('id' => 'v1', 'size' => '987654')
raise "numeric-string size should coerce to Integer" unless str_size.size == 987_654

bad_size = ArtifactRegistry::Version.new('id' => 'v1', 'size' => 'not-a-number')
raise "non-numeric size should coerce to nil" unless bad_size.size.nil?

puts "OK: package_id and size coercion behave as expected"

Then the spec commands:

  1. Value-object unit spec (uses fast_spec_helper, run alone): bundle exec rspec ee/spec/lib/artifact_registry/version_spec.rb -> 14 examples, 0 failures.
  2. GraphQL unit specs: bundle exec rspec ee/spec/graphql/resolvers/artifact_registry/version_resolver_spec.rb ee/spec/graphql/types/artifact_registry/version_type_spec.rb ee/spec/graphql/types/artifact_registry/repository_details_type_spec.rb -> 32 examples, 0 failures.
  3. Request specs (need VITE_ENABLED=true): VITE_ENABLED=true bundle exec rspec ee/spec/requests/api/graphql/organizations/artifact_registry_version_spec.rb ee/spec/requests/api/graphql/organizations/artifact_registry_versions_spec.rb -> 50 examples, 0 failures.
  4. Frontend Jest (the Apollo composition and mock/query suites): node_modules/.bin/jest ee/spec/frontend/packages_and_registries/artifact_registry/graphql/ -> 238 tests pass across 6 suites.
  5. GraphQL gates: bundle exec rake gitlab:graphql:validate and node scripts/frontend/graphql_possible_types_extraction.js --check, plus an Apollo schema composition check of the frontend typedefs against the dumped server schema -> all pass.

Note: the local Artifact Registry service could not be booted this session (its build now requires an iam block or authorization.unenforced that the local-stack config snapshot predates, unrelated to this MR), so validation is via the WebMock-backed request specs, shaped to the AR v1 OpenAPI contract.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Narendran

Merge request reports

Loading
Loading