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