Skip to content

Commit eac731b

Browse files
wiebrenclaude
andauthored
[rust] fix: free-form object query parameters reference a nonexistent models::serde_json (#24866)
* fix: [rust] type free-form object parameters as serde_json::Value, not models::serde_json::Value A free-form object parameter (type: object with no properties) maps to serde_json::Value, but the api templates qualify every non-primitive, non-container parameter type with models:: - and models re-exports no serde_json, so the generated crate fails with E0433. Free-form body parameters already avoid this because updateRequestBodyForObject marks them primitive; give non-body free-form parameters the same treatment. Free-form schemas with additionalProperties stay containers (HashMap) and keep their JSON serialization. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CxDNCjqJycKTfzVWg2SeTJ * ci: retrigger after ubuntu mirror outage * [rust] tidy: shorten the free-form parameter comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [rust] test: cover free-form object parameters in all four libraries Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * [rust] test: cover free-form object path and header parameters The fix marks every free-form entry in allParams, so the fixture now also carries a free-form path parameter and a free-form header parameter, and the test asserts both are typed serde_json::Value. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent a85be6d commit eac731b

3 files changed

Lines changed: 92 additions & 0 deletions

File tree

‎modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
795795
param.isPrimitiveType = true;
796796
param.isString = true;
797797
}
798+
799+
// Free-form objects are `serde_json::Value`, which is not in `models`: mark them primitive, as
800+
// DefaultCodegen.updateRequestBodyForObject does for bodies, so no `models::` prefix is added.
801+
// Free-form maps (`additionalProperties`) are containers and keep their handling.
802+
if (param.isFreeFormObject && !param.isContainer) {
803+
param.isPrimitiveType = true;
804+
}
798805
}
799806

800807
if (operation.pathParams != null && operation.pathParams.size() > 0) {

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,37 @@ public void testIntegerPropertyEnum() throws IOException {
319319
TestUtils.assertFileNotContains(outputPath, linearize("#[serde(rename = \"0\")]"));
320320
}
321321

322+
@Test
323+
public void testFreeFormObjectParams() throws IOException {
324+
for (String library : List.of("reqwest", "hyper", "hyper0x", "reqwest-trait")) {
325+
Path target = Files.createTempDirectory("test");
326+
target.toFile().deleteOnExit();
327+
final CodegenConfigurator configurator = new CodegenConfigurator()
328+
.setGeneratorName("rust")
329+
.setLibrary(library)
330+
.setInputSpec("src/test/resources/3_0/rust/free-form-object-params.yaml")
331+
.setSkipOverwrite(false)
332+
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
333+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
334+
files.forEach(File::deleteOnExit);
335+
Path outputPath = Path.of(target.toString(), "/src/apis/default_api.rs");
336+
TestUtils.assertFileExists(outputPath);
337+
// A free-form object query, path or header parameter maps to `serde_json::Value`, which lives
338+
// outside of the `models` module.
339+
TestUtils.assertFileContains(outputPath, "filter: Option<serde_json::Value>");
340+
TestUtils.assertFileContains(outputPath, "selector: serde_json::Value");
341+
TestUtils.assertFileContains(outputPath, "x_filter: Option<serde_json::Value>");
342+
TestUtils.assertFileNotContains(outputPath, "models::serde_json");
343+
// A free-form object with additionalProperties stays a map.
344+
TestUtils.assertFileContains(outputPath, "tags: Option<std::collections::HashMap<String, String>>");
345+
TestUtils.assertFileContains(outputPath, "meta: Option<std::collections::HashMap<String, serde_json::Value>>");
346+
if (library.equals("reqwest")) {
347+
// Maps keep their JSON serialization (`HashMap` does not implement `Display`).
348+
TestUtils.assertFileContains(outputPath, "req_builder.query(&[(\"meta\", &serde_json::to_string(param_value)?)])");
349+
}
350+
}
351+
}
352+
322353
@Test
323354
public void testArrayWithObjectEnumValues() throws IOException {
324355
Path target = Files.createTempDirectory("test");
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Free-form object parameters
4+
version: 1.0.0
5+
paths:
6+
/things:
7+
get:
8+
operationId: listThings
9+
parameters:
10+
- name: filter
11+
in: query
12+
description: A free-form object query parameter.
13+
schema:
14+
type: object
15+
- name: tags
16+
in: query
17+
description: A free-form object with typed additionalProperties (a map).
18+
schema:
19+
type: object
20+
additionalProperties:
21+
type: string
22+
- name: meta
23+
in: query
24+
description: A free-form object with additionalProperties true (also a map).
25+
schema:
26+
type: object
27+
additionalProperties: true
28+
responses:
29+
'200':
30+
description: OK
31+
content:
32+
application/json:
33+
schema:
34+
type: array
35+
items:
36+
type: string
37+
/things/{selector}:
38+
get:
39+
operationId: getThing
40+
parameters:
41+
- name: selector
42+
in: path
43+
required: true
44+
description: A free-form object path parameter.
45+
schema:
46+
type: object
47+
- name: X-Filter
48+
in: header
49+
description: A free-form object header parameter.
50+
schema:
51+
type: object
52+
responses:
53+
'200':
54+
description: OK

0 commit comments

Comments
 (0)