4.1 KiB
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/yamlAND the body parses as YAML
Scenario: filtered view excludes other plugins
- WHEN
GET /docs/billingis 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"