Skip to content

Commit 70844fa

Browse files
Added Model Base for angular template (with mappers)
1 parent 6e5cb2b commit 70844fa

File tree

6 files changed

+211
-27
lines changed

6 files changed

+211
-27
lines changed

ApiBase/api-base.service.ts

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
HttpClient,
44
HttpHeaders,
55
HttpErrorResponse,
6+
HttpParams,
67
} from '@angular/common/http';
78
import { Observable } from 'rxjs/Observable';
89
import { Subscription } from 'rxjs/internal/Subscription';
@@ -21,6 +22,23 @@ export interface APIError {
2122
errorText: string;
2223
}
2324

25+
export interface HttpOptions {
26+
headers?:
27+
| HttpHeaders
28+
| {
29+
[header: string]: string | string[];
30+
};
31+
observe?: 'body';
32+
params?:
33+
| HttpParams
34+
| {
35+
[param: string]: string | string[];
36+
};
37+
reportProgress?: boolean;
38+
responseType?: 'json';
39+
withCredentials?: boolean;
40+
}
41+
2442
/**
2543
* Implementation of angular-http
2644
* https://angular.io/guide/http
@@ -83,50 +101,80 @@ export class ApiBaseService {
83101
this.serverURL = url;
84102
}
85103

86-
public doGet(url, params = null, data = null): Observable<any> {
87-
const options = this.getOptions(url, params, data);
104+
public doGet(
105+
url,
106+
params = null,
107+
data = null,
108+
customOptions?: Partial<HttpOptions>,
109+
): Observable<any> {
110+
const options = this.getOptions(customOptions);
88111
url = this.parseURLParams(url, params);
89112
// TODO: Add support to http.get<RESPONSETYPE>() or on .subscribe
90113
return this.http.get(this.serverURL + url, options).pipe(
91114
// retry(1),
92-
catchError((error) => this.handleError(error))
115+
catchError((error) => this.handleError(error)),
93116
);
94117
}
95118

96-
public doPost(url, params, data): Observable<any> {
97-
const options = this.getOptions(url, params, data);
119+
public doPost(
120+
url,
121+
params,
122+
data,
123+
customOptions?: Partial<HttpOptions>,
124+
): Observable<any> {
125+
const options = this.getOptions(customOptions);
98126
url = this.parseURLParams(url, params);
99127
return this.http
100128
.post(this.serverURL + url, data, options)
101129
.pipe(catchError((error) => this.handleError(error)));
102130
}
103131

104-
public doPut(url, params, data): Observable<any> {
105-
const options = this.getOptions(url, params, data);
132+
public doPut(
133+
url,
134+
params,
135+
data,
136+
customOptions?: Partial<HttpOptions>,
137+
): Observable<any> {
138+
const options = this.getOptions(customOptions);
106139
url = this.parseURLParams(url, params);
107140
return this.http
108141
.put(this.serverURL + url, data, options)
109142
.pipe(catchError((error) => this.handleError(error)));
110143
}
111144

112-
public doOptions(url, params, data = null): Observable<any> {
113-
const options = this.getOptions(url, params, data);
145+
public doOptions(
146+
url,
147+
params,
148+
data = null,
149+
customOptions?: Partial<HttpOptions>,
150+
): Observable<any> {
151+
const options = this.getOptions(customOptions);
114152
url = this.parseURLParams(url, params);
115153
return this.http
116154
.options(this.serverURL + url, options)
117155
.pipe(catchError((error) => this.handleError(error)));
118156
}
119157

120-
public doPatch(url, params, data = null): Observable<any> {
121-
const options = this.getOptions(url, params, data);
158+
public doPatch(
159+
url,
160+
params,
161+
data = null,
162+
customOptions?: Partial<HttpOptions>,
163+
): Observable<any> {
164+
const options = this.getOptions(customOptions);
122165
url = this.parseURLParams(url, params);
123166
return this.http
124167
.patch(this.serverURL + url, options)
125168
.pipe(catchError((error) => this.handleError(error)));
126169
}
127170

128-
public doDelete(url, params, data = null): Observable<any> {
129-
const options = this.getOptions(url, params, data);
171+
public doDelete(
172+
url,
173+
params,
174+
data = null,
175+
customOptions?: Partial<HttpOptions>,
176+
): Observable<any> {
177+
const options = this.getOptions(customOptions);
130178
url = this.parseURLParams(url, params);
131179
return this.http
132180
.delete(this.serverURL + url, options)
@@ -194,11 +242,8 @@ export class ApiBaseService {
194242
} as APIError);
195243
}
196244

197-
protected getOptions(url, params = null, data = null) {
198-
return {
199-
headers: this.httpHeaders,
200-
// params: this.parseURLParams(url, params),
201-
};
245+
protected getOptions(customOptions?: Partial<HttpOptions>) {
246+
return Object.assign({ headers: { ...this.httpHeaders } }, customOptions);
202247
}
203248

204249
/**

ApiBase/model-base.model.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
export interface ModelPropertyDef {
2+
type: any;
3+
}
4+
5+
export function recursiveInstance(classType, value: any): any {
6+
if (value?.__proto__?.constructor?.name === 'Array') {
7+
return value.map((element) => recursiveInstance(classType, element));
8+
} else {
9+
return new classType(value);
10+
}
11+
}
12+
13+
export function recursiveStringfy(model: any): any {
14+
if ((model as any)?.__proto__?.constructor?.name === 'Array') {
15+
return model.map((el) => recursiveStringfy(el));
16+
}
17+
const data = {};
18+
let paramsList;
19+
if (model.PARAMS_MAPPER) {
20+
paramsList = model.PARAMS_MAPPER;
21+
} else {
22+
paramsList = model;
23+
}
24+
for (const key in paramsList) {
25+
if (!model[key]) {
26+
continue;
27+
}
28+
const value = model[key];
29+
if (value?.__proto__?.constructor?.name === 'Array') {
30+
data[key] = value.map((el) => recursiveStringfy(el));
31+
} else if (value.json) {
32+
data[key] = value.json();
33+
} else {
34+
data[key] = value;
35+
}
36+
}
37+
return data;
38+
}
39+
40+
export abstract class ModelBase {
41+
protected readonly PARAMS_MAPPER: { [param: string]: ModelPropertyDef } = {};
42+
43+
parse(params: any): void {
44+
if (!params) {
45+
return;
46+
}
47+
48+
for (const paramKey in params) {
49+
if (!params[paramKey]) {
50+
continue;
51+
}
52+
const paramValue = params[paramKey];
53+
if (this.PARAMS_MAPPER[paramKey]) {
54+
this[paramKey] = this.parseParam(paramKey, paramValue);
55+
}
56+
}
57+
}
58+
59+
parseParam(key: string, value: any): any {
60+
const mapper = this.PARAMS_MAPPER[key];
61+
if (
62+
mapper &&
63+
typeof mapper?.type === 'function' &&
64+
typeof value !== 'function' &&
65+
value?.__proto__?.constructor?.name !== 'Array'
66+
) {
67+
return recursiveInstance(mapper.type, value);
68+
}
69+
return value;
70+
}
71+
72+
json(): any {
73+
return recursiveStringfy(this);
74+
}
75+
76+
toString(): string {
77+
return JSON.stringify(this.json());
78+
}
79+
}

src/models/api.model.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class ApiModel implements PhysycalFile {
1616
private _operationId: string;
1717

1818
queryParams: ModelAttributessModel;
19+
requestContentType?: string;
1920
requestBody: ModelAttributessModel;
2021
response: ModelAttributessModel;
2122

@@ -27,6 +28,14 @@ export class ApiModel implements PhysycalFile {
2728
return this.tags[0]?.length ? capital(this.tags[0], '', true) : null;
2829
}
2930

31+
get isFormRequest(): boolean {
32+
return this.requestContentType === 'multipart/form-data';
33+
}
34+
35+
get isResponsePrimitive(): boolean {
36+
return !this.response || this.response?.typeIsPrimitive;
37+
}
38+
3039
get models(): ModelAttributessModel[] {
3140
return [this.queryParams, this.requestBody, this.response].filter(
3241
(el) => !!el,

src/services/parsers/open-api-v3/api-parser.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export class APIParserService extends ParserBaseService {
8383
} else if (this.isRequestBodyObject(scheme)) {
8484
const keys = Object.keys(scheme.content);
8585
const mainKey = keys[0];
86+
apiModel.requestContentType = mainKey;
8687
apiModel.requestBody = this.parseSchema(
8788
scheme.content[mainKey].schema,
8889
getApiDefaultModelName(apiModel, 'Request'),

templates/angular2/api.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
// Angular dependences
55
import { Injectable } from '@angular/core';
66
import { Observable } from 'rxjs/Observable';
7+
import { map } from 'rxjs/operators';
78

89
// Main dependences
910
import { ApiBaseService } from '../../ApiBase/api-base.service';
11+
import { recursiveInstance, recursiveStringfy } from '../../ApiBase/model-base.model';
1012

1113
// Models dependences
1214
{{#dependences}}
@@ -32,10 +34,10 @@ export class {{groupName}}Service {
3234
{{#hasComments}}
3335
/**
3436
{{#description}}
35-
* {{description}}
37+
* {{{description}}}
3638
{{/description}}
3739
{{#example}}
38-
* @example {{example}}
40+
* @example {{{example}}}
3941
{{/example}}
4042
{{#deprecated}}
4143
* @deprecated
@@ -55,8 +57,22 @@ export class {{groupName}}Service {
5557
return this.apiService.do{{verb}}(
5658
'{{{ url }}}',
5759
{{#queryParamsType}}uriOptions{{/queryParamsType}}{{^queryParamsType}}null{{/queryParamsType}},
58-
{{#requestBodyType}}requestBody{{/requestBodyType}}{{^requestBodyType}}null{{/requestBodyType}}
59-
);
60+
{{^requestBodyType}}null{{/requestBodyType}}{{
61+
#requestBodyType}}{{
62+
^isFormRequest}}recursiveStringfy(requestBody){{/isFormRequest
63+
}}{{
64+
#isFormRequest}}this.apiService.generateFormData(recursiveStringfy(requestBody)){{/isFormRequest
65+
}}{{/requestBodyType}},
66+
{{!
67+
// Remove "null" line and uncomment line below
68+
// if you want to set "content-type" on headers
69+
}}
70+
null,
71+
// { {{#requestContentType}} headers: { 'Content-Type': '{{{requestContentType}}}' } {{/requestContentType}} },
72+
){{^isResponsePrimitive}}
73+
.pipe(
74+
map(response => recursiveInstance({{responseType}}, response))
75+
){{/isResponsePrimitive}};
6076
}
6177

6278
{{/apis}}

templates/angular2/model.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ModelBase } from '../../ApiBase/model-base.model';
12
{{#dependences}}
23
import { {{name}} } from './{{fileName}}';
34
{{/dependences}}
@@ -16,10 +17,11 @@ import { {{name}} } from './{{fileName}}';
1617
*/
1718
{{/hasComments}}
1819
{{#model}}
19-
export class {{ name }} {
20+
export interface {{ name }}I {
2021

21-
{{#attributes}}
22+
{{#attributes}}
2223
{{#hasComments}}
24+
{{#description}}
2325
/**
2426
{{#description}}
2527
* {{description}}
@@ -31,14 +33,46 @@ export class {{ name }} {
3133
* @deprecated
3234
{{/deprecated}}
3335
*/
36+
{{/description}}
3437
{{/hasComments}}
35-
{{name}}{{
36-
#isOptional}}?{{/isOptional
37-
}}: {{type}}{{
38+
{{name}}{{#isOptional}}?{{/isOptional}}: {{type}}{{#isArray}}[]{{/isArray}};
39+
40+
{{/attributes}}
41+
}
42+
43+
44+
export class {{ name }} extends ModelBase implements {{ name }}I {
45+
46+
protected readonly PARAMS_MAPPER = PARAMS_MAPPER;
47+
48+
{{#attributes}}
49+
private _{{name}}: {{type}}{{
3850
#arrayLevelsRepeater}}[]{{/arrayLevelsRepeater
3951
}}{{#default}} = {{default}}{{/default
4052
}};
53+
get {{name}}(): {{type}}{{#arrayLevelsRepeater}}[]{{/arrayLevelsRepeater}} {
54+
return this._{{name}};
55+
}
56+
set {{name}}(value: {{type}}{{#arrayLevelsRepeater}}[]{{/arrayLevelsRepeater}}) {
57+
this._{{name}} = this.parseParam('{{name}}', value);
58+
}
4159

4260
{{/attributes}}
61+
constructor(params?: {{name}}I) {
62+
super();
63+
this.parse(params);
64+
}
4365
}
4466
{{/model}}
67+
68+
const PARAMS_MAPPER = {
69+
{{#model}}
70+
{{#attributes}}
71+
{{name}}: {
72+
type: {{#typeIsPrimitive}}'{{/typeIsPrimitive}}{{
73+
type
74+
}}{{#typeIsPrimitive}}'{{/typeIsPrimitive}},
75+
},
76+
{{/attributes}}
77+
{{/model}}
78+
};

0 commit comments

Comments
 (0)