API-first: OpenAPI 3.0-spec för plattforms-API:t

- services/plattform/openapi.yaml dokumenterar hela ytan: auth
  (registrera organisation, logga in), användarhantering (admin),
  ärenden + append-only händelselogg (idempotent synk), arbetsledar-
  översikten, publik Live Share-delning och AI-orkestern — inklusive
  scheman för alla 15 händelsetyper, roller, JWT-anspråken och
  API:ts bärande principer (append-only, multi-tenant-404).
- Specen serveras live av plattformstjänsten på GET /api/openapi.yaml
  och följer med i containern.
- Verifierad i tre lager: maskinell validering (swagger-cli),
  paritetstest i vitest (varje dokumenterad väg finns i servern,
  händelsetyperna är kompletta) och integrationsteststeg som hämtar
  specen från den körande tjänsten.

Verifierat: integrationstestets 18 kontroller gröna mot Postgres 16,
29 vitest-tester gröna, produktionsbygge ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-03 07:22:05 +00:00
parent 04ab80a0b0
commit 12c5f1c617
7 changed files with 516 additions and 4 deletions
+27
View File
@@ -61,6 +61,33 @@ describe("AI-orkestern", () => {
expect(schema).toContain("check (roll in ('tekniker', 'arbetsledare', 'admin'))");
});
it("OpenAPI-specen och plattformsservern täcker samma endpoints", () => {
const spec = readFileSync("services/plattform/openapi.yaml", "utf8");
const server = readFileSync("services/plattform/server.mjs", "utf8");
// Varje dokumenterad väg finns i servern …
const vagar: [string, string][] = [
["/halsa", "/halsa"],
["/api/openapi.yaml", "/api/openapi.yaml"],
["/api/auth/registrera", "/api/auth/registrera"],
["/api/auth/logga-in", "/api/auth/logga-in"],
["/api/anvandare", "/api/anvandare"],
["/api/arenden", "/api/arenden"],
["/api/arenden/{arendeId}/handelser", "handelser"],
["/api/oversikt", "/api/oversikt"],
["/api/delad/{delningskod}", "delad"],
];
for (const [iSpec, iServer] of vagar) {
expect(spec).toContain(`${iSpec}:`);
expect(server).toContain(iServer);
}
// … och AI-endpointen + händelsetyperna är dokumenterade.
expect(spec).toContain("/api/ai:");
expect(spec).toContain("append-only");
for (const typ of ["objekt_identifierat", "kontroll_utford", "ai_svar", "arende_avslutat"]) {
expect(spec).toContain(typ);
}
});
it("endpointen kodar AI-reglerna i grundprompten", () => {
expect(ENDPOINT).toContain("Hitta aldrig på fakta");
expect(ENDPOINT).toContain("aldrig en hypotes som ett konstaterat fel");