# API2 Specialist Report — System (CommissioningSystem) CR Endpoints Spec: `docs/specs/20260710-140533-agent-feature.md` Three project-scoped, read/create endpoints for the existing `xyz.CommissioningSystem` entity (exposed as **systems**), modelled on the **Asset** endpoint pattern (`src/api/v2/projects/assets/`): - `POST /api/v2/projects/{projectId}/systems` - `GET /api/v2/projects/{projectId}/systems/{systemId}` - `GET /api/v2/projects/{projectId}/systems` (keyset-paginated) ## Revision addressed **Reviewer feedback: "fix the failing api test."** ### Root cause (cross-clone) The failing test was the **systems e2e suite**. Every scenario that first creates a system (the `POST` 201 case, plus the pagination, get-by-id, and 404-mapping cases that seed data via `POST`) was failing. The root cause was **on the database side, not in API2**: the create path had been implemented in the Postgres clone as a **procedure** (`usp_InsertCommissioningSystem`, returning through `OUT` params), while the API2 service calls it as a **function**: ```ts INSERT: `SELECT * FROM xyz."fn_InsertCommissioningSystem"($1, $2, $3, $4)` ``` A procedure is not invocable via `SELECT * FROM …`, so every create failed and cascaded into the dependent read/pagination scenarios. That mismatch has been resolved in the Postgres clone (procedure replaced with `fn_InsertCommissioningSystem`, mirroring `fn_InsertAsset`) — see `postgres/_SPECIALIST_REPORT.md`. The API2 layer was **already calling the correct function form**, so the API2 fix is confirming and verifying that the service, controller, validator, routes, swagger, and tests are all aligned with the corrected DB contract. No API2 behavioural change was required. ### Verification performed (API2 side) Against the corrected schema in the sibling `postgres` clone (`Database/xyz/Functions/fn_*CommissioningSystem*.sql`, `Tables/130_xyz_commissioning_system.sql`, `Constraints/078_xyz_commissioning_system_constraints.sql`): - **Call-site alignment (name + arity):** - `fn_GetCommissioningSystemList($1,$2,$3)` ↔ `(_projectId, _lastFetchedIndexId, _pageSize)` ✓ - `fn_GetCommissioningSystem($1,$2)` ↔ `(_projectId, _commissioningSystemId)` ✓ - `fn_InsertCommissioningSystem($1,$2,$3,$4)` ↔ `(_projectId, _systemTypeId, _name, _createdBy)` ✓ - No leftover `CALL`/`usp_…` usage for systems anywhere in `src/` or `test/`. - **Column mapping:** `mapRow` reads `CommissioningSystemId`, `Name`, `SystemTypeId`, `CreatedBy`, `InsertedOn`, `LastModifiedOn`, `LastModifiedBy`, and `Id` (pagination cursor) — all present in the functions' `RETURNS TABLE`. - **404 mapping:** the insert function raises `'SystemType with id % not found in project'`; `mapError` matches the substring `'SystemType with id'` → `NotFoundError` → HTTP 404, exactly mirroring Asset's `'AssetType with id'` mapping. - **Unit tests:** full suite green — **1736 passing, 0 failing** (`npm test`); the 8 systems controller specs pass. - **Type-check:** `npx tsc --noEmit` clean. - **Swagger:** `scripts/generate-swagger.ts` runs cleanly and resolves the new `System` `$ref`; the generated spec (`docs/xyz-platform-api-spec.json`) was regenerated only to verify, then reverted (CI regenerates & commits it). The **e2e suite could not be executed locally** — Docker is unavailable in this environment (no `docker` binary; Postgres/mountebank not reachable). The integration pipeline runs it. Every e2e assertion was traced by hand against the corrected backend and the `Assets` reference suite; all should pass. ## Files changed (API2) New: - `src/api/v2/projects/systems/systems.routes.ts` — routes + swagger JSDoc (`PROJECT_VIEW` for reads, `PROJECT_EDIT` for create, plus `INTERNAL_ROLE`). - `src/api/v2/projects/systems/systems.controller.ts` - `src/api/v2/projects/systems/systems.validator.ts` - `src/services/systems.service.ts` - `test/unit/api/v2/systems/systems.controller.spec.ts` - `test/e2e/api/systems.e2e.spec.ts` Modified: - `src/api/v2/api.v2.routes.ts` — mount `/:projectId/systems`. - `src/swagger.components.schemas.json` — add the `System` schema (referenced by `$ref`, per repo convention; no inline duplication). - `test/e2e/util/db-helper.ts` — add `getSystemById` / `getSystemsByProject` e2e DB helpers. **This revision** also restored the trailing newline that a prior edit had dropped (the only file content change in this pass). ## Deviations from the spec - The spec text mentions a **"System View"** permission; per its own revision note ("no new permissions; reuse existing Project Edit / System View") this is satisfied by the established `PROJECT_VIEW` / `PROJECT_EDIT` pair — no new authority was added, matching Assets/AssetTypes. ## Open questions for the reviewer - `CommissioningSystem` has a `UNIQUE ("ProjectShardId", "Name")` constraint (`CommissioningSystem_key`). Following the chosen **Asset** pattern, the service does **not** map a duplicate-name violation to `409` (Asset behaves identically and its service does the same); a duplicate name currently surfaces as `500`. The current e2e never triggers this (each run uses a fresh project), so it is not the failing test — but if a `409` conflict contract is desired for systems, say so and it can be added (mirroring the `SystemType` service's `ResourceConflictError` mapping) with matching tests.