[REQ] [typescript-angular]: support for object as query parameters
See original GitHub issueIs your feature request related to a problem? Please describe.
At present, typescript-angular only supports arrays and simple types (string, number, Date) as query parameters. However, when you use openapi-generator in conjuction with springdoc and (java) spring, you wish to have support for objects as query parameters as well.
In spring-data, there is REST support for an idea called
Pageable
. This would
be the openapi schema for it:
"Pageable": {
"nullable": false,
"type": "object",
"required": [
"size",
"page"
],
"properties": {
"size": {
"format": "int32",
"type": "integer"
},
"sort": {"$ref": "#/components/schemas/Sort"},
"page": {
"format": "int32",
"type": "integer"
}
}
},
"Sort": {
"nullable": false,
"type": "array",
"items": {"type": "string"}
},
The items
of Sort are the names of the properties to sort the data with.
Optionally, there could be a sort direction (ASC, DESC) appended like in
ansatz,DESC
. The comma (,) should not be urlencoded.
What I want is sane support for Pageable
and other objects.
Describe the solution you’d like
Lets think about a (GET) REST endpoint that takes an Pageable
and a map
with parameters as query parameters. openapi-generator
will emits something
like:
public getAllAnsatz(pageable: Pageable, map: { [key: string]: Array<string>; }, observe?: 'body', reportProgress?: boolean): Observable<Array<AnsatzDTO>>;
public getAllAnsatz(pageable: Pageable, map: { [key: string]: Array<string>; }, observe?: 'response', reportProgress?: boolean): Observable<HttpResponse<Array<AnsatzDTO>>>;
public getAllAnsatz(pageable: Pageable, map: { [key: string]: Array<string>; }, observe?: 'events', reportProgress?: boolean): Observable<HttpEvent<Array<AnsatzDTO>>>;
public getAllAnsatz(pageable: Pageable, map: { [key: string]: Array<string>; }, observe: any = 'body', reportProgress: boolean = false ): Observable<any> {
if (pageable === null || pageable === undefined) {
throw new Error('Required parameter pageable was null or undefined when calling getAllAnsatzfreigabes.');
}
if (map === null || map === undefined) {
throw new Error('Required parameter map was null or undefined when calling getAllAnsatzfreigabes.');
}
let queryParameters = new HttpParams({encoder: this.encoder});
if (pageable !== undefined && pageable !== null) {
queryParameters = queryParameters.set('pageable', <any>pageable);
}
if (map !== undefined && map !== null) {
queryParameters = queryParameters.set('map', <any>map);
}
Certainly, this will not lead to the desired result, as implicit toString
is called on pageable
and map
.
The idea is to have a method that will act according to the dynamic type of the variable
that should be added to queryParameters
:
let queryParameters = new HttpParams({encoder: this.encoder});
if (pageable !== undefined && pageable !== null) {
queryParameters = this.addToHttpParams(queryParameters, <any>pageable, 'pageable')
}
if (map !== undefined && map !== null) {
queryParameters = this.addToHttpParams(queryParameters, <any>map, 'map')
}
Proposed implementation of addToHttpParams
private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams {
if (typeof value === "object") {
httpParams = this.addToHttpParams1(httpParams, value);
} else {
httpParams = this.addToHttpParams1(httpParams, value, key);
}
return httpParams;
}
private addToHttpParams1(httpParams: HttpParams, value: any, key?: string): HttpParams {
if (typeof value === "object") {
if (this.isArrayLike(value)) {
(value as []).forEach( elem => httpParams = this.addToHttpParams1(httpParams, elem, key));
} else if (value instanceof Date) {
if (key != null) {
httpParams = httpParams.append(key,
(value as Date).toISOString().substr(0, 10));
} else {
throw Error("key may not be null if value is Date");
}
} else {
Object.keys(value).forEach( k => httpParams = this.addToHttpParams1(
httpParams, value[k], key != null ? `${key}.${k}` : k));
}
} else if (key != null) {
httpParams = httpParams.append(key, value);
} else {
throw Error("key may not be null if value is not object or array");
}
return httpParams;
}
private isArrayLike(obj: any): boolean {
return (Array.isArray(obj) ||
typeof obj === "object" &&
typeof (obj.length) === "number" &&
(obj.length === 0 ||
(obj.length > 0 && (obj.length - 1) in obj))
);
}
The implementation will flatten the top level object
to properties, but nested
are added with .
Syntax to the key (like in
toplevelProperty.propertyOfNestedObject
). Arrays and simple types are treated
like before (no change). The advantage of the new solution is that REST endpoints
from spring-data are supported out of the box.
Issue Analytics
- State:
- Created 4 years ago
- Comments:7 (3 by maintainers)
@kostadin-mandulov You are right, and I proposed it this way on purpose: This is the way (java) spring-data is supporting
Pageable
out of the box, and I was mainly interested inPageable
support.I never thought about several objects as query parameters. Flattening is one solution, but wrapping your objects in one root object is also possible!
@macjohnny thanks for confirming. I wouldn’t say it’s a bug, but a rather unfortuantely behaving feature 😅 No point in changing it, especially considering the amount of people affected. May be something to note going forward.
We have already worked around the issue by flattening the API parameters themselves (e.g.
object1Key
,object2Key
).