Add Projects API and enforce project_id existence for External Results sessions - #322
Merged
Conversation
…tion Agent-Logs-Url: https://github.com/bg-playground/BGSTM/sessions/95c768fa-691c-4f33-8d8f-b78925a4a958 Co-authored-by: bg-playground <259109604+bg-playground@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Implement projects API and validate project_id in external results
Add Projects API and enforce May 8, 2026
project_id existence for External Results sessions
bg-playground
marked this pull request as ready for review
May 8, 2026 20:04
Owner
|
LGTM ✅ — comprehensive implementation, CI green including the smoke workflow. Strong work on the second-order details:
Acceptance check:
Closes #315. v0.2 step 1 of 2 done; #316 is the remaining piece. 🚀 Two non-blocking nits for awareness, no action needed:
|
This was referenced May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR closes the v0.2 gap where
/api/v1/projectswas missing and external session creation accepted non-existentproject_idvalues. It adds a real Projects API, enforces project existence at session creation time, and updates smoke bootstrap/specs to match enforced behavior.Projects API surface (
/api/v1/projects)POST, paginatedGETlist,GET {id}, andPATCH {id}endpoints.main.pyundersettings.API_V1_PREFIXwithtags=["projects"].Projects domain implementation
ProjectSQLAlchemy model and Alembic migration forprojectstable.ProjectCreateProjectResponse(from_attributes=True)ProjectUpdatecreate_projectget_projectlist_projectsupdate_projectAuth + audit behavior
POST,PATCH) require reviewer/admin.project.create(full payload snapshot)project.update(changed-fields diff)actor_kind="user",user_id=current_user.id,actor_token_id=None,resource_type="project".External Results FK validation
create_session, added pre-insert lookup againstprojects.create_external_session, extended existingValueErrorhandling to map this code to HTTP 400.Smoke bootstrap alignment
scripts/smoke/bootstrap.py.POST /api/v1/projectsand fails loudly on error.Tests
backend/tests/integration/test_projects.pycovering auth matrix, CRUD behavior, 404s, pagination shape, and audit entries.backend/tests/integration/test_external_results_session_project_fk.pyfor:project_id→400 session.project_not_foundproject_id→201Spec updates
docs/specs/external_results_v1.mdto statesession.project_not_foundis enforced.Original prompt
Goal
Implement issue #315 — add a real
/api/v1/projectsAPI and FK-validateproject_idin External Results sessions. This closes one of two v0.2 gaps the smoke workflow surfaced (the other is #316).Closes: #315
Related: #314 (smoke workflow — bootstrap synthetic-UUID fallback gets removed by this PR), #316 (separate v0.2 follow-up, do not touch in this PR)
Scope
Backend — new projects API
backend/app/api/projects.py— new router with:POST /api/v1/projects→201 CreatedwithProjectResponseGET /api/v1/projects→ list, paginated mirroring/api/v1/requirementsshapeGET /api/v1/projects/{project_id}→200or404PATCH /api/v1/projects/{project_id}→ partial updatebackend/app/schemas/project.py—ProjectCreate,ProjectResponse,ProjectUpdate. Match the existingProjectmodel's columns; mirror the patterns inschemas/requirement.py(Pydantic v2model_config,from_attributes=True).backend/app/crud/project.py— async helpers:create_project,get_project,list_projects,update_project. Mirrorcrud/requirement.pystyle.backend/app/main.py— register the new router undersettings.API_V1_PREFIXwithtags=["projects"].Auth and audit
POST,PATCH) requirerevieweroradminrole — mirror the requirements API guard exactly.external_results.session.start):actor_kind = "user",user_id = current_user.id,actor_token_id = Noneaction = "project.create"or"project.update"resource_type = "project",resource_id = project.iddetailsincludes the relevant changed fields (for create: full payload snapshot; for update: changed-fields diff)External Results FK validation
backend/app/crud/external_results.create_session: before insert, verifypayload.project_idresolves to an existing row in theprojectstable. If not, raiseValueErrorwith the spec-aligned shape:backend/app/api/external_results.pycreate_external_session: catch the newValueErrorshape and map to400 Bad Request(per existing spec § c.1 — the table already lists this code). Add to the existingValueErrorexception handler — don't introduce a parallel one.Smoke workflow updates (this same PR)
scripts/smoke/bootstrap.py:_get_or_generate_project_idsynthetic-UUID fallback. Always create a real project via the newPOST /api/v1/projectsendpoint.# NOTE: /api/v1/projects currently 404s on maincomment block..github/workflows/external-results-smoke.yml) should not need changes — bootstrap.py is invoked the same way.Tests
backend/tests/integration/test_projects.py— mirrortest_requirements.py:backend/tests/integration/test_external_results_session_project_fk.py— new file:project_id→ 400 withcode: "session.project_not_found"project_id→ 201 (regression for the happy path)scripts/smoke/test_assert.py— no changes expected (it doesn't exercise bootstrap).Spec doc
docs/specs/external_results_v1.md§ c.1 ("Create session" → "Error codes"):400 session.project_not_foundrow to indicate it is now enforced (was previously aspirational).POST /api/v1/projects.project_idand require it to exist.Out of scope
project_idcolumns to other tables (requirements, test_cases, etc.) — projects table only.bgstm:requirementannotation handling — tracked in [v0.2] BGSTMReporter should transmitbgstm:requirementannotations and BGSTM should resolve external→UUID for case-result linking #316.ValueError.Acceptance criteria
POST /api/v1/projectsreturns 201 withProjectResponse; persists row.This pull request was created from Copilot chat.