Skip to content

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

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

khayashi4337 wants to merge 46 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. Operations using them are now skipped with a warning in generators without support, instead of being sent through ordinary name=value serialization that corrupted the already-encoded value.
  • 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. Unsupported generators no longer flatten inline schemas from callbacks attached to 3.2 operations.
  • When the parser produces no OpenAPI object, generation fails immediately with the parser's diagnostics instead of a generic downstream error.

Additional fixes

  • k6 now skips 3.2 query operations with a warning instead of emitting http.query() calls (which throw at runtime), and no longer leaves empty request groups when a query-only path coexists with a sibling dataextract operation.
  • 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. Kotlin also escapes $ in paths and wire names across all libraries (retrofit2 applies it only to string literals, not identifiers), jvm-vertx multipart file params transmit file content instead of the local path with array values as separate parts, and jvm-ktor binds its form builder explicitly so a field named it no longer shadows it.
  • 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.
  • The dropped-operation warning for issue [BUG] Unrecognized path-item operations (e.g. OpenAPI 3.2 'query') are silently dropped from generated code with --skip-validate-spec #24212 no longer false-positives on quoted segments nested inside path names or on dotted custom method names; verbatim query handling was also hardened across Go (raw query ordering), Python (case-exact method dispatch), and typescript-fetch (no longer treats a '?' inside a URL fragment as an existing query, and warns on fetch-forbidden verbs). Rust escapes markdown specials in generated method-name docs.
  • The FILTER normalizer evaluates additionalOperations entries independently, so a key like GET alongside the fixed get: field can no longer overwrite the fixed method's entry.

Written for commit e9d413c. 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.

8 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/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/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="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/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="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="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>
</file>

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

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/python/rest.mustache Outdated
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>

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>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for the review, @cubic-dev-ai.

This is correct, and stems from the same cause as the finding on ApiClient.mustache/build.gradle.mustache/build.sbt.mustache below: the OpenAPI 3.2 model support this PR depends on (PathItem.query, PathItem.additionalOperations, etc.) doesn't exist in any released swagger-core/swagger-parser version yet -- only in a local snapshot build.

This PR is intentionally a Draft for that reason, tracking an in-progress upstream chain:

Once those land upstream I'll drop the snapshot pin and rebase. Flagging this explicitly so it isn't mistaken for an oversight.

// 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>

'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
khayashi4337 and others added 15 commits September 30, 2026 13:39
cubic-dev-ai found that supportsQueryStringParameters() gated only
per-language serialization, so a generator that returns false for it
(the default) still emitted the OpenAPI 3.2 in:querystring parameter
through ordinary name=value query serialization -- corrupting the
already-encoded value on the wire instead of skipping the operation.

DefaultGenerator now checks the flag itself and skips (with a warning)
any operation whose parameters include one, matching how additionalOperations
and 'query' methods are already handled. The check requires
supportsQueryStringParameters() to be callable from DefaultGenerator, so
CodegenConfig gains a public default and every override across the eight
generators is widened from protected accordingly. Ruby's ad hoc
degradeQueryStringParams() fallback (which emitted a corrupted name=value
parameter instead of skipping) is removed in favor of the shared gate.

Also documents that Java's dynamicOperations ApiClient lookup has never
covered OpenAPI webhooks (a pre-existing gap unrelated to 3.2, verified by
git history) so the new hasQueryOrAdditionalOperations flag intentionally
does not scan them.

Verified: RubyClientCodegenTest 31/31 pass (1 environment-skip, httpx gem
unavailable).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ded query

cubic-dev-ai found that appending the path-embedded in:querystring value
after the ordinary query parameters reversed the existing precedence
(previously, and per RFC 3986, no ordering guarantee existed either way,
but appending changed which value wins when a key appears in both). Put
the raw, path-declared query string first and the newly encoded
parameters after it, matching the order the path/query already implied.

Regenerated all 12 go-client samples (generatorName: go) with
./bin/generate-samples.sh; no other output changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cubic-dev-ai found that _pool_request() classified a method as standard
by checking method.upper() against the standard-method set, so a
lower/mixed-case additionalOperations key that happened to match a
standard verb case-insensitively (e.g. an operation literally named
'get') was dispatched through pool_manager.request(), which
unconditionally re-uppercases the method -- corrupting the verbatim
casing the OpenAPI 3.2 contract requires. Classify by exact match against
_STANDARD_METHODS instead, and dispatch non-matching methods through the
encode_url/encode_body helpers directly so their casing survives.

Also replaces the `assert` used to validate non-standard method tokens
with an explicit ApiValueError (assert is stripped under `python -O`,
silently disabling the check), and narrows the GET/HEAD no-body carve-out
so it doesn't unintentionally start sending bodies for those two methods
when a spec attaches one.

Regenerated the 5 affected python-client samples with
./bin/generate-samples.sh (python.yaml, python-lazyImports,
python-echo-api, python-echo-api-disallowAdditionalPropertiesIfNotPresent-true,
python-legacy-model-dictionaries); verified python3 -m py_compile on all
five and confirmed no other language's samples changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…standard classification

cubic-dev-ai found that flagOpenAPI32Operations only checked
FETCH_FORBIDDEN_METHODS (CONNECT/TRACE/TRACK) inside the
"!STANDARD_HTTP_METHODS.contains(...)" branch. CONNECT is itself listed
in STANDARD_HTTP_METHODS (it keeps the existing single-quoted-literal
output shape and can only ever arrive via additionalOperations, since
PathItem has no native CONNECT field), so that branch never ran for it --
generating a client with no warning that fetch() throws TypeError for
CONNECT at runtime. TRACE has the same exposure. Moved the
FETCH_FORBIDDEN_METHODS check out of the standard/non-standard branch so
it runs independently; CONNECT/TRACE keep their standard-method output
shape, they just also get the warning.

Added a new fixture (3_2/typescript-fetch-forbidden-methods.yaml, kept
separate from the shared query-operation.yaml used by other languages'
tests) and testFetchForbiddenMethodsStillWarn, which captures the WARN
log for connectPets/tracePets/trackPets and confirms the generated
output still uses the literal 'CONNECT'/'TRACE'/"TRACK" method strings.

Verified: TypeScriptFetchClientCodegenTest 53/53 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…erations

cubic-dev-ai flagged that MergedSpecBuilder's additionalOperations merge
looked like it bypassed propagateRootSecurityToOperations. Verified this
is not the case: PathItem.readOperations() (the basis for the
propagation) already includes additionalOperations.values(), and the
merged additionalOperations Operation objects are the same instances
readOperations() walked, so root-level security already reaches them by
reference before the merge runs. No production code change; adds
shouldPropagateRootSecurityToAdditionalOperations to pin the behavior.

Verified: MergedSpecBuilderTest 55/55 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two cubic-dev-ai findings against the same UNEXPECTED_PATH_ITEM_ATTRIBUTE
pattern, addressed together since both touch it:

- False positive: the greedy path-capture group `(.+)` matched past a
  legitimate quoted path name into a second quoted segment nested inside
  it (e.g. "attribute paths.'/tasks'.'x-custom'.nested is unexpected",
  which the parser emits whenever the path template itself contains a
  literal quote), misattributing the nested member as a dropped
  operation. Restricted the path segment to non-quote characters.
- False negative: the attribute-name group excluded '.', so a valid
  custom HTTP method containing a dot (e.g. 'M.FOO', permitted by RFC
  9110's tchar) silently produced no MISSING warning when dropped.
  Dots are now allowed; nested locations remain excluded via the
  brackets/parens/quotes they're formatted with instead.

Regression tests: shouldNotFalsePositiveOnNestedQuotedPathItemMemberTypo
and shouldWarnAboutDroppedCustomMethodContainingDot, each backed by a new
fixture verified against the real swagger-parser output (not asserted
from memory).

Also fixes CodegenConfiguratorTest's own cubic-dev-ai finding: several
tests attach a ListAppender to the shared static CodegenConfigurator
LOGGER, which is unsafe if these tests ever run in parallel with other
classes touching the same logger (pom.xml enables
<parallel>classes</parallel>). Added
attachListAppenderCapturingCurrentThread, a thread-name filter every
capturing test now uses, plus shouldNotCaptureLogEventsFromOtherThreads
to pin that foreign-thread events are excluded. A throwaway hammer/victim
test pair (not part of this commit) reproduced 1.48M foreign-thread
events leaking into an unfiltered appender under real TestNG parallel
execution, confirming the filtered version drops all of them.

Verified: CodegenConfiguratorTest 9/9 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cubic-dev-ai found that runCargo() combines cargo's stdout/stderr into
one pipe (redirectErrorStream(true)) but only reads it after
waitFor(15, TimeUnit.MINUTES) returns. Cargo output beyond the OS pipe
buffer (~64KB on Linux) would then block cargo's own write, and the test
would wait the full 15 minutes for a process that can't make progress.
Start a daemon thread to drain the pipe as soon as the process starts,
concurrently with the wait, and join it with a bound so a stray
grandchild process still holding the pipe open can't hang the test either.

Verified: RustClientCodegenTest 18/18 pass (1 environment-skip, cargo
unavailable in this sandbox); the deadlock itself needs a cargo toolchain
to reproduce live, so this fix is verified by code review plus the
existing suite staying green, not by a live repro.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…backs

cubic-dev-ai found that InlineModelResolver.flatten()'s callback-discovery
loop iterated path.readOperations() directly, bypassing the
supports32Ops filter that addOperationEntries() already applies to the
same path's top-level operations. A generator without
supportsAdditionalOperations() support would therefore still flatten
inline schemas from callbacks attached to a 'query' or additionalOperations
entry, even though that entry itself is skipped and never generated.

Iterate a snapshot of the already-filtered toFlatten list instead (a
snapshot because the loop itself appends further callback entries into
that same list). Verified equivalence against swagger-models source:
readOperations() returns fixed-method ops (QUERY included) plus
additionalOperations.values(), so the filtered toFlatten list is exactly
readOperations() with the supports32Ops filter applied.

New test callbacksOn32OperationsSkippedWhenUnsupported pins this with a
negative control: reverting the fix makes its QUERY-callback assertion
fail, confirming the test actually catches the regression.

Verified: InlineModelResolverTest 63/63 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n existing query

cubic-dev-ai found that the in:querystring append logic
(url.includes('?') ? '&' : '?') matches a literal '?' anywhere in the
URL, including inside a fragment (e.g. a base URL ending in
.../v1#frag?notaquery), so it would append with '&' as if a query
already existed. Search only the part before '#' instead
(url.split('#')[0].includes('?')). The same expression exists twice
(runtime.mustache and apis.mustache, both added by this PR's
e2b405d); fixed both.

Verified (Node v22): base=.../v1#frag?notaquery now yields '?' as
expected; the four other cases (query present/absent, fragment-only,
query+fragment) are unchanged from before. A residual gap noted but left
out of scope: when the base path itself (not just a caller-supplied
fragment) contains a fragment, appending at the end still lands inside
it -- fixing that needs inserting before '#', not just changing which
'?' is checked.

Regenerated all 24 typescript-fetch samples; only the runtime.ts fix
line changed. apis.mustache's equivalent line isn't exercised by any
committed sample (isQueryStringParam only appears with a 3.2 in:
querystring parameter, which no committed fixture uses).

Verified: TypeScriptFetchClientCodegenTest 53/53 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The shared fixture used by C#/Go/PHP/Python/Ruby/Rust webhook tests was
renamed from go-webhook-operations.yaml to webhook-operations.yaml in
the previous commit (git mv) -- it has no Go-specific content and the
old name misled maintenance of the other five languages' tests. This
commit updates all six test files' references; grep confirms no
remaining references to the old name outside build output.

Also strengthens GoClientCodegenTest's rawQueryString regression check
(cubic-dev-ai): the test asserted only that a "rawQueryString :=
url.RawQuery" line exists, not the behavior its name describes. It now
additionally checks the capture happens before url.Query() merges/
re-encodes (via the reset-then-recombine lines) and before the request
is built, using indexOf ordering against the existing three-line
sequence.

fix(rust): also escape *, _, and ` in generated method-name markdown docs

cubic-dev-ai found that x-rust-http-method-doc only escaped '|' before
embedding an OpenAPI 3.2 custom method name in Rust's markdown-table
docs, even though RFC 9110 tchar also permits '*', '_', and '`', all of
which have markdown meaning. Escape all four with one replaceAll.

New fixture rust-method-doc-escape.yaml and
testReqwestEscapesMarkdownSpecialsInMethodDoc verify both that the
generated doc text is escaped (A\|B, A\*B, A\_B, A\`B in DefaultApi.md)
and that the wire method name stays verbatim/unescaped
(from_bytes(b"A|B") etc.) -- the escaping must be doc-only.

Verified: GoClientCodegenTest 31/31, RustClientCodegenTest 19/19 (1
environment-skip, cargo unavailable), CSharpClientCodegenTest 19/19 (1
skip), PythonClientCodegenTest 66/66, RubyClientCodegenTest 31/31 (1
skip), PhpClientCodegenTest 13/13 (1 skip) all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…the 3.2 codegen fixes

Unrelated to OpenAPI 3.2 support -- these were found while independently
reviewing this branch's earlier Kotlin fix commits (OpenAPITools#17-1..5), verified
against real generated code (kotlinc + wire captures), not just read.

D1 (P1, regression from an earlier commit on this branch): jvm-vertx sent
multipart File parameters as their path string
(`localVariableForm.attribute("file", file.toString())`) instead of their
content. File params now go through `binaryFileUpload(name, it.name,
it.absolutePath, contentType)`; array form/multipart params go through
`toMultiValue(...)` instead of a single toString() blob. New wire test
(3_0/kotlin-vertx-multipart-capture, testJvmVertxGeneratedClientSendsMultipartFileContent)
opens a raw ServerSocket, runs the generated+compiled client against it,
and confirms actual file content arrives (not a local path) and array
values arrive as separate parts. All 4 vertx petstore samples regenerated
(their previously-committed PetApi.kt had the bad code).

D2: `{{{path}}}` was unescaped for `$` in every library including
jvm-okhttp (an OpenAPI path like `/odata/$count` interpolates or fails to
compile as a Kotlin string template); `{{keyParamName}}` was inconsistently
escaped (missing in ktor/multiplatform/okhttp's ApiClient.kt.mustache).
Applied the existing escapeDollar lambda uniformly. Fixture
kotlin-dollar-wire-name.yaml extended with an `/odata/$count` path and a
`$id` path parameter, verified generated across all 8 libraries.

D3: retrofit2's explodedQueryParam.mustache applied escapeDollar to the
Kotlin identifier position too (`fun explodeQ(@query("\$a") \$a: ...)`),
producing an invalid identifier. Only the string literal side (the
`@Query(...)` argument) should be escaped; the parameter's own name now
uses the already-sanitized `name`.

D4 (sibling of OpenAPITools#17-3, missed in that commit): jvm-ktor's urlencoded array
branch called a receiver-less `append(...)` inside `.also { localVariableBuilder
-> ... }`, which cannot resolve. Changed to `localVariableBuilder.append(...)`.
New array fixture in kotlin-form-it-param.yaml; new compile test
testJvmKtorGeneratedClientCompilesFormParams actually kotlinc-compiles the
generated client (the existing coverage for this branch was assert-on-
generated-text only, which is why D4 slipped through OpenAPITools#17-3 undetected).

Verified: ./bin/generate-samples.sh (67 kotlin configs) -- diff limited to
the 5 files above. `PATH=.../kotlinc/bin:$PATH mvn -pl modules/openapi-generator
-Dtest=KotlinClientCodegenApiTest test`: 46/46 pass, including the two new
kotlinc-compile-and-wire tests actually executed (not skipped). Re-verified
independently: same 46/46 pass on a fresh run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Regression coverage requested after independently reviewing
7ab9da5 (collectRequiredFromAllOfChain, fixing OpenAPITools#16's original P1:
oneOf branch-only fields incorrectly forced required when mixed with a
top-level required list). No production code change -- the fix itself
was already verified correct; these tests just pin it against six edge
cases the original fix's own tests didn't cover:

- 3-level allOf $ref chain: required collected from every level
- oneOf nested inside an allOf member: branch-only fields (x, y) stay
  optional, the member's own field (c) becomes required
- circular allOf $refs (CycA allOf[CycB], CycB allOf[CycA]): no
  StackOverflow, both sides' fields end up required
- snake_case property names reaching required through an allOf $ref
  chain, matched by baseName
- a composed (allOf) property nested inside another schema: its own
  inner `required` does not leak out to the outer schema's fields
- anyOf branches mixed with top-level properties+required: the
  top-level field is required, the anyOf-only branch fields are not

Self-review caught that the "no leak" case was a placebo as first
written -- with no top-level property sharing the leaked field's name,
a regression couldn't have failed the assertion either way. Added an
optional top-level property with that name so a future regression
actually flips it to required and fails the test.

Verified: DefaultCodegenTest 184/184 pass (178 existing + 6 new).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n FILTER normalizer

normalizePaths() built one methodMap combining the 9 fixed HTTP methods
with additionalOperations entries, keyed by method.toLowerCase(). An
additionalOperations key differing from a fixed method only by case
(e.g. "GET" alongside the fixed `get:` field) silently overwrote that
fixed method's entry once lowercased, so the FILTER normalizer rule
(--openapi-normalizer FILTER=...) never evaluated the fixed operation at
all -- it was missing from x-internal marking entirely. Only reachable
with --skip-validate-spec (default validateSpec=true already rejects
this shape via swagger-parser's own "duplicates a fixed Path Item method
name" warning).

methodMap goes back to the original immutable 9-entry Map.of(); additionalOperations
are no longer merged into it. Filter gained a new single-operation
apply(path, operation, method) overload (the existing public
apply(path, PathItem, methodMap) now delegates to it per entry, so
existing custom Filter subclasses keep compiling against the same
signature); normalizePaths calls it once per additionalOperations entry
directly, so a same-named fixed method and additionalOperations entry
are both evaluated independently -- no shared map slot to collide over.

New regression test testFilterWithCaseCollidingAdditionalOperation
builds a PathItem with a fixed getGet() and an additionalOperations
"GET" entry and checks both filter directions (operationId filter
targeting only the additionalOperations one; method:get filter matching
both). Confirmed the bug reproduces first: running the new test against
the pre-fix code throws NPE (the fixed operation's getExtensions() was
never reached).

Verified: OpenAPINormalizerTest 72/72 pass. DefaultCodegenTest +
ExclusiveMinMaxTest 198/198 pass. cpp-boost-beast's 5 test classes (the
only other OpenAPINormalizer subclass consumer) 62/62 pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Brings the branch up to date with upstream (dda8e0e..ea65d43).
Resolved 4 real conflicts, all independently reviewed by Fable before
resolving:

- JavaClientCodegenTest.java, PythonClientCodegenTest.java,
  RubyClientCodegenTest.java: both sides appended unrelated new test
  methods at the same insertion point (end of class). Kept both sets;
  no content was dropped (confirmed: zero removed lines vs. either
  parent).

- kotlin-client/libraries/jvm-okhttp/api.mustache: upstream added a
  second `{{#queryParams}}` loop for `x-kotlin-explode-form-object`
  (Map-typed query params exploded to one key-value pair per entry),
  closing the first loop rather than appending inside it. Restructured
  to two sequential loops -- the branch's existing per-param
  serialization (now also gated on `{{^vendorExtensions.x-kotlin-explode-form-object}}`
  for clarity) followed by upstream's explode loop, matching the
  parameter-then-explode ordering upstream's own test asserts.
  `isQueryStringParam` (in:querystring, always a plain string) and
  `x-kotlin-explode-form-object` (Map-typed, explode=true) are mutually
  exclusive by construction (DefaultCodegen.fromParameter forces
  StringSchema for in:querystring; the explode flag requires isMap) --
  confirmed by reading both code paths, not assumed.

  Upstream's explode block used `val values`/`val name` and an
  unqualified `parameterToString(it)`, which would have failed this
  branch's own lint test (testKotlinApiTemplatesUseLocalVariablePrefix,
  added in 94aca3a) and broken its `this@{{classname}}.`
  qualification convention. Renamed to `localVariableValues`/
  `localVariableName` and qualified the call; updated upstream's own
  test assertions (KotlinClientCodegenApiTest) to match the renamed
  locals.

Verified (Devin implemented per a self-contained spec reviewed by
Fable; independently re-run here): JavaClientCodegenTest 300/300,
PythonClientCodegenTest 97/97, RubyClientCodegenTest 36/36 (1 env-skip),
KotlinClientCodegenApiTest 47/47, RustClientCodegenTest 24/24 (1
env-skip), TypeScriptFetchClientCodegenTest 54/54 -- all pass. The 7
toolchain-dependent tests skipped without dev-env in PATH were
individually re-run with kotlinc/ruby+httpx/cargo available and all
pass. Samples regenerated across all 199 kotlin/ruby/go/python/
typescript-fetch configs (./bin/generate-samples.sh); diff limited to
2 files (python-httpx2, python-httpx2-sync -- new upstream samples the
in:querystring fix correctly reached). No leftover conflict markers
(git grep across *.java/*.mustache clean).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
….query()

K6ClientCodegen builds its HTTPRequest/HTTPRequestGroup data (the input
to script.mustache) through two of its own direct
PathItem.readOperationsMap() traversals, bypassing the
supportsAdditionalOperations() gate that DefaultGenerator/DefaultCodegen
use elsewhere to skip-and-warn on unsupported OpenAPI 3.2 operations (k6
doesn't override the capability, so it defaults to false). A path with a
`query` operation therefore still reached HTTPRequest.method =
"query", and script.mustache's `http.{{method}}(...)` emitted
`http.query(url)` -- even though DefaultGenerator's own warning for this
path claimed the operation "will be missing from the generated output".

k6's http module has no `query` function in any released version
(verified against v2.3.0, 2026-09-21, the latest release: only get/post/
put/patch/del/options/request are exported) -- the generated script
throws `TypeError: http.query is not a function` at runtage. (Note: k6's
unreleased master branch has since added a query export for the same
RFC 9110 HTTP QUERY method, but no shipped version has it yet.)

Both traversals now skip PathItem.HttpMethod.QUERY when
!supportsAdditionalOperations(), logging the same warning text
DefaultGenerator already uses elsewhere for consistency. Self-review
caught a derived bug: a path whose only operation was the skipped
`query` left `pathVariables.get(groupName)` returning null, which
produced an HTTPRequestGroup with a null `variables` field -- NPEs in
initializeDataExtractSubstituteParameters for specs with a sibling
dataextract operation, and an empty `group(...)` block left in script.js
for specs without one. Guarded against registering a group with zero
requests.

New K6ClientCodegenTest (no prior Java test existed for this generator):
testQueryOperationIsSkipped (get+query mixed path: http.get present,
http.query/searchItems absent) and a query-only path with a sibling
dataextract operation (no NPE, no empty group). Confirmed the bug
reproduces: running the new test against the pre-fix code (production
change stashed) fails with http.query(/searchItems detected in script.js.

Verified: K6ClientCodegenTest 2/2, DefaultGeneratorTest 27/27,
DefaultCodegenTest 187/187, OpenAPINormalizerTest 72/72,
InlineModelResolverTest 63/63 pass. generate-samples.sh on the 3 k6
configs: zero diff (existing k6 samples unaffected).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

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