oip-common 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ng-package.json CHANGED
@@ -4,5 +4,16 @@
4
4
  "lib": {
5
5
  "entryFile": "src/public-api.ts"
6
6
  },
7
- "assets": [{ "input": "src/assets", "glob": "**/*", "output": "assets" }]
7
+ "assets": [
8
+ {
9
+ "input": "./src/assets",
10
+ "glob": "**/*",
11
+ "output": "assets"
12
+ },
13
+ {
14
+ "input": "./templates",
15
+ "glob": "**/*",
16
+ "output": "templates"
17
+ }
18
+ ]
8
19
  }
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
- {
2
- "name": "oip-common",
3
- "version": "0.0.2",
4
- "description": "A template for cross-platform web applications based on sakai-ng and primeNG",
5
- "main": "index.js",
6
- "scripts": {
7
- "pub": "npm publish"
8
- },
9
- "keywords": ["oip", "template"],
10
- "author": "Igor Tyulyakov aka g101k",
11
- "license": "MIT",
12
- "repository": {
13
- "type": "git",
14
- "url": "https://github.com/g10101k/Oip"
15
- }
16
- }
1
+ {
2
+ "name": "oip-common",
3
+ "version": "0.0.3",
4
+ "description": "A template for cross-platform web applications based on sakai-ng and primeNG",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "pub": "npm publish"
8
+ },
9
+ "keywords": [
10
+ "oip",
11
+ "template"
12
+ ],
13
+ "author": "Igor Tyulyakov aka g101k",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/g10101k/Oip.git"
18
+ }
19
+ }
@@ -60,7 +60,7 @@ export function convertToPrimeNgDateFormat(dateformat: string): string {
60
60
  }
61
61
 
62
62
  /**
63
- * Преобразовать все свойства объекта со строковыми датами в даты
63
+ * Convert all properties of an object with string dates to Date objects
64
64
  * @param obj
65
65
  */
66
66
  export function restoreDates(obj: any) {
@@ -86,7 +86,7 @@ export function restoreDates(obj: any) {
86
86
  }
87
87
 
88
88
  /**
89
- * Проверить, является ли строка датой в ISO формате
89
+ * Check if a string is a date in ISO format
90
90
  * @param str
91
91
  */
92
92
  export function isIsoDate(str: string) {
@@ -0,0 +1,30 @@
1
+ <%
2
+ const { utils, route, config, modelTypes } = it;
3
+ const { _, pascalCase, require } = utils;
4
+ const apiClassName = pascalCase(route.moduleName);
5
+ const routes = route.routes;
6
+ const dataContracts = _.map(modelTypes, "name");
7
+ %>
8
+
9
+ <% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import type { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %>
10
+
11
+ import { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
12
+ <% if (dataContracts.length) { %>
13
+ import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
14
+ <% } %>
15
+ import { Injectable } from "@angular/core";
16
+
17
+ @Injectable()
18
+ export class <%= apiClassName %><SecurityDataType = unknown><% if (!config.singleHttpClient) { %> extends HttpClient<SecurityDataType> <% } %> {
19
+ <% if(config.singleHttpClient) { %>
20
+ http: HttpClient<SecurityDataType>;
21
+
22
+ constructor (http: HttpClient<SecurityDataType>) {
23
+ this.http = http;
24
+ }
25
+ <% } %>
26
+
27
+ <% for (const route of routes) { %>
28
+ <%~ includeFile('./procedure-call.ejs', { ...it, route }) %>
29
+ <% } %>
30
+ }
@@ -0,0 +1,37 @@
1
+ <%
2
+ const { data, utils } = it;
3
+ const { formatDescription, require, _ } = utils;
4
+
5
+ const stringify = (value) => _.isObject(value) ? JSON.stringify(value) : _.isString(value) ? `"${value}"` : value
6
+
7
+ const jsDocLines = _.compact([
8
+ data.title,
9
+ data.description && formatDescription(data.description),
10
+ !_.isUndefined(data.deprecated) && data.deprecated && '@deprecated',
11
+ !_.isUndefined(data.format) && `@format ${data.format}`,
12
+ !_.isUndefined(data.minimum) && `@min ${data.minimum}`,
13
+ !_.isUndefined(data.multipleOf) && `@multipleOf ${data.multipleOf}`,
14
+ !_.isUndefined(data.exclusiveMinimum) && `@exclusiveMin ${data.exclusiveMinimum}`,
15
+ !_.isUndefined(data.maximum) && `@max ${data.maximum}`,
16
+ !_.isUndefined(data.minLength) && `@minLength ${data.minLength}`,
17
+ !_.isUndefined(data.maxLength) && `@maxLength ${data.maxLength}`,
18
+ !_.isUndefined(data.exclusiveMaximum) && `@exclusiveMax ${data.exclusiveMaximum}`,
19
+ !_.isUndefined(data.maxItems) && `@maxItems ${data.maxItems}`,
20
+ !_.isUndefined(data.minItems) && `@minItems ${data.minItems}`,
21
+ !_.isUndefined(data.uniqueItems) && `@uniqueItems ${data.uniqueItems}`,
22
+ !_.isUndefined(data.default) && `@default ${stringify(data.default)}`,
23
+ !_.isUndefined(data.pattern) && `@pattern ${data.pattern}`,
24
+ !_.isUndefined(data.example) && `@example ${stringify(data.example)}`
25
+ ]).join('\n').split('\n');
26
+ %>
27
+ <% if (jsDocLines.every(_.isEmpty)) { %>
28
+ <% } else if (jsDocLines.length === 1) { %>
29
+ /** <%~ jsDocLines[0] %> */
30
+ <% } else if (jsDocLines.length) { %>
31
+ /**
32
+ <% for (jsDocLine of jsDocLines) { %>
33
+ * <%~ jsDocLine %>
34
+
35
+ <% } %>
36
+ */
37
+ <% } %>
@@ -0,0 +1,52 @@
1
+ <%
2
+ const { modelTypes, utils, config } = it;
3
+ const { formatDescription, require, _, Ts } = utils;
4
+
5
+
6
+ const buildGenerics = (contract) => {
7
+ if (!contract.genericArgs || !contract.genericArgs.length) return '';
8
+
9
+ return '<' + contract.genericArgs.map(({ name, default: defaultType, extends: extendsType }) => {
10
+ return [
11
+ name,
12
+ extendsType && `extends ${extendsType}`,
13
+ defaultType && `= ${defaultType}`,
14
+ ].join('')
15
+ }).join(',') + '>'
16
+ }
17
+
18
+ const dataContractTemplates = {
19
+ enum: (contract) => {
20
+ return `enum ${contract.name} {\r\n${contract.content} \r\n }`;
21
+ },
22
+ interface: (contract) => {
23
+ return `interface ${contract.name}${buildGenerics(contract)} {\r\n${contract.content}}\r\n\r\n`;
24
+ },
25
+ type: (contract) => {
26
+ return `type ${contract.name}${buildGenerics(contract)} = ${contract.content}`;
27
+ },
28
+ }
29
+ %>
30
+
31
+ <% if (config.internalTemplateOptions.addUtilRequiredKeysType) { %>
32
+ type <%~ config.Ts.CodeGenKeyword.UtilRequiredKeys %><T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
33
+ <% } %>
34
+
35
+ <% for (const contract of modelTypes) {
36
+ if (contract.typeIdentifier === 'interface' && contract.rawContent && Array.isArray(contract.rawContent)) {
37
+ contract.rawContent.forEach((prop) => {
38
+ if (prop.format ==='date-time') {
39
+ prop.field = prop.field.replace(': string', ': Date');
40
+ }
41
+ })
42
+ let customContent = '';
43
+ contract.rawContent.forEach((prop) => {
44
+ if (prop.description) customContent += `/** ${prop.description} */ \r\n`;
45
+ customContent += `${prop.field};\r\n`;
46
+ })
47
+ contract.content = customContent;
48
+ }
49
+ %>
50
+ <%~ includeFile('./data-contract-jsdoc.ejs', { ...it, data: { ...contract, ...contract.typeData } }) %>
51
+ <%~ contract.internal ? '' : 'export'%> <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %>
52
+ <% } %>
@@ -0,0 +1,12 @@
1
+ <%
2
+ const { contract, utils, config } = it;
3
+ const { formatDescription, require, _ } = utils;
4
+ const { name, $content } = contract;
5
+ %>
6
+ <% if (config.generateUnionEnums) { %>
7
+ export type <%~ name %> = <%~ _.map($content, ({ value }) => value).join(" | ") %>
8
+ <% } else { %>
9
+ export enum <%~ name %> {
10
+ <%~ _.map($content, ({ key, value }) => `${key} = ${value}`).join(",\n") %>
11
+ }
12
+ <% } %>
@@ -0,0 +1,245 @@
1
+ <%
2
+ const { apiConfig, generateResponses, config } = it;
3
+ %>
4
+
5
+ import { SecurityService } from "oip-common";
6
+ import { LayoutService } from "oip-common";
7
+ import { inject, Injectable } from "@angular/core";
8
+
9
+ export type QueryParamsType = Record<string | number, any>;
10
+ export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
11
+
12
+ export interface FullRequestParams extends Omit<RequestInit, "body"> {
13
+ /** set parameter to `true` for call `securityWorker` for this request */
14
+ secure?: boolean;
15
+ /** request path */
16
+ path: string;
17
+ /** content type of request body */
18
+ type?: ContentType;
19
+ /** query params */
20
+ query?: QueryParamsType;
21
+ /** format of response (i.e. response.json() -> format: "json") */
22
+ format?: ResponseFormat;
23
+ /** request body */
24
+ body?: unknown;
25
+ /** base url */
26
+ baseUrl?: string;
27
+ /** request cancellation token */
28
+ cancelToken?: CancelToken;
29
+ }
30
+
31
+ export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">
32
+
33
+
34
+ export interface ApiConfig<SecurityDataType = unknown> {
35
+ baseUrl?: string;
36
+ baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
37
+ securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
38
+ customFetch?: typeof fetch;
39
+ }
40
+
41
+ export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
42
+ data: D;
43
+ error: E;
44
+ }
45
+
46
+ type CancelToken = Symbol | string | number;
47
+
48
+ export enum ContentType {
49
+ Json = "application/json",
50
+ FormData = "multipart/form-data",
51
+ UrlEncoded = "application/x-www-form-urlencoded",
52
+ Text = "text/plain",
53
+ }
54
+
55
+ @Injectable({ providedIn: 'root' })
56
+ export class HttpClient<SecurityDataType = unknown> {
57
+ protected securityService = inject(SecurityService);
58
+ protected layoutService = inject(LayoutService);
59
+ public baseUrl: string = "<%~ apiConfig.baseUrl %>";
60
+ private securityData: SecurityDataType | null = null;
61
+ private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"] =
62
+ (securityData) => ({
63
+ headers: {
64
+ "Accept-language": this.layoutService.language()
65
+ ? this.layoutService.language()
66
+ : 'en',
67
+ "X-Timezone": this.layoutService.timeZone(),
68
+ Authorization: `Bearer ${securityData}`,
69
+ },
70
+ });
71
+ private abortControllers = new Map<CancelToken, AbortController>();
72
+ private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
73
+
74
+ private baseApiParams: RequestParams = {
75
+ credentials: 'same-origin',
76
+ headers: {},
77
+ redirect: 'follow',
78
+ referrerPolicy: 'no-referrer',
79
+ }
80
+
81
+ constructor() {
82
+ this.securityService.getAccessToken().subscribe((token) => {
83
+ this.securityData = token;
84
+ });
85
+ }
86
+
87
+ public setSecurityData = (data: SecurityDataType | null) => {
88
+ this.securityData = data;
89
+ }
90
+
91
+ protected encodeQueryParam(key: string, value: any) {
92
+ const encodedKey = encodeURIComponent(key);
93
+ return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
94
+ }
95
+
96
+ protected addQueryParam(query: QueryParamsType, key: string) {
97
+ return this.encodeQueryParam(key, query[key]);
98
+ }
99
+
100
+ protected addArrayQueryParam(query: QueryParamsType, key: string) {
101
+ const value = query[key];
102
+ return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
103
+ }
104
+
105
+ protected toQueryString(rawQuery?: QueryParamsType): string {
106
+ const query = rawQuery || {};
107
+ const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
108
+ return keys
109
+ .map((key) =>
110
+ Array.isArray(query[key])
111
+ ? this.addArrayQueryParam(query, key)
112
+ : this.addQueryParam(query, key),
113
+ )
114
+ .join("&");
115
+ }
116
+
117
+ protected addQueryParams(rawQuery?: QueryParamsType): string {
118
+ const queryString = this.toQueryString(rawQuery);
119
+ return queryString ? `?${queryString}` : "";
120
+ }
121
+
122
+ private contentFormatters: Record<ContentType, (input: any) => any> = {
123
+ [ContentType.Json]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
124
+ [ContentType.Text]: (input:any) => input !== null && typeof input !== "string" ? JSON.stringify(input) : input,
125
+ [ContentType.FormData]: (input: any) =>
126
+ Object.keys(input || {}).reduce((formData, key) => {
127
+ const property = input[key];
128
+ formData.append(
129
+ key,
130
+ property instanceof Blob ?
131
+ property :
132
+ typeof property === "object" && property !== null ?
133
+ JSON.stringify(property) :
134
+ `${property}`
135
+ );
136
+ return formData;
137
+ }, new FormData()),
138
+ [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
139
+ }
140
+
141
+ protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
142
+ return {
143
+ ...this.baseApiParams,
144
+ ...params1,
145
+ ...(params2 || {}),
146
+ headers: {
147
+ ...(this.baseApiParams.headers || {}),
148
+ ...(params1.headers || {}),
149
+ ...((params2 && params2.headers) || {}),
150
+ },
151
+ };
152
+ }
153
+
154
+ protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
155
+ if (this.abortControllers.has(cancelToken)) {
156
+ const abortController = this.abortControllers.get(cancelToken);
157
+ if (abortController) {
158
+ return abortController.signal;
159
+ }
160
+ return void 0;
161
+ }
162
+
163
+ const abortController = new AbortController();
164
+ this.abortControllers.set(cancelToken, abortController);
165
+ return abortController.signal;
166
+ }
167
+
168
+ public abortRequest = (cancelToken: CancelToken) => {
169
+ const abortController = this.abortControllers.get(cancelToken)
170
+
171
+ if (abortController) {
172
+ abortController.abort();
173
+ this.abortControllers.delete(cancelToken);
174
+ }
175
+ }
176
+
177
+ public request = async <T = any, E = any>({
178
+ body,
179
+ secure,
180
+ path,
181
+ type,
182
+ query,
183
+ format,
184
+ baseUrl,
185
+ cancelToken,
186
+ ...params
187
+ <% if (config.unwrapResponseData) { %>
188
+ }: FullRequestParams): Promise<T> => {
189
+ <% } else { %>
190
+ }: FullRequestParams): Promise<HttpResponse<T, E>> => {
191
+ <% } %>
192
+ const secureParams = ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && this.securityWorker && await this.securityWorker(this.securityData)) || {};
193
+ const requestParams = this.mergeRequestParams(params, secureParams);
194
+ const queryString = query && this.toQueryString(query);
195
+ const payloadFormatter = this.contentFormatters[type || ContentType.Json];
196
+ let responseFormat = format || requestParams.format;
197
+
198
+ return this.customFetch(
199
+ `${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`,
200
+ {
201
+ ...requestParams,
202
+ headers: {
203
+ ...(requestParams.headers || {}),
204
+ ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
205
+ },
206
+ signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
207
+ body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
208
+ }
209
+ ).then(async (response) => {
210
+ const r = response.clone() as HttpResponse<T, E>;
211
+ r.data = (null as unknown) as T;
212
+ r.error = (null as unknown) as E;
213
+
214
+ if (typeof E !== undefined && responseFormat === undefined)
215
+ responseFormat = 'json';
216
+
217
+ const data = !responseFormat ? r : await response[responseFormat]()
218
+ .then((data) => {
219
+ if (r.ok) {
220
+ r.data = data;
221
+ } else {
222
+ r.error = data;
223
+ }
224
+ return r;
225
+ })
226
+ .catch((e) => {
227
+ r.error = e;
228
+ return r;
229
+ });
230
+
231
+ if (cancelToken) {
232
+ this.abortControllers.delete(cancelToken);
233
+ }
234
+
235
+ <% if (!config.disableThrowOnError) { %>
236
+ if (!response.ok) throw data;
237
+ <% } %>
238
+ <% if (config.unwrapResponseData) { %>
239
+ return data.data;
240
+ <% } else { %>
241
+ return data;
242
+ <% } %>
243
+ });
244
+ };
245
+ }
@@ -0,0 +1,10 @@
1
+ <%
2
+ const { contract, utils } = it;
3
+ const { formatDescription, require, _ } = utils;
4
+ %>
5
+ export interface <%~ contract.name %> {
6
+ <% for (const field of contract.$content) { %>
7
+ <%~ includeFile('./object-field-jsdoc.ejs', { ...it, field }) %>
8
+ <%~ field.name %><%~ field.isRequired ? '' : '?' %>: <%~ field.value %><%~ field.isNullable ? ' | null' : ''%>;
9
+ <% } %>
10
+ }
@@ -0,0 +1,28 @@
1
+ <%
2
+ const { field, utils } = it;
3
+ const { formatDescription, require, _ } = utils;
4
+
5
+ const comments = _.uniq(
6
+ _.compact([
7
+ field.title,
8
+ field.description,
9
+ field.deprecated && ` * @deprecated`,
10
+ !_.isUndefined(field.format) && `@format ${field.format}`,
11
+ !_.isUndefined(field.minimum) && `@min ${field.minimum}`,
12
+ !_.isUndefined(field.maximum) && `@max ${field.maximum}`,
13
+ !_.isUndefined(field.pattern) && `@pattern ${field.pattern}`,
14
+ !_.isUndefined(field.example) &&
15
+ `@example ${_.isObject(field.example) ? JSON.stringify(field.example) : field.example}`,
16
+ ]).reduce((acc, comment) => [...acc, ...comment.split(/\n/g)], []),
17
+ );
18
+ %>
19
+ <% if (comments.length === 1) { %>
20
+ /** <%~ comments[0] %> */
21
+ <% } else if (comments.length) { %>
22
+ /**
23
+ <% comments.forEach(comment => { %>
24
+ * <%~ comment %>
25
+
26
+ <% }) %>
27
+ */
28
+ <% } %>
@@ -0,0 +1,100 @@
1
+ <%
2
+ const { utils, route, config } = it;
3
+ const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route;
4
+ const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils;
5
+ const { parameters, path, method, payload, query, formData, security, requestParams } = route.request;
6
+ const { type, errorType, contentTypes } = route.response;
7
+ const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants;
8
+ const routeDocs = includeFile("./route-docs", { config, route, utils });
9
+ const queryName = (query && query.name) || "query";
10
+ const pathParams = _.values(parameters);
11
+ const pathParamsNames = _.map(pathParams, "name");
12
+
13
+ const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH;
14
+
15
+ const requestConfigParam = {
16
+ name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES),
17
+ optional: true,
18
+ type: "RequestParams",
19
+ defaultValue: "{}",
20
+ }
21
+
22
+ const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`;
23
+
24
+ const rawWrapperArgs = config.extractRequestParams ?
25
+ _.compact([
26
+ requestParams && {
27
+ name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName,
28
+ optional: false,
29
+ type: getInlineParseContent(requestParams),
30
+ },
31
+ ...(!requestParams ? pathParams : []),
32
+ payload,
33
+ requestConfigParam,
34
+ ]) :
35
+ _.compact([
36
+ ...pathParams,
37
+ query,
38
+ payload,
39
+ requestConfigParam,
40
+ ])
41
+
42
+ const wrapperArgs = _
43
+ // Sort by optionality
44
+ .sortBy(rawWrapperArgs, [o => o.optional])
45
+ .map(argToTmpl)
46
+ .join(', ')
47
+
48
+ // RequestParams["type"]
49
+ const requestContentKind = {
50
+ "JSON": "ContentType.Json",
51
+ "URL_ENCODED": "ContentType.UrlEncoded",
52
+ "FORM_DATA": "ContentType.FormData",
53
+ "TEXT": "ContentType.Text",
54
+ }
55
+ // RequestParams["format"]
56
+ const responseContentKind = {
57
+ "JSON": '"json"',
58
+ "IMAGE": '"blob"',
59
+ "FORM_DATA": isFetchTemplate ? '"formData"' : '"document"'
60
+ }
61
+
62
+ const bodyTmpl = _.get(payload, "name") || null;
63
+ const queryTmpl = (query != null && queryName) || null;
64
+ const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null;
65
+ const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null;
66
+ const securityTmpl = security ? 'true' : null;
67
+
68
+ const describeReturnType = () => {
69
+ if (!config.toJS) return "";
70
+
71
+ switch(config.httpClientType) {
72
+ case HTTP_CLIENT.AXIOS: {
73
+ return `Promise<AxiosResponse<${type}>>`
74
+ }
75
+ default: {
76
+ return `Promise<HttpResponse<${type}, ${errorType}>`
77
+ }
78
+ }
79
+ }
80
+
81
+ %>
82
+ /**
83
+ <%~ routeDocs.description %>
84
+
85
+ *<% /* Here you can add some other JSDoc tags */ %>
86
+
87
+ <%~ routeDocs.lines %>
88
+
89
+ */
90
+ <%~ route.routeName.usage %> = (<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> =>
91
+ <%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({
92
+ path: `<%~ path %>`,
93
+ method: '<%~ _.upperCase(method) %>',
94
+ <%~ queryTmpl ? `query: ${queryTmpl},` : '' %>
95
+ <%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %>
96
+ <%~ securityTmpl ? `secure: ${securityTmpl},` : '' %>
97
+ <%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %>
98
+ <%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %>
99
+ ...<%~ _.get(requestConfigParam, "name") %>,
100
+ })
@@ -0,0 +1,29 @@
1
+ <%
2
+ const { config, route, utils } = it;
3
+ const { _, formatDescription, fmtToJSDocLine, pascalCase, require } = utils;
4
+ const { raw, request, routeName } = route;
5
+
6
+ const jsDocDescription = raw.summary ?
7
+ ` * @description ${formatDescription(raw.summary, true)}` :
8
+ fmtToJSDocLine('No description', { eol: false });
9
+ const jsDocLines = _.compact([
10
+ _.size(raw.tags) && ` * @tags ${raw.tags.join(", ")}`,
11
+ ` * @name ${_.camelCase(routeName.usage)}`,
12
+ ` * @request ${_.upperCase(request.method)}:${raw.route}`,
13
+ raw.deprecated && ` * @deprecated`,
14
+ routeName.duplicate && ` * @originalName ${routeName.original}`,
15
+ routeName.duplicate && ` * @duplicate`,
16
+ request.security && ` * @secure`,
17
+ ...(config.generateResponses && raw.responsesTypes.length
18
+ ? raw.responsesTypes.map(
19
+ ({ type, status, description, isSuccess }) =>
20
+ ` * @response \`${status}\` \`${_.replace(_.replace(type, /\/\*/g, "\\*"), /\*\//g, "*\\")}\` ${description}`,
21
+ )
22
+ : []),
23
+ ]).map(str => str.trimEnd()).join("\n");
24
+
25
+ return {
26
+ description: jsDocDescription,
27
+ lines: jsDocLines,
28
+ }
29
+ %>
@@ -0,0 +1,42 @@
1
+ <%
2
+ const { routeInfo, utils } = it;
3
+ const {
4
+ operationId,
5
+ method,
6
+ route,
7
+ moduleName,
8
+ responsesTypes,
9
+ description,
10
+ tags,
11
+ summary,
12
+ pathArgs,
13
+ } = routeInfo;
14
+ const { _, fmtToJSDocLine, require } = utils;
15
+
16
+ const methodAliases = {
17
+ get: (pathName, hasPathInserts) => _.camelCase(`${pathName}`),
18
+ post: (pathName, hasPathInserts) => _.camelCase(`${pathName}`),
19
+ put: (pathName, hasPathInserts) => _.camelCase(`${pathName}`),
20
+ patch: (pathName, hasPathInserts) => _.camelCase(`${pathName}`),
21
+ delete: (pathName, hasPathInserts) => _.camelCase(`${pathName}`),
22
+ };
23
+
24
+ const createCustomOperationId = (method, route, moduleName) => {
25
+ const hasPathInserts = /\{(\w){1,}\}$/g.test(route);
26
+ const splittedRouteBySlash = _.compact(_.replace(route, /\{(\w){1,}\}/g, "").split("/"));
27
+ const routeParts = (splittedRouteBySlash.length > 1
28
+ ? splittedRouteBySlash.splice(1)
29
+ : splittedRouteBySlash
30
+ ).join("_");
31
+ return routeParts.length > 3 && methodAliases[method]
32
+ ? methodAliases[method](routeParts, hasPathInserts)
33
+ : _.camelCase(_.lowerCase(method) + "_" + [moduleName].join("_")) || "index";
34
+ };
35
+
36
+ if (operationId)
37
+ return _.camelCase(operationId);
38
+ if (route === "/")
39
+ return _.camelCase(`${_.lowerCase(method)}Root`);
40
+
41
+ return createCustomOperationId(method, route, moduleName);
42
+ %>
@@ -0,0 +1,23 @@
1
+ <%
2
+ const { route, utils, config } = it;
3
+ const { _, pascalCase, require } = utils;
4
+ const { query, payload, pathParams, headers } = route.request;
5
+
6
+ const routeDocs = includeFile("./route-docs", { config, route, utils });
7
+ const routeNamespace = pascalCase(route.routeName.usage);
8
+
9
+ %>
10
+
11
+ /**
12
+ <%~ routeDocs.description %>
13
+
14
+ <%~ routeDocs.lines %>
15
+
16
+ */
17
+ export namespace <%~ routeNamespace %> {
18
+ export type RequestParams = <%~ (pathParams && pathParams.type) || '{}' %>;
19
+ export type RequestQuery = <%~ (query && query.type) || '{}' %>;
20
+ export type RequestBody = <%~ (payload && payload.type) || 'never' %>;
21
+ export type RequestHeaders = <%~ (headers && headers.type) || '{}' %>;
22
+ export type ResponseBody = <%~ route.response.type %>;
23
+ }
@@ -0,0 +1,18 @@
1
+ <%
2
+ const { utils, config, route, modelTypes } = it;
3
+ const { _, pascalCase } = utils;
4
+ const { routes, moduleName } = route;
5
+ const dataContracts = config.modular ? _.map(modelTypes, "name") : [];
6
+
7
+ %>
8
+ <% if (dataContracts.length) { %>
9
+ import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
10
+ <% } %>
11
+
12
+ export namespace <%~ pascalCase(moduleName) %> {
13
+ <% for (const route of routes) { %>
14
+
15
+ <%~ includeFile('./route-type.ejs', { ...it, route }) %>
16
+
17
+ <% } %>
18
+ }
@@ -0,0 +1,15 @@
1
+ <%
2
+ const { contract, utils } = it;
3
+ const { formatDescription, require, _ } = utils;
4
+
5
+ %>
6
+ <% if (contract.$content.length) { %>
7
+ export type <%~ contract.name %> = {
8
+ <% for (const field of contract.$content) { %>
9
+ <%~ includeFile('./object-field-jsdoc.ejs', { ...it, field }) %>
10
+ <%~ field.field %>;
11
+ <% } %>
12
+ }<%~ utils.isNeedToAddNull(contract) ? ' | null' : ''%>
13
+ <% } else { %>
14
+ export type <%~ contract.name %> = Record<string, any>;
15
+ <% } %>