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