Deep objects are not fully escaped with deepObject
and explode
#2659
Description
When using parameters with deepObject
and explode
, the parameters are only partially escaped.
Q&A (please complete the following information)
- OS: linux mint 20.3
- Environment: node v14.19.2
- Method of installation: npm install
- Swagger-Client version: 3.18.5
- Swagger/OpenAPI version: OpenAPI 3.0.3
Content & configuration
Swagger/OpenAPI definition:
const spec = {
openapi: '3.0.3',
paths: {
'/export': {
post: {
operationId: 'exportProducts',
parameters: [
{
name: 'filters',
in: 'query',
style: 'deepObject',
explode: true,
schema: { type: 'object', additionalProperties: true },
},
{
name: 'filtersDeep',
in: 'query',
style: 'deepObject',
explode: true,
schema: { type: 'object', additionalProperties: true },
},
],
},
},
},
};
Swagger-Client usage:
const parameters = {
filters: { 'a+b+c': '123=456' },
filtersDeep: { 'a+b+c': { 'd+e+f': '123=456' } },
};
const SwaggerClient = require('swagger-client');
const request = SwaggerClient.buildRequest({ spec, operationId: 'exportProducts', parameters });
console.log(request.url);
// prints:
// /export?filters%5Ba%2Bb%2Bc%5D=123%3D456&filtersDeep%5Ba%2Bb%2Bc%5D[d+e+f]=123=456
Describe the bug you're encountering
When using parameters with deepObject
and explode
, the parameters are only partially escaped. If an object with only 1 depth is provided, it is escaped correctly. But objects with 2 or more depth levels are not escaped correctly. In deeper objects, only the parameter name and 1st level of keys are escaped. Nothing else is escaped including the value.
To reproduce...
Steps to reproduce the behavior:
- Create spec with deepObject and explode parameters.
- Pass in a 1 depth object as a parameter, observe it is fully escaped.
- Pass in a 2 depth object as a parameter, observe it is not fully escaped.
Expected behavior
I expect deep objects passed as parameters with deepObject
and explode
to be fully escaped, including all the keys and values.
Additional context or thoughts
I see that there are other tickets/issues/prs for deepObject/explode support so I'm not sure how finalized these parameters are. However, the behaviour here doesn't seem correct in any situation.
I have a hacky solution to apply a fix through the parameterBuilders
system. It is a bit hacky, both because it grabs the default un-exported oas3 builder and it modifies the value using qs
before the http escaping system happens. Changes to serializationOption
had no effect.
Click for fix with parameterBuilders
// npm install swagger-client@3.18.5
// npm install qs@6.11.0
const parameters = {
filters: { 'a+b+c': '123=456' },
filtersDeep: { 'a+b+c': { 'd+e+f': '123=456' } },
};
// QS version:
const qs = require('qs');
console.log('QS Version:');
console.log(`/export?${qs.stringify(parameters)}`);
// Swagger version:
const SwaggerClient = require('swagger-client');
const spec = {
openapi: '3.0.3',
paths: {
'/export': {
post: {
operationId: 'exportProducts',
parameters: [
{
name: 'filters',
in: 'query',
style: 'deepObject',
explode: true,
schema: { type: 'object', additionalProperties: true },
},
{
name: 'filtersDeep',
in: 'query',
style: 'deepObject',
explode: true,
schema: { type: 'object', additionalProperties: true },
},
],
},
},
},
};
const request = SwaggerClient.buildRequest({ spec, operationId: 'exportProducts', parameters });
console.log('Swagger Version:');
console.log(request.url);
// Customize with parameterBuilders
const oas3Builder = require('swagger-client/lib/execute/oas3/parameter-builders');
const requestCustomBuilder = SwaggerClient.buildRequest({
spec,
operationId: 'exportProducts',
parameters,
parameterBuilders: {
body: oas3Builder.body,
header: oas3Builder.header,
path: oas3Builder.path,
formData: oas3Builder.formData,
cookie: oas3Builder.cookie,
query: ({ req, value, parameter }) => {
if (parameter.style === 'deepObject' && parameter.explode) {
const flatParams = [...new URLSearchParams(qs.stringify({ value }))];
const newValue = Object.fromEntries(flatParams.map(
([key, val]) => [key.replace(/^value\[/, '').replace(/]$/, ''), val],
));
oas3Builder.query({ req, value: newValue, parameter });
} else {
oas3Builder.query({ req, value, parameter });
}
},
},
});
console.log('Swagger Custom Builder Version:');
console.log(requestCustomBuilder.url);