@devrev/typescript-sdk 1.0.2 → 1.1.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/LICENSE +21 -0
- package/README.md +31 -48
- package/package.json +17 -10
- package/.eslintignore +0 -2
- package/.github/workflows/npm-publish.yml +0 -27
- package/.prettierignore +0 -4
- package/.prettierrc +0 -15
- package/.vscode/extensions.json +0 -8
- package/.vscode/launch.json +0 -17
- package/.vscode/settings.json +0 -31
- package/USER_README.md +0 -16
- package/nodemon.json +0 -5
- package/src/auto-generated/beta/beta-devrev-sdk.ts +0 -1826
- package/src/auto-generated/internal/private-internal-devrev-sdk.ts +0 -54739
- package/src/auto-generated/public-devrev-sdk.ts +0 -4009
- package/src/beta-data-contracts.json +0 -3271
- package/src/client_setup.ts +0 -64
- package/src/private-internal-data-contracts.json +0 -98025
- package/src/public-data-contracts.json +0 -6922
- package/src/workflow/workflow.test.ts +0 -26
- package/src/workflow/workflow.ts +0 -54
- package/templates/README.md +0 -17
- package/templates/base/README.md +0 -8
- package/templates/base/data-contract-jsdoc.ejs +0 -37
- package/templates/base/data-contracts.ejs +0 -28
- package/templates/base/enum-data-contract.ejs +0 -12
- package/templates/base/http-client.ejs +0 -3
- package/templates/base/http-clients/axios-http-client.ejs +0 -138
- package/templates/base/http-clients/fetch-http-client.ejs +0 -224
- package/templates/base/interface-data-contract.ejs +0 -10
- package/templates/base/object-field-jsdoc.ejs +0 -28
- package/templates/base/route-docs.ejs +0 -30
- package/templates/base/route-name.ejs +0 -43
- package/templates/base/route-type.ejs +0 -22
- package/templates/base/type-data-contract.ejs +0 -15
- package/templates/default/README.md +0 -7
- package/templates/default/api.ejs +0 -64
- package/templates/default/procedure-call.ejs +0 -100
- package/templates/default/route-types.ejs +0 -26
- package/templates/modular/README.md +0 -7
- package/templates/modular/api.ejs +0 -28
- package/templates/modular/procedure-call.ejs +0 -100
- package/templates/modular/route-types.ejs +0 -18
- package/tsconfig.eslint.json +0 -4
- package/tsconfig.json +0 -25
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import * as client from '../client_setup';
|
|
2
|
-
import * as workflows from './workflow';
|
|
3
|
-
|
|
4
|
-
const devrevSDK = client.setup();
|
|
5
|
-
const betaSDK = client.setupBeta();
|
|
6
|
-
|
|
7
|
-
test('Test createTicket workflow', async () => {
|
|
8
|
-
const data = await workflows.createTicket(devrevSDK, 'Test item response', 'i-ekaterina.arshinova@devrev.ai', 'Default Product 1');
|
|
9
|
-
console.info(data);
|
|
10
|
-
expect([200, 201]).toContain(data);
|
|
11
|
-
});
|
|
12
|
-
test('Test createIssue workflow', async () => {
|
|
13
|
-
const data = await workflows.createIssue(devrevSDK, 'Test item new response', 'i-ekaterina.arshinova@devrev.ai', 'Default Product 1');
|
|
14
|
-
expect([200, 201]).toContain(data);
|
|
15
|
-
});
|
|
16
|
-
// unauthorized
|
|
17
|
-
test('Test create_conversation workflow', async () => {
|
|
18
|
-
const data = await workflows.createConversation(betaSDK);
|
|
19
|
-
expect([200, 201]).toContain(data);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('Test create_timeline workflow', async () => {
|
|
23
|
-
const data = await workflows.createComment(devrevSDK, 'TKT-1');
|
|
24
|
-
expect([200, 201]).toContain(data);
|
|
25
|
-
});
|
|
26
|
-
|
package/src/workflow/workflow.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import * as beta from '../auto-generated/beta/beta-devrev-sdk';
|
|
2
|
-
import * as sdk from '../auto-generated/public-devrev-sdk';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export async function createTicket(devrevSDK: sdk.Api<unknown>, title: string, user_email: string, part_name: string) {
|
|
7
|
-
const userListResponse = await devrevSDK.devUsersList({
|
|
8
|
-
email: [user_email],
|
|
9
|
-
});
|
|
10
|
-
let ownedBy = [];
|
|
11
|
-
ownedBy.push(userListResponse.data.dev_users[0].id);
|
|
12
|
-
const partListResponse = await devrevSDK.partsList({ name: [part_name] });
|
|
13
|
-
const ticketResponse = await devrevSDK.worksCreate({
|
|
14
|
-
type: sdk.WorkType.Ticket,
|
|
15
|
-
applies_to_part: partListResponse.data.parts[0].id,
|
|
16
|
-
owned_by: ownedBy,
|
|
17
|
-
title: title,
|
|
18
|
-
});
|
|
19
|
-
await devrevSDK.worksDelete({ id: ticketResponse.data.work.id });
|
|
20
|
-
return ticketResponse.status;
|
|
21
|
-
}
|
|
22
|
-
export async function createIssue(devrevSDK: sdk.Api<unknown>, title: string, user_email: string, part_name: string) {
|
|
23
|
-
const userListRespone = await devrevSDK.devUsersList({ email: [user_email] });
|
|
24
|
-
let ownedBy = [];
|
|
25
|
-
ownedBy.push(userListRespone.data.dev_users[0].id);
|
|
26
|
-
const partListResponse = await devrevSDK.partsList({ name: [part_name] });
|
|
27
|
-
const issueResponse = await devrevSDK.worksCreate({
|
|
28
|
-
type: sdk.WorkType.Issue,
|
|
29
|
-
applies_to_part: partListResponse.data.parts[0].id,
|
|
30
|
-
owned_by: ownedBy,
|
|
31
|
-
title: title,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
await devrevSDK.worksDelete({ id: issueResponse.data.work.id });
|
|
35
|
-
return issueResponse.status;
|
|
36
|
-
}
|
|
37
|
-
export async function createConversation(devrevSDK: beta.Api<unknown>) {
|
|
38
|
-
const conversationCreateResponse = await devrevSDK.conversationsList({
|
|
39
|
-
});
|
|
40
|
-
return conversationCreateResponse.status;
|
|
41
|
-
}
|
|
42
|
-
export async function createComment(devrevSDK: sdk.Api<unknown>, object: string) {
|
|
43
|
-
const timelineCreateResponse = await devrevSDK.timelineEntriesCreate({
|
|
44
|
-
type: sdk.TimelineEntriesCreateRequestType.TimelineComment,
|
|
45
|
-
object: object,
|
|
46
|
-
body: 'new test',
|
|
47
|
-
});
|
|
48
|
-
return timelineCreateResponse.status;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
package/templates/README.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# swagger-typescript-api
|
|
2
|
-
|
|
3
|
-
# templates
|
|
4
|
-
|
|
5
|
-
Templates:
|
|
6
|
-
- `api.ejs` - *(generates file)* Api class module (locations: [default](https://github.com/acacode/swagger-typescript-api/tree/next/templates/default/api.ejs), [modular](https://github.com/acacode/swagger-typescript-api/tree/next/templates/modular/api.ejs))
|
|
7
|
-
- `data-contracts.ejs` - *(generates file)* all types (data contracts) from swagger schema (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/data-contracts.ejs))
|
|
8
|
-
- `http-client.ejs` - *(generates file)* HttpClient class module (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/http-client.ejs))
|
|
9
|
-
- `procedure-call.ejs` - *(subtemplate)* route in Api class (locations: [default](https://github.com/acacode/swagger-typescript-api/tree/next/templates/default/procedure-call.ejs), [modular](https://github.com/acacode/swagger-typescript-api/tree/next/templates/modular/procedure-call.ejs))
|
|
10
|
-
- `route-docs.ejs` - *(generates file)* documentation for route in Api class (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/route-docs.ejs))
|
|
11
|
-
- `route-name.ejs` - *(subtemplate)* route name for route in Api class (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/route-name.ejs))
|
|
12
|
-
- `route-type.ejs` - *(`--route-types` option)* *(subtemplate)* (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/route-type.ejs))
|
|
13
|
-
- `route-types.ejs` - *(`--route-types` option)* *(subtemplate)* (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/route-types.ejs)) - `data-contract-jsdoc.ejs` - *(subtemplate)* generates JSDOC for data contract (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/data-contract-jsdoc.ejs))
|
|
14
|
-
|
|
15
|
-
[//]: # (- `enum-data-contract.ejs` - *(subtemplate)* generates `enum` data contract (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/enum-data-contract.ejs)))
|
|
16
|
-
[//]: # (- `interface-data-contract.ejs` - *(subtemplate)* generates `interface` data contract (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/interface-data-contract.ejs)))
|
|
17
|
-
[//]: # (- `type-data-contract.ejs` - *(subtemplate)* generates `type` data contract (locations: [base](https://github.com/acacode/swagger-typescript-api/tree/next/templates/base/type-data-contract.ejs)))
|
package/templates/base/README.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
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
|
-
<% } %>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
const { modelTypes, utils, config } = it;
|
|
3
|
-
const { formatDescription, require, _, Ts } = utils;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const dataContractTemplates = {
|
|
7
|
-
enum: (contract) => {
|
|
8
|
-
return `enum ${contract.name} {\r\n${contract.content} \r\n }`;
|
|
9
|
-
},
|
|
10
|
-
interface: (contract) => {
|
|
11
|
-
return `interface ${contract.name} {\r\n${contract.content}}`;
|
|
12
|
-
},
|
|
13
|
-
type: (contract) => {
|
|
14
|
-
return `type ${contract.name} = ${contract.content}`;
|
|
15
|
-
},
|
|
16
|
-
}
|
|
17
|
-
%>
|
|
18
|
-
|
|
19
|
-
<% if (config.internalTemplateOptions.addUtilRequiredKeysType) { %>
|
|
20
|
-
type <%~ config.Ts.CodeGenKeyword.UtilRequiredKeys %><T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
|
|
21
|
-
<% } %>
|
|
22
|
-
|
|
23
|
-
<% modelTypes.forEach((contract) => { %>
|
|
24
|
-
<%~ includeFile('@base/data-contract-jsdoc.ejs', { ...it, data: { ...contract, ...contract.typeData } }) %>
|
|
25
|
-
export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<% }) %>
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
<% } %>
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
const { apiConfig, generateResponses, config } = it;
|
|
3
|
-
%>
|
|
4
|
-
|
|
5
|
-
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, ResponseType, HeadersDefaults } from "axios";
|
|
6
|
-
|
|
7
|
-
export type QueryParamsType = Record<string | number, any>;
|
|
8
|
-
|
|
9
|
-
export interface FullRequestParams extends Omit<AxiosRequestConfig, "data" | "params" | "url" | "responseType"> {
|
|
10
|
-
/** set parameter to `true` for call `securityWorker` for this request */
|
|
11
|
-
secure?: boolean;
|
|
12
|
-
/** request path */
|
|
13
|
-
path: string;
|
|
14
|
-
/** content type of request body */
|
|
15
|
-
type?: ContentType;
|
|
16
|
-
/** query params */
|
|
17
|
-
query?: QueryParamsType;
|
|
18
|
-
/** format of response (i.e. response.json() -> format: "json") */
|
|
19
|
-
format?: ResponseType;
|
|
20
|
-
/** request body */
|
|
21
|
-
body?: unknown;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
|
|
25
|
-
|
|
26
|
-
export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequestConfig, "data" | "cancelToken"> {
|
|
27
|
-
securityWorker?: (securityData: SecurityDataType | null) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void;
|
|
28
|
-
secure?: boolean;
|
|
29
|
-
format?: ResponseType;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export enum ContentType {
|
|
33
|
-
Json = "application/json",
|
|
34
|
-
FormData = "multipart/form-data",
|
|
35
|
-
UrlEncoded = "application/x-www-form-urlencoded",
|
|
36
|
-
Text = "text/plain",
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class HttpClient<SecurityDataType = unknown> {
|
|
40
|
-
public instance: AxiosInstance;
|
|
41
|
-
private securityData: SecurityDataType | null = null;
|
|
42
|
-
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
|
|
43
|
-
private secure?: boolean;
|
|
44
|
-
private format?: ResponseType;
|
|
45
|
-
|
|
46
|
-
constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {
|
|
47
|
-
this.instance = axios.create({ ...axiosConfig, baseURL: axiosConfig.baseURL || "<%~ apiConfig.baseUrl %>" })
|
|
48
|
-
this.secure = secure;
|
|
49
|
-
this.format = format;
|
|
50
|
-
this.securityWorker = securityWorker;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public setSecurityData = (data: SecurityDataType | null) => {
|
|
54
|
-
this.securityData = data
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig {
|
|
58
|
-
const method = params1.method || (params2 && params2.method)
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
...this.instance.defaults,
|
|
62
|
-
...params1,
|
|
63
|
-
...(params2 || {}),
|
|
64
|
-
headers: {
|
|
65
|
-
...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}),
|
|
66
|
-
...(params1.headers || {}),
|
|
67
|
-
...((params2 && params2.headers) || {}),
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
protected stringifyFormItem(formItem: unknown) {
|
|
73
|
-
if (typeof formItem === "object" && formItem !== null) {
|
|
74
|
-
return JSON.stringify(formItem);
|
|
75
|
-
} else {
|
|
76
|
-
return `${formItem}`;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
protected createFormData(input: Record<string, unknown>): FormData {
|
|
81
|
-
return Object.keys(input || {}).reduce((formData, key) => {
|
|
82
|
-
const property = input[key];
|
|
83
|
-
const propertyContent: any[] = (property instanceof Array) ? property : [property]
|
|
84
|
-
|
|
85
|
-
for (const formItem of propertyContent) {
|
|
86
|
-
const isFileType = formItem instanceof Blob || formItem instanceof File;
|
|
87
|
-
formData.append(
|
|
88
|
-
key,
|
|
89
|
-
isFileType ? formItem : this.stringifyFormItem(formItem)
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return formData;
|
|
94
|
-
}, new FormData());
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public request = async <T = any, _E = any>({
|
|
98
|
-
secure,
|
|
99
|
-
path,
|
|
100
|
-
type,
|
|
101
|
-
query,
|
|
102
|
-
format,
|
|
103
|
-
body,
|
|
104
|
-
...params
|
|
105
|
-
<% if (config.unwrapResponseData) { %>
|
|
106
|
-
}: FullRequestParams): Promise<T> => {
|
|
107
|
-
<% } else { %>
|
|
108
|
-
}: FullRequestParams): Promise<AxiosResponse<T>> => {
|
|
109
|
-
<% } %>
|
|
110
|
-
const secureParams = ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {};
|
|
111
|
-
const requestParams = this.mergeRequestParams(params, secureParams);
|
|
112
|
-
const responseFormat = (format || this.format) || undefined;
|
|
113
|
-
|
|
114
|
-
if (type === ContentType.FormData && body && body !== null && typeof body === "object") {
|
|
115
|
-
body = this.createFormData(body as Record<string, unknown>);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (type === ContentType.Text && body && body !== null && typeof body !== "string") {
|
|
119
|
-
body = JSON.stringify(body);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return this.instance.request({
|
|
123
|
-
...requestParams,
|
|
124
|
-
headers: {
|
|
125
|
-
...(requestParams.headers || {}),
|
|
126
|
-
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
|
|
127
|
-
},
|
|
128
|
-
params: query,
|
|
129
|
-
responseType: responseFormat,
|
|
130
|
-
data: body,
|
|
131
|
-
url: path,
|
|
132
|
-
<% if (config.unwrapResponseData) { %>
|
|
133
|
-
}).then(response => response.data);
|
|
134
|
-
<% } else { %>
|
|
135
|
-
});
|
|
136
|
-
<% } %>
|
|
137
|
-
};
|
|
138
|
-
}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
const { apiConfig, generateResponses, config } = it;
|
|
3
|
-
%>
|
|
4
|
-
|
|
5
|
-
export type QueryParamsType = Record<string | number, any>;
|
|
6
|
-
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
|
|
7
|
-
|
|
8
|
-
export interface FullRequestParams extends Omit<RequestInit, "body"> {
|
|
9
|
-
/** set parameter to `true` for call `securityWorker` for this request */
|
|
10
|
-
secure?: boolean;
|
|
11
|
-
/** request path */
|
|
12
|
-
path: string;
|
|
13
|
-
/** content type of request body */
|
|
14
|
-
type?: ContentType;
|
|
15
|
-
/** query params */
|
|
16
|
-
query?: QueryParamsType;
|
|
17
|
-
/** format of response (i.e. response.json() -> format: "json") */
|
|
18
|
-
format?: ResponseFormat;
|
|
19
|
-
/** request body */
|
|
20
|
-
body?: unknown;
|
|
21
|
-
/** base url */
|
|
22
|
-
baseUrl?: string;
|
|
23
|
-
/** request cancellation token */
|
|
24
|
-
cancelToken?: CancelToken;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export interface ApiConfig<SecurityDataType = unknown> {
|
|
31
|
-
baseUrl?: string;
|
|
32
|
-
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
|
|
33
|
-
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
|
|
34
|
-
customFetch?: typeof fetch;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
|
|
38
|
-
data: D;
|
|
39
|
-
error: E;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
type CancelToken = Symbol | string | number;
|
|
43
|
-
|
|
44
|
-
export enum ContentType {
|
|
45
|
-
Json = "application/json",
|
|
46
|
-
FormData = "multipart/form-data",
|
|
47
|
-
UrlEncoded = "application/x-www-form-urlencoded",
|
|
48
|
-
Text = "text/plain",
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export class HttpClient<SecurityDataType = unknown> {
|
|
52
|
-
public baseUrl: string = "<%~ apiConfig.baseUrl %>";
|
|
53
|
-
private securityData: SecurityDataType | null = null;
|
|
54
|
-
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
|
|
55
|
-
private abortControllers = new Map<CancelToken, AbortController>();
|
|
56
|
-
private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
|
|
57
|
-
|
|
58
|
-
private baseApiParams: RequestParams = {
|
|
59
|
-
credentials: 'same-origin',
|
|
60
|
-
headers: {},
|
|
61
|
-
redirect: 'follow',
|
|
62
|
-
referrerPolicy: 'no-referrer',
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
|
|
66
|
-
Object.assign(this, apiConfig);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public setSecurityData = (data: SecurityDataType | null) => {
|
|
70
|
-
this.securityData = data;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
protected encodeQueryParam(key: string, value: any) {
|
|
74
|
-
const encodedKey = encodeURIComponent(key);
|
|
75
|
-
return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
protected addQueryParam(query: QueryParamsType, key: string) {
|
|
79
|
-
return this.encodeQueryParam(key, query[key]);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
protected addArrayQueryParam(query: QueryParamsType, key: string) {
|
|
83
|
-
const value = query[key];
|
|
84
|
-
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected toQueryString(rawQuery?: QueryParamsType): string {
|
|
88
|
-
const query = rawQuery || {};
|
|
89
|
-
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
|
|
90
|
-
return keys
|
|
91
|
-
.map((key) =>
|
|
92
|
-
Array.isArray(query[key])
|
|
93
|
-
? this.addArrayQueryParam(query, key)
|
|
94
|
-
: this.addQueryParam(query, key),
|
|
95
|
-
)
|
|
96
|
-
.join("&");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
protected addQueryParams(rawQuery?: QueryParamsType): string {
|
|
100
|
-
const queryString = this.toQueryString(rawQuery);
|
|
101
|
-
return queryString ? `?${queryString}` : "";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private contentFormatters: Record<ContentType, (input: any) => any> = {
|
|
105
|
-
[ContentType.Json]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
|
106
|
-
[ContentType.Text]: (input:any) => input !== null && typeof input !== "string" ? JSON.stringify(input) : input,
|
|
107
|
-
[ContentType.FormData]: (input: any) =>
|
|
108
|
-
Object.keys(input || {}).reduce((formData, key) => {
|
|
109
|
-
const property = input[key];
|
|
110
|
-
formData.append(
|
|
111
|
-
key,
|
|
112
|
-
property instanceof Blob ?
|
|
113
|
-
property :
|
|
114
|
-
typeof property === "object" && property !== null ?
|
|
115
|
-
JSON.stringify(property) :
|
|
116
|
-
`${property}`
|
|
117
|
-
);
|
|
118
|
-
return formData;
|
|
119
|
-
}, new FormData()),
|
|
120
|
-
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
|
|
124
|
-
return {
|
|
125
|
-
...this.baseApiParams,
|
|
126
|
-
...params1,
|
|
127
|
-
...(params2 || {}),
|
|
128
|
-
headers: {
|
|
129
|
-
...(this.baseApiParams.headers || {}),
|
|
130
|
-
...(params1.headers || {}),
|
|
131
|
-
...((params2 && params2.headers) || {}),
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
|
|
137
|
-
if (this.abortControllers.has(cancelToken)) {
|
|
138
|
-
const abortController = this.abortControllers.get(cancelToken);
|
|
139
|
-
if (abortController) {
|
|
140
|
-
return abortController.signal;
|
|
141
|
-
}
|
|
142
|
-
return void 0;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const abortController = new AbortController();
|
|
146
|
-
this.abortControllers.set(cancelToken, abortController);
|
|
147
|
-
return abortController.signal;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public abortRequest = (cancelToken: CancelToken) => {
|
|
151
|
-
const abortController = this.abortControllers.get(cancelToken)
|
|
152
|
-
|
|
153
|
-
if (abortController) {
|
|
154
|
-
abortController.abort();
|
|
155
|
-
this.abortControllers.delete(cancelToken);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public request = async <T = any, E = any>({
|
|
160
|
-
body,
|
|
161
|
-
secure,
|
|
162
|
-
path,
|
|
163
|
-
type,
|
|
164
|
-
query,
|
|
165
|
-
format,
|
|
166
|
-
baseUrl,
|
|
167
|
-
cancelToken,
|
|
168
|
-
...params
|
|
169
|
-
<% if (config.unwrapResponseData) { %>
|
|
170
|
-
}: FullRequestParams): Promise<T> => {
|
|
171
|
-
<% } else { %>
|
|
172
|
-
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
|
|
173
|
-
<% } %>
|
|
174
|
-
const secureParams = ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && this.securityWorker && await this.securityWorker(this.securityData)) || {};
|
|
175
|
-
const requestParams = this.mergeRequestParams(params, secureParams);
|
|
176
|
-
const queryString = query && this.toQueryString(query);
|
|
177
|
-
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
|
|
178
|
-
const responseFormat = format || requestParams.format;
|
|
179
|
-
|
|
180
|
-
return this.customFetch(
|
|
181
|
-
`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`,
|
|
182
|
-
{
|
|
183
|
-
...requestParams,
|
|
184
|
-
headers: {
|
|
185
|
-
...(requestParams.headers || {}),
|
|
186
|
-
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
|
|
187
|
-
},
|
|
188
|
-
signal: cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal,
|
|
189
|
-
body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
|
|
190
|
-
}
|
|
191
|
-
).then(async (response) => {
|
|
192
|
-
const r = response as HttpResponse<T, E>;
|
|
193
|
-
r.data = (null as unknown) as T;
|
|
194
|
-
r.error = (null as unknown) as E;
|
|
195
|
-
|
|
196
|
-
const data = !responseFormat ? r : await response[responseFormat]()
|
|
197
|
-
.then((data) => {
|
|
198
|
-
if (r.ok) {
|
|
199
|
-
r.data = data;
|
|
200
|
-
} else {
|
|
201
|
-
r.error = data;
|
|
202
|
-
}
|
|
203
|
-
return r;
|
|
204
|
-
})
|
|
205
|
-
.catch((e) => {
|
|
206
|
-
r.error = e;
|
|
207
|
-
return r;
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
if (cancelToken) {
|
|
211
|
-
this.abortControllers.delete(cancelToken);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
<% if (!config.disableThrowOnError) { %>
|
|
215
|
-
if (!response.ok) throw data;
|
|
216
|
-
<% } %>
|
|
217
|
-
<% if (config.unwrapResponseData) { %>
|
|
218
|
-
return data.data;
|
|
219
|
-
<% } else { %>
|
|
220
|
-
return data;
|
|
221
|
-
<% } %>
|
|
222
|
-
});
|
|
223
|
-
};
|
|
224
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
const { contract, utils } = it;
|
|
3
|
-
const { formatDescription, require, _ } = utils;
|
|
4
|
-
%>
|
|
5
|
-
export interface <%~ contract.name %> {
|
|
6
|
-
<% _.forEach(contract.$content, (field) => { %>
|
|
7
|
-
<%~ includeFile('@base/object-field-jsdoc.ejs', { ...it, field }) %>
|
|
8
|
-
<%~ field.name %><%~ field.isRequired ? '' : '?' %>: <%~ field.value %><%~ field.isNullable ? ' | null' : ''%>;
|
|
9
|
-
<% }) %>
|
|
10
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
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
|
-
<% } %>
|
|
@@ -1,30 +0,0 @@
|
|
|
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.description ?
|
|
7
|
-
` * @description ${formatDescription(raw.description, true)}` :
|
|
8
|
-
fmtToJSDocLine('No description', { eol: false });
|
|
9
|
-
const jsDocLines = _.compact([
|
|
10
|
-
_.size(raw.tags) && ` * @tags ${raw.tags.join(", ")}`,
|
|
11
|
-
` * @name ${pascalCase(routeName.usage)}`,
|
|
12
|
-
raw.summary && ` * @summary ${raw.summary}`,
|
|
13
|
-
` * @request ${_.upperCase(request.method)}:${raw.route}`,
|
|
14
|
-
raw.deprecated && ` * @deprecated`,
|
|
15
|
-
routeName.duplicate && ` * @originalName ${routeName.original}`,
|
|
16
|
-
routeName.duplicate && ` * @duplicate`,
|
|
17
|
-
request.security && ` * @secure`,
|
|
18
|
-
...(config.generateResponses && raw.responsesTypes.length
|
|
19
|
-
? raw.responsesTypes.map(
|
|
20
|
-
({ type, status, description, isSuccess }) =>
|
|
21
|
-
` * @response \`${status}\` \`${_.replace(_.replace(type, /\/\*/g, "\\*"), /\*\//g, "*\\")}\` ${description}`,
|
|
22
|
-
)
|
|
23
|
-
: []),
|
|
24
|
-
]).map(str => str.trimEnd()).join("\n");
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
description: jsDocDescription,
|
|
28
|
-
lines: jsDocLines,
|
|
29
|
-
}
|
|
30
|
-
%>
|
|
@@ -1,43 +0,0 @@
|
|
|
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) =>
|
|
18
|
-
_.camelCase(`${pathName}_${hasPathInserts ? "detail" : "list"}`),
|
|
19
|
-
post: (pathName, hasPathInserts) => _.camelCase(`${pathName}_create`),
|
|
20
|
-
put: (pathName, hasPathInserts) => _.camelCase(`${pathName}_update`),
|
|
21
|
-
patch: (pathName, hasPathInserts) => _.camelCase(`${pathName}_partial_update`),
|
|
22
|
-
delete: (pathName, hasPathInserts) => _.camelCase(`${pathName}_delete`),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const createCustomOperationId = (method, route, moduleName) => {
|
|
26
|
-
const hasPathInserts = /\{(\w){1,}\}/g.test(route);
|
|
27
|
-
const splitedRouteBySlash = _.compact(_.replace(route, /\{(\w){1,}\}/g, "").split("/"));
|
|
28
|
-
const routeParts = (splitedRouteBySlash.length > 1
|
|
29
|
-
? splitedRouteBySlash.splice(1)
|
|
30
|
-
: splitedRouteBySlash
|
|
31
|
-
).join("_");
|
|
32
|
-
return routeParts.length > 3 && methodAliases[method]
|
|
33
|
-
? methodAliases[method](routeParts, hasPathInserts)
|
|
34
|
-
: _.camelCase(_.lowerCase(method) + "_" + [moduleName].join("_")) || "index";
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
if (operationId)
|
|
38
|
-
return _.camelCase(operationId);
|
|
39
|
-
if (route === "/")
|
|
40
|
-
return _.camelCase(`${_.lowerCase(method)}Root`);
|
|
41
|
-
|
|
42
|
-
return createCustomOperationId(method, route, moduleName);
|
|
43
|
-
%>
|