feat(managementapi): serve the maven arms of the bulk delete routes

Why

The three package-family bulk-delete routes (POST .../{format}/packages|versions|files/bulk_delete) have dispatched per format since the write-route scaffold landed, but every arm answered the placeholder 501, so a Maven repository had no reachable bulk delete while the merged Maven bulk worker sat unreachable behind them. This MR serves the Maven arms, enqueue-only: decode the route's selector, resolve the repository with the hosted-kind narrowing and the stored-format comparison the single-artifact arms apply, and enqueue one job on the shared BulkEnqueuer seam, stamped with the acceptance instant read off the database clock. The npm arms keep their 501.

What (the non-obvious parts)

  • The enqueue seam merged inert: nothing tied a consumer to the bootDeps river-client holder, and a route arm holding a fresh holder answers 503 for the life of the pod while every unit test stays green. The boot-level walk (cmd/artifact-registry/wire_management_bulk_route_integration_test.go) drives the mounted route through the real composition root with nothing overridden and reads the job row back out of river_job. Under that exact fresh-holder mutation it fails after exhausting its budget while the unit suites stay green, verified locally in both directions.
  • The selector decode runs inside the served arm, ahead of any repository read, rather than ahead of the dispatch as the shell's earlier comment sketched. The npm arm must keep answering its stateless 501 without reading the body, and the contract sweep's body-less POST pins that.
  • The payload names the repositories row and the parent path id, read against no store. Parent liveness is the worker's live re-resolve, so a batch under a deleted parent still answers 202 and a resubmitted batch keeps its 202, which is the retry contract entries already have.
  • One job per batch, no chunking: the widest Maven selector is 1,000 canonical UUIDs at about 39 KB against the 64 KiB payload cap, and a unit test holds the arithmetic against the encoder.

Test plan

  • go test ./... and go test -race ./internal/managementapi/ ./cmd/artifact-registry/ green.
  • go test -tags=integration ./internal/managementapi/ green, including one end-to-end walk per route (packages subset plus resubmit, versions delete_all, files subset), each applying the captured job through the real MavenBulkWorker and asserting the collection's end state through the live read routes.
  • go test -tags=integration ./cmd/artifact-registry/ -run 'TestIntegration_BulkDeleteRoute|TestIntegration_RiverClientHolder|TestIntegration_BulkEnqueuer|TestWireManagementAPI_ThreadsUsageTracker' green.
  • golangci-lint 2.12 clean on both touched packages, untagged and --build-tags=integration with --max-same-issues=0 --max-issues-per-linter=0.

LOC

1,940 insertions across 16 files: 351 production, 1,589 tests. The tests are the step's own suite (the route unit tables across the three routes, the contract-sweep drain, the selector ceiling and enqueue-gate suites, the three integration walks, and the boot-level composition walk), and splitting them out would open an MR that serves routes without the tests that pin them.

Delete-family e2e scenario rows ride E2e lifecycle catalogs: one delete-family pass ... (#638) • Unassigned.

Related to #313 (closed)

Edited by Hayley Swimelar

Merge request reports

Loading
Loading