Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md grounded in the project's real conventions; six EARS-aware persona spec-review checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/ api-contract templates; a fully worked _example feature; a living RTM; and an adrs/ pointer that reuses the existing docs/adr/ archive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
95 lines
3.9 KiB
Markdown
95 lines
3.9 KiB
Markdown
# API Contract Stub
|
|
|
|
This project is **REST + OpenAPI**. The backend serves the live spec via springdoc at
|
|
`http://localhost:8080/v3/api-docs` (dev profile only), and the frontend generates its
|
|
TypeScript client from it with `npm run generate:api` (`openapi-typescript` →
|
|
`frontend/src/lib/generated/api.ts`). There is no GraphQL in this stack.
|
|
|
|
> **The live spec is generated from the Java controllers — it is the source of truth.** A
|
|
> hand-written contract under `.specify/features/<name>/api-contract.yaml` is a *design
|
|
> artifact*: it pins the intended shape during spec review and is checked against the
|
|
> generated spec once the endpoint exists. Keep it OpenAPI **3.1**, and keep
|
|
> `@Schema(requiredMode = REQUIRED)` on the Java side as the real driver of `required`.
|
|
|
|
## How to use this stub
|
|
|
|
1. Copy the skeleton below to `.specify/features/<name>/api-contract.yaml`.
|
|
2. Fill in the paths/methods/schemas your feature adds. Every mutating path documents the
|
|
`403`/`401` responses and the `cookieAuth` security requirement (matching the real
|
|
`@RequirePermission` gate).
|
|
3. The `sdd-gate.yml` CI job lints any changed `api-contract.yaml` with Spectral
|
|
(`npx @stoplight/spectral-cli lint`). Run it locally the same way before pushing.
|
|
4. After the endpoint ships, run `npm run generate:api` and diff the generated types against
|
|
this contract; reconcile any drift (the generated spec wins — update the contract).
|
|
|
|
## OpenAPI 3.1 skeleton
|
|
|
|
```yaml
|
|
openapi: 3.1.0
|
|
info:
|
|
title: Familienarchiv API — <feature name>
|
|
version: 0.0.1-SNAPSHOT
|
|
description: Design-time contract for <feature>. Source of truth is the generated /v3/api-docs.
|
|
servers:
|
|
- url: http://localhost:8080
|
|
description: Local backend (dev profile)
|
|
- url: https://archiv.raddatz.cloud
|
|
description: Production (behind Caddy)
|
|
components:
|
|
securitySchemes:
|
|
cookieAuth: # Spring Session JDBC — opaque session id in the SESSION cookie
|
|
type: apiKey
|
|
in: cookie
|
|
name: SESSION
|
|
schemas:
|
|
ErrorResponse: # shape produced by GlobalExceptionHandler
|
|
type: object
|
|
required: [code, message]
|
|
properties:
|
|
code:
|
|
type: string
|
|
description: Machine-readable ErrorCode (see ErrorCode.java / errors.ts).
|
|
example: FORBIDDEN
|
|
message:
|
|
type: string
|
|
# <YourResponseView>: # always a view, never a lazy-collection entity (ADR-036)
|
|
# type: object
|
|
# required: [id]
|
|
# properties:
|
|
# id: { type: string, format: uuid }
|
|
security:
|
|
- cookieAuth: [] # default: every path requires a session unless overridden to []
|
|
paths:
|
|
/api/<resource>:
|
|
post:
|
|
summary: <create …>
|
|
operationId: <createResource>
|
|
security:
|
|
- cookieAuth: [] # plus @RequirePermission(Permission.X) on the controller
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/<CreateDTO>' }
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/<YourResponseView>' }
|
|
'400': { description: Validation failed, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
|
'401': { description: Unauthenticated, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
|
'403': { description: Missing permission, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
|
|
```
|
|
|
|
## Validating the contract in CI
|
|
|
|
The `sdd-gate.yml` workflow runs, on PRs that touch a `api-contract.yaml`:
|
|
|
|
```bash
|
|
npx @stoplight/spectral-cli lint .specify/features/**/api-contract.yaml
|
|
```
|
|
|
|
Spectral's default OpenAPI ruleset catches malformed specs, missing `operationId`s, and
|
|
undefined `$ref`s. Add a `.spectral.yaml` at the repo root to tune rules if needed.
|