Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1378,14 +1378,23 @@ public void postProcessParameter(CodegenParameter parameter) {
* `vars` (e.g. jvm-ktor) need the outer baseName to build URL keys like
* `paramBaseName[fieldBaseName]` per the OAS deepObject style, and
* Mustache provides no access to the outer scope from inside `{{#vars}}`.
*
* <p>The vars come from {@code DefaultCodegen.fromProperty()}'s cache, so
* the same instance can back multiple parameters; they are cloned before
* the parameter-local extension is attached to avoid leaking one
* parameter's baseName into another's wire keys.
*/
private void propagateParamBaseNameToVars(CodegenParameter param) {
if (!param.isQueryParam || !param.isModel || param.vars == null) {
return;
}
List<CodegenProperty> vars = new ArrayList<>(param.vars.size());
for (CodegenProperty v : param.vars) {
v.vendorExtensions.put("x-kotlin-param-base-name", param.baseName);
CodegenProperty copy = v.clone();
copy.vendorExtensions.put("x-kotlin-param-base-name", param.baseName);
vars.add(copy);
}
param.vars = vars;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ import {{packageName}}.infrastructure.Serializer
{{#isModel}}
{{#vars}}
if ({{{paramName}}}.{{name}} != null) {
put("{{#isDeepObject}}{{{paramName}}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
put("{{#isDeepObject}}{{#lambda.escapeDollar}}{{{vendorExtensions.x-kotlin-param-base-name}}}{{/lambda.escapeDollar}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
}
{{/vars}}
{{/isModel}}
Expand All @@ -230,7 +230,7 @@ import {{packageName}}.infrastructure.Serializer
{{#isModel}}
{{#vars}}
if ({{{paramName}}}.{{name}} != null) {
put("{{#isDeepObject}}{{{paramName}}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
put("{{#isDeepObject}}{{#lambda.escapeDollar}}{{{vendorExtensions.x-kotlin-param-base-name}}}{{/lambda.escapeDollar}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
}
{{/vars}}
{{/isModel}}
Expand All @@ -243,7 +243,7 @@ import {{packageName}}.infrastructure.Serializer
{{#isModel}}
{{#vars}}
if ({{{paramName}}}.{{name}} != null) {
put("{{#isDeepObject}}{{{paramName}}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
put("{{#isDeepObject}}{{#lambda.escapeDollar}}{{{vendorExtensions.x-kotlin-param-base-name}}}{{/lambda.escapeDollar}}[{{/isDeepObject}}{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}{{#isDeepObject}}]{{/isDeepObject}}", {{#isContainer}}toMultiValue({{{paramName}}}.{{name}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDateTime}}{{#isDate}}parseDateToQueryString<{{{dataType}}}>({{{paramName}}}.{{name}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.{{name}}.value{{/isString}}{{^isString}}{{{paramName}}}.{{name}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.{{name}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}})
}
{{/vars}}
{{/isModel}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,43 @@ public void testExplodedObjectQueryParameterJvmOkhttp() throws IOException {
"exploded entries must be added after the declared query parameters");
}

@DataProvider(name = "librariesWithDeepObjectWireKeys")
public static Object[][] librariesWithDeepObjectWireKeys() {
return new Object[][]{
{ClientLibrary.JVM_KTOR},
{ClientLibrary.JVM_OKHTTP4}
};
}

// Two deepObject query parameters share the property instances of the same
// $ref'd schema (fromProperty cache). The per-parameter wire-key prefix must
// stay parameter-local: filter-a[...] must not be overwritten by filter-b's
// propagation. jvm-okhttp prefixes keys with the spec baseName (via
// x-kotlin-param-base-name), not the camelized paramName.
@Test(dataProvider = "librariesWithDeepObjectWireKeys")
public void testDeepObjectParamsKeepOwnWireKeyPrefix(ClientLibrary library) throws IOException {
OpenAPI openAPI = readOpenAPI("src/test/resources/3_0/kotlin/kotlin-deepobject-shared-vars.yaml");

KotlinClientCodegen codegen = createCodegen(library);
DefaultGenerator generator = new DefaultGenerator();
enableOnlyApiGeneration(generator);

List<File> files = generator.opts(createClientOptInput(openAPI, codegen)).generate();
File searchApi = files.stream().filter(file -> file.getName().equals("DefaultApi.kt")).findAny().orElseThrow();

// the wire key must be paired with the value of the parameter it names:
// filter-a[status] must read filterA's property, filter-b[status] filterB's
if (library == ClientLibrary.JVM_KTOR) {
assertFileContains(searchApi.toPath(),
"filterA?.status?.let { localVariableQuery[\"filter-a[status]\"] = listOf(\"$it\") }",
"filterB?.status?.let { localVariableQuery[\"filter-b[status]\"] = listOf(\"$it\") }");
} else {
assertFileContains(searchApi.toPath(),
"put(\"filter-a[status]\", listOf(filterA.status.toString()))",
"put(\"filter-b[status]\", listOf(filterB.status.toString()))");
}
}

private static void assertFileContainsLine(List<String> lines, String line) {
Assert.assertListContains(lines, s -> s.equals(line), line);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
openapi: 3.0.3
info:
title: kotlin deepObject vars repro
version: 1.0.0
paths:
/search:
get:
operationId: search
parameters:
- name: filter-a
in: query
style: deepObject
explode: true
schema:
$ref: '#/components/schemas/Filter'
- name: filter-b
in: query
style: deepObject
explode: true
schema:
$ref: '#/components/schemas/Filter'
responses:
'200':
description: ok
components:
schemas:
Filter:
type: object
properties:
status:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -400,22 +400,22 @@ open class QueryApi(basePath: kotlin.String = defaultBasePath, client: Call.Fact
.apply {
if (queryObject != null) {
if (queryObject.id != null) {
put("queryObject[id]", listOf(queryObject.id.toString()))
put("query_object[id]", listOf(queryObject.id.toString()))
}
if (queryObject.name != null) {
put("queryObject[name]", listOf(queryObject.name.toString()))
put("query_object[name]", listOf(queryObject.name.toString()))
}
if (queryObject.category != null) {
put("queryObject[category]", listOf(queryObject.category.toString()))
put("query_object[category]", listOf(queryObject.category.toString()))
}
if (queryObject.photoUrls != null) {
put("queryObject[photoUrls]", toMultiValue(queryObject.photoUrls.toList(), ""))
put("query_object[photoUrls]", toMultiValue(queryObject.photoUrls.toList(), ""))
}
if (queryObject.tags != null) {
put("queryObject[tags]", toMultiValue(queryObject.tags.toList(), ""))
put("query_object[tags]", toMultiValue(queryObject.tags.toList(), ""))
}
if (queryObject.status != null) {
put("queryObject[status]", listOf(queryObject.status.value))
put("query_object[status]", listOf(queryObject.status.value))
}
}
}
Expand Down
Loading