@weclapp/sdk 2.0.0-dev.53 → 2.0.0-dev.54
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/dist/cli.js +1263 -1367
- package/package.json +4 -6
package/dist/cli.js
CHANGED
|
@@ -1,1494 +1,1390 @@
|
|
|
1
|
-
import { fileURLToPath } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
import prettyMs from 'pretty-ms';
|
|
17
|
-
|
|
1
|
+
import { fileURLToPath } from "url";
|
|
2
|
+
import { dirname, resolve } from "path";
|
|
3
|
+
import { rolldown } from "rolldown";
|
|
4
|
+
import indentString from "indent-string";
|
|
5
|
+
import { camelCase, pascalCase, snakeCase } from "change-case";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { OpenAPIV3 } from "openapi-types";
|
|
8
|
+
import { createHash } from "crypto";
|
|
9
|
+
import { cp, mkdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
10
|
+
import { config } from "dotenv";
|
|
11
|
+
import yargs from "yargs";
|
|
12
|
+
import { hideBin } from "yargs/helpers";
|
|
13
|
+
import pkg from "../package.json" with { type: "json" };
|
|
14
|
+
import prettyMs from "pretty-ms";
|
|
15
|
+
//#region src/utils/currentDirname.ts
|
|
18
16
|
const currentDirname = () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
17
|
+
return fileURLToPath(new URL("..", import.meta.url));
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/target.ts
|
|
21
|
+
let Target = /* @__PURE__ */ function(Target) {
|
|
22
|
+
Target["BROWSER_PROMISES"] = "browser";
|
|
23
|
+
Target["BROWSER_RX"] = "browser.rx";
|
|
24
|
+
Target["NODE_PROMISES"] = "node";
|
|
25
|
+
Target["NODE_RX"] = "node.rx";
|
|
26
|
+
return Target;
|
|
27
|
+
}({});
|
|
30
28
|
const isNodeTarget = (target) => {
|
|
31
|
-
|
|
29
|
+
return target === Target.NODE_PROMISES || target === Target.NODE_RX;
|
|
32
30
|
};
|
|
33
31
|
const isRXTarget = (target) => {
|
|
34
|
-
|
|
32
|
+
return target === Target.BROWSER_RX || target === Target.NODE_RX;
|
|
35
33
|
};
|
|
36
34
|
const resolveResponseType = (target) => {
|
|
37
|
-
|
|
35
|
+
return isRXTarget(target) ? "Observable" : "Promise";
|
|
38
36
|
};
|
|
39
37
|
const resolveBinaryType = (target) => {
|
|
40
|
-
|
|
38
|
+
return isNodeTarget(target) ? "Buffer" : "Blob";
|
|
41
39
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/bundle.ts
|
|
42
|
+
const tsconfig = resolve(currentDirname(), "./tsconfig.sdk.json");
|
|
43
|
+
const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, "*"]));
|
|
45
44
|
const generateOutput = (config) => ({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
sourcemap: false,
|
|
46
|
+
banner: `/* weclapp sdk */`,
|
|
47
|
+
...config
|
|
49
48
|
});
|
|
50
49
|
const bundle = async (workingDirectory, target) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
|
|
50
|
+
const dist = (...paths) => resolve(workingDirectory, "dist", ...paths);
|
|
51
|
+
const src = (...paths) => resolve(workingDirectory, "src", ...paths);
|
|
52
|
+
const generateNodeOutput = () => [generateOutput({
|
|
53
|
+
file: dist("index.cjs"),
|
|
54
|
+
format: "cjs",
|
|
55
|
+
globals: resolveGlobals("node-fetch", "url")
|
|
56
|
+
}), generateOutput({
|
|
57
|
+
file: dist("index.js"),
|
|
58
|
+
format: "es",
|
|
59
|
+
globals: resolveGlobals("node-fetch", "url")
|
|
60
|
+
})];
|
|
61
|
+
const config = {
|
|
62
|
+
[Target.BROWSER_PROMISES]: () => ({
|
|
63
|
+
input: src("index.ts"),
|
|
64
|
+
resolve: { tsconfigFilename: tsconfig },
|
|
65
|
+
output: [generateOutput({
|
|
66
|
+
file: dist("index.js"),
|
|
67
|
+
format: "es",
|
|
68
|
+
minify: true
|
|
69
|
+
})]
|
|
70
|
+
}),
|
|
71
|
+
[Target.BROWSER_RX]: () => ({
|
|
72
|
+
input: src("index.ts"),
|
|
73
|
+
resolve: { tsconfigFilename: tsconfig },
|
|
74
|
+
external: ["rxjs"],
|
|
75
|
+
output: [generateOutput({
|
|
76
|
+
file: dist("index.js"),
|
|
77
|
+
format: "es",
|
|
78
|
+
minify: true,
|
|
79
|
+
globals: resolveGlobals("rxjs")
|
|
80
|
+
})]
|
|
81
|
+
}),
|
|
82
|
+
[Target.NODE_PROMISES]: () => ({
|
|
83
|
+
input: src("index.ts"),
|
|
84
|
+
resolve: { tsconfigFilename: tsconfig },
|
|
85
|
+
external: ["node-fetch", "url"],
|
|
86
|
+
output: generateNodeOutput().map((o) => ({
|
|
87
|
+
...o,
|
|
88
|
+
minify: true
|
|
89
|
+
}))
|
|
90
|
+
}),
|
|
91
|
+
[Target.NODE_RX]: () => ({
|
|
92
|
+
input: src("index.ts"),
|
|
93
|
+
resolve: { tsconfigFilename: tsconfig },
|
|
94
|
+
external: [
|
|
95
|
+
"node-fetch",
|
|
96
|
+
"url",
|
|
97
|
+
"rxjs"
|
|
98
|
+
],
|
|
99
|
+
output: generateNodeOutput().map((o) => ({
|
|
100
|
+
...o,
|
|
101
|
+
minify: true
|
|
102
|
+
}))
|
|
103
|
+
})
|
|
104
|
+
}[target]();
|
|
105
|
+
const result = await rolldown(config);
|
|
106
|
+
if (Array.isArray(config.output)) await Promise.all(config.output.map((o) => result.write(o)));
|
|
107
|
+
else if (config.output) await result.write(config.output);
|
|
108
|
+
await result.close();
|
|
109
|
+
};
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/ts/generateString.ts
|
|
112
112
|
const generateString = (str) => `'${str}'`;
|
|
113
113
|
const generateStrings = (str) => str.map(generateString);
|
|
114
|
-
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/ts/generateImport.ts
|
|
115
116
|
const generateImport = (opt) => {
|
|
116
|
-
|
|
117
|
-
return `import ${imports.filter(Boolean).join(', ')} from ${generateString(opt.src)};`;
|
|
117
|
+
return `import ${[opt.default, opt.imports?.length ? `{${opt.imports.join(", ")}}` : ""].filter(Boolean).join(", ")} from ${generateString(opt.src)};`;
|
|
118
118
|
};
|
|
119
|
-
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/utils/indent.ts
|
|
120
121
|
/**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
* Indents each line of the given string
|
|
123
|
+
* @param s String to indent
|
|
124
|
+
* @param level Indentation level
|
|
125
|
+
*/
|
|
125
126
|
const indent = (s, level = 1) => {
|
|
126
|
-
|
|
127
|
+
return indentString(s, 4 * level);
|
|
127
128
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.filter((v) => v.length)
|
|
132
|
-
.join('\n\n');
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/ts/generateStatements.ts
|
|
131
|
+
const generateStatements = (...statements) => statements.map((v) => v.trim()).filter((v) => v.length).join("\n\n");
|
|
133
132
|
const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
var
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
var
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/generator/01-base/static/globalConfig.ts.txt
|
|
135
|
+
var globalConfig_ts_default = "export default \"export type RequestPayloadMethod =\\n | 'GET'\\n | 'HEAD'\\n | 'POST'\\n | 'PUT'\\n | 'DELETE'\\n | 'CONNECT'\\n | 'OPTIONS'\\n | 'TRACE'\\n | 'PATCH';\\n\\nexport interface RequestPayload {\\n method?: RequestPayloadMethod;\\n query?: Record<string, any>;\\n body?: any;\\n unwrap?: boolean;\\n forceBlob?: boolean;\\n}\\n\\nexport interface ServiceConfig {\\n // Your API-Key, this is optional in the sense of if you omit this, and you're in a browser, the\\n // cookie-authentication (include-credentials) will be used.\\n key?: string;\\n\\n // Your domain, if omitted location.host will be used (browser env).\\n host?: string;\\n\\n // If you want to use https, defaults to location.protocol (browser env).\\n secure?: boolean;\\n\\n // If you want that some and count requests are bundled into multi requests.\\n multiRequest?: boolean;\\n\\n // If you want that the ignoreMissingProperties parameter to be set to true for every post request.\\n ignoreMissingProperties?: boolean;\\n\\n // Optional request/response interceptors.\\n interceptors?: {\\n // Takes the generated request, you can either return a new request,\\n // a response (which will be taken as \\\"the\\\" response) or nothing.\\n // The payload contains the raw input generated by the SDK.\\n request?: (\\n request: Request,\\n payload: RequestPayload\\n ) => Request | Response | void | Promise<Request | Response | void>;\\n\\n // Takes the response. This can either be the one from the server or an\\n // artificially-crafted one by the request interceptor.\\n response?: (response: Response) => Response | void | Promise<Response | void>;\\n };\\n\\n // Whether POST should be used instead of GET for some() and count() operations\\n usePost?: boolean;\\n}\\n\\nexport interface RequestOptions {\\n signal?: AbortSignal;\\n}\\n\\ntype ServiceConfigWithoutMultiRequest = Omit<ServiceConfig, 'multiRequest'>;\\n\\nlet globalConfig: ServiceConfig | undefined;\\nexport const getGlobalConfig = (): ServiceConfig | undefined => globalConfig;\\nexport const setGlobalConfig = (cfg?: ServiceConfig) => (globalConfig = cfg);\\n\\nexport const getHost = (cfg: ServiceConfig) => {\\n let host = cfg.host?.replace(/^https?:\\\\/\\\\//, '');\\n if (!host && typeof location !== 'undefined') {\\n host = location.host;\\n }\\n\\n if (!host) {\\n throw new Error('Please specify a host');\\n }\\n\\n return host;\\n};\\n\\nexport const getProtocol = (cfg: ServiceConfig) => {\\n const protocol =\\n cfg.secure !== undefined\\n ? cfg.secure\\n ? 'https:'\\n : 'http:'\\n : typeof location !== 'undefined'\\n ? location.protocol\\n : undefined;\\n\\n if (!protocol) {\\n throw new Error('Please specify a protocol (secure)');\\n }\\n\\n return protocol;\\n};\\n\";";
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/generator/01-base/static/multiRequest.ts.txt
|
|
138
|
+
var multiRequest_ts_default = "export default \"type RequestTask = {\\n uri: string;\\n resolve: (result: unknown) => void;\\n reject: (error: unknown) => void;\\n};\\n\\ntype BatchRequestTask = RequestTask & {\\n settled: boolean;\\n};\\n\\ntype MultiRequestResponse = {\\n status: number;\\n body: object;\\n};\\n\\nlet microtaskQueued: boolean = false;\\nconst tasksSet: Set<RequestTask> = new Set<RequestTask>();\\n\\nconst SQUARE_BRACKET_OPEN = '['.charCodeAt(0);\\nconst COMMA = ','.charCodeAt(0);\\nconst DECODER = new TextDecoder();\\n\\nconst readNextResponse = (bytes: Uint8Array<ArrayBuffer>) => {\\n let headerStart: number | undefined = undefined;\\n let commasSeen = 0;\\n\\n for (let i = 0; i < bytes.length; i++) {\\n const byte = bytes[i];\\n if (headerStart === undefined) {\\n if (byte === SQUARE_BRACKET_OPEN || byte === COMMA) {\\n headerStart = i + 1;\\n }\\n } else {\\n if (byte === COMMA) {\\n commasSeen++;\\n }\\n if (commasSeen === 2) {\\n const headerArrayString = `[${DECODER.decode(bytes.subarray(headerStart, i))}]`;\\n const [index, jsonLength] = JSON.parse(headerArrayString);\\n if (!(typeof index === 'number') || !(typeof jsonLength === 'number')) {\\n throw new Error(`unexpected header: ${headerArrayString}`);\\n }\\n\\n const endIndex = i + 1 + jsonLength;\\n if (endIndex > bytes.length) {\\n // not all bytes available yet\\n return undefined;\\n }\\n const jsonString = DECODER.decode(bytes.subarray(i + 1, endIndex));\\n const data = JSON.parse(jsonString) as MultiRequestResponse;\\n return {\\n index,\\n data,\\n remainingBytes: bytes.subarray(endIndex)\\n };\\n }\\n }\\n }\\n return undefined;\\n};\\n\\nconst fetchMultiRequest = async (requests: string[]) => {\\n const cfg = getGlobalConfig();\\n\\n if (!cfg) {\\n throw new Error(`ServiceConfig missing.`);\\n }\\n\\n const host = getHost(cfg);\\n const protocol = getProtocol(cfg);\\n\\n return await fetch(`${protocol}//${host}/webapp/api/v2/batch/query`, {\\n method: 'POST',\\n headers: {\\n 'Content-Type': 'application/json',\\n ...(cfg.key && { AuthenticationToken: cfg.key })\\n },\\n body: JSON.stringify({ requests })\\n });\\n};\\n\\nconst rejectTasks = (tasks: BatchRequestTask[], error: unknown) => {\\n for (const task of tasks) {\\n if (!task.settled) {\\n task.reject(error);\\n }\\n }\\n};\\n\\nconst processStream = (\\n { value: chunk, done }: ReadableStreamReadResult<Uint8Array>,\\n remainingBytes: Uint8Array,\\n reader: ReadableStreamDefaultReader<Uint8Array>,\\n tasks: BatchRequestTask[]\\n) => {\\n if (done) {\\n return;\\n }\\n if (chunk) {\\n let bytes = new Uint8Array(remainingBytes.length + chunk.length);\\n bytes.set(remainingBytes);\\n bytes.set(chunk, remainingBytes.length);\\n\\n while (bytes.length) {\\n const result = readNextResponse(bytes);\\n if (!result) {\\n break;\\n }\\n const task = tasks[result.index];\\n if (result.data.status >= 100 && result.data.status < 400) {\\n task.resolve({\\n ...result.data.body\\n });\\n } else {\\n task.reject({\\n ...result.data.body\\n });\\n }\\n task.settled = true;\\n bytes = result.remainingBytes;\\n }\\n reader\\n .read()\\n .then((readResult) => processStream(readResult, bytes, reader, tasks))\\n .catch((error) => rejectTasks(tasks, error));\\n }\\n};\\n\\nconst batch = async (tasks: BatchRequestTask[]) => {\\n try {\\n const requests = tasks.map(({ uri }) => uri);\\n const resp = await fetchMultiRequest(requests);\\n const reader = resp.body?.getReader();\\n\\n if (!reader) {\\n throw new Error('Stream reader is undefined');\\n }\\n reader\\n .read()\\n .then((readResult) => processStream(readResult, new Uint8Array(0), reader, tasks))\\n .catch((error) => rejectTasks(tasks, error));\\n } catch (e) {\\n rejectTasks(tasks, e);\\n throw e;\\n }\\n};\\n\\nconst addTask = (task: RequestTask) => {\\n tasksSet.add(task);\\n\\n if (!microtaskQueued) {\\n queueMicrotask(() => {\\n microtaskQueued = false;\\n if (tasksSet.size > 0) {\\n const batchTasks = Array.from(tasksSet).map((task) => ({ ...task, settled: false }));\\n void batch(batchTasks);\\n tasksSet.clear();\\n }\\n });\\n microtaskQueued = true;\\n }\\n};\\n\\nconst addRequest = (uri: string) => new Promise((resolve, reject) => addTask({ uri, resolve, reject }));\\n\";";
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/generator/01-base/static/queriesWithFilter.ts.txt
|
|
141
|
+
var queriesWithFilter_ts_default = "export default \"export type EqualityOperator = 'EQ' | 'NE';\\n\\nexport type ComparisonOperator =\\n | 'LT'\\n | 'GT'\\n | 'LE'\\n | 'GE'\\n | 'LIKE'\\n | 'ILIKE'\\n | 'NOT_LIKE'\\n | 'NOT_ILIKE'\\n | 'IEQ'\\n | 'NOT_IEQ';\\n\\nexport type ArrayOperator = 'IN' | 'NOT_IN';\\n\\nexport type Operator = EqualityOperator | ComparisonOperator | ArrayOperator;\\n\\nexport type MapOperators<T> = { [K in EqualityOperator]?: T | null } & { [K in ComparisonOperator]?: T } & {\\n [K in ArrayOperator]?: T[];\\n};\\n\\nexport type QueryFilter<T> = {\\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\\n ? U extends Record<any, any>\\n ? QueryFilter<U>\\n : MapOperators<U>\\n : T[P] extends Record<any, any> | undefined\\n ? QueryFilter<T[P]>\\n : MapOperators<T[P]>;\\n};\\n\\nexport type CountQuery<F> = {\\n filter?: QueryFilter<F>;\\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\\n};\\n\\nexport type SomeQuery<E, F, I, P> = {\\n serializeNulls?: boolean;\\n include?: QuerySelect<I>;\\n properties?: P;\\n filter?: QueryFilter<F> & CustomAttributeFilter;\\n select?: QuerySelect<E>;\\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\\n sort?: Sort<E>[];\\n pagination?: Pagination;\\n};\\n\\nconst equality: string[] = ['EQ', 'NE', 'IEQ', 'NOT_IEQ'];\\n\\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\\n\\nconst array: string[] = ['IN', 'NOT_IN'];\\n\\nconst filterMap: Record<Operator, string> = {\\n EQ: 'eq',\\n NE: 'ne',\\n LT: 'lt',\\n GT: 'gt',\\n LE: 'le',\\n GE: 'ge',\\n LIKE: 'like',\\n NOT_LIKE: 'notlike',\\n ILIKE: 'ilike',\\n NOT_ILIKE: 'notilike',\\n IN: 'in',\\n NOT_IN: 'notin',\\n IEQ: 'ieq',\\n NOT_IEQ: 'notieq'\\n};\\n\\nconst flattenCustomAttributes = (obj: CustomAttributeFilter = {}): [string, string][] => {\\n const entries: [string, string][] = [];\\n\\n for (const [id, filter] of Object.entries(obj)) {\\n const key = `customAttribute${id}`;\\n\\n if (typeof filter === 'object') {\\n for (const [prop, value] of Object.entries(filter)) {\\n entries.push([`${key}.${prop}-eq`, String(value)]);\\n }\\n } else if (filter !== undefined) {\\n entries.push([`${key}-eq`, String(filter)]);\\n }\\n }\\n\\n return entries;\\n};\\n\\nconst flatten = (obj: QueryFilter<any> = {}): [string, string][] => {\\n const entries: [string, string][] = [];\\n\\n for (const [prop, propValue] of Object.entries(obj)) {\\n for (const [filter, value] of Object.entries(propValue as object)) {\\n if (value === undefined) continue;\\n\\n if (simple.includes(filter) || array.includes(filter)) {\\n if (value === null && equality.includes(filter)) {\\n entries.push([`${prop}-${filter === 'EQ' ? 'null' : 'notnull'}`, '']);\\n } else {\\n entries.push([`${prop}-${filterMap[filter as Operator]}`, value]);\\n }\\n } else {\\n entries.push(\\n ...(flatten(propValue as QueryFilter<any>).map((v) => [`${prop}.${v[0]}`, v[1]]) as [string, string][])\\n );\\n break;\\n }\\n }\\n }\\n\\n return entries;\\n};\\n\\nconst flattenFilter = (obj: QueryFilter<any> = {}): Record<string, string> => {\\n const filter: [string, any][] = [],\\n customAttributes: [string, any][] = [];\\n\\n Object.entries(obj).forEach((value) => {\\n (value[0].match(/^\\\\d+$/) ? customAttributes : filter).push(value);\\n });\\n\\n return Object.fromEntries([\\n ...flatten(Object.fromEntries(filter)),\\n ...flattenCustomAttributes(Object.fromEntries(customAttributes) as CustomAttributeFilter)\\n ]);\\n};\\n\\nconst flattenOrFilter = (obj: QueryFilter<any>[] = []): Record<string, string> => {\\n const entries: [string, any][] = [];\\n\\n for (let i = 0; i < obj.length; i++) {\\n entries.push(...(flatten(obj[i]).map((v) => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]));\\n }\\n\\n return Object.fromEntries(entries);\\n};\\n\\nconst _count = (\\n cfg: ServiceConfig | undefined,\\n endpoint: string,\\n query?: CountQuery<any> & { params?: Record<any, any> },\\n requestOptions?: RequestOptions\\n) =>\\n{\\n const usePost = cfg?.usePost ?? globalConfig?.usePost\\n const payload = {\\n ...flattenFilter(query?.filter),\\n ...flattenOrFilter(query?.or),\\n ...query?.params\\n }\\n\\n return wrapResponse(() =>\\n raw(cfg, endpoint, {\\n method: usePost ? 'POST' : 'GET',\\n unwrap: true,\\n ...(usePost ? { body: payload } : { query: payload })\\n }, requestOptions)\\n )\\n };\\n\\nconst _some = (\\n cfg: ServiceConfig | undefined,\\n endpoint: string,\\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> },\\n requestOptions?: RequestOptions\\n) =>\\n{\\n const usePost = cfg?.usePost ?? globalConfig?.usePost\\n const payload = {\\n serializeNulls: query?.serializeNulls,\\n additionalProperties: query?.properties?.join(','),\\n properties: query?.select ? flattenSelect(query.select).join(',') : undefined,\\n includeReferencedEntities: query?.include ? Object.keys(query.include).join(',') : undefined,\\n ...flattenOrFilter(query?.or),\\n ...flattenFilter(query?.filter),\\n ...flattenSort(query?.sort),\\n ...query?.params,\\n ...query?.pagination\\n }\\n\\n return wrapResponse(() =>\\n raw(cfg, usePost ? `${endpoint}/query` : endpoint, {\\n method: usePost ? 'POST' : 'GET',\\n ...(usePost ? { body: payload } : { query: payload })\\n }, requestOptions).then((data) => ({\\n entities: data.result,\\n references: data.referencedEntities ?? {},\\n properties: data.additionalProperties ?? {}\\n }))\\n )\\n };\\n\";";
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/generator/01-base/static/queriesWithQueryLanguage.ts.txt
|
|
144
|
+
var queriesWithQueryLanguage_ts_default = "export default \"export type ComparisonOperator =\\n | 'EQ'\\n | 'NE'\\n | 'LT'\\n | 'GT'\\n | 'LE'\\n | 'GE'\\n | 'LIKE';\\n\\nexport type LengthOperator = 'LENGTH';\\n\\nexport type ArrayOperator = 'IN';\\n\\nexport type NullOperator = 'NULL';\\n\\nexport type Operator = ComparisonOperator | ArrayOperator | NullOperator;\\n\\nexport type ModifierFunction = 'LOWER' | 'TRIM';\\n\\n// use globalThis to use typescript's 'Pick', since the SDK contains an interface which is also called 'Pick'\\nexport type RequireAtLeastOne<T> = {\\n [K in keyof T]-?: Required<globalThis.Pick<T, K>> & Partial<Omit<T, K>>;\\n}[keyof T];\\n\\nexport type LengthExpr = {\\n [K in LengthOperator]: {\\n [K in Exclude<ComparisonOperator, 'LIKE'>]?: number\\n }\\n};\\n\\nexport type LengthExprWithModifier =\\n LengthExpr &\\n { [K in ComparisonOperator]?: never } &\\n { [K in ArrayOperator]?: never } &\\n { [K in NullOperator]?: never } &\\n { [K in ModifierFunction]?: boolean };\\n\\nexport type FilterExpr<T> =\\n { [K in ComparisonOperator]?: T } &\\n { [K in ArrayOperator]?: T[] } &\\n { [K in keyof LengthExpr]?: never };\\n\\nexport type FilterExprWithoutNull<T> =\\n RequireAtLeastOne<FilterExpr<T>> &\\n { [K in NullOperator]?: never } &\\n { [K in ModifierFunction]?: boolean };\\n\\nexport type FilterExprWithNull<T> =\\n FilterExpr<T> &\\n { [K in NullOperator]?: boolean} &\\n { [K in ModifierFunction]?: never };\\n\\nexport type MapOperators<T> = LengthExprWithModifier | FilterExprWithoutNull<T> | FilterExprWithNull<T>;\\n\\nexport type SingleFilterExpr<T> = {\\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\\n ? U extends Record<any, any>\\n ? SingleFilterExpr<U> | { NOT?: SingleFilterExpr<U> }\\n : MapOperators<U>\\n : T[P] extends Record<any, any> | undefined\\n ? SingleFilterExpr<T[P]> | { NOT?: SingleFilterExpr<T[P]> }\\n : MapOperators<T[P]>;\\n};\\n\\nexport type QueryFilter<T> = SingleFilterExpr<T> & {\\n OR?: QueryFilter<T>[];\\n AND?: QueryFilter<T>[];\\n NOT?: QueryFilter<T>;\\n};\\n\\nexport type CountQuery<F> = {\\n where?: QueryFilter<F>;\\n};\\n\\nexport type SomeQuery<E, F, I, P> = {\\n serializeNulls?: boolean;\\n include?: QuerySelect<I>;\\n properties?: P;\\n where?: QueryFilter<F>;\\n select?: QuerySelect<E>;\\n sort?: Sort<E>[];\\n pagination?: Pagination;\\n};\\n\\nconst comparisonOperatorList: ComparisonOperator[] = [\\n 'EQ',\\n 'NE',\\n 'LT',\\n 'GT',\\n 'LE',\\n 'GE',\\n 'LIKE'\\n];\\n\\nconst comparisonOperatorMap: Record<Operator, string> = {\\n EQ: '=',\\n NE: '!=',\\n LT: '<',\\n GT: '>',\\n LE: '<=',\\n GE: '>=',\\n LIKE: '~',\\n IN: 'in',\\n NULL: 'null'\\n};\\n\\nconst modifierFunctionList: ModifierFunction[] = ['LOWER', 'TRIM'];\\n\\nconst flattenWhere = (\\n obj: QueryFilter<any> = {},\\n nestedPaths: string[]\\n): string[] => {\\n const entries: string[] = [];\\n for (const [prop, propValue] of Object.entries(obj)) {\\n const setModifiers = findAllModifierFunctions(propValue ?? {}, modifierFunctionList).filter(\\n (modifier) => modifier[1]\\n );\\n if (prop === 'OR') {\\n const flattedOr: string[][] = [];\\n for (let i = 0; i < (obj.OR?.length ?? 0); i++) {\\n flattedOr.push(flattenWhere(obj.OR?.[i], nestedPaths));\\n }\\n entries.push(\\n `(${flattedOr\\n .map((x) => {\\n const joined = x.join(' and ');\\n\\n if (x.length > 1) {\\n return `(${joined})`;\\n } else {\\n return joined;\\n }\\n })\\n .join(' or ')})`\\n );\\n } else if (prop === 'AND') {\\n const flattedAnd: string[][] = [];\\n for (let i = 0; i < (obj.AND?.length ?? 0); i++) {\\n flattedAnd.push(flattenWhere(obj.AND?.[i], nestedPaths));\\n }\\n entries.push(\\n `(${flattedAnd\\n .map((x) => {\\n const joined = x.join(' and ');\\n\\n if (x.length > 1) {\\n return `(${joined})`;\\n } else {\\n return joined;\\n }\\n })\\n .join(' and ')})`\\n );\\n } else if (prop === 'NOT') {\\n const flattedNot = flattenWhere(obj.NOT, nestedPaths);\\n entries.push(\\n `not ${flattedNot.length > 1 ? '(' : ''}${flattedNot.join(' and ')}${flattedNot.length > 1 ? ')' : ''}`\\n );\\n } else if (propValue) {\\n for (const [operator, value] of Object.entries(propValue)) {\\n if (value === undefined) continue;\\n if (comparisonOperatorList.includes(operator as ComparisonOperator)) {\\n entries.push(\\n `${setModifiers.reduce(\\n (acc, [first]) => `${first.toLowerCase()}(${acc})`,\\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\\n )} ${comparisonOperatorMap[operator as Operator]} ${\\n typeof value === 'string' ? JSON.stringify(value) : value\\n }`\\n );\\n } else if ((operator as Operator) === 'NULL') {\\n entries.push(\\n `${!value ? 'not ' : ''}${nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')} ${comparisonOperatorMap[operator as Operator]}`\\n );\\n } else if ((operator as Operator) === 'IN') {\\n if(value.length === 0) {\\n entries.push('1 = 0')\\n } else {\\n entries.push(\\n `${setModifiers.reduce(\\n (acc, [first]) => `${first.toLowerCase()}(${acc})`,\\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\\n )} ${comparisonOperatorMap[operator as Operator]} [${value.map((v: string | number) =>\\n typeof v === 'string' ? JSON.stringify(v) : v\\n )}]`\\n );\\n }\\n } else if ((operator as LengthOperator) === 'LENGTH') {\\n const lengthProp = `length(${setModifiers.reduce(\\n (acc, [first]) => `${first.toLowerCase()}(${acc})`,\\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\\n )})`;\\n entries.push(...flattenWhere({ [lengthProp]: value } as QueryFilter<any>, []));\\n } else if (\\n !modifierFunctionList.includes(operator as ModifierFunction)\\n && typeof value === 'object'\\n ) {\\n entries.push(\\n ...flattenWhere(propValue as QueryFilter<any>, [\\n ...nestedPaths,\\n prop\\n ])\\n );\\n break;\\n }\\n }\\n }\\n }\\n return entries;\\n};\\n\\nconst assembleFilterParam = (\\n obj: QueryFilter<any> = {}\\n): Record<string, string> => {\\n const flattedFilter = flattenWhere(obj, []);\\n return flattedFilter.length ? { filter: flattedFilter.join(' and ') } : {};\\n};\\n\\nconst findAllModifierFunctions = (\\n obj: Record<string, any>,\\n types: ModifierFunction[]\\n) => {\\n const result: [string, any][] = [];\\n for (const key in obj) {\\n if (types.includes(key as ModifierFunction)) {\\n result[types.indexOf(key as ModifierFunction)] = [key, obj[key]];\\n }\\n }\\n return result.filter((modifierTuple) => modifierTuple);\\n};\\n\\nconst _count = (\\n cfg: ServiceConfig | undefined,\\n endpoint: string,\\n query?: CountQuery<any> & { params?: Record<any, any> },\\n requestOptions?: RequestOptions\\n) =>\\n {\\n const usePost = cfg?.usePost ?? globalConfig?.usePost\\n const payload = {\\n ...assembleFilterParam(query?.where),\\n ...query?.params\\n }\\n\\n return wrapResponse(() =>\\n raw(cfg, endpoint, {\\n unwrap: true,\\n method: usePost ? 'POST' : 'GET',\\n ...(usePost ? { body: payload } : { query: payload })\\n }, requestOptions)\\n )\\n };\\n\\nconst _some = (\\n cfg: ServiceConfig | undefined,\\n endpoint: string,\\n query?: SomeQuery<any, any, any, any> & { params?: Record<any, any> },\\n requestOptions?: RequestOptions\\n) =>\\n {\\n const usePost = cfg?.usePost ?? globalConfig?.usePost\\n const payload = {\\n serializeNulls: query?.serializeNulls,\\n additionalProperties: query?.properties?.join(','),\\n properties: query?.select\\n ? flattenSelect(query.select).join(',')\\n : undefined,\\n includeReferencedEntities: query?.include\\n ? Object.keys(query.include).join(',')\\n : undefined,\\n ...assembleFilterParam(query?.where),\\n ...flattenSort(query?.sort),\\n ...query?.params,\\n ...query?.pagination\\n }\\n\\n return wrapResponse(() =>\\n raw(cfg, usePost ? `${endpoint}/query` : endpoint, {\\n method: usePost ? 'POST' : 'GET',\\n ...(usePost ? { body: payload } : { query: payload })\\n }, requestOptions).then((data) => ({\\n entities: data.result,\\n references: data.referencedEntities ?? {},\\n properties: data.additionalProperties ?? {}\\n }))\\n )\\n };\\n\";";
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/generator/01-base/static/root.ts.txt
|
|
147
|
+
var root_ts_default = "export default \"export const raw = async (\\n cfg: ServiceConfig | undefined,\\n endpoint: string,\\n payload: RequestPayload = {},\\n requestOptions?: RequestOptions\\n): Promise<any> => {\\n if (!cfg && !globalConfig) {\\n throw new Error(`ServiceConfig missing.`);\\n }\\n\\n const localCfg = {\\n ...globalConfig,\\n ...cfg,\\n interceptors: { ...globalConfig?.interceptors, ...cfg?.interceptors }\\n };\\n\\n const isBinaryData = payload.body instanceof resolveBinaryObject();\\n const params = new URLSearchParams(Object.entries(payload.query ?? {}).filter((v) => v[1] !== undefined)\\n .map(([key, value]) => [key, typeof value === 'string' ? value : JSON.stringify(value)])\\n );\\n\\n const protocol = getProtocol(localCfg);\\n\\n const interceptRequest = localCfg.interceptors?.request ?? ((v) => v);\\n const interceptResponse = localCfg.interceptors?.response ?? ((v) => v);\\n\\n const host = getHost(localCfg);\\n\\n let data;\\n if (!cfg && localCfg.multiRequest) {\\n let ep = endpoint;\\n if (endpoint.startsWith('/')) {\\n ep = endpoint.replace('/', '');\\n }\\n data = await addRequest(`${ep}?${params}`);\\n } else {\\n const request = new Request(`${protocol}//${host}/webapp/api/v${apiVersion}${endpoint}?${params}`, {\\n ...(payload.body && {\\n body: isBinaryData\\n ? payload.body\\n : JSON.stringify(payload.body, (_key, value) => (value === undefined ? null : value))\\n }),\\n ...(!localCfg.key && { credentials: 'same-origin' }),\\n method: payload.method ?? 'get',\\n headers: {\\n Accept: 'application/json',\\n ...(localCfg.key && { AuthenticationToken: localCfg.key }),\\n ...(!isBinaryData && { 'Content-Type': 'application/json' })\\n }\\n });\\n let res = (await interceptRequest(request, payload)) ?? request;\\n if (!(res instanceof Response)) {\\n res = requestOptions?.signal ? await fetch(res, { signal: requestOptions.signal } ) : await fetch(res);\\n }\\n res = (await interceptResponse(res)) ?? res;\\n\\n if (res.ok) {\\n data =\\n payload.forceBlob || !res.headers?.get('content-type')?.includes('application/json')\\n ? await res.blob()\\n : await res.json();\\n } else {\\n data = res.headers?.get('content-type')?.includes('application/json')\\n ? await res.json()\\n : res;\\n }\\n\\n // Check if response was successful\\n if (!res.ok) {\\n return Promise.reject(data);\\n }\\n }\\n\\n return payload.unwrap ? data.result : data;\\n};\\n\\nconst _remove = (\\n cfg: ServiceConfigWithoutMultiRequest | undefined,\\n endpoint: string,\\n { dryRun = false }: RemoveQuery = {},\\n requestOptions?: RequestOptions\\n) =>\\n wrapResponse(() =>\\n raw({ ...cfg, multiRequest: false }, endpoint, {\\n method: 'DELETE',\\n query: { dryRun }\\n }, requestOptions).then(() => undefined)\\n );\\n\\nconst _create = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, data: any, requestOptions?: RequestOptions) =>\\n wrapResponse(() =>\\n raw({ ...cfg, multiRequest: false }, endpoint, {\\n method: 'POST',\\n body: data\\n }, requestOptions)\\n );\\n\\nconst _update = (\\n cfg: ServiceConfigWithoutMultiRequest | undefined,\\n endpoint: string,\\n data: any,\\n { ignoreMissingProperties, dryRun = false }: UpdateQuery = {},\\n requestOptions?: RequestOptions\\n) =>\\n wrapResponse(() =>\\n raw({ ...cfg, multiRequest: false }, endpoint, {\\n method: 'PUT',\\n body: data,\\n query: {\\n ignoreMissingProperties:\\n ignoreMissingProperties ?? cfg?.ignoreMissingProperties ?? globalConfig?.ignoreMissingProperties,\\n dryRun\\n }\\n }, requestOptions)\\n );\\n\\nconst _generic = (\\n cfg: ServiceConfigWithoutMultiRequest | undefined,\\n method: RequestPayloadMethod,\\n endpoint: string,\\n payload?: GenericQuery<any, any>,\\n forceBlob?: boolean,\\n requestOptions?: RequestOptions\\n) =>\\n{\\n const usePost = cfg?.usePost ?? globalConfig?.usePost\\n return wrapResponse(() =>\\n raw({ ...cfg, multiRequest: false }, endpoint, {\\n method: usePost ? 'POST' : method,\\n forceBlob,\\n ...(usePost && method === 'GET' ?\\n { body: { ...payload?.params } }\\n :\\n { body: payload?.body, query: payload?.params } )\\n }, requestOptions))\\n};\\n\";";
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/generator/01-base/static/unique.ts.txt
|
|
150
|
+
var unique_ts_default = "export default \"const _unique = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, query?: UniqueQuery, requestOptions?: RequestOptions) =>\\n wrapResponse(() => raw({ ...cfg, multiRequest: false }, endpoint, { query }, requestOptions));\\n\";";
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/generator/01-base/static/types.ts.txt
|
|
153
|
+
var types_ts_default = "export default \"export type DeepPartial<T> = T extends object\\n ? {\\n [P in keyof T]?: DeepPartial<T[P]>;\\n }\\n : T;\\n\\nexport type Sort<T> = {\\n [K in keyof T]?: {\\n [V in keyof T]?: V extends K\\n ? T[V] extends Array<infer U> | undefined\\n ? U extends object\\n ? Sort<U>\\n : never\\n : T[V] extends object | undefined\\n ? Sort<T[V]>\\n : 'asc' | 'desc'\\n : never;\\n };\\n}[keyof T];\\n\\nexport type CustomAttributeFilter = {\\n [K in number]:| string | number | boolean | { id: string } | { entityName: string; entityId: string };\\n};\\n\\nexport type QuerySelect<T> = {\\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\\n ? QuerySelect<U> | boolean\\n : T[P] extends Record<any, any> | undefined\\n ? QuerySelect<T[P]> | boolean\\n : boolean;\\n};\\n\\nexport type Select<T, Q extends QuerySelect<T> | undefined> =\\n Q extends QuerySelect<T>\\n ? {\\n // Filter out excluded properties beforehand\\n [P in keyof T as Q[P] extends boolean ? P : Q[P] extends object ? P : never]: // Property\\n Q[P] extends true\\n ? T[P]\\n : // Array\\n T[P] extends Array<infer U>\\n ? Select<U, Q[P] & QuerySelect<any>>[]\\n : // Object\\n T[P] extends Record<any, any>\\n ? Select<T[P], Q[P] & QuerySelect<any>>\\n : never;\\n }\\n : undefined;\\n\\nexport type MapKeys<T, S extends Record<keyof T, string>> = {\\n [K in keyof T as S[K]]: T[K];\\n};\\n\\nexport type ValueOf<T> = T[keyof T];\\n\\nexport type Pagination = {\\n page: number;\\n pageSize: number;\\n};\\n\\nexport type UniqueQuery = {\\n serializeNulls?: boolean;\\n};\\n\\nexport type SomeQueryReturn<E, R, P> = {\\n entities: E[];\\n references?: R;\\n properties?: P[];\\n};\\n\\nexport type GenericQuery<P, B> = {\\n params?: P;\\n body?: B;\\n};\\n\\nexport type UpdateQuery = {\\n ignoreMissingProperties?: boolean;\\n dryRun?: boolean;\\n};\\n\\nexport type RemoveQuery = {\\n dryRun?: boolean;\\n};\\n\\nexport type WEntityPropertyMeta =\\n | {\\n type: 'string';\\n format?: 'decimal' | 'html' | 'email' | 'password';\\n maxLength?: number;\\n pattern?: string;\\n entity?: WEntity;\\n service?: WService;\\n }\\n | {\\n type: 'integer';\\n format: 'int32' | 'int64' | 'duration' | 'date' | 'timestamp';\\n }\\n | { type: 'array'; format: 'reference'; entity: WEntity; service?: WService }\\n | { type: 'array'; format: 'reference'; enum: WEnum }\\n | { type: 'array'; format: 'string' }\\n | { type: 'number'; format: 'double' }\\n | { type: 'reference'; entity: WEntity }\\n | { type: 'reference'; enum: WEnum }\\n | { type: 'boolean' }\\n | { type: 'object' };\\n\";";
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/generator/01-base/static/utils.ts.txt
|
|
156
|
+
var utils_ts_default = "export default \"const flattenSelect = (obj: Select<any, any> = {}): string[] => {\\n const entries: string[] = [];\\n\\n for (const [prop, value] of Object.entries(obj)) {\\n if (typeof value === 'object' && value) {\\n entries.push(...flattenSelect(value).map((v) => `${prop}.${v}`));\\n } else if (value) {\\n entries.push(prop);\\n }\\n }\\n\\n return entries;\\n};\\n\\nexport const flattenSort = (obj: Sort<any>[] = []): { sort?: string } => {\\n const flatten = (obj: Sort<any>, base = ''): string | undefined => {\\n const [key, value] = Object.entries(obj ?? {})[0] ?? [];\\n\\n if (key && value) {\\n const path = base + key;\\n\\n if (typeof value === 'object') {\\n return flatten(value, path ? `${path}.` : '');\\n } else if (['asc', 'desc'].includes(value)) {\\n return `${value === 'desc' ? '-' : ''}${path}`;\\n }\\n }\\n\\n return undefined;\\n };\\n\\n const sorts = obj.map((v) => flatten(v)).filter(Boolean);\\n return sorts.length ? { sort: sorts.join(',') } : {};\\n};\\n\";";
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/generator/01-base/index.ts
|
|
151
159
|
const resolveImports = (target) => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
const imports = [];
|
|
161
|
+
if (isRXTarget(target)) imports.push(generateImport({
|
|
162
|
+
src: "rxjs",
|
|
163
|
+
imports: ["defer", "Observable"]
|
|
164
|
+
}));
|
|
165
|
+
return imports.join("\n");
|
|
166
|
+
};
|
|
167
|
+
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? "defer" : "(v: (...args: any[]) => any) => v()"};`;
|
|
159
168
|
const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
|
|
160
169
|
const generateBase = (apiVersion, { target, useQueryLanguage, generateUnique }) => {
|
|
161
|
-
|
|
170
|
+
return generateStatements(resolveImports(target), `const apiVersion = ${apiVersion}`, resolveMappings(target), resolveBinaryClass(target), globalConfig_ts_default, types_ts_default, utils_ts_default, root_ts_default, useQueryLanguage ? queriesWithQueryLanguage_ts_default : queriesWithFilter_ts_default, generateUnique ? unique_ts_default : "", multiRequest_ts_default);
|
|
162
171
|
};
|
|
163
|
-
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/ts/generateEnum.ts
|
|
164
174
|
const transformKey = (s) => snakeCase(s).toUpperCase();
|
|
165
175
|
const generateEnum = (name, values) => {
|
|
166
|
-
|
|
167
|
-
return `export enum ${name} {\n${props}\n}`;
|
|
176
|
+
return `export enum ${name} {\n${indent(values.map((v) => `${transformKey(v)} = ${generateString(v)}`).join(",\n"))}\n}`;
|
|
168
177
|
};
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/utils/case.ts
|
|
171
180
|
const loosePascalCase = (str) => str[0].toUpperCase() + str.slice(1);
|
|
172
|
-
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/utils/openapi/guards.ts
|
|
173
183
|
const isObject = (v) => {
|
|
174
|
-
|
|
184
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
175
185
|
};
|
|
176
186
|
const isParameterObject = (v) => {
|
|
177
|
-
|
|
187
|
+
return isObject(v) && typeof v.name === "string" && typeof v.in === "string";
|
|
178
188
|
};
|
|
179
189
|
const isReferenceObject = (v) => {
|
|
180
|
-
|
|
190
|
+
return isObject(v) && typeof v.$ref === "string";
|
|
181
191
|
};
|
|
182
192
|
const isObjectSchemaObject = (v) => {
|
|
183
|
-
|
|
193
|
+
return isObject(v) && v.type === "object" && isObject(v.properties);
|
|
184
194
|
};
|
|
185
195
|
const isEnumSchemaObject = (v) => {
|
|
186
|
-
|
|
196
|
+
return isObject(v) && v.type === "string" && Array.isArray(v.enum);
|
|
187
197
|
};
|
|
188
198
|
const isArraySchemaObject = (v) => {
|
|
189
|
-
|
|
199
|
+
return isObject(v) && v.type === "array" && typeof v.items === "object";
|
|
190
200
|
};
|
|
191
201
|
const isResponseObject = (v) => {
|
|
192
|
-
|
|
202
|
+
return isObject(v) && typeof v.description === "string";
|
|
193
203
|
};
|
|
194
|
-
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/generator/02-enums/index.ts
|
|
195
206
|
const generateEnums = (context) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
else {
|
|
219
|
-
return joined;
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
|
|
207
|
+
const enums = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const [schemaName, schema] of context.schemas) if (isEnumSchemaObject(schema)) {
|
|
209
|
+
const enumName = loosePascalCase(schemaName);
|
|
210
|
+
if (!enums.has(enumName)) enums.set(enumName, {
|
|
211
|
+
name: enumName,
|
|
212
|
+
properties: schema.enum,
|
|
213
|
+
source: generateEnum(enumName, schema.enum)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return enums;
|
|
217
|
+
};
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/utils/concat.ts
|
|
220
|
+
const concat = (strings, separator = ", ", maxLength = 80) => {
|
|
221
|
+
const joined = strings.join(separator);
|
|
222
|
+
if (joined.length > maxLength) {
|
|
223
|
+
const length = strings.length - 1;
|
|
224
|
+
return `\n${indent(strings.map((value, index) => index === length ? value : `${(value + separator).trim()}\n`).join(""))}\n`;
|
|
225
|
+
} else return joined;
|
|
226
|
+
};
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/utils/openapi/convertParametersToSchemaObject.ts
|
|
223
229
|
const convertParametersToSchemaObject = (parameters) => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
|
|
230
|
+
const properties = [];
|
|
231
|
+
const required = [];
|
|
232
|
+
for (const param of parameters) if (param.in === "query" && param.schema) {
|
|
233
|
+
properties.push([param.name, param.schema]);
|
|
234
|
+
if (param.required) required.push(param.name);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: Object.fromEntries(properties),
|
|
239
|
+
required
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/utils/openapi/convertToTypeScriptType.ts
|
|
240
244
|
const createReferenceType = (value) => ({
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
type: "reference",
|
|
246
|
+
toString: () => loosePascalCase(value)
|
|
243
247
|
});
|
|
244
248
|
const createRawType = (value) => ({
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
type: "raw",
|
|
250
|
+
toString: () => value
|
|
247
251
|
});
|
|
248
252
|
const createArrayType = (value) => ({
|
|
249
|
-
|
|
250
|
-
|
|
253
|
+
type: "array",
|
|
254
|
+
toString: () => `(${value.toString()})[]`
|
|
251
255
|
});
|
|
252
256
|
const createTupleType = (value) => ({
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
type: "tuple",
|
|
258
|
+
toString: () => concat([...new Set(value.map((v) => typeof v === "string" ? `'${v}'` : v.toString()))], " | ")
|
|
255
259
|
});
|
|
256
260
|
const createObjectType = (value, required = []) => ({
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const value = v[1];
|
|
270
|
-
const isRequired = required.includes(name) ||
|
|
271
|
-
propertyPropagationOption === 'force' ||
|
|
272
|
-
(value.type === 'object' && !value.isFullyOptional() && propertyPropagationOption === 'propagate');
|
|
273
|
-
return `${name + (isRequired ? '' : '?')}: ${value.toString()};`;
|
|
274
|
-
});
|
|
275
|
-
return properties.length ? `{\n${indent(properties.join('\n'))}\n}` : '{}';
|
|
276
|
-
}
|
|
261
|
+
type: "object",
|
|
262
|
+
isFullyOptional: () => {
|
|
263
|
+
return !required.length && Object.values(value).filter((v) => v?.type === "object").every((v) => v.isFullyOptional());
|
|
264
|
+
},
|
|
265
|
+
toString: (propertyPropagationOption = "ignore") => {
|
|
266
|
+
const properties = Object.entries(value).filter((v) => v[1]).map((v) => {
|
|
267
|
+
const name = v[0];
|
|
268
|
+
const value = v[1];
|
|
269
|
+
return `${name + (required.includes(name) || propertyPropagationOption === "force" || value.type === "object" && !value.isFullyOptional() && propertyPropagationOption === "propagate" ? "" : "?")}: ${value.toString()};`;
|
|
270
|
+
});
|
|
271
|
+
return properties.length ? `{\n${indent(properties.join("\n"))}\n}` : "{}";
|
|
272
|
+
}
|
|
277
273
|
});
|
|
278
274
|
const getRefName = (obj) => {
|
|
279
|
-
|
|
275
|
+
return obj.$ref.replace(/.*\//, "");
|
|
280
276
|
};
|
|
281
277
|
const convertToTypeScriptType = (schema) => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
case 'boolean':
|
|
301
|
-
return createRawType('boolean');
|
|
302
|
-
case 'object': {
|
|
303
|
-
const { properties = {}, required = [] } = schema;
|
|
304
|
-
return createObjectType(Object.fromEntries(Object.entries(properties).map(([prop, propSchema]) => [prop, convertToTypeScriptType(propSchema)])), required);
|
|
305
|
-
}
|
|
306
|
-
case 'array':
|
|
307
|
-
return createArrayType(convertToTypeScriptType(schema.items));
|
|
308
|
-
default:
|
|
309
|
-
return createRawType('unknown');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
|
|
278
|
+
if (Array.isArray(schema)) return convertToTypeScriptType(convertParametersToSchemaObject(schema));
|
|
279
|
+
else if (isReferenceObject(schema)) return createReferenceType(getRefName(schema));
|
|
280
|
+
else switch (schema.type) {
|
|
281
|
+
case "integer":
|
|
282
|
+
case "number": return createRawType("number");
|
|
283
|
+
case "string": if (schema.enum) return createTupleType(schema.enum);
|
|
284
|
+
else return schema.format === "binary" ? createRawType("binary") : createRawType("string");
|
|
285
|
+
case "boolean": return createRawType("boolean");
|
|
286
|
+
case "object": {
|
|
287
|
+
const { properties = {}, required = [] } = schema;
|
|
288
|
+
return createObjectType(Object.fromEntries(Object.entries(properties).map(([prop, propSchema]) => [prop, convertToTypeScriptType(propSchema)])), required);
|
|
289
|
+
}
|
|
290
|
+
case "array": return createArrayType(convertToTypeScriptType(schema.items));
|
|
291
|
+
default: return createRawType("unknown");
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/generator/03-entities/utils/extractPropertyMetaData.ts
|
|
314
296
|
const setReferenceMeta = (prop, metaData, context) => {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
metaData.enum = loosePascalCase(referenceName);
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
metaData.entity = referenceName;
|
|
322
|
-
}
|
|
297
|
+
const referenceName = getRefName(prop);
|
|
298
|
+
if (isEnumSchemaObject(context.schemas.get(referenceName))) metaData.enum = loosePascalCase(referenceName);
|
|
299
|
+
else metaData.entity = referenceName;
|
|
323
300
|
};
|
|
324
301
|
const extractPropertyMetaData = (prop, context) => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return metaData;
|
|
351
|
-
};
|
|
352
|
-
|
|
302
|
+
const metaData = {};
|
|
303
|
+
const weclappExtension = prop["x-weclapp"];
|
|
304
|
+
if (weclappExtension) {
|
|
305
|
+
metaData.service = weclappExtension.service;
|
|
306
|
+
metaData.entity = weclappExtension.entity;
|
|
307
|
+
}
|
|
308
|
+
if (isReferenceObject(prop)) {
|
|
309
|
+
metaData.type = "reference";
|
|
310
|
+
setReferenceMeta(prop, metaData, context);
|
|
311
|
+
} else {
|
|
312
|
+
metaData.type = prop.type;
|
|
313
|
+
metaData.format = prop.format;
|
|
314
|
+
metaData.maxLength = prop.maxLength;
|
|
315
|
+
metaData.pattern = prop.pattern;
|
|
316
|
+
if (isArraySchemaObject(prop)) if (isReferenceObject(prop.items)) {
|
|
317
|
+
metaData.format = "reference";
|
|
318
|
+
setReferenceMeta(prop.items, metaData, context);
|
|
319
|
+
} else metaData.format = "string";
|
|
320
|
+
}
|
|
321
|
+
return metaData;
|
|
322
|
+
};
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/ts/generateComment.ts
|
|
353
325
|
const generateInlineComment = (comment) => `/** ${comment} */`;
|
|
354
|
-
const generateBlockComment = (comment, body) => `/**\n${comment.trim().replace(/^ */gm,
|
|
355
|
-
|
|
326
|
+
const generateBlockComment = (comment, body) => `/**\n${comment.trim().replace(/^ */gm, " * ")}\n */${body ? `\n${body}` : ""}`;
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/ts/generateType.ts
|
|
356
329
|
const generateType = (name, value) => {
|
|
357
|
-
|
|
330
|
+
return `export type ${name} = ${value.trim()};`;
|
|
358
331
|
};
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region src/utils/arrayify.ts
|
|
334
|
+
const arrayify = (v) => Array.isArray(v) ? v : [v];
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/ts/generateInterface.ts
|
|
362
337
|
const generateInterfaceProperties = (entries) => {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const rol = readonly ? 'readonly ' : '';
|
|
370
|
-
return `${cmd + rol + name + req}: ${type};`;
|
|
371
|
-
})
|
|
372
|
-
.join('\n');
|
|
373
|
-
return properties.length ? `{\n${indent(properties)}\n}` : `{}`;
|
|
338
|
+
const properties = entries.filter((v) => v.type !== void 0).filter((value, index, array) => array.findIndex((v) => v.name === value.name) === index).map(({ name, type, required, readonly, comment }) => {
|
|
339
|
+
const cmd = comment ? `${generateInlineComment(comment)}\n` : "";
|
|
340
|
+
const req = required ? "" : "?";
|
|
341
|
+
return `${cmd + (readonly ? "readonly " : "") + name + req}: ${type};`;
|
|
342
|
+
}).join("\n");
|
|
343
|
+
return properties.length ? `{\n${indent(properties)}\n}` : `{}`;
|
|
374
344
|
};
|
|
375
345
|
const generateInterfaceFromObject = (name, obj, propertyPropagationOption) => `export interface ${name} ${obj.toString(propertyPropagationOption)}`;
|
|
376
346
|
const generateInterface = (name, entries, extend) => {
|
|
377
|
-
|
|
378
|
-
const body = generateInterfaceProperties(entries);
|
|
379
|
-
return `export interface ${signature} ${body}`;
|
|
347
|
+
return `export interface ${`${name} ${extend ? `extends ${arrayify(extend).join(", ")}` : ""}`.trim()} ${generateInterfaceProperties(entries)}`;
|
|
380
348
|
};
|
|
381
349
|
const generateInterfaceType = (name, entries, extend) => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
typeDefinition += ` & ${body}`;
|
|
393
|
-
}
|
|
394
|
-
return generateType(name, typeDefinition);
|
|
395
|
-
};
|
|
396
|
-
|
|
350
|
+
const body = generateInterfaceProperties(entries);
|
|
351
|
+
const bases = extend ? arrayify(extend).join(" & ") : void 0;
|
|
352
|
+
let typeDefinition = "";
|
|
353
|
+
if (bases) typeDefinition = bases;
|
|
354
|
+
else typeDefinition = body;
|
|
355
|
+
if (bases && body !== "{}") typeDefinition += ` & ${body}`;
|
|
356
|
+
return generateType(name, typeDefinition);
|
|
357
|
+
};
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/utils/pluralize.ts
|
|
397
360
|
/**
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
361
|
+
* Pluralizes a word, most of the time correct.
|
|
362
|
+
* @param s String to pluralize.
|
|
363
|
+
*/
|
|
401
364
|
const pluralize = (s) => {
|
|
402
|
-
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
})();
|
|
465
|
-
|
|
466
|
-
const FILTER_PROPS_SUFFIX = 'Filter_Props';
|
|
365
|
+
return s.endsWith("s") ? s : s.endsWith("y") ? `${s.slice(0, -1)}ies` : `${s}s`;
|
|
366
|
+
};
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/utils/logger.ts
|
|
369
|
+
const logger = new class {
|
|
370
|
+
active = true;
|
|
371
|
+
warnings = 0;
|
|
372
|
+
errors = 0;
|
|
373
|
+
write(str = "") {
|
|
374
|
+
process.stdout.write(str);
|
|
375
|
+
}
|
|
376
|
+
blankLn(str = "") {
|
|
377
|
+
this.blank(`${str}\n`);
|
|
378
|
+
}
|
|
379
|
+
warnLn(str) {
|
|
380
|
+
this.warn(`${str}\n`);
|
|
381
|
+
}
|
|
382
|
+
errorLn(str) {
|
|
383
|
+
this.error(`${str}\n`);
|
|
384
|
+
}
|
|
385
|
+
successLn(str) {
|
|
386
|
+
this.success(`${str}\n`);
|
|
387
|
+
}
|
|
388
|
+
infoLn(str) {
|
|
389
|
+
this.info(`${str}\n`);
|
|
390
|
+
}
|
|
391
|
+
debugLn(str) {
|
|
392
|
+
this.debug(`${str}\n`);
|
|
393
|
+
}
|
|
394
|
+
blank(str) {
|
|
395
|
+
this.write(str);
|
|
396
|
+
}
|
|
397
|
+
warn(str) {
|
|
398
|
+
this.write(`${chalk.yellowBright("[!]")} ${str}`);
|
|
399
|
+
this.warnings++;
|
|
400
|
+
}
|
|
401
|
+
error(str) {
|
|
402
|
+
this.write(`${chalk.redBright("[X]")} ${str}`);
|
|
403
|
+
this.errors++;
|
|
404
|
+
}
|
|
405
|
+
success(str) {
|
|
406
|
+
this.write(`${chalk.greenBright("[✓]")} ${str}`);
|
|
407
|
+
}
|
|
408
|
+
info(str) {
|
|
409
|
+
this.write(`${chalk.blueBright("[i]")} ${str}`);
|
|
410
|
+
}
|
|
411
|
+
debug(str) {
|
|
412
|
+
this.write(`[-] ${str}`);
|
|
413
|
+
}
|
|
414
|
+
printSummary() {
|
|
415
|
+
const format = (v, name, fail, ok) => {
|
|
416
|
+
const color = v ? fail : ok;
|
|
417
|
+
return v === 0 ? `${color("zero")} ${pluralize(name)}` : v === 1 ? `${color("one")} ${name}` : `${color(v)} ${pluralize(name)}`;
|
|
418
|
+
};
|
|
419
|
+
const info = `Finished with ${format(this.warnings, "warning", chalk.yellowBright, chalk.greenBright)} and ${format(this.errors, "error", chalk.redBright, chalk.greenBright)}.`;
|
|
420
|
+
this[this.errors ? "errorLn" : this.warnings ? "warnLn" : "successLn"](info);
|
|
421
|
+
}
|
|
422
|
+
}();
|
|
423
|
+
//#endregion
|
|
424
|
+
//#region src/generator/03-entities/index.ts
|
|
425
|
+
const FILTER_PROPS_SUFFIX = "Filter_Props";
|
|
467
426
|
const generateEntities = (context) => {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
processProperties(schema.properties);
|
|
553
|
-
}
|
|
554
|
-
entities.set(entityName, {
|
|
555
|
-
name: entityName,
|
|
556
|
-
interfaceName: entityInterfaceName,
|
|
557
|
-
properties,
|
|
558
|
-
source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName)),
|
|
559
|
-
filterInterfaceName: entityFilterInterfaceName,
|
|
560
|
-
filterSource: generateStatements(generateInterface(entityFilterInterfaceName, entityFilterInterfaceProperties, parentEntityFilterInterfaceName)),
|
|
561
|
-
parentName: parentEntityName,
|
|
562
|
-
parentInterfaceName: parentEntityInterfaceName
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
return entities;
|
|
566
|
-
};
|
|
567
|
-
|
|
427
|
+
const entities = /* @__PURE__ */ new Map();
|
|
428
|
+
for (const [schemaName, schema] of context.schemas) {
|
|
429
|
+
if (isEnumSchemaObject(schema)) continue;
|
|
430
|
+
const entityName = schemaName;
|
|
431
|
+
const entityInterfaceName = loosePascalCase(entityName);
|
|
432
|
+
const entityInterfaceProperties = [];
|
|
433
|
+
const properties = /* @__PURE__ */ new Map();
|
|
434
|
+
let parentEntityName = void 0;
|
|
435
|
+
let parentEntityInterfaceName = void 0;
|
|
436
|
+
const entityFilterInterfaceName = `${entityInterfaceName}_${FILTER_PROPS_SUFFIX}`;
|
|
437
|
+
const entityFilterInterfaceProperties = [];
|
|
438
|
+
let parentEntityFilterInterfaceName = void 0;
|
|
439
|
+
const processProperties = (props = {}) => {
|
|
440
|
+
for (const [propertyName, propertySchema] of Object.entries(props)) {
|
|
441
|
+
const weclappExtension = propertySchema["x-weclapp"];
|
|
442
|
+
properties.set(propertyName, extractPropertyMetaData(propertySchema, context));
|
|
443
|
+
const type = convertToTypeScriptType(propertySchema).toString();
|
|
444
|
+
const castedSchema = propertySchema;
|
|
445
|
+
const comment = castedSchema.deprecated ? "@deprecated will be removed." : castedSchema.format ? `format: ${castedSchema.format}` : void 0;
|
|
446
|
+
if (weclappExtension?.filterable !== false) entityFilterInterfaceProperties.push({
|
|
447
|
+
name: propertyName,
|
|
448
|
+
type,
|
|
449
|
+
comment
|
|
450
|
+
});
|
|
451
|
+
entityInterfaceProperties.push({
|
|
452
|
+
name: propertyName,
|
|
453
|
+
type,
|
|
454
|
+
required: weclappExtension?.required,
|
|
455
|
+
readonly: castedSchema.readOnly,
|
|
456
|
+
comment
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
const processExtraFilterProperties = (props = {}) => {
|
|
461
|
+
for (const [propertyName, propertySchema] of Object.entries(props)) {
|
|
462
|
+
if (isReferenceObject(propertySchema)) continue;
|
|
463
|
+
const type = convertToTypeScriptType(propertySchema).toString();
|
|
464
|
+
const comment = propertySchema.deprecated ? "@deprecated will be removed." : propertySchema.format ? `format: ${propertySchema.format}` : void 0;
|
|
465
|
+
entityFilterInterfaceProperties.push({
|
|
466
|
+
name: propertyName,
|
|
467
|
+
type,
|
|
468
|
+
comment
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const processFilterPaths = (filterPaths = {}) => {
|
|
473
|
+
for (const [filterProp, entityName] of Object.entries(filterPaths)) if (!filterProp.includes(".") && context.schemas.get(entityName)) entityFilterInterfaceProperties.push({
|
|
474
|
+
name: filterProp,
|
|
475
|
+
type: `${loosePascalCase(entityName)}_${FILTER_PROPS_SUFFIX}`
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
if (schema.allOf?.length) {
|
|
479
|
+
if (schema.allOf.length > 2) {
|
|
480
|
+
logger.errorLn(`Failed to process schema for ${schemaName}: invalid allOf length`);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
for (const item of schema.allOf) if (isReferenceObject(item)) {
|
|
484
|
+
parentEntityName = getRefName(item);
|
|
485
|
+
parentEntityInterfaceName = createReferenceType(parentEntityName).toString();
|
|
486
|
+
parentEntityFilterInterfaceName = `${parentEntityInterfaceName}_${FILTER_PROPS_SUFFIX}`;
|
|
487
|
+
} else if (item.type === "object") {
|
|
488
|
+
processProperties(item.properties);
|
|
489
|
+
processExtraFilterProperties(item["x-weclapp-filterProperties"]);
|
|
490
|
+
processFilterPaths(item["x-weclapp-filterPaths"]);
|
|
491
|
+
} else logger.errorLn(`Failed to process schema for ${schemaName}: invalid schema type in allOf`);
|
|
492
|
+
} else processProperties(schema.properties);
|
|
493
|
+
entities.set(entityName, {
|
|
494
|
+
name: entityName,
|
|
495
|
+
interfaceName: entityInterfaceName,
|
|
496
|
+
properties,
|
|
497
|
+
source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName)),
|
|
498
|
+
filterInterfaceName: entityFilterInterfaceName,
|
|
499
|
+
filterSource: generateStatements(generateInterface(entityFilterInterfaceName, entityFilterInterfaceProperties, parentEntityFilterInterfaceName)),
|
|
500
|
+
parentName: parentEntityName,
|
|
501
|
+
parentInterfaceName: parentEntityInterfaceName
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return entities;
|
|
505
|
+
};
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/utils/weclapp/parseEndpointPath.ts
|
|
568
508
|
/**
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
509
|
+
* ROOT => /article
|
|
510
|
+
* COUNT => /article/count
|
|
511
|
+
* ENTITY => /article/{id}
|
|
512
|
+
* SPECIAL_ROOT => /article/generateImage
|
|
513
|
+
* SPECIAL_ENTITY => /article/id/{id}/generateImag
|
|
514
|
+
*/
|
|
515
|
+
let WeclappEndpointType = /* @__PURE__ */ function(WeclappEndpointType) {
|
|
516
|
+
WeclappEndpointType["ROOT"] = "ROOT";
|
|
517
|
+
WeclappEndpointType["COUNT"] = "COUNT";
|
|
518
|
+
WeclappEndpointType["ENTITY"] = "ENTITY";
|
|
519
|
+
WeclappEndpointType["GENERIC_ROOT"] = "GENERIC_ROOT";
|
|
520
|
+
WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
|
|
521
|
+
return WeclappEndpointType;
|
|
522
|
+
}({});
|
|
583
523
|
const parseEndpointPath = (path) => {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
return undefined;
|
|
524
|
+
const [, service, ...rest] = path.split("/");
|
|
525
|
+
if (!service) return;
|
|
526
|
+
if (!rest.length) return {
|
|
527
|
+
path,
|
|
528
|
+
service,
|
|
529
|
+
type: WeclappEndpointType.ROOT
|
|
530
|
+
};
|
|
531
|
+
else if (rest[0] === "count") return {
|
|
532
|
+
path,
|
|
533
|
+
service,
|
|
534
|
+
type: WeclappEndpointType.COUNT
|
|
535
|
+
};
|
|
536
|
+
else if (rest[0] === "id") return rest.length === 2 ? {
|
|
537
|
+
path,
|
|
538
|
+
service,
|
|
539
|
+
type: WeclappEndpointType.ENTITY
|
|
540
|
+
} : {
|
|
541
|
+
path,
|
|
542
|
+
service,
|
|
543
|
+
method: rest[2],
|
|
544
|
+
type: WeclappEndpointType.GENERIC_ENTITY
|
|
545
|
+
};
|
|
546
|
+
else if (rest.length === 1) return {
|
|
547
|
+
path,
|
|
548
|
+
service,
|
|
549
|
+
method: rest[1],
|
|
550
|
+
type: WeclappEndpointType.GENERIC_ROOT
|
|
551
|
+
};
|
|
613
552
|
};
|
|
614
553
|
const isMultiPartUploadPath = (path) => {
|
|
615
|
-
|
|
616
|
-
|
|
554
|
+
const [, entity, ...rest] = path.split("/");
|
|
555
|
+
return entity && rest.length === 2 && rest[1] === "multipartUpload";
|
|
617
556
|
};
|
|
618
557
|
const parseEndpointsPaths = (paths) => {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
558
|
+
const endpoints = /* @__PURE__ */ new Map();
|
|
559
|
+
for (const [rawPath, path] of Object.entries(paths)) {
|
|
560
|
+
const endpoint = parseEndpointPath(rawPath);
|
|
561
|
+
if (!endpoint || !path) {
|
|
562
|
+
if (isMultiPartUploadPath(rawPath)) continue;
|
|
563
|
+
logger.errorLn(`Failed to parse ${rawPath}`);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (endpoints.has(endpoint.service)) endpoints.get(endpoint.service)?.push({
|
|
567
|
+
endpoint,
|
|
568
|
+
path
|
|
569
|
+
});
|
|
570
|
+
else endpoints.set(endpoint.service, [{
|
|
571
|
+
endpoint,
|
|
572
|
+
path
|
|
573
|
+
}]);
|
|
574
|
+
}
|
|
575
|
+
return endpoints;
|
|
576
|
+
};
|
|
577
|
+
//#endregion
|
|
578
|
+
//#region src/ts/generateArrowFunction.ts
|
|
640
579
|
const generateArrowFunction = ({ name, signature, returns, params }) => {
|
|
641
|
-
|
|
580
|
+
return `const ${name}: ${signature} = (${params?.join(", ") ?? ""}) =>\n${indent(returns)};`;
|
|
642
581
|
};
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
return generateType(type, `${genericsString + paramsString} =>\n${indent(returns)}`);
|
|
582
|
+
//#endregion
|
|
583
|
+
//#region src/ts/generateArrowFunctionType.ts
|
|
584
|
+
const generateArrowFunctionType = ({ type, returns = "void", generics, params }) => {
|
|
585
|
+
return generateType(type, `${(generics?.length ? `<\n${indent(generics.join(",\n"))}\n>` : "") + (params?.length ? `(${params.join(", ")})` : `()`)} =>\n${indent(returns)}`);
|
|
648
586
|
};
|
|
649
|
-
|
|
587
|
+
//#endregion
|
|
588
|
+
//#region src/generator/04-services/utils/resolveParameters.ts
|
|
650
589
|
const resolveParameters = (resolvableParameters = [], parameters) => {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
590
|
+
if (!resolvableParameters) return [];
|
|
591
|
+
return resolvableParameters.flatMap((param) => {
|
|
592
|
+
if (isReferenceObject(param)) {
|
|
593
|
+
const resolved = parameters.get(getRefName(param));
|
|
594
|
+
return resolved ? [resolved] : [];
|
|
595
|
+
}
|
|
596
|
+
return [param];
|
|
597
|
+
});
|
|
598
|
+
};
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/generator/04-services/endpoints/count.ts
|
|
662
601
|
const generateCountEndpoint = ({ endpoint, operationObject, entities, context, options }) => {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
602
|
+
const functionName = "count";
|
|
603
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
604
|
+
const relatedEntityName = context.aliases.get(endpoint.service);
|
|
605
|
+
const relatedEntity = !!relatedEntityName && entities.get(relatedEntityName);
|
|
606
|
+
if (!relatedEntity) throw Error(`Related entity schema for service ${endpoint.service} not found`);
|
|
607
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
608
|
+
const parametersType = createObjectType({ params: convertToTypeScriptType(resolveParameters(operationObject.parameters, context.parameters)) });
|
|
609
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, "propagate");
|
|
610
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
611
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${relatedEntity.filterInterfaceName}`]);
|
|
612
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
613
|
+
type: functionTypeName,
|
|
614
|
+
params: [`query${parametersType.isFullyOptional() ? "?" : ""}: CountQuery<${filterTypeName}>${operationObject.parameters?.length ? " & " + parametersTypeName : ""}`, "requestOptions?: RequestOptions"],
|
|
615
|
+
returns: `${resolveResponseType(options.target)}<number>`
|
|
616
|
+
});
|
|
617
|
+
const functionSource = generateArrowFunction({
|
|
618
|
+
name: functionName,
|
|
619
|
+
signature: functionTypeName,
|
|
620
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
621
|
+
params: ["query", "requestOptions?: RequestOptions"]
|
|
622
|
+
});
|
|
623
|
+
return {
|
|
624
|
+
name: functionName,
|
|
625
|
+
type: {
|
|
626
|
+
name: functionTypeName,
|
|
627
|
+
source: functionTypeSource
|
|
628
|
+
},
|
|
629
|
+
func: {
|
|
630
|
+
name: functionName,
|
|
631
|
+
source: functionSource
|
|
632
|
+
},
|
|
633
|
+
interfaces: [...operationObject.parameters?.length ? [{
|
|
634
|
+
name: parametersTypeName,
|
|
635
|
+
source: parametersTypeSource
|
|
636
|
+
}] : [], {
|
|
637
|
+
name: filterTypeName,
|
|
638
|
+
source: filterTypeSource
|
|
639
|
+
}]
|
|
640
|
+
};
|
|
641
|
+
};
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/generator/04-services/utils/generateContentType.ts
|
|
644
|
+
const generateContentType = (body, fallback = "unknown") => {
|
|
645
|
+
if (!body?.content) return createRawType(fallback);
|
|
646
|
+
const types = [];
|
|
647
|
+
for (const { schema } of Object.values(body.content)) if (schema) types.push(convertToTypeScriptType(schema));
|
|
648
|
+
return (types.length > 1 ? createTupleType(types) : types[0]) ?? createRawType(fallback);
|
|
649
|
+
};
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/generator/04-services/utils/generateRequestBodyType.ts
|
|
714
652
|
const generateRequestBodyType = ({ requestBody }, requestBodies) => {
|
|
715
|
-
|
|
716
|
-
return generateContentType(requestBodyObject);
|
|
653
|
+
return generateContentType(requestBody && isReferenceObject(requestBody) ? requestBodies.get(getRefName(requestBody)) : requestBody);
|
|
717
654
|
};
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/generator/04-services/utils/resolveResponsesObject.ts
|
|
657
|
+
const resolveResponsesObject = (responses) => Object.entries(responses).find(([statusCode]) => statusCode.startsWith("2"))?.[1];
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region src/generator/04-services/utils/generateResponseType.ts
|
|
721
660
|
const generateResponseType = (operationObject, responses) => {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
return generateContentType(responseObject, 'void');
|
|
661
|
+
const response = resolveResponsesObject(operationObject.responses);
|
|
662
|
+
return generateContentType(response && isReferenceObject(response) ? responses.get(getRefName(response)) : response, "void");
|
|
725
663
|
};
|
|
726
|
-
|
|
664
|
+
//#endregion
|
|
665
|
+
//#region src/generator/04-services/endpoints/create.ts
|
|
727
666
|
const generateCreateEndpoint = ({ endpoint, operationObject, context, options }) => {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
667
|
+
const functionName = "create";
|
|
668
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
669
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
670
|
+
type: functionTypeName,
|
|
671
|
+
params: [`data: DeepPartial<${generateRequestBodyType(operationObject, context.requestBodies).toString()}>`, "requestOptions?: RequestOptions"],
|
|
672
|
+
returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
|
|
673
|
+
});
|
|
674
|
+
const functionSource = generateArrowFunction({
|
|
675
|
+
name: functionName,
|
|
676
|
+
signature: functionTypeName,
|
|
677
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, data, requestOptions)`,
|
|
678
|
+
params: ["data", "requestOptions?: RequestOptions"]
|
|
679
|
+
});
|
|
680
|
+
return {
|
|
681
|
+
name: functionName,
|
|
682
|
+
type: {
|
|
683
|
+
name: functionTypeName,
|
|
684
|
+
source: functionTypeSource
|
|
685
|
+
},
|
|
686
|
+
func: {
|
|
687
|
+
name: functionName,
|
|
688
|
+
source: functionSource
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
};
|
|
692
|
+
//#endregion
|
|
693
|
+
//#region src/generator/04-services/utils/generateGenericFunctionName.ts
|
|
694
|
+
const generateGenericFunctionName = (path, suffix = "", prefix = "") => {
|
|
695
|
+
return camelCase(`${prefix}_` + path.replace(/.*\//, "").replace(/\W+/, "_").replace(/[_]+/, "_") + `_${suffix}`);
|
|
696
|
+
};
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/generator/04-services/utils/insertPathPlaceholder.ts
|
|
755
699
|
const insertPathPlaceholder = (path, record) => {
|
|
756
|
-
|
|
700
|
+
return path.replace(/{(\w+)}/g, (_, name) => record[name]);
|
|
757
701
|
};
|
|
758
|
-
|
|
702
|
+
//#endregion
|
|
703
|
+
//#region src/generator/04-services/endpoints/generic.ts
|
|
759
704
|
const wrapBody = (type, target) => {
|
|
760
|
-
|
|
705
|
+
return type.toString() === "binary" ? createRawType(isNodeTarget(target) ? "BodyInit" : "Blob") : type;
|
|
761
706
|
};
|
|
762
707
|
const generateGenericEndpoint = (suffix) => ({ method, endpoint, operationObject, context, options }) => {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
708
|
+
const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
|
|
709
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
710
|
+
const entityQuery = `${functionTypeName}_Query`;
|
|
711
|
+
const hasId = endpoint.path.includes("{id}");
|
|
712
|
+
const params = createObjectType({
|
|
713
|
+
params: operationObject.parameters && convertToTypeScriptType(resolveParameters(operationObject.parameters, context.parameters)),
|
|
714
|
+
body: method === "get" ? void 0 : wrapBody(generateRequestBodyType(operationObject, context.requestBodies), options.target)
|
|
715
|
+
});
|
|
716
|
+
const responseBody = generateResponseType(operationObject, context.responses);
|
|
717
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
718
|
+
type: functionTypeName,
|
|
719
|
+
params: [
|
|
720
|
+
...hasId ? ["id: string"] : [],
|
|
721
|
+
`query${params.isFullyOptional() ? "?" : ""}: ${entityQuery}`,
|
|
722
|
+
"requestOptions?: RequestOptions"
|
|
723
|
+
],
|
|
724
|
+
returns: `${resolveResponseType(options.target)}<${wrapBody(responseBody, options.target).toString("force")}>`
|
|
725
|
+
});
|
|
726
|
+
const functionSource = generateArrowFunction({
|
|
727
|
+
name: functionName,
|
|
728
|
+
signature: functionTypeName,
|
|
729
|
+
params: hasId ? [
|
|
730
|
+
"id",
|
|
731
|
+
"query",
|
|
732
|
+
"requestOptions?: RequestOptions"
|
|
733
|
+
] : ["query", "requestOptions?: RequestOptions"],
|
|
734
|
+
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, query, ${String(responseBody.toString() === "binary")}, requestOptions)`
|
|
735
|
+
});
|
|
736
|
+
return {
|
|
737
|
+
name: functionName,
|
|
738
|
+
type: {
|
|
739
|
+
name: functionTypeName,
|
|
740
|
+
source: functionTypeSource
|
|
741
|
+
},
|
|
742
|
+
func: {
|
|
743
|
+
name: functionName,
|
|
744
|
+
source: functionSource
|
|
745
|
+
},
|
|
746
|
+
interfaces: [{
|
|
747
|
+
name: entityQuery,
|
|
748
|
+
source: generateInterfaceFromObject(entityQuery, params, "propagate")
|
|
749
|
+
}]
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/generator/04-services/endpoints/remove.ts
|
|
803
754
|
const generateRemoveEndpoint = ({ endpoint, options }) => {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
755
|
+
const functionName = "remove";
|
|
756
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
757
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
758
|
+
type: functionTypeName,
|
|
759
|
+
params: [
|
|
760
|
+
"id: string",
|
|
761
|
+
"options?: RemoveQuery",
|
|
762
|
+
"requestOptions?: RequestOptions"
|
|
763
|
+
],
|
|
764
|
+
returns: `${resolveResponseType(options.target)}<void>`
|
|
765
|
+
});
|
|
766
|
+
const functionSource = generateArrowFunction({
|
|
767
|
+
name: functionName,
|
|
768
|
+
signature: functionTypeName,
|
|
769
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, options, requestOptions)`,
|
|
770
|
+
params: [
|
|
771
|
+
"id",
|
|
772
|
+
"options?: RemoveQuery",
|
|
773
|
+
"requestOptions?: RequestOptions"
|
|
774
|
+
]
|
|
775
|
+
});
|
|
776
|
+
return {
|
|
777
|
+
name: functionName,
|
|
778
|
+
type: {
|
|
779
|
+
name: functionTypeName,
|
|
780
|
+
source: functionTypeSource
|
|
781
|
+
},
|
|
782
|
+
func: {
|
|
783
|
+
name: functionName,
|
|
784
|
+
source: functionSource
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region src/generator/04-services/endpoints/some.ts
|
|
824
790
|
const excludedParameters = [
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
791
|
+
"page",
|
|
792
|
+
"pageSize",
|
|
793
|
+
"sort",
|
|
794
|
+
"serializeNulls",
|
|
795
|
+
"properties",
|
|
796
|
+
"includeReferencedEntities",
|
|
797
|
+
"additionalProperties"
|
|
832
798
|
];
|
|
833
799
|
const resolveAdditionalPropertiesSchema = ({ responses }) => {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
return undefined;
|
|
800
|
+
const body = resolveResponsesObject(responses);
|
|
801
|
+
if (isResponseObject(body)) {
|
|
802
|
+
const schema = body?.content?.["application/json"]?.schema;
|
|
803
|
+
if (isObjectSchemaObject(schema)) {
|
|
804
|
+
const obj = schema?.properties?.additionalProperties;
|
|
805
|
+
if (isObjectSchemaObject(obj)) return obj;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
845
808
|
};
|
|
846
809
|
const resolveReferences = (entity, entities) => {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
if (generatedEntity.parentName) {
|
|
860
|
-
references.push(...resolveReferences(generatedEntity.parentName, entities));
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return references;
|
|
810
|
+
const references = [];
|
|
811
|
+
const generatedEntity = entities.get(entity);
|
|
812
|
+
if (generatedEntity) {
|
|
813
|
+
for (const [property, propertyMetaData] of generatedEntity.properties) if (propertyMetaData.service) references.push({
|
|
814
|
+
name: property,
|
|
815
|
+
type: generateString(propertyMetaData.service),
|
|
816
|
+
required: true
|
|
817
|
+
});
|
|
818
|
+
if (generatedEntity.parentName) references.push(...resolveReferences(generatedEntity.parentName, entities));
|
|
819
|
+
}
|
|
820
|
+
return references;
|
|
864
821
|
};
|
|
865
822
|
const resolveReferencedEntities = (entity, entities) => {
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (generatedEntity.parentName) {
|
|
881
|
-
referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
return referencedEntities;
|
|
823
|
+
const referencedEntities = [];
|
|
824
|
+
const generatedEntity = entities.get(entity);
|
|
825
|
+
if (generatedEntity) {
|
|
826
|
+
for (const [, propertyMetaData] of generatedEntity.properties) if (propertyMetaData.service && propertyMetaData.entity) {
|
|
827
|
+
const referencedEntity = entities.get(propertyMetaData.entity);
|
|
828
|
+
if (referencedEntity) referencedEntities.push({
|
|
829
|
+
name: propertyMetaData.service,
|
|
830
|
+
type: `${referencedEntity.interfaceName}[]`,
|
|
831
|
+
required: true
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (generatedEntity.parentName) referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
|
|
835
|
+
}
|
|
836
|
+
return referencedEntities;
|
|
885
837
|
};
|
|
886
838
|
const generateSomeEndpoint = ({ endpoint, operationObject, entities, context, options }) => {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
|
|
839
|
+
const functionName = "some";
|
|
840
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
841
|
+
const relatedEntityName = context.aliases.get(endpoint.service);
|
|
842
|
+
const relatedEntity = !!relatedEntityName && entities.get(relatedEntityName);
|
|
843
|
+
if (!relatedEntity) throw Error(`Related entity schema for service ${endpoint.service} not found`);
|
|
844
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
845
|
+
const parameters = operationObject.parameters?.filter((v) => isParameterObject(v) ? !excludedParameters.includes(v.name) : false);
|
|
846
|
+
const parametersType = createObjectType({ params: parameters && convertToTypeScriptType(resolveParameters(parameters, context.parameters)) });
|
|
847
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, "propagate");
|
|
848
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
849
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${relatedEntity.filterInterfaceName}`]);
|
|
850
|
+
const referencesTypeName = `${functionTypeName}_References`;
|
|
851
|
+
const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
|
|
852
|
+
const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
|
|
853
|
+
const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, "string");
|
|
854
|
+
const queryTypeName = `${functionTypeName}_Query`;
|
|
855
|
+
const queryTypeSource = generateType(queryTypeName, `SomeQuery<${relatedEntity.interfaceName}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
|
|
856
|
+
const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
|
|
857
|
+
const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
|
|
858
|
+
const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
|
|
859
|
+
const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(operationObject);
|
|
860
|
+
const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : "{}");
|
|
861
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
862
|
+
type: functionTypeName,
|
|
863
|
+
params: [`query${parametersType.isFullyOptional() ? "?" : ""}: ${queryTypeName}, requestOptions?: RequestOptions`],
|
|
864
|
+
returns: `${resolveResponseType(options.target)}<SomeQueryReturn<${relatedEntity.interfaceName}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
|
|
865
|
+
});
|
|
866
|
+
const functionSource = generateArrowFunction({
|
|
867
|
+
name: functionName,
|
|
868
|
+
signature: functionTypeName,
|
|
869
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query, requestOptions)`,
|
|
870
|
+
params: ["query", "requestOptions?: RequestOptions"]
|
|
871
|
+
});
|
|
872
|
+
return {
|
|
873
|
+
name: functionName,
|
|
874
|
+
type: {
|
|
875
|
+
name: functionTypeName,
|
|
876
|
+
source: functionTypeSource
|
|
877
|
+
},
|
|
878
|
+
func: {
|
|
879
|
+
name: functionName,
|
|
880
|
+
source: functionSource
|
|
881
|
+
},
|
|
882
|
+
interfaces: [
|
|
883
|
+
{
|
|
884
|
+
name: parametersTypeName,
|
|
885
|
+
source: parametersTypeSource
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
name: filterTypeName,
|
|
889
|
+
source: filterTypeSource
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
name: referencesTypeName,
|
|
893
|
+
source: referencesTypeSource
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
name: additionalPropertyTypeName,
|
|
897
|
+
source: additionalPropertyTypeSource
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
name: queryTypeName,
|
|
901
|
+
source: queryTypeSource
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
name: referencedEntitiesTypeName,
|
|
905
|
+
source: referencedEntitiesTypeSource
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
name: additionalPropertiesTypeName,
|
|
909
|
+
source: additionalPropertiesTypeSource
|
|
910
|
+
}
|
|
911
|
+
]
|
|
912
|
+
};
|
|
913
|
+
};
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/generator/04-services/endpoints/unique.ts
|
|
940
916
|
const generateUniqueEndpoint = ({ operationObject, endpoint, context, options }) => {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
917
|
+
const functionName = "unique";
|
|
918
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
919
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
920
|
+
type: functionTypeName,
|
|
921
|
+
params: [
|
|
922
|
+
"id: string",
|
|
923
|
+
"query?: Q",
|
|
924
|
+
"requestOptions?: RequestOptions"
|
|
925
|
+
],
|
|
926
|
+
generics: ["Q extends UniqueQuery"],
|
|
927
|
+
returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
|
|
928
|
+
});
|
|
929
|
+
const functionSource = generateArrowFunction({
|
|
930
|
+
name: functionName,
|
|
931
|
+
signature: functionTypeName,
|
|
932
|
+
params: [
|
|
933
|
+
"id",
|
|
934
|
+
"query",
|
|
935
|
+
"requestOptions?: RequestOptions"
|
|
936
|
+
],
|
|
937
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, query, requestOptions)`
|
|
938
|
+
});
|
|
939
|
+
return {
|
|
940
|
+
name: functionName,
|
|
941
|
+
type: {
|
|
942
|
+
name: functionTypeName,
|
|
943
|
+
source: functionTypeSource
|
|
944
|
+
},
|
|
945
|
+
func: {
|
|
946
|
+
name: functionName,
|
|
947
|
+
source: functionSource
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
};
|
|
951
|
+
//#endregion
|
|
952
|
+
//#region src/generator/04-services/endpoints/update.ts
|
|
962
953
|
const generateUpdateEndpoint = ({ endpoint, operationObject, context, options }) => {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
954
|
+
const functionName = "update";
|
|
955
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
956
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
957
|
+
type: functionTypeName,
|
|
958
|
+
params: [
|
|
959
|
+
"id: string",
|
|
960
|
+
`data: DeepPartial<${generateRequestBodyType(operationObject, context.requestBodies).toString()}>`,
|
|
961
|
+
"options?: UpdateQuery",
|
|
962
|
+
"requestOptions?: RequestOptions"
|
|
963
|
+
],
|
|
964
|
+
returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
|
|
965
|
+
});
|
|
966
|
+
const functionSource = generateArrowFunction({
|
|
967
|
+
name: functionName,
|
|
968
|
+
signature: functionTypeName,
|
|
969
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, data, options, requestOptions)`,
|
|
970
|
+
params: [
|
|
971
|
+
"id",
|
|
972
|
+
"data",
|
|
973
|
+
"options",
|
|
974
|
+
"requestOptions?: RequestOptions"
|
|
975
|
+
]
|
|
976
|
+
});
|
|
977
|
+
return {
|
|
978
|
+
name: functionName,
|
|
979
|
+
type: {
|
|
980
|
+
name: functionTypeName,
|
|
981
|
+
source: functionTypeSource
|
|
982
|
+
},
|
|
983
|
+
func: {
|
|
984
|
+
name: functionName,
|
|
985
|
+
source: functionSource
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
};
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region src/generator/04-services/index.ts
|
|
988
991
|
const generators = {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
[OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint('ById')
|
|
1008
|
-
},
|
|
1009
|
-
/* /article/method */
|
|
1010
|
-
[WeclappEndpointType.GENERIC_ROOT]: {
|
|
1011
|
-
[OpenAPIV3.HttpMethods.GET]: generateGenericEndpoint(),
|
|
1012
|
-
[OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint()
|
|
1013
|
-
}
|
|
992
|
+
[WeclappEndpointType.ROOT]: {
|
|
993
|
+
[OpenAPIV3.HttpMethods.GET]: generateSomeEndpoint,
|
|
994
|
+
[OpenAPIV3.HttpMethods.POST]: generateCreateEndpoint
|
|
995
|
+
},
|
|
996
|
+
[WeclappEndpointType.COUNT]: { [OpenAPIV3.HttpMethods.GET]: generateCountEndpoint },
|
|
997
|
+
[WeclappEndpointType.ENTITY]: {
|
|
998
|
+
[OpenAPIV3.HttpMethods.GET]: generateUniqueEndpoint,
|
|
999
|
+
[OpenAPIV3.HttpMethods.PUT]: generateUpdateEndpoint,
|
|
1000
|
+
[OpenAPIV3.HttpMethods.DELETE]: generateRemoveEndpoint
|
|
1001
|
+
},
|
|
1002
|
+
[WeclappEndpointType.GENERIC_ENTITY]: {
|
|
1003
|
+
[OpenAPIV3.HttpMethods.GET]: generateGenericEndpoint("ById"),
|
|
1004
|
+
[OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint("ById")
|
|
1005
|
+
},
|
|
1006
|
+
[WeclappEndpointType.GENERIC_ROOT]: {
|
|
1007
|
+
[OpenAPIV3.HttpMethods.GET]: generateGenericEndpoint(),
|
|
1008
|
+
[OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint()
|
|
1009
|
+
}
|
|
1014
1010
|
};
|
|
1015
1011
|
const generateServices = (entities, context, options) => {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const relatedEntityName = context.aliases.get(serviceName);
|
|
1068
|
-
const relatedEntity = relatedEntityName ? entities.get(relatedEntityName) : undefined;
|
|
1069
|
-
services.set(serviceName, {
|
|
1070
|
-
name: serviceName,
|
|
1071
|
-
serviceFnName,
|
|
1072
|
-
functions,
|
|
1073
|
-
source: generateStatements(serviceTypes, serviceFn),
|
|
1074
|
-
deprecated: functions.every((v) => v.path.deprecated),
|
|
1075
|
-
relatedEntity
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
return services;
|
|
1079
|
-
};
|
|
1080
|
-
|
|
1012
|
+
const services = /* @__PURE__ */ new Map();
|
|
1013
|
+
for (const [serviceName, serviceEndpoints] of context.endpoints) {
|
|
1014
|
+
const serviceFnName = camelCase(`${serviceName}Service`);
|
|
1015
|
+
const serviceTypeName = pascalCase(`${serviceName}Service`);
|
|
1016
|
+
const functions = [];
|
|
1017
|
+
for (const { path, endpoint } of serviceEndpoints) for (const method of [
|
|
1018
|
+
OpenAPIV3.HttpMethods.GET,
|
|
1019
|
+
OpenAPIV3.HttpMethods.POST,
|
|
1020
|
+
OpenAPIV3.HttpMethods.PUT,
|
|
1021
|
+
OpenAPIV3.HttpMethods.DELETE
|
|
1022
|
+
]) {
|
|
1023
|
+
if (method === OpenAPIV3.HttpMethods.GET && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique || method === OpenAPIV3.HttpMethods.POST && (endpoint.type === WeclappEndpointType.COUNT || endpoint.path.endsWith("query"))) continue;
|
|
1024
|
+
const operationObject = path[method];
|
|
1025
|
+
const generatorFn = generators[endpoint.type][method];
|
|
1026
|
+
if (operationObject && generatorFn) {
|
|
1027
|
+
if (!operationObject.deprecated || options.deprecated) functions.push({
|
|
1028
|
+
...generatorFn({
|
|
1029
|
+
method,
|
|
1030
|
+
endpoint,
|
|
1031
|
+
operationObject,
|
|
1032
|
+
entities,
|
|
1033
|
+
context,
|
|
1034
|
+
options
|
|
1035
|
+
}),
|
|
1036
|
+
path: operationObject
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (!functions.length) continue;
|
|
1041
|
+
const serviceTypes = generateStatements(...functions.flatMap((v) => generateBlockComment(`${serviceTypeName} - ${pascalCase(v.name)}`, generateStatements(...[...v.interfaces?.map((v) => v.source) ?? [], v.type.source]))), generateBlockComment(`${serviceTypeName}`, generateInterface(serviceTypeName, [...functions.map((v) => ({
|
|
1042
|
+
required: true,
|
|
1043
|
+
comment: v.path.deprecated ? "@deprecated" : void 0,
|
|
1044
|
+
name: v.func.name,
|
|
1045
|
+
type: v.type.name
|
|
1046
|
+
}))])));
|
|
1047
|
+
const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
|
|
1048
|
+
const relatedEntityName = context.aliases.get(serviceName);
|
|
1049
|
+
const relatedEntity = relatedEntityName ? entities.get(relatedEntityName) : void 0;
|
|
1050
|
+
services.set(serviceName, {
|
|
1051
|
+
name: serviceName,
|
|
1052
|
+
serviceFnName,
|
|
1053
|
+
functions,
|
|
1054
|
+
source: generateStatements(serviceTypes, serviceFn),
|
|
1055
|
+
deprecated: functions.every((v) => v.path.deprecated),
|
|
1056
|
+
relatedEntity
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
return services;
|
|
1060
|
+
};
|
|
1061
|
+
//#endregion
|
|
1062
|
+
//#region src/generator/05-maps/utils/generateCustomValueServices.ts
|
|
1081
1063
|
const generateCustomValueServices = (services) => {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1089
|
-
return generateStatements(generateType('WCustomValueService', concat(generateStrings(customValueEntities), ' | ')), `export const wCustomValueServiceNames: WCustomValueService[] = [${concat(generateStrings(customValueEntities))}];`, `export const isWCustomValueService = (service: string | undefined): service is WCustomValueService =>\n${indent('wCustomValueServiceNames.includes(service as WCustomValueService);')}`);
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1064
|
+
const customValueEntities = [];
|
|
1065
|
+
for (const service of services) if (service.relatedEntity?.name === "customValue") customValueEntities.push(service.name);
|
|
1066
|
+
return generateStatements(generateType("WCustomValueService", concat(generateStrings(customValueEntities), " | ")), `export const wCustomValueServiceNames: WCustomValueService[] = [${concat(generateStrings(customValueEntities))}];`, `export const isWCustomValueService = (service: string | undefined): service is WCustomValueService =>\n${indent("wCustomValueServiceNames.includes(service as WCustomValueService);")}`);
|
|
1067
|
+
};
|
|
1068
|
+
//#endregion
|
|
1069
|
+
//#region src/ts/generateObject.ts
|
|
1092
1070
|
const generateObject = (properties) => {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${String(value)}`);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
|
|
1109
|
-
};
|
|
1110
|
-
|
|
1071
|
+
const body = [];
|
|
1072
|
+
for (const { key, value, comment } of properties) {
|
|
1073
|
+
if (value === void 0) continue;
|
|
1074
|
+
if (Array.isArray(value)) {
|
|
1075
|
+
const str = generateObject(value);
|
|
1076
|
+
if (str.length > 2) body.push(`${comment ? generateInlineComment(comment) + "\n" : ""}${key}: ${str}`);
|
|
1077
|
+
} else body.push(`${comment ? generateInlineComment(comment) + "\n" : ""}${key}: ${String(value)}`);
|
|
1078
|
+
}
|
|
1079
|
+
return body.length ? `{\n${indent(body.join(",\n"))}\n}` : `{}`;
|
|
1080
|
+
};
|
|
1081
|
+
//#endregion
|
|
1082
|
+
//#region src/generator/05-maps/utils/generateEntityProperties.ts
|
|
1111
1083
|
const resolveInheritedEntities = (root, entities) => {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
};
|
|
1115
|
-
const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
})
|
|
1125
|
-
.map(([property, meta]) => ({
|
|
1126
|
-
key: property,
|
|
1127
|
-
value: Object.entries(meta).map(([key, value]) => ({
|
|
1128
|
-
key,
|
|
1129
|
-
value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
|
|
1130
|
-
}))
|
|
1084
|
+
const parent = root.parentName ? entities.get(root.parentName) : void 0;
|
|
1085
|
+
return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
|
|
1086
|
+
};
|
|
1087
|
+
const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties].filter(([, meta]) => {
|
|
1088
|
+
if (options.deprecated) return true;
|
|
1089
|
+
const service = services.find((v) => v.name === meta.service);
|
|
1090
|
+
return !meta.service || service && !service.deprecated;
|
|
1091
|
+
}).map(([property, meta]) => ({
|
|
1092
|
+
key: property,
|
|
1093
|
+
value: Object.entries(meta).map(([key, value]) => ({
|
|
1094
|
+
key,
|
|
1095
|
+
value: value !== void 0 ? typeof value === "number" ? value : generateString(value) : void 0
|
|
1096
|
+
}))
|
|
1131
1097
|
}));
|
|
1132
1098
|
const generateEntityProperties = (entities, services, options) => {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
value: generatePropertyDescriptors(entity, entities, services, options)
|
|
1145
|
-
}));
|
|
1146
|
-
return generateStatements(`export type ${typeName} = Partial<Record<WEntity, Partial<Record<string, WEntityPropertyMeta>>>>;`, `export const wEntityProperties: ${typeName} = ${generateObject(propertyMap)};`);
|
|
1147
|
-
};
|
|
1148
|
-
|
|
1099
|
+
const typeName = "WEntityProperties";
|
|
1100
|
+
const propertyMap = [...entities.entries(), ...services.filter(({ relatedEntity }) => !!relatedEntity).filter(({ name }) => !entities.get(name)).map(({ name, relatedEntity }) => {
|
|
1101
|
+
return [name, relatedEntity];
|
|
1102
|
+
})].map(([entityName, entity]) => ({
|
|
1103
|
+
key: entityName,
|
|
1104
|
+
value: generatePropertyDescriptors(entity, entities, services, options)
|
|
1105
|
+
}));
|
|
1106
|
+
return generateStatements(`export type ${typeName} = Partial<Record<WEntity, Partial<Record<string, WEntityPropertyMeta>>>>;`, `export const wEntityProperties: ${typeName} = ${generateObject(propertyMap)};`);
|
|
1107
|
+
};
|
|
1108
|
+
//#endregion
|
|
1109
|
+
//#region src/ts/generateArray.ts
|
|
1149
1110
|
const generateArray = (values) => {
|
|
1150
|
-
|
|
1111
|
+
return `[${concat(values.map((v) => generateString(String(v))))}]`;
|
|
1151
1112
|
};
|
|
1152
|
-
|
|
1153
|
-
|
|
1113
|
+
//#endregion
|
|
1114
|
+
//#region src/generator/05-maps/utils/generateGroupedServices.ts
|
|
1154
1115
|
const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
|
|
1155
1116
|
/**
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1117
|
+
* Generates for each function a map with the entity-name as key and service type as value.
|
|
1118
|
+
* E.g. WServicesWith[Function] where [Function] may be something like "some" or "create".
|
|
1119
|
+
*
|
|
1120
|
+
* This function also generates an exported array with the names of each service for each name.
|
|
1121
|
+
*/
|
|
1161
1122
|
const generateGroupedServices = (services) => {
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1186
|
-
return generateStatements(...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)), ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)), ...descriptors.map(([name, props]) => {
|
|
1187
|
-
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1188
|
-
const type = pascalCase(`WServiceWith_${name}`);
|
|
1189
|
-
const value = generateArray(props.map((v) => v.name));
|
|
1190
|
-
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1191
|
-
}), ...typeGuards);
|
|
1192
|
-
};
|
|
1193
|
-
|
|
1123
|
+
const entityDescriptors = /* @__PURE__ */ new Map();
|
|
1124
|
+
for (const service of services) for (const fn of service.functions) {
|
|
1125
|
+
if (!FILTER_REGEX.test(fn.name)) continue;
|
|
1126
|
+
entityDescriptors.set(fn.name, [...entityDescriptors.get(fn.name) ?? [], {
|
|
1127
|
+
name: service.name,
|
|
1128
|
+
required: true,
|
|
1129
|
+
type: `${pascalCase(service.name)}Service_${pascalCase(fn.name)}`
|
|
1130
|
+
}]);
|
|
1131
|
+
}
|
|
1132
|
+
const descriptors = [...entityDescriptors.entries()];
|
|
1133
|
+
const typeGuards = [];
|
|
1134
|
+
for (const [name] of descriptors) {
|
|
1135
|
+
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1136
|
+
const service = pascalCase(`WServiceWith_${name}`);
|
|
1137
|
+
const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
|
|
1138
|
+
typeGuards.push(`export const is${service} = ${guard}`);
|
|
1139
|
+
}
|
|
1140
|
+
return generateStatements(...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)), ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)), ...descriptors.map(([name, props]) => {
|
|
1141
|
+
return `export const ${camelCase(`wServiceWith_${name}_Names`)}: ${pascalCase(`WServiceWith_${name}`)}[] = ${generateArray(props.map((v) => v.name))};`;
|
|
1142
|
+
}), ...typeGuards);
|
|
1143
|
+
};
|
|
1144
|
+
//#endregion
|
|
1145
|
+
//#region src/generator/05-maps/index.ts
|
|
1194
1146
|
const generateMaps = (enums, entities, services, context, options) => {
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
/* Services */
|
|
1228
|
-
serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
|
|
1229
|
-
/* Service Utils */
|
|
1230
|
-
generateGroupedServices(generatedServices), generateCustomValueServices(generatedServices),
|
|
1231
|
-
/* Entity Properties (Runtime Meta Infos) */
|
|
1232
|
-
generateEntityProperties(entities, generatedServices, options));
|
|
1233
|
-
};
|
|
1234
|
-
|
|
1147
|
+
const enumInstances = `export const wEnums = ${generateObject([...enums.keys()].map((v) => ({
|
|
1148
|
+
key: v,
|
|
1149
|
+
value: v
|
|
1150
|
+
})))};`;
|
|
1151
|
+
const entityNames = `export const wEntityNames: WEntity[] = ${generateArray([...entities.keys()])};`;
|
|
1152
|
+
const generatedServices = [...services.values()];
|
|
1153
|
+
const serviceInstances = `export const wServices = ${generateObject(generatedServices.map((v) => ({
|
|
1154
|
+
key: v.name,
|
|
1155
|
+
value: `${v.serviceFnName}()`,
|
|
1156
|
+
comment: v.deprecated ? "@deprecated" : void 0
|
|
1157
|
+
})))};`;
|
|
1158
|
+
const serviceFactories = `export const wServiceFactories = ${generateObject(generatedServices.map((v) => ({
|
|
1159
|
+
key: v.name,
|
|
1160
|
+
value: v.serviceFnName,
|
|
1161
|
+
comment: v.deprecated ? "@deprecated" : void 0
|
|
1162
|
+
})))};`;
|
|
1163
|
+
return generateStatements(generateInterface("WEnums", [...enums.keys()].map((name) => ({
|
|
1164
|
+
name,
|
|
1165
|
+
type: name,
|
|
1166
|
+
required: true
|
|
1167
|
+
}))), generateType("WEnum", "keyof WEnums"), enumInstances, generateInterface("WEntities", [...[...entities.entries()].map(([name, entity]) => ({
|
|
1168
|
+
name,
|
|
1169
|
+
type: entity.interfaceName,
|
|
1170
|
+
required: true
|
|
1171
|
+
})), ...generatedServices.filter(({ relatedEntity }) => !!relatedEntity).filter(({ name }) => !entities.get(name)).map(({ name, relatedEntity }) => ({
|
|
1172
|
+
name,
|
|
1173
|
+
type: relatedEntity.interfaceName,
|
|
1174
|
+
required: true
|
|
1175
|
+
}))].sort((a, b) => a.name > b.name ? 1 : -1)), generateType("WEntity", "keyof WEntities"), entityNames, serviceInstances, generateType("WServices", "typeof wServices"), generateType("WService", "keyof WServices"), serviceFactories, generateType("WServiceFactories", "typeof wServiceFactories"), generateGroupedServices(generatedServices), generateCustomValueServices(generatedServices), generateEntityProperties(entities, generatedServices, options));
|
|
1176
|
+
};
|
|
1177
|
+
//#endregion
|
|
1178
|
+
//#region src/utils/weclapp/extractRelatedEntityName.ts
|
|
1235
1179
|
function extractRelatedEntityName(serviceEndpoints, responses) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (!resultSchema) {
|
|
1257
|
-
return;
|
|
1258
|
-
}
|
|
1259
|
-
if (isReferenceObject(resultSchema)) {
|
|
1260
|
-
return getRefName(resultSchema);
|
|
1261
|
-
}
|
|
1262
|
-
else if (isArraySchemaObject(resultSchema)) {
|
|
1263
|
-
const resultItemSchema = resultSchema.items;
|
|
1264
|
-
if (isReferenceObject(resultItemSchema)) {
|
|
1265
|
-
return getRefName(resultItemSchema);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1180
|
+
const rootEndpoint = serviceEndpoints.find((v) => v.endpoint.type === WeclappEndpointType.ROOT);
|
|
1181
|
+
if (!rootEndpoint) return;
|
|
1182
|
+
const response = rootEndpoint?.path.get?.responses["200"];
|
|
1183
|
+
if (!response) return;
|
|
1184
|
+
let responseObject;
|
|
1185
|
+
if (isReferenceObject(response)) {
|
|
1186
|
+
const refName = getRefName(response);
|
|
1187
|
+
responseObject = responses.get(refName);
|
|
1188
|
+
} else responseObject = response;
|
|
1189
|
+
const responseSchema = responseObject?.content?.["application/json"].schema;
|
|
1190
|
+
if (responseSchema) {
|
|
1191
|
+
if (isReferenceObject(responseSchema)) return;
|
|
1192
|
+
const resultSchema = responseSchema.properties?.result;
|
|
1193
|
+
if (!resultSchema) return;
|
|
1194
|
+
if (isReferenceObject(resultSchema)) return getRefName(resultSchema);
|
|
1195
|
+
else if (isArraySchemaObject(resultSchema)) {
|
|
1196
|
+
const resultItemSchema = resultSchema.items;
|
|
1197
|
+
if (isReferenceObject(resultItemSchema)) return getRefName(resultItemSchema);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1269
1200
|
}
|
|
1270
|
-
|
|
1201
|
+
//#endregion
|
|
1202
|
+
//#region src/utils/weclapp/extractContext.ts
|
|
1271
1203
|
const extractServiceAliases = (endpoints, responses) => {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
return aliases;
|
|
1204
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const [serviceName, serviceEndpoints] of endpoints) {
|
|
1206
|
+
const relatedEntityName = extractRelatedEntityName(serviceEndpoints, responses);
|
|
1207
|
+
if (relatedEntityName) aliases.set(serviceName, relatedEntityName);
|
|
1208
|
+
}
|
|
1209
|
+
return aliases;
|
|
1279
1210
|
};
|
|
1280
1211
|
const extractContext = (doc) => {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
for (const [name, requestBody] of Object.entries(doc.components?.requestBodies ?? {})) {
|
|
1302
|
-
if (!isReferenceObject(requestBody)) {
|
|
1303
|
-
requestBodies.set(name, requestBody);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
const aliases = extractServiceAliases(endpoints, responses);
|
|
1307
|
-
return { endpoints, schemas, responses, parameters, requestBodies, aliases };
|
|
1308
|
-
};
|
|
1309
|
-
|
|
1212
|
+
const endpoints = parseEndpointsPaths(doc.paths);
|
|
1213
|
+
const schemas = /* @__PURE__ */ new Map();
|
|
1214
|
+
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) if (!isReferenceObject(schema)) schemas.set(name, schema);
|
|
1215
|
+
const responses = /* @__PURE__ */ new Map();
|
|
1216
|
+
for (const [name, response] of Object.entries(doc.components?.responses ?? {})) if (!isReferenceObject(response)) responses.set(name, response);
|
|
1217
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
1218
|
+
for (const [name, parameter] of Object.entries(doc.components?.parameters ?? {})) if (!isReferenceObject(parameter)) parameters.set(name, parameter);
|
|
1219
|
+
const requestBodies = /* @__PURE__ */ new Map();
|
|
1220
|
+
for (const [name, requestBody] of Object.entries(doc.components?.requestBodies ?? {})) if (!isReferenceObject(requestBody)) requestBodies.set(name, requestBody);
|
|
1221
|
+
return {
|
|
1222
|
+
endpoints,
|
|
1223
|
+
schemas,
|
|
1224
|
+
responses,
|
|
1225
|
+
parameters,
|
|
1226
|
+
requestBodies,
|
|
1227
|
+
aliases: extractServiceAliases(endpoints, responses)
|
|
1228
|
+
};
|
|
1229
|
+
};
|
|
1230
|
+
//#endregion
|
|
1231
|
+
//#region src/generator/generate.ts
|
|
1310
1232
|
const generate = (doc, options) => {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
};
|
|
1330
|
-
|
|
1331
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
1233
|
+
const context = extractContext(doc);
|
|
1234
|
+
const base = generateBase(doc.info.version, options);
|
|
1235
|
+
const enums = generateEnums(context);
|
|
1236
|
+
const entities = generateEntities(context);
|
|
1237
|
+
const services = generateServices(entities, context, options);
|
|
1238
|
+
const maps = generateMaps(enums, entities, services, context, options);
|
|
1239
|
+
return generateStatements(generateBlockComment("BASE", base), generateBlockComment("ENUMS", generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment("ENTITIES", generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment("FILTERS", generateStatements(...[...entities.values()].map((v) => v.filterSource))), generateBlockComment("SERVICES", generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment("MAPS", maps));
|
|
1240
|
+
};
|
|
1241
|
+
//#endregion
|
|
1242
|
+
//#region src/utils/hash.ts
|
|
1243
|
+
const hash = (content, algorithm = "sha256") => {
|
|
1244
|
+
const hash = createHash(algorithm);
|
|
1245
|
+
if (Array.isArray(content)) content.map(hash.update.bind(hash));
|
|
1246
|
+
else hash.update(content);
|
|
1247
|
+
return hash.digest("hex");
|
|
1248
|
+
};
|
|
1249
|
+
//#endregion
|
|
1250
|
+
//#region src/cli.ts
|
|
1332
1251
|
const cli = async () => {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
logger.infoLn('Bundle... (this may take some time)');
|
|
1471
|
-
await bundle(workingDir, options.target);
|
|
1472
|
-
// Remove index.ts (only bundle is required)
|
|
1473
|
-
await rm(await workingDirPath('src'), { recursive: true, force: true });
|
|
1474
|
-
if (useCache) {
|
|
1475
|
-
// Copy SDK to cache
|
|
1476
|
-
logger.successLn(`Caching SDK: (${cachedSdkDir})`);
|
|
1477
|
-
await mkdir(cachedSdkDir, { recursive: true });
|
|
1478
|
-
await cp(workingDir, cachedSdkDir, { recursive: true });
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
// Print job summary
|
|
1482
|
-
const duration = (process.hrtime.bigint() - start) / 1000000n;
|
|
1483
|
-
logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
|
|
1484
|
-
logger.printSummary();
|
|
1485
|
-
})()
|
|
1486
|
-
.catch((error) => {
|
|
1487
|
-
logger.errorLn(`Fatal error:`);
|
|
1488
|
-
/* eslint-disable no-console */
|
|
1489
|
-
console.error(error);
|
|
1490
|
-
})
|
|
1491
|
-
.finally(() => {
|
|
1492
|
-
if (logger.errors)
|
|
1493
|
-
process.exit(1);
|
|
1252
|
+
const version = pkg.version;
|
|
1253
|
+
const { argv } = yargs(hideBin(process.argv)).scriptName("build-weclapp-sdk").usage("Usage: $0 <source> [flags]").version(version).example("$0 openapi.json", "Generate the SDK based on a local openapi file").example("$0 openapi.json", "Generate the SDK based on a local openapi file").example("$0 xxx.weclapp.com --key ...", "Generate the SDK based on the openapi file from the given weclapp instance").help("h").alias("v", "version").alias("h", "help").option("k", {
|
|
1254
|
+
alias: "key",
|
|
1255
|
+
describe: "API Key (only needed when not using a local file)",
|
|
1256
|
+
type: "string"
|
|
1257
|
+
}).option("c", {
|
|
1258
|
+
alias: "cache",
|
|
1259
|
+
describe: "If the generated SDK should cached",
|
|
1260
|
+
type: "boolean"
|
|
1261
|
+
}).option("q", {
|
|
1262
|
+
alias: "query",
|
|
1263
|
+
describe: "Extra query params when fetching the openapi.json from a server",
|
|
1264
|
+
type: "string"
|
|
1265
|
+
}).option("generate-unique", {
|
|
1266
|
+
describe: "Generate .unique functions",
|
|
1267
|
+
type: "boolean"
|
|
1268
|
+
}).option("d", {
|
|
1269
|
+
alias: "deprecated",
|
|
1270
|
+
describe: "Include deprecated functions and services",
|
|
1271
|
+
type: "boolean"
|
|
1272
|
+
}).option("e", {
|
|
1273
|
+
alias: "from-env",
|
|
1274
|
+
describe: "Use env variables WECLAPP_BACKEND_URL and WECLAPP_API_KEY as credentials",
|
|
1275
|
+
type: "boolean"
|
|
1276
|
+
}).option("t", {
|
|
1277
|
+
alias: "target",
|
|
1278
|
+
describe: "Specify the target platform",
|
|
1279
|
+
type: "string",
|
|
1280
|
+
choices: [
|
|
1281
|
+
"browser",
|
|
1282
|
+
"browser.rx",
|
|
1283
|
+
"node",
|
|
1284
|
+
"node.rx"
|
|
1285
|
+
]
|
|
1286
|
+
}).option("use-query-language", {
|
|
1287
|
+
describe: "Generate the new where property for some and count queries",
|
|
1288
|
+
type: "boolean"
|
|
1289
|
+
}).option("apiVersion", {
|
|
1290
|
+
describe: "Specify the api version (only needed when not using a local file)",
|
|
1291
|
+
type: "string"
|
|
1292
|
+
}).epilog(`Copyright ${(/* @__PURE__ */ new Date()).getFullYear()} weclapp GmbH`);
|
|
1293
|
+
if (argv.fromEnv) config();
|
|
1294
|
+
const { WECLAPP_API_KEY, WECLAPP_BACKEND_URL } = process.env;
|
|
1295
|
+
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, apiVersion, _: [src = WECLAPP_BACKEND_URL] } = argv;
|
|
1296
|
+
const options = {
|
|
1297
|
+
deprecated,
|
|
1298
|
+
generateUnique: argv.generateUnique ?? false,
|
|
1299
|
+
target: argv.target ?? Target.BROWSER_PROMISES,
|
|
1300
|
+
useQueryLanguage: argv.useQueryLanguage ?? false
|
|
1301
|
+
};
|
|
1302
|
+
if (!src || typeof src === "number") return Promise.reject(/* @__PURE__ */ new Error("Expected string as command"));
|
|
1303
|
+
if (!Object.values(Target).includes(options.target)) {
|
|
1304
|
+
logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(", ")}`);
|
|
1305
|
+
return Promise.reject(/* @__PURE__ */ new Error());
|
|
1306
|
+
}
|
|
1307
|
+
if (await stat(src).catch(() => false)) {
|
|
1308
|
+
logger.infoLn(`Source is a file`);
|
|
1309
|
+
return {
|
|
1310
|
+
cache,
|
|
1311
|
+
content: JSON.parse(await readFile(src, "utf-8")),
|
|
1312
|
+
options
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
logger.infoLn(`Source is a URL`);
|
|
1316
|
+
if (!key) return Promise.reject(/* @__PURE__ */ new Error("API key is missing"));
|
|
1317
|
+
if (!apiVersion) return Promise.reject(/* @__PURE__ */ new Error("API version is missing"));
|
|
1318
|
+
const url = new URL(src.startsWith("http") ? src : `https://${src}`);
|
|
1319
|
+
url.pathname = `/webapp/api/${apiVersion}/meta/openapi.json`;
|
|
1320
|
+
if (query?.length) for (const param of query.split(",")) {
|
|
1321
|
+
const [name, value] = param.split("=");
|
|
1322
|
+
url.searchParams.set(name, value);
|
|
1323
|
+
}
|
|
1324
|
+
const content = await fetch(url.toString(), { headers: {
|
|
1325
|
+
Accept: "application/json",
|
|
1326
|
+
AuthenticationToken: key
|
|
1327
|
+
} }).then((res) => res.ok ? res.json() : void 0);
|
|
1328
|
+
if (!content) {
|
|
1329
|
+
logger.errorLn(`Couldn't fetch file ${url.toString()} `);
|
|
1330
|
+
return Promise.reject(/* @__PURE__ */ new Error());
|
|
1331
|
+
} else logger.infoLn(`Use remote file: ${url.toString()}`);
|
|
1332
|
+
return {
|
|
1333
|
+
cache,
|
|
1334
|
+
content,
|
|
1335
|
+
options
|
|
1336
|
+
};
|
|
1337
|
+
};
|
|
1338
|
+
//#endregion
|
|
1339
|
+
//#region src/index.ts
|
|
1340
|
+
const workingDir = resolve(currentDirname(), "./sdk");
|
|
1341
|
+
const cacheDir = resolve(currentDirname(), "./.cache");
|
|
1342
|
+
(async () => {
|
|
1343
|
+
const start = process.hrtime.bigint();
|
|
1344
|
+
const { content: doc, cache: useCache, options } = await cli();
|
|
1345
|
+
const workingDirPath = async (...paths) => {
|
|
1346
|
+
const fullPath = resolve(workingDir, ...paths);
|
|
1347
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
1348
|
+
return fullPath;
|
|
1349
|
+
};
|
|
1350
|
+
const cacheKey = hash([
|
|
1351
|
+
pkg.version,
|
|
1352
|
+
JSON.stringify(doc),
|
|
1353
|
+
JSON.stringify(options)
|
|
1354
|
+
]).slice(-8);
|
|
1355
|
+
const cachedSdkDir = resolve(cacheDir, cacheKey);
|
|
1356
|
+
await rm(workingDir, {
|
|
1357
|
+
recursive: true,
|
|
1358
|
+
force: true
|
|
1359
|
+
});
|
|
1360
|
+
if (useCache) logger.infoLn(`Cache ID: ${cacheKey}`);
|
|
1361
|
+
if (useCache && await stat(cachedSdkDir).catch(() => false)) {
|
|
1362
|
+
logger.successLn(`Cache match! (${cachedSdkDir})`);
|
|
1363
|
+
await cp(cachedSdkDir, workingDir, { recursive: true });
|
|
1364
|
+
} else {
|
|
1365
|
+
await writeFile(await workingDirPath("openapi.json"), JSON.stringify(doc, null, 2));
|
|
1366
|
+
logger.infoLn(`Generate sdk (target: ${options.target})`);
|
|
1367
|
+
const sdk = generate(doc, options);
|
|
1368
|
+
await writeFile(await workingDirPath("src", "index.ts"), sdk.trim() + "\n");
|
|
1369
|
+
logger.infoLn("Bundle... (this may take some time)");
|
|
1370
|
+
await bundle(workingDir, options.target);
|
|
1371
|
+
await rm(await workingDirPath("src"), {
|
|
1372
|
+
recursive: true,
|
|
1373
|
+
force: true
|
|
1374
|
+
});
|
|
1375
|
+
if (useCache) {
|
|
1376
|
+
logger.successLn(`Caching SDK: (${cachedSdkDir})`);
|
|
1377
|
+
await mkdir(cachedSdkDir, { recursive: true });
|
|
1378
|
+
await cp(workingDir, cachedSdkDir, { recursive: true });
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
const duration = (process.hrtime.bigint() - start) / 1000000n;
|
|
1382
|
+
logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
|
|
1383
|
+
logger.printSummary();
|
|
1384
|
+
})().catch((error) => {
|
|
1385
|
+
logger.errorLn(`Fatal error:`);
|
|
1386
|
+
console.error(error);
|
|
1387
|
+
}).finally(() => {
|
|
1388
|
+
if (logger.errors) process.exit(1);
|
|
1494
1389
|
});
|
|
1390
|
+
//#endregion
|