Loading
chore(managementapi): scaffold route mount and slug resolution
What
Phase 1 Step 2 of the S17 REST management API: scaffolds the
internal/managementapi package and mounts it on the application mux.
NewHandler: a privatehttp.ServeMuxregistering the five hosted-repository CRUD routes under/api/v1/{slug}/repositoriesand.../{repository_name}as501placeholders (method-scoped, so an unserved method returns405with anAllowheader automatically).slugMiddleware: resolves{slug}to a namespace viaFindBySlugand injects the namespace id into the request context. A missing slug returns404 not_found(existence-hiding); a finder error (or a(nil, nil)contract violation) returns500and logs the cause, never masking an outage as a404. Routes are wrapped at registration, so Steps 8-10 handlers read the id vianamespaceIDFromContextand cannot bypass resolution.wireManagementAPI: mounts the handler behind the existing auth/body-size/logging middleware chain.
CRUD handler bodies stay 501 placeholders; they land in Steps 8-10.
Review
/review-branch(APPROVE): added the500-path error logging (the only diagnostic when the response envelope hides the cause)./plannotator-review: dropped rot-prone spec-section and sibling-package references from comments.- MR review (GitLabDuo + AppSec): resolved the slug in middleware so
handlers read the namespace id from context instead of a
(uuid, bool)return (removes the "forgot to check the bool" footgun); added a defensive nil-namespace guard so a(nil, nil)finder result returns500rather than panicking.
Spec coverage
Spec: S17 REST management API.
This step owns the scaffold rows (route registration, slug-404
existence-hiding, the 500-not-masked-as-404 seam, mount wiring, the
error-envelope shape); per-handler CRUD rows are owned by Steps 8-10 / 5-7.
Acceptance criteria
| # | Criterion | This step |
|---|---|---|
| AC-7/8/10/11 | Route registration for detail/list/update/delete | TestHandler_RouteTable_EveryRouteResolves (each route 501s, not a bare miss) |
| AC-12 | Cross-slug isolation | Slug-resolution seam: TestHandler_UnknownSlug_Returns404WithEnvelope, TestSlugMiddleware_InjectsNamespaceID, TestWireManagementAPIWithFinder_UnknownSlugIs404 |
| AC-13 | Every error path returns the error envelope with request_id |
assertErrorEnvelope across the route-table, slug-404, and finder-failure tests |
| AC-1..6, 9 | OpenAPI + per-handler CRUD behavior | Owned by Step 1 (!637) and Steps 5-10 |
Error cases
| Endpoint | Condition | Status | This step |
|---|---|---|---|
| All | Slug resolves to no namespace | 404 not_found |
TestHandler_UnknownSlug_Returns404WithEnvelope, TestSlugMiddleware_ShortCircuitsOnFailure |
| All | Unexpected datastore failure / (nil, nil) |
500 (logged, not masked) |
TestHandler_FinderFailure_Returns500WithEnvelope, TestHandler_FinderFailure_LogsCause, TestResolveSlug_NilNamespaceWithoutError_Writes500 |
| Registered path, wrong method | 405 + Allow |
TestHandler_WrongMethod_Returns405WithAllow |
|
Per-handler 400/409/422 |
Owned by Steps 8-10 |
Security
| Concern | This step |
|---|---|
| Existence-hiding | Unknown slug 404s identically to a missing repo; a real datastore failure is a 500, never masked as 404 |
| Resolution bypass | slugMiddleware is the single resolve site; handlers read the id from context, so a handler cannot run against an unresolved slug |
| Tenant isolation | The slug-resolution seam scoping every request to a namespace_id is scaffolded here; per-query scoping lands in Steps 5-7 |
| Auth/authz, input validation, injection | Owned by S08/S09 and the handler/datastore steps |
Plan
Step 2 of docs/plans/2026-06-22-s17-phase1-repository-crud.md.
Part of epic gitlab-org#22342 (closed).
Related to #171 (closed)
Edited by João Pereira