@weclapp/sdk 2.0.0-dev.30 → 2.0.0-dev.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +331 -353
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { rollup } from 'rollup';
|
|
|
4
4
|
import terser from '@rollup/plugin-terser';
|
|
5
5
|
import ts from '@rollup/plugin-typescript';
|
|
6
6
|
import indentString from 'indent-string';
|
|
7
|
-
import { snakeCase,
|
|
7
|
+
import { snakeCase, camelCase, pascalCase } from 'change-case';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { OpenAPIV3 } from 'openapi-types';
|
|
10
10
|
import { createHash } from 'crypto';
|
|
@@ -15,6 +15,11 @@ import { hideBin } from 'yargs/helpers';
|
|
|
15
15
|
import pkg from '../package.json' with { type: 'json' };
|
|
16
16
|
import prettyMs from 'pretty-ms';
|
|
17
17
|
|
|
18
|
+
const currentDirname = () => {
|
|
19
|
+
// Go one level up as the CLI is inside a folder
|
|
20
|
+
return fileURLToPath(new URL('..', import.meta.url));
|
|
21
|
+
};
|
|
22
|
+
|
|
18
23
|
var Target;
|
|
19
24
|
(function (Target) {
|
|
20
25
|
Target["BROWSER_PROMISES"] = "browser";
|
|
@@ -35,11 +40,6 @@ const resolveBinaryType = (target) => {
|
|
|
35
40
|
return isNodeTarget(target) ? 'Buffer' : 'Blob';
|
|
36
41
|
};
|
|
37
42
|
|
|
38
|
-
const currentDirname = () => {
|
|
39
|
-
// Go one level up as the CLI is inside a folder
|
|
40
|
-
return fileURLToPath(new URL('..', import.meta.url));
|
|
41
|
-
};
|
|
42
|
-
|
|
43
43
|
const tsconfig = resolve(currentDirname(), './tsconfig.sdk.json');
|
|
44
44
|
const resolveGlobals = (...globals) => Object.fromEntries(globals.map((v) => [v, '*']));
|
|
45
45
|
const generateOutput = (config) => ({
|
|
@@ -134,15 +134,19 @@ const generateBlockStatements = (...statements) => `{\n${indent(generateStatemen
|
|
|
134
134
|
|
|
135
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";
|
|
136
136
|
|
|
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";
|
|
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 void batch(batchTasks);\n tasksSet.clear();\n }\n });\n microtaskQueued = true;\n }\n};\n\nconst addRequest = (uri: string) => new Promise((resolve, reject) => addTask({ uri, resolve, reject }));\n";
|
|
138
|
+
|
|
139
|
+
var queriesWithFilter = "export type EqualityOperator = 'EQ' | 'NE';\n\nexport type ComparisonOperator =\n | 'LT'\n | 'GT'\n | 'LE'\n | 'GE'\n | 'LIKE'\n | 'ILIKE'\n | 'NOT_LIKE'\n | 'NOT_ILIKE'\n | 'IEQ'\n | 'NOT_IEQ';\n\nexport type ArrayOperator = 'IN' | 'NOT_IN';\n\nexport type Operator = EqualityOperator | ComparisonOperator | ArrayOperator;\n\nexport type MapOperators<T> = { [K in EqualityOperator]?: T | null } & { [K in ComparisonOperator]?: T } & {\n [K in ArrayOperator]?: T[];\n};\n\nexport type QueryFilter<T> = {\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\n ? U extends Record<any, any>\n ? QueryFilter<U>\n : MapOperators<U>\n : T[P] extends Record<any, any> | undefined\n ? QueryFilter<T[P]>\n : MapOperators<T[P]>;\n};\n\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n};\n\nexport type SomeQuery<E, F, I, P> = {\n serializeNulls?: boolean;\n include?: QuerySelect<I>;\n properties?: P;\n filter?: QueryFilter<F> & CustomAttributeFilter;\n select?: QuerySelect<E>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst equality: string[] = ['EQ', 'NE', 'IEQ', 'NOT_IEQ'];\n\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\n\nconst array: string[] = ['IN', 'NOT_IN'];\n\nconst filterMap: Record<Operator, string> = {\n EQ: 'eq',\n NE: 'ne',\n LT: 'lt',\n GT: 'gt',\n LE: 'le',\n GE: 'ge',\n LIKE: 'like',\n NOT_LIKE: 'notlike',\n ILIKE: 'ilike',\n NOT_ILIKE: 'notilike',\n IN: 'in',\n NOT_IN: 'notin',\n IEQ: 'ieq',\n NOT_IEQ: 'notieq'\n};\n\nconst flattenCustomAttributes = (obj: CustomAttributeFilter = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [id, filter] of Object.entries(obj)) {\n const key = `customAttribute${id}`;\n\n if (typeof filter === 'object') {\n for (const [prop, value] of Object.entries(filter)) {\n entries.push([`${key}.${prop}-eq`, String(value)]);\n }\n } else if (filter !== undefined) {\n entries.push([`${key}-eq`, String(filter)]);\n }\n }\n\n return entries;\n};\n\nconst flatten = (obj: QueryFilter<any> = {}): [string, string][] => {\n const entries: [string, string][] = [];\n\n for (const [prop, propValue] of Object.entries(obj)) {\n for (const [filter, value] of Object.entries(propValue as object)) {\n if (value === undefined) continue;\n\n if (simple.includes(filter) || array.includes(filter)) {\n if (value === null && equality.includes(filter)) {\n entries.push([`${prop}-${filter === 'EQ' ? 'null' : 'notnull'}`, '']);\n } else {\n entries.push([`${prop}-${filterMap[filter as Operator]}`, value]);\n }\n } else {\n entries.push(\n ...(flatten(propValue as QueryFilter<any>).map((v) => [`${prop}.${v[0]}`, v[1]]) as [string, string][])\n );\n break;\n }\n }\n }\n\n return entries;\n};\n\nconst flattenFilter = (obj: QueryFilter<any> = {}): Record<string, string> => {\n const filter: [string, any][] = [],\n customAttributes: [string, any][] = [];\n\n Object.entries(obj).forEach((value) => {\n (value[0].match(/^\\d+$/) ? customAttributes : filter).push(value);\n });\n\n return Object.fromEntries([\n ...flatten(Object.fromEntries(filter)),\n ...flattenCustomAttributes(Object.fromEntries(customAttributes) as CustomAttributeFilter)\n ]);\n};\n\nconst flattenOrFilter = (obj: QueryFilter<any>[] = []): Record<string, string> => {\n const entries: [string, any][] = [];\n\n for (let i = 0; i < obj.length; i++) {\n entries.push(...(flatten(obj[i]).map((v) => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]));\n }\n\n return Object.fromEntries(entries);\n};\n\nconst _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & { params?: Record<any, any> }\n) =>\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> & { 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";
|
|
140
|
+
|
|
141
|
+
var queriesWithQueryLanguage = "export type ComparisonOperator =\n | 'EQ'\n | 'NE'\n | 'LT'\n | 'GT'\n | 'LE'\n | 'GE'\n | 'LIKE';\n\nexport type ArrayOperator = 'IN';\n\nexport type NullOperator = 'NULL';\n\nexport type Operator = ComparisonOperator | ArrayOperator | NullOperator;\n\nexport type ModifierFunction = 'lower';\n\nexport type MapOperators<T> =\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: T[] } & {\n [K in NullOperator]?: never } & {\n [K in ModifierFunction]?: boolean\n })\n | ({ [K in ComparisonOperator]?: T } & { [K in ArrayOperator]?: T[] } & {\n [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\nexport type CountQuery<F> = {\n where?: QueryFilter<F> & CustomAttributeFilter;\n};\n\nexport type SomeQuery<E, F, I, P> = {\n serializeNulls?: boolean;\n include?: QuerySelect<I>;\n properties?: P;\n where?: QueryFilter<F> & CustomAttributeFilter;\n select?: QuerySelect<E>;\n sort?: Sort<E>[];\n pagination?: Pagination;\n};\n\nconst comparisonOperatorList: ComparisonOperator[] = [\n 'EQ',\n 'NE',\n 'LT',\n 'GT',\n 'LE',\n 'GE',\n 'LIKE'\n];\n\nconst comparisonOperatorMap: Record<Operator, string> = {\n EQ: '=',\n NE: '!=',\n LT: '<',\n GT: '>',\n LE: '<=',\n GE: '>=',\n LIKE: '~',\n IN: 'in',\n NULL: 'null'\n};\n\nconst modifierFunctionList: ModifierFunction[] = ['lower'];\n\nconst flattenWhere = (\n obj: QueryFilter<any> = {},\n nestedPaths: string[]\n): string[] => {\n const entries: string[] = [];\n for (const [prop, propValue] of Object.entries(obj)) {\n const setModifiers = findAllModifierFunctions(propValue ?? {}, modifierFunctionList).filter(\n (modifier) => modifier[1]\n );\n if (prop === 'OR') {\n const flattedOr: string[][] = [];\n for (let i = 0; i < (obj.OR?.length ?? 0); i++) {\n flattedOr.push(flattenWhere(obj.OR?.[i], nestedPaths));\n }\n entries.push(\n `(${flattedOr\n .map((x) => {\n const joined = x.join(' and ');\n\n if (x.length > 1) {\n return `(${joined})`;\n } else {\n return joined;\n }\n })\n .join(' or ')})`\n );\n } else if (prop === 'AND') {\n const flattedAnd: string[][] = [];\n for (let i = 0; i < (obj.AND?.length ?? 0); i++) {\n flattedAnd.push(flattenWhere(obj.AND?.[i], nestedPaths));\n }\n entries.push(\n `(${flattedAnd\n .map((x) => {\n const joined = x.join(' and ');\n\n if (x.length > 1) {\n return `(${joined})`;\n } else {\n return joined;\n }\n })\n .join(' and ')})`\n );\n } else if (prop === 'NOT') {\n const flattedNot = flattenWhere(obj.NOT, nestedPaths);\n entries.push(\n `not ${flattedNot.length > 1 ? '(' : ''}${flattedNot.join(' and ')}${flattedNot.length > 1 ? ')' : ''}`\n );\n } else if (propValue) {\n for (const [operator, value] of Object.entries(propValue)) {\n if (value === undefined) continue;\n if (comparisonOperatorList.includes(operator as ComparisonOperator)) {\n entries.push(\n `${setModifiers.reduce(\n (acc, [first]) => `${first}(${acc})`,\n nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')\n )} ${comparisonOperatorMap[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('.')} ${comparisonOperatorMap[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 )} ${comparisonOperatorMap[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 (\n !modifierFunctionList.includes(operator as ModifierFunction)\n ) {\n entries.push(\n ...flattenWhere(propValue as QueryFilter<any>, [\n ...nestedPaths,\n prop\n ])\n );\n break;\n }\n }\n }\n }\n return entries;\n};\n\nconst assembleFilterParam = (\n obj: QueryFilter<any> = {}\n): Record<string, string> => {\n const flattedFilter = flattenWhere(obj, []);\n return flattedFilter.length ? { filter: flattedFilter.join(' and ') } : {};\n};\n\nconst findAllModifierFunctions = (\n obj: Record<string, any>,\n types: ModifierFunction[]\n) => {\n const result: 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> & { 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\n ? flattenSelect(query.select).join(',')\n : undefined,\n includeReferencedEntities: query?.include\n ? Object.keys(query.include).join(',')\n : undefined,\n ...assembleFilterParam(query?.where),\n ...flattenSort(query?.sort),\n ...query?.params,\n ...query?.pagination\n }\n }).then((data) => ({\n entities: data.result,\n references: data.referencedEntities ?? {},\n properties: data.additionalProperties ?? {}\n }))\n );\n";
|
|
138
142
|
|
|
139
|
-
var
|
|
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;\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 _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";
|
|
140
144
|
|
|
141
|
-
var
|
|
145
|
+
var unique = "const _unique = (cfg: ServiceConfigWithoutMultiRequest | undefined, endpoint: string, query?: UniqueQuery) =>\n wrapResponse(() => raw({ ...cfg, multiRequest: false }, endpoint, { query }));\n";
|
|
142
146
|
|
|
143
|
-
var
|
|
147
|
+
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\nexport type CustomAttributeFilter = {\n [K in number]:| string | number | boolean | { id: string } | { entityName: string; entityId: string };\n};\n\nexport type QuerySelect<T> = {\n [P in keyof T]?: T[P] extends Array<infer U> | undefined\n ? QuerySelect<U> | boolean\n : T[P] extends Record<any, any> | undefined\n ? QuerySelect<T[P]> | boolean\n : boolean;\n};\n\nexport type Select<T, Q extends QuerySelect<T> | undefined> =\n Q extends QuerySelect<T>\n ? {\n // Filter out excluded properties beforehand\n [P in keyof T as Q[P] extends boolean ? P : Q[P] extends object ? P : never]: // Property\n Q[P] extends true\n ? T[P]\n : // Array\n T[P] extends Array<infer U>\n ? Select<U, Q[P] & QuerySelect<any>>[]\n : // Object\n T[P] extends Record<any, any>\n ? Select<T[P], Q[P] & QuerySelect<any>>\n : never;\n }\n : undefined;\n\nexport type MapKeys<T, S extends Record<keyof T, string>> = {\n [K in keyof T as S[K]]: T[K];\n};\n\nexport type ValueOf<T> = T[keyof T];\n\nexport type Pagination = {\n page: number;\n pageSize: number;\n};\n\nexport type UniqueQuery = {\n serializeNulls?: boolean;\n};\n\nexport type SomeQueryReturn<E, R, P> = {\n entities: E[];\n references?: R;\n properties?: P[];\n};\n\nexport type GenericQuery<P, B> = {\n params?: P;\n body?: B;\n};\n\nexport type UpdateQuery = {\n ignoreMissingProperties?: boolean;\n dryRun?: boolean;\n};\n\nexport type RemoveQuery = {\n dryRun?: boolean;\n};\n\nexport type WEntityPropertyMeta =\n | {\n type: 'string';\n format?: 'decimal' | 'html' | 'email' | 'password';\n maxLength?: number;\n pattern?: string;\n entity?: WEntity;\n service?: WService;\n }\n | {\n type: 'integer';\n format: 'int32' | 'int64' | 'duration' | 'date' | 'timestamp';\n }\n | { type: 'array'; format: 'reference'; entity: WEntity; service?: WService }\n | { type: 'array'; format: 'reference'; enum: WEnum }\n | { type: 'array'; format: 'string' }\n | { type: 'number'; format: 'double' }\n | { type: 'reference'; entity: WEntity }\n | { type: 'reference'; enum: WEnum }\n | { type: 'boolean' }\n | { type: 'object' };\n";
|
|
144
148
|
|
|
145
|
-
var
|
|
149
|
+
var utils = "const flattenSelect = (obj: Select<any, any> = {}): string[] => {\n const entries: string[] = [];\n\n for (const [prop, value] of Object.entries(obj)) {\n if (typeof value === 'object' && value) {\n entries.push(...flattenSelect(value).map((v) => `${prop}.${v}`));\n } else if (value) {\n entries.push(prop);\n }\n }\n\n return entries;\n};\n\nexport const flattenSort = (obj: Sort<any>[] = []): { sort?: string } => {\n const flatten = (obj: Sort<any>, base = ''): string | undefined => {\n const [key, value] = Object.entries(obj ?? {})[0] ?? [];\n\n if (key && value) {\n const path = base + key;\n\n if (typeof value === 'object') {\n return flatten(value, path ? `${path}.` : '');\n } else if (['asc', 'desc'].includes(value)) {\n return `${value === 'desc' ? '-' : ''}${path}`;\n }\n }\n\n return undefined;\n };\n\n const sorts = obj.map((v) => flatten(v)).filter(Boolean);\n return sorts.length ? { sort: sorts.join(',') } : {};\n};\n";
|
|
146
150
|
|
|
147
151
|
const resolveImports = (target) => {
|
|
148
152
|
const imports = [];
|
|
@@ -153,8 +157,8 @@ const resolveImports = (target) => {
|
|
|
153
157
|
};
|
|
154
158
|
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
|
|
155
159
|
const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
|
|
156
|
-
const generateBase = (target, apiVersion,
|
|
157
|
-
return generateStatements(resolveImports(target),
|
|
160
|
+
const generateBase = (target, apiVersion, options) => {
|
|
161
|
+
return generateStatements(resolveImports(target), `const apiVersion = ${apiVersion}`, resolveMappings(target), resolveBinaryClass(target), globalConfig, types, utils, root, options.useQueryLanguage ? queriesWithQueryLanguage : queriesWithFilter, options.generateUnique ? unique : '', multiRequest);
|
|
158
162
|
};
|
|
159
163
|
|
|
160
164
|
const transformKey = (s) => snakeCase(s).toUpperCase();
|
|
@@ -196,13 +200,14 @@ const isRelatedEntitySchema = (v) => {
|
|
|
196
200
|
|
|
197
201
|
const generateEnums = (schemas) => {
|
|
198
202
|
const enums = new Map();
|
|
199
|
-
for (const [
|
|
203
|
+
for (const [schemaName, schema] of schemas) {
|
|
200
204
|
if (isEnumSchemaObject(schema)) {
|
|
201
|
-
const
|
|
202
|
-
if (!enums.has(
|
|
203
|
-
enums.set(
|
|
205
|
+
const enumName = loosePascalCase(schemaName);
|
|
206
|
+
if (!enums.has(enumName)) {
|
|
207
|
+
enums.set(enumName, {
|
|
208
|
+
name: enumName,
|
|
204
209
|
properties: schema.enum,
|
|
205
|
-
source: generateEnum(
|
|
210
|
+
source: generateEnum(enumName, schema.enum)
|
|
206
211
|
});
|
|
207
212
|
}
|
|
208
213
|
}
|
|
@@ -368,35 +373,13 @@ const generateEntities = (schemas, enums) => {
|
|
|
368
373
|
if (isEnumSchemaObject(schema)) {
|
|
369
374
|
continue;
|
|
370
375
|
}
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
const filterInterface = [];
|
|
375
|
-
// Referenced entities and property-to-referenced-entity mapping
|
|
376
|
-
const referenceInterface = [];
|
|
377
|
-
const referenceMappingsInterface = [];
|
|
376
|
+
const entityInterfaceName = loosePascalCase(schemaName);
|
|
377
|
+
let parentEntityInterfaceName = undefined;
|
|
378
|
+
const entityInterfaceProperties = [];
|
|
378
379
|
const properties = new Map();
|
|
379
|
-
// The parent entity
|
|
380
|
-
let extend = undefined;
|
|
381
380
|
const processProperties = (props = {}) => {
|
|
382
381
|
for (const [name, property] of Object.entries(props)) {
|
|
383
382
|
const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
|
|
384
|
-
if (meta.entity) {
|
|
385
|
-
const type = `${pascalCase(meta.entity)}[]`;
|
|
386
|
-
if (schemas.has(meta.entity)) {
|
|
387
|
-
referenceInterface.push({ name, type, required: true });
|
|
388
|
-
filterInterface.push({ name: meta.entity, type, required: true });
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
if (meta.service) {
|
|
392
|
-
if (schemas.has(meta.service)) {
|
|
393
|
-
referenceMappingsInterface.push({
|
|
394
|
-
name,
|
|
395
|
-
type: generateString(meta.service),
|
|
396
|
-
required: true
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
383
|
const type = convertToTypeScriptType(property, name).toString();
|
|
401
384
|
const comment = isNonArraySchemaObject(property)
|
|
402
385
|
? property.deprecated
|
|
@@ -405,7 +388,7 @@ const generateEntities = (schemas, enums) => {
|
|
|
405
388
|
? `format: ${property.format}`
|
|
406
389
|
: undefined
|
|
407
390
|
: undefined;
|
|
408
|
-
|
|
391
|
+
entityInterfaceProperties.push({
|
|
409
392
|
name,
|
|
410
393
|
type,
|
|
411
394
|
comment,
|
|
@@ -418,7 +401,7 @@ const generateEntities = (schemas, enums) => {
|
|
|
418
401
|
if (schema.allOf?.length) {
|
|
419
402
|
for (const item of schema.allOf) {
|
|
420
403
|
if (isReferenceObject(item)) {
|
|
421
|
-
|
|
404
|
+
parentEntityInterfaceName = convertToTypeScriptType(item).toString();
|
|
422
405
|
}
|
|
423
406
|
else if (isObjectSchemaObject(item)) {
|
|
424
407
|
processProperties(item.properties);
|
|
@@ -426,11 +409,11 @@ const generateEntities = (schemas, enums) => {
|
|
|
426
409
|
}
|
|
427
410
|
}
|
|
428
411
|
processProperties(schema.properties);
|
|
429
|
-
const source = generateStatements(generateInterface(entity, entityInterface, extend), generateInterface(`${entity}_References`, referenceInterface, extend ? [`${extend}_References`] : undefined), generateInterface(`${entity}_Mappings`, referenceMappingsInterface, extend ? [`${extend}_Mappings`] : undefined), generateInterfaceType(`${entity}_Filter`, filterInterface, extend ? [entity, `${extend}_Filter`] : undefined));
|
|
430
412
|
entities.set(schemaName, {
|
|
431
|
-
|
|
413
|
+
name: schemaName,
|
|
432
414
|
properties,
|
|
433
|
-
|
|
415
|
+
parentName: parentEntityInterfaceName ? camelCase(parentEntityInterfaceName) : undefined,
|
|
416
|
+
source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName))
|
|
434
417
|
});
|
|
435
418
|
}
|
|
436
419
|
return entities;
|
|
@@ -506,10 +489,10 @@ const logger = new (class {
|
|
|
506
489
|
})();
|
|
507
490
|
|
|
508
491
|
/**
|
|
509
|
-
* ROOT
|
|
510
|
-
* COUNT
|
|
511
|
-
* ENTITY
|
|
512
|
-
* SPECIAL_ROOT
|
|
492
|
+
* ROOT => /article
|
|
493
|
+
* COUNT => /article/count
|
|
494
|
+
* ENTITY => /article/{id}
|
|
495
|
+
* SPECIAL_ROOT => /article/generateImage
|
|
513
496
|
* SPECIAL_ENTITY => /article/id/{id}/generateImag
|
|
514
497
|
*/
|
|
515
498
|
var WeclappEndpointType;
|
|
@@ -521,22 +504,22 @@ var WeclappEndpointType;
|
|
|
521
504
|
WeclappEndpointType["GENERIC_ENTITY"] = "GENERIC_ENTITY";
|
|
522
505
|
})(WeclappEndpointType || (WeclappEndpointType = {}));
|
|
523
506
|
const parseEndpointPath = (path) => {
|
|
524
|
-
const [,
|
|
525
|
-
if (!
|
|
507
|
+
const [, service, ...rest] = path.split('/');
|
|
508
|
+
if (!service) {
|
|
526
509
|
return undefined;
|
|
527
510
|
}
|
|
528
511
|
if (!rest.length) {
|
|
529
|
-
return { path,
|
|
512
|
+
return { path, service, type: WeclappEndpointType.ROOT };
|
|
530
513
|
}
|
|
531
514
|
else if (rest[0] === 'count') {
|
|
532
|
-
return { path,
|
|
515
|
+
return { path, service, type: WeclappEndpointType.COUNT };
|
|
533
516
|
}
|
|
534
517
|
else if (rest[0] === 'id') {
|
|
535
518
|
return rest.length === 2
|
|
536
|
-
? { path,
|
|
519
|
+
? { path, service, type: WeclappEndpointType.ENTITY }
|
|
537
520
|
: {
|
|
538
521
|
path,
|
|
539
|
-
|
|
522
|
+
service,
|
|
540
523
|
method: rest[2],
|
|
541
524
|
type: WeclappEndpointType.GENERIC_ENTITY
|
|
542
525
|
};
|
|
@@ -544,7 +527,7 @@ const parseEndpointPath = (path) => {
|
|
|
544
527
|
else if (rest.length === 1) {
|
|
545
528
|
return {
|
|
546
529
|
path,
|
|
547
|
-
|
|
530
|
+
service,
|
|
548
531
|
method: rest[1],
|
|
549
532
|
type: WeclappEndpointType.GENERIC_ROOT
|
|
550
533
|
};
|
|
@@ -581,38 +564,38 @@ const convertParametersToSchema = (parameters = []) => {
|
|
|
581
564
|
};
|
|
582
565
|
};
|
|
583
566
|
|
|
584
|
-
const functionName$5 = 'count';
|
|
585
567
|
const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
568
|
+
const functionName = 'count';
|
|
569
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
570
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
571
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
572
|
+
const parametersType = createObjectType({
|
|
573
|
+
params: convertToTypeScriptType(convertParametersToSchema(path.parameters))
|
|
574
|
+
});
|
|
575
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
576
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
577
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [entity]);
|
|
578
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
579
|
+
type: functionTypeName,
|
|
580
|
+
params: [
|
|
581
|
+
`query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${path.parameters?.length ? ' & ' + parametersTypeName : ''}`
|
|
582
|
+
],
|
|
583
|
+
returns: `${resolveResponseType(target)}<number>`
|
|
594
584
|
});
|
|
595
585
|
const functionSource = generateArrowFunction({
|
|
596
|
-
name: functionName
|
|
597
|
-
signature:
|
|
598
|
-
returns: `_${functionName
|
|
586
|
+
name: functionName,
|
|
587
|
+
signature: functionTypeName,
|
|
588
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query)`,
|
|
599
589
|
params: ['query']
|
|
600
590
|
});
|
|
601
|
-
const interfaceSource = generateArrowFunctionType({
|
|
602
|
-
type: interfaceName,
|
|
603
|
-
params: [`query${parameters.isFullyOptional() ? '?' : ''}: CountQuery<${entityFilter}> & ${entityParameters}`],
|
|
604
|
-
returns: `${resolveResponseType(target)}<number>`
|
|
605
|
-
});
|
|
606
591
|
return {
|
|
607
592
|
entity,
|
|
608
|
-
name: functionName
|
|
609
|
-
type: { name:
|
|
610
|
-
func: { name: functionName
|
|
593
|
+
name: functionName,
|
|
594
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
595
|
+
func: { name: functionName, source: functionSource },
|
|
611
596
|
interfaces: [
|
|
612
|
-
{
|
|
613
|
-
|
|
614
|
-
source: generateInterfaceFromObject(entityParameters, parameters, true)
|
|
615
|
-
}
|
|
597
|
+
...(path.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
|
|
598
|
+
{ name: filterTypeName, source: filterTypeSource }
|
|
616
599
|
]
|
|
617
600
|
};
|
|
618
601
|
};
|
|
@@ -637,26 +620,25 @@ const generateRequestBodyType = ({ requestBody }) => {
|
|
|
637
620
|
const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
|
|
638
621
|
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
|
|
639
622
|
|
|
640
|
-
const functionName$4 = 'create';
|
|
641
623
|
const generateCreateEndpoint = ({ target, path, endpoint }) => {
|
|
642
|
-
const
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
signature: interfaceName,
|
|
647
|
-
returns: `_${functionName$4}(cfg, ${generateString(endpoint.path)}, data)`,
|
|
648
|
-
params: ['data']
|
|
649
|
-
});
|
|
650
|
-
const interfaceSource = generateArrowFunctionType({
|
|
651
|
-
type: interfaceName,
|
|
624
|
+
const functionName = 'create';
|
|
625
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
626
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
627
|
+
type: functionTypeName,
|
|
652
628
|
params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`],
|
|
653
629
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
654
630
|
});
|
|
631
|
+
const functionSource = generateArrowFunction({
|
|
632
|
+
name: functionName,
|
|
633
|
+
signature: functionTypeName,
|
|
634
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, data)`,
|
|
635
|
+
params: ['data']
|
|
636
|
+
});
|
|
655
637
|
return {
|
|
656
|
-
entity,
|
|
657
|
-
name: functionName
|
|
658
|
-
type: { name:
|
|
659
|
-
func: { name: functionName
|
|
638
|
+
entity: pascalCase(endpoint.service),
|
|
639
|
+
name: functionName,
|
|
640
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
641
|
+
func: { name: functionName, source: functionSource }
|
|
660
642
|
};
|
|
661
643
|
};
|
|
662
644
|
|
|
@@ -673,31 +655,29 @@ const wrapBody = (type, target) => {
|
|
|
673
655
|
};
|
|
674
656
|
const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
|
|
675
657
|
const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
const entityQuery = `${interfaceName}_Query`;
|
|
658
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
659
|
+
const entityQuery = `${functionTypeName}_Query`;
|
|
679
660
|
const hasId = endpoint.path.includes('{id}');
|
|
680
661
|
const params = createObjectType({
|
|
681
662
|
params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
|
|
682
663
|
body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
|
|
683
664
|
});
|
|
684
665
|
const responseBody = generateResponseBodyType(path);
|
|
685
|
-
const
|
|
666
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
667
|
+
type: functionTypeName,
|
|
668
|
+
params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
|
|
669
|
+
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
|
|
670
|
+
});
|
|
686
671
|
const functionSource = generateArrowFunction({
|
|
687
672
|
name: functionName,
|
|
688
|
-
signature:
|
|
673
|
+
signature: functionTypeName,
|
|
689
674
|
params: hasId ? ['id', 'query'] : ['query'],
|
|
690
|
-
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${
|
|
691
|
-
});
|
|
692
|
-
const interfaceSource = generateArrowFunctionType({
|
|
693
|
-
type: interfaceName,
|
|
694
|
-
params: [...(hasId ? ['id: string'] : []), `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`],
|
|
695
|
-
returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString()}>`
|
|
675
|
+
returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${String(responseBody.toString() === 'binary')})`
|
|
696
676
|
});
|
|
697
677
|
return {
|
|
698
|
-
entity,
|
|
678
|
+
entity: pascalCase(endpoint.service),
|
|
699
679
|
name: functionName,
|
|
700
|
-
type: { name:
|
|
680
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
701
681
|
func: { name: functionName, source: functionSource },
|
|
702
682
|
interfaces: [
|
|
703
683
|
{
|
|
@@ -708,32 +688,38 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
|
|
|
708
688
|
};
|
|
709
689
|
};
|
|
710
690
|
|
|
711
|
-
const functionName$3 = 'remove';
|
|
712
691
|
const generateRemoveEndpoint = ({ target, endpoint }) => {
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
signature: interfaceName,
|
|
718
|
-
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
|
|
719
|
-
params: ['id', 'options?: RemoveQuery']
|
|
720
|
-
});
|
|
721
|
-
const interfaceSource = generateArrowFunctionType({
|
|
722
|
-
type: interfaceName,
|
|
692
|
+
const functionName = 'remove';
|
|
693
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
694
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
695
|
+
type: functionTypeName,
|
|
723
696
|
params: ['id: string', 'options?: RemoveQuery'],
|
|
724
697
|
returns: `${resolveResponseType(target)}<void>`
|
|
725
698
|
});
|
|
699
|
+
const functionSource = generateArrowFunction({
|
|
700
|
+
name: functionName,
|
|
701
|
+
signature: functionTypeName,
|
|
702
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
|
|
703
|
+
params: ['id', 'options?: RemoveQuery']
|
|
704
|
+
});
|
|
726
705
|
return {
|
|
727
|
-
entity,
|
|
728
|
-
name: functionName
|
|
729
|
-
type: { name:
|
|
730
|
-
func: { name: functionName
|
|
706
|
+
entity: pascalCase(endpoint.service),
|
|
707
|
+
name: functionName,
|
|
708
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
709
|
+
func: { name: functionName, source: functionSource }
|
|
731
710
|
};
|
|
732
711
|
};
|
|
733
712
|
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
713
|
+
const excludedParameters = [
|
|
714
|
+
'page',
|
|
715
|
+
'pageSize',
|
|
716
|
+
'sort',
|
|
717
|
+
'serializeNulls',
|
|
718
|
+
'properties',
|
|
719
|
+
'includeReferencedEntities',
|
|
720
|
+
'additionalProperties'
|
|
721
|
+
];
|
|
722
|
+
const resolveAdditionalPropertiesSchema = (path) => {
|
|
737
723
|
const body = resolveBodyType(path);
|
|
738
724
|
if (isResponseObject(body)) {
|
|
739
725
|
const schema = body?.content?.['application/json']?.schema;
|
|
@@ -746,101 +732,136 @@ const resolveAdditionalProperties = (path) => {
|
|
|
746
732
|
}
|
|
747
733
|
return undefined;
|
|
748
734
|
};
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
735
|
+
const resolveReferences = (entity, entities) => {
|
|
736
|
+
const references = [];
|
|
737
|
+
const generatedEntity = entities.get(entity);
|
|
738
|
+
if (generatedEntity) {
|
|
739
|
+
for (const [property, propertyMetaData] of generatedEntity.properties) {
|
|
740
|
+
if (propertyMetaData.service) {
|
|
741
|
+
references.push({
|
|
742
|
+
name: property,
|
|
743
|
+
type: generateString(propertyMetaData.service),
|
|
744
|
+
required: true
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (generatedEntity.parentName) {
|
|
749
|
+
references.push(...resolveReferences(generatedEntity.parentName, entities));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return references;
|
|
753
|
+
};
|
|
754
|
+
const resolveReferencedEntities = (entity, entities) => {
|
|
755
|
+
const referencedEntities = [];
|
|
756
|
+
const generatedEntity = entities.get(entity);
|
|
757
|
+
if (generatedEntity) {
|
|
758
|
+
for (const [, propertyMetaData] of generatedEntity.properties) {
|
|
759
|
+
if (propertyMetaData.entity && propertyMetaData.service) {
|
|
760
|
+
referencedEntities.push({
|
|
761
|
+
name: propertyMetaData.service,
|
|
762
|
+
type: `${pascalCase(propertyMetaData.entity)}[]`,
|
|
763
|
+
required: true
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (generatedEntity.parentName) {
|
|
768
|
+
referencedEntities.push(...resolveReferencedEntities(generatedEntity.parentName, entities));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return referencedEntities;
|
|
772
|
+
};
|
|
773
|
+
const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) => {
|
|
774
|
+
const functionName = 'some';
|
|
775
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
776
|
+
const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
|
|
777
|
+
const parametersTypeName = `${functionTypeName}_Parameters`;
|
|
778
|
+
const parameters = path.parameters?.filter((v) => (isParameterObject(v) ? !excludedParameters.includes(v.name) : false)) ?? [];
|
|
779
|
+
const parametersType = createObjectType({
|
|
780
|
+
params: convertToTypeScriptType(convertParametersToSchema(parameters))
|
|
768
781
|
});
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
782
|
+
const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, true);
|
|
783
|
+
const filterTypeName = `${functionTypeName}_Filter`;
|
|
784
|
+
const filterTypeSource = generateInterfaceType(filterTypeName, [], [entity]);
|
|
785
|
+
const referencesTypeName = `${functionTypeName}_References`;
|
|
786
|
+
const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
|
|
787
|
+
const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
|
|
788
|
+
const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, 'string');
|
|
789
|
+
const queryTypeName = `${functionTypeName}_Query`;
|
|
790
|
+
const queryTypeSource = generateType(queryTypeName, `SomeQuery<${entity}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
|
|
791
|
+
const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
|
|
792
|
+
const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
|
|
793
|
+
const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
|
|
794
|
+
const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(path);
|
|
795
|
+
const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : '{}');
|
|
796
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
797
|
+
type: functionTypeName,
|
|
798
|
+
params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}`],
|
|
799
|
+
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
|
|
780
800
|
});
|
|
781
801
|
const functionSource = generateArrowFunction({
|
|
782
|
-
name: functionName
|
|
783
|
-
signature:
|
|
784
|
-
returns: `_${functionName
|
|
802
|
+
name: functionName,
|
|
803
|
+
signature: functionTypeName,
|
|
804
|
+
returns: `_${functionName}(cfg, ${generateString(endpoint.path)}, query)`,
|
|
785
805
|
params: ['query']
|
|
786
806
|
});
|
|
787
807
|
return {
|
|
788
808
|
entity,
|
|
789
|
-
name: functionName
|
|
790
|
-
type: { name:
|
|
791
|
-
func: { name: functionName
|
|
809
|
+
name: functionName,
|
|
810
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
811
|
+
func: { name: functionName, source: functionSource },
|
|
792
812
|
interfaces: [
|
|
793
|
-
{
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
813
|
+
{ name: parametersTypeName, source: parametersTypeSource },
|
|
814
|
+
{ name: filterTypeName, source: filterTypeSource },
|
|
815
|
+
{ name: referencesTypeName, source: referencesTypeSource },
|
|
816
|
+
{ name: additionalPropertyTypeName, source: additionalPropertyTypeSource },
|
|
817
|
+
{ name: queryTypeName, source: queryTypeSource },
|
|
818
|
+
{ name: referencedEntitiesTypeName, source: referencedEntitiesTypeSource },
|
|
819
|
+
{ name: additionalPropertiesTypeName, source: additionalPropertiesTypeSource }
|
|
797
820
|
]
|
|
798
821
|
};
|
|
799
822
|
};
|
|
800
823
|
|
|
801
|
-
const functionName$1 = 'unique';
|
|
802
824
|
const generateUniqueEndpoint = ({ target, path, endpoint }) => {
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
signature: interfaceName,
|
|
808
|
-
params: ['id', 'query'],
|
|
809
|
-
returns: `_${functionName$1}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
|
|
810
|
-
});
|
|
811
|
-
const interfaceSource = generateArrowFunctionType({
|
|
812
|
-
type: interfaceName,
|
|
825
|
+
const functionName = 'unique';
|
|
826
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
827
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
828
|
+
type: functionTypeName,
|
|
813
829
|
params: ['id: string', 'query?: Q'],
|
|
814
830
|
generics: ['Q extends UniqueQuery'],
|
|
815
831
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
816
832
|
});
|
|
833
|
+
const functionSource = generateArrowFunction({
|
|
834
|
+
name: functionName,
|
|
835
|
+
signature: functionTypeName,
|
|
836
|
+
params: ['id', 'query'],
|
|
837
|
+
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query)`
|
|
838
|
+
});
|
|
817
839
|
return {
|
|
818
|
-
entity,
|
|
819
|
-
name: functionName
|
|
820
|
-
type: { name:
|
|
821
|
-
func: { name: functionName
|
|
840
|
+
entity: pascalCase(endpoint.service),
|
|
841
|
+
name: functionName,
|
|
842
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
843
|
+
func: { name: functionName, source: functionSource }
|
|
822
844
|
};
|
|
823
845
|
};
|
|
824
846
|
|
|
825
|
-
const functionName = 'update';
|
|
826
847
|
const generateUpdateEndpoint = ({ target, path, endpoint }) => {
|
|
827
|
-
const
|
|
828
|
-
const
|
|
829
|
-
const
|
|
830
|
-
type:
|
|
848
|
+
const functionName = 'update';
|
|
849
|
+
const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
|
|
850
|
+
const functionTypeSource = generateArrowFunctionType({
|
|
851
|
+
type: functionTypeName,
|
|
831
852
|
params: ['id: string', `data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'options?: UpdateQuery'],
|
|
832
853
|
returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
|
|
833
854
|
});
|
|
834
855
|
const functionSource = generateArrowFunction({
|
|
835
856
|
name: functionName,
|
|
836
|
-
signature:
|
|
857
|
+
signature: functionTypeName,
|
|
837
858
|
returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, data, options)`,
|
|
838
859
|
params: ['id', 'data', 'options']
|
|
839
860
|
});
|
|
840
861
|
return {
|
|
841
|
-
entity,
|
|
862
|
+
entity: pascalCase(endpoint.service),
|
|
842
863
|
name: functionName,
|
|
843
|
-
type: { name:
|
|
864
|
+
type: { name: functionTypeName, source: functionTypeSource },
|
|
844
865
|
func: { name: functionName, source: functionSource }
|
|
845
866
|
};
|
|
846
867
|
};
|
|
@@ -849,7 +870,7 @@ const isMultiPartUploadPath = (path) => {
|
|
|
849
870
|
const [, entity, ...rest] = path.split('/');
|
|
850
871
|
return entity && rest.length === 2 && rest[1] === 'multipartUpload';
|
|
851
872
|
};
|
|
852
|
-
const
|
|
873
|
+
const parseEndpointsAndGroupByEntity = (paths) => {
|
|
853
874
|
const endpoints = new Map();
|
|
854
875
|
for (const [rawPath, path] of Object.entries(paths)) {
|
|
855
876
|
const endpoint = parseEndpointPath(rawPath);
|
|
@@ -861,11 +882,11 @@ const groupEndpointsByEntity = (paths) => {
|
|
|
861
882
|
logger.errorLn(`Failed to parse ${rawPath}`);
|
|
862
883
|
continue;
|
|
863
884
|
}
|
|
864
|
-
if (endpoints.has(endpoint.
|
|
865
|
-
endpoints.get(endpoint.
|
|
885
|
+
if (endpoints.has(endpoint.service)) {
|
|
886
|
+
endpoints.get(endpoint.service)?.push({ endpoint, path });
|
|
866
887
|
}
|
|
867
888
|
else {
|
|
868
|
-
endpoints.set(endpoint.
|
|
889
|
+
endpoints.set(endpoint.service, [{ endpoint, path }]);
|
|
869
890
|
}
|
|
870
891
|
}
|
|
871
892
|
return endpoints;
|
|
@@ -898,26 +919,34 @@ const generators = {
|
|
|
898
919
|
post: generateGenericEndpoint()
|
|
899
920
|
}
|
|
900
921
|
};
|
|
901
|
-
const generateServices = (
|
|
922
|
+
const generateServices = (paths, entities, aliases, options) => {
|
|
902
923
|
const services = new Map();
|
|
903
|
-
const
|
|
904
|
-
for (const [
|
|
905
|
-
const
|
|
906
|
-
const serviceTypeName = pascalCase(`${
|
|
907
|
-
// Service functions
|
|
924
|
+
const endpoints = parseEndpointsAndGroupByEntity(paths);
|
|
925
|
+
for (const [serviceName, paths] of endpoints) {
|
|
926
|
+
const serviceFnName = camelCase(`${serviceName}Service`);
|
|
927
|
+
const serviceTypeName = pascalCase(`${serviceName}Service`);
|
|
908
928
|
const functions = [];
|
|
909
929
|
for (const { path, endpoint } of paths) {
|
|
910
|
-
const
|
|
930
|
+
const generator = generators[endpoint.type];
|
|
911
931
|
for (const [method, config] of Object.entries(path)) {
|
|
912
932
|
if (method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique) {
|
|
933
|
+
// Skip unique endpoints if generateUnique option is not set
|
|
913
934
|
continue;
|
|
914
935
|
}
|
|
915
|
-
|
|
936
|
+
const generatorFn = generator[method];
|
|
937
|
+
if (generatorFn) {
|
|
916
938
|
const path = config;
|
|
917
939
|
const target = options.target;
|
|
918
940
|
if (!path.deprecated || options.deprecated) {
|
|
919
941
|
functions.push({
|
|
920
|
-
...
|
|
942
|
+
...generatorFn({
|
|
943
|
+
endpoint,
|
|
944
|
+
method,
|
|
945
|
+
target,
|
|
946
|
+
path,
|
|
947
|
+
entities,
|
|
948
|
+
aliases
|
|
949
|
+
}),
|
|
921
950
|
path
|
|
922
951
|
});
|
|
923
952
|
}
|
|
@@ -930,33 +959,28 @@ const generateServices = (doc, aliases, options) => {
|
|
|
930
959
|
if (!functions.length) {
|
|
931
960
|
continue;
|
|
932
961
|
}
|
|
933
|
-
|
|
934
|
-
const types = generateStatements(...functions.flatMap((v) => v.interfaces?.map((v) => v.source) ?? []), ...functions.map((v) => v.type.source), generateInterface(serviceTypeName, [
|
|
962
|
+
const serviceTypes = generateStatements(...functions.flatMap((v) => generateBlockComment(`${serviceTypeName} - ${pascalCase(v.name)}`, generateStatements(...[...(v.interfaces?.map((v) => v.source) ?? []), v.type.source]))), generateBlockComment(`${serviceTypeName}`, generateInterface(serviceTypeName, [
|
|
935
963
|
...functions.map((v) => ({
|
|
936
964
|
required: true,
|
|
937
965
|
comment: v.path.deprecated ? '@deprecated' : undefined,
|
|
938
966
|
name: v.func.name,
|
|
939
967
|
type: v.type.name
|
|
940
968
|
}))
|
|
941
|
-
]));
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const deprecated = functions.every((v) => v.path.deprecated);
|
|
947
|
-
services.set(endpoint, {
|
|
948
|
-
entity: endpoint,
|
|
949
|
-
deprecated,
|
|
950
|
-
serviceName,
|
|
969
|
+
])));
|
|
970
|
+
const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
|
|
971
|
+
services.set(serviceName, {
|
|
972
|
+
name: serviceName,
|
|
973
|
+
serviceFnName,
|
|
951
974
|
serviceTypeName,
|
|
952
|
-
|
|
953
|
-
|
|
975
|
+
functions,
|
|
976
|
+
source: generateStatements(serviceTypes, serviceFn),
|
|
977
|
+
deprecated: functions.every((v) => v.path.deprecated)
|
|
954
978
|
});
|
|
955
979
|
}
|
|
956
980
|
return services;
|
|
957
981
|
};
|
|
958
982
|
|
|
959
|
-
const
|
|
983
|
+
const generateCustomValueServices = (entities, services) => {
|
|
960
984
|
const customValueEntity = entities.get('customValue');
|
|
961
985
|
const customValueEntities = [];
|
|
962
986
|
if (!customValueEntity) {
|
|
@@ -977,32 +1001,32 @@ const generateCustomValueUtilities = (entities, services) => {
|
|
|
977
1001
|
continue serviceLoop;
|
|
978
1002
|
}
|
|
979
1003
|
}
|
|
980
|
-
customValueEntities.push(service.
|
|
1004
|
+
customValueEntities.push(service.name);
|
|
981
1005
|
}
|
|
982
|
-
return
|
|
1006
|
+
return generateStatements(generateType('WCustomValueService', concat(generateStrings(customValueEntities), ' | ')), `export const wCustomValueServiceNames: WCustomValueService[] = [${concat(generateStrings(customValueEntities))}];`, `export const isWCustomValueService = (service: string | undefined): service is WCustomValueService =>\n${indent('wCustomValueServiceNames.includes(service as WCustomValueService);')}`);
|
|
983
1007
|
};
|
|
984
1008
|
|
|
985
1009
|
const generateObject = (properties) => {
|
|
986
1010
|
const body = [];
|
|
987
|
-
for (const { key, value } of properties) {
|
|
1011
|
+
for (const { key, value, comment } of properties) {
|
|
988
1012
|
if (value === undefined) {
|
|
989
1013
|
continue;
|
|
990
1014
|
}
|
|
991
1015
|
if (Array.isArray(value)) {
|
|
992
1016
|
const str = generateObject(value);
|
|
993
1017
|
if (str.length > 2) {
|
|
994
|
-
body.push(`${key}: ${str}`);
|
|
1018
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${str}`);
|
|
995
1019
|
}
|
|
996
1020
|
}
|
|
997
1021
|
else {
|
|
998
|
-
body.push(`${key}: ${String(value)}`);
|
|
1022
|
+
body.push(`${comment ? generateInlineComment(comment) + '\n' : ''}${key}: ${String(value)}`);
|
|
999
1023
|
}
|
|
1000
1024
|
}
|
|
1001
1025
|
return body.length ? `{\n${indent(body.join(',\n'))}\n}` : `{}`;
|
|
1002
1026
|
};
|
|
1003
1027
|
|
|
1004
1028
|
const resolveInheritedEntities = (root, entities) => {
|
|
1005
|
-
const parent = root.
|
|
1029
|
+
const parent = root.parentName ? entities.get(root.parentName) : undefined;
|
|
1006
1030
|
return parent ? [parent, ...resolveInheritedEntities(parent, entities)] : [];
|
|
1007
1031
|
};
|
|
1008
1032
|
const generatePropertyDescriptors = (entity, entities, services, options) => [...resolveInheritedEntities(entity, entities).flatMap((v) => [...v.properties]), ...entity.properties]
|
|
@@ -1012,7 +1036,7 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [..
|
|
|
1012
1036
|
return true;
|
|
1013
1037
|
}
|
|
1014
1038
|
// Check if corresponding service is deprecated and can be removed
|
|
1015
|
-
const service = services.find((v) => v.
|
|
1039
|
+
const service = services.find((v) => v.name === meta.service);
|
|
1016
1040
|
return !meta.service || (service && !service.deprecated);
|
|
1017
1041
|
})
|
|
1018
1042
|
.map(([property, meta]) => ({
|
|
@@ -1022,9 +1046,12 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [..
|
|
|
1022
1046
|
value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
|
|
1023
1047
|
}))
|
|
1024
1048
|
}));
|
|
1025
|
-
const
|
|
1049
|
+
const generateEntityProperties = (entities, aliases, services, options) => {
|
|
1026
1050
|
const typeName = 'WEntityProperties';
|
|
1027
|
-
const propertyMap = [
|
|
1051
|
+
const propertyMap = [
|
|
1052
|
+
...entities.entries(),
|
|
1053
|
+
...[...aliases.entries()].map(([service, type]) => [service, entities.get(camelCase(type))])
|
|
1054
|
+
].map(([entity, data]) => ({
|
|
1028
1055
|
key: entity,
|
|
1029
1056
|
value: generatePropertyDescriptors(data, entities, services, options)
|
|
1030
1057
|
}));
|
|
@@ -1045,17 +1072,17 @@ const FILTER_REGEX = /^(some|count|create|remove|unique|update)$/;
|
|
|
1045
1072
|
*/
|
|
1046
1073
|
const generateGroupedServices = (services) => {
|
|
1047
1074
|
const entityDescriptors = new Map();
|
|
1048
|
-
for (const
|
|
1049
|
-
for (const
|
|
1050
|
-
if (!FILTER_REGEX.test(name)) {
|
|
1075
|
+
for (const service of services) {
|
|
1076
|
+
for (const fn of service.functions) {
|
|
1077
|
+
if (!FILTER_REGEX.test(fn.name)) {
|
|
1051
1078
|
continue;
|
|
1052
1079
|
}
|
|
1053
|
-
entityDescriptors.set(name, [
|
|
1054
|
-
...(entityDescriptors.get(name) ?? []),
|
|
1080
|
+
entityDescriptors.set(fn.name, [
|
|
1081
|
+
...(entityDescriptors.get(fn.name) ?? []),
|
|
1055
1082
|
{
|
|
1056
|
-
name:
|
|
1083
|
+
name: service.name,
|
|
1057
1084
|
required: true,
|
|
1058
|
-
type: `${pascalCase(
|
|
1085
|
+
type: `${pascalCase(service.name)}Service_${pascalCase(fn.name)}`
|
|
1059
1086
|
}
|
|
1060
1087
|
]);
|
|
1061
1088
|
}
|
|
@@ -1068,131 +1095,86 @@ const generateGroupedServices = (services) => {
|
|
|
1068
1095
|
const guard = `(service: string | undefined): service is ${service} =>\n${indent(`${constant}.includes(service as ${service});`)}`;
|
|
1069
1096
|
typeGuards.push(`export const is${service} = ${guard}`);
|
|
1070
1097
|
}
|
|
1071
|
-
return [
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const value = generateArray(props.map((v) => v.name));
|
|
1078
|
-
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1079
|
-
}),
|
|
1080
|
-
generateBlockComment('Type guards for service classes.', generateStatements(...typeGuards))
|
|
1081
|
-
];
|
|
1098
|
+
return generateStatements(...descriptors.map(([name, props]) => generateInterface(pascalCase(`WServicesWith_${name}`), props)), ...descriptors.map(([name]) => generateType(pascalCase(`WServiceWith_${name}`), `keyof ${pascalCase(`WServicesWith_${name}`)}`)), ...descriptors.map(([name, props]) => {
|
|
1099
|
+
const constant = camelCase(`wServiceWith_${name}_Names`);
|
|
1100
|
+
const type = pascalCase(`WServiceWith_${name}`);
|
|
1101
|
+
const value = generateArray(props.map((v) => v.name));
|
|
1102
|
+
return `export const ${constant}: ${type}[] = ${value};`;
|
|
1103
|
+
}), ...typeGuards);
|
|
1082
1104
|
};
|
|
1083
1105
|
|
|
1084
|
-
const
|
|
1085
|
-
const
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
name: entity,
|
|
1099
|
-
type: pascalCase(entity),
|
|
1100
|
-
required: true
|
|
1101
|
-
})),
|
|
1102
|
-
...services.map((service) => {
|
|
1103
|
-
const alias = aliases.get(service.entity);
|
|
1104
|
-
return {
|
|
1105
|
-
name: service.entity,
|
|
1106
|
-
type: alias ?? 'never',
|
|
1107
|
-
required: true,
|
|
1108
|
-
comment: alias ? undefined : 'no response defined or inlined'
|
|
1109
|
-
};
|
|
1110
|
-
})
|
|
1111
|
-
];
|
|
1112
|
-
const createMappingType = (type, prefix) => (type !== 'never' ? `${type}_${prefix}` : type);
|
|
1113
|
-
const entitiesList = generateInterface('WEntities', entityInterfaces);
|
|
1114
|
-
const entityReferences = generateInterface('WEntityReferences', entityInterfaces.map((v) => ({
|
|
1115
|
-
...v,
|
|
1116
|
-
type: createMappingType(v.type, 'References')
|
|
1117
|
-
})));
|
|
1118
|
-
const entityMappings = generateInterface('WEntityMappings', entityInterfaces.map((v) => ({
|
|
1119
|
-
...v,
|
|
1120
|
-
type: createMappingType(v.type, 'Mappings')
|
|
1121
|
-
})));
|
|
1122
|
-
const entityFilter = generateInterface('WEntityFilters', entityInterfaces.map((v) => ({
|
|
1123
|
-
...v,
|
|
1124
|
-
type: createMappingType(v.type, 'Filter')
|
|
1125
|
-
})));
|
|
1106
|
+
const generateMaps = (enums, entities, services, aliases, options) => {
|
|
1107
|
+
const enumInstances = `export const wEnums = ${generateObject([...enums.keys()].map((v) => ({ key: v, value: v })))};`;
|
|
1108
|
+
const entityNames = `export const wEntityNames: WEntity[] = ${generateArray([...entities.keys()])};`;
|
|
1109
|
+
const generatedServices = [...services.values()];
|
|
1110
|
+
const serviceInstances = `export const wServices = ${generateObject(generatedServices.map((v) => ({
|
|
1111
|
+
key: v.name,
|
|
1112
|
+
value: `${v.serviceFnName}()`,
|
|
1113
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1114
|
+
})))};`;
|
|
1115
|
+
const serviceFactories = `export const wServiceFactories = ${generateObject(generatedServices.map((v) => ({
|
|
1116
|
+
key: v.name,
|
|
1117
|
+
value: v.serviceFnName,
|
|
1118
|
+
comment: v.deprecated ? '@deprecated' : undefined
|
|
1119
|
+
})))};`;
|
|
1126
1120
|
return {
|
|
1127
1121
|
source: generateStatements(
|
|
1128
|
-
/*
|
|
1129
|
-
|
|
1130
|
-
/*
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
Where [serviceName] is the name of an endpoint (e.g. for /article its 'article')
|
|
1143
|
-
|
|
1144
|
-
Where [entityInterfaceName] is
|
|
1145
|
-
- the underlying type for this entity
|
|
1146
|
-
- the type for what is returned by the api
|
|
1147
|
-
`, entitiesList),
|
|
1148
|
-
/* type-ofs and types */
|
|
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'),
|
|
1150
|
-
/* Utilities. */
|
|
1151
|
-
generateCustomValueUtilities(entities, services),
|
|
1152
|
-
/* All functions grouped by service supporting it */
|
|
1153
|
-
...generateGroupedServices(services))
|
|
1122
|
+
/* Enums */
|
|
1123
|
+
generateInterface('WEnums', [...enums.keys()].map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
|
|
1124
|
+
/* Entities */
|
|
1125
|
+
generateInterface('WEntities', [
|
|
1126
|
+
...[...entities.keys()].map((name) => ({ name, type: loosePascalCase(name), required: true })),
|
|
1127
|
+
...[...aliases.entries()].map(([name, type]) => ({ name, type, required: true }))
|
|
1128
|
+
].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
|
|
1129
|
+
/* Services */
|
|
1130
|
+
serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
|
|
1131
|
+
/* Service Utils */
|
|
1132
|
+
generateGroupedServices(generatedServices), generateCustomValueServices(entities, generatedServices),
|
|
1133
|
+
/* Entity Properties (Runtime Meta Infos) */
|
|
1134
|
+
generateEntityProperties(entities, aliases, generatedServices, options))
|
|
1154
1135
|
};
|
|
1155
1136
|
};
|
|
1156
1137
|
|
|
1157
|
-
const
|
|
1158
|
-
const
|
|
1159
|
-
const schemas = new Map();
|
|
1138
|
+
const parseReferencedEntityType = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
|
|
1139
|
+
const extractServiceAliases = (doc, schemas) => {
|
|
1160
1140
|
const aliases = new Map();
|
|
1161
|
-
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
|
|
1162
|
-
if (!isReferenceObject(schema)) {
|
|
1163
|
-
schemas.set(name, schema);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Referenced schemas in responses, in some case the response from the root endpoint
|
|
1168
|
-
* refers to a schema with a different name
|
|
1169
|
-
*/
|
|
1170
1141
|
for (const [path, methods] of Object.entries(doc.paths)) {
|
|
1171
1142
|
const parsed = parseEndpointPath(path);
|
|
1172
|
-
if (!parsed || schemas.has(parsed.
|
|
1143
|
+
if (!parsed || !methods || parsed.type !== WeclappEndpointType.ROOT || schemas.has(parsed.service)) {
|
|
1173
1144
|
continue;
|
|
1174
1145
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1146
|
+
const body = methods[OpenAPIV3.HttpMethods.GET]?.responses['200'];
|
|
1147
|
+
if (isResponseObject(body) && body.content?.['application/json']) {
|
|
1148
|
+
const responseSchema = body.content['application/json'].schema;
|
|
1149
|
+
if (!responseSchema || isReferenceObject(responseSchema)) {
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
const resultSchema = responseSchema.properties?.result;
|
|
1153
|
+
if (!resultSchema) {
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (isReferenceObject(resultSchema)) {
|
|
1157
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultSchema));
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
if (isArraySchemaObject(resultSchema)) {
|
|
1161
|
+
const resultItemSchema = resultSchema.items;
|
|
1162
|
+
if (isReferenceObject(resultItemSchema)) {
|
|
1163
|
+
aliases.set(parsed.service, parseReferencedEntityType(resultItemSchema));
|
|
1192
1164
|
}
|
|
1193
1165
|
}
|
|
1194
1166
|
}
|
|
1195
1167
|
}
|
|
1168
|
+
return aliases;
|
|
1169
|
+
};
|
|
1170
|
+
const extractSchemas = (doc) => {
|
|
1171
|
+
const schemas = new Map();
|
|
1172
|
+
for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
|
|
1173
|
+
if (!isReferenceObject(schema)) {
|
|
1174
|
+
schemas.set(name, schema);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const aliases = extractServiceAliases(doc, schemas);
|
|
1196
1178
|
return { schemas, aliases };
|
|
1197
1179
|
};
|
|
1198
1180
|
|
|
@@ -1200,14 +1182,9 @@ const generate = (doc, options) => {
|
|
|
1200
1182
|
const { schemas, aliases } = extractSchemas(doc);
|
|
1201
1183
|
const enums = generateEnums(schemas);
|
|
1202
1184
|
const entities = generateEntities(schemas, enums);
|
|
1203
|
-
const services = generateServices(doc, aliases, options);
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
enums: [...enums.keys()],
|
|
1207
|
-
options,
|
|
1208
|
-
entities,
|
|
1209
|
-
aliases
|
|
1210
|
-
}).source));
|
|
1185
|
+
const services = generateServices(doc.paths, entities, aliases, options);
|
|
1186
|
+
const maps = generateMaps(enums, entities, services, aliases, options);
|
|
1187
|
+
return generateStatements(generateBase(options.target, doc.info.version, options), 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', generateStatements(maps.source)));
|
|
1211
1188
|
};
|
|
1212
1189
|
|
|
1213
1190
|
const hash = (content, algorithm = 'sha256') => {
|
|
@@ -1229,6 +1206,7 @@ const cli = async () => {
|
|
|
1229
1206
|
.usage('Usage: $0 <source> [flags]')
|
|
1230
1207
|
.version(version)
|
|
1231
1208
|
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1209
|
+
.example('$0 openapi.json', 'Generate the SDK based on a local openapi file')
|
|
1232
1210
|
.example('$0 xxx.weclapp.com --key ...', 'Generate the SDK based on the openapi file from the given weclapp instance')
|
|
1233
1211
|
.help('h')
|
|
1234
1212
|
.alias('v', 'version')
|