Read OpenAPI 3.2 documents with the released swagger-parser via a compatibility spec loader
Implements the approach proposed in #25477: openapi-generator can read OpenAPI 3.2 documents with the released swagger-parser (2.1.48), without waiting for 3.2 support in swagger-core/swagger-parser.
Today, openapi: 3.2.x gives no OpenAPI object at all. swagger-parser falls back to the Swagger 2 converter and reports attribute openapi is unexpected / attribute swagger is missing, so generation fails.
How it works
-
3.0/3.1 are not touched.
SpecLoader.forInputSpecparses the document exactly as before, with onereadLocation. Only when that fails withattribute openapi is unexpecteddoes it read the raw document again, with the same authorization values and timeouts as the parser. If the document declares3.2.x, it is parsed byOas32CompatSpecLoader. -
Oas32CompatSpecLoaderrewrites the raw tree before parsing:openapibecomes3.1.0.- 3.2-only members move into
x-oas32-*extensions:query,additionalOperations,in: querystring,$self, tagparent/kind,itemSchema, and so on. style: cookiebecomesform, with the original kept.- Missing response descriptions are filled with
"". - Relative
$refs are made absolute so the parser can be called without a location. The 3.1 dereferencer re-reads the location even for same-file references, which would bypass the rewrite.
-
Moved operations go through the parser too, so they get default values (
style/explode) and$refresolution. They are exposed throughOas32Extensions.allOperations/allOperationsList, each matching the call site's original enumeration order (readOperationsMap/readOperations). These are used in:DefaultGeneratorDefaultCodegen(preprocessOpenAPI,fromCallback,fromParameter)AbstractJavaCodegenInlineModelResolverOpenAPINormalizer(includingFILTER)ModelUtils
Documents that did not go through the compat loader enumerate exactly as before. Which documents did is tracked by object identity, not by an extension, so it can't leak into output or be forged by a document.
Verification
-
3.0/3.1 unchanged.
3_0/petstore,3_1/petstore,3_0/callbacks,3_1/webhooksand a HEAD+POST shared-schema case were generated asjavaandopenapi-yamlwith this branch and with master. There is no difference other than the generation timestamp.bin/generate-samples.shfor java-okhttp-gson, python and go-petstore produces no diff. -
3.2 documents generate with the default
validateSpec:QUERY/PURGEoperations, including ones inside callbacks and webhooks, with inline request/response bodies turned into models;- same-file and external
$refs.
The generated Java client compiles (
mvn compile).java --library feigngets the rightAccept/Content-Typeheaders for them. Generatingopenapi-yamltwice gives identical output. -
Oas32CompatSpecLoaderTestplus the existingDefaultGenerator/DefaultCodegen/InlineModelResolver/OpenAPINormalizer/ModelUtilstests pass, including forbiddenapis. The new tests fail without the change.
Known limits (warned at load time where applicable)
- 3.2-only members inside external documents are not rewritten. That would need the parser's resolver to go through the loader.
- Schema
$idand the root$selfare not used as a reference base. The result matches what the 3.1 parser does today. $refs that point into a moved member (e.g.#/paths/~1x/query/...) can't be resolved.- Generator-specific preprocessing in
languages/other thanAbstractJavaCodegen, plusvalidate,MergedSpecBuilderand the online service, still call the parser orreadOperations*directly. - Whether each language's templates can send a non-standard method such as
QUERYis the scope of #24982. - After the Swagger 2 converter attempt, swagger-parser logs two
failed to read resource listingERROR stack traces even when the 3.2 document then loads fine. An INFO line says so.
Questions for maintainers (from #25477)
- Is rewriting a 3.2 document to 3.1 at the generate entry point acceptable as a stopgap until swagger-parser ships 3.2?
- Are extension-held operations, exposed through
Oas32Extensions, acceptable until swagger-core has the model fields?
This is a large change, mostly in one new class (Oas32CompatSpecLoader) and its tests. I'm happy to split it if you prefer: for example, the SpecLoader refactor first, then the 3.2 loader.
PR checklist
- Read the contribution guidelines.
- Run the following to build the project and update samples:
Regenerated java-okhttp-gson, python and go-petstore samples: no diff. Generator options and docs are unchanged../mvnw clean package || exit ./bin/generate-samples.sh ./bin/configs/*.yaml || exit ./bin/utils/export_docs_generators.sh || exit - If your PR is targeting a particular programming language, @mention the technical committee members. (This is a core change, not language-specific.)
Parts of this change were drafted with AI coding assistants (Devin, Claude Code, with reviews by Codex and Fable) and reviewed and verified by me.
š¤ Generated with Claude Code
Summary by cubic
Lets openapi-generator read OpenAPI 3.2 documents with the released swagger-parser (2.1.48), which otherwise fails with attribute openapi is unexpected. 3.0/3.1 documents keep the existing single parse path unchanged.
- When the parser fails, the raw document is re-read with the same authorization values and timeouts and, if it declares 3.2.x, rewritten to 3.1.0 before parsing.
- 3.2-only members (
query,additionalOperations,in: querystring,$self, tagparent/kind,itemSchema) move intox-oas32-*extensions;style: cookiebecomesform, missing response descriptions are filled, and relative$refs are made absolute. - Restored operations go through the parser for defaults and
$refresolution and are exposed viaOas32ExtensionstoDefaultGenerator,DefaultCodegen,InlineModelResolver,OpenAPINormalizer,ModelUtils, andAbstractJavaCodegen; other documents enumerate exactly as before. - A root
$selfnow supplies the base URI for relative$refs and an authoredx-oas32-*extension occupying a compat slot is preserved underx-oas32-authored-<key>instead of overwritten.additionalOperationskeys colliding with a fixed method remain visible to the normalizer's FILTER but are skipped during operation generation, so no duplicate operation is emitted. - Whether a document went through the loader is tracked by object identity, so it can't leak into output or be forged.
Known limits
- 3.2-only members inside external documents aren't rewritten,
$idisn't used as a reference base, and$refs pointing into moved members can't be resolved;validate,MergedSpecBuilder, and the online service still parse directly. - Generators serialize
in: querystringparameters as regular named query parameters (warned once per load).