Repository navigation
Description
Summary
Correctness and cleanup pass over the OpenAPI plugin, made possible by the golden-file tests added in #159. Every defect below was found by reading the generated document against the JSON:API and OpenAPI specs, and each fix is pinned by a test.
The reference document shrank from 5407 to 4197 lines while gaining content: request bodies, typed linkage meta and nine new schemas.
| before | after | |
|---|---|---|
| Write operations documenting a request body | 0 / 9 | 9 / 9 |
Non-OpenAPI keys (exampleSetFlag) |
612 | 0 |
Phantom empty property |
34 | 0 |
Dangling $ref |
3 classes of | 0 |
| Schemas nothing references | 2 | 0 |
| Unit tests in the plugin | 2 | 32 |
Defects fixed
Security requirements referenced schemes that were never declared
JsonApiOperationsCustomizer hardcoded Authorization_Code_PKCE, while CommonOpenApiCustomizer declares the scheme under the configured name (Authorization_Code_With_PKCE in the samples). Every PKCE requirement was a dangling reference, so the Swagger UI Authorize button silently did nothing. Client_Credentials matched only by coincidence and would break on any rename.
Scheme names now come from OasProperties, the same source the declaration uses. A grant flow that is not configured is skipped rather than referenced.
No write operation documented a request body
POST /users, PATCH /users/{id} and all seven relationship writes had no requestBody at all — "Try it out" offered no body field, and generated clients took no argument. Two code paths were permanently dead: the plugin stores an OasOperationInfoModel, but both consumers tested instanceof OasOperationInfo, the annotation type. Always false, so payloadType had never worked.
Request bodies are now derived from what JSON:API prescribes per operation; @OasOperationInfo(payloadType = …) remains as an override. The parameter is typed, so the mismatch is no longer expressible.
Request and response schemas were the same schema
id was required on the resource object, so a spec-conformant POST without a client-generated id violated the published contract. Requests also advertised links and included, which a request never carries.
The request side now has its own schemas: <Type>CreateRequestDoc (id optional) and <Type>UpdateRequestDoc (id required), plus linkage-only relationship documents.
Every relationship was marked required
UsersRelationships.required = [citizenships, placeOfBirth, relatives] meant any response using sparse fieldsets — or simply omitting a relationship — violated its own schema.
Custom to-one linkage meta produced dangling references
The to-many branch registered the schemas it built; the to-one branch built them, $ref-ed them and dropped them. Invisible until now because no sample declared resourceLinkageMetaType — which was itself the bug: relatives carries meta.relationshipType in real requests and responses, and the document described it as an untyped object.
The sample now declares it, and RelativeLinkageMeta is the single definition of that shape — used by the relationship, by the operations that read and validate it, and by the plugin. Previously the same shape was spelled out three times as raw Map access on a string key.
The document contained keys OpenAPI does not define
OasServlet serialized with a plain Jackson mapper instead of swagger's, publishing internal bookkeeping — exampleSetFlag (612), types (268), jsonSchema (62), valueSetFlag — as if they were OpenAPI keywords.
Vendor extensions were affected too: they were nested under an extensions wrapper rather than emitted as top-level x- members, so tooling could not see x-operation-properties at all.
LinksObject was dead, and links published a phantom property
The hand-written LinksObject schema — the one describing a link as either a URL string or a link object — was registered and referenced by nothing. Instead 18 schemas reflected the Java class in place, publishing its isEmpty() accessor as an empty boolean that no response has ever returned.
Implementation artifacts leaked into the contract
ResourceObjectObjectObject — ResourceObject with its erased type arguments appended — was a public schema name referenced five times, with attributes and relationships typed as bare objects. It appeared wherever the includable types could not be determined. included is now published only when those types are known.
CurrenciesSingleResourceDoc was generated because the resource had some operation, though nothing returns a single currency. Documents are now generated per operation that returns them.
Improvements
- OAuth2 scopes now apply to
client_credentials, per RFC 6749 §4.4.2, and match how the Access Control plugin enforces them — one flow-agnostic scope set per operation, sincePrincipal.authenticatedClientScopes()is a flat client-level set that carries no notion of grant flow. @OasOperationInfogainedsummaryanddescriptionas overrides over the generated text. The documented example used them already and did not compile.- Scopes decoupled from entitlements.
x-scopes-required-entitlementsandscopes[*].requiredEntitlementswere a leftover of the removed ordered Access Tiers: mechanically renamed when tiers went away, the mapping lost its meaning. Nothing consumed it, no sample configured it, it was unreachable on Quarkus, and the Access Control plugin treats scopes and entitlements as independent gates. Removed. PrimaryAndNestedSchemascopies its nested list, soaddToNestedon a result ofgenerateAllSchemasFromTypeno longer throwsUnsupportedOperationException.- Shared
OasLinkageMetaUtil— request and response sides now build the typed linkage identifier with one code path, so they cannot drift.
Verification
- Golden document tests pass on all three sample apps (Servlet, Quarkus, Spring Boot), which is the guarantee that every integration emits an identical document.
- 32 unit tests across the customizers, up from 2. The regression test for the to-one linkage defect was confirmed to fail without the fix.
- The generated document was checked for dangling
$refs and unreferenced schemas: none of either.
Not addressed
Tracked separately: operations that return no 2xx (9 of 19), error examples serialized as escaped strings rather than JSON, two inverted conditions in CommonOpenApiCustomizer, the unsynchronized document cache in OasServlet, and the absence of operationId.
Activity
#160 OAS: fix Security Config naming issue
#160 OAS: allow OAuth Scopes for client_credentials (according to RFC…
#160 OAS: fix missing request body configs
#160 OAS: repair custom Meta types, apply massive spec cleanup, other…