feat(api): add project update endpoint#6475
Conversation
f765ace to
ec990a0
Compare
38aac6a to
0e657f1
Compare
0e657f1 to
e784ce1
Compare
ec990a0 to
b9bc707
Compare
e784ce1 to
ce4d46c
Compare
b9bc707 to
96584f9
Compare
ce4d46c to
3943366
Compare
96584f9 to
8048b4e
Compare
26ec6eb to
5cb28c9
Compare
3943366 to
5f6c290
Compare
a1acb00 to
1853fec
Compare
a953ec0 to
32c9792
Compare
32c9792 to
7fd4c79
Compare
1853fec to
cef4b82
Compare
cef4b82 to
8ed92af
Compare
7fd4c79 to
7ce8785
Compare
7ce8785 to
b6ca98d
Compare
8ed92af to
190cd54
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — re-reviewed the delta since the prior Pullfrog review (73ca2b16): the endpoint switched its identifier from the project slug to the immutable projectId, made slug itself a patchable field, and added 409 conflict handling for duplicate slugs.
- Lookup by immutable
projectId—handler.gonow resolves the project viaFindProjectByWorkspaceAndIdand the request body requiresprojectId(^[a-zA-Z0-9_]+$, len 8-255), byte-identical to the siblinggetProjectrequest schema. TheUpdateProjectSQL is now keyed byWHERE workspace_id = ? AND id = ?. slugis now a mutable field — a newslug = CASE WHEN ... THEN ? ELSE p.slug ENDclause plusSlugSpecified/Slugparams let callers rename the slug; the no-op short-circuit and the response/auditslugnow reflect the requested value via theslug := project.Slug; if req.Slug != nil { ... }block.409on duplicate slug —db.IsDuplicateKeyError(MySQL 1062) maps theprojects.workspace_slug_idx UNIQUE(workspace_id, slug)violation tocodes.Data.Project.Duplicate, with a409spec block andConflictErrorResponse$refadded to the path.- Test coverage extended — new
409_test.go(slug collision), anupdate slug only200 case asserting name survival, and400cases forprojectIdlength/charset;400tests now useptr.Pfor optionalslug.
The identifier scheme now fully mirrors getProject, the read-independent CASE-on-specified-flags UPDATE keeps the persisted write race-free, and the 409 path is only reachable when the slug is actually being changed (the message correctly interpolates the requested slug). Verified IsDuplicateKeyError, the Duplicate URN, FindProjectByWorkspaceAndId, ConflictErrorResponse, and ptr.P all exist and the unique constraint backs the conflict. Both humans approved at 1853feca. Mergeable as-is.
ℹ️ Slug mutation vs. deployment domains
The new request-body description states that changing the slug "affects the deployment domains generated for this project," but the handler only updates the projects row.
- Deployment domains are built in
svc/ctrl/worker/deploy/domains.gofrom<prefix>-<deploymentID>-<workspaceSlug>.<apex>— they key off the workspace slug, not the project slug. - Already-generated domains are not retroactively rewritten by this endpoint, so existing deployments keep their current hostnames after a slug change.
This is a forward-looking scope question for the human, not a bug — flagging it only so the doc wording and any downstream expectation (e.g. whether future deployments pick up the renamed slug) stay aligned.
Claude Opus | 𝕏



Important
Unkey is not accepting external pull requests at this time. Pull requests from people outside the Unkey team will not be reviewed or merged.
What does this PR do?
Adds a new
POST /v2/projects.updateProjectendpoint that allows updating a project's display name and delete protection setting, identified by its slug. The slug itself is immutable. Omitted fields are left unchanged, so callers can patch only what they need.This includes:
UpdateProjectSQL query and generated database codeupdate_projectRBAC action (project.*.update_projectorproject.<project_id>.update_project)project.updateaudit log event emitted on every successful updateupdate_projectpermission exposed in the dashboard root key permissions UIFixes # (issue)
Type of change
How should this be tested?
POST /v2/projects.updateProjectwith a valid root key bearingproject.*.update_projectand a body containing a known projectslugplus an updatednameordeleteProtectionvalue — expect a200response with the updated project reflected indata.nameanddeleteProtectionfrom the body (onlyslugprovided) — expect a200response with the project unchanged.404.update_projectpermission — expect403.401.name— expect400.project.updateaudit log entry is created after a successful update.Checklist
Required
pnpm buildpnpm fmtmise run fmtconsole.logsgit pull origin mainAppreciated