feat(npm): implement deprecate handler (S11 Step 17, part 2/2)

Part 2 of 2 of the npm hosted planStep 17: deprecate handler.

🗂️ Stacked MRs

Split into 2 stacked MRs to keep each within the review size limit (~600 ideal reviewable LoC). Each part targets the previous one (part 1 → main); review and merge bottom-up.

📦 What this part adds

  • NpmVersionDeprecateWriter — the READ COMMITTED transaction envelope that batch-updates the targeted versions' package_json and force-expires the packument cache atomically, returning ErrNotFound (→ 404 version_not_found) when the batch touches fewer rows than requested (a version soft-deleted between resolve and write).
  • DeprecateHandler for PUT /{slug}/npm/{repo}/{package} (no _attachments, non-empty versions["{ver}"].deprecated): resolves every targeted version in one query, patches each stored document (empty string clears deprecated), returns 200 {"ok": true}, and enqueues one package-level cache rebuild off the request path (TODO(s27-cache-rebuild)).
  • Client input maps to 4xx, never 500: empty / unknown version key → 404 version_not_found; oversized deprecation message → 422 package_json_too_large; malformed body → 400 bad_request; invalid package name → 422 package_name_invalid. A request that targets no version short-circuits to 200 without a transaction or a rebuild.

ℹ️ The route stays on the 501 placeholder until Step 23 conformance; mounting the real handler is a deliberate later step, the same pattern used for the other npm handlers.

Spec coverage

Acceptance criterion / case Test
AC 24 — deprecate sets the field, fires one package rebuild TestDeprecateIntegration_Success
AC 27 — cache force-expired in the same transaction TestDeprecateIntegration_Success
AC 34 — missing / soft-deleted package → 404 package_not_found TestDeprecateIntegration_PackageNotFound
AC 35 — missing / soft-deleted version → 404 version_not_found TestDeprecateIntegration_VersionNotFound
AC 58 — no resolution → 500 (auth-gate stand-in) TestDeprecateIntegration_MissingResolution
Undeprecate — empty string clears deprecated, siblings preserved TestDeprecateIntegration_Undeprecate
Non-targets skipped; a no-target request short-circuits to 200 TestDeprecateIntegration_SkipsNonTargets
Empty version key → 404; oversized message → 422 TestDeprecateIntegration_EmptyVersionKey, TestDeprecateIntegration_OversizedMessage
Malformed body → 400; invalid package name → 422 TestDeprecateIntegration_MalformedBody, TestDeprecateIntegration_InvalidPackageName
Writer rolls back when a co-batched target is stale TestDeprecateIntegration_WriterRollsBackStaleVersion

📚 References

Related to #135 (closed)

Edited by Dzmitry (Dima) Meshcharakou

Merge request reports

Loading
Loading