@weclapp/sdk 2.0.0-dev.0 → 2.0.0-dev.11
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 +57 -19
- package/package.json +13 -13
package/dist/cli.js
CHANGED
|
@@ -144,9 +144,13 @@ const generateStatements = (...statements) => statements
|
|
|
144
144
|
.join('\n\n');
|
|
145
145
|
const generateBlockStatements = (...statements) => `{\n${indent(generateStatements(...statements))}\n}`;
|
|
146
146
|
|
|
147
|
-
var
|
|
147
|
+
var globalConfig = "export type RequestPayloadMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';\n\nexport interface RequestPayload {\n method?: RequestPayloadMethod;\n query?: Record<string, any>;\n body?: any;\n unwrap?: boolean;\n forceBlob?: boolean;\n}\n\nexport interface ServiceConfig {\n\n // Your API-Key, this is optional in the sense of if you omit this, and you're in a browser, the\n // cookie-authentication (include-credentials) will be used.\n key?: string;\n\n // Your domain, if omitted location.host will be used.\n host?: string;\n\n // If you want to use https, defaults to true.\n secure?: boolean;\n\n // If you want that some and count requests are bundled into multi requests.\n multiRequest?: boolean;\n\n // Optional request/response interceptors.\n interceptors?: {\n\n // Takes the generated request, you can either return a new request,\n // a response (which will be taken as \"the\" response) or nothing.\n // The payload contains the raw input generated by the SDK.\n request?: (request: Request, payload: RequestPayload) => Request | Response | void | Promise<Request | Response | void>;\n\n // Takes the response. This can either be the one from the server or an\n // artificially-crafted one by the request interceptor.\n response?: (response: Response) => Response | void | Promise<Response | void>;\n };\n}\n\ntype ServiceConfigWithoutMultiRequest = Omit<ServiceConfig, 'multiRequest'>;\n\nlet globalConfig: ServiceConfig | undefined;\nexport const getGlobalConfig = (): ServiceConfig | undefined => globalConfig;\nexport const setGlobalConfig = (cfg?: ServiceConfig) => globalConfig = cfg;\n\nexport const getHost = (cfg: ServiceConfig) => {\n let host = cfg.host?.replace(/^https?:\\/\\//, '');\n if (!host && typeof location !== 'undefined') {\n host = location.host;\n }\n\n if (!host) {\n throw new Error('Please specify a domain');\n }\n\n return host;\n};\n\nexport const getProtocol = (cfg: ServiceConfig) => (cfg.secure ?? true) ? 'https' : 'http';";
|
|
148
148
|
|
|
149
|
-
var
|
|
149
|
+
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.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.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}));";
|
|
150
|
+
|
|
151
|
+
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, ...cfg,\n interceptors: {...globalConfig?.interceptors, ...cfg?.interceptors}\n };\n\n const isBinaryData = payload.body instanceof resolveBinaryObject();\n const params = new URLSearchParams(Object.entries(payload.query ?? {}).filter(v => v[1] !== undefined));\n 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/v1${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 = (!payload.forceBlob || !res.ok) && res.headers?.get('content-type')?.includes('application/json') ?\n await res.json() : 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 _count = (\n cfg: ServiceConfig | undefined,\n endpoint: string,\n query?: CountQuery<any> & {params?: Record<any, any>}\n) => wrapResponse(() => raw(cfg, endpoint, {\n unwrap: true,\n query: {\n ...flattenFilter(query?.filter),\n ...flattenOrFilter(query?.or),\n ...query?.params\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) => wrapResponse(() => 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 _remove = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n {dryRun = false}: RemoveQuery = {}\n) => wrapResponse(() => raw({...cfg, multiRequest: false}, endpoint, {\n method: 'DELETE',\n query: {dryRun}\n}).then(() => undefined));\n\nconst _create = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n data: any\n) => wrapResponse(() => raw({...cfg, multiRequest: false}, endpoint, {\n method: 'POST',\n body: data\n}));\n\nconst _update = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n data: any,\n {ignoreMissingProperties = true, dryRun = false}: UpdateQuery = {}\n) => wrapResponse(() => raw({...cfg, multiRequest: false}, endpoint, {\n method: 'PUT',\n body: data,\n query: {ignoreMissingProperties, dryRun}\n}));\n\nconst _unique = (\n cfg: ServiceConfigWithoutMultiRequest | undefined,\n endpoint: string,\n 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) => wrapResponse(() => raw({...cfg, multiRequest: false}, endpoint, {\n method,\n forceBlob,\n body: payload?.body,\n query: payload?.params\n}));\n";
|
|
152
|
+
|
|
153
|
+
var types = "export type DeepPartial<T> = T extends object ? {\n [P in keyof T]?: DeepPartial<T[P]>;\n} : T;\n\n// Filter properties\nexport type EqualityOperators = 'EQ' | 'NE';\nexport type ComparisonOperators = 'LT' | 'GT' | 'LE' | 'GE' | 'LIKE' | 'ILIKE' | 'NOT_LIKE' | 'NOT_ILIKE';\nexport type ArrayOperators = 'IN' | 'NOT_IN';\nexport type Operator = EqualityOperators | ComparisonOperators | ArrayOperators;\n\nexport type MapOperators<T> = { [K in EqualityOperators]?: T | null; } &\n { [K in ComparisonOperators]?: T; } &\n { [K in ArrayOperators]?: T[]; };\n\nexport type QueryFilter<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ?\n U extends Record<any, any> ? QueryFilter<U> : MapOperators<U> :\n T[P] extends Record<any, any> | undefined ? QueryFilter<T[P]> : MapOperators<T[P]>;\n};\n\nexport type Sort<T> = {\n [K in keyof T]?: {\n [V in keyof T]?:\n V extends K ?\n T[V] extends Array<infer U> | undefined ?\n U extends object ?\n Sort<U> : never :\n T[V] extends object | undefined ?\n Sort<T[V]> : 'asc' | 'desc' : never;\n };\n}[keyof T];\n\n// Select properties\nexport type CustomAttributeFilter = {\n [K in number]: string | number | boolean |\n {id: string;} |\n {entityName: string; entityId: string;};\n}\n\nexport type QuerySelect<T> = {\n [P in keyof T]?:\n T[P] extends Array<infer U> | undefined ? (QuerySelect<U> | boolean) :\n T[P] extends Record<any, any> | undefined ? (QuerySelect<T[P]> | boolean) : boolean;\n}\n\nexport type Select<T, Q extends (QuerySelect<T> | undefined)> = 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]:\n\n // Property\n Q[P] extends true ? T[P] :\n\n // Array\n T[P] extends Array<infer U> ? Select<U, Q[P] & QuerySelect<any>>[] :\n\n // Object\n T[P] extends Record<any, any> ? Select<T[P], Q[P] & QuerySelect<any>> : never\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\n// Endpoint configurations\nexport type CountQuery<F> = {\n filter?: QueryFilter<F>;\n or?: (QueryFilter<F> & CustomAttributeFilter)[];\n};\n\nexport type Pagination = {\n page: number;\n pageSize: number;\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\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 { type: 'string'; format?: 'decimal' | 'html' | 'email' | 'password'; maxLength?: number; pattern?: string; entity?: WEntity; service?: WService; } |\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\n// Utils\nconst equality: string[] = ['EQ', 'NE'];\nconst simple: string[] = [...equality, 'LT', 'GT', 'LE', 'GE', 'LIKE', 'NOT_LIKE', 'ILIKE', 'NOT_ILIKE'];\nconst array: string[] = ['IN', 'NOT_IN'];\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};\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)) {\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 if (array.includes(filter)) {\n entries.push([\n `${prop}-${filterMap[filter as Operator]}`,\n `[${value.map((v: string | number) => typeof v === 'string' ? `\"${v}\"` : v)}]`\n ]);\n } else {\n entries.push(\n ...flatten(propValue as QueryFilter<any>)\n .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][] = [], 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(\n ...flatten(obj[i])\n .map(v => [`or${i || ''}-${v[0]}`, v[1]]) as [string, string][]\n );\n }\n\n return Object.fromEntries(entries);\n};\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";
|
|
150
154
|
|
|
151
155
|
const resolveImports = (target) => {
|
|
152
156
|
const imports = [];
|
|
@@ -158,7 +162,7 @@ const resolveImports = (target) => {
|
|
|
158
162
|
const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
|
|
159
163
|
const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
|
|
160
164
|
const generateBase = (target) => {
|
|
161
|
-
return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), types, root);
|
|
165
|
+
return generateStatements(resolveImports(target), resolveMappings(target), resolveBinaryClass(target), globalConfig, multiRequest, types, root);
|
|
162
166
|
};
|
|
163
167
|
|
|
164
168
|
const transformKey = (s) => snakeCase(s).toUpperCase();
|
|
@@ -238,7 +242,7 @@ const createRawType = (value) => ({
|
|
|
238
242
|
});
|
|
239
243
|
const createArrayType = (value) => ({
|
|
240
244
|
type: 'array',
|
|
241
|
-
toString: () =>
|
|
245
|
+
toString: () => `(${value.toString()})[]`
|
|
242
246
|
});
|
|
243
247
|
const createTupleType = (value) => ({
|
|
244
248
|
type: 'tuple',
|
|
@@ -314,8 +318,10 @@ const extractPropertyMetaData = (enums, meta, prop) => {
|
|
|
314
318
|
result.type = 'reference';
|
|
315
319
|
return result;
|
|
316
320
|
}
|
|
317
|
-
result.format = prop.format;
|
|
318
321
|
result.type = prop.type;
|
|
322
|
+
result.format = prop.format;
|
|
323
|
+
result.maxLength = prop.maxLength;
|
|
324
|
+
result.pattern = prop.pattern;
|
|
319
325
|
if (isArraySchemaObject(prop)) {
|
|
320
326
|
if (isReferenceObject(prop.items)) {
|
|
321
327
|
setEntityEnumProperty(enums, prop.items, result);
|
|
@@ -384,11 +390,19 @@ const generateEntities = (schemas, enums) => {
|
|
|
384
390
|
const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
|
|
385
391
|
if (meta.entity) {
|
|
386
392
|
const type = `${pascalCase(meta.entity)}[]`;
|
|
387
|
-
|
|
388
|
-
|
|
393
|
+
if (schemas.has(meta.entity)) {
|
|
394
|
+
referenceInterface.push({ name, type, required: true });
|
|
395
|
+
filterInterface.push({ name: meta.entity, type, required: true });
|
|
396
|
+
}
|
|
389
397
|
}
|
|
390
398
|
if (meta.service) {
|
|
391
|
-
|
|
399
|
+
if (schemas.has(meta.service)) {
|
|
400
|
+
referenceMappingsInterface.push({
|
|
401
|
+
name,
|
|
402
|
+
type: generateString(meta.service),
|
|
403
|
+
required: true,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
392
406
|
}
|
|
393
407
|
const type = convertToTypeScriptType(property, name).toString();
|
|
394
408
|
const comment = isNonArraySchemaObject(property) ?
|
|
@@ -610,10 +624,9 @@ const generateRequestBodyType = ({ requestBody }) => {
|
|
|
610
624
|
return generateBodyType(requestBody) ?? createRawType('unknown');
|
|
611
625
|
};
|
|
612
626
|
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
};
|
|
627
|
+
const resolveBodyType = ({ responses }) => Object.entries(responses)
|
|
628
|
+
.filter(v => v[0].startsWith('2'))[0]?.[1];
|
|
629
|
+
const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
|
|
617
630
|
|
|
618
631
|
const functionName$4 = 'create';
|
|
619
632
|
const generateCreateEndpoint = ({ target, path, endpoint }) => {
|
|
@@ -699,12 +712,12 @@ const generateRemoveEndpoint = ({ target, endpoint }) => {
|
|
|
699
712
|
const functionSource = generateArrowFunction({
|
|
700
713
|
name: functionName$3,
|
|
701
714
|
signature: interfaceName,
|
|
702
|
-
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}
|
|
703
|
-
params: ['id']
|
|
715
|
+
returns: `_${functionName$3}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, options)`,
|
|
716
|
+
params: ['id', 'options?: RemoveQuery']
|
|
704
717
|
});
|
|
705
718
|
const interfaceSource = generateArrowFunctionType({
|
|
706
719
|
type: interfaceName,
|
|
707
|
-
params: ['id: string'],
|
|
720
|
+
params: ['id: string', 'options?: RemoveQuery'],
|
|
708
721
|
returns: `${resolveResponseType(target)}<void>`
|
|
709
722
|
});
|
|
710
723
|
return {
|
|
@@ -720,6 +733,19 @@ const excludedParameters = [
|
|
|
720
733
|
'page', 'pageSize', 'sort',
|
|
721
734
|
'serializeNulls', 'properties', 'includeReferencedEntities'
|
|
722
735
|
];
|
|
736
|
+
const resolveAdditionalProperties = (path) => {
|
|
737
|
+
const body = resolveBodyType(path);
|
|
738
|
+
if (isResponseObject(body)) {
|
|
739
|
+
const schema = body?.content?.['application/json']?.schema;
|
|
740
|
+
if (isObjectSchemaObject(schema)) {
|
|
741
|
+
const obj = schema?.properties?.additionalProperties;
|
|
742
|
+
if (isObjectSchemaObject(obj)) {
|
|
743
|
+
return obj;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return undefined;
|
|
748
|
+
};
|
|
723
749
|
const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
|
|
724
750
|
// Required interface names
|
|
725
751
|
const service = pascalCase(endpoint.entity);
|
|
@@ -730,20 +756,24 @@ const generateSomeEndpoint = ({ aliases, target, path, endpoint }) => {
|
|
|
730
756
|
const entityReferences = `${entity}_References`;
|
|
731
757
|
const entityParameters = `${service}_Parameters`;
|
|
732
758
|
const parameterSchema = convertParametersToSchema(path.parameters);
|
|
733
|
-
|
|
759
|
+
const additionalProperties = resolveAdditionalProperties(path);
|
|
760
|
+
const additionalPropertyNames = generateStrings(Object.keys(additionalProperties?.properties ?? {}));
|
|
761
|
+
const additionalPropertyNamesType = additionalPropertyNames.length ? `(${concat(additionalPropertyNames, ' | ')})[]` : '[]';
|
|
762
|
+
// We already cover some properties
|
|
734
763
|
parameterSchema.properties = Object.fromEntries(Object.entries(parameterSchema.properties ?? {})
|
|
735
764
|
.filter(v => !excludedParameters.includes(v[0])));
|
|
736
765
|
const parameters = createObjectType({
|
|
737
766
|
params: convertToTypeScriptType(parameterSchema)
|
|
738
767
|
});
|
|
768
|
+
const properties = additionalProperties ? convertToTypeScriptType(additionalProperties).toString() : '{}';
|
|
739
769
|
const interfaceSource = generateArrowFunctionType({
|
|
740
770
|
type: interfaceName,
|
|
741
771
|
generics: [
|
|
742
772
|
`S extends (QuerySelect<${entity}> | undefined) = undefined`,
|
|
743
773
|
`I extends (QuerySelect<${entityMappings}> | undefined) = undefined`
|
|
744
774
|
],
|
|
745
|
-
params: [`query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S> & ${entityParameters}`],
|
|
746
|
-
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S>>`
|
|
775
|
+
params: [`query${parameters.isFullyOptional() ? '?' : ''}: SomeQuery<${entity}, ${entityFilter}, I, S, ${additionalPropertyNamesType}> & ${entityParameters}`],
|
|
776
|
+
returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${entityReferences}, ${entityMappings}, I, S, ${properties}>>`
|
|
747
777
|
});
|
|
748
778
|
const functionSource = generateArrowFunction({
|
|
749
779
|
name: functionName$2,
|
|
@@ -812,11 +842,19 @@ const generateUpdateEndpoint = ({ target, path, endpoint }) => {
|
|
|
812
842
|
};
|
|
813
843
|
};
|
|
814
844
|
|
|
845
|
+
const isMultiPartUploadPath = (path) => {
|
|
846
|
+
const [, entity, ...rest] = path.split('/');
|
|
847
|
+
return entity && rest.length === 2 && rest[1] === 'multiPartUpload';
|
|
848
|
+
};
|
|
815
849
|
const groupEndpointsByEntity = (paths) => {
|
|
816
850
|
const endpoints = new Map();
|
|
817
851
|
for (const [rawPath, path] of Object.entries(paths)) {
|
|
818
852
|
const endpoint = parseEndpointPath(rawPath);
|
|
819
853
|
if (!endpoint || !path) {
|
|
854
|
+
// Todo: Should be removed if sdk supports multi part upload.
|
|
855
|
+
if (isMultiPartUploadPath(rawPath)) {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
820
858
|
logger.errorLn(`Failed to parse ${rawPath}`);
|
|
821
859
|
continue;
|
|
822
860
|
}
|
|
@@ -972,7 +1010,7 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [
|
|
|
972
1010
|
key: property,
|
|
973
1011
|
value: Object.entries(meta).map(([key, value]) => ({
|
|
974
1012
|
key,
|
|
975
|
-
value: value ? generateString(value) : undefined
|
|
1013
|
+
value: value !== undefined ? typeof value === 'number' ? value : generateString(value) : undefined
|
|
976
1014
|
}))
|
|
977
1015
|
}));
|
|
978
1016
|
const generateEntityPropertyMap = (entities, services, options) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weclapp/sdk",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.11",
|
|
4
4
|
"description": "SDK generator based on a weclapp api swagger file",
|
|
5
5
|
"author": "weclapp",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"tsconfig.lib.json"
|
|
22
22
|
],
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": "^
|
|
25
|
-
"npm": "^
|
|
24
|
+
"node": "^20",
|
|
25
|
+
"npm": "^10"
|
|
26
26
|
},
|
|
27
27
|
"types": "./sdk/dist/index.d.ts",
|
|
28
28
|
"main": "./sdk/dist/index.cjs",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"cli:build": "cross-env NODE_ENV=production rollup -c rollup.config.js",
|
|
40
40
|
"cli:watch": "cross-env NODE_ENV=development rollup -c rollup.config.js --watch",
|
|
41
41
|
"sdk:build": "./bin/cli.js test/openapi.json --target node",
|
|
42
|
-
"lint": "eslint
|
|
42
|
+
"lint": "eslint src/**/*.ts",
|
|
43
43
|
"lint:fix": "npm run lint -- --fix",
|
|
44
44
|
"ci:test": "npm run lint:fix && npm run cli:build && npm run sdk:build",
|
|
45
45
|
"release": "standard-version"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
49
|
-
"@typescript-eslint/parser": "^
|
|
50
|
-
"eslint": "^8.
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
|
49
|
+
"@typescript-eslint/parser": "^6.2.1",
|
|
50
|
+
"eslint": "^8.46.0",
|
|
51
51
|
"rollup-plugin-string": "^3.0.0",
|
|
52
52
|
"standard-version": "^9.5.0"
|
|
53
53
|
},
|
|
@@ -56,19 +56,19 @@
|
|
|
56
56
|
"@rollup/plugin-terser": "^0.4.3",
|
|
57
57
|
"@types/fs-extra": "^11.0.1",
|
|
58
58
|
"@types/yargs": "^17.0.24",
|
|
59
|
-
"chalk": "^5.
|
|
59
|
+
"chalk": "^5.3.0",
|
|
60
60
|
"change-case": "^4.1.2",
|
|
61
61
|
"cross-env": "^7.0.3",
|
|
62
|
-
"dotenv": "^16.1
|
|
62
|
+
"dotenv": "^16.3.1",
|
|
63
63
|
"indent-string": "^5.0.0",
|
|
64
64
|
"openapi-types": "^12.1.3",
|
|
65
65
|
"pretty-ms": "^8.0.0",
|
|
66
|
-
"rollup": "^3.
|
|
67
|
-
"rollup-plugin-ts": "^3.
|
|
68
|
-
"typescript": "^5.1.
|
|
66
|
+
"rollup": "^3.27.2",
|
|
67
|
+
"rollup-plugin-ts": "^3.4.3",
|
|
68
|
+
"typescript": "^5.1.6",
|
|
69
69
|
"yargs": "^17.7.2"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"rxjs": "^7.
|
|
72
|
+
"rxjs": "^7.8.0"
|
|
73
73
|
}
|
|
74
74
|
}
|