@weclapp/sdk 2.0.0-dev.23 → 2.0.0-dev.25
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/README.md +5 -5
- package/bin/cli.js +1 -1
- package/dist/cli.js +476 -383
- package/package.json +34 -27
- package/tsconfig.sdk.json +14 -0
- package/tsconfig.lib.json +0 -17
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { fileURLToPath } from 'url';
|
|
2
2
|
import { resolve, dirname } from 'path';
|
|
3
|
-
import { rmdir, stat, readFile, writeFile, rm, cp, mkdir } from 'fs/promises';
|
|
4
3
|
import { rollup } from 'rollup';
|
|
5
4
|
import terser from '@rollup/plugin-terser';
|
|
6
|
-
import ts from 'rollup
|
|
5
|
+
import ts from '@rollup/plugin-typescript';
|
|
7
6
|
import indentString from 'indent-string';
|
|
8
7
|
import { snakeCase, pascalCase, camelCase } from 'change-case';
|
|
9
8
|
import chalk from 'chalk';
|
|
10
9
|
import { OpenAPIV3 } from 'openapi-types';
|
|
11
10
|
import { createHash } from 'crypto';
|
|
11
|
+
import { stat, readFile, rm, cp, writeFile, mkdir } from 'fs/promises';
|
|
12
12
|
import { config } from 'dotenv';
|
|
13
13
|
import yargs from 'yargs';
|
|
14
14
|
import { hideBin } from 'yargs/helpers';
|
|
15
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
15
16
|
import prettyMs from 'pretty-ms';
|
|
16
17
|
|
|
17
18
|
var Target;
|
|
@@ -28,87 +29,74 @@ const isRXTarget = (target) => {
|
|
|
28
29
|
return target === Target.BROWSER_RX || target === Target.NODE_RX;
|
|
29
30
|
};
|
|
30
31
|
const resolveResponseType = (target) => {
|
|
31
|
-
return isRXTarget(target) ?
|
|
32
|
+
return isRXTarget(target) ? "Observable" : "Promise";
|
|
32
33
|
};
|
|
33
34
|
const resolveBinaryType = (target) => {
|
|
34
|
-
return isNodeTarget(target) ?
|
|
35
|
+
return isNodeTarget(target) ? "Buffer" : "Blob";
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
const currentDirname = () => {
|
|
38
39
|
// Go one level up as the CLI is inside a folder
|
|
39
|
-
return fileURLToPath(new URL(
|
|
40
|
+
return fileURLToPath(new URL("..", import.meta.url));
|
|
40
41
|
};
|
|
41
42
|
|
|
42
|
-
const tsconfig = resolve(currentDirname(),
|
|
43
|
-
const resolveGlobals = (...globals) => Object.fromEntries(globals.map(v => [v,
|
|
43
|
+
const tsconfig = resolve(currentDirname(), "./tsconfig.sdk.json");
|
|
44
|
+
const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, "*"]));
|
|
44
45
|
const generateOutput = (config) => ({
|
|
45
46
|
sourcemap: true,
|
|
46
47
|
banner: `/* weclapp sdk */`,
|
|
47
|
-
...config
|
|
48
|
+
...config,
|
|
48
49
|
});
|
|
49
50
|
const bundle = async (workingDirectory, target) => {
|
|
50
|
-
const dist = (...paths) => resolve(workingDirectory,
|
|
51
|
-
const src = (...paths) => resolve(workingDirectory,
|
|
51
|
+
const dist = (...paths) => resolve(workingDirectory, "dist", ...paths);
|
|
52
|
+
const src = (...paths) => resolve(workingDirectory, "src", ...paths);
|
|
52
53
|
const generateNodeOutput = () => [
|
|
53
54
|
generateOutput({
|
|
54
|
-
file: dist(
|
|
55
|
-
format:
|
|
56
|
-
globals: resolveGlobals(
|
|
55
|
+
file: dist("index.cjs"),
|
|
56
|
+
format: "cjs",
|
|
57
|
+
globals: resolveGlobals("node-fetch", "url"),
|
|
57
58
|
}),
|
|
58
59
|
generateOutput({
|
|
59
|
-
file: dist(
|
|
60
|
-
format:
|
|
61
|
-
globals: resolveGlobals(
|
|
62
|
-
})
|
|
60
|
+
file: dist("index.js"),
|
|
61
|
+
format: "es",
|
|
62
|
+
globals: resolveGlobals("node-fetch", "url"),
|
|
63
|
+
}),
|
|
63
64
|
];
|
|
64
|
-
// Remove build dir
|
|
65
|
-
await rmdir(dist()).catch(() => void 0);
|
|
66
65
|
const bundles = {
|
|
67
66
|
[Target.BROWSER_PROMISES]: () => ({
|
|
68
|
-
input: src(
|
|
67
|
+
input: src("index.ts"),
|
|
68
|
+
plugins: [ts({ tsconfig, declarationDir: dist() }), terser()],
|
|
69
69
|
output: [
|
|
70
70
|
generateOutput({
|
|
71
|
-
file: dist(
|
|
72
|
-
|
|
73
|
-
format: 'umd'
|
|
71
|
+
file: dist("index.js"),
|
|
72
|
+
format: "es",
|
|
74
73
|
}),
|
|
75
|
-
generateOutput({
|
|
76
|
-
file: dist('index.js'),
|
|
77
|
-
format: 'es'
|
|
78
|
-
})
|
|
79
74
|
],
|
|
80
|
-
plugins: [ts({ tsconfig }), terser()]
|
|
81
75
|
}),
|
|
82
76
|
[Target.BROWSER_RX]: () => ({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
input: src("index.ts"),
|
|
78
|
+
plugins: [ts({ tsconfig, declarationDir: dist() }), terser()],
|
|
79
|
+
external: ["rxjs"],
|
|
86
80
|
output: [
|
|
87
81
|
generateOutput({
|
|
88
|
-
file: dist(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
globals: resolveGlobals('rxjs')
|
|
82
|
+
file: dist("index.js"),
|
|
83
|
+
format: "es",
|
|
84
|
+
globals: resolveGlobals("rxjs"),
|
|
92
85
|
}),
|
|
93
|
-
|
|
94
|
-
file: dist('index.js'),
|
|
95
|
-
format: 'es',
|
|
96
|
-
globals: resolveGlobals('rxjs')
|
|
97
|
-
})
|
|
98
|
-
]
|
|
86
|
+
],
|
|
99
87
|
}),
|
|
100
88
|
[Target.NODE_PROMISES]: () => ({
|
|
101
|
-
input: src(
|
|
89
|
+
input: src("index.ts"),
|
|
90
|
+
plugins: [ts({ tsconfig, declarationDir: dist() }), terser()],
|
|
91
|
+
external: ["node-fetch", "url"],
|
|
102
92
|
output: generateNodeOutput(),
|
|
103
|
-
external: ['node-fetch', 'url'],
|
|
104
|
-
plugins: [ts({ tsconfig })]
|
|
105
93
|
}),
|
|
106
94
|
[Target.NODE_RX]: () => ({
|
|
107
|
-
input: src(
|
|
95
|
+
input: src("index.ts"),
|
|
96
|
+
plugins: [ts({ tsconfig, declarationDir: dist() }), terser()],
|
|
97
|
+
external: ["node-fetch", "url", "rxjs"],
|
|
108
98
|
output: generateNodeOutput(),
|
|
109
|
-
|
|
110
|
-
plugins: [ts({ tsconfig })]
|
|
111
|
-
})
|
|
99
|
+
}),
|
|
112
100
|
};
|
|
113
101
|
const config = bundles[target]();
|
|
114
102
|
const bundle = await rollup(config);
|
|
@@ -125,8 +113,11 @@ const generateString = (str) => `'${str}'`;
|
|
|
125
113
|
const generateStrings = (str) => str.map(generateString);
|
|
126
114
|
|
|
127
115
|
const generateImport = (opt) => {
|
|
128
|
-
const imports = [
|
|
129
|
-
|
|
116
|
+
const imports = [
|
|
117
|
+
opt.default,
|
|
118
|
+
opt.imports?.length ? `{${opt.imports.join(", ")}}` : "",
|
|
119
|
+
];
|
|
120
|
+
return `import ${imports.filter(Boolean).join(", ")} from ${generateString(opt.src)};`;
|
|
130
121
|
};
|
|
131
122
|
|
|
132
123
|
/**
|
|
@@ -139,9 +130,9 @@ const indent = (s, level = 1) => {
|
|
|
139
130
|
};
|
|
140
131
|
|
|
141
132
|
const generateStatements = (...statements) => statements
|
|
142
|
-
.map(v => v.trim())
|
|
143
|
-
.filter(v => v.length)
|
|
144
|
-
.join(
|
|
133
|
+
.map((v) => v.trim())
|
|
134
|
+
.filter((v) => v.length)
|
|
135
|
+
.join("\n\n");
|
|
145
136
|
const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
|
|
146
137
|
|
|
147
138
|
var globalConfig = "export type RequestPayloadMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | '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\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\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?: (request: Request, payload: RequestPayload) => 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\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};";
|
|
@@ -155,11 +146,11 @@ var types = "export type DeepPartial<T> = T extends object ? {\n [P in keyof
|
|
|
155
146
|
const resolveImports = (target) => {
|
|
156
147
|
const imports = [];
|
|
157
148
|
if (isRXTarget(target)) {
|
|
158
|
-
imports.push(generateImport({ src:
|
|
149
|
+
imports.push(generateImport({ src: "rxjs", imports: ["defer", "Observable"] }));
|
|
159
150
|
}
|
|
160
|
-
return imports.join(
|
|
151
|
+
return imports.join("\n");
|
|
161
152
|
};
|
|
162
|
-
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ?
|
|
153
|
+
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? "defer" : "(v: (...args: any[]) => any) => v()"};`;
|
|
163
154
|
const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
|
|
164
155
|
const generateBase = (target, apiVersion) => {
|
|
165
156
|
return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), `const apiVersion = ${apiVersion}`, globalConfig, multiRequest, types, root);
|
|
@@ -167,7 +158,7 @@ const generateBase = (target, apiVersion) => {
|
|
|
167
158
|
|
|
168
159
|
const transformKey = (s) => snakeCase(s).toUpperCase();
|
|
169
160
|
const generateEnum = (name, values) => {
|
|
170
|
-
const props = indent(values.map(v => `${transformKey(v)} = ${generateString(v)}`).join(
|
|
161
|
+
const props = indent(values.map((v) => `${transformKey(v)} = ${generateString(v)}`).join(",\n"));
|
|
171
162
|
return `export enum ${name} {\n${props}\n}`;
|
|
172
163
|
};
|
|
173
164
|
|
|
@@ -175,31 +166,34 @@ const generateEnum = (name, values) => {
|
|
|
175
166
|
const loosePascalCase = (str) => str[0].toUpperCase() + str.slice(1);
|
|
176
167
|
|
|
177
168
|
const isObject = (v) => {
|
|
178
|
-
return v !== null && typeof v ===
|
|
169
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
179
170
|
};
|
|
180
171
|
const isParameterObject = (v) => {
|
|
181
|
-
return isObject(v) && typeof v.name ===
|
|
172
|
+
return isObject(v) && typeof v.name === "string" && typeof v.in === "string";
|
|
182
173
|
};
|
|
183
174
|
const isReferenceObject = (v) => {
|
|
184
|
-
return isObject(v) && typeof v.$ref ===
|
|
175
|
+
return isObject(v) && typeof v.$ref === "string";
|
|
185
176
|
};
|
|
186
177
|
const isObjectSchemaObject = (v) => {
|
|
187
|
-
return isObject(v) && v.type ===
|
|
178
|
+
return isObject(v) && v.type === "object" && isObject(v.properties);
|
|
188
179
|
};
|
|
189
180
|
const isEnumSchemaObject = (v) => {
|
|
190
|
-
return isObject(v) && v.type ===
|
|
181
|
+
return isObject(v) && v.type === "string" && Array.isArray(v.enum);
|
|
191
182
|
};
|
|
192
183
|
const isArraySchemaObject = (v) => {
|
|
193
|
-
return isObject(v) && v.type ===
|
|
184
|
+
return isObject(v) && v.type === "array" && typeof v.items === "object";
|
|
194
185
|
};
|
|
195
186
|
const isResponseObject = (v) => {
|
|
196
|
-
return isObject(v) && typeof v.description ===
|
|
187
|
+
return isObject(v) && typeof v.description === "string";
|
|
197
188
|
};
|
|
198
189
|
const isNonArraySchemaObject = (v) => {
|
|
199
|
-
return isObject(v) && [
|
|
190
|
+
return isObject(v) && ["string", "undefined"].includes(typeof v.type);
|
|
200
191
|
};
|
|
201
192
|
const isRelatedEntitySchema = (v) => {
|
|
202
|
-
return isObject(v) &&
|
|
193
|
+
return (isObject(v) &&
|
|
194
|
+
isNonArraySchemaObject(v) &&
|
|
195
|
+
"x-weclapp" in v &&
|
|
196
|
+
isObject(v["x-weclapp"]));
|
|
203
197
|
};
|
|
204
198
|
|
|
205
199
|
const generateEnums = (schemas) => {
|
|
@@ -210,7 +204,7 @@ const generateEnums = (schemas) => {
|
|
|
210
204
|
if (!enums.has(name)) {
|
|
211
205
|
enums.set(name, {
|
|
212
206
|
properties: schema.enum,
|
|
213
|
-
source: generateEnum(name, schema.enum)
|
|
207
|
+
source: generateEnum(name, schema.enum),
|
|
214
208
|
});
|
|
215
209
|
}
|
|
216
210
|
}
|
|
@@ -218,57 +212,62 @@ const generateEnums = (schemas) => {
|
|
|
218
212
|
return enums;
|
|
219
213
|
};
|
|
220
214
|
|
|
221
|
-
const concat = (strings, separator =
|
|
215
|
+
const concat = (strings, separator = ", ", maxLength = 80) => {
|
|
222
216
|
const joined = strings.join(separator);
|
|
223
217
|
if (joined.length > maxLength) {
|
|
224
218
|
const length = strings.length - 1;
|
|
225
219
|
return `\n${indent(strings
|
|
226
220
|
.map((value, index) => index === length ? value : `${(value + separator).trim()}\n`)
|
|
227
|
-
.join(
|
|
221
|
+
.join(""))}\n`;
|
|
228
222
|
}
|
|
229
223
|
else {
|
|
230
224
|
return joined;
|
|
231
225
|
}
|
|
232
226
|
};
|
|
233
227
|
|
|
234
|
-
/* eslint-disable no-use-before-define */
|
|
235
228
|
const createReferenceType = (value) => ({
|
|
236
|
-
type:
|
|
237
|
-
toString: () => loosePascalCase(value)
|
|
229
|
+
type: "reference",
|
|
230
|
+
toString: () => loosePascalCase(value),
|
|
238
231
|
});
|
|
239
232
|
const createRawType = (value) => ({
|
|
240
|
-
type:
|
|
241
|
-
toString: () => value
|
|
233
|
+
type: "raw",
|
|
234
|
+
toString: () => value,
|
|
242
235
|
});
|
|
243
236
|
const createArrayType = (value) => ({
|
|
244
|
-
type:
|
|
245
|
-
toString: () => `(${value.toString()})[]
|
|
237
|
+
type: "array",
|
|
238
|
+
toString: () => `(${value.toString()})[]`,
|
|
246
239
|
});
|
|
247
240
|
const createTupleType = (value) => ({
|
|
248
|
-
type:
|
|
249
|
-
toString: () => concat([
|
|
241
|
+
type: "tuple",
|
|
242
|
+
toString: () => concat([
|
|
243
|
+
...new Set(value.map((v) => (typeof v === "string" ? `'${v}'` : v.toString()))),
|
|
244
|
+
], " | "),
|
|
250
245
|
});
|
|
251
246
|
const createObjectType = (value, required = []) => ({
|
|
252
|
-
type:
|
|
247
|
+
type: "object",
|
|
253
248
|
isFullyOptional: () => {
|
|
254
|
-
return !required.length &&
|
|
255
|
-
.
|
|
256
|
-
|
|
249
|
+
return (!required.length &&
|
|
250
|
+
Object.values(value)
|
|
251
|
+
.filter((v) => v?.type === "object")
|
|
252
|
+
.every((v) => v.isFullyOptional()));
|
|
257
253
|
},
|
|
258
254
|
toString: (propagateOptionalProperties = false) => {
|
|
259
255
|
const properties = Object.entries(value)
|
|
260
|
-
.filter(v => v[1])
|
|
261
|
-
.map(v => {
|
|
256
|
+
.filter((v) => v[1])
|
|
257
|
+
.map((v) => {
|
|
262
258
|
const name = v[0];
|
|
263
259
|
const value = v[1];
|
|
264
|
-
const isRequired = required.includes(name) ||
|
|
265
|
-
|
|
260
|
+
const isRequired = required.includes(name) ||
|
|
261
|
+
(value.type === "object" &&
|
|
262
|
+
!value.isFullyOptional() &&
|
|
263
|
+
propagateOptionalProperties);
|
|
264
|
+
return `${name + (isRequired ? "" : "?")}: ${value.toString()};`;
|
|
266
265
|
});
|
|
267
|
-
return properties.length ? `{\n${indent(properties.join(
|
|
268
|
-
}
|
|
266
|
+
return properties.length ? `{\n${indent(properties.join("\n"))}\n}` : "{}";
|
|
267
|
+
},
|
|
269
268
|
});
|
|
270
269
|
const getRefName = (obj) => {
|
|
271
|
-
return obj.$ref.replace(/.*\//,
|
|
270
|
+
return obj.$ref.replace(/.*\//, "");
|
|
272
271
|
};
|
|
273
272
|
const convertToTypeScriptType = (schema, property) => {
|
|
274
273
|
if (isReferenceObject(schema)) {
|
|
@@ -276,27 +275,33 @@ const convertToTypeScriptType = (schema, property) => {
|
|
|
276
275
|
}
|
|
277
276
|
else {
|
|
278
277
|
switch (schema.type) {
|
|
279
|
-
case
|
|
280
|
-
case
|
|
281
|
-
return createRawType(
|
|
282
|
-
case
|
|
278
|
+
case "integer":
|
|
279
|
+
case "number":
|
|
280
|
+
return createRawType("number");
|
|
281
|
+
case "string":
|
|
283
282
|
if (schema.enum) {
|
|
284
|
-
return property
|
|
283
|
+
return property
|
|
284
|
+
? createReferenceType(property)
|
|
285
|
+
: createTupleType(schema.enum);
|
|
285
286
|
}
|
|
286
287
|
else {
|
|
287
|
-
return schema.format ===
|
|
288
|
+
return schema.format === "binary"
|
|
289
|
+
? createRawType("binary")
|
|
290
|
+
: createRawType("string");
|
|
288
291
|
}
|
|
289
|
-
case
|
|
290
|
-
return createRawType(
|
|
291
|
-
case
|
|
292
|
+
case "boolean":
|
|
293
|
+
return createRawType("boolean");
|
|
294
|
+
case "object": {
|
|
292
295
|
const { properties = {}, required = [] } = schema;
|
|
293
|
-
return createObjectType(Object.fromEntries(Object.entries(properties)
|
|
294
|
-
|
|
296
|
+
return createObjectType(Object.fromEntries(Object.entries(properties).map((v) => [
|
|
297
|
+
v[0],
|
|
298
|
+
convertToTypeScriptType(v[1]),
|
|
299
|
+
])), required);
|
|
295
300
|
}
|
|
296
|
-
case
|
|
301
|
+
case "array":
|
|
297
302
|
return createArrayType(convertToTypeScriptType(schema.items, property));
|
|
298
303
|
default:
|
|
299
|
-
return createRawType(
|
|
304
|
+
return createRawType("unknown");
|
|
300
305
|
}
|
|
301
306
|
}
|
|
302
307
|
};
|
|
@@ -312,10 +317,13 @@ const setEntityEnumProperty = (enums, prop, meta) => {
|
|
|
312
317
|
}
|
|
313
318
|
};
|
|
314
319
|
const extractPropertyMetaData = (enums, meta, prop) => {
|
|
315
|
-
const result = {
|
|
320
|
+
const result = {
|
|
321
|
+
service: meta.service,
|
|
322
|
+
entity: meta.entity,
|
|
323
|
+
};
|
|
316
324
|
if (isReferenceObject(prop)) {
|
|
317
325
|
setEntityEnumProperty(enums, prop, result);
|
|
318
|
-
result.type =
|
|
326
|
+
result.type = "reference";
|
|
319
327
|
return result;
|
|
320
328
|
}
|
|
321
329
|
result.type = prop.type;
|
|
@@ -325,47 +333,47 @@ const extractPropertyMetaData = (enums, meta, prop) => {
|
|
|
325
333
|
if (isArraySchemaObject(prop)) {
|
|
326
334
|
if (isReferenceObject(prop.items)) {
|
|
327
335
|
setEntityEnumProperty(enums, prop.items, result);
|
|
328
|
-
result.format =
|
|
336
|
+
result.format = "reference";
|
|
329
337
|
}
|
|
330
338
|
else {
|
|
331
|
-
result.format =
|
|
339
|
+
result.format = "string";
|
|
332
340
|
}
|
|
333
341
|
}
|
|
334
342
|
return result;
|
|
335
343
|
};
|
|
336
344
|
|
|
337
345
|
const generateInlineComment = (comment) => `/** ${comment} */`;
|
|
338
|
-
const generateBlockComment = (comment, body) => `/**\n${comment.trim().replace(/^ */gm,
|
|
346
|
+
const generateBlockComment = (comment, body) => `/**\n${comment.trim().replace(/^ */gm, " * ")}\n */${body ? `\n${body}` : ""}`;
|
|
339
347
|
|
|
340
348
|
const generateType = (name, value) => {
|
|
341
349
|
return `export type ${name} = ${value.trim()};`;
|
|
342
350
|
};
|
|
343
351
|
|
|
344
|
-
const arrayify = (v) => Array.isArray(v) ? v : [v];
|
|
352
|
+
const arrayify = (v) => (Array.isArray(v) ? v : [v]);
|
|
345
353
|
|
|
346
354
|
const generateInterfaceProperties = (entries) => {
|
|
347
355
|
const properties = entries
|
|
348
|
-
.filter(v => v.type !== undefined)
|
|
349
|
-
.filter((value, index, array) => array.findIndex(v => v.name === value.name) === index)
|
|
356
|
+
.filter((v) => v.type !== undefined)
|
|
357
|
+
.filter((value, index, array) => array.findIndex((v) => v.name === value.name) === index)
|
|
350
358
|
.map(({ name, type, required, readonly, comment }) => {
|
|
351
|
-
const cmd = comment ? `${generateInlineComment(comment)}\n` :
|
|
352
|
-
const req = required ?
|
|
353
|
-
const rol = readonly ?
|
|
359
|
+
const cmd = comment ? `${generateInlineComment(comment)}\n` : "";
|
|
360
|
+
const req = required ? "" : "?";
|
|
361
|
+
const rol = readonly ? "readonly " : "";
|
|
354
362
|
return `${cmd + rol + name + req}: ${type};`;
|
|
355
363
|
})
|
|
356
|
-
.join(
|
|
364
|
+
.join("\n");
|
|
357
365
|
return properties.length ? `{\n${indent(properties)}\n}` : `{}`;
|
|
358
366
|
};
|
|
359
367
|
const generateInterfaceFromObject = (name, obj, propagateOptionalProperties) => `export interface ${name} ${obj.toString(propagateOptionalProperties)}`;
|
|
360
368
|
const generateInterface = (name, entries, extend) => {
|
|
361
|
-
const signature = `${name} ${extend ? `extends ${arrayify(extend).join(
|
|
369
|
+
const signature = `${name} ${extend ? `extends ${arrayify(extend).join(", ")}` : ""}`.trim();
|
|
362
370
|
const body = generateInterfaceProperties(entries);
|
|
363
371
|
return `export interface ${signature} ${body}`;
|
|
364
372
|
};
|
|
365
373
|
const generateInterfaceType = (name, entries, extend) => {
|
|
366
374
|
const body = generateInterfaceProperties(entries);
|
|
367
|
-
const bases = extend ? arrayify(extend).join(
|
|
368
|
-
return generateType(name, `${bases ? `${bases} & ` :
|
|
375
|
+
const bases = extend ? arrayify(extend).join(" & ") : undefined;
|
|
376
|
+
return generateType(name, `${bases ? `${bases} & ` : ""}${body}`);
|
|
369
377
|
};
|
|
370
378
|
|
|
371
379
|
const generateEntities = (schemas, enums) => {
|
|
@@ -387,7 +395,9 @@ const generateEntities = (schemas, enums) => {
|
|
|
387
395
|
let extend = undefined;
|
|
388
396
|
const processProperties = (props = {}) => {
|
|
389
397
|
for (const [name, property] of Object.entries(props)) {
|
|
390
|
-
const meta = isRelatedEntitySchema(property)
|
|
398
|
+
const meta = isRelatedEntitySchema(property)
|
|
399
|
+
? property["x-weclapp"]
|
|
400
|
+
: {};
|
|
391
401
|
if (meta.entity) {
|
|
392
402
|
const type = `${pascalCase(meta.entity)}[]`;
|
|
393
403
|
if (schemas.has(meta.entity)) {
|
|
@@ -405,14 +415,19 @@ const generateEntities = (schemas, enums) => {
|
|
|
405
415
|
}
|
|
406
416
|
}
|
|
407
417
|
const type = convertToTypeScriptType(property, name).toString();
|
|
408
|
-
const comment = isNonArraySchemaObject(property)
|
|
409
|
-
property.deprecated
|
|
410
|
-
|
|
411
|
-
|
|
418
|
+
const comment = isNonArraySchemaObject(property)
|
|
419
|
+
? property.deprecated
|
|
420
|
+
? "@deprecated will be removed."
|
|
421
|
+
: property.format
|
|
422
|
+
? `format: ${property.format}`
|
|
423
|
+
: undefined
|
|
424
|
+
: undefined;
|
|
412
425
|
entityInterface.push({
|
|
413
|
-
name,
|
|
426
|
+
name,
|
|
427
|
+
type,
|
|
428
|
+
comment,
|
|
414
429
|
required: meta.required,
|
|
415
|
-
readonly: !isReferenceObject(property) && property.readOnly
|
|
430
|
+
readonly: !isReferenceObject(property) && property.readOnly,
|
|
416
431
|
});
|
|
417
432
|
properties.set(name, extractPropertyMetaData(enums, meta, property));
|
|
418
433
|
}
|
|
@@ -432,7 +447,7 @@ const generateEntities = (schemas, enums) => {
|
|
|
432
447
|
entities.set(schemaName, {
|
|
433
448
|
extends: extend ? camelCase(extend) : extend,
|
|
434
449
|
properties,
|
|
435
|
-
source
|
|
450
|
+
source,
|
|
436
451
|
});
|
|
437
452
|
}
|
|
438
453
|
return entities;
|
|
@@ -443,20 +458,21 @@ const generateEntities = (schemas, enums) => {
|
|
|
443
458
|
* @param s String to pluralize.
|
|
444
459
|
*/
|
|
445
460
|
const pluralize = (s) => {
|
|
446
|
-
return s.endsWith(
|
|
447
|
-
|
|
448
|
-
|
|
461
|
+
return s.endsWith("s")
|
|
462
|
+
? s
|
|
463
|
+
: s.endsWith("y")
|
|
464
|
+
? `${s.slice(0, -1)}ies`
|
|
465
|
+
: `${s}s`;
|
|
449
466
|
};
|
|
450
467
|
|
|
451
|
-
|
|
452
|
-
const logger = new class {
|
|
468
|
+
const logger = new (class {
|
|
453
469
|
active = true;
|
|
454
470
|
warnings = 0;
|
|
455
471
|
errors = 0;
|
|
456
|
-
write(str =
|
|
472
|
+
write(str = "") {
|
|
457
473
|
process.stdout.write(str);
|
|
458
474
|
}
|
|
459
|
-
blankLn(str =
|
|
475
|
+
blankLn(str = "") {
|
|
460
476
|
this.blank(`${str}\n`);
|
|
461
477
|
}
|
|
462
478
|
warnLn(str) {
|
|
@@ -478,18 +494,18 @@ const logger = new class {
|
|
|
478
494
|
this.write(str);
|
|
479
495
|
}
|
|
480
496
|
warn(str) {
|
|
481
|
-
this.write(`${chalk.yellowBright(
|
|
497
|
+
this.write(`${chalk.yellowBright("[!]")} ${str}`);
|
|
482
498
|
this.warnings++;
|
|
483
499
|
}
|
|
484
500
|
error(str) {
|
|
485
|
-
this.write(`${chalk.redBright(
|
|
501
|
+
this.write(`${chalk.redBright("[X]")} ${str}`);
|
|
486
502
|
this.errors++;
|
|
487
503
|
}
|
|
488
504
|
success(str) {
|
|
489
|
-
this.write(`${chalk.greenBright(
|
|
505
|
+
this.write(`${chalk.greenBright("[✓]")} ${str}`);
|
|
490
506
|
}
|
|
491
507
|
info(str) {
|
|
492
|
-
this.write(`${chalk.blueBright(
|
|
508
|
+
this.write(`${chalk.blueBright("[i]")} ${str}`);
|
|
493
509
|
}
|
|
494
510
|
debug(str) {
|
|
495
511
|
this.write(`[-] ${str}`);
|
|
@@ -497,15 +513,18 @@ const logger = new class {
|
|
|
497
513
|
printSummary() {
|
|
498
514
|
const format = (v, name, fail, ok) => {
|
|
499
515
|
const color = v ? fail : ok;
|
|
500
|
-
return v === 0
|
|
501
|
-
|
|
516
|
+
return v === 0
|
|
517
|
+
? `${color("zero")} ${pluralize(name)}`
|
|
518
|
+
: v === 1
|
|
519
|
+
? `${color("one")} ${name}`
|
|
520
|
+
: `${color(v)} ${pluralize(name)}`;
|
|
502
521
|
};
|
|
503
|
-
const warnings = format(this.warnings,
|
|
504
|
-
const errors = format(this.errors,
|
|
522
|
+
const warnings = format(this.warnings, "warning", chalk.yellowBright, chalk.greenBright);
|
|
523
|
+
const errors = format(this.errors, "error", chalk.redBright, chalk.greenBright);
|
|
505
524
|
const info = `Finished with ${warnings} and ${errors}.`;
|
|
506
|
-
this[this.errors ?
|
|
525
|
+
this[this.errors ? "errorLn" : this.warnings ? "warnLn" : "successLn"](info);
|
|
507
526
|
}
|
|
508
|
-
};
|
|
527
|
+
})();
|
|
509
528
|
|
|
510
529
|
/**
|
|
511
530
|
* ROOT => /article
|
|
@@ -523,34 +542,46 @@ var WeclappEndpointType;
|
|
|
523
542
|
WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
|
|
524
543
|
})(WeclappEndpointType || (WeclappEndpointType = {}));
|
|
525
544
|
const parseEndpointPath = (path) => {
|
|
526
|
-
const [, entity, ...rest] = path.split(
|
|
545
|
+
const [, entity, ...rest] = path.split("/");
|
|
527
546
|
if (!entity) {
|
|
528
547
|
return undefined;
|
|
529
548
|
}
|
|
530
549
|
if (!rest.length) {
|
|
531
550
|
return { path, entity, type: WeclappEndpointType.ROOT };
|
|
532
551
|
}
|
|
533
|
-
else if (rest[0] ===
|
|
552
|
+
else if (rest[0] === "count") {
|
|
534
553
|
return { path, entity, type: WeclappEndpointType.COUNT };
|
|
535
554
|
}
|
|
536
|
-
else if (rest[0] ===
|
|
537
|
-
return rest.length === 2
|
|
538
|
-
{ path, entity, type: WeclappEndpointType.ENTITY }
|
|
539
|
-
|
|
555
|
+
else if (rest[0] === "id") {
|
|
556
|
+
return rest.length === 2
|
|
557
|
+
? { path, entity, type: WeclappEndpointType.ENTITY }
|
|
558
|
+
: {
|
|
559
|
+
path,
|
|
560
|
+
entity,
|
|
561
|
+
method: rest[2],
|
|
562
|
+
type: WeclappEndpointType.GENERIC_ENTITY,
|
|
563
|
+
};
|
|
540
564
|
}
|
|
541
565
|
else if (rest.length === 1) {
|
|
542
|
-
return {
|
|
566
|
+
return {
|
|
567
|
+
path,
|
|
568
|
+
entity,
|
|
569
|
+
method: rest[1],
|
|
570
|
+
type: WeclappEndpointType.GENERIC_ROOT,
|
|
571
|
+
};
|
|
543
572
|
}
|
|
544
573
|
return undefined;
|
|
545
574
|
};
|
|
546
575
|
|
|
547
|
-
const generateArrowFunction = ({ name, signature, returns, params }) => {
|
|
548
|
-
return `const ${name}: ${signature} = (${params?.join(
|
|
576
|
+
const generateArrowFunction = ({ name, signature, returns, params, }) => {
|
|
577
|
+
return `const ${name}: ${signature} = (${params?.join(", ") ?? ""}) =>\n${indent(returns)};`;
|
|
549
578
|
};
|
|
550
579
|
|
|
551
|
-
const generateArrowFunctionType = ({ type, returns =
|
|
552
|
-
const genericsString = generics?.length
|
|
553
|
-
|
|
580
|
+
const generateArrowFunctionType = ({ type, returns = "void", generics, params, }) => {
|
|
581
|
+
const genericsString = generics?.length
|
|
582
|
+
? `<\n${indent(generics.join(",\n"))}\n>`
|
|
583
|
+
: "";
|
|
584
|
+
const paramsString = params?.length ? `(${params.join(", ")})` : `()`;
|
|
554
585
|
return generateType(type, `${genericsString + paramsString} =>\n${indent(returns)}`);
|
|
555
586
|
};
|
|
556
587
|
|
|
@@ -558,21 +589,23 @@ const convertParametersToSchema = (parameters = []) => {
|
|
|
558
589
|
const properties = [];
|
|
559
590
|
const required = [];
|
|
560
591
|
for (const param of parameters) {
|
|
561
|
-
if (isParameterObject(param) && param.in ===
|
|
592
|
+
if (isParameterObject(param) && param.in === "query") {
|
|
562
593
|
if (param.schema) {
|
|
563
594
|
properties.push([param.name, param.schema]);
|
|
564
|
-
|
|
595
|
+
if (param.required)
|
|
596
|
+
required.push(param.name);
|
|
565
597
|
}
|
|
566
598
|
}
|
|
567
599
|
}
|
|
568
600
|
return {
|
|
569
|
-
type:
|
|
570
|
-
|
|
601
|
+
type: "object",
|
|
602
|
+
required,
|
|
603
|
+
properties: Object.fromEntries(properties),
|
|
571
604
|
};
|
|
572
605
|
};
|
|
573
606
|
|
|
574
|
-
const functionName$5 =
|
|
575
|
-
const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
|
|
607
|
+
const functionName$5 = "count";
|
|
608
|
+
const generateCountEndpoint = ({ aliases, path, target, endpoint, }) => {
|
|
576
609
|
const service = pascalCase(endpoint.entity);
|
|
577
610
|
const entity = aliases.get(endpoint.entity) ?? service;
|
|
578
611
|
const entityFilter = `${entity}_Filter`;
|
|
@@ -580,18 +613,20 @@ const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
|
|
|
580
613
|
const entityParameters = `${interfaceName}_Parameters`;
|
|
581
614
|
const parameterSchema = convertParametersToSchema(path.parameters);
|
|
582
615
|
const parameters = createObjectType({
|
|
583
|
-
params: convertToTypeScriptType(parameterSchema)
|
|
616
|
+
params: convertToTypeScriptType(parameterSchema),
|
|
584
617
|
});
|
|
585
618
|
const functionSource = generateArrowFunction({
|
|
586
619
|
name: functionName$5,
|
|
587
620
|
signature: interfaceName,
|
|
588
621
|
returns: `_${functionName$5}(cfg, ${generateString(endpoint.path)}, query)`,
|
|
589
|
-
params: [
|
|
622
|
+
params: ["query"],
|
|
590
623
|
});
|
|
591
624
|
const interfaceSource = generateArrowFunctionType({
|
|
592
625
|
type: interfaceName,
|
|
593
|
-
params: [
|
|
594
|
-
|
|
626
|
+
params: [
|
|
627
|
+
`query${parameters.isFullyOptional() ? "?" : ""}: CountQuery<${entityFilter}> & ${entityParameters}`,
|
|
628
|
+
],
|
|
629
|
+
returns: `${resolveResponseType(target)}<number>`,
|
|
595
630
|
});
|
|
596
631
|
return {
|
|
597
632
|
entity,
|
|
@@ -601,9 +636,9 @@ const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
|
|
|
601
636
|
interfaces: [
|
|
602
637
|
{
|
|
603
638
|
name: entityParameters,
|
|
604
|
-
source: generateInterfaceFromObject(entityParameters, parameters, true)
|
|
605
|
-
}
|
|
606
|
-
]
|
|
639
|
+
source: generateInterfaceFromObject(entityParameters, parameters, true),
|
|
640
|
+
},
|
|
641
|
+
],
|
|
607
642
|
};
|
|
608
643
|
};
|
|
609
644
|
|
|
@@ -620,43 +655,40 @@ const generateBodyType = (body) => {
|
|
|
620
655
|
return types.length ? createTupleType(types) : undefined;
|
|
621
656
|
};
|
|
622
657
|
|
|
623
|
-
const generateRequestBodyType = ({ requestBody }) => {
|
|
624
|
-
return generateBodyType(requestBody) ?? createRawType(
|
|
658
|
+
const generateRequestBodyType = ({ requestBody, }) => {
|
|
659
|
+
return generateBodyType(requestBody) ?? createRawType("unknown");
|
|
625
660
|
};
|
|
626
661
|
|
|
627
|
-
const resolveBodyType = ({ responses }) => Object.entries(responses)
|
|
628
|
-
|
|
629
|
-
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
|
|
662
|
+
const resolveBodyType = ({ responses, }) => Object.entries(responses).filter((v) => v[0].startsWith("2"))[0]?.[1];
|
|
663
|
+
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType("void");
|
|
630
664
|
|
|
631
|
-
const functionName$4 =
|
|
632
|
-
const generateCreateEndpoint = ({ target, path, endpoint }) => {
|
|
665
|
+
const functionName$4 = "create";
|
|
666
|
+
const generateCreateEndpoint = ({ target, path, endpoint, }) => {
|
|
633
667
|
const entity = pascalCase(endpoint.entity);
|
|
634
668
|
const interfaceName = `${entity}Service_${pascalCase(functionName$4)}`;
|
|
635
669
|
const functionSource = generateArrowFunction({
|
|
636
670
|
name: functionName$4,
|
|
637
671
|
signature: interfaceName,
|
|
638
672
|
returns: `_${functionName$4}(cfg, ${generateString(endpoint.path)}, data)`,
|
|
639
|
-
params: [
|
|
673
|
+
params: ["data"],
|
|
640
674
|
});
|
|
641
675
|
const interfaceSource = generateArrowFunctionType({
|
|
642
676
|
type: interfaceName,
|
|
643
677
|
params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`],
|
|
644
|
-
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}
|
|
678
|
+
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`,
|
|
645
679
|
});
|
|
646
680
|
return {
|
|
647
681
|
entity,
|
|
648
682
|
name: functionName$4,
|
|
649
683
|
type: { name: interfaceName, source: interfaceSource },
|
|
650
|
-
func: { name: functionName$4, source: functionSource }
|
|
684
|
+
func: { name: functionName$4, source: functionSource },
|
|
651
685
|
};
|
|
652
686
|
};
|
|
653
687
|
|
|
654
|
-
const generateGenericFunctionName = (path, suffix =
|
|
688
|
+
const generateGenericFunctionName = (path, suffix = "", prefix = "") => {
|
|
655
689
|
return camelCase(`${prefix}_` +
|
|
656
|
-
path
|
|
657
|
-
|
|
658
|
-
.replace(/\W+/, '_')
|
|
659
|
-
.replace(/[_]+/, '_') + `_${suffix}`);
|
|
690
|
+
path.replace(/.*\//, "").replace(/\W+/, "_").replace(/[_]+/, "_") +
|
|
691
|
+
`_${suffix}`);
|
|
660
692
|
};
|
|
661
693
|
|
|
662
694
|
const insertPathPlaceholder = (path, record) => {
|
|
@@ -664,32 +696,37 @@ const insertPathPlaceholder = (path, record) => {
|
|
|
664
696
|
};
|
|
665
697
|
|
|
666
698
|
const wrapBody = (type, target) => {
|
|
667
|
-
return type.toString() ===
|
|
668
|
-
createRawType(isNodeTarget(target) ?
|
|
669
|
-
type; // node-fetch returns a Blob as well
|
|
699
|
+
return type.toString() === "binary"
|
|
700
|
+
? createRawType(isNodeTarget(target) ? "BodyInit" : "Blob")
|
|
701
|
+
: type; // node-fetch returns a Blob as well
|
|
670
702
|
};
|
|
671
703
|
const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
|
|
672
704
|
const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
|
|
673
705
|
const entity = pascalCase(endpoint.entity);
|
|
674
706
|
const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
|
|
675
707
|
const entityQuery = `${interfaceName}_Query`;
|
|
676
|
-
const hasId = endpoint.path.includes(
|
|
708
|
+
const hasId = endpoint.path.includes("{id}");
|
|
677
709
|
const params = createObjectType({
|
|
678
710
|
params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
|
|
679
|
-
body: method ===
|
|
711
|
+
body: method === "get"
|
|
712
|
+
? undefined
|
|
713
|
+
: wrapBody(generateRequestBodyType(path), target),
|
|
680
714
|
});
|
|
681
715
|
const responseBody = generateResponseBodyType(path);
|
|
682
|
-
const forceBlobResponse = String(responseBody.toString() ===
|
|
716
|
+
const forceBlobResponse = String(responseBody.toString() === "binary");
|
|
683
717
|
const functionSource = generateArrowFunction({
|
|
684
718
|
name: functionName,
|
|
685
719
|
signature: interfaceName,
|
|
686
|
-
params: hasId ? [
|
|
687
|
-
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id:
|
|
720
|
+
params: hasId ? ["id", "query"] : ["query"],
|
|
721
|
+
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, query, ${forceBlobResponse})`,
|
|
688
722
|
});
|
|
689
723
|
const interfaceSource = generateArrowFunctionType({
|
|
690
724
|
type: interfaceName,
|
|
691
|
-
params: [
|
|
692
|
-
|
|
725
|
+
params: [
|
|
726
|
+
...(hasId ? ["id: string"] : []),
|
|
727
|
+
`query${params.isFullyOptional() ? "?" : ""}: ${entityQuery}`,
|
|
728
|
+
],
|
|
729
|
+
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`,
|
|
693
730
|
});
|
|
694
731
|
return {
|
|
695
732
|
entity,
|
|
@@ -699,44 +736,48 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
|
|
|
699
736
|
interfaces: [
|
|
700
737
|
{
|
|
701
738
|
name: entityQuery,
|
|
702
|
-
source: generateInterfaceFromObject(entityQuery, params, true)
|
|
703
|
-
}
|
|
704
|
-
]
|
|
739
|
+
source: generateInterfaceFromObject(entityQuery, params, true),
|
|
740
|
+
},
|
|
741
|
+
],
|
|
705
742
|
};
|
|
706
743
|
};
|
|
707
744
|
|
|
708
|
-
const functionName$3 =
|
|
709
|
-
const generateRemoveEndpoint = ({ target, endpoint }) => {
|
|
745
|
+
const functionName$3 = "remove";
|
|
746
|
+
const generateRemoveEndpoint = ({ target, endpoint, }) => {
|
|
710
747
|
const entity = pascalCase(endpoint.entity);
|
|
711
748
|
const interfaceName = `${entity}Service_${pascalCase(functionName$3)}`;
|
|
712
749
|
const functionSource = generateArrowFunction({
|
|
713
750
|
name: functionName$3,
|
|
714
751
|
signature: interfaceName,
|
|
715
|
-
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id:
|
|
716
|
-
params: [
|
|
752
|
+
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, options)`,
|
|
753
|
+
params: ["id", "options?: RemoveQuery"],
|
|
717
754
|
});
|
|
718
755
|
const interfaceSource = generateArrowFunctionType({
|
|
719
756
|
type: interfaceName,
|
|
720
|
-
params: [
|
|
721
|
-
returns: `${resolveResponseType(target)}<void
|
|
757
|
+
params: ["id: string", "options?: RemoveQuery"],
|
|
758
|
+
returns: `${resolveResponseType(target)}<void>`,
|
|
722
759
|
});
|
|
723
760
|
return {
|
|
724
761
|
entity,
|
|
725
762
|
name: functionName$3,
|
|
726
763
|
type: { name: interfaceName, source: interfaceSource },
|
|
727
|
-
func: { name: functionName$3, source: functionSource }
|
|
764
|
+
func: { name: functionName$3, source: functionSource },
|
|
728
765
|
};
|
|
729
766
|
};
|
|
730
767
|
|
|
731
|
-
const functionName$2 =
|
|
768
|
+
const functionName$2 = "some";
|
|
732
769
|
const excludedParameters = [
|
|
733
|
-
|
|
734
|
-
|
|
770
|
+
"page",
|
|
771
|
+
"pageSize",
|
|
772
|
+
"sort",
|
|
773
|
+
"serializeNulls",
|
|
774
|
+
"properties",
|
|
775
|
+
"includeReferencedEntities",
|
|
735
776
|
];
|
|
736
777
|
const resolveAdditionalProperties = (path) => {
|
|
737
778
|
const body = resolveBodyType(path);
|
|
738
779
|
if (isResponseObject(body)) {
|
|
739
|
-
const schema = body?.content?.[
|
|
780
|
+
const schema = body?.content?.["application/json"]?.schema;
|
|
740
781
|
if (isObjectSchemaObject(schema)) {
|
|
741
782
|
const obj = schema?.properties?.additionalProperties;
|
|
742
783
|
if (isObjectSchemaObject(obj)) {
|
|
@@ -746,7 +787,7 @@ const resolveAdditionalProperties = (path) => {
|
|
|
746
787
|
}
|
|
747
788
|
return undefined;
|
|
748
789
|
};
|
|
749
|
-
const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
|
|
790
|
+
const generateSomeEndpoint = ({ aliases, target, path, endpoint, }) => {
|
|
750
791
|
// Required interface names
|
|
751
792
|
const service = pascalCase(endpoint.entity);
|
|
752
793
|
const entity = aliases.get(endpoint.entity) ?? service;
|
|
@@ -758,28 +799,33 @@ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
|
|
|
758
799
|
const parameterSchema = convertParametersToSchema(path.parameters);
|
|
759
800
|
const additionalProperties = resolveAdditionalProperties(path);
|
|
760
801
|
const additionalPropertyNames = generateStrings(Object.keys(additionalProperties?.properties ?? {}));
|
|
761
|
-
const additionalPropertyNamesType = additionalPropertyNames.length
|
|
802
|
+
const additionalPropertyNamesType = additionalPropertyNames.length
|
|
803
|
+
? `(${concat(additionalPropertyNames, " | ")})[]`
|
|
804
|
+
: "[]";
|
|
762
805
|
// We already cover some properties
|
|
763
|
-
parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {})
|
|
764
|
-
.filter(v => !excludedParameters.includes(v[0])));
|
|
806
|
+
parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {}).filter((v) => !excludedParameters.includes(v[0])));
|
|
765
807
|
const parameters = createObjectType({
|
|
766
|
-
params: convertToTypeScriptType(parameterSchema)
|
|
808
|
+
params: convertToTypeScriptType(parameterSchema),
|
|
767
809
|
});
|
|
768
|
-
const properties = additionalProperties
|
|
810
|
+
const properties = additionalProperties
|
|
811
|
+
? convertToTypeScriptType(additionalProperties).toString()
|
|
812
|
+
: "{}";
|
|
769
813
|
const interfaceSource = generateArrowFunctionType({
|
|
770
814
|
type: interfaceName,
|
|
771
815
|
generics: [
|
|
772
816
|
`S extends (QuerySelect<${entity}> | undefined) = undefined`,
|
|
773
|
-
`I extends (QuerySelect<${entityMappings}> | undefined) = undefined
|
|
817
|
+
`I extends (QuerySelect<${entityMappings}> | undefined) = undefined`,
|
|
774
818
|
],
|
|
775
|
-
params: [
|
|
776
|
-
|
|
819
|
+
params: [
|
|
820
|
+
`query${parameters.isFullyOptional() ? "?" : ""}: SomeQuery<${entity}, ${entityFilter}, I, S, ${additionalPropertyNamesType}> & ${entityParameters}`,
|
|
821
|
+
],
|
|
822
|
+
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S, ${properties}>>`,
|
|
777
823
|
});
|
|
778
824
|
const functionSource = generateArrowFunction({
|
|
779
825
|
name: functionName$2,
|
|
780
826
|
signature: interfaceName,
|
|
781
827
|
returns: `_${functionName$2}(cfg, ${generateString(endpoint.path)}, query)`,
|
|
782
|
-
params: [
|
|
828
|
+
params: ["query"],
|
|
783
829
|
});
|
|
784
830
|
return {
|
|
785
831
|
entity,
|
|
@@ -789,62 +835,66 @@ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
|
|
|
789
835
|
interfaces: [
|
|
790
836
|
{
|
|
791
837
|
name: entityParameters,
|
|
792
|
-
source: generateInterfaceFromObject(entityParameters, parameters, true)
|
|
793
|
-
}
|
|
794
|
-
]
|
|
838
|
+
source: generateInterfaceFromObject(entityParameters, parameters, true),
|
|
839
|
+
},
|
|
840
|
+
],
|
|
795
841
|
};
|
|
796
842
|
};
|
|
797
843
|
|
|
798
|
-
const functionName$1 =
|
|
799
|
-
const generateUniqueEndpoint = ({ target, path, endpoint }) => {
|
|
844
|
+
const functionName$1 = "unique";
|
|
845
|
+
const generateUniqueEndpoint = ({ target, path, endpoint, }) => {
|
|
800
846
|
const entity = pascalCase(endpoint.entity);
|
|
801
847
|
const interfaceName = `${entity}Service_${pascalCase(functionName$1)}`;
|
|
802
848
|
const functionSource = generateArrowFunction({
|
|
803
849
|
name: functionName$1,
|
|
804
850
|
signature: interfaceName,
|
|
805
|
-
params: [
|
|
806
|
-
returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id:
|
|
851
|
+
params: ["id", "query"],
|
|
852
|
+
returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, query)`,
|
|
807
853
|
});
|
|
808
854
|
const interfaceSource = generateArrowFunctionType({
|
|
809
855
|
type: interfaceName,
|
|
810
|
-
params: [
|
|
811
|
-
generics: [
|
|
812
|
-
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}
|
|
856
|
+
params: ["id: string", "query?: Q"],
|
|
857
|
+
generics: ["Q extends UniqueQuery"],
|
|
858
|
+
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`,
|
|
813
859
|
});
|
|
814
860
|
return {
|
|
815
861
|
entity,
|
|
816
862
|
name: functionName$1,
|
|
817
863
|
type: { name: interfaceName, source: interfaceSource },
|
|
818
|
-
func: { name: functionName$1, source: functionSource }
|
|
864
|
+
func: { name: functionName$1, source: functionSource },
|
|
819
865
|
};
|
|
820
866
|
};
|
|
821
867
|
|
|
822
|
-
const functionName =
|
|
823
|
-
const generateUpdateEndpoint = ({ target, path, endpoint }) => {
|
|
868
|
+
const functionName = "update";
|
|
869
|
+
const generateUpdateEndpoint = ({ target, path, endpoint, }) => {
|
|
824
870
|
const entity = pascalCase(endpoint.entity);
|
|
825
871
|
const interfaceName = `${entity}Service_${pascalCase(functionName)}`;
|
|
826
872
|
const interfaceSource = generateArrowFunctionType({
|
|
827
873
|
type: interfaceName,
|
|
828
|
-
params: [
|
|
829
|
-
|
|
874
|
+
params: [
|
|
875
|
+
"id: string",
|
|
876
|
+
`data: DeepPartial<${generateRequestBodyType(path).toString()}>`,
|
|
877
|
+
"options?: UpdateQuery",
|
|
878
|
+
],
|
|
879
|
+
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`,
|
|
830
880
|
});
|
|
831
881
|
const functionSource = generateArrowFunction({
|
|
832
882
|
name: functionName,
|
|
833
883
|
signature: interfaceName,
|
|
834
|
-
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id:
|
|
835
|
-
params: [
|
|
884
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: "${id}" })}\`, data, options)`,
|
|
885
|
+
params: ["id", "data", "options"],
|
|
836
886
|
});
|
|
837
887
|
return {
|
|
838
888
|
entity,
|
|
839
889
|
name: functionName,
|
|
840
890
|
type: { name: interfaceName, source: interfaceSource },
|
|
841
|
-
func: { name: functionName, source: functionSource }
|
|
891
|
+
func: { name: functionName, source: functionSource },
|
|
842
892
|
};
|
|
843
893
|
};
|
|
844
894
|
|
|
845
895
|
const isMultiPartUploadPath = (path) => {
|
|
846
|
-
const [, entity, ...rest] = path.split(
|
|
847
|
-
return entity && rest.length === 2 && rest[1] ===
|
|
896
|
+
const [, entity, ...rest] = path.split("/");
|
|
897
|
+
return entity && rest.length === 2 && rest[1] === "multipartUpload";
|
|
848
898
|
};
|
|
849
899
|
const groupEndpointsByEntity = (paths) => {
|
|
850
900
|
const endpoints = new Map();
|
|
@@ -872,28 +922,28 @@ const generators = {
|
|
|
872
922
|
/* /article */
|
|
873
923
|
[WeclappEndpointType.ROOT]: {
|
|
874
924
|
get: generateSomeEndpoint,
|
|
875
|
-
post: generateCreateEndpoint
|
|
925
|
+
post: generateCreateEndpoint,
|
|
876
926
|
},
|
|
877
927
|
/* /article/count */
|
|
878
928
|
[WeclappEndpointType.COUNT]: {
|
|
879
|
-
get: generateCountEndpoint
|
|
929
|
+
get: generateCountEndpoint,
|
|
880
930
|
},
|
|
881
931
|
/* /article/:id */
|
|
882
932
|
[WeclappEndpointType.ENTITY]: {
|
|
883
933
|
get: generateUniqueEndpoint,
|
|
884
934
|
delete: generateRemoveEndpoint,
|
|
885
|
-
put: generateUpdateEndpoint
|
|
935
|
+
put: generateUpdateEndpoint,
|
|
886
936
|
},
|
|
887
937
|
/* /article/:id/method */
|
|
888
938
|
[WeclappEndpointType.GENERIC_ENTITY]: {
|
|
889
|
-
get: generateGenericEndpoint(
|
|
890
|
-
post: generateGenericEndpoint(
|
|
939
|
+
get: generateGenericEndpoint("ById"),
|
|
940
|
+
post: generateGenericEndpoint("ById"),
|
|
891
941
|
},
|
|
892
942
|
/* /article/method */
|
|
893
943
|
[WeclappEndpointType.GENERIC_ROOT]: {
|
|
894
944
|
get: generateGenericEndpoint(),
|
|
895
|
-
post: generateGenericEndpoint()
|
|
896
|
-
}
|
|
945
|
+
post: generateGenericEndpoint(),
|
|
946
|
+
},
|
|
897
947
|
};
|
|
898
948
|
const generateServices = (doc, aliases, options) => {
|
|
899
949
|
const services = new Map();
|
|
@@ -906,7 +956,9 @@ const generateServices = (doc, aliases, options) => {
|
|
|
906
956
|
for (const { path, endpoint } of paths) {
|
|
907
957
|
const resolver = generators[endpoint.type];
|
|
908
958
|
for (const [method, config] of Object.entries(path)) {
|
|
909
|
-
if (method ===
|
|
959
|
+
if (method === "get" &&
|
|
960
|
+
endpoint.type === WeclappEndpointType.ENTITY &&
|
|
961
|
+
!options.generateUnique) {
|
|
910
962
|
continue;
|
|
911
963
|
}
|
|
912
964
|
if (resolver[method]) {
|
|
@@ -915,7 +967,7 @@ const generateServices = (doc, aliases, options) => {
|
|
|
915
967
|
if (!path.deprecated || options.deprecated) {
|
|
916
968
|
functions.push({
|
|
917
969
|
...resolver[method]({ endpoint, method, target, path, aliases }),
|
|
918
|
-
path
|
|
970
|
+
path,
|
|
919
971
|
});
|
|
920
972
|
}
|
|
921
973
|
}
|
|
@@ -928,33 +980,40 @@ const generateServices = (doc, aliases, options) => {
|
|
|
928
980
|
continue;
|
|
929
981
|
}
|
|
930
982
|
// Construct service type
|
|
931
|
-
const types = generateStatements(...functions.flatMap(v => v.interfaces?.map(v => v.source) ?? []), ...functions.map(v => v.type.source), generateInterface(serviceTypeName, [
|
|
932
|
-
...functions.map(v => ({
|
|
983
|
+
const types = generateStatements(...functions.flatMap((v) => v.interfaces?.map((v) => v.source) ?? []), ...functions.map((v) => v.type.source), generateInterface(serviceTypeName, [
|
|
984
|
+
...functions.map((v) => ({
|
|
933
985
|
required: true,
|
|
934
|
-
comment: v.path.deprecated ?
|
|
986
|
+
comment: v.path.deprecated ? "@deprecated" : undefined,
|
|
935
987
|
name: v.func.name,
|
|
936
|
-
type: v.type.name
|
|
937
|
-
}))
|
|
988
|
+
type: v.type.name,
|
|
989
|
+
})),
|
|
938
990
|
]));
|
|
939
991
|
// Construct service value
|
|
940
|
-
const funcBody = generateBlockStatements(...functions.map(v => v.func.source), `return {${concat(functions.map(v => v.func.name))}};`);
|
|
992
|
+
const funcBody = generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`);
|
|
941
993
|
const func = `export const ${serviceName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${funcBody};`;
|
|
942
994
|
const source = generateBlockComment(`${pascalCase(endpoint)} service`, generateStatements(types, func));
|
|
943
|
-
const deprecated = functions.every(v => v.path.deprecated);
|
|
944
|
-
services.set(endpoint, {
|
|
995
|
+
const deprecated = functions.every((v) => v.path.deprecated);
|
|
996
|
+
services.set(endpoint, {
|
|
997
|
+
entity: endpoint,
|
|
998
|
+
deprecated,
|
|
999
|
+
serviceName,
|
|
1000
|
+
serviceTypeName,
|
|
1001
|
+
source,
|
|
1002
|
+
functions,
|
|
1003
|
+
});
|
|
945
1004
|
}
|
|
946
1005
|
return services;
|
|
947
1006
|
};
|
|
948
1007
|
|
|
949
1008
|
const generateCustomValueUtilities = (entities, services) => {
|
|
950
|
-
const customValueEntity = entities.get(
|
|
1009
|
+
const customValueEntity = entities.get("customValue");
|
|
951
1010
|
const customValueEntities = [];
|
|
952
1011
|
if (!customValueEntity) {
|
|
953
|
-
logger.warn(
|
|
954
|
-
return
|
|
1012
|
+
logger.warn("Cannot generate custom value utils, type not found.");
|
|
1013
|
+
return "";
|
|
955
1014
|
}
|
|
956
1015
|
serviceLoop: for (const service of services) {
|
|
957
|
-
const someFunction = service.functions.find(v => v.name ===
|
|
1016
|
+
const someFunction = service.functions.find((v) => v.name === "some");
|
|
958
1017
|
if (!someFunction) {
|
|
959
1018
|
continue;
|
|
960
1019
|
}
|
|
@@ -969,7 +1028,7 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
969
1028
|
}
|
|
970
1029
|
customValueEntities.push(service.entity);
|
|
971
1030
|
}
|
|
972
|
-
return generateBlockComment(
|
|
1031
|
+
return generateBlockComment("Utilities to identify services that return an entity that is an alias to CustomValue.", 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);")}`));
|
|
973
1032
|
};
|
|
974
1033
|
|
|
975
1034
|
const generateObject = (properties) => {
|
|
@@ -988,7 +1047,7 @@ const generateObject = (properties) => {
|
|
|
988
1047
|
body.push(`${key}: ${String(value)}`);
|
|
989
1048
|
}
|
|
990
1049
|
}
|
|
991
|
-
return body.length ? `{\n${indent(body.join(
|
|
1050
|
+
return body.length ? `{\n${indent(body.join(",\n"))}\n}` : `{}`;
|
|
992
1051
|
};
|
|
993
1052
|
|
|
994
1053
|
const resolveInheritedEntities = (root, entities) => {
|
|
@@ -996,34 +1055,42 @@ const resolveInheritedEntities = (root, entities) => {
|
|
|
996
1055
|
return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
|
|
997
1056
|
};
|
|
998
1057
|
const generatePropertyDescriptors = (entity, entities, services, options) => [
|
|
999
|
-
...resolveInheritedEntities(entity, entities).flatMap(v => [
|
|
1000
|
-
|
|
1001
|
-
]
|
|
1058
|
+
...resolveInheritedEntities(entity, entities).flatMap((v) => [
|
|
1059
|
+
...v.properties,
|
|
1060
|
+
]),
|
|
1061
|
+
...entity.properties,
|
|
1062
|
+
]
|
|
1063
|
+
.filter(([, meta]) => {
|
|
1002
1064
|
// If we generate deprecated things we can skip the filtering
|
|
1003
1065
|
if (options.deprecated) {
|
|
1004
1066
|
return true;
|
|
1005
1067
|
}
|
|
1006
1068
|
// Check if corresponding service is deprecated and can be removed
|
|
1007
|
-
const service = services.find(v => v.entity === meta.service);
|
|
1069
|
+
const service = services.find((v) => v.entity === meta.service);
|
|
1008
1070
|
return !meta.service || (service && !service.deprecated);
|
|
1009
|
-
})
|
|
1071
|
+
})
|
|
1072
|
+
.map(([property, meta]) => ({
|
|
1010
1073
|
key: property,
|
|
1011
1074
|
value: Object.entries(meta).map(([key, value]) => ({
|
|
1012
1075
|
key,
|
|
1013
|
-
value: value !== undefined
|
|
1014
|
-
|
|
1076
|
+
value: value !== undefined
|
|
1077
|
+
? typeof value === "number"
|
|
1078
|
+
? value
|
|
1079
|
+
: generateString(value)
|
|
1080
|
+
: undefined,
|
|
1081
|
+
})),
|
|
1015
1082
|
}));
|
|
1016
1083
|
const generateEntityPropertyMap = (entities, services, options) => {
|
|
1017
|
-
const typeName =
|
|
1084
|
+
const typeName = "WEntityProperties";
|
|
1018
1085
|
const propertyMap = [...entities].map(([entity, data]) => ({
|
|
1019
1086
|
key: entity,
|
|
1020
|
-
value: generatePropertyDescriptors(data, entities, services, options)
|
|
1087
|
+
value: generatePropertyDescriptors(data, entities, services, options),
|
|
1021
1088
|
}));
|
|
1022
1089
|
return generateStatements(`export type ${typeName} = Partial<Record<WEntity, Partial<Record<string, WEntityPropertyMeta>>>>;`, `export const wEntityProperties: ${typeName} = ${generateObject(propertyMap)};`);
|
|
1023
1090
|
};
|
|
1024
1091
|
|
|
1025
1092
|
const generateArray = (values) => {
|
|
1026
|
-
return `[${concat(values.map(v => generateString(String(v))))}]`;
|
|
1093
|
+
return `[${concat(values.map((v) => generateString(String(v))))}]`;
|
|
1027
1094
|
};
|
|
1028
1095
|
|
|
1029
1096
|
// Only functions matching this regex are included in the generation.
|
|
@@ -1042,11 +1109,12 @@ const generateGroupedServices = (services) => {
|
|
|
1042
1109
|
continue;
|
|
1043
1110
|
}
|
|
1044
1111
|
entityDescriptors.set(name, [
|
|
1045
|
-
...(entityDescriptors.get(name) ?? []),
|
|
1112
|
+
...(entityDescriptors.get(name) ?? []),
|
|
1113
|
+
{
|
|
1046
1114
|
name: entity,
|
|
1047
1115
|
required: true,
|
|
1048
|
-
type: `${pascalCase(entity)}Service_${pascalCase(name)}
|
|
1049
|
-
}
|
|
1116
|
+
type: `${pascalCase(entity)}Service_${pascalCase(name)}`,
|
|
1117
|
+
},
|
|
1050
1118
|
]);
|
|
1051
1119
|
}
|
|
1052
1120
|
}
|
|
@@ -1064,46 +1132,57 @@ const generateGroupedServices = (services) => {
|
|
|
1064
1132
|
...descriptors.map(([name, props]) => {
|
|
1065
1133
|
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1066
1134
|
const type = pascalCase(`WServiceWith_${name}`);
|
|
1067
|
-
const value = generateArray(props.map(v => v.name));
|
|
1135
|
+
const value = generateArray(props.map((v) => v.name));
|
|
1068
1136
|
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1069
1137
|
}),
|
|
1070
|
-
generateBlockComment(
|
|
1138
|
+
generateBlockComment("Type guards for service classes.", generateStatements(...typeGuards)),
|
|
1071
1139
|
];
|
|
1072
1140
|
};
|
|
1073
1141
|
|
|
1074
|
-
const obj = (list) => `{\n${indent(list.join(
|
|
1075
|
-
const arr = (list) => `[\n${indent(list.join(
|
|
1076
|
-
const generateMaps = ({ services, entities, aliases, enums, options }) => {
|
|
1142
|
+
const obj = (list) => `{\n${indent(list.join(",\n"))}\n}`;
|
|
1143
|
+
const arr = (list) => `[\n${indent(list.join(",\n"))}\n]`;
|
|
1144
|
+
const generateMaps = ({ services, entities, aliases, enums, options, }) => {
|
|
1077
1145
|
const entitiesKeys = [...entities.keys()];
|
|
1078
1146
|
const enumsArray = `export const wEnums = ${obj(enums)};`;
|
|
1079
|
-
const entityNames = `export const wEntityNames: WEntity[] = ${arr(entitiesKeys.map(v => `'${v}'`))};`;
|
|
1080
|
-
const serviceNames = `export const wServiceNames: WService[] = ${arr(services.map(v => `'${v.entity}'`))};`;
|
|
1081
|
-
const serviceValues = `export const wServiceFactories = ${obj(services.map(v => `${v.entity}: ${v.serviceName}`))};`;
|
|
1082
|
-
const serviceInstanceValues = `export const wServices = ${obj(services.map(v => {
|
|
1147
|
+
const entityNames = `export const wEntityNames: WEntity[] = ${arr(entitiesKeys.map((v) => `'${v}'`))};`;
|
|
1148
|
+
const serviceNames = `export const wServiceNames: WService[] = ${arr(services.map((v) => `'${v.entity}'`))};`;
|
|
1149
|
+
const serviceValues = `export const wServiceFactories = ${obj(services.map((v) => `${v.entity}: ${v.serviceName}`))};`;
|
|
1150
|
+
const serviceInstanceValues = `export const wServices = ${obj(services.map((v) => {
|
|
1083
1151
|
const src = `${v.entity}: ${v.serviceName}()`;
|
|
1084
|
-
return v.deprecated
|
|
1152
|
+
return v.deprecated
|
|
1153
|
+
? generateInlineComment("@deprecated") + `\n${src}`
|
|
1154
|
+
: src;
|
|
1085
1155
|
}))};`;
|
|
1086
1156
|
const entityInterfaces = [
|
|
1087
|
-
...entitiesKeys.map(entity => ({
|
|
1157
|
+
...entitiesKeys.map((entity) => ({
|
|
1088
1158
|
name: entity,
|
|
1089
1159
|
type: pascalCase(entity),
|
|
1090
|
-
required: true
|
|
1160
|
+
required: true,
|
|
1091
1161
|
})),
|
|
1092
|
-
...services.map(service => {
|
|
1162
|
+
...services.map((service) => {
|
|
1093
1163
|
const alias = aliases.get(service.entity);
|
|
1094
1164
|
return {
|
|
1095
1165
|
name: service.entity,
|
|
1096
|
-
type: alias ??
|
|
1166
|
+
type: alias ?? "never",
|
|
1097
1167
|
required: true,
|
|
1098
|
-
comment: alias ? undefined :
|
|
1168
|
+
comment: alias ? undefined : "no response defined or inlined",
|
|
1099
1169
|
};
|
|
1100
|
-
})
|
|
1170
|
+
}),
|
|
1101
1171
|
];
|
|
1102
|
-
const createMappingType = (type, prefix) => type !==
|
|
1103
|
-
const entitiesList = generateInterface(
|
|
1104
|
-
const entityReferences = generateInterface(
|
|
1105
|
-
|
|
1106
|
-
|
|
1172
|
+
const createMappingType = (type, prefix) => type !== "never" ? `${type}_${prefix}` : type;
|
|
1173
|
+
const entitiesList = generateInterface("WEntities", entityInterfaces);
|
|
1174
|
+
const entityReferences = generateInterface("WEntityReferences", entityInterfaces.map((v) => ({
|
|
1175
|
+
...v,
|
|
1176
|
+
type: createMappingType(v.type, "References"),
|
|
1177
|
+
})));
|
|
1178
|
+
const entityMappings = generateInterface("WEntityMappings", entityInterfaces.map((v) => ({
|
|
1179
|
+
...v,
|
|
1180
|
+
type: createMappingType(v.type, "Mappings"),
|
|
1181
|
+
})));
|
|
1182
|
+
const entityFilter = generateInterface("WEntityFilters", entityInterfaces.map((v) => ({
|
|
1183
|
+
...v,
|
|
1184
|
+
type: createMappingType(v.type, "Filter"),
|
|
1185
|
+
})));
|
|
1107
1186
|
return {
|
|
1108
1187
|
source: generateStatements(
|
|
1109
1188
|
/* JS Values */
|
|
@@ -1127,16 +1206,15 @@ const generateMaps = ({ services, entities, aliases, enums, options }) => {
|
|
|
1127
1206
|
- the type for what is returned by the api
|
|
1128
1207
|
`, entitiesList),
|
|
1129
1208
|
/* type-ofs and types */
|
|
1130
|
-
generateType(
|
|
1209
|
+
generateType("WServices", "typeof wServices"), generateType("WServiceFactories", "typeof wServiceFactories"), generateType("WService", "keyof WServices"), generateType("WEntity", "keyof WEntities"), generateType("WEnums", "typeof wEnums"), generateType("WEnum", "keyof WEnums"),
|
|
1131
1210
|
/* Utilities. */
|
|
1132
1211
|
generateCustomValueUtilities(entities, services),
|
|
1133
1212
|
/* All functions grouped by service supporting it */
|
|
1134
|
-
...generateGroupedServices(services))
|
|
1213
|
+
...generateGroupedServices(services)),
|
|
1135
1214
|
};
|
|
1136
1215
|
};
|
|
1137
1216
|
|
|
1138
|
-
const parseReferencedEntity = (obj) => pascalCase(obj.$ref.replace(/.*\//,
|
|
1139
|
-
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
1217
|
+
const parseReferencedEntity = (obj) => pascalCase(obj.$ref.replace(/.*\//, ""));
|
|
1140
1218
|
const extractSchemas = (doc) => {
|
|
1141
1219
|
const schemas = new Map();
|
|
1142
1220
|
const aliases = new Map();
|
|
@@ -1155,7 +1233,7 @@ const extractSchemas = (doc) => {
|
|
|
1155
1233
|
continue;
|
|
1156
1234
|
}
|
|
1157
1235
|
for (const method of Object.values(OpenAPIV3.HttpMethods)) {
|
|
1158
|
-
const body = methods[method]?.responses[
|
|
1236
|
+
const body = methods[method]?.responses["200"];
|
|
1159
1237
|
if (isResponseObject(body) && body.content) {
|
|
1160
1238
|
const responseSchema = Object.values(body.content)[0]?.schema;
|
|
1161
1239
|
if (isReferenceObject(responseSchema)) {
|
|
@@ -1183,16 +1261,16 @@ const generate = (doc, options) => {
|
|
|
1183
1261
|
const enums = generateEnums(schemas);
|
|
1184
1262
|
const entities = generateEntities(schemas, enums);
|
|
1185
1263
|
const services = generateServices(doc, aliases, options);
|
|
1186
|
-
return generateStatements(generateBase(options.target, doc.info.version), generateBlockComment(
|
|
1264
|
+
return generateStatements(generateBase(options.target, doc.info.version), generateBlockComment("ENUMS", generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment("ENTITIES", generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment("SERVICES", generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment("MAPS", generateMaps({
|
|
1187
1265
|
services: [...services.values()],
|
|
1188
1266
|
enums: [...enums.keys()],
|
|
1189
1267
|
options,
|
|
1190
1268
|
entities,
|
|
1191
|
-
aliases
|
|
1269
|
+
aliases,
|
|
1192
1270
|
}).source));
|
|
1193
1271
|
};
|
|
1194
1272
|
|
|
1195
|
-
const hash = (content, algorithm =
|
|
1273
|
+
const hash = (content, algorithm = "sha256") => {
|
|
1196
1274
|
const hash = createHash(algorithm);
|
|
1197
1275
|
if (Array.isArray(content)) {
|
|
1198
1276
|
content.map(hash.update.bind(hash));
|
|
@@ -1200,99 +1278,103 @@ const hash = (content, algorithm = 'sha256') => {
|
|
|
1200
1278
|
else {
|
|
1201
1279
|
hash.update(content);
|
|
1202
1280
|
}
|
|
1203
|
-
return hash.digest(
|
|
1281
|
+
return hash.digest("hex");
|
|
1204
1282
|
};
|
|
1205
1283
|
|
|
1206
1284
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
1207
1285
|
const cli = async () => {
|
|
1208
|
-
const
|
|
1286
|
+
const version = pkg.version;
|
|
1209
1287
|
const { argv } = yargs(hideBin(process.argv))
|
|
1210
|
-
.scriptName(
|
|
1211
|
-
.usage(
|
|
1288
|
+
.scriptName("build-weclapp-sdk")
|
|
1289
|
+
.usage("Usage: $0 <source> [flags]")
|
|
1212
1290
|
.version(version)
|
|
1213
|
-
.example(
|
|
1214
|
-
.example(
|
|
1215
|
-
.help(
|
|
1216
|
-
.alias(
|
|
1217
|
-
.alias(
|
|
1218
|
-
.option(
|
|
1219
|
-
alias:
|
|
1220
|
-
describe:
|
|
1221
|
-
type:
|
|
1291
|
+
.example("$0 openapi.json", "Generate the SDK based on a local openapi file")
|
|
1292
|
+
.example("$0 xxx.weclapp.com --key ...", "Generate the SDK based on the openapi file from the given weclapp instance")
|
|
1293
|
+
.help("h")
|
|
1294
|
+
.alias("v", "version")
|
|
1295
|
+
.alias("h", "help")
|
|
1296
|
+
.option("k", {
|
|
1297
|
+
alias: "key",
|
|
1298
|
+
describe: "API Key (only needed when not using a local file)",
|
|
1299
|
+
type: "string",
|
|
1222
1300
|
})
|
|
1223
|
-
.option(
|
|
1224
|
-
alias:
|
|
1225
|
-
describe:
|
|
1226
|
-
type:
|
|
1301
|
+
.option("c", {
|
|
1302
|
+
alias: "cache",
|
|
1303
|
+
describe: "If the generated SDK should cached",
|
|
1304
|
+
type: "boolean",
|
|
1227
1305
|
})
|
|
1228
|
-
.option(
|
|
1229
|
-
alias:
|
|
1230
|
-
describe:
|
|
1231
|
-
type:
|
|
1306
|
+
.option("q", {
|
|
1307
|
+
alias: "query",
|
|
1308
|
+
describe: "Extra query params when fetching the openapi.json from a server",
|
|
1309
|
+
type: "string",
|
|
1232
1310
|
})
|
|
1233
|
-
.option(
|
|
1234
|
-
describe:
|
|
1235
|
-
type:
|
|
1311
|
+
.option("generate-unique", {
|
|
1312
|
+
describe: "Generate .unique functions",
|
|
1313
|
+
type: "boolean",
|
|
1236
1314
|
})
|
|
1237
|
-
.option(
|
|
1238
|
-
alias:
|
|
1239
|
-
describe:
|
|
1240
|
-
type:
|
|
1315
|
+
.option("d", {
|
|
1316
|
+
alias: "deprecated",
|
|
1317
|
+
describe: "Include deprecated functions and services",
|
|
1318
|
+
type: "boolean",
|
|
1241
1319
|
})
|
|
1242
|
-
.option(
|
|
1243
|
-
alias:
|
|
1244
|
-
describe:
|
|
1245
|
-
type:
|
|
1320
|
+
.option("e", {
|
|
1321
|
+
alias: "from-env",
|
|
1322
|
+
describe: "Use env variables WECLAPP_BACKEND_URL and WECLAPP_API_KEY as credentials",
|
|
1323
|
+
type: "boolean",
|
|
1246
1324
|
})
|
|
1247
|
-
.option(
|
|
1248
|
-
alias:
|
|
1249
|
-
describe:
|
|
1250
|
-
type:
|
|
1251
|
-
choices: [
|
|
1325
|
+
.option("t", {
|
|
1326
|
+
alias: "target",
|
|
1327
|
+
describe: "Specify the target platform",
|
|
1328
|
+
type: "string",
|
|
1329
|
+
choices: ["browser", "browser.rx", "node", "node.rx"],
|
|
1252
1330
|
})
|
|
1253
|
-
.option(
|
|
1254
|
-
alias:
|
|
1255
|
-
describe:
|
|
1256
|
-
type:
|
|
1331
|
+
.option("d", {
|
|
1332
|
+
alias: "deprecated",
|
|
1333
|
+
describe: "Include deprecated functions and services",
|
|
1334
|
+
type: "boolean",
|
|
1257
1335
|
})
|
|
1258
1336
|
.epilog(`Copyright ${new Date().getFullYear()} weclapp GmbH`);
|
|
1259
1337
|
if (argv.fromEnv) {
|
|
1260
1338
|
config();
|
|
1261
1339
|
}
|
|
1262
1340
|
const { WECLAPP_API_KEY, WECLAPP_BACKEND_URL } = process.env;
|
|
1263
|
-
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, _: [src = WECLAPP_BACKEND_URL] } = argv;
|
|
1341
|
+
const { query, cache = false, deprecated = false, key = WECLAPP_API_KEY, _: [src = WECLAPP_BACKEND_URL], } = argv;
|
|
1264
1342
|
const options = {
|
|
1265
1343
|
deprecated,
|
|
1266
1344
|
generateUnique: argv.generateUnique ?? false,
|
|
1267
|
-
target: argv.target ?? Target.BROWSER_PROMISES
|
|
1345
|
+
target: argv.target ?? Target.BROWSER_PROMISES,
|
|
1268
1346
|
};
|
|
1269
|
-
if (typeof src ===
|
|
1270
|
-
return Promise.reject(
|
|
1347
|
+
if (!src || typeof src === "number") {
|
|
1348
|
+
return Promise.reject(new Error("Expected string as command"));
|
|
1271
1349
|
}
|
|
1272
1350
|
if (!Object.values(Target).includes(options.target)) {
|
|
1273
|
-
logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(
|
|
1274
|
-
return Promise.reject();
|
|
1351
|
+
logger.errorLn(`Unknown target: ${options.target}. Possible values are ${Object.values(Target).join(", ")}`);
|
|
1352
|
+
return Promise.reject(new Error());
|
|
1275
1353
|
}
|
|
1276
1354
|
if (await stat(src).catch(() => false)) {
|
|
1277
|
-
logger.infoLn(`Source is a file
|
|
1278
|
-
const content = JSON.parse(await readFile(src,
|
|
1355
|
+
logger.infoLn(`Source is a file`);
|
|
1356
|
+
const content = JSON.parse(await readFile(src, "utf-8"));
|
|
1279
1357
|
return { cache, content, options };
|
|
1280
1358
|
}
|
|
1281
|
-
|
|
1359
|
+
logger.infoLn(`Source is a URL`);
|
|
1360
|
+
if (!key) {
|
|
1361
|
+
return Promise.reject(new Error("API key is missing"));
|
|
1362
|
+
}
|
|
1363
|
+
const url = new URL(src.startsWith("http") ? src : `https://${src}`);
|
|
1282
1364
|
// At the moment just v1
|
|
1283
|
-
url.pathname =
|
|
1365
|
+
url.pathname = "/webapp/api/v1/meta/openapi.json";
|
|
1284
1366
|
if (query?.length) {
|
|
1285
|
-
for (const param of query.split(
|
|
1286
|
-
const [name, value] = param.split(
|
|
1367
|
+
for (const param of query.split(",")) {
|
|
1368
|
+
const [name, value] = param.split("=");
|
|
1287
1369
|
url.searchParams.set(name, value);
|
|
1288
1370
|
}
|
|
1289
1371
|
}
|
|
1290
1372
|
const content = await fetch(url.toString(), {
|
|
1291
|
-
headers: {
|
|
1292
|
-
}).then(res => res.ok ? res.json() : undefined);
|
|
1373
|
+
headers: { Accept: "application/json", AuthenticationToken: key },
|
|
1374
|
+
}).then((res) => (res.ok ? res.json() : undefined));
|
|
1293
1375
|
if (!content) {
|
|
1294
1376
|
logger.errorLn(`Couldn't fetch file ${url.toString()} `);
|
|
1295
|
-
return Promise.reject();
|
|
1377
|
+
return Promise.reject(new Error());
|
|
1296
1378
|
}
|
|
1297
1379
|
else {
|
|
1298
1380
|
logger.infoLn(`Use remote file: ${url.toString()}`);
|
|
@@ -1300,50 +1382,61 @@ const cli = async () => {
|
|
|
1300
1382
|
return { cache, content, options };
|
|
1301
1383
|
};
|
|
1302
1384
|
|
|
1303
|
-
const
|
|
1304
|
-
const
|
|
1385
|
+
const workingDir = resolve(currentDirname(), "./sdk");
|
|
1386
|
+
const cacheDir = resolve(currentDirname(), "./.cache");
|
|
1305
1387
|
void (async () => {
|
|
1306
1388
|
const start = process.hrtime.bigint();
|
|
1307
|
-
const { default: { version } } = await import('../package.json', { assert: { type: 'json' } });
|
|
1308
1389
|
const { content: doc, cache: useCache, options } = await cli();
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
const dist = (...paths) => resolve(workingDirectory, ...paths);
|
|
1313
|
-
const tmp = async (...paths) => {
|
|
1314
|
-
const fullPath = resolve(cacheDir, ...paths);
|
|
1315
|
-
await mkdir(dirname(fullPath), { recursive: true }).catch(() => null);
|
|
1390
|
+
const workingDirPath = async (...paths) => {
|
|
1391
|
+
const fullPath = resolve(workingDir, ...paths);
|
|
1392
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
1316
1393
|
return fullPath;
|
|
1317
1394
|
};
|
|
1395
|
+
// Resolve cache dir and key
|
|
1396
|
+
const cacheKey = hash([
|
|
1397
|
+
pkg.version,
|
|
1398
|
+
JSON.stringify(doc),
|
|
1399
|
+
JSON.stringify(options),
|
|
1400
|
+
]).slice(-8);
|
|
1401
|
+
const cachedSdkDir = resolve(cacheDir, cacheKey);
|
|
1402
|
+
// Remove old SDK
|
|
1403
|
+
await rm(workingDir, { recursive: true, force: true });
|
|
1318
1404
|
if (useCache) {
|
|
1319
1405
|
logger.infoLn(`Cache ID: ${cacheKey}`);
|
|
1320
1406
|
}
|
|
1321
|
-
if (useCache && await stat(
|
|
1322
|
-
|
|
1407
|
+
if (useCache && (await stat(cachedSdkDir).catch(() => false))) {
|
|
1408
|
+
// Copy cached SDK to working dir
|
|
1409
|
+
logger.successLn(`Cache match! (${cachedSdkDir})`);
|
|
1410
|
+
await cp(cachedSdkDir, workingDir, { recursive: true });
|
|
1323
1411
|
}
|
|
1324
1412
|
else {
|
|
1325
|
-
//
|
|
1326
|
-
await writeFile(await
|
|
1413
|
+
// Write swagger.json file
|
|
1414
|
+
await writeFile(await workingDirPath("openapi.json"), JSON.stringify(doc, null, 2));
|
|
1327
1415
|
logger.infoLn(`Generate sdk (target: ${options.target})`);
|
|
1328
|
-
// Generate
|
|
1416
|
+
// Generate and write SDK
|
|
1329
1417
|
const sdk = generate(doc, options);
|
|
1330
|
-
await writeFile(await
|
|
1331
|
-
// Bundle
|
|
1332
|
-
logger.infoLn(
|
|
1333
|
-
await bundle(
|
|
1334
|
-
|
|
1335
|
-
|
|
1418
|
+
await writeFile(await workingDirPath("src", "index.ts"), sdk.trim() + "\n");
|
|
1419
|
+
// Bundle and write SDK
|
|
1420
|
+
logger.infoLn("Bundle... (this may take some time)");
|
|
1421
|
+
await bundle(workingDir, options.target);
|
|
1422
|
+
if (useCache) {
|
|
1423
|
+
// Copy SDK to cache
|
|
1424
|
+
logger.successLn(`Caching SDK: (${cachedSdkDir})`);
|
|
1425
|
+
await mkdir(cachedSdkDir, { recursive: true });
|
|
1426
|
+
await cp(workingDir, cachedSdkDir, { recursive: true });
|
|
1427
|
+
}
|
|
1336
1428
|
}
|
|
1337
|
-
// Copy bundled SDK
|
|
1338
|
-
await cp(cacheDir, workingDirectory, { recursive: true });
|
|
1339
1429
|
// Print job summary
|
|
1340
1430
|
const duration = (process.hrtime.bigint() - start) / 1000000n;
|
|
1341
1431
|
logger.successLn(`SDK built in ${prettyMs(Number(duration))}`);
|
|
1342
1432
|
logger.printSummary();
|
|
1343
|
-
})()
|
|
1433
|
+
})()
|
|
1434
|
+
.catch((error) => {
|
|
1344
1435
|
logger.errorLn(`Fatal error:`);
|
|
1345
1436
|
/* eslint-disable no-console */
|
|
1346
1437
|
console.error(error);
|
|
1347
|
-
})
|
|
1348
|
-
|
|
1438
|
+
})
|
|
1439
|
+
.finally(() => {
|
|
1440
|
+
if (logger.errors)
|
|
1441
|
+
process.exit(1);
|
|
1349
1442
|
});
|