Commit 00e668a
authored
feat(core): add splitOperationsByContentType option to divide operati… (#23935)
* feat(core): add splitOperationsByContentType option to divide operations by content-type (#6708)
OpenAPI lets a single operation expose several request/response content-types with different schemas, but generators keep only the first one ("Multiple schemas found in the OAS 'content' section, returning only the first").
This yields a single, mistyped method and makes the other content-types unusable.
Add an opt-in option `splitOperationsByContentType` (default false) that divides such an operation into one operation per content-type — the cartesian product of the request-body and success-response content-types (deduplicated by schema).
The division happens at the spec level in DefaultCodegen#preprocessOpenAPI.
Each variant is a narrowed swagger Operation (single content-type on each axis) with a typed, collision-free operationId (request -> "With<Subtype>", response -> "As<Subtype>", e.g. createReportWithXmlAsPdf).
Variants are stored on the original operation under the "x-content-type-variants" extension and expanded by DefaultGenerator#processOperation, so every variant re-enters fromOperation and is typed natively by the target generator.
The feature is therefore language-neutral: no per-language type re-derivation, no template change.
Tested in DefaultCodegenTest for both the request×response cartesian (4 variants) and the response-only case (2 variants), and that unambiguous operations are left untouched.
* #6708 :
Move the clone methods to ModelUtils to lightweight DefaultCodegen
* #6708 :
Add new split operations option in every providers
* #6708 :
Build project and update samples
* #6708 :
Other solution without X variant and using divide directly while processing operations
* #6708
Simplification of code, removing useless "findMultiSchemaSuccessResponseCode"
* #6708 :
Remove default implemenation in Interface. No sense because the default implementation is in CodegenDefault
* #6708 : make splitOperationsByContentType a global property, merged back per generator
Following the review on #23935: the option was a CLI option repeated in every
generator that wanted it. It is now a global property, read once from
GlobalSettings in DefaultCodegen.processOpts, like skipFormModel already is.
DefaultGenerator.processOperation asks the config to divide an operation before
processing it, and DefaultCodegen implements the division once, language
neutrally: an operation whose request body and/or method response expose several
content-types with different schemas becomes one operation per (request,
response) content-type pair, each narrowed to a single media-type and given a
typed, collision-free operationId. Every variant re-enters fromOperation, so its
body and return types are resolved natively by the target generator - the shared
code re-derives no types of its own.
Each variant also carries x-content-type-variant-* extensions recording where it
sits in the matrix, so a generator able to express the whole matrix in a single
construct can merge the variants back instead of emitting one method per
combination. typescript-fetch does: the variants collapse into one method whose
request type is a union discriminated by `contentType`, and whose return type is
picked by overloads on `accept`, each branch keeping the types the split
resolved for it. A generator that does not merge simply gets the separate
methods, which is what a statically-typed language needs anyway.
With the option off, divideOperationsByContentType returns the operation as a
singleton and the merge returns immediately: regenerating the 19 typescript-fetch
sample configs produces a byte-identical tree.
* Fix problem with samples of petstore in resttemplate springboot4-jackson3
* #6708 : add a typescript-fetch sample generated with the option on
Nothing committed showed what splitOperationsByContentType emits, and nothing in
CI compiled it: the option was exercised only by unit tests asserting on strings
in a temp directory. Reviewers had to build the branch to see the feature, and a
regression that produced uncompilable TypeScript would have gone unnoticed - the
review of this branch found four of those.
The sample's spec gathers the shapes the option has to handle: a response-only
split, a split on both axes, a multipart body whose operation is split on the
response axis only, a request split mixing JSON and multipart, and an enum
parameter carried by a split operation.
bin/ts-typecheck-all.sh discovers samples on its own - any generated directory
holding both a tsconfig.json and a package.json is typechecked - so setting
npmName is all it takes for CI to compile this one. No workflow change needed.
Writing the sample immediately paid for itself: the form body assembled inside
the content-type switch was under-indented by eight columns. IndentedLambda
leaves the first line alone, and moving the partial to column zero in the
previous commit removed the literal spaces that used to indent it.
* #6708 : answer the review of the split-by-content-type sample
Three of the four points raised were about the sample, which is what the sample
is for - the output is readable now.
A media type is case-insensitive (RFC 9110), so a server answering
`Application/PDF` fell through the dispatch chain and had its body decoded as
the default content-type. Both sides of the comparison are lower-cased now.
The merged operation only advertised one media type per axis: the split narrows
each variant to a single one and the merge never put them back, so the generated
documentation hid that createReport also accepts a patch body and can answer
with a PDF. The union is restored on the merged operation. apis.mustache reads
consumes only where the request axis was not split - a case where the union is
the single value anyway - and never reads produces, so nothing but the
documentation changes.
The sample demonstrated the request axis with an `application/xml` body backed
by an object schema. typescript-fetch has no XML serialiser and JSON-encodes
that body under an XML Content-Type - with or without this option, as generating
the same spec with the option off shows. The behaviour is not this option's
doing, but advertising it in the sample promised something the generator does
not deliver, so the sample now splits on `application/merge-patch+json`, which
it does. The limitation is stated in docs/global-properties.md instead: the
option decides which content-types get an operation, not how a body is encoded.
The remaining point, body serialisation reading the pre-override header map in
runtime.ts, is upstream code this branch does not touch; it applies to every
typescript-fetch client and belongs in its own change.1 parent cc71163 commit 00e668a
53 files changed
Lines changed: 3953 additions & 126 deletions
File tree
- bin/configs
- docs
- modules/openapi-generator/src
- main
- java/org/openapitools/codegen
- languages
- resources/typescript-fetch
- test
- java/org/openapitools/codegen
- typescript/fetch
- resources/3_0
- samples/client/petstore/typescript-fetch/builds/split-by-content-type
- .openapi-generator
- docs
- src
- apis
- models
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
24 | 80 | | |
25 | 81 | | |
26 | 82 | | |
| |||
Lines changed: 19 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
130 | 131 | | |
131 | 132 | | |
132 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
133 | 152 | | |
134 | 153 | | |
135 | 154 | | |
| |||
Lines changed: 18 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
159 | 177 | | |
160 | 178 | | |
161 | 179 | | |
| |||
0 commit comments