[REQ] [typescript-angular]: support for object as query parameters
Created by: aanno2
Is 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')
}
addToHttpParams
Proposed implementation of 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.