Initial Commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: api-openapi (api-openapi)
|
||||
|
||||
**Package**: `@suaveplan/api-openapi`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: OpenAPI 3.1 document emitted from Zod schemas
|
||||
|
||||
The package SHALL emit an OpenAPI 3.1 document from every plugin's manifest plus every registered route's Zod schemas (request body / params / query / response). Conversion uses `@asteasolutions/zod-to-openapi`.
|
||||
|
||||
#### Scenario: full document covers every plugin
|
||||
|
||||
- WHEN two plugins are registered each with two routes
|
||||
- THEN `emitter.getJson()` returns an OpenAPI 3.1 document with paths for all four routes
|
||||
|
||||
**Verified by:** `src/emitter/emitter.test.ts::"emits a 3.1 document covering every registered route"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Namespacing rules are deterministic
|
||||
|
||||
The package SHALL set tags to each plugin's `displayName`, prefix component schemas with `<pluginName>.<TypeName>`, and set `operationId` to `<pluginName>.<method>.<routePath>`. operationIds MUST be unique across the merged document; collisions raise `OPENAPI_OPERATION_ID_COLLISION`.
|
||||
|
||||
#### Scenario: operationId is deterministic
|
||||
|
||||
- WHEN a billing plugin declares `GET /invoices/:id`
|
||||
- THEN the emitted operationId is `billing.get./invoices/{id}`
|
||||
|
||||
#### Scenario: operationId collision raises error
|
||||
|
||||
- WHEN two plugins register routes that produce identical operationIds
|
||||
- THEN emission raises `OPENAPI_OPERATION_ID_COLLISION`
|
||||
|
||||
**Verified by:** `src/namespacing/namespacing.test.ts::"operationId is deterministic"` AND `src/namespacing/namespacing.test.ts::"collision raises OPENAPI_OPERATION_ID_COLLISION"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Security schemes inherit from the active auth adapter
|
||||
|
||||
The package SHALL read the active auth adapter's `securitySchemes` contribution and attach the default scheme to every route except those listed in any plugin's `publicRoutes`.
|
||||
|
||||
#### Scenario: public route has no security
|
||||
|
||||
- WHEN a route is listed in `publicRoutes`
|
||||
- THEN the emitted operation has `security: []`
|
||||
|
||||
#### Scenario: non-public route inherits default scheme
|
||||
|
||||
- WHEN a route is not on `publicRoutes`
|
||||
- THEN the emitted operation lists the auth adapter's default scheme
|
||||
|
||||
**Verified by:** `src/emitter/emitter.test.ts::"public route emits empty security"` AND `src/emitter/emitter.test.ts::"non-public route inherits adapter scheme"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Endpoints serve json, yaml, and per-plugin filtered views
|
||||
|
||||
The package SHALL expose `GET /openapi.json`, `GET /openapi.yaml`, `GET /docs`, and `GET /docs/{plugin}` handlers. The filtered view MUST return only operations owned by the named plugin; an unknown plugin name raises `OPENAPI_UNKNOWN_PLUGIN_IN_FILTER` (HTTP 404).
|
||||
|
||||
#### Scenario: yaml endpoint returns yaml content-type
|
||||
|
||||
- WHEN a client requests `GET /openapi.yaml`
|
||||
- THEN response Content-Type is `application/yaml` AND the body parses as YAML
|
||||
|
||||
#### Scenario: filtered view excludes other plugins
|
||||
|
||||
- WHEN `GET /docs/billing` is requested in a host with billing + users plugins
|
||||
- THEN the response document contains billing routes only AND no users routes
|
||||
|
||||
**Verified by:** `src/endpoints/endpoints.test.ts::"yaml endpoint returns application/yaml"` AND `src/endpoints/endpoints.test.ts::"filtered view excludes other plugins"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Missing schema is handled per strictness setting
|
||||
|
||||
The package SHALL read `host.openapi.strictness`. When `"warn"`, missing request/response schemas log a warning AND emit with `description: "schema-missing"`. When `"refuse-install"`, missing schemas raise `OPENAPI_MISSING_SCHEMA` BEFORE the plugin reaches `installed`.
|
||||
|
||||
#### Scenario: warn mode emits with placeholder
|
||||
|
||||
- WHEN a route has no response schema AND strictness is `"warn"`
|
||||
- THEN emission succeeds AND the warning counter `api.openapi.missing_schema_total{plugin, route}` increments
|
||||
|
||||
#### Scenario: refuse-install raises
|
||||
|
||||
- WHEN strictness is `"refuse-install"`
|
||||
- THEN emission raises `OPENAPI_MISSING_SCHEMA`
|
||||
|
||||
**Verified by:** `src/emitter/emitter.spec-conformance.test.ts::"warn mode emits placeholder and increments warning counter"` AND `src/emitter/emitter.spec-conformance.test.ts::"refuse-install raises OPENAPI_MISSING_SCHEMA"`
|
||||
Reference in New Issue
Block a user