feat(api): add project creation endpoint#6472
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
There was a problem hiding this comment.
ℹ️ No critical issues — one minor robustness note inline.
Reviewed changes — initial review of the new POST /v2/projects.createProject endpoint and the shared projects scaffolding it lands.
- Add
createProjecthandler —GetPrincipal→BindBody→Authorize(project.*.create_project)→TxRetrywrappingInsertProject+ aproject.createaudit log, returning{id, slug}. Ordering matches existing create handlers. - Duplicate handling — relies on the existing
projectsUNIQUE(workspace_id, slug)constraint;db.IsDuplicateKeyErrormaps to the newerr:unkey:data:project_already_existscode (409 via the middleware mapping). - RBAC — new
rbac.CreateProjectaction (Go) andcreate_projectinprojectActions(TS) plus both dashboard permission catalogs. - Errors / codes / docs —
project_already_existscode, conflict middleware mapping, error.mdxpage, anddocs.jsonnav entry. - Audit —
ProjectCreateEvent(project.create) andProjectResourceType(project). - OpenAPI — request body (
name1-256,slug1-256 pattern^[a-z0-9]+(-[a-z0-9]+)*$), response{id, slug}, and the path index documenting 200/400/401/403/409/429/500. Generatedgen.go+ yaml regenerated. - Query rename —
FindProjectByWorkspaceSlug→FindProjectByWorkspaceAndSlugacross the.sql, generated code,BUILD.bazel, seed, and the 200 test. - Tests — 200 (incl. multi-create + audit-log assertion), 400 (missing/invalid/too-long fields, bad JSON, missing auth), 401, 403 action matrix, and 409 (dup within workspace + same slug in a different workspace succeeds).
Claude Opus | 𝕏
1876780 to
586a415
Compare
There was a problem hiding this comment.
ℹ️ No critical issues — one minor test-coverage note inline.
Reviewed changes — re-review of the delta since the prior pullfrog review: a tighter slug pattern, the new project id in the response, and audit-log metadata.
- Tightened slug pattern —
^[a-z0-9-]+$→^[a-z0-9]+(-[a-z0-9]+)*$across request body, response data, and the rendered OpenAPI/gen.go, forbidding leading, trailing, and consecutive hyphens; descriptions and the 400 example detail updated to match. - Added project
idto the response —V2ProjectsCreateProjectResponseDatanow returns{id, slug}(idpattern^[a-zA-Z0-9_]+$,minLength8), wired through the handler and asserted in the 200 test (proj_prefix + echo equals the persistedproject.ID). - Populated audit-log
Meta— theproject.createresourceMetachanged fromnilto{"name", "slug"}. - Renamed query —
FindProjectByWorkspaceSlug→FindProjectByWorkspaceAndSlugacross.sql, generated code,BUILD.bazel, seed, and the 200 test. - Duplicate-key error — switched from
fault.Wrap(err, …)tofault.New("project already exists", …)while keeping the same 409 code and public message.
Claude Opus | 𝕏
chronark
left a comment
There was a problem hiding this comment.
let's clean up the schema a little, rest looks good
586a415 to
4b06029
Compare
query name was off. it felt like we are querying projects by workspaceSlug and workspaceID
4850277 to
379d65d
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — re-review of the delta since the prior pullfrog review (fe6e252, now rebased onto main): commit 379d65d refactor: allow actor util to accept session, which addresses chronark's request to have ctrlclient.Actor parse the session itself.
Actornow takes a*zen.Session—ctrlclient.Actor(p *principal.Principal, remoteIP, userAgent string)becameActor(s *zen.Session) (*ctrlv1.ActorInfo, error), reading the principal offs.GetPrincipal()and the origin offs.Location()/s.UserAgent()so handlers no longer assemble the arguments by hand; it now also seedsMetawith an empty map and returns an error when the session has no principal.- Updated the lone caller — the create-project handler calls
ctrlclient.Actor(s)and propagates its error; the//pkg/zendep was added toctrlclient'sBUILD.bazel.
This implements chronark's suggestion cleanly. The handler already calls s.GetPrincipal() before Actor(s), and GetPrincipal() is a cached field read (pkg/zen/session.go:129), so the second call inside Actor is safe and cheap. The only caller is updated and all three session methods used (GetPrincipal/Location/UserAgent) exist. chronark's review thread is already resolved.
Claude Opus | 𝕏


What does this PR do?
Adds
POST /v2/projects.createProject, the first endpoint of the new projects API.Creates a project (
name+ workspace-uniqueslug) scoped to the caller's workspace, then returns the project'sslug. Duplicate slugs within a workspace are rejected with409. Delete protection defaults to off.Because it's the base of the stack, this PR also lands the shared scaffolding the later endpoints build on:
ProjectOpenAPI schema + theprojects.*path scaffold.projectresource type +create_projectaction.project.createaudit event.projectstable queries (insert) and generated code.Fixes # (issue)
Type of change
How should this be tested?
Covered by Go integration tests (
svc/api/routes/v2_projects_create_project/):slug+meta.requestId, and aproject.createaudit log.err:unkey:data:project_already_exists)._), slug too long, malformed JSON, missing auth header.create_project.Checklist
mise run fmt