Skip to content

Generate code for OpenAPI 3.2 query/additionalOperations/in:querystring - #24982

Draft
khayashi4337 wants to merge 31 commits into
OpenAPITools:masterfrom
khayashi4337:openapi-3.2-codegen
Draft

khayashi4337 wants to merge 31 commits into
OpenAPITools:masterfrom
khayashi4337:openapi-3.2-codegen

Conversation

@khayashi4337

@khayashi4337 khayashi4337 commented Sep 22, 2026 •

Copy link
Copy Markdown

Status: Draft — blocked on swagger-parser releasing OAS 3.2 support

This PR is not ready to merge. It builds on #24969 (already open, please
review that one first — it warns instead of silently dropping unrecognized
path-item operations like 3.2's query), and depends on parsing support
that only exists in a fork of swagger-parser
(swagger-api/swagger-parser#2402, itself a draft blocked on a swagger-core
fork). pom.xml currently points at a locally-installed swagger-parser
SNAPSHOT that isn't available to anyone else — this PR cannot build or pass
CI (the RequireReleaseDeps enforcer rule rejects it outright) until that
chain lands upstream.

I opened it as a draft anyway so the codegen work is visible and reviewable
in context. I'll drop the dependency on the forks and rebase once
swagger-core/swagger-parser cut releases with OAS 3.2 support.

What this adds (on top of #24969)

Generates code for the OAS 3.2 constructs that the swagger-parser fork now
parses into the model: the query HTTP method, additionalOperations
(arbitrary-named path-item operations), and in: querystring parameters.

Scope: the cross-language core path (so query/additionalOperations
are picked up structurally everywhere), plus nine generator/library
combinations wired up end-to-end and verified on the wire:

Generator Library
java okhttp-gson (default)
go —
python urllib3
typescript-fetch fetch
rust reqwest
ruby httpx
php guzzle
csharp generichost
kotlin jvm-okhttp4

Generators that have not opted in warn and skip 3.2 operations instead of
emitting non-compiling references to HTTP-method enum constants that don't
exist for their target language.

  • additionalOperations keys are sent as wire HTTP method names verbatim
    (a new wireHttpMethod path bypasses the existing uppercase-normalization
    that's correct for the 8 fixed methods but wrong for arbitrary ones)
  • in: querystring becomes CodegenParameter.isQueryStringParam, typed as
    a plain string via the target language's own type mapping (content-derived
    model import/setContent is skipped for it)
  • Each language gates supportsAdditionalOperations() /
    supportsQueryStringParameters() per-library with a concrete reason —
    e.g. Python: aiohttp/httpx call method.upper() and would corrupt
    customMethod into CUSTOMMETHOD; Ruby: typhoeus crashes on
    non-alphanumeric tokens and faraday rejects them; C#: restsharp's Method
    enum rejects non-standard tokens; Kotlin: only jvm-okhttp4's shadow
    request config can carry custom methods.
  • A supportsAdditionalOperations() hook (default false, mirroring the
    existing supportsQueryStringParameters() hook) makes generators that
    haven't opted in warn-and-skip rather than silently emit references to
    HTTP-method enum constants (PURGE, QUERY, ...) that don't exist for
    their target language — this was a real gap caught during review: the
    shared pipeline originally let these values reach every language's
    templates, and 15+ non-Java templates (Spring, Kotlin, TypeScript, ...)
    would have produced non-compiling output for a 3.2 spec with no warning at
    all.

Additional fixes in this branch (kept as separate commits)

  • allOf form required flags (bc20a2c12d3, 7ab9da53d49): required
    flags for form parameters are now collected only from the schema's own
    required plus its allOf chain, matched by baseName — oneOf/anyOf
    branch properties no longer leak into required, fixing a regression where
    properties+oneOf models made every form field required.
  • Generated-code identifier collisions (Kotlin, PHP): spec parameters
    named like template locals (localVariable*), API members
    (request, basePath, parseDateToQueryString), or implicit receivers
    (it, value) no longer shadow generated code — names are renamed or
    qualified while wire names stay unchanged. Includes a wire-level Vert.x
    fix where collected form parameters were never actually sent.
  • $ in wire names (Kotlin): wire names containing $ (e.g. OData
    $filter) are escaped in generated Kotlin string literals across all
    client libraries, matching jvm-okhttp's existing behavior.

Known limitations (disclosed, not blocking this draft)

  • Only the nine generator/library combinations above emit 3.2 operations;
    every other library (including the other Kotlin libraries, Java's
    non-okhttp-gson libraries, and the non-fetch TypeScript generators)
    warns and skips.
  • At least one generator (k6) builds its supporting script from its own
    independent traversal of the model rather than going through the shared,
    now-gated pipeline, so it isn't covered by the new
    supportsAdditionalOperations() guard. A handful of other generators use
    a similar direct-traversal pattern and haven't been individually audited.
    None of this is new — it follows from swagger-core's PathItem.HttpMethod
    enum gaining a QUERY value, which any code iterating that enum directly
    will now see, independent of anything in this PR.
  • MergedSpecBuilder (multi-spec merge) now includes additionalOperations
    in its merge and operation-ID conflict detection.
  • OpenApiEvaluator still assumes the fixed HttpMethod enum and doesn't
    support arbitrary additionalOperations.

Testing

Core classes: DefaultGeneratorTest (27), DefaultCodegenTest (174+7),
JavaClientCodegenTest (287), InlineModelResolverTest (62),
OpenAPINormalizerTest (71) — 628 total, 0 failures.

Per-language verification goes beyond unit tests — generated clients were
compiled and exercised against raw socket captures to prove the wire
behavior (verbatim method casing, verbatim query-string appending, form
bodies actually transmitted):

  • Kotlin package: 579 tests, 0 failures; kotlinc compile checks for
    okhttp4/vertx/ktor/multiplatform/spring generated clients, plus a live
    ServerSocket capture asserting form fields reach the wire.
  • C# (generichost): dotnet restore+run with a raw TcpListener capture of
    QUERY/custom-method requests on both net8.0 and net10.0.
  • PHP (guzzle), Ruby (httpx), Rust (reqwest), Go, Python (urllib3),
    typescript-fetch: raw TCP/socket captures of the actual HTTP request
    lines and bodies.

Sample regeneration (./bin/generate-samples.sh, 124+ configs) confirmed
clean — diffs limited to the intended template changes.


🤖 Generated with Claude Code


Summary by cubic

Generates code for OpenAPI 3.2's query HTTP method, additionalOperations map, and in: querystring parameters, wired end-to-end for Java (okhttp-gson), Go, Python (urllib3), typescript-fetch, Rust (reqwest), Ruby (httpx), PHP (guzzle), C# (generichost), and Kotlin (jvm-okhttp4). Also fixes issue #24212: with --skip-validate-spec, unrecognized path-item operations are flagged with an explicit warning naming the dropped member instead of being silently dropped.

Behavior changes

  • additionalOperations keys are sent verbatim as the wire HTTP method name; invalid RFC 9110 tokens, and case-variants some clients would normalize, are warned about and skipped.
  • in: querystring parameters become CodegenParameter.isQueryStringParam and append their value verbatim to the request path, skipping content-derived model imports.
  • A new supportsAdditionalOperations() hook (default false) makes unsupported generators warn-and-skip instead of emitting references to HTTP-method enum constants that don't exist for their target language.
  • When the parser produces no OpenAPI object, generation fails immediately with the parser's diagnostics instead of a generic downstream error.

Additional fixes

  • Parameters and properties colliding with generated template locals or API members are renamed (wire names preserved) across Kotlin and PHP so generated clients compile and send the intended values.
  • Form-parameter required flags now match the schema property name, so allOf-inherited required fields are no longer dropped and oneOf/anyOf branches no longer force fields required.

Generators not opted in (e.g. Spring, aiohttp/httpx Python libraries, hyper-based Rust libraries) skip 3.2 operations with a warning. Anything iterating PathItem.HttpMethod directly (the k6 supporting script) now sees the new QUERY value regardless, and OpenApiEvaluator still assumes the fixed HttpMethod enum.

Written for commit a274eae. Summary will update on new commits.

Review in cubic

khayashi4337 and others added 7 commits September 20, 2026 17:46
…perations (OpenAPITools#24212)

Under --skip-validate-spec, when the parser encounters a path-item member
it doesn't recognize (e.g. a future operation like OpenAPI 3.2's 'query'
HTTP method), it silently drops it and generation reports success with
no indication that an operation is missing.

- Detect swagger-parser's "attribute paths.'X'.Y is unexpected" messages
  and escalate matching ones into an explicit WARN naming exactly which
  path-item members will be missing from the generated output.
- When the parser cannot produce an OpenAPI object at all (e.g. an
  unsupported spec version), fail immediately with a message that
  includes the parser's own diagnostics, instead of letting null
  propagate through several layers before a generic error surfaces
  later in DefaultGenerator.generate().

Closes OpenAPITools#24212
…erystring

- ingest PathItem.query and PathItem.additionalOperations through the same
  core paths as the fixed methods (operation collection, preprocess,
  callback discovery, inline model resolution, filter marking, PathItem
  serialization); additionalOperations keys are sent verbatim as the HTTP
  method name
- represent `in: querystring` parameters as a plain string via the new
  CodegenParameter.isQueryStringParam flag; generators without dedicated
  support are warned once
- okhttp-gson: serialize querystring params as the whole already-encoded
  query string appended to the path, register query/additional operations
  in dynamicOperations lookup (only when the spec uses them, so generated
  clients still compile against the pinned swagger-parser), and accept
  querystring in fillParametersFromOperation
- bump swagger-parser to 2.1.49-SNAPSHOT for OpenAPI 3.2 model classes
Generators whose templates embed enumerated HTTP method constants
(e.g. HttpMethod.QUERY) produced uncompilable code for OpenAPI 3.2
query/additionalOperations. Introduce supportsAdditionalOperations()
(default false; only okhttp-gson opts in) so unsupported generators
warn and skip instead.

- CodegenConfig: supportsAdditionalOperations() capability hook
- DefaultGenerator/DefaultCodegen/InlineModelResolver: skip 3.2
  query/additionalOperations with a warning when unsupported
- JavaClientCodegen: enable the capability for okhttp-gson only
- fromCallback: keep additionalOperations method names verbatim
  ("customMethod" no longer becomes CUSTOMMETHOD) and warn on skip
- MergedSpecBuilder: merge additionalOperations and cover them in
  operationId conflict detection instead of dropping them
- ApiClient.mustache: put section tags on their own lines so
  generated indentation is stable in both branches
- regenerate okhttp-gson-dynamicOperations sample
…ystring

Opt the Go client generator into the 3.2 capability gates and emit
operations that actually compile and behave correctly on the wire:

- supportsAdditionalOperations()/supportsQueryStringParameters() return
  true for the go client generator only
- Non-standard HTTP methods are snapshotted verbatim before
  AbstractGoCodegen camelizes httpMethod (which would corrupt
  "customMethod" into "Custommethod"), then restored and flagged with
  x-go-http-method-literal. IdentityHashMap is required because
  CodegenOperation.hashCode() includes the mutating httpMethod field.
  The same snapshot/restore is applied to webhooks via
  postProcessWebhooksWithModels, which renders through api.mustache too.
- api.mustache emits standard methods as http.MethodXxx constants and
  non-standard methods as unescaped string literals so valid HTTP token
  punctuation (e.g. CHECK&FETCH) survives intact
- in:querystring parameters append their raw, already-encoded value to
  the request path instead of name=value serialization
- client.mustache captures url.RawQuery before url.Query() merges it
  into the normal parameter map, then re-appends it verbatim. Note:
  for requests whose path already embeds a query string, the embedded
  pairs now keep their verbatim form/order instead of being merged,
  decoded and re-sorted by Encode(); the only producer of such paths is
  the 3.2 querystring parameter (it cannot coexist with in:query per
  spec validation)
- "strings" import is added when a querystring parameter exists and
  deduplicated against the path-parameter import

Verified: go build on generated client, httptest request-capture tests
(QUERY method, verbatim customMethod and CHECK&FETCH casing, verbatim
raw query, ordinary GET regression, pathParam+querystring on one op),
go-gin-server warns and omits unsupported 3.2 operations.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Python (urllib3 library only):
- flag non-standard HTTP methods and querystring params after
  postProcessOperationsWithModels/WebhooksWithModels
- emit non-standard methods via unescaped literals; append in: querystring
  values verbatim to the request path
- bypass urllib3's request() for non-standard methods (it unconditionally
  uppercases); dispatch via request_encode_url/request_encode_body
- fix double '?' when querystring path combines with serialized query
  params (e.g. apiKey-in-query auth)
- validate additionalOperations keys as RFC 9110 tokens at codegen time;
  warn and skip invalid ones
- asyncio(aiohttp)/httpx stay unsupported (they uppercase internally) and
  warn+skip 3.2 operations

Go: same RFC 9110 token validation for verbatim method literals.

Generated clients verified by request capture: QUERY/customMethod/
CHECK&FETCH sent verbatim, querystring preserved as written, standard
GET unchanged.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ns and in:querystring

- enable supportsAdditionalOperations/supportsQueryStringParameters:
  fetch() passes RequestInit.method verbatim, preserving arbitrary
  method names
- emit non-standard methods as double-quoted unescaped literals
  (apostrophes and backticks are valid tchars); warn+skip keys that
  are not RFC 9110 tokens, warn when fetch forbids the method
- widen HTTPMethod with (string & {}) so custom methods typecheck
  while keeping autocomplete for the standard set
- append in:querystring values verbatim to urlPath ('&' when a '?'
  is already present); match that in createFetchParams so
  apiKey-in-query auth params chain with '&' instead of a second '?'
- escape '|' in generated markdown docs via x-ts-http-method-doc
- fix ExtendedCodegenParameter's manual field copy silently dropping
  ~30 CodegenParameter fields (isQueryStringParam among them); copy
  every field/accessor now
- webhooks get the same flags via postProcessWebhooksWithModels

Verified by tsc strict compile and a raw-TCP capture test: QUERY,
customMethod and CHECK&FETCH reach the wire verbatim, the querystring
value is preserved as written, standard GET unchanged. typescript-axios
and other siblings warn and skip 3.2 operations as before.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…erystring

Enable OpenAPI 3.2 constructs for the rust client generator, reqwest
library only (hyper/hyper0x/reqwest-trait keep warning and skipping
unsupported operations):

- supportsAdditionalOperations()/supportsQueryStringParameters() return
  true for the reqwest library
- Non-standard HTTP methods (query op, additionalOperations) are
  snapshotted before the per-library method-case conversion and emitted
  verbatim via reqwest::Method::from_bytes(b"..."), preserving case
  (e.g. customMethod) and RFC 9110 tchar punctuation (e.g. CHECK&FETCH)
- Invalid RFC 9110 method tokens are warned about and skipped instead of
  producing uncompilable Rust
- `in: querystring` parameters append the caller-supplied query
  component verbatim to the URI (with ? or & as needed), bypassing
  reqwest's name=value .query() serialization; excluded from the
  queryParams template loop
- Webhook operations get the same verbatim-method and querystring
  handling via postProcessWebhooksWithModels
- Doc templates escape | in method names so markdown tables stay intact
- New TestNG coverage includes a committed wire-level test: the
  generated blocking client is built with cargo and its request lines
  are captured on a raw TCP listener, verifying QUERY, customMethod,
  CHECK&FETCH, PURGE and a verbatim, non-double-encoded querystring
  (skipped when cargo or crates.io is unavailable)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wing328
wing328 marked this pull request as ready for review September 22, 2026 14:21
@wing328
wing328 marked this pull request as draft September 22, 2026 14:21

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27 issues found across 98 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java:255">
P3: This loop still discovers callbacks from operations that `addOperationEntries` intentionally skips for unsupported generators. Filter the callback-discovery source with the same capability check so skipped `query`/`additionalOperations` cannot add orphan callback models.</violation>
</file>

<file name="samples/client/petstore/typescript-fetch/builds/with-interfaces/runtime.ts">

<violation number="1" location="samples/client/petstore/typescript-fetch/builds/with-interfaces/runtime.ts:148">
P3: `url.includes('?')` treats any '?' anywhere in the URL (e.g., inside a fragment) as an existing query, so `context.query` gets appended after the fragment with '&' instead of into the query position. Check only whether '?' appears before any '#': `const qi = url.indexOf('?'); const hi = url.indexOf('#'); const hasQuery = qi !== -1 && (hi === -1 || qi < hi);`. Apply the same fix to the template (runtime.mustache) and regenerate.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java:613">
P3: This test asserts only that the `rawQueryString := url.RawQuery` line exists, so it does not verify the behavior its name describes (“keeps a path-embedded raw query string verbatim”) — deleting the re-append logic in client.mustache would still pass. Also assert the conditional append, e.g. `encodedQuery += rawQueryString`, so the preservation path is actually covered.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java:1300">
P3: The RETRY (additionalOperation inside the callback path item) assertion checks only that the request-body schema became a `#/components/schemas/` $ref, but never resolves the referenced component or verifies its content. Mirror the first block: resolve via ModelUtils.getSimpleRef, assert the component is present, and assert the `retryId` property is a StringSchema, so a mis-registered or content-less extraction fails the test.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/python/rest.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/python/rest.mustache:220">
P1: This uppercases valid lower/mixed-case `additionalOperations` methods such as `get`, contrary to the verbatim wire-method contract. Preserve only the already-uppercase fixed methods and dispatch non-standard methods without applying `upper()` in either check.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java:1435">
P1: This enables 3.2 webhook operations, but dynamic webhook methods cannot resolve them because the okhttp-gson lookup map contains paths only. With `dynamicOperations=true`, those webhook calls throw `ApiException("Operation not found in OAS")`; register webhook operations in the lookup map or guard this support for dynamic webhook generation.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java:901">
P1: `supportsAdditionalOperations()` emits `CONNECT` and `TRACE` even though fetch rejects them, and classifying them as standard makes the forbidden-method warning unreachable. Filter or reject these methods before generation instead of advertising support for operations that always fail at runtime.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java:5195">
P2: Unsupported generators still generate `in: querystring` parameters through their ordinary query serialization path. Gate these parameters for generators without `supportsQueryStringParameters()` support, or provide the corresponding whole-query-string serialization in every template that receives them; otherwise non-okhttp Java clients can generate incorrect or uncompilable code.</violation>
</file>

<file name="modules/openapi-generator/src/test/resources/3_2/go-webhook-operations.yaml">

<violation number="1" location="modules/openapi-generator/src/test/resources/3_2/go-webhook-operations.yaml:1">
P3: This fixture is shared by the Go, Python, and Rust webhook operation tests, and its content is pure spec YAML with no Go specifics. The `go-` prefix misleads maintenance for the Python/Rust paths; rename to something neutral like `webhook-operations.yaml` (and update the three references).</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java:483">
P3: These assertions depend on the iteration order of `PathItem.getAdditionalOperations()`, which is a plain `Map` — its insertion order is not guaranteed by the swagger-core fork, and `DefaultGenerator` iterates it with `forEach` in that order. If the map is a `HashMap`, the `get(1)`/`get(2)` assertions for "PURGE" vs "customMethod" rest on hash-ordered iteration. Make the assertions order-independent, e.g., look up each operation by `operationId` and assert its `httpMethod`.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/config/CodegenConfiguratorTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/config/CodegenConfiguratorTest.java:165">
P2: These tests capture log events from the shared static CodegenConfigurator.LOGGER, but surefire runs test classes in parallel (pom.xml: <parallel>classes</parallel>, threadCountClasses=3). Any concurrently running test that triggers the new "MISSING" WARN in CodegenConfigurator.toContext will land in this test's ListAppender. Tests 2 and 3 filter only on the "MISSING" substring (no level or thread check), so a parallel class parsing an unrecognized path-item attribute with setValidateSpec(false) can fail them spuriously. Restrict the filters to the current thread's WARN events, or better, scope assertions to events whose message also pins the spec/path (as test 1 already does).</violation>
</file>

<file name="pom.xml">

<violation number="1" location="pom.xml:1290">
P1: This pins the core build to an unavailable fork snapshot, so clean CI and consumer builds fail during dependency resolution before generation tests run. Keep the released parser version until the required upstream artifact is published, or declare the repository/artifact source that supplies this snapshot.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java:747">
P1: `existing.addAdditionalOperation` merges custom operations without the source spec's root-level security. Extend `propagateRootSecurityToOperations` to cover `additionalOperations` before merging, or these generated methods lose their authentication requirements.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java:846">
P3: This documentation escaping handles only `|`, but valid additional-operation methods may contain Markdown delimiter characters such as `*`, `_`, or `` ` ``. Escape those characters before placing the value inside the templates’ `**...**` markup so generated Rust documentation remains readable.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/python/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/python/api.mustache:445">
P2: This branch does not preserve lower- or mixed-case additional-operation names that resemble fixed methods: the generated urllib3 transport uppercases `get` to `GET`. Limit that transport normalization to fixed operations, while dispatching additional-operation values verbatim.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java:68">
P2: This matcher silently misses valid custom method names containing `.`. The parser will drop an operation such as `M.FOO`, but generation emits no `MISSING` warning; allow dots in the direct attribute token while retaining the nested-member delimiters.</violation>
</file>

<file name="samples/openapi3/client/petstore/python/petstore_api/rest.py">

<violation number="1" location="samples/openapi3/client/petstore/python/petstore_api/rest.py:224">
P2: `request` case-folds arbitrary wire methods before dispatch. A valid `additionalOperations` key such as `Get` is emitted verbatim by the generator but reaches the server as `GET`; classify fixed operations without case-folding custom names.</violation>

<violation number="2" location="samples/openapi3/client/petstore/python/petstore_api/rest.py:236">
P2: This token validation disappears under Python’s optimized mode. Raise `ApiValueError` (or use an ordinary conditional) so invalid method names remain rejected in optimized deployments.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache:1918">
P1: Dynamic clients generated for 3.2 specs will not compile because this branch calls parser methods absent from the generated client's pinned `swagger-parser-v3:2.0.30`. Update the generated build dependencies to a parser release containing the OpenAPI 3.2 `PathItem` methods before emitting these calls.</violation>
</file>

<file name="samples/openapi3/client/petstore/python-lazyImports/petstore_api/rest.py">

<violation number="1" location="samples/openapi3/client/petstore/python-lazyImports/petstore_api/rest.py:239">
P2: Do not use `assert` for this runtime validation. Running the generated client with `python -O` removes the check and allows malformed method names to reach the transport; raise `ApiValueError` when the token regex does not match.</violation>
</file>

<file name="samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/rest.py">

<violation number="1" location="samples/client/others/python-legacy-model-dictionaries/legacy_model_dict_client/rest.py:224">
P2: This still case-folds arbitrary additional-operation methods that happen to match a standard verb case-insensitively. A key such as `get` is a valid HTTP token but is sent as `GET`, violating the verbatim-method contract; preserve exact casing and only normalize fixed operations before reaching this client.</violation>
</file>

<file name="modules/openapi-generator/src/test/resources/3_2/rust-invalid-method.yaml">

<violation number="1" location="modules/openapi-generator/src/test/resources/3_2/rust-invalid-method.yaml:13">
P3: This fixture also documents a promised WARN ("skipped with a warning"), but the only test consuming it (RustClientCodegenTest.testReqwestSkipsInvalidMethodNames) asserts just that the operation is dropped, so the skip could silently lose its warning without failing. Capture the logger output and assert the "not a valid RFC 9110 token" warning is emitted for the "MY METHOD" operation.</violation>
</file>

<file name="samples/client/echo_api/python/openapi_client/rest.py">

<violation number="1" location="samples/client/echo_api/python/openapi_client/rest.py:263">
P2: This condition now sends request bodies and form data for `GET` and `HEAD` as well as custom methods. Restrict the new body/form check to non-GET/HEAD methods so existing GET/HEAD behavior is not changed unintentionally.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java:5281">
P3: These new generation tests only assert substrings and never compile the output, unlike every other generation test in this file (e.g., the MicroProfile test above calls validateJavaSourceFiles(files)). The PR's core risk is generated-code validity for a brand-new parameter kind (in: querystring) and verbatim HTTP method names; a regression that produces non-compiling DefaultApi.java/ApiClient.java would pass these tests. The non-dynamic test (no pathItem.getQuery()/getAdditionalOperations() references) and the negative dynamic test can compile against the pinned parser, so capture `List<File> files = ...generate();` and call validateJavaSourceFiles(files). Skipping validation is only unavoidable for the positive dynamic test, whose output intentionally references parser methods the pinned release lacks.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java:492">
P2: This end-to-end test runs a full `cargo build` + `cargo run` with network dependency resolution in the default unit-test suite whenever cargo is on PATH and index.crates.io:443 is reachable. On any dev machine or CI image where those hold, every `mvn test` of this module now spends minutes compiling and can flake on crates.io/network hiccups. Gate it behind an opt-in system property (e.g. `-Drust.e2e=true`) and skip otherwise, so the default suite stays fast and offline-deterministic.</violation>

<violation number="2" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java:562">
P2: `runCargo` blocks in `p.waitFor(15, TimeUnit.MINUTES)` before draining the child's output. Because `redirectErrorStream(true)` merges all output into one pipe, a cargo download/compile log larger than the ~64 KB OS pipe buffer makes the child block on write, so `waitFor` never returns until the forced destroy. Drain `getInputStream()` concurrently while waiting (or redirect output to a file) so large builds cannot stall the test for the full timeout.</violation>
</file>

<file name="samples/openapi3/client/petstore/go-petstore-withXml/client.go">

<violation number="1" location="samples/openapi3/client/petstore/go-petstore-withXml/client.go:398">
P2: This reverses the existing ordering between query data embedded in `path` and regular query parameters. Duplicate query keys can therefore reach the server in a different order, affecting order-sensitive APIs and request signing; prepend `rawQueryString` instead of appending it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

# the standard set; anything else is validated as an HTTP token
# (RFC 9110 tchar) and sent verbatim so casing like 'customMethod'
# survives
_upper_method = method.upper()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This uppercases valid lower/mixed-case additionalOperations methods such as get, contrary to the verbatim wire-method contract. Preserve only the already-uppercase fixed methods and dispatch non-standard methods without applying upper() in either check.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/python/rest.mustache, line 220:

<comment>This uppercases valid lower/mixed-case `additionalOperations` methods such as `get`, contrary to the verbatim wire-method contract. Preserve only the already-uppercase fixed methods and dispatch non-standard methods without applying `upper()` in either check.</comment>

<file context>
@@ -195,16 +212,24 @@ class RESTClientObject:
+        # the standard set; anything else is validated as an HTTP token
+        # (RFC 9110 tchar) and sent verbatim so casing like 'customMethod'
+        # survives
+        _upper_method = method.upper()
+        if _upper_method in [
             'GET',
</file context>

protected boolean supportsQueryStringParameters() {
// only the okhttp-gson api.mustache serializes an `in: querystring`
// parameter as the whole (already-encoded) query string
return isLibrary(OKHTTP_GSON) || StringUtils.isBlank(getLibrary());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This enables 3.2 webhook operations, but dynamic webhook methods cannot resolve them because the okhttp-gson lookup map contains paths only. With dynamicOperations=true, those webhook calls throw ApiException("Operation not found in OAS"); register webhook operations in the lookup map or guard this support for dynamic webhook generation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java, line 1435:

<comment>This enables 3.2 webhook operations, but dynamic webhook methods cannot resolve them because the okhttp-gson lookup map contains paths only. With `dynamicOperations=true`, those webhook calls throw `ApiException("Operation not found in OAS")`; register webhook operations in the lookup map or guard this support for dynamic webhook generation.</comment>

<file context>
@@ -1426,4 +1427,31 @@ protected void applyJspecify() {
+    protected boolean supportsQueryStringParameters() {
+        // only the okhttp-gson api.mustache serializes an `in: querystring`
+        // parameter as the whole (already-encoded) query string
+        return isLibrary(OKHTTP_GSON) || StringUtils.isBlank(getLibrary());
+    }
+
</file context>

public boolean supportsAdditionalOperations() {
// fetch() passes RequestInit.method through verbatim, preserving
// arbitrary OpenAPI 3.2 method names
return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: supportsAdditionalOperations() emits CONNECT and TRACE even though fetch rejects them, and classifying them as standard makes the forbidden-method warning unreachable. Filter or reject these methods before generation instead of advertising support for operations that always fail at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java, line 901:

<comment>`supportsAdditionalOperations()` emits `CONNECT` and `TRACE` even though fetch rejects them, and classifying them as standard makes the forbidden-method warning unreachable. Filter or reject these methods before generation instead of advertising support for operations that always fail at runtime.</comment>

<file context>
@@ -871,10 +873,85 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L
+    public boolean supportsAdditionalOperations() {
+        // fetch() passes RequestInit.method through verbatim, preserving
+        // arbitrary OpenAPI 3.2 method names
+        return true;
+    }
+
</file context>

Comment thread pom.xml
<spotbugs-plugin.version>3.1.12.2</spotbugs-plugin.version>
<swagger-parser-groupid.version>io.swagger.parser.v3</swagger-parser-groupid.version>
<swagger-parser.version>2.1.47</swagger-parser.version>
<swagger-parser.version>2.1.49-SNAPSHOT</swagger-parser.version>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This pins the core build to an unavailable fork snapshot, so clean CI and consumer builds fail during dependency resolution before generation tests run. Keep the released parser version until the required upstream artifact is published, or declare the repository/artifact source that supplies this snapshot.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pom.xml, line 1290:

<comment>This pins the core build to an unavailable fork snapshot, so clean CI and consumer builds fail during dependency resolution before generation tests run. Keep the released parser version until the required upstream artifact is published, or declare the repository/artifact source that supplies this snapshot.</comment>

<file context>
@@ -1287,7 +1287,7 @@
         <spotbugs-plugin.version>3.1.12.2</spotbugs-plugin.version>
         <swagger-parser-groupid.version>io.swagger.parser.v3</swagger-parser-groupid.version>
-        <swagger-parser.version>2.1.47</swagger-parser.version>
+        <swagger-parser.version>2.1.49-SNAPSHOT</swagger-parser.version>
         <testng.version>7.10.2</testng.version>
         <violations-maven-plugin.version>1.34</violations-maven-plugin.version>
</file context>
Suggested change
<swagger-parser.version>2.1.49-SNAPSHOT</swagger-parser.version>
<swagger-parser.version>2.1.47</swagger-parser.version>

// WARN: keep the first (existing) operation, skip the incoming one.
return;
}
existing.addAdditionalOperation(method, operation);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: existing.addAdditionalOperation merges custom operations without the source spec's root-level security. Extend propagateRootSecurityToOperations to cover additionalOperations before merging, or these generated methods lose their authentication requirements.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java, line 747:

<comment>`existing.addAdditionalOperation` merges custom operations without the source spec's root-level security. Extend `propagateRootSecurityToOperations` to cover `additionalOperations` before merging, or these generated methods lose their authentication requirements.</comment>

<file context>
@@ -722,6 +727,27 @@ private void mergePathItem(PathItem existing, PathItem incoming, String pathKey)
+                    // WARN: keep the first (existing) operation, skip the incoming one.
+                    return;
+                }
+                existing.addAdditionalOperation(method, operation);
+            });
+        }
</file context>

Assert.assertEquals(defaultList.size(), 3);
Assert.assertEquals(defaultList.get(0).operationId, "queryPets");
Assert.assertEquals(defaultList.get(0).httpMethod, "QUERY");
Assert.assertEquals(defaultList.get(1).operationId, "purgePets");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: These assertions depend on the iteration order of PathItem.getAdditionalOperations(), which is a plain Map — its insertion order is not guaranteed by the swagger-core fork, and DefaultGenerator iterates it with forEach in that order. If the map is a HashMap, the get(1)/get(2) assertions for "PURGE" vs "customMethod" rest on hash-ordered iteration. Make the assertions order-independent, e.g., look up each operation by operationId and assert its httpMethod.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java, line 483:

<comment>These assertions depend on the iteration order of `PathItem.getAdditionalOperations()`, which is a plain `Map` — its insertion order is not guaranteed by the swagger-core fork, and `DefaultGenerator` iterates it with `forEach` in that order. If the map is a `HashMap`, the `get(1)`/`get(2)` assertions for "PURGE" vs "customMethod" rest on hash-ordered iteration. Make the assertions order-independent, e.g., look up each operation by `operationId` and assert its `httpMethod`.</comment>

<file context>
@@ -448,6 +448,102 @@ public void testProcessPaths() throws Exception {
+        Assert.assertEquals(defaultList.size(), 3);
+        Assert.assertEquals(defaultList.get(0).operationId, "queryPets");
+        Assert.assertEquals(defaultList.get(0).httpMethod, "QUERY");
+        Assert.assertEquals(defaultList.get(1).operationId, "purgePets");
+        Assert.assertEquals(defaultList.get(1).httpMethod, "PURGE");
+        // additionalOperations keys are HTTP method names and must be sent verbatim
</file context>

operation.httpMethod = method;
operation.vendorExtensions.put("x-rust-http-method-literal", true);
// `|` is a valid RFC 9110 tchar but breaks markdown tables in doc templates
operation.vendorExtensions.put("x-rust-http-method-doc", method.replace("|", "\\|"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This documentation escaping handles only |, but valid additional-operation methods may contain Markdown delimiter characters such as *, _, or `. Escape those characters before placing the value inside the templates’ **...** markup so generated Rust documentation remains readable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java, line 846:

<comment>This documentation escaping handles only `|`, but valid additional-operation methods may contain Markdown delimiter characters such as `*`, `_`, or `` ` ``. Escape those characters before placing the value inside the templates’ `**...**` markup so generated Rust documentation remains readable.</comment>

<file context>
@@ -780,11 +781,98 @@ public void postProcessParameter(CodegenParameter parameter) {
+            operation.httpMethod = method;
+            operation.vendorExtensions.put("x-rust-http-method-literal", true);
+            // `|` is a valid RFC 9110 tchar but breaks markdown tables in doc templates
+            operation.vendorExtensions.put("x-rust-http-method-doc", method.replace("|", "\\|"));
+        }
+    }
</file context>
Suggested change
operation.vendorExtensions.put("x-rust-http-method-doc", method.replace("|", "\\|"));
operation.vendorExtensions.put("x-rust-http-method-doc", method
.replace("*", "\\*")
.replace("_", "\\_")
.replace("`", "\\`")
.replace("|", "\\|"));

'200':
description: ok
additionalOperations:
# contains a space - not a valid RFC 9110 token; must be skipped with a warning

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This fixture also documents a promised WARN ("skipped with a warning"), but the only test consuming it (RustClientCodegenTest.testReqwestSkipsInvalidMethodNames) asserts just that the operation is dropped, so the skip could silently lose its warning without failing. Capture the logger output and assert the "not a valid RFC 9110 token" warning is emitted for the "MY METHOD" operation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/resources/3_2/rust-invalid-method.yaml, line 13:

<comment>This fixture also documents a promised WARN ("skipped with a warning"), but the only test consuming it (RustClientCodegenTest.testReqwestSkipsInvalidMethodNames) asserts just that the operation is dropped, so the skip could silently lose its warning without failing. Capture the logger output and assert the "not a valid RFC 9110 token" warning is emitted for the "MY METHOD" operation.</comment>

<file context>
@@ -0,0 +1,18 @@
+        '200':
+          description: ok
+    additionalOperations:
+      # contains a space - not a valid RFC 9110 token; must be skipped with a warning
+      "MY METHOD":
+        operationId: badMethod
</file context>

.contains("purgePetsCall(")
.contains("\"QUERY\"")
.contains("\"PURGE\"")
.contains("localVarPath = localVarPath + (localVarPath.contains(\"?\") ? \"&\" : \"?\") + qs;");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: These new generation tests only assert substrings and never compile the output, unlike every other generation test in this file (e.g., the MicroProfile test above calls validateJavaSourceFiles(files)). The PR's core risk is generated-code validity for a brand-new parameter kind (in: querystring) and verbatim HTTP method names; a regression that produces non-compiling DefaultApi.java/ApiClient.java would pass these tests. The non-dynamic test (no pathItem.getQuery()/getAdditionalOperations() references) and the negative dynamic test can compile against the pinned parser, so capture List<File> files = ...generate(); and call validateJavaSourceFiles(files). Skipping validation is only unavoidable for the positive dynamic test, whose output intentionally references parser methods the pinned release lacks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java, line 5281:

<comment>These new generation tests only assert substrings and never compile the output, unlike every other generation test in this file (e.g., the MicroProfile test above calls validateJavaSourceFiles(files)). The PR's core risk is generated-code validity for a brand-new parameter kind (in: querystring) and verbatim HTTP method names; a regression that produces non-compiling DefaultApi.java/ApiClient.java would pass these tests. The non-dynamic test (no pathItem.getQuery()/getAdditionalOperations() references) and the negative dynamic test can compile against the pinned parser, so capture `List<File> files = ...generate();` and call validateJavaSourceFiles(files). Skipping validation is only unavoidable for the positive dynamic test, whose output intentionally references parser methods the pinned release lacks.</comment>

<file context>
@@ -5259,6 +5259,69 @@ public void testInsecureTlsHookOmittedWhenDisabled(String library) {
+                .contains("purgePetsCall(")
+                .contains("\"QUERY\"")
+                .contains("\"PURGE\"")
+                .contains("localVarPath = localVarPath + (localVarPath.contains(\"?\") ? \"&\" : \"?\") + qs;");
+    }
+
</file context>

khayashi4337 and others added 20 commits September 23, 2026 00:48
…erystring (httpx)

Scope: httpx library only - raw TCP probes showed typhoeus up-cases custom
verbs and crashes on non-alphanumeric tokens, and faraday rejects them, so
both keep warning/skipping 3.2 operations.

- arbitrary RFC 9110 method tokens are emitted as quoted Ruby symbols and
  reach the wire verbatim; HTTPX::Request internally stores
  @verb = verb.to_s.upcase, so non-standard verbs build the request via
  session.build_request and then restore @verb (HTTPX-internal dependency
  documented in the generated code)
- invalid RFC 9110 tokens are warned about and skipped at codegen time
- '#' is escaped inside the :"..." literal to avoid Ruby interpolation,
  '|' is escaped in generated markdown docs
- in:querystring parameters are appended verbatim with automatic ?/&
  delimiter and excluded from the regular query_params hash
- adds an executable raw TCP capture test (6 request lines verified) and
  unit tests for invalid-token skip, typhoeus skipping, and webhooks;
  samples regenerated (symbol quoting change is functionally equivalent)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
File.deleteOnExit() cannot remove non-empty directories, so generated
output trees (and cargo target dirs) accumulated in /tmp. GoClientCodegenTest
now collects temp dirs and deletes them recursively in @afterclass.

isCommandAvailable() drained the child's stdout before waitFor(), which
blocked forever when the spawned command waited on stdin (e.g. bare `ruby`
with no arguments) - the timeout was never reached. Wait first, drain after.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…rystring (guzzle)

Scope: guzzle library only - psr-18 delegates method handling to the
injected PSR-17/PSR-18 implementation (Guzzle's factory up-cases, Symfony
rejects non-uppercase tokens), and php-nextgen/php-dt are out of scope, so
those keep warning/skipping 3.2 operations.

- non-standard RFC 9110 methods build the request through an anonymous
  Request subclass that keeps the verbatim token: guzzlehttp/psr7
  upper-cases in the constructor/withMethod, but Guzzle handlers only
  ever read RequestInterface::getMethod()
- invalid RFC 9110 tokens are warned about and skipped at codegen time
- "'" is escaped for PHP single-quoted literals, "|" for markdown docs
- in:querystring parameters append verbatim with automatic ?/& delimiter
  and are excluded from the regular query params
- adds an executable raw TCP capture test (composer install + php run,
  6 request lines verified) and unit tests for psr-18 skipping, invalid
  tokens, and webhooks; all 15 php sample configs regenerate byte-identical

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…querystring (generichost)

Non-standard RFC 9110 method tokens are emitted as new HttpMethod("token")
instead of HttpMethod.Xxx, which does not exist for e.g. QUERY and whose
Normalize() would fold casing onto a standard method. Tokens that are not
valid tchar, or case-insensitively match a standard method (cannot be
preserved verbatim through HttpMethod), are warned about and skipped.
`in: querystring` parameters append verbatim to the request query after
UriBuilder and are excluded from ParseQueryString serialization.
AbstractCSharpCodegen.getOperationInputModels also scans
pathItem.getAdditionalOperations() so generichost webhooks get the same
treatment. restsharp/httpclient/unityWebRequest remain unsupported.

Adds a committed end-to-end test that builds the generated generichost
client and verifies six request lines via raw TcpListener capture;
dotnet restore failure or missing SDK skips, build/run failures fail.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…querystring (jvm-okhttp4)

The shared RequestMethod enum is untouched (adding QUERY would break
exhaustive when expressions in ktor/multiplatform templates). Instead the
jvm-okhttp-shadowed RequestConfig carries customMethod/encodedQueryString:
non-standard RFC 9110 tokens are emitted verbatim through okhttp's
Request.Builder.method(String, ...) which preserves casing, and QUERY
always gets a possibly-empty body since OkHttp 5 rejects bodyless QUERY.

in:querystring params are excluded from the name=value query map and
appended verbatim via encodedQuery. '$' is escaped for Kotlin string
literals, '|' for markdown tables; non-tchar tokens warn+skip. Infra
changes are gated on bundle flags so non-3.2 output is byte-identical;
the other 7 kotlin libraries warn+skip.

The committed test compiles the generated client with a PATH-detected
kotlinc and captures raw request lines over a ServerSocket.
…te verbatim path to httpx

- build_request now builds a body for non-standard methods too, so
  QUERY/additionalOperations with request bodies no longer drop them
- build_request_url normalizes slashes on the path component only, so
  a querystring value containing '//' survives verbatim
- isQueryStringParam is cleared for libraries other than httpx, so
  faraday/typhoeus fall back to normal name=value serialization instead
  of silently losing the parameter (fixed on both allParams and
  queryParams, which are independent copies)
- fix debug logging in the httpx partial referencing undefined req_body
  (body_params is the local), which crashed debugging on 3.2 verbs
- expand the shared 3.2 fixture with body-bearing QUERY, REPORT,
  PROPPATCH, a querystring param named 'uri', and a find op for
  unsupported-library fallback checks; extend the httpx wire capture
  to 9 cases incl. bodies and '//' preservation

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…2 ops

- bump guzzle psr-7 constraint to ^2.10: 1.x could reconstruct the
  request and lose the verbatim-method anonymous subclass. This is a
  compatibility break for consumers pinned to psr-7 1.x
- rename the internal \$uri variable to \$__requestUri so an
  'in: querystring' parameter literally named 'uri' no longer collides
- extend the guzzle wire capture for the uri-named param and
  body-bearing QUERY/REPORT/PROPPATCH cases

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…d case-variants

- getOperationInputModels dropped its redundant additionalOperations
  traversal (PathItem.readOperations() already includes them) and now
  also traverses openAPI.getWebhooks(), so models referenced only by
  top-level webhooks get public ctors instead of internal
- verified on real SDKs: net8 has no HttpMethod.Query and sends 'qUeRy'
  verbatim, net10 exposes HttpMethod.Query and normalizes 'qUeRy' to
  QUERY on the wire. Since generichost multi-targets, case-variants of
  normalized methods now warn+skip; uppercase QUERY stays verbatim via
  the literal path on both TFMs
- wire capture extended to read request bodies and cover body-bearing
  QUERY/REPORT/PROPPATCH and the verbatim querystring-with-body case

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…or 3.2 verbs

- toParamName now renames params named localVariableBody/
  localVariableQuery/localVariableHeaders to param* (same pattern as the
  existing callback -> paramCallback escape), fixing compile-breaking
  collisions in generated api functions across all kotlin libraries
- jvm-okhttp deepObject serialization now prefixes wire keys with the
  spec baseName (via x-kotlin-param-base-name) instead of paramName, so
  the rename no longer leaks into query keys
- body-required custom methods handled as OkHttp 5's requiresRequestBody
  set (QUERY/REPORT/PROPPATCH) instead of QUERY alone, so REPORT and
  PROPPATCH no longer throw 'must have a request body'
- fixture gains a /collide op with the three colliding query-param names
  plus a kotlin-only deepObject collision spec (shared fixture stays
  scalar-only: rust reqwest cannot serialize deepObject params)
- wire capture extended to read bodies and cover REPORT/PROPPATCH, a
  body-bearing QUERY, and the collision op

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
A spec parameter named e.g. `query`, `headers`, `multipart`, `operationHost`,
`hostIndex`, `returnType` or `options` landed in the same scope as the internal
locals the generated api functions declare, silently dropping the caller's
value (or producing duplicate declarations / array-to-string errors). Renaming
only the `$uri` accumulator was a symptom-level patch.

toParamName now compares the normalized parameter name against the full set of
function-scope internal names in php/api.mustache (signature internals,
xxxRequest locals, sync/async wrapper locals, $__requestUri, and the illegal
`$this`) and prefixes colliding parameters with `param_`, keeping the wire
baseName unchanged.

Verified with a raw-TCP wire capture for required and optional/null `query`,
`headers`, `multipart`, `uri` querystring params and a `headers` body param:
the caller's value reaches the wire in every case.
…isions

propagateParamBaseNameToVars wrote x-kotlin-param-base-name onto
CodegenProperty instances that come from DefaultCodegen.fromProperty()'s
cache, so two deepObject parameters sharing a model/property overwrote each
other's wire-key prefix. Vars are now cloned before the parameter-local
extension is attached; verified with a two-parameter fixture sharing the same
property — each wire key keeps its own baseName.

The collision guard in toParamName compared raw spec names, so variants like
local_variable_body or LocalVariableBody normalized to the same
localVariableBody/localVariableQuery/localVariableHeaders identifiers but
slipped past the check and collided with template locals at codegen time.
The guard now compares the normalized identifier; a /variants fixture
exercises the spelling variants.
Eight of the nine temp-directory sites still used File.deleteOnExit, which
cannot remove non-empty directories and left generated output behind on every
run. Wrap them in try/finally + FileUtils.deleteDirectory like the wire-level
test already does.
fromRequestBodyToFormParameters compared each allOf member's required
entries against the normalized paramName, so any property whose paramName
differs from its schema name (snake_case -> camelCase, or a collision
rename) silently lost its required flag. A top-level required list also
bypassed the allOf-member lists entirely.

allRequired already unions the top-level and allOf-member required lists;
compare it against the schema property name and also honor a single-allOf
wrapper's own required list.
A form field `param_query` plus a query param `query` (renamed to
`param_query` by the internal-variable collision guard) already resolves to
distinct names under prependFormOrBodyParameters=true — the parameters-loop
uniqueness pass sees the prepended form params in allParams. Add a fixture
and assertion so a future regression cannot silently reintroduce a duplicate
signature.
…c parameters

Spec parameters named after generated operation locals (body, query,
headers, ...) or api members (basePath, vertx, request, ...) previously
collided with or shadowed them in the generated Kotlin source, producing
uncompilable or miswired clients.

- AbstractKotlinCodegen.toParamName: treat any normalized name starting
  with `localVar` as reserved (covers both the localVar* and
  localVariable* template-local families) and rename such parameters
  with a `param` prefix; wire names are preserved.
- Operation templates: prefix every generated statement-level local with
  `localVariable` across jvm-okhttp, jvm-vertx, jvm-volley,
  jvm-spring-restclient and jvm-spring-webclient.
- Qualify api-member references with `this.` so same-named parameters
  cannot shadow them (vertx auth/vertx/basePath/handleResponse/
  responseBody/encodeURIComponent/parseDateToQueryString, volley
  requestFactory/postProcessors/requestQueue/basePath, spring request()).
- jvm-okhttp: use a labeled `this@{{classname}}.` receiver for member
  calls made inside `apply {}` blocks, where plain `this` would bind to
  the map receiver.
- jvm-spring-*: parseDateToQueryString is a top-level helper, so call it
  package-qualified (`{{packageName}}.infrastructure.`) instead of
  `this.`-qualified.
- Add a template lint test enforcing the localVar prefix on all
  operation-scope val/var declarations (mustache tags stripped, partials
  included) and a cross-library canary spec + test asserting param
  renames, preserved wire names and member qualifications.
- pom.xml: move the okhttp5/moshi jars used by the generated-client
  capture test out of the shared test classpath into a copied dependency
  dir; okhttp5's Kotlin 2.x metadata broke the embedded Kotlin 1.6 test
  compiler used by KotlinTestUtils.
- Regenerate all kotlin samples.

Generated jvm-vertx, jvm-spring-restclient, jvm-spring-webclient and
jvm-okhttp4 outputs verified to compile with kotlinc against real
dependency jars.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The api_client.rb changes for verbatim bodies on 3.2 verbs and
path-only slash collapsing (51fa9b9), and the psr-7 ^2.10 bump plus
param_* collision renames (62a98c2, 9f2676e), were committed
without regenerating the samples. Backfill the drift so the committed
samples match current templates.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
bc20a2c regressed a case the old code avoided by accident: for a
form schema carrying both `properties`/`required` and oneOf/anyOf
branches, ModelUtils.isOneOf/isAnyOf returns false (they require empty
properties), so every property fell into the allOf-required path — and
because addProperties unions the required lists of oneOf/anyOf
alternatives too, branch-only fields were wrongly forced required.

Keep collecting properties via addProperties but compute the effective
required set with a dedicated traversal that only follows the schema's
own required list and the allOf chain (resolving $ref, and starting
from the pre-unwrap schema so a single-allOf wrapper's own required is
covered). oneOf/anyOf branch requireds no longer force form fields.

Note for users: this intentionally changes generated form-parameter
signatures — fields required only inside a oneOf/anyOf alternative are
now optional, matching the schema semantics. Specs where a renamed
parameter (snake_case, collision) is required via allOf keep the fix
from bc20a2c and stay required.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ate-call qualification

Follow-up fixes for the member-collision work:

- jvm-ktor and multiplatform emitted bare calls to the inherited
  request()/jsonRequest()/urlEncodedFormRequest()/multipartFormRequest()
  ApiClient members, so a spec parameter named `request` (or matching a
  member) shadowed the call. Qualify them with `this.`.
- jvm-vertx rebound member names in `?.let { accessToken ->` lambda
  bindings; rename the bindings to the localVariable prefix so a bare
  member name is always a real violation.
- jvm-spring-restclient/webclient: revert the package-qualified
  {{packageName}}.infrastructure.parseDateToQueryString() back to a bare
  call. Kotlin resolves a call site to the function even when a value
  parameter shares its name, and a package-qualified call breaks when a
  parameter is named `org` (first segment of the default package).
  Regression params `parseDateToQueryString` and `org` added to the
  kotlin-member-collision fixture; generated spring clients compile.
- Add a lint test that mechanically extracts val/var member names from
  each library's ApiClient constructor (volley: its own class header)
  and fails on any bare `name` occurrence inside api.mustache's
  {{#operation}} block or the operation partials, unless this./this@
  qualified. Verified by mutation: a bare `basePath` reference fails.
- Assert ktor/multiplatform emit this.request/this.jsonRequest/
  this.urlEncodedFormRequest alongside the spec `request` parameter.
- Copy spring/reactor jars into target/kotlin-capture-deps so the
  generated spring clients can be compiled by the external kotlinc
  without adding 2.x-metadata jars to the shared test classpath.
- Correct the collision-guard comment in AbstractKotlinCodegen.

Regenerate kotlin samples (bare spring calls restored; this.request()
emitted by ktor/multiplatform; localVariable* let-bindings in vertx).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Wire names containing `$` (OData-style `$filter`/`$top`) were emitted
unescaped inside Kotlin string literals in every library except
jvm-okhttp. Kotlin string interpolation then turned the wire key into
the same-named parameter's value — or failed compilation outright when
no such variable was in scope (`"$top"`).

Wrap every baseName/keyParamName/x-kotlin-param-base-name that renders
inside a Kotlin string literal with the existing escapeDollar mustache
lambda in the jvm-vertx, jvm-ktor, multiplatform, jvm-spring-restclient,
jvm-spring-webclient, jvm-volley and jvm-retrofit2 templates (74 sites
across 12 files), matching what jvm-okhttp already did.

Add kotlin-dollar-wire-name.yaml (params `$filter`/`$top` plus a
same-stem `filter` param) and a per-library regression test asserting
the escaped literal is emitted; the jvm-vertx generated client compiles
cleanly under kotlinc.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…alue

`.map(block: T.() -> V)` is a receiver lambda, so `.map { value }`
normally resolves to the wrapper's `value` field. But a spec parameter
named `value` shadows it and the lambda returns the caller's argument
instead of the decoded response (or fails to compile when types
diverge). Emit `this.value` so the receiver member is unambiguous.

Fixture kotlin-receiver-value.yaml exercises array and map responses
with a `value` query param; generated commonMain sources compile under
kotlinc.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
khayashi4337 and others added 4 commits September 24, 2026 13:54
jvm-ktor and multiplatform built urlencoded/multipart form bodies via
`ParametersBuilder().also { it.append(...) }`, relying on the implicit
`it`. A form field named `it` shadows the implicit parameter, so
`it.append` resolved against the String parameter — a compile error for
every form field in that operation, not just the colliding one.

Bind the builder to `localVariableBuilder` explicitly.

Fixture kotlin-form-it-param.yaml sends form fields `it` + `name`;
generated ktor and multiplatform clients compile cleanly under kotlinc.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
jvm-vertx populated a `localVariableForm` MultiMap for form params but
the request was dispatched via `.sendBuffer(body)` or `.send()` only —
the form map was silently dropped, a pre-existing functional gap.

Send it: `.sendForm(localVariableForm)` for urlencoded forms and
`.sendMultipartForm(localVariableForm)` (io.vertx.ext.web.multipart.
MultipartForm) when the operation is multipart.

Add an end-to-end wire test: generate the vertx client from
kotlin-form-it-param.yaml, compile it with kotlinc against the vertx
jars in ~/.m2, and capture the raw HTTP request on a ServerSocket —
asserting `it=v1&name=n2` arrives with the urlencoded content type.
Vert.x sends chunked bodies, which the capture decodes. Previously the
body was empty (CAPTURE-FAIL); now it passes.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- multiplatform emitted `append({{{baseName}}})` for single file
  params — the wire name as a bare expression (`append(my-file)` is a
  syntax error; `append(file)` only worked by coincidence when names
  matched). The FormPart carries its own key: emit `append(paramName)`.
- jvm-ktor's file-array loop emitted `append(it)` inside
  `for (x in param ?: listOf())`, where `it` is undefined — emit
  `append(x)`.

Fixture kotlin-multipart-file.yaml uses a `my-file` wire name (param
`myFile`) plus a file array and a plain field; generated ktor and
multiplatform clients compile under kotlinc.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
12 files across kotlin-jvm-ktor (gson/jackson/kotlinx_serialization),
kotlin-jvm-vertx (gson/jackson/jackson-coroutines/moshi) and
kotlin-multiplatform samples, covering:

- vertx: `.send()` -> `.sendForm()`/`.sendMultipartForm()` so form
  bodies actually reach the wire; multipart ops now build a
  MultipartForm
- multiplatform: `.map { value }` -> `.map { this.value }` and the
  named `localVariableBuilder` form builder
- jvm-ktor: named `localVariableBuilder` form builder

No `$`-escape or file-append drift: no committed sample spec uses
`$` wire names or file-array form fields.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@khayashi4337 khayashi4337 changed the title Generate code for OpenAPI 3.2 query/additionalOperations/in:querystring (Java okhttp-gson) Generate code for OpenAPI 3.2 query/additionalOperations/in:querystring Sep 24, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant