[typescript-axios] doesn't serialize deepObject query params
Bug Report Checklist
- [x] Have you provided a full/minimal spec to reproduce the issue?
- [x] Have you validated the input using an OpenAPI validator (example)?
- [x] Have you tested with the latest master to confirm the issue still exists?
- [x] Have you searched for related issues/PRs?
- [x] What's the actual output vs expected output?
input:
{day: 20, year: {min: 1990, max: 2000}}actual output:?day=20&yearexpected output:?day=20&year[min]=1990&year[max]=2000
Description
I'm using [typescript-axios] api client generator. Some of the properties I pass to it specified as deepObject. It looks that it doesn't support this type of args at all. Is there any possible ways to fix it?
openapi-generator version
v4.3.1 v5.0.0-beta
OpenAPI declaration file content or url
YearRange:
in: query
name: year
required: false
style: deepObject
schema:
type: object
properties:
min:
type: string
example: 2010
max:
type: string
example: 2019
Generation Details
[typescript-axios] generator
Steps to reproduce
Related issues/PRs
https://github.com/OpenAPITools/openapi-generator/pull/6075
Suggest a fix
Replace old deprecated url API with URLSearchParams.
👍 Thanks for opening this issue! 🏷 I have applied any labels matching special text in your issue.
The team will review the labels and make any necessary changes.
@wamujlb do you have a snippet to work around this?
i did this on the generated file
sed -i "s/localVarQueryParameter\['filter'\] \= filter/localVarQueryParameter\['filter'\] \= JSON.stringify(filter)/g" api.ts
@tsraza this will not work in my case. API expects something like this:
{date: 'desc'} => ?sort[date]=desc.
I have made a workaround but I think that it will be good to add it to the generator.
export const setSearchParams = function (url: URL, ...objects: any[]) {
const searchParams = new URLSearchParams(url.search);
for (const object of objects) {
for (const key in object) {
/**
* @workaround
* Convert object value to the string
* {sort: {date: 'desc', order: 'asc'}} => 'sort[date]=desc&sort[order]=asc'
*/
const value = object[key];
if(typeof value === 'object'){
Object.entries(value).forEach(([entryKey, entryValue]) => {
searchParams.set(`${key}[${entryKey}]`, String(entryValue));
})
} else {
searchParams.set(key, value);
}
}
}
url.search = searchParams.toString();
}
Any update on this?
Any update on this?
++, nothing changed
I've just had a similar problem. Loopback 4 expects filter parameters as serialised JSON.
It can be solved in a 'nice' way using template overrides.
- Create a template override directory eg.
openapi-templates - Copy
apiInner.mustacheintoopenapi-templatesfrom https://github.com/OpenAPITools/openapi-generator/blob/7ffd0711c3c3e7e08110a3e8d63e324daa1773d8/modules/openapi-generator/src/main/resources/typescript-axios/apiInner.mustache - Change the serialisation logic to your taste around lines 94 - 136. Search for
localVarQueryParameter. My example is below. - Pass
-t /path/to/openapi-templatestoopenapi-generator-cli - Rebuild
I modified the template to serialise any object passed as a parameter to JSON:
if(typeof {{paramName}} === 'object' && {{paramName}} !== null)
localVarQueryParameter['{{baseName}}'] = JSON.stringify({{paramName}});
else
localVarQueryParameter['{{baseName}}'] = {{paramName}};
@wamujlb - In your case, you could probably add your code to the template.
In my case I updated the common.mustache template with @wamujlb's answer. Its working as expected.
But it really would be great to add this to the default templates.
FYI since array is not being handled well, i updated based on @wamujlb answer
export const setSearchParams = function (url: URL, ...objects: any[]) {
const searchParams = new URLSearchParams(url.search);
for (const object of objects) {
for (const key in object) {
/**
* @workaround for deep object
* Convert object value to the string
* {sort: {date: 'desc', order: 'asc', multiple: ['1','2']}} => 'sort[date]=desc&sort[order]=asc&multiple[]=1&multiple[]=2'
*/
const value = object[key];
if(typeof value === 'object'){
Object.entries(value).forEach(([entryKey, entryValue]) => {
if(Array.isArray(entryValue)){
entryValue.forEach((item) => {
searchParams.append(`${key}[${entryKey}][]`, String(item));
})
}
else {
searchParams.set(`${key}[${entryKey}]`, String(entryValue));
}
})
} else {
searchParams.set(key, value);
}
}
}
url.search = searchParams.toString();
}
Any update on this?
I'm not sure how my solution is correct, however, I needed to fix it, and here is what I did for that.
in the apiinner.mustache
I changed that
{{^isPrimitiveType}}
{{^isEnumRef}}
{{^isEnum}}
for (const [key, value] of Object.entries({{paramName}})) {
localVarQueryParameter[key] = value;
}
{{/isEnum}}
{{/isEnumRef}}
for this
{{^isPrimitiveType}}
{{^isEnumRef}}
{{^isEnum}}
// deepObject serialization for nested params (e.g. filter[field]=…)
for (const [key, value] of Object.entries({{paramName}})) {
if (Array.isArray(value)) {
value.forEach(item => {
localVarQueryParameter[`{{baseName}}[${key}]`] = item;
});
} else {
localVarQueryParameter[`{{baseName}}[${key}]`] = value;
}
}
{{/isEnum}}
{{/isEnumRef}}
and then I give it up to the axios + qs to handle the parameters properly
import axios, { AxiosInstance, InternalAxiosRequestConfig, AxiosHeaders } from 'axios';
import qs from 'qs';
const sharedAxios: AxiosInstance = axios.create({
paramsSerializer: (params) => qs.stringify(params, { encodeValuesOnly: true, arrayFormat: 'repeat' }),
});
which I also imported via modification of the baseApi.mustache
import sharedAxios from 'my-shared-axios-instance';
const globalAxios = sharedAxios;
It may not be a great solution because it may block me from the upgrade, but at least it works for me as expected.
Will this be fixed?