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 @@ -1559,6 +1559,14 @@ public ExtendedCodegenParameter(CodegenParameter cp) {
this.isPrimitiveType = cp.isPrimitiveType;
this.isModel = cp.isModel;
this.isExplode = cp.isExplode;
this.isExplode = cp.isExplode;
this.isDeepObject = cp.isDeepObject;
Comment thread
macjohnny marked this conversation as resolved.
this.isFormStyle = cp.isFormStyle;
this.isMatrix = cp.isMatrix;
this.isAllowEmptyValue = cp.isAllowEmptyValue;
this.isSpaceDelimited = cp.isSpaceDelimited;
this.isPipeDelimited = cp.isPipeDelimited;

this.baseName = cp.baseName;
this.paramName = cp.paramName;
this.dataType = cp.dataType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,23 @@ export class {{classname}} extends runtime.BaseAPI {
{{^isArray}}
if (requestParameters['{{paramName}}'] != null) {
{{#isExplode}}
{{#isContainer}}
{{! form + explode: one parameter per entry. isMap (not isContainer) also covers free-form objects; deepObject is assigned whole so the runtime brackets it. }}
{{#isDeepObject}}
{{>apisAssignQueryParam}}
{{/isDeepObject}}
{{^isDeepObject}}
{{#isMap}}
for (let key of Object.keys(requestParameters['{{paramName}}'])) {
queryParameters[key] = requestParameters['{{paramName}}'][key];
const value = (requestParameters['{{paramName}}'] as any)[key];
if (value != null) {
Object.defineProperty(queryParameters, key, { value, enumerable: true, writable: true, configurable: true });
}
}
{{/isContainer}}
{{^isContainer}}
{{/isMap}}
{{^isMap}}
{{>apisAssignQueryParam}}
{{/isContainer}}
{{/isMap}}
{{/isDeepObject}}
{{/isExplode}}
{{^isExplode}}
{{>apisAssignQueryParam}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,36 @@ public void testDateFormatIsSerializedAsACalendarDate() throws IOException {
TestUtils.assertFileContains(api, "formParams.append('createdAt', runtime.serializeDateTime(requestParameters['createdAt'] as any))");
}

@Test(description = "Verify a form style, exploded map query parameter goes on the wire one entry per parameter")
public void testExplodedObjectQueryParameter() throws IOException {
File output = generate(new HashMap<>(), "src/test/resources/3_0/exploded-object-query-param.yaml");
Path api = Paths.get(output + "/apis/DefaultApi.ts");

// form style with explode - the default - puts every entry on the wire under its own
// property name. A free-form object is isMap but not isContainer, so it used to fall
// through to a whole-object assignment and end up bracketed by the runtime.
TestUtils.assertFileContains(api,
"for (let key of Object.keys(requestParameters['filter'])) {",
"const value = (requestParameters['filter'] as any)[key];");
TestUtils.assertFileNotContains(api, "queryParameters['filter'] = requestParameters['filter'];");

// a declared map behaves the same way
TestUtils.assertFileContains(api,
"for (let key of Object.keys(requestParameters['typedFilter'])) {",
"const value = (requestParameters['typedFilter'] as any)[key];");

// pins the emitted loop: a null or undefined entry is skipped, and the entry is defined rather than assigned so a key named __proto__ survives
TestUtils.assertFileContains(api, "if (value != null) { Object.defineProperty(queryParameters, key, { value, enumerable: true, writable: true, configurable: true }); }");

// deepObject nests under the parameter name, which the runtime does for a whole object
TestUtils.assertFileContains(api, "queryParameters['deepFilter'] = requestParameters['deepFilter'];");
TestUtils.assertFileNotContains(api, "Object.keys(requestParameters['deepFilter'])");

// without explode the object stays a single parameter
TestUtils.assertFileContains(api, "queryParameters['flatFilter'] = requestParameters['flatFilter'];");
TestUtils.assertFileNotContains(api, "Object.keys(requestParameters['flatFilter'])");
}

private static final String DATE_HANDLING_SPEC = "src/test/resources/3_0/typescript-fetch/date-handling.yaml";

private static File generate(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
openapi: 3.0.3
info:
title: Exploded object query parameters
description: >
Object typed query parameters under form/explode (as a free-form object and as a typed map), deepObject, and form without explode. The free-form variant matters because it is flagged isMap but not isContainer.
version: 1.0.0
servers:
- url: localhost:8080
paths:
/items:
get:
operationId: listItems
parameters:
# style and explode both left out, so the form/true defaults apply: every entry
# becomes its own parameter, keyed by the property name alone.
- in: query
name: filter
schema:
type: object
# the same, but declared as a map rather than as a free-form object
- in: query
name: typedFilter
schema:
type: object
additionalProperties:
type: string
# deepObject nests each entry under the parameter name: deepFilter[key]=value
- in: query
name: deepFilter
style: deepObject
explode: true
schema:
type: object
# form without explode keeps a single parameter carrying the whole object
- in: query
name: flatFilter
style: form
explode: false
schema:
type: object
responses:
'200':
description: a list of items
content:
application/json:
schema:
type: array
items:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,10 @@ export class FakeApi extends runtime.BaseAPI {

if (requestParameters['language'] != null) {
for (let key of Object.keys(requestParameters['language'])) {
queryParameters[key] = requestParameters['language'][key];
const value = (requestParameters['language'] as any)[key];
if (value != null) {
Object.defineProperty(queryParameters, key, { value, enumerable: true, writable: true, configurable: true });
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,10 @@ export class FakeApi extends runtime.BaseAPI {

if (requestParameters['language'] != null) {
for (let key of Object.keys(requestParameters['language'])) {
queryParameters[key] = requestParameters['language'][key];
const value = (requestParameters['language'] as any)[key];
if (value != null) {
Object.defineProperty(queryParameters, key, { value, enumerable: true, writable: true, configurable: true });
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,10 @@ export class FakeApi extends runtime.BaseAPI {

if (requestParameters['language'] != null) {
for (let key of Object.keys(requestParameters['language'])) {
queryParameters[key] = requestParameters['language'][key];
const value = (requestParameters['language'] as any)[key];
if (value != null) {
Object.defineProperty(queryParameters, key, { value, enumerable: true, writable: true, configurable: true });
}
}
}

Expand Down
Loading