From 799effd5011d601803e9f898aff1dbfecf9ba059 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Thu, 28 Oct 2021 22:55:43 +0800 Subject: [PATCH 01/50] update --- __test__/3.0/api-auth.ts | 333 ------------------------------------- swagger2ts.ts | 19 +-- test.ts | 116 +++++++++---- v3/generateDefinition.ts | 311 ---------------------------------- v3/generator/api.ts | 114 +++++++++++++ v3/generator/definition.ts | 223 +++++++++++++++++++++++++ v3/generator/generator.ts | 205 +++++++++++++++++++++++ v3/generator/index.ts | 8 + v3/helper.ts | 37 +---- v3/helper_test.ts | 72 -------- v3/index.ts | 2 +- v3/types.ts | 4 +- 12 files changed, 645 insertions(+), 799 deletions(-) delete mode 100644 v3/generateDefinition.ts create mode 100644 v3/generator/api.ts create mode 100644 v3/generator/definition.ts create mode 100644 v3/generator/generator.ts create mode 100644 v3/generator/index.ts delete mode 100644 v3/helper_test.ts diff --git a/__test__/3.0/api-auth.ts b/__test__/3.0/api-auth.ts index b78b537..381cf09 100644 --- a/__test__/3.0/api-auth.ts +++ b/__test__/3.0/api-auth.ts @@ -92,336 +92,3 @@ export interface SwaggerApi{ id: string }} & IDefaultOptions): Promise } - -// swagger runtime. generate by swagger2ts -interface IRuntimeHeaderMapString { - [key: string]: string; -} - -interface IRuntimeHeaderConfig { - common: IRuntimeHeaderMapString; - [method: string]: IRuntimeHeaderMapString; -} - -interface IRuntimeRequestCommonOptions extends Omit { - path?: { - [key: string]: string; - }; - query?: { - [key: string]: string; - }; - header?: { - [key: string]: string; - }; - body?: any; - timeout?: number; -} - -interface IRuntimeRequestOptions extends IRuntimeRequestCommonOptions { - url: string; - method: Uppercase; -} - -interface IRequestInterceptor { - use(fn: IRequestInterceptorFn): IInterceptorCancelFn; -} - -interface IResponseInterceptor { - use(success: IResponseInterceptorSuccessFn, error: IResponseInterceptorErrorFn): IInterceptorCancelFn; -} - -type IInterceptorCancelFn = () => void; -type IRequestInterceptorFn = (config: IRuntimeRequestOptions) => Promise; -type IResponseInterceptorSuccessFn = (config: IRuntimeRequestOptions, response: Response, data: T) => Promise; -type IResponseInterceptorErrorFn = (config: IRuntimeRequestOptions, Error: RuntimeError) => Promise; - -interface IRuntimeForm { - [key: string]: any; -} -class RequestInterceptor implements IRequestInterceptor { - private _fns: IRequestInterceptorFn[] = []; - public use(fn: IRequestInterceptorFn) { - this._fns.push(fn); - - return () => { - const index = this._fns.findIndex((v) => v === fn); - - if (index > -1) { - this._fns.splice(index, 1); - } - }; - } - - async run(config: IRuntimeRequestOptions): Promise { - for (const fn of this._fns) { - config = await fn(config); - } - - return config; - } -} - -class ResponseInterceptor implements IResponseInterceptor { - private _fnsSuccess: IResponseInterceptorSuccessFn[] = []; - private _fnsError: IResponseInterceptorErrorFn[] = []; - public use(successFn: IResponseInterceptorSuccessFn, errorFn: IResponseInterceptorErrorFn) { - this._fnsSuccess.push(successFn); - this._fnsError.push(errorFn); - - return () => { - const successIndex = this._fnsSuccess.findIndex((v) => v === successFn); - const errorIndex = this._fnsError.findIndex((v) => v === errorFn); - - if (successIndex > -1) { - this._fnsSuccess.splice(successIndex, 1); - } - - if (errorIndex > -1) { - this._fnsError.splice(errorIndex, 1); - } - }; - } - - async runSuccess(config: IRuntimeRequestOptions, response: Response, data: T): Promise { - for (const fn of this._fnsSuccess) { - data = await fn(config, response, data); - } - - return data; - } - - async runError(config: IRuntimeRequestOptions, err: RuntimeError): Promise { - let res!: T; - - for (const fn of this._fnsError) { - res = await fn(config, err); - } - - return res; - } -} - -export class RuntimeForm { - constructor(private _form: T) {} - public formData(): FormData { - const form = new FormData(); - - for (const key in this._form) { - if (this._form[key] !== undefined) { - form.append(key, this._form[key]); - } - } - - return form; - } -} - -export class RuntimeError extends Error { - constructor(message: string, private _resp?: Response) { - super(message); - } - - public get response(): Response | undefined { - return this._resp; - } - - static fromResponse(resp: Response) { - return new RuntimeError(resp.statusText, resp); - } - - static fromError(err: Error) { - return new RuntimeError(err.message || "unknown error: " + err); - } -} - -export interface IRuntime { - readonly interceptors: { readonly request: IRequestInterceptor; readonly response: IResponseInterceptor }; - readonly defaults: { readonly timeout: number; readonly headers: IRuntimeHeaderConfig }; - readonly baseURL: string; - domain: string; - prefix: string; - request(config: IRuntimeRequestOptions): Promise; -} -export class Runtime implements IRuntime { - constructor(private _domain: string, private _prefix: string) { - const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; - - for (const method of methods) { - // @ts-ignore ignore error - this[method] = (url: string, config?: IRuntimeRequestCommonOptions = {}) => { - return this.request({ - method: method.toUpperCase(), - url, - ...config, - }); - }; - } - } - - private _requestInterceptor = new RequestInterceptor(); - private _responseInterceptor = new ResponseInterceptor(); - - private _defaults = { - timeout: 60 * 1000, // 60s, - headers: { - common: { - "Content-Type": "application/json", - }, - } as IRuntimeHeaderConfig, - }; - - public get interceptors() { - const self = this; - return { - get request() { - return self._requestInterceptor as IRequestInterceptor; - }, - get response() { - return self._responseInterceptor as IResponseInterceptor; - }, - }; - } - - public get defaults() { - return this._defaults; - } - - private _timeout(ms: number, promise: Promise) { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new RuntimeError(`timeout of ${ms}ms`)); - }, ms); - - promise - .then((value) => { - clearTimeout(timer); - resolve(value); - }) - .catch((reason) => { - clearTimeout(timer); - reject(reason); - }); - }); - } - - public get baseURL(): string { - const baseUrl = this._domain.replace(/\/$/, "") + (!/^\//.test(this._prefix) ? "/" : "") + this._prefix; - - return baseUrl.replace(/\/$/, ""); - } - - public set domain(domain: string) { - this._domain = domain; - } - - public set prefix(prefix: string) { - this._prefix = prefix; - } - - public async request(config: IRuntimeRequestOptions): Promise { - const url = new URL(this.baseURL + config.url); - config.header = config.header || {}; - - const defaults = this.defaults; - - // set default header - for (const key in defaults.headers.common) { - config.header[key] = defaults.headers.common[key]; - } - - // set header for this method - for (const key in defaults.headers[config.method] || {}) { - config.header[key] = defaults.headers[config.method][key]; - } - - if (config.query) { - for (const key in config.query) { - const value = config.query[key]; - if (value !== undefined) { - url.searchParams.append(key, value); - } - } - } - - if (config.path) { - for (const key in config.path) { - const t1 = encodeURI("{"); - const t2 = encodeURI("}"); - const reg = new RegExp(`${t1}${key}${t2}`, "g"); - url.pathname = url.pathname.replace(reg, config.path[key]); - } - } - - config = await this._requestInterceptor.run(config); - - const headers = new Headers(); - - for (const key in config.header) { - const value = config.header[key]; - if (value !== undefined) { - headers.set(key, value); - } - } - - const timeout = config.timeout || defaults.timeout; - - const body = - config.body === undefined - ? undefined - : ["GET", "HEAD"].indexOf(config.method.toUpperCase()) > -1 - ? undefined - : config.body instanceof RuntimeForm - ? config.body.formData() - : config.body instanceof Blob - ? config.body - : typeof config.body === "object" - ? JSON.stringify(config.body) - : config.body.toString(); - - const exec = () => - fetch(url.toString(), { - method: config.method, - body: body, - headers: headers, - - // common options - cache: config.cache, - credentials: config.credentials, - integrity: config.integrity, - keepalive: config.keepalive, - mode: config.mode, - redirect: config.redirect, - referrer: config.referrer, - referrerPolicy: config.referrerPolicy, - signal: config.signal, - window: config.window, - }); - - return (timeout ? this._timeout(timeout, exec()) : exec()) - .then(async (resp) => { - if (!resp.ok) return Promise.reject(RuntimeError.fromResponse(resp)); - const contentType = resp.headers.get("content-type"); - switch (contentType) { - case "application/json": - return { data: await resp.json(), resp }; - case "application/x-www-form-urlencoded": - return { data: await resp.formData(), resp }; - case "application/octet-stream": - return { data: await resp.blob(), resp }; - default: - return { data: await resp.text(), resp }; - } - }) - .then(({ data, resp }) => { - return this._responseInterceptor.runSuccess(config, resp, data); - }) - .catch((err) => { - const runtimeErr = err instanceof RuntimeError ? err : err instanceof Error ? RuntimeError.fromError(err) : new RuntimeError(err + ""); - - return this._responseInterceptor.runError(config, runtimeErr); - }); - } -} - -export type IClient = SwaggerApi & IRuntime -export const unknownApi = new Runtime("http://localhost", "/") as unknown as IClient \ No newline at end of file diff --git a/swagger2ts.ts b/swagger2ts.ts index f95d166..42a2824 100644 --- a/swagger2ts.ts +++ b/swagger2ts.ts @@ -1,5 +1,5 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; -import { generateDefinition, generateImplement } from "./v3/index.ts"; +import { generate as generateTsFile } from "./v3/generator/index.ts"; import "./runtime/fetch.ts"; // import to check type and download deps async function getDeps(url: string): Promise { @@ -36,12 +36,12 @@ export async function generate(target: string): Promise { // remote file if (/^https?:\/\//.test(target)) { const url = new URL(target); - const headers = new Headers() + const headers = new Headers(); - headers.set("Content-Type", "application/json;charset=UTF-8") + headers.set("Content-Type", "application/json;charset=UTF-8"); const resp = await fetch(url, { - headers: headers + headers: headers, }); swaggerJSONContent = await resp.text(); @@ -63,14 +63,9 @@ export async function generate(target: string): Promise { throw new Error("can not found sdk file"); } - const definition = generateDefinition(swaggerJSONContent); - const implement = generateImplement(swaggerJSONContent, new TextDecoder().decode(await Deno.readFile(sdkFilepath)), domain); + const definition = generateTsFile(swaggerJSONContent); - const result = `// Generate by swagger2ts -${definition} - -${implement} -`; + const result = `${definition}`; return result.trim(); } @@ -78,5 +73,5 @@ ${implement} if (import.meta.main) { const result = await generate(Deno.args[0]); - Deno.stdout.writeSync(new TextEncoder().encode(result)) + Deno.stdout.writeSync(new TextEncoder().encode(result)); } diff --git a/test.ts b/test.ts index 0d03fed..27a7e0f 100644 --- a/test.ts +++ b/test.ts @@ -1,36 +1,86 @@ -import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; -import { assertEquals, assert } from "https://deno.land/std@0.105.0/testing/asserts.ts"; -import { generate } from "./swagger2ts.ts"; -import { URL2filepath } from "./helper.ts"; - -async function testDir(dirName: string) { - const testDir = URL2filepath(new URL(`./__test__/${dirName}`, import.meta.url)); - - for await (const dirEntry of Deno.readDir(testDir)) { - if (/\.json$/.test(dirEntry.name)) { - const actual = await generate(path.join(testDir, dirEntry.name)); - - const expect = await Deno.readTextFile(path.join(testDir, dirEntry.name.replace(/\.json$/, ".ts"))); - - if (Deno.build.os !== 'windows') { - assertEquals(actual.trim(), expect.trim()); - } else { - assert(actual.length !== 0) - } - } +export interface ClientVO { + /** + * @description 授权方式 + */ + authorizationGrantTypes?: string + /** + * @description client_id + */ + clientId?: string + /** + * @description 到期时间 + */ + clientSecretExpiresAt?: string + /** + * @description 录入时间 + */ + gmtCreate?: string + /** + * @description 主键 + */ + id?: string + /** + * @description 应用名称 + */ + name?: string + /** + * @description 回调地址 + */ + redirectUris?: string +} + +export interface PageResultVOOfClientVO { + /** + * @description 信息 + */ + data?: Array + /** + * @description 总数 + */ + total?: number +} + +export interface ResponseEntity { + body?: { + [key: string]: unknown } + statusCode?: 'ACCEPTED' | 'ALREADY_REPORTED' | 'BAD_GATEWAY' | 'BAD_REQUEST' | 'BANDWIDTH_LIMIT_EXCEEDED' | 'CHECKPOINT' | 'CONFLICT' | 'CONTINUE' | 'CREATED' | 'DESTINATION_LOCKED' | 'EXPECTATION_FAILED' | 'FAILED_DEPENDENCY' | 'FORBIDDEN' | 'FOUND' | 'GATEWAY_TIMEOUT' | 'GONE' | 'HTTP_VERSION_NOT_SUPPORTED' | 'IM_USED' | 'INSUFFICIENT_SPACE_ON_RESOURCE' | 'INSUFFICIENT_STORAGE' | 'INTERNAL_SERVER_ERROR' | 'I_AM_A_TEAPOT' | 'LENGTH_REQUIRED' | 'LOCKED' | 'LOOP_DETECTED' | 'METHOD_FAILURE' | 'METHOD_NOT_ALLOWED' | 'MOVED_PERMANENTLY' | 'MOVED_TEMPORARILY' | 'MULTIPLE_CHOICES' | 'MULTI_STATUS' | 'NETWORK_AUTHENTICATION_REQUIRED' | 'NON_AUTHORITATIVE_INFORMATION' | 'NOT_ACCEPTABLE' | 'NOT_EXTENDED' | 'NOT_FOUND' | 'NOT_IMPLEMENTED' | 'NOT_MODIFIED' | 'NO_CONTENT' | 'OK' | 'PARTIAL_CONTENT' | 'PAYLOAD_TOO_LARGE' | 'PAYMENT_REQUIRED' | 'PERMANENT_REDIRECT' | 'PRECONDITION_FAILED' | 'PRECONDITION_REQUIRED' | 'PROCESSING' | 'PROXY_AUTHENTICATION_REQUIRED' | 'REQUESTED_RANGE_NOT_SATISFIABLE' | 'REQUEST_ENTITY_TOO_LARGE' | 'REQUEST_HEADER_FIELDS_TOO_LARGE' | 'REQUEST_TIMEOUT' | 'REQUEST_URI_TOO_LONG' | 'RESET_CONTENT' | 'SEE_OTHER' | 'SERVICE_UNAVAILABLE' | 'SWITCHING_PROTOCOLS' | 'TEMPORARY_REDIRECT' | 'TOO_EARLY' | 'TOO_MANY_REQUESTS' | 'UNAUTHORIZED' | 'UNAVAILABLE_FOR_LEGAL_REASONS' | 'UNPROCESSABLE_ENTITY' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPGRADE_REQUIRED' | 'URI_TOO_LONG' | 'USE_PROXY' | 'VARIANT_ALSO_NEGOTIATES' + statusCodeValue?: number +} + + + +export interface SwaggerPath { + [key: string]: string +} + +export type Stringable = { + + toString(): string +} | null | undefined | void +export interface SwaggerQuery { + [key: string]: Stringable | Stringable[] +} + +export interface SwaggerHeaders { + [key: string]: Stringable | Stringable[] +} + +export type SwaggerCommonOptions = Omit & { timeout?: number } + +export type RequireKeys = Required> & Omit + +export interface SwaggerOptions

extends SwaggerCommonOptions { + path?: P + query?: Q + headers?: H + body?: B } -Deno.test({ - name: "Generate for swagger 3.0", - fn: async () => { - await testDir("3.0"); - }, -}); - -Deno.test({ - name: "Generate for swagger 3.1", - fn: async () => { - await testDir("3.1"); - }, -}); +export interface SwaggerApi { + post(url: '/other', options: SwaggerOptions<{}, {name: string}, {}, Blob | Uint8Array>): Promise + get(url: '/register/client', options: SwaggerOptions<{}, {size: number}, {}, unknown>): Promise + post(url: '/register/client', options: SwaggerOptions<{}, {redirectUri: string}, {}, unknown>): Promise + post(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise + delete(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise +} \ No newline at end of file diff --git a/v3/generateDefinition.ts b/v3/generateDefinition.ts deleted file mode 100644 index be503ee..0000000 --- a/v3/generateDefinition.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { generateMultipleLineComments, indentTxt, linesOfText, isValidVarName } from "./helper.ts"; -import { - IOperationObject, - IParameterObject, - IReferenceObject, - IRequestBodyObject, - IResponseObject, - IResponsesObject, - ISchemaObject, - isReferenceObject, - isRequestBodyObject, - ISwagger, -} from "./types.ts"; - -function generateParamsComment(schema: ISchemaObject) { - return schema.description ? ` /* ${schema.description} */` : ""; -} - -/** - * generate document for rate - */ -function generateSchema(name: string, schema: ISchemaObject | IReferenceObject | undefined, indent: number, optionalThenUndefined?: boolean): string { - if (!schema) return "any"; - - // "#/components/schemas/Address" - if (isReferenceObject(schema)) { - const schemaPropertyPaths = schema.$ref.replace(/^#\/components/, "").split("/"); - - const refSchemaName = schemaPropertyPaths[schemaPropertyPaths.length - 1]; - - return refSchemaName; - } else { - const isRequired = schema.required; - const isNullable = schema.nullable; - - function generateLiteral(...types: string[]) { - let stringResult = `${name ? `type ${name} = ` : ""}${types.join(" | ")}`; - if (!isRequired) { - if (!optionalThenUndefined) { - stringResult += " | undefined"; - } - } - - if (isNullable) { - stringResult += " | null"; - } - - stringResult += generateParamsComment(schema as ISchemaObject); - - return stringResult; - } - - switch (schema.type) { - case "string": - if (schema.format === "binary") return generateLiteral("File", "Blob"); - if (schema.enum) { - return generateLiteral(schema.enum.map((v) => `"${v}"`).join(" | ")); - } - return generateLiteral("string"); - case "boolean": - if (schema.enum) { - return generateLiteral(schema.enum.join(" | ")); - } - return generateLiteral("boolean"); - case "integer": - case "number": - if (schema.enum) { - return generateLiteral(schema.enum.join(" | ")); - } - return generateLiteral("number"); - case "array": - if (!schema.items) return "any[]"; - return `Array<${generateSchema("", schema.items, indent)}>${generateParamsComment(schema)}`; - case "object": - const outputObject: string[] = []; - for (const prop in schema.properties) { - const propSchema = schema.properties[prop]; - const indentStr = " ".repeat(indent + 2); - const propertyName = isValidVarName(prop) ? prop : '"' + prop + '"'; - const optional = !propSchema.required ? "?" : ""; - - outputObject.push(`${indentStr}${propertyName}${optional}: ${generateSchema("", propSchema, indent, true)}`); - } - if (!outputObject.length) return name ? `interface ${name} {}` : "{}"; - return [name ? `interface ${name} {` : "{", ...outputObject, "}"].filter((v) => v.trim()).join("\n"); - default: - return `${name ? `type ${name} = ` : ""}any${generateParamsComment(schema)}`; - } - } -} - -function generateParamsArray(params: Array, indent: number): string { - const output: string[] = []; - for (const param of params) { - if (isReferenceObject(param)) { - throw new Error("not support ref param"); - } - const paramName = isValidVarName(param.name) ? param.name : '"' + param.name + '"'; - const optional = !param.required ? "?" : ""; - - output.push(`${paramName}${optional}: ${generateSchema("", param.schema, 0, true)}`); - } - - if (!output.length) return "{}"; - - return ["{", ...output.map((v) => " ".repeat(indent + 2) + v), "}"].join("\n"); -} - -function generateParams(param: IParameterObject | IReferenceObject, indent: number): string { - if (isReferenceObject(param)) { - const schemaPropertyPaths = param.$ref.replace(/^#\/components/, "").split("/"); - - const refSchemaName = schemaPropertyPaths[schemaPropertyPaths.length - 1]; - - return refSchemaName; - } else if (!param.schema) { - return "any"; - } else { - return generateSchema("", param.schema, indent, true); - } -} - -function generateComponent(swagger: ISwagger, indent: number): string { - const output: string[] = []; - - if (swagger.components) { - if (swagger.components.schemas) { - for (const componentName in swagger.components.schemas) { - const schema = swagger.components.schemas[componentName]; - - let componentOutput = ""; - - if (!isReferenceObject(schema)) { - const comments: string[] = []; - - if (schema.title) { - comments.push(`${schema.title}`); - } - - if (schema.description) { - comments.push(`@description ${schema.description}`); - } - - const jsdocComments = generateMultipleLineComments(comments).trim(); - - if (jsdocComments) { - componentOutput += jsdocComments + "\n"; - } - } - - componentOutput += `export ${generateSchema(componentName, schema, indent)}`; - - output.push(componentOutput); - } - } - } - - return output.join("\n\n"); -} - -function generateBody(body: IRequestBodyObject | IResponseObject, indent: number) { - if (!body.content) return "null"; - const jsonBody = body.content["application/json"]; - const xmlBody = body.content["application/xml"]; - const streamBody = body.content["application/octet-stream"]; - const formBody = body.content["multipart/form-data"]; - const anyBody = body.content["*/*"]; - - const mediaSchema = jsonBody || xmlBody || streamBody || formBody || anyBody; - - if (!mediaSchema || !mediaSchema.schema) return "null"; - - if (formBody && mediaSchema === formBody) { - if (!formBody.schema) return "RuntimeForm"; - return `RuntimeForm<${generateSchema("", formBody.schema, 0)}>`; - } - - return generateSchema("", mediaSchema.schema, 0); -} - -function generateResponseBody(response: IResponsesObject): string { - if (!response) return "Promise"; - - let responseType = "null"; - - if (response["200"] || response.default) { - const successResponse = response["200"] || response.default; - if (isReferenceObject(successResponse)) { - responseType = generateSchema("", successResponse, 0); - } else { - responseType = generateBody(successResponse, 0); - } - } else { - responseType = "unknown"; - } - - return `Promise<${responseType}>`; -} - -function generateApi(swagger: ISwagger, indent: number): string { - const urlBlock: string[] = []; - const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; - - for (const url in swagger.paths) { - const pathItemObject = swagger.paths[url]; - - for (const method of methods) { - // @ts-expect-error ignore error - const operation = pathItemObject[method] as IOperationObject; - if (!operation) continue; - - const options: string[] = []; - - if (operation.parameters) { - const paramsArray: string[] = []; - const inTypes = ["path", "query", "header"]; - const parameters = operation.parameters; - - inTypes.forEach((action) => { - const paths = parameters.filter((v) => !isReferenceObject(v)).filter((v) => (v as IParameterObject).in === action); - - const pathsType = generateParamsArray(paths, 0); - - const isEmpty = pathsType === "{}"; - - if (!isEmpty) { - paramsArray.push(`${action}: ${pathsType}`); - } - }); - - options.push(...paramsArray); - } - - if (operation.requestBody) { - let paramsBody: string = ""; - if (isRequestBodyObject(operation.requestBody)) { - paramsBody = generateBody(operation.requestBody, 0); - } else { - paramsBody = generateSchema("", operation.requestBody, 0, true); - } - if (paramsBody) { - options.push(`body: ${paramsBody}`); - } - } - - const docs: string[] = []; - - if (operation.tags) { - for (const tag of operation.tags) { - docs.push(`@tag ${tag}`); - } - } - - if (operation.summary) { - linesOfText(operation.summary) - .filter((v) => v.trim()) - .forEach((line) => { - docs.push(`@summary ${line}`); - }); - } - - if (operation.description) { - linesOfText(operation.description) - .filter((v) => v.trim()) - .forEach((line) => { - docs.push(`@description ${line}`); - }); - } - - const rows = [ - generateMultipleLineComments(docs), - `${method}(url: "${url}", options${!options.length ? "?" : ""}: {${options.join(", ")}} & IDefaultOptions): ${generateResponseBody( - operation.responses - )}`, - // - ] - .filter((v) => v) - .join("\n"); - - urlBlock.push(rows); - } - } - - return `export interface SwaggerApi{ -${indentTxt(urlBlock.join("\n\n"), indent, true)} -}`; -} - -function generateDefaultTypes(): string { - return `/* default type by generation start */ -interface MapAny { - [key: string]: any -} -interface MapString { - [key: string]: string | undefined -} - -type IDefaultOptions = Omit & { timeout?: number } -/* default type by generation end */`; -} - -// generate HTTP definition for swagger api -export function generateDefinition(content: string): string { - const indent = 2; - - const swagger = JSON.parse(content) as ISwagger; - - const output: string[] = [generateDefaultTypes(), generateComponent(swagger, 0), generateApi(swagger, indent)]; - - return output.join("\n\n"); -} diff --git a/v3/generator/api.ts b/v3/generator/api.ts new file mode 100644 index 0000000..1018d3b --- /dev/null +++ b/v3/generator/api.ts @@ -0,0 +1,114 @@ +import { + IOperationObject, + IParameterObject, + IReferenceObject, + IRequestBodyObject, + IResponseObject, + IResponsesObject, + ISchemaObject, + isReferenceObject, + isRequestBodyObject, + ISwagger, +} from "../types.ts"; +import { ApiGenerator, DefinitionGenerator } from "./generator.ts"; +import { traverse } from "./definition.ts"; + +function generatePath(swagger: ISwagger): string { + const g = new ApiGenerator(); + + g.start(); + + const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; + + for (const path in swagger.paths) { + const pathObject = swagger.paths[path]; + + const defineMethods = methods.filter((v) => v in pathObject); + + for (const method of defineMethods) { + const inTypes = ["path", "query", "header"]; + + const params: { [key: string]: string } = {}; + + // @ts-ignore ignore error + const parameters = pathObject[method].parameters as Array; + + if (parameters) { + inTypes.forEach((action) => { + params[action] = ""; + + const paths = parameters.filter((v) => !isReferenceObject(v)).filter((v) => (v as IParameterObject).in === action) as IParameterObject[]; + + for (const path of paths) { + if (path.schema) { + const g = new DefinitionGenerator(); + path.schema.required = typeof path.schema.required === "boolean" ? path.schema.required : path.required; + g.write("{"); + g.write(`${g.indentStr}${path.name}: `); + traverse(g, path.schema); + g.write("}"); + params[action] = g.toString(); + } else { + params[action] = "unknown"; + } + } + }); + } + + // @ts-ignore ignore error + const requestBody = pathObject[method].requestBody as IRequestBodyObject | IReferenceObject; + // @ts-ignore ignore error + const responseBody = pathObject[method].responses as IResponsesObject; + + function generateBody(b?: IRequestBodyObject | IResponseObject | IReferenceObject): string { + if (!b) return "unknown"; + + if (isReferenceObject(b)) { + const g = new DefinitionGenerator(); + traverse(g, b); + + return g.toString(); + } + + const body: IRequestBodyObject | IResponseObject = b; + + if (!body.content) return "unknown"; + + const jsonBody = body.content["application/json"] || body.content["text/json"] || body.content["text/plain"]; + const xmlBody = body.content["application/xml"]; + const streamBody = body.content["application/octet-stream"]; + const formBody = body.content["multipart/form-data"]; + const anyBody = body.content["*/*"]; + + const mediaSchema = jsonBody || xmlBody || streamBody || formBody || anyBody; + + if (!mediaSchema || !mediaSchema.schema) return "unknown"; + + const g = new DefinitionGenerator(); + + if (formBody && mediaSchema === formBody) { + g.write("FormData"); + } else { + traverse(g, mediaSchema.schema); + } + + return g.toString(); + } + + const body = generateBody(requestBody); + const response = generateBody(responseBody ? responseBody[200] || responseBody.default : undefined); + + g.writeApi(method, path, params.path, params.query, params.headers, body, response); + } + } + + g.end(); + + return g.toString(); +} + +export function generateApi(content: string): string { + const swagger = JSON.parse(content) as ISwagger; + + return generatePath(swagger); +} diff --git a/v3/generator/definition.ts b/v3/generator/definition.ts new file mode 100644 index 0000000..e18226a --- /dev/null +++ b/v3/generator/definition.ts @@ -0,0 +1,223 @@ +import { IReferenceObject, ISchemaObject, isReferenceObject, ISwagger } from "../types.ts"; +import { DefinitionGenerator } from "./generator.ts"; + +function getRealType(type?: string) { + if (type === undefined) return "unknown"; + + const typeMapper: { [key: string]: string } = { + integer: "number", + }; + + return typeMapper[type] || type; +} + +/** + * 获取引用名称 + */ +function getRefName(ref: IReferenceObject): string { + const schemaPropertyPaths = ref.$ref.replace(/^#\/components/, "").split("/"); + + return schemaPropertyPaths[schemaPropertyPaths.length - 1].trim(); +} + +/** + * 生成节点 + */ +export function traverse(g: DefinitionGenerator, schema: IReferenceObject | ISchemaObject) { + if (isReferenceObject(schema)) { + g.write(`${getRefName(schema)}`); + return; + } + + // 递归生成 + switch (schema.type) { + case "object": + traverseObject(g, schema); + break; + case "array": + traverseArray(g, schema); + break; + default: + if (schema.format && schema.format === "binary") { + g.write("Blob | Uint8Array"); + } else if (schema.enum) { + if (schema.type === "string") { + g.write(schema.enum.map((v) => `'${v}'`).join(" | ")); + } else { + g.write(schema.enum.join(" | ")); + } + } else { + g.write(getRealType(schema.type)); + } + + if (schema.nullable) { + g.write(" | null"); + } + } +} + +/** + * 生成 Object + */ +function traverseObject(g: DefinitionGenerator, object: ISchemaObject) { + if (isReferenceObject(object)) { + g.write(`${getRefName(object)}`); + return; + } + + const objectInterface = g.createInterfaceBlock(); + + objectInterface.start(); + + object.required; + + if (object.properties) { + for (const attr in object.properties) { + const propertyType = object.properties[attr]; + + const comment = g.createCommentBlock(); + + if (propertyType.description) { + comment.start(); + comment.write("description", propertyType.description); + comment.end(); + } + + const isPropertyRequired = + typeof propertyType.required === "boolean" ? propertyType.required : Array.isArray(object.required) ? object.required.includes(attr) : false; + + if (isReferenceObject(propertyType)) { + objectInterface.writeProperty(attr, getRefName(propertyType), !isPropertyRequired, propertyType.nullable); + continue; + } + + // 递归生成 + switch (propertyType.type) { + case "object": + g.write(`${g.indentStr}${attr}${isPropertyRequired ? "" : "?"}: `); + traverse(g, propertyType); + + if (propertyType.nullable) { + g.dropLastEmptyLine(1); + g.write(" | null"); + g.write(g.EOL); + } + + break; + case "array": + g.write(`${g.indentStr}${attr}${isPropertyRequired ? "" : "?"}: `); + traverse(g, propertyType); + + if (propertyType.nullable) { + g.write(" | null"); + } + + g.write(g.EOL); + break; + default: + g.write(`${g.indentStr}${attr}${isPropertyRequired ? "" : "?"}: `); + traverse(g, propertyType); + g.write(g.EOL); + } + } + } + + if (object.additionalProperties) { + g.write(`${g.indentStr}[key: string]: `); + traverse(g, object.additionalProperties!); + g.write(g.EOL); + } + + // 如果是一个空对象 + if (!object.properties && !object.additionalProperties) { + g.writeln(`[key: string]: unknown`); + } + + objectInterface.end(); +} + +/** + * 生成数组 + */ +function traverseArray(g: DefinitionGenerator, array: ISchemaObject) { + if (isReferenceObject(array.items)) { + g.write(`Array<${getRefName(array.items)}>`); + } else { + g.write(`Array<`); + switch (array.items?.type) { + case "object": + traverseObject(g, array.items); + break; + case "array": + traverseArray(g, array.items); + break; + default: + g.write(getRealType(array.items?.type)); + } + + g.write(">"); + } +} + +/** + * 生成组件 + */ +function generateComponent(swagger: ISwagger): string { + const g = new DefinitionGenerator(); + + if (swagger.components) { + if (swagger.components.schemas) { + for (const componentName in swagger.components.schemas) { + const schema = swagger.components.schemas[componentName]; + + const comment = g.createCommentBlock(); + + if (isReferenceObject(schema)) { + g.write("export "); + g.declareType(componentName, getRefName(schema)); + continue; + } + + if (schema.description) { + comment.start(); + comment.write("description", schema.description); + comment.end(); + } + + switch (schema.type) { + case "object": + g.write(`export interface ${componentName} `); + traverse(g, schema); + g.write(g.EOL); + + break; + case "array": + g.write(`export type ${componentName} = Array<`); + traverse(g, schema.items!); + g.writeln(">"); + break; + default: + g.write("export "); + if (schema.enum) { + g.declareEnum(componentName, schema.enum); + g.write(g.EOL); + } else { + g.declareType(componentName, getRealType(schema.type)); + g.write(g.EOL); + } + + g.write(g.EOL); + } + } + } + } + + return g.toString(); +} + +// generate HTTP definition for swagger api +export function generateDefinition(content: string): string { + const swagger = JSON.parse(content) as ISwagger; + + return generateComponent(swagger); +} diff --git a/v3/generator/generator.ts b/v3/generator/generator.ts new file mode 100644 index 0000000..4071b05 --- /dev/null +++ b/v3/generator/generator.ts @@ -0,0 +1,205 @@ +class BaseGenerator { + public indent = 0; + private indentBase = 2; + private content = ""; + public EOL = "\n"; + + public get indentStr() { + return " ".repeat(this.indent * this.indentBase); + } + + public write(str: string) { + this.content += str; + } + + public writeln(str: string) { + this.content += this.indentStr + str + this.EOL; + } + + public dropLastEmptyLine(n: number) { + if (n <= 0) return; + + while (n > 0) { + this.content = this.content.replace(new RegExp(this.EOL + "$"), ""); + n--; + } + } + + public toString() { + return this.content; + } +} + +interface Block { + start(): void; + end(): void; +} + +class InterfaceBlock implements Block { + constructor(private g: BaseGenerator) {} + + public writeProperty(name: string, type: string, optional: boolean | undefined, nullable: boolean | undefined) { + this.g.write(`${this.g.indentStr}${name}${optional ? "?" : ""}: ${type}`); + this.g.write(nullable ? " | null" : ""); + this.g.write(this.g.EOL); + } + + public start() { + this.g.write("{"); + this.g.write(this.g.EOL); + this.g.indent++; + } + + public end() { + this.g.indent--; + this.g.writeln("}"); + } +} + +export class CommentBlock implements Block { + constructor(private g: BaseGenerator) {} + + public start() { + this.g.writeln(`/**`); + } + + public write(tag: string, content: string) { + this.g.writeln(` * @${tag} ${content}`); + } + + public end() { + this.g.writeln(` */`); + } +} + +export class DefinitionGenerator extends BaseGenerator { + public declareType(name: string, type: string) { + this.write(`type ${name} = ${type}`); + } + + public declareEnum(name: string, values: Array) { + this.write(`type ${name} = ${values.join(" | ")}`); + } + + public declareInterface(name: string, properties: { [key: string]: any }) { + this.writeln(`interface ${name} {`); + this.indent++; + + for (const attr in properties) { + const value = properties[attr]; + + this.writeln(`${attr}: ${value}`); + } + + this.indent--; + this.writeln("}"); + } + + public createInterfaceBlock(): InterfaceBlock { + return new InterfaceBlock(this); + } + + public createCommentBlock(): CommentBlock { + return new CommentBlock(this); + } +} + +export class ApiGenerator extends DefinitionGenerator { + private interface = this.createInterfaceBlock(); + + start() { + const options = this.createInterfaceBlock(); + + this.write(`export interface SwaggerPath `); + options.start(); + options.writeProperty("[key: string]", "string", false, false); + options.end(); + this.write(this.EOL); + + this.writeln("export type Stringable = {"); + this.write(this.EOL); + this.indent++ + this.writeln("toString(): string"); + this.indent-- + this.writeln("} | null | undefined | void"); + + this.write(`export interface SwaggerQuery `); + options.start(); + options.writeProperty("[key: string]", "Stringable | Stringable[]", false, false); + options.end(); + this.write(this.EOL); + + this.write(`export interface SwaggerHeaders `); + options.start(); + options.writeProperty("[key: string]", "Stringable | Stringable[]", false, false); + options.end(); + this.write(this.EOL); + + this.writeln(`export type SwaggerCommonOptions = Omit & { timeout?: number }`); + this.write(this.EOL); + this.writeln(`export type RequireKeys = Required> & Omit`); + this.write(this.EOL); + + this.write( + `export interface SwaggerOptions

extends SwaggerCommonOptions ` + ); + options.start(); + options.writeProperty("path", "P", true, false); + options.writeProperty("query", "Q", true, false); + options.writeProperty("headers", "H", true, false); + options.writeProperty("body", "B", true, false); + options.end(); + this.write(this.EOL); + + this.write(`export interface SwaggerApi `); + this.interface.start(); + } + + end() { + this.interface.end(); + } + + writeApi(method: string, url: string, path: string, query: string, headers: string, body: string, returnValue: string) { + this.write(this.indentStr); + this.write(method + "("); + this.write(`url: '${url}', options: `); + + const requireKeys = []; + + if (path) { + this.write("RequireKeys"); + + if (requireKeys.length) { + this.write(`, ${requireKeys.map((v) => `'${v}'`).join(" | ")}>`); + } + + this.write("): "); + this.write(`Promise<${returnValue}>`); + this.write(this.EOL); + } +} diff --git a/v3/generator/index.ts b/v3/generator/index.ts new file mode 100644 index 0000000..b1d2674 --- /dev/null +++ b/v3/generator/index.ts @@ -0,0 +1,8 @@ +import { generateDefinition } from "./definition.ts"; +import { generateApi } from "./api.ts"; + +export function generate(content: string): string { + const output = [generateDefinition(content), generateApi(content)]; + + return output.join("\n\n"); +} diff --git a/v3/helper.ts b/v3/helper.ts index c2aeb0e..cae56fc 100644 --- a/v3/helper.ts +++ b/v3/helper.ts @@ -1,36 +1,3 @@ -/** - * Generate comments - * @param comments - * @param indent - * @returns - */ -export function generateMultipleLineComments(comments: string[]): string { - if (!comments.length) return ""; - - let raw: string[] = ["/**"]; - - comments.forEach((comment, index) => { - raw.push(` * ${comment}`); - }); - - raw.push(" */"); - - return raw.join("\n"); -} - -/** - * @description get lines of a text - */ -export function linesOfText(txt: string): string[] { - return txt.replace(/\r\n|\n\r|\n|\r/g, "\n").split(/\n/g); -} - -export function indentTxt(txt: string, indent: number, ignoreEmptyLine: boolean): string { - const arrayOfLines = linesOfText(txt); - - return arrayOfLines.map((line) => (/^\s*$/.test(line) ? line : " ".repeat(indent) + line)).join("\n"); -} - export function isValidVarName(name: string): boolean { - return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name) -} \ No newline at end of file + return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); +} diff --git a/v3/helper_test.ts b/v3/helper_test.ts deleted file mode 100644 index 4b0df64..0000000 --- a/v3/helper_test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { assertEquals } from "https://deno.land/std@0.105.0/testing/asserts.ts"; -import { linesOfText, indentTxt, generateMultipleLineComments } from "./helper.ts"; - -Deno.test({ - name: "linesOfText()", - fn: () => { - assertEquals(linesOfText("hello world"), ["hello world"]); - assertEquals(linesOfText("line 1\nline 2"), ["line 1", "line 2"]); - assertEquals(linesOfText("line 1\r\nline 2"), ["line 1", "line 2"]); - assertEquals(linesOfText("line 1\n\rline 2"), ["line 1", "line 2"]); - assertEquals(linesOfText("line 1\rline 2"), ["line 1", "line 2"]); - }, -}); - -Deno.test({ - name: "indentText()", - fn: () => { - assertEquals(indentTxt("hello world", 0, true), "hello world"); - assertEquals(indentTxt("hello world", 2, true), " hello world"); - assertEquals(indentTxt("line 1\nline 2", 2, true), " line 1\n line 2"); - assertEquals( - indentTxt( - `@tag user -@param name -@param age`, - 2, - true - ), - ` @tag user - @param name - @param age` - ); - - assertEquals( - indentTxt( - `@tag user - -@param name -@param age`, - 2, - true - ), - ` @tag user - - @param name - @param age` - ); - }, -}); - -Deno.test({ - name: "generateMultipleLineComments()", - fn: () => { - assertEquals( - generateMultipleLineComments(["@tag user"]), - ` -/** - * @tag user - */`.trimStart() - ); - - assertEquals( - generateMultipleLineComments(["@tag user", "@param comments", "@param indent"]), - ` -/** - * @tag user - * @param comments - * @param indent - */`.trimStart() - ); - }, -}); diff --git a/v3/index.ts b/v3/index.ts index 19fa558..ef66e6c 100644 --- a/v3/index.ts +++ b/v3/index.ts @@ -1,3 +1,3 @@ -export * from "./generateDefinition.ts"; +export * from "./generator/index.ts"; export * from "./generateImplement.ts"; export * from "./types.ts"; diff --git a/v3/types.ts b/v3/types.ts index 95101fa..e36e098 100644 --- a/v3/types.ts +++ b/v3/types.ts @@ -167,7 +167,7 @@ interface JSONSchema { uniqueItems?: number; maxProperties?: number; minProperties?: number; - required?: boolean; + required?: boolean | string[]; enum?: Array; // swagger extension @@ -178,7 +178,7 @@ interface JSONSchema { not?: ISchemaObject[]; items?: ISchemaObject | IReferenceObject; properties?: { [key: string]: ISchemaObject }; - additionalProperties?: { [key: string]: ISchemaObject }; + additionalProperties?: ISchemaObject | IReferenceObject; description?: string; format?: string; } From 15b20a157f4e5ed22bd13187624e1810e825b3f8 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Sun, 20 Feb 2022 02:13:39 +0800 Subject: [PATCH 02/50] update --- .gitignore | 3 ++- .vscode/settings.json | 2 +- helper.ts | 12 --------- v3/generateImplement.ts | 14 +++++++--- v3/generator/api.ts | 53 ++++++++++++++++++++++++++++++-------- v3/generator/definition.ts | 26 +++++++++++++++---- v3/generator/generator.ts | 45 +++++++++++++++++++++++++------- 7 files changed, 112 insertions(+), 43 deletions(-) delete mode 100644 helper.ts diff --git a/.gitignore b/.gitignore index 81b5493..5473ec2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ swagger.json .DS_Store swagger swagger.exe -dist/*.js \ No newline at end of file +dist/*.js +dist/*.mjs \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 5bab78a..ad683a8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "deno.enable": true, "deno.unstable": true, - "deno.lint": false, + "deno.lint": true, "deno.suggest.imports.hosts": { "https://deno.land": false } diff --git a/helper.ts b/helper.ts deleted file mode 100644 index fae8424..0000000 --- a/helper.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * cover URL to filepath - * @param fileURL - * @returns - */ -export function URL2filepath(fileURL: URL): string { - // Unix: file:///home/runner/work/swagger2ts/swagger2ts/test.ts - // Windows: file:///D:/a/swagger2ts/swagger2ts/test.ts - const reg = Deno.build.os === "windows" ? /^file:\/\/\// : /^file:\/\//; - - return fileURL.toString().replace(reg, ""); -} diff --git a/v3/generateImplement.ts b/v3/generateImplement.ts index eb69c72..8ca29b4 100644 --- a/v3/generateImplement.ts +++ b/v3/generateImplement.ts @@ -56,12 +56,16 @@ function path2apiName(serverInfo: IServerObject): string { } // generate HTTP implement for swagger api -export function generateImplement(content: string, sdkContent: string, domain: string): string { +export function generateImplement( + content: string, + sdkContent: string, + domain: string, +): string { const swagger = JSON.parse(content) as ISwagger; domain = domain.replace(/\/$/, ""); - sdkContent += "\nexport type IClient = SwaggerApi & IRuntime" + sdkContent += "\nexport type IClient = SwaggerApi & IRuntime"; if (swagger.servers) { const apis: string[] = []; @@ -70,14 +74,16 @@ export function generateImplement(content: string, sdkContent: string, domain: s const apiName = path2apiName(server); const serverURL = getServerUrl(server); - const api = `export const ${apiName} = new Runtime("${domain}", "${serverURL.pathname}") as unknown as IClient`; + const api = + `export const ${apiName} = new Runtime("${domain}", "${serverURL.pathname}") as unknown as IClient`; apis.push(api); } sdkContent += "\n" + apis.join("\n"); } else { - sdkContent += `\nexport const defaultApi = new Runtime("${domain}", "") as unknown as IClient`; + sdkContent += + `\nexport const defaultApi = new Runtime("${domain}", "") as unknown as IClient`; } return sdkContent; diff --git a/v3/generator/api.ts b/v3/generator/api.ts index 1018d3b..50d8f65 100644 --- a/v3/generator/api.ts +++ b/v3/generator/api.ts @@ -18,7 +18,16 @@ function generatePath(swagger: ISwagger): string { g.start(); - const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; + const methods = [ + "get", + "post", + "delete", + "put", + "head", + "options", + "trace", + "patch", + ]; for (const path in swagger.paths) { const pathObject = swagger.paths[path]; @@ -31,18 +40,24 @@ function generatePath(swagger: ISwagger): string { const params: { [key: string]: string } = {}; // @ts-ignore ignore error - const parameters = pathObject[method].parameters as Array; + const parameters = pathObject[method].parameters as Array< + IParameterObject | IReferenceObject + >; if (parameters) { inTypes.forEach((action) => { params[action] = ""; - const paths = parameters.filter((v) => !isReferenceObject(v)).filter((v) => (v as IParameterObject).in === action) as IParameterObject[]; + const paths = parameters.filter((v) => !isReferenceObject(v)).filter(( + v, + ) => (v as IParameterObject).in === action) as IParameterObject[]; for (const path of paths) { if (path.schema) { const g = new DefinitionGenerator(); - path.schema.required = typeof path.schema.required === "boolean" ? path.schema.required : path.required; + path.schema.required = typeof path.schema.required === "boolean" + ? path.schema.required + : path.required; g.write("{"); g.write(`${g.indentStr}${path.name}: `); traverse(g, path.schema); @@ -56,11 +71,15 @@ function generatePath(swagger: ISwagger): string { } // @ts-ignore ignore error - const requestBody = pathObject[method].requestBody as IRequestBodyObject | IReferenceObject; + const requestBody = pathObject[method].requestBody as + | IRequestBodyObject + | IReferenceObject; // @ts-ignore ignore error const responseBody = pathObject[method].responses as IResponsesObject; - function generateBody(b?: IRequestBodyObject | IResponseObject | IReferenceObject): string { + function generateBody( + b?: IRequestBodyObject | IResponseObject | IReferenceObject, + ): string { if (!b) return "unknown"; if (isReferenceObject(b)) { @@ -74,13 +93,15 @@ function generatePath(swagger: ISwagger): string { if (!body.content) return "unknown"; - const jsonBody = body.content["application/json"] || body.content["text/json"] || body.content["text/plain"]; + const jsonBody = body.content["application/json"] || + body.content["text/json"] || body.content["text/plain"]; const xmlBody = body.content["application/xml"]; const streamBody = body.content["application/octet-stream"]; const formBody = body.content["multipart/form-data"]; const anyBody = body.content["*/*"]; - const mediaSchema = jsonBody || xmlBody || streamBody || formBody || anyBody; + const mediaSchema = jsonBody || xmlBody || streamBody || formBody || + anyBody; if (!mediaSchema || !mediaSchema.schema) return "unknown"; @@ -96,9 +117,19 @@ function generatePath(swagger: ISwagger): string { } const body = generateBody(requestBody); - const response = generateBody(responseBody ? responseBody[200] || responseBody.default : undefined); - - g.writeApi(method, path, params.path, params.query, params.headers, body, response); + const response = generateBody( + responseBody ? responseBody[200] || responseBody.default : undefined, + ); + + g.writeApi( + method, + path, + params.path, + params.query, + params.headers, + body, + response, + ); } } diff --git a/v3/generator/definition.ts b/v3/generator/definition.ts index e18226a..2da8f5e 100644 --- a/v3/generator/definition.ts +++ b/v3/generator/definition.ts @@ -1,4 +1,9 @@ -import { IReferenceObject, ISchemaObject, isReferenceObject, ISwagger } from "../types.ts"; +import { + IReferenceObject, + ISchemaObject, + isReferenceObject, + ISwagger, +} from "../types.ts"; import { DefinitionGenerator } from "./generator.ts"; function getRealType(type?: string) { @@ -23,7 +28,10 @@ function getRefName(ref: IReferenceObject): string { /** * 生成节点 */ -export function traverse(g: DefinitionGenerator, schema: IReferenceObject | ISchemaObject) { +export function traverse( + g: DefinitionGenerator, + schema: IReferenceObject | ISchemaObject, +) { if (isReferenceObject(schema)) { g.write(`${getRefName(schema)}`); return; @@ -83,11 +91,19 @@ function traverseObject(g: DefinitionGenerator, object: ISchemaObject) { comment.end(); } - const isPropertyRequired = - typeof propertyType.required === "boolean" ? propertyType.required : Array.isArray(object.required) ? object.required.includes(attr) : false; + const isPropertyRequired = typeof propertyType.required === "boolean" + ? propertyType.required + : Array.isArray(object.required) + ? object.required.includes(attr) + : false; if (isReferenceObject(propertyType)) { - objectInterface.writeProperty(attr, getRefName(propertyType), !isPropertyRequired, propertyType.nullable); + objectInterface.writeProperty( + attr, + getRefName(propertyType), + !isPropertyRequired, + propertyType.nullable, + ); continue; } diff --git a/v3/generator/generator.ts b/v3/generator/generator.ts index 4071b05..136c2db 100644 --- a/v3/generator/generator.ts +++ b/v3/generator/generator.ts @@ -38,7 +38,12 @@ interface Block { class InterfaceBlock implements Block { constructor(private g: BaseGenerator) {} - public writeProperty(name: string, type: string, optional: boolean | undefined, nullable: boolean | undefined) { + public writeProperty( + name: string, + type: string, + optional: boolean | undefined, + nullable: boolean | undefined, + ) { this.g.write(`${this.g.indentStr}${name}${optional ? "?" : ""}: ${type}`); this.g.write(nullable ? " | null" : ""); this.g.write(this.g.EOL); @@ -118,30 +123,44 @@ export class ApiGenerator extends DefinitionGenerator { this.writeln("export type Stringable = {"); this.write(this.EOL); - this.indent++ + this.indent++; this.writeln("toString(): string"); - this.indent-- + this.indent--; this.writeln("} | null | undefined | void"); this.write(`export interface SwaggerQuery `); options.start(); - options.writeProperty("[key: string]", "Stringable | Stringable[]", false, false); + options.writeProperty( + "[key: string]", + "Stringable | Stringable[]", + false, + false, + ); options.end(); this.write(this.EOL); this.write(`export interface SwaggerHeaders `); options.start(); - options.writeProperty("[key: string]", "Stringable | Stringable[]", false, false); + options.writeProperty( + "[key: string]", + "Stringable | Stringable[]", + false, + false, + ); options.end(); this.write(this.EOL); - this.writeln(`export type SwaggerCommonOptions = Omit & { timeout?: number }`); + this.writeln( + `export type SwaggerCommonOptions = Omit & { timeout?: number }`, + ); this.write(this.EOL); - this.writeln(`export type RequireKeys = Required> & Omit`); + this.writeln( + `export type RequireKeys = Required> & Omit`, + ); this.write(this.EOL); this.write( - `export interface SwaggerOptions

extends SwaggerCommonOptions ` + `export interface SwaggerOptions

extends SwaggerCommonOptions `, ); options.start(); options.writeProperty("path", "P", true, false); @@ -159,7 +178,15 @@ export class ApiGenerator extends DefinitionGenerator { this.interface.end(); } - writeApi(method: string, url: string, path: string, query: string, headers: string, body: string, returnValue: string) { + writeApi( + method: string, + url: string, + path: string, + query: string, + headers: string, + body: string, + returnValue: string, + ) { this.write(this.indentStr); this.write(method + "("); this.write(`url: '${url}', options: `); From b9e558c03a0ca1baf4b47ecdd387382d38bf2d8f Mon Sep 17 00:00:00 2001 From: Axetroy Date: Sun, 20 Feb 2022 02:45:04 +0800 Subject: [PATCH 03/50] refactor --- Makefile | 10 +- index.mjs | 10 +- runtime/fetch.ts | 23 +- swagger2ts.ts | 11 +- test.ts | 348 +++++++++++++++++++- v3/index.ts | 3 - v3/{generator => interface}/api.ts | 3 - v3/{generator => interface}/definition.ts | 0 v3/{generator => interface}/generator.ts | 0 v3/{generator/index.ts => interface/mod.ts} | 2 +- v3/mod.ts | 3 + v3/{generateImplement.ts => runtime/mod.ts} | 4 +- 12 files changed, 388 insertions(+), 29 deletions(-) delete mode 100644 v3/index.ts rename v3/{generator => interface}/api.ts (98%) rename v3/{generator => interface}/definition.ts (100%) rename v3/{generator => interface}/generator.ts (100%) rename v3/{generator/index.ts => interface/mod.ts} (76%) create mode 100644 v3/mod.ts rename v3/{generateImplement.ts => runtime/mod.ts} (96%) diff --git a/Makefile b/Makefile index 8ebb688..21c1165 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ default: - @deno compile --unstable --lite --target x86_64-pc-windows-msvc -A mod.ts - @deno compile --unstable --lite --target x86_64-unknown-linux-gnu -A mod.ts - @deno compile --unstable --lite --target x86_64-apple-darwin -A mod.ts - @deno compile --unstable --lite --target aarch64-apple-darwin -A mod.ts + @deno compile --unstable --target x86_64-pc-windows-msvc -A mod.ts + @deno compile --unstable --target x86_64-unknown-linux-gnu -A mod.ts + @deno compile --unstable --target x86_64-apple-darwin -A mod.ts + @deno compile --unstable --target aarch64-apple-darwin -A mod.ts bundle: - @deno bundle ./v3/index.ts ./dist/v3.mjs + @deno bundle ./v3/mod.ts ./dist/v3.mjs format-ceck: @deno fmt --check diff --git a/index.mjs b/index.mjs index 5cec829..bef81b4 100644 --- a/index.mjs +++ b/index.mjs @@ -27,16 +27,16 @@ async function generate(target) { const sdkFilepath = new URL("./runtime/fetch.ts", import.meta.url); - const definition = v3.generateDefinition(swaggerJSONContent); - const implement = v3.generateImplement(swaggerJSONContent, new TextDecoder().decode(readFileSync(sdkFilepath)), domain); + const definition = v3.generateInterface(swaggerJSONContent); + const implement = v3.generateRuntime(swaggerJSONContent, new TextDecoder().decode(readFileSync(sdkFilepath)), domain); const result = `// Generate by swagger2ts - ${definition} +${definition} - ${implement} +${implement} `; - return result + return result.trim(); } export { generate }; \ No newline at end of file diff --git a/runtime/fetch.ts b/runtime/fetch.ts index f8cc8d0..48c0507 100644 --- a/runtime/fetch.ts +++ b/runtime/fetch.ts @@ -1,6 +1,6 @@ // swagger runtime. generate by swagger2ts interface IRuntimeHeaderMapString { - [key: string]: string; + [key: string]: string | string[]; } interface IRuntimeHeaderConfig { @@ -16,7 +16,7 @@ interface IRuntimeRequestCommonOptions extends Omit Promise = (config: IRuntimeRequestOptions, response: Response, data: T) => Promise; type IResponseInterceptorErrorFn = (config: IRuntimeRequestOptions, Error: RuntimeError) => Promise; -interface IRuntimeForm { +export interface IRuntimeForm { [key: string]: any; } -class RequestInterceptor implements IRequestInterceptor { +export class RequestInterceptor implements IRequestInterceptor { private _fns: IRequestInterceptorFn[] = []; public use(fn: IRequestInterceptorFn) { this._fns.push(fn); @@ -66,7 +66,7 @@ class RequestInterceptor implements IRequestInterceptor { } } -class ResponseInterceptor implements IResponseInterceptor { +export class ResponseInterceptor implements IResponseInterceptor { private _fnsSuccess: IResponseInterceptorSuccessFn[] = []; private _fnsError: IResponseInterceptorErrorFn[] = []; public use(successFn: IResponseInterceptorSuccessFn, errorFn: IResponseInterceptorErrorFn) { @@ -146,7 +146,9 @@ export interface IRuntime { domain: string; prefix: string; request(config: IRuntimeRequestOptions): Promise; + clone(): IRuntime; } + export class Runtime implements IRuntime { constructor(private _domain: string, private _prefix: string) { const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; @@ -264,7 +266,12 @@ export class Runtime implements IRuntime { for (const key in config.header) { const value = config.header[key]; if (value !== undefined) { - headers.set(key, value); + if (Array.isArray(value)) { + headers.delete(key); + value.forEach((v) => headers.append(key, v)); + } else { + headers.set(key, value); + } } } @@ -326,4 +333,8 @@ export class Runtime implements IRuntime { return this._responseInterceptor.runError(config, runtimeErr); }); } + + public clone() { + return new Runtime(this._domain, this._prefix); + } } diff --git a/swagger2ts.ts b/swagger2ts.ts index 42a2824..b10cf2e 100644 --- a/swagger2ts.ts +++ b/swagger2ts.ts @@ -1,5 +1,5 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; -import { generate as generateTsFile } from "./v3/generator/index.ts"; +import * as v3 from "./v3/mod.ts"; import "./runtime/fetch.ts"; // import to check type and download deps async function getDeps(url: string): Promise { @@ -63,9 +63,14 @@ export async function generate(target: string): Promise { throw new Error("can not found sdk file"); } - const definition = generateTsFile(swaggerJSONContent); + const definition = v3.generateInterface(swaggerJSONContent); + const implement = v3.generateRuntime(swaggerJSONContent, new TextDecoder().decode(Deno.readFileSync(sdkFilepath)), domain); - const result = `${definition}`; + const result = `// Generate by swagger2ts +${definition} + +${implement} + `; return result.trim(); } diff --git a/test.ts b/test.ts index 27a7e0f..8cdd923 100644 --- a/test.ts +++ b/test.ts @@ -1,3 +1,4 @@ +// Generate by swagger2ts export interface ClientVO { /** * @description 授权方式 @@ -83,4 +84,349 @@ export interface SwaggerApi { post(url: '/register/client', options: SwaggerOptions<{}, {redirectUri: string}, {}, unknown>): Promise post(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise delete(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise -} \ No newline at end of file +} + + +// swagger runtime. generate by swagger2ts +interface IRuntimeHeaderMapString { + [key: string]: string | string[]; +} + +interface IRuntimeHeaderConfig { + common: IRuntimeHeaderMapString; + [method: string]: IRuntimeHeaderMapString; +} + +interface IRuntimeRequestCommonOptions extends Omit { + path?: { + [key: string]: string; + }; + query?: { + [key: string]: string; + }; + header?: { + [key: string]: string | string[]; + }; + body?: any; + timeout?: number; +} + +interface IRuntimeRequestOptions extends IRuntimeRequestCommonOptions { + url: string; + method: Uppercase; +} + +interface IRequestInterceptor { + use(fn: IRequestInterceptorFn): IInterceptorCancelFn; +} + +interface IResponseInterceptor { + use(success: IResponseInterceptorSuccessFn, error: IResponseInterceptorErrorFn): IInterceptorCancelFn; +} + +type IInterceptorCancelFn = () => void; +type IRequestInterceptorFn = (config: IRuntimeRequestOptions) => Promise; +type IResponseInterceptorSuccessFn = (config: IRuntimeRequestOptions, response: Response, data: T) => Promise; +type IResponseInterceptorErrorFn = (config: IRuntimeRequestOptions, Error: RuntimeError) => Promise; + +export interface IRuntimeForm { + [key: string]: any; +} +export class RequestInterceptor implements IRequestInterceptor { + private _fns: IRequestInterceptorFn[] = []; + public use(fn: IRequestInterceptorFn) { + this._fns.push(fn); + + return () => { + const index = this._fns.findIndex((v) => v === fn); + + if (index > -1) { + this._fns.splice(index, 1); + } + }; + } + + async run(config: IRuntimeRequestOptions): Promise { + for (const fn of this._fns) { + config = await fn(config); + } + + return config; + } +} + +export class ResponseInterceptor implements IResponseInterceptor { + private _fnsSuccess: IResponseInterceptorSuccessFn[] = []; + private _fnsError: IResponseInterceptorErrorFn[] = []; + public use(successFn: IResponseInterceptorSuccessFn, errorFn: IResponseInterceptorErrorFn) { + this._fnsSuccess.push(successFn); + this._fnsError.push(errorFn); + + return () => { + const successIndex = this._fnsSuccess.findIndex((v) => v === successFn); + const errorIndex = this._fnsError.findIndex((v) => v === errorFn); + + if (successIndex > -1) { + this._fnsSuccess.splice(successIndex, 1); + } + + if (errorIndex > -1) { + this._fnsError.splice(errorIndex, 1); + } + }; + } + + async runSuccess(config: IRuntimeRequestOptions, response: Response, data: T): Promise { + for (const fn of this._fnsSuccess) { + data = await fn(config, response, data); + } + + return data; + } + + async runError(config: IRuntimeRequestOptions, err: RuntimeError): Promise { + let res!: T; + + for (const fn of this._fnsError) { + res = await fn(config, err); + } + + return res; + } +} + +export class RuntimeForm { + constructor(private _form: T) {} + public formData(): FormData { + const form = new FormData(); + + for (const key in this._form) { + if (this._form[key] !== undefined) { + form.append(key, this._form[key]); + } + } + + return form; + } +} + +export class RuntimeError extends Error { + constructor(message: string, private _resp?: Response) { + super(message); + } + + public get response(): Response | undefined { + return this._resp; + } + + static fromResponse(resp: Response) { + return new RuntimeError(resp.statusText, resp); + } + + static fromError(err: Error) { + return new RuntimeError(err.message || "unknown error: " + err); + } +} + +export interface IRuntime { + readonly interceptors: { readonly request: IRequestInterceptor; readonly response: IResponseInterceptor }; + readonly defaults: { readonly timeout: number; readonly headers: IRuntimeHeaderConfig }; + readonly baseURL: string; + domain: string; + prefix: string; + request(config: IRuntimeRequestOptions): Promise; + clone(): IRuntime; +} + +export class Runtime implements IRuntime { + constructor(private _domain: string, private _prefix: string) { + const methods = ["get", "post", "delete", "put", "head", "options", "trace", "patch"]; + + for (const method of methods) { + // @ts-ignore ignore error + this[method] = (url: string, config?: IRuntimeRequestCommonOptions = {}) => { + return this.request({ + method: method.toUpperCase(), + url, + ...config, + }); + }; + } + } + + private _requestInterceptor = new RequestInterceptor(); + private _responseInterceptor = new ResponseInterceptor(); + + private _defaults = { + timeout: 60 * 1000, // 60s, + headers: { + common: { + "Content-Type": "application/json", + }, + } as IRuntimeHeaderConfig, + }; + + public get interceptors() { + const self = this; + return { + get request() { + return self._requestInterceptor as IRequestInterceptor; + }, + get response() { + return self._responseInterceptor as IResponseInterceptor; + }, + }; + } + + public get defaults() { + return this._defaults; + } + + private _timeout(ms: number, promise: Promise) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new RuntimeError(`timeout of ${ms}ms`)); + }, ms); + + promise + .then((value) => { + clearTimeout(timer); + resolve(value); + }) + .catch((reason) => { + clearTimeout(timer); + reject(reason); + }); + }); + } + + public get baseURL(): string { + const baseUrl = this._domain.replace(/\/$/, "") + (!/^\//.test(this._prefix) ? "/" : "") + this._prefix; + + return baseUrl.replace(/\/$/, ""); + } + + public set domain(domain: string) { + this._domain = domain; + } + + public set prefix(prefix: string) { + this._prefix = prefix; + } + + public async request(config: IRuntimeRequestOptions): Promise { + const url = new URL(this.baseURL + config.url); + config.header = config.header || {}; + + const defaults = this.defaults; + + // set default header + for (const key in defaults.headers.common) { + config.header[key] = defaults.headers.common[key]; + } + + // set header for this method + for (const key in defaults.headers[config.method] || {}) { + config.header[key] = defaults.headers[config.method][key]; + } + + if (config.query) { + for (const key in config.query) { + const value = config.query[key]; + if (value !== undefined) { + url.searchParams.append(key, value); + } + } + } + + if (config.path) { + for (const key in config.path) { + const t1 = encodeURI("{"); + const t2 = encodeURI("}"); + const reg = new RegExp(`${t1}${key}${t2}`, "g"); + url.pathname = url.pathname.replace(reg, config.path[key]); + } + } + + config = await this._requestInterceptor.run(config); + + const headers = new Headers(); + + for (const key in config.header) { + const value = config.header[key]; + if (value !== undefined) { + if (Array.isArray(value)) { + headers.delete(key); + value.forEach((v) => headers.append(key, v)); + } else { + headers.set(key, value); + } + } + } + + const timeout = config.timeout || defaults.timeout; + + const body = + config.body === undefined + ? undefined + : ["GET", "HEAD"].indexOf(config.method.toUpperCase()) > -1 + ? undefined + : config.body instanceof RuntimeForm + ? config.body.formData() + : config.body instanceof Blob + ? config.body + : typeof config.body === "object" + ? JSON.stringify(config.body) + : config.body.toString(); + + const exec = () => + fetch(url.toString(), { + method: config.method, + body: body, + headers: headers, + + // common options + cache: config.cache, + credentials: config.credentials, + integrity: config.integrity, + keepalive: config.keepalive, + mode: config.mode, + redirect: config.redirect, + referrer: config.referrer, + referrerPolicy: config.referrerPolicy, + signal: config.signal, + window: config.window, + }); + + return (timeout ? this._timeout(timeout, exec()) : exec()) + .then(async (resp) => { + if (!resp.ok) return Promise.reject(RuntimeError.fromResponse(resp)); + const contentType = resp.headers.get("content-type"); + switch (contentType) { + case "application/json": + return { data: await resp.json(), resp }; + case "application/x-www-form-urlencoded": + return { data: await resp.formData(), resp }; + case "application/octet-stream": + return { data: await resp.blob(), resp }; + default: + return { data: await resp.text(), resp }; + } + }) + .then(({ data, resp }) => { + return this._responseInterceptor.runSuccess(config, resp, data); + }) + .catch((err) => { + const runtimeErr = err instanceof RuntimeError ? err : err instanceof Error ? RuntimeError.fromError(err) : new RuntimeError(err + ""); + + return this._responseInterceptor.runError(config, runtimeErr); + }); + } + + public clone() { + return new Runtime(this._domain, this._prefix); + } +} + +export type IClient = SwaggerApi & IRuntime +export const unknownApi = new Runtime("http://localhost", "/") as unknown as IClient \ No newline at end of file diff --git a/v3/index.ts b/v3/index.ts deleted file mode 100644 index ef66e6c..0000000 --- a/v3/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./generator/index.ts"; -export * from "./generateImplement.ts"; -export * from "./types.ts"; diff --git a/v3/generator/api.ts b/v3/interface/api.ts similarity index 98% rename from v3/generator/api.ts rename to v3/interface/api.ts index 50d8f65..79690c3 100644 --- a/v3/generator/api.ts +++ b/v3/interface/api.ts @@ -1,13 +1,10 @@ import { - IOperationObject, IParameterObject, IReferenceObject, IRequestBodyObject, IResponseObject, IResponsesObject, - ISchemaObject, isReferenceObject, - isRequestBodyObject, ISwagger, } from "../types.ts"; import { ApiGenerator, DefinitionGenerator } from "./generator.ts"; diff --git a/v3/generator/definition.ts b/v3/interface/definition.ts similarity index 100% rename from v3/generator/definition.ts rename to v3/interface/definition.ts diff --git a/v3/generator/generator.ts b/v3/interface/generator.ts similarity index 100% rename from v3/generator/generator.ts rename to v3/interface/generator.ts diff --git a/v3/generator/index.ts b/v3/interface/mod.ts similarity index 76% rename from v3/generator/index.ts rename to v3/interface/mod.ts index b1d2674..efabbe3 100644 --- a/v3/generator/index.ts +++ b/v3/interface/mod.ts @@ -1,7 +1,7 @@ import { generateDefinition } from "./definition.ts"; import { generateApi } from "./api.ts"; -export function generate(content: string): string { +export function generateInterface(content: string): string { const output = [generateDefinition(content), generateApi(content)]; return output.join("\n\n"); diff --git a/v3/mod.ts b/v3/mod.ts new file mode 100644 index 0000000..a22183c --- /dev/null +++ b/v3/mod.ts @@ -0,0 +1,3 @@ +export * from "./interface/mod.ts"; +export * from "./runtime/mod.ts"; +export * from "./types.ts"; diff --git a/v3/generateImplement.ts b/v3/runtime/mod.ts similarity index 96% rename from v3/generateImplement.ts rename to v3/runtime/mod.ts index 8ca29b4..d6333ab 100644 --- a/v3/generateImplement.ts +++ b/v3/runtime/mod.ts @@ -1,5 +1,5 @@ import { camelCase } from "https://deno.land/x/case@v2.1.0/mod.ts"; -import { IServerObject, ISwagger } from "./types.ts"; +import { IServerObject, ISwagger } from "../types.ts"; function getServerUrl(serverInfo: IServerObject): URL { let urlStr = serverInfo.url; @@ -56,7 +56,7 @@ function path2apiName(serverInfo: IServerObject): string { } // generate HTTP implement for swagger api -export function generateImplement( +export function generateRuntime( content: string, sdkContent: string, domain: string, From a82901ed0faba21cb6437de512ba83247220e9dd Mon Sep 17 00:00:00 2001 From: Axetroy Date: Sun, 20 Feb 2022 03:02:09 +0800 Subject: [PATCH 04/50] update --- test.ts | 24 ++++++++++++++++++++++++ v3/interface/api.ts | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/test.ts b/test.ts index 8cdd923..cebddbd 100644 --- a/test.ts +++ b/test.ts @@ -79,10 +79,34 @@ export interface SwaggerOptions

): Promise + /** + * @description oauth2.0 应用接入信息查询 + * @summary 查询 + * @tags 应用接入管理 + */ get(url: '/register/client', options: SwaggerOptions<{}, {size: number}, {}, unknown>): Promise + /** + * @description oauth2.0 应用接入提交信息 + * @summary 新增 + * @tags 应用接入管理 + */ post(url: '/register/client', options: SwaggerOptions<{}, {redirectUri: string}, {}, unknown>): Promise + /** + * @description oauth2.0 应用接入信息变更 + * @summary 修改 + * @tags 应用接入管理 + */ post(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise + /** + * @description oauth2.0 应用接入信息删除 + * @summary 删除 + * @tags 应用接入管理 + */ delete(url: '/register/client/{id}', options: RequireKeys, 'path'>): Promise } diff --git a/v3/interface/api.ts b/v3/interface/api.ts index 79690c3..ef86b87 100644 --- a/v3/interface/api.ts +++ b/v3/interface/api.ts @@ -1,4 +1,5 @@ import { + IOperationObject, IParameterObject, IReferenceObject, IRequestBodyObject, @@ -118,6 +119,25 @@ function generatePath(swagger: ISwagger): string { responseBody ? responseBody[200] || responseBody.default : undefined, ); + const comment = g.createCommentBlock(); + + // @ts-expect-error ignore error + const op = pathObject[method] as IOperationObject; + + if (op.description || op.summary) { + comment.start(); + if (op.description) { + comment.write("description", op.description); + } + if (op.summary) { + comment.write("summary", op.summary); + } + if (op.tags && op.tags.length) { + comment.write("tag", op.tags.join(", ")); + } + comment.end(); + } + g.writeApi( method, path, From 41d65b9f2f523cfb661eb10fcc3ea38b734630e8 Mon Sep 17 00:00:00 2001 From: Axetroy Date: Sun, 20 Feb 2022 03:06:08 +0800 Subject: [PATCH 05/50] update --- __test__/3.0/LDFCore.ts | 2981 +++++++++++++++++++---------- __test__/3.0/Petstore.ts | 240 ++- __test__/3.0/api-auth.ts | 498 ++++- __test__/3.0/api-upms.ts | 427 +++-- __test__/3.0/api-with-examples.ts | 61 +- __test__/3.0/callback-example.ts | 60 +- __test__/3.0/link-example.ts | 103 +- __test__/3.0/petstore-expanded.ts | 96 +- __test__/3.0/uspto.ts | 117 +- __test__/3.1/webhook-example.ts | 59 +- test.ts | 478 +---- 11 files changed, 3118 insertions(+), 2002 deletions(-) diff --git a/__test__/3.0/LDFCore.ts b/__test__/3.0/LDFCore.ts index f505e1a..2d279ef 100644 --- a/__test__/3.0/LDFCore.ts +++ b/__test__/3.0/LDFCore.ts @@ -1,39 +1,52 @@ // Generate by swagger2ts -/* default type by generation start */ -interface MapAny { - [key: string]: any -} -interface MapString { - [key: string]: string | undefined -} - -type IDefaultOptions = Omit & { timeout?: number } -/* default type by generation end */ - -export type AccountType = 0 | 1 | 2 | -1 | undefined +export type AccountType = 0 | 1 | 2 | -1 -export type PlatformEnum = 0 | 1 | 2 | 3 | 4 | 5 | undefined +export type PlatformEnum = 0 | 1 | 2 | 3 | 4 | 5 /** * @description 登录模型 */ export interface LoginModel { - clientId?: string | null /* 客户端 */ - userName?: string | null /* 用户名 */ - password?: string | null /* 密码 */ + /** + * @description 客户端 + */ + clientId?: string | null + /** + * @description 用户名 + */ + userName?: string | null + /** + * @description 密码 + */ + password?: string | null accountType?: AccountType platform?: PlatformEnum - pictureId?: string | null /* 验证码图片编号 */ - code?: string | null /* 验证码 */ + /** + * @description 验证码图片编号 + */ + pictureId?: string | null + /** + * @description 验证码 + */ + code?: string | null } /** * @description 用户登录返回信息Dto */ export interface LoginUserOutput { - userId?: string | null /* 用户Id */ - userName?: string | null /* 用户名称 */ - realName?: string | null /* 姓名(昵称) */ + /** + * @description 用户Id + */ + userId?: string | null + /** + * @description 用户名称 + */ + userName?: string | null + /** + * @description 姓名(昵称) + */ + realName?: string | null } export interface LoginUserOutputAuthResut { @@ -65,63 +78,168 @@ export interface IResultModel { /** * @description 客户端枚举 */ -export type ClientEnums = 1 | 2 | undefined /* 客户端枚举 */ +export type ClientEnums = 1 | 2 export interface MenuOutput { - id?: string | null /* 标识 */ - name?: string | null /* 名称 */ - link?: string | null /* 链接、路由 */ - remarks?: string | null /* 备注 */ - show?: boolean /* 是否显示 */ - sort?: number /* 排序码 */ - target?: number /* 打开方式 */ - type?: number /* 菜单类型 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 链接、路由 + */ + link?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 是否显示 + */ + show?: boolean + /** + * @description 排序码 + */ + sort?: number + /** + * @description 打开方式 + */ + target?: number + /** + * @description 菜单类型 + */ + type?: number client?: ClientEnums - icon?: string | null /* 图标 */ - children?: Array /* 子菜单 */ - component?: string | null /* 组件 */ - routeParams?: string | null /* 路由参数 */ + /** + * @description 图标 + */ + icon?: string | null + /** + * @description 子菜单 + */ + children?: Array | null + /** + * @description 组件 + */ + component?: string | null + /** + * @description 路由参数 + */ + routeParams?: string | null } /** * @description 角色Dto */ export interface RolesDto { - id?: string | null /* 标识 */ - roleName?: string | null /* 角色名/组名 */ - code?: string | null /* 编码 */ - remarks?: string | null /* 备注 */ - sortId?: number /* 排序值 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 角色名/组名 + */ + roleName?: string | null + /** + * @description 编码 + */ + code?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序值 + */ + sortId?: number } /** * @description 用户信息 */ export interface InitAppUserInfoOutput { - id?: string | null /* 主键 */ - userId?: string | null /* 用户编号 */ - userName?: string | null /* 用户名 */ - email?: string | null /* 邮箱 */ - phoneNumber?: string | null /* 手机号 */ - headPortraitCode?: string | null /* 头像url */ - realName?: string | null /* 真实姓名 */ - sex?: number /* 性别(1.男 2.女) */ - status?: number /* 状态 */ - contact?: string | null /* 办公电话 */ - rolesId?: Array /* 角色id */ - rolesName?: Array /* 角色id */ - userType?: number /* 用户类型 */ - idCard?: string | null /* 身份证号 */ - roles?: Array /* 角色 */ - enterpriseName?: string | null /* 机构名称 */ + /** + * @description 主键 + */ + id?: string | null + /** + * @description 用户编号 + */ + userId?: string | null + /** + * @description 用户名 + */ + userName?: string | null + /** + * @description 邮箱 + */ + email?: string | null + /** + * @description 手机号 + */ + phoneNumber?: string | null + /** + * @description 头像url + */ + headPortraitCode?: string | null + /** + * @description 真实姓名 + */ + realName?: string | null + /** + * @description 性别(1.男 2.女) + */ + sex?: number + /** + * @description 状态 + */ + status?: number + /** + * @description 办公电话 + */ + contact?: string | null + /** + * @description 角色id + */ + rolesId?: Array | null + /** + * @description 角色id + */ + rolesName?: Array | null + /** + * @description 用户类型 + */ + userType?: number + /** + * @description 身份证号 + */ + idCard?: string | null + /** + * @description 角色 + */ + roles?: Array | null + /** + * @description 机构名称 + */ + enterpriseName?: string | null } /** * @description 初始化客户端返回Dto */ export interface InitAppOutput { - menus?: Array /* 菜单 */ - buttons?: Array /* 按钮权限点 */ + /** + * @description 菜单 + */ + menus?: Array | null + /** + * @description 按钮权限点 + */ + buttons?: Array | null userInfo?: InitAppUserInfoOutput } @@ -133,14 +251,38 @@ export interface InitAppOutputIResultModel { * @description APP版本Dto */ export interface AppVersionDto { - id?: string | null /* Id */ - appId?: string | null /* app_id */ - describe?: string | null /* 更新描述 */ - fileCode?: string | null /* 文件编码 */ - showTips?: boolean /* 是否显示更新提示 */ - version?: string | null /* 版本号 */ - type?: number /* 类型(安卓/IOS) */ - creationTime?: string /* 版本更新时间(前端无需赋值) */ + /** + * @description Id + */ + id?: string | null + /** + * @description app_id + */ + appId?: string | null + /** + * @description 更新描述 + */ + describe?: string | null + /** + * @description 文件编码 + */ + fileCode?: string | null + /** + * @description 是否显示更新提示 + */ + showTips?: boolean + /** + * @description 版本号 + */ + version?: string | null + /** + * @description 类型(安卓/IOS) + */ + type?: number + /** + * @description 版本更新时间(前端无需赋值) + */ + creationTime?: string } export interface AppVersionDtoPageList { @@ -148,7 +290,7 @@ export interface AppVersionDtoPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface AppVersionDtoPageListIResultModel { @@ -159,13 +301,34 @@ export interface AppVersionDtoPageListIResultModel { * @description AreaListOutput */ export interface AreaListOutput { - id?: string | null /* 编号 */ - name?: string | null /* 名称 */ - code?: string | null /* 编码 */ - parentId?: string | null /* 父级编号 */ - pinYin?: string | null /* 拼音简码 */ - level?: number /* 级数 */ - type?: boolean /* 启用 */ + /** + * @description 编号 + */ + id?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 编码 + */ + code?: string | null + /** + * @description 父级编号 + */ + parentId?: string | null + /** + * @description 拼音简码 + */ + pinYin?: string | null + /** + * @description 级数 + */ + level?: number + /** + * @description 启用 + */ + type?: boolean } export interface AreaListOutputPageList { @@ -173,7 +336,7 @@ export interface AreaListOutputPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface AreaListOutputPageListIResultModel { @@ -184,18 +347,54 @@ export interface AreaListOutputPageListIResultModel { * @description 区域Dto */ export interface AreaDto { - id?: string | null /* 编号 */ - code?: string | null /* 区域编码 */ - name?: string | null /* 区域名称 */ - enabled?: boolean /* 是否启用 */ - sortId?: number | null /* 排序号 */ - pinYin?: string | null /* 拼音简码 */ - fullPinYin?: string | null /* 拼音 */ - isLeaf?: boolean /* 是否叶子节点 */ - level?: number /* 级数 */ - parentId?: string | null /* 父编号 */ - path?: string | null /* 路径 */ - type?: boolean /* 是否启用 */ + /** + * @description 编号 + */ + id?: string | null + /** + * @description 区域编码 + */ + code?: string | null + /** + * @description 区域名称 + */ + name?: string | null + /** + * @description 是否启用 + */ + enabled?: boolean + /** + * @description 排序号 + */ + sortId?: number | null + /** + * @description 拼音简码 + */ + pinYin?: string | null + /** + * @description 拼音 + */ + fullPinYin?: string | null + /** + * @description 是否叶子节点 + */ + isLeaf?: boolean + /** + * @description 级数 + */ + level?: number + /** + * @description 父编号 + */ + parentId?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 是否启用 + */ + type?: boolean } export interface RemoveModel { @@ -209,10 +408,10 @@ export interface StringTreeOptionResultModel { disabled?: boolean isLeaf?: boolean level?: number | null - data?: any + data?: unknown | null id?: string | null pid?: string | null - children?: Array + children?: Array | null sortId?: number } @@ -222,27 +421,27 @@ export interface TreeOptionResultModel { disabled?: boolean isLeaf?: boolean level?: number | null - data?: any + data?: unknown | null id?: string | null pid?: string | null - children?: Array + children?: Array | null sortId?: number } export interface TreeOptionResultModelListIResultModel { - data?: Array + data?: Array | null } export interface StringOptionResultModel { label?: string | null value?: string | null disabled?: boolean - data?: any + data?: unknown | null sortId?: number } export interface StringOptionResultModelListIResultModel { - data?: Array + data?: Array | null } export interface AuditListOutput { @@ -263,7 +462,7 @@ export interface AuditListOutputPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface AuditListOutputPageListIResultModel { @@ -274,15 +473,42 @@ export interface AuditListOutputPageListIResultModel { * @description 数据字典Dto */ export interface DictionaryDto { - id?: string | null /* 标识 */ - extend?: string | null /* 扩展 */ - name?: string | null /* 名称 */ - parentId?: string | null /* 父级id */ - remark?: string | null /* 备注 */ - sortId?: number | null /* 排序 */ - typeId?: string | null /* 数据字典类型 */ - value?: string | null /* 枚举值 */ - isEnable?: boolean /* 启用 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 扩展 + */ + extend?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 父级id + */ + parentId?: string | null + /** + * @description 备注 + */ + remark?: string | null + /** + * @description 排序 + */ + sortId?: number | null + /** + * @description 数据字典类型 + */ + typeId?: string | null + /** + * @description 枚举值 + */ + value?: string | null + /** + * @description 启用 + */ + isEnable?: boolean } /** @@ -297,9 +523,15 @@ export interface DictionaryType { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - code?: string | null /* 唯一编码 */ - name?: string | null /* 名称 */ - dictionarys?: Array + /** + * @description 唯一编码 + */ + code?: string | null + /** + * @description 名称 + */ + name?: string | null + dictionarys?: Array | null } /** @@ -314,17 +546,41 @@ export interface Dictionary { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - typeId?: string | null /* 数据字典类型 */ - isEnable?: boolean /* 启用 */ - parentId?: string | null /* 父级Id */ - extend?: string | null /* 扩展 */ - name?: string | null /* 名称 */ - remark?: string | null /* 备注 */ - sortId?: number | null /* 排序 */ - value?: string | null /* 枚举值 */ + /** + * @description 数据字典类型 + */ + typeId?: string | null + /** + * @description 启用 + */ + isEnable?: boolean + /** + * @description 父级Id + */ + parentId?: string | null + /** + * @description 扩展 + */ + extend?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 备注 + */ + remark?: string | null + /** + * @description 排序 + */ + sortId?: number | null + /** + * @description 枚举值 + */ + value?: string | null pDictionary?: Dictionary dictionaryType?: DictionaryType - dictionarys?: Array + dictionarys?: Array | null } export interface DictionaryIResultModel { @@ -336,7 +592,7 @@ export interface DictionaryDtoPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface DictionaryDtoPageListIResultModel { @@ -349,7 +605,7 @@ export interface StringRemoveModel { } export interface DictionaryListIResultModel { - data?: Array + data?: Array | null } export interface StringTreeOptionResultModelDto { @@ -358,25 +614,40 @@ export interface StringTreeOptionResultModelDto { disabled?: boolean isLeaf?: boolean level?: number | null - data?: any + data?: unknown | null id?: string | null pid?: string | null - children?: Array /* 重写隐藏父类Children */ + /** + * @description 重写隐藏父类Children + */ + children?: Array | null sortId?: number - extend?: string | null /* 扩展 */ + /** + * @description 扩展 + */ + extend?: string | null } export interface StringTreeOptionResultModelDtoListIResultModel { - data?: Array + data?: Array | null } /** * @description 数据字典类型Dto */ export interface DictionaryTypeDto { - id?: string | null /* 标识 */ - code?: string | null /* 唯一编码 */ - name?: string | null /* 名称 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 唯一编码 + */ + code?: string | null + /** + * @description 名称 + */ + name?: string | null } export interface DictionaryTypeIResultModel { @@ -388,7 +659,7 @@ export interface DictionaryTypeDtoPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface DictionaryTypeDtoPageListIResultModel { @@ -399,23 +670,74 @@ export interface DictionaryTypeDtoPageListIResultModel { * @description 企业(enterprise)分页列表输出模型 */ export interface EnterpriseListOutput { - id?: string | null /* 机构标识 */ - areaCode?: string | null /* 行政区代码 */ - areaName?: string | null /* 行政区名称 */ - code?: string | null /* 机构编码 */ - contactNumber?: string | null /* 联系人电话 */ - contactPerson?: string | null /* 联系人 */ - isEnabled?: boolean /* 是否启用 */ - legalRepresentative?: string | null /* 法人代表 */ - legalRepresentativeNumber?: string | null /* 法人代表电话 */ - level?: number /* 级数 */ - name?: string | null /* 机构名称 */ - path?: string | null /* 路径 */ - pathText?: string | null /* 中文全路径 */ - pinYin?: string | null /* 拼音简码 */ - simpleName?: string | null /* 机构简称(用于上报部级数据展示名称) */ - sortId?: number /* 排序号 */ - type?: number /* 企业类型(1:企业, 2:个人) */ + /** + * @description 机构标识 + */ + id?: string | null + /** + * @description 行政区代码 + */ + areaCode?: string | null + /** + * @description 行政区名称 + */ + areaName?: string | null + /** + * @description 机构编码 + */ + code?: string | null + /** + * @description 联系人电话 + */ + contactNumber?: string | null + /** + * @description 联系人 + */ + contactPerson?: string | null + /** + * @description 是否启用 + */ + isEnabled?: boolean + /** + * @description 法人代表 + */ + legalRepresentative?: string | null + /** + * @description 法人代表电话 + */ + legalRepresentativeNumber?: string | null + /** + * @description 级数 + */ + level?: number + /** + * @description 机构名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 中文全路径 + */ + pathText?: string | null + /** + * @description 拼音简码 + */ + pinYin?: string | null + /** + * @description 机构简称(用于上报部级数据展示名称) + */ + simpleName?: string | null + /** + * @description 排序号 + */ + sortId?: number + /** + * @description 企业类型(1:企业, 2:个人) + */ + type?: number } export interface EnterpriseListOutputPageList { @@ -423,7 +745,7 @@ export interface EnterpriseListOutputPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface EnterpriseListOutputPageListIResultModel { @@ -434,24 +756,75 @@ export interface EnterpriseListOutputPageListIResultModel { * @description 企业(enterprise)输出模型 */ export interface EnterpriseOutput { - id?: string | null /* 机构标识 */ - areaCode?: string | null /* 行政区代码 */ - areaName?: string | null /* 行政区名称 */ - code?: string | null /* 机构编码 */ - contactNumber?: string | null /* 联系人电话 */ - contactPerson?: string | null /* 联系人 */ - isEnabled?: boolean /* 是否启用 */ - legalRepresentative?: string | null /* 法人代表 */ - legalRepresentativeNumber?: string | null /* 法人代表电话 */ - level?: number /* 级数 */ - name?: string | null /* 机构名称 */ - path?: string | null /* 路径 */ - pathText?: string | null /* 中文全路径 */ - pinYin?: string | null /* 拼音简码 */ - simpleName?: string | null /* 机构简称(用于上报部级数据展示名称) */ - sortId?: number /* 排序号 */ - type?: number /* 企业类型(1:企业, 2:个人) */ -} + /** + * @description 机构标识 + */ + id?: string | null + /** + * @description 行政区代码 + */ + areaCode?: string | null + /** + * @description 行政区名称 + */ + areaName?: string | null + /** + * @description 机构编码 + */ + code?: string | null + /** + * @description 联系人电话 + */ + contactNumber?: string | null + /** + * @description 联系人 + */ + contactPerson?: string | null + /** + * @description 是否启用 + */ + isEnabled?: boolean + /** + * @description 法人代表 + */ + legalRepresentative?: string | null + /** + * @description 法人代表电话 + */ + legalRepresentativeNumber?: string | null + /** + * @description 级数 + */ + level?: number + /** + * @description 机构名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 中文全路径 + */ + pathText?: string | null + /** + * @description 拼音简码 + */ + pinYin?: string | null + /** + * @description 机构简称(用于上报部级数据展示名称) + */ + simpleName?: string | null + /** + * @description 排序号 + */ + sortId?: number + /** + * @description 企业类型(1:企业, 2:个人) + */ + type?: number +} export interface EnterpriseOutputIResultModel { data?: EnterpriseOutput @@ -461,56 +834,140 @@ export interface EnterpriseOutputIResultModel { * @description 企业(enterprise)输入模型 */ export interface EnterpriseInput { - id?: string | null /* 机构标识 */ - parentId?: string | null /* 父级Id */ - areaCode?: string | null /* 行政区代码 */ - areaName?: string | null /* 行政区名称 */ - code?: string | null /* 机构编码 */ - contactNumber?: string | null /* 联系人电话 */ - contactPerson?: string | null /* 联系人 */ - isEnabled?: boolean /* 是否启用 */ - legalRepresentative?: string | null /* 法人代表 */ - legalRepresentativeNumber?: string | null /* 法人代表电话 */ - name?: string | null /* 机构名称 */ - sortId?: number /* 排序号 */ - type?: number /* 企业类型(1:企业, 2:个人) */ + /** + * @description 机构标识 + */ + id?: string | null + /** + * @description 父级Id + */ + parentId?: string | null + /** + * @description 行政区代码 + */ + areaCode?: string | null + /** + * @description 行政区名称 + */ + areaName?: string | null + /** + * @description 机构编码 + */ + code?: string | null + /** + * @description 联系人电话 + */ + contactNumber?: string | null + /** + * @description 联系人 + */ + contactPerson?: string | null + /** + * @description 是否启用 + */ + isEnabled?: boolean + /** + * @description 法人代表 + */ + legalRepresentative?: string | null + /** + * @description 法人代表电话 + */ + legalRepresentativeNumber?: string | null + /** + * @description 机构名称 + */ + name?: string | null + /** + * @description 排序号 + */ + sortId?: number + /** + * @description 企业类型(1:企业, 2:个人) + */ + type?: number } /** * @description 枚举 */ -export type FaqCategoryOutputType = 0 | 1 | undefined /* 枚举 */ +export type FaqCategoryOutputType = 0 | 1 /** * @description 帮助分类分页列表输出模型 */ export interface FaqCategoryTreeOutput { - id?: string | null /* 编号 */ - parentId?: string | null /* 父编号 */ - icon?: string | null /* 图标 */ - name?: string | null /* 名称 */ - path?: string | null /* 路径 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ + /** + * @description 编号 + */ + id?: string | null + /** + * @description 父编号 + */ + parentId?: string | null + /** + * @description 图标 + */ + icon?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number type?: FaqCategoryOutputType - children?: Array /* 子级 */ + /** + * @description 子级 + */ + children?: Array | null } export interface FaqCategoryTreeOutputListIResultModel { - data?: Array + data?: Array | null } /** * @description 帮助分类输出模型 */ export interface FaqCategoryOutput { - id?: string | null /* 编号 */ - parentId?: string | null /* 父编号 */ - icon?: string | null /* 图标 */ - name?: string | null /* 名称 */ - path?: string | null /* 路径 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ + /** + * @description 编号 + */ + id?: string | null + /** + * @description 父编号 + */ + parentId?: string | null + /** + * @description 图标 + */ + icon?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number } export interface FaqCategoryOutputIResultModel { @@ -521,26 +978,68 @@ export interface FaqCategoryOutputIResultModel { * @description 帮助分类输入模型 */ export interface FaqCategoryInput { - id?: string | null /* 编号 */ - parentId?: string | null /* 父编号 */ - icon?: string | null /* 图标 */ - name?: string | null /* 名称 */ - path?: string | null /* 路径 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ + /** + * @description 编号 + */ + id?: string | null + /** + * @description 父编号 + */ + parentId?: string | null + /** + * @description 图标 + */ + icon?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number } /** * @description 帮助内容分页列表输出模型 */ export interface FaqContentListOutput { - id?: string | null /* 标识 */ - categoryId?: string | null /* 分类标识 */ - content?: string | null /* 内容 */ - readingQuantity?: number /* 阅读量 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ - title?: string | null /* 标题 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 分类标识 + */ + categoryId?: string | null + /** + * @description 内容 + */ + content?: string | null + /** + * @description 阅读量 + */ + readingQuantity?: number + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number + /** + * @description 标题 + */ + title?: string | null } export interface FaqContentListOutputPageList { @@ -548,7 +1047,7 @@ export interface FaqContentListOutputPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface FaqContentListOutputPageListIResultModel { @@ -559,13 +1058,34 @@ export interface FaqContentListOutputPageListIResultModel { * @description 帮助内容输出模型 */ export interface FaqContentOutput { - id?: string | null /* 标识 */ - categoryId?: string | null /* 分类标识 */ - content?: string | null /* 内容 */ - readingQuantity?: number /* 阅读量 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ - title?: string | null /* 标题 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 分类标识 + */ + categoryId?: string | null + /** + * @description 内容 + */ + content?: string | null + /** + * @description 阅读量 + */ + readingQuantity?: number + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number + /** + * @description 标题 + */ + title?: string | null } export interface FaqContentOutputIResultModel { @@ -576,30 +1096,84 @@ export interface FaqContentOutputIResultModel { * @description 帮助内容输入模型 */ export interface FaqContentInput { - id?: string | null /* 标识 */ - categoryId?: string | null /* 分类标识 */ - content?: string | null /* 内容 */ - readingQuantity?: number /* 阅读量 */ - remarks?: string | null /* 备注 */ - sort?: number /* 排序码 */ - title?: string | null /* 标题 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 分类标识 + */ + categoryId?: string | null + /** + * @description 内容 + */ + content?: string | null + /** + * @description 阅读量 + */ + readingQuantity?: number + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 排序码 + */ + sort?: number + /** + * @description 标题 + */ + title?: string | null } /** * @description LoginLogListOutput */ export interface LoginLogListOutput { - id?: string | null /* Id */ - ip?: string | null /* 登录IP */ - browser?: string | null /* 浏览器 */ - os?: string | null /* 操作系统 */ - device?: string | null /* 设备 */ - elapsedMilliseconds?: number /* 耗时(毫秒) */ - status?: boolean /* 登录状态(true成功,false失败) */ - msg?: string | null /* 返回消息 */ - userName?: string | null /* 用户名 */ - creationTime?: string /* 创建时间 */ - realName?: string | null /* 姓名(昵称) */ + /** + * @description Id + */ + id?: string | null + /** + * @description 登录IP + */ + ip?: string | null + /** + * @description 浏览器 + */ + browser?: string | null + /** + * @description 操作系统 + */ + os?: string | null + /** + * @description 设备 + */ + device?: string | null + /** + * @description 耗时(毫秒) + */ + elapsedMilliseconds?: number + /** + * @description 登录状态(true成功,false失败) + */ + status?: boolean + /** + * @description 返回消息 + */ + msg?: string | null + /** + * @description 用户名 + */ + userName?: string | null + /** + * @description 创建时间 + */ + creationTime?: string + /** + * @description 姓名(昵称) + */ + realName?: string | null } export interface LoginLogListOutputPageList { @@ -607,7 +1181,7 @@ export interface LoginLogListOutputPageList { pageSize?: number totalCount?: number order?: string | null - list?: Array + list?: Array | null } export interface LoginLogListOutputPageListIResultModel { @@ -615,20 +1189,59 @@ export interface LoginLogListOutputPageListIResultModel { } export interface MenuInput { - id?: string | null /* 标识 */ - parentId?: string | null /* 父级Id */ - icon?: string | null /* 图标 */ - iconColor?: string | null /* 图表颜色 */ - link?: string | null /* 链接、路由 */ - moduleCode?: string | null /* 所属模块 */ - name?: string | null /* 名称 */ - remarks?: string | null /* 备注 */ - routeParams?: string | null /* 路由参数 */ - show?: boolean /* 是否显示 */ - sort?: number /* 排序码 */ - target?: number /* 打开方式 */ + /** + * @description 标识 + */ + id?: string | null + /** + * @description 父级Id + */ + parentId?: string | null + /** + * @description 图标 + */ + icon?: string | null + /** + * @description 图表颜色 + */ + iconColor?: string | null + /** + * @description 链接、路由 + */ + link?: string | null + /** + * @description 所属模块 + */ + moduleCode?: string | null + /** + * @description 名称 + */ + name?: string | null + /** + * @description 备注 + */ + remarks?: string | null + /** + * @description 路由参数 + */ + routeParams?: string | null + /** + * @description 是否显示 + */ + show?: boolean + /** + * @description 排序码 + */ + sort?: number + /** + * @description 打开方式 + */ + target?: number client?: ClientEnums - component?: string | null /* 组件 */ + /** + * @description 组件 + */ + component?: string | null } /** @@ -636,15 +1249,42 @@ export interface MenuInput { */ export interface Api { id?: string | null - parentId?: string | null /* 父级id(默认为"") */ - code?: string | null /* 权限点 */ - enable?: boolean /* 启用(默认为启用) */ - httpMethod?: string | null /* 请求方式 */ - level?: number /* 层级(从1开始) */ - name?: string | null /* 资源名称 */ - path?: string | null /* id全路径 */ - pathText?: string | null /* 中文全路径 */ - remarks?: string | null /* 备注 */ + /** + * @description 父级id(默认为"") + */ + parentId?: string | null + /** + * @description 权限点 + */ + code?: string | null + /** + * @description 启用(默认为启用) + */ + enable?: boolean + /** + * @description 请求方式 + */ + httpMethod?: string | null + /** + * @description 层级(从1开始) + */ + level?: number + /** + * @description 资源名称 + */ + name?: string | null + /** + * @description id全路径 + */ + path?: string | null + /** + * @description 中文全路径 + */ + pathText?: string | null + /** + * @description 备注 + */ + remarks?: string | null } /** @@ -659,25 +1299,76 @@ export interface Enterprise { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - parentId?: string | null /* 父级编号 */ - areaCode?: string | null /* 行政区代码 */ - areaName?: string | null /* 行政区名称 */ - code?: string | null /* 机构编码 */ - contactNumber?: string | null /* 联系人电话 */ - contactPerson?: string | null /* 联系人 */ - isEnabled?: boolean /* 是否启用 */ - legalRepresentative?: string | null /* 法人代表 */ - legalRepresentativeNumber?: string | null /* 法人代表电话 */ - level?: number /* 级数 */ - name?: string | null /* 机构名称 */ - path?: string | null /* 路径 */ - pathText?: string | null /* 中文全路径 */ - pinYin?: string | null /* 拼音简码 */ - simpleName?: string | null /* 机构简称(用于上报部级数据展示名称) */ - sortId?: number /* 排序号 */ - type?: number /* 企业类型(1:企业, 2:个人) */ - organizations?: Array - userInfos?: Array + /** + * @description 父级编号 + */ + parentId?: string | null + /** + * @description 行政区代码 + */ + areaCode?: string | null + /** + * @description 行政区名称 + */ + areaName?: string | null + /** + * @description 机构编码 + */ + code?: string | null + /** + * @description 联系人电话 + */ + contactNumber?: string | null + /** + * @description 联系人 + */ + contactPerson?: string | null + /** + * @description 是否启用 + */ + isEnabled?: boolean + /** + * @description 法人代表 + */ + legalRepresentative?: string | null + /** + * @description 法人代表电话 + */ + legalRepresentativeNumber?: string | null + /** + * @description 级数 + */ + level?: number + /** + * @description 机构名称 + */ + name?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 中文全路径 + */ + pathText?: string | null + /** + * @description 拼音简码 + */ + pinYin?: string | null + /** + * @description 机构简称(用于上报部级数据展示名称) + */ + simpleName?: string | null + /** + * @description 排序号 + */ + sortId?: number + /** + * @description 企业类型(1:企业, 2:个人) + */ + type?: number + organizations?: Array | null + userInfos?: Array | null } /** @@ -692,24 +1383,48 @@ export interface Organizations { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - enterpriseId?: string | null /* 所属企业id */ - leader?: string | null /* 负责人 */ - level?: number /* 级别 */ - name?: string | null /* 名称 */ - parentId?: string | null /* 父级编号 */ - path?: string | null /* 路径 */ - pathText?: string | null /* 中文路径 */ - sortId?: number /* 排序号 */ + /** + * @description 所属企业id + */ + enterpriseId?: string | null + /** + * @description 负责人 + */ + leader?: string | null + /** + * @description 级别 + */ + level?: number + /** + * @description 名称 + */ + name?: string | null + /** + * @description 父级编号 + */ + parentId?: string | null + /** + * @description 路径 + */ + path?: string | null + /** + * @description 中文路径 + */ + pathText?: string | null + /** + * @description 排序号 + */ + sortId?: number enterprise?: Enterprise - userInfos?: Array + userInfos?: Array | null } export interface ClaimsIdentity { authenticationType?: string | null isAuthenticated?: boolean actor?: ClaimsIdentity - bootstrapContext?: any - claims?: Array + bootstrapContext?: unknown | null + claims?: Array | null label?: string | null name?: string | null nameClaimType?: string | null @@ -719,7 +1434,9 @@ export interface ClaimsIdentity { export interface Claim { issuer?: string | null originalIssuer?: string | null - properties?: {} + properties?: { + [key: string]: string + } | null subject?: ClaimsIdentity type?: string | null value?: string | null @@ -738,23 +1455,65 @@ export interface UserInfo { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - enterpriseId?: string | null /* 企业Id */ - orgId?: string | null /* 组织架构Id */ - email?: string | null /* 邮箱 */ - headPortraitCode?: string | null /* 头像 */ - idCard?: string | null /* 身份证 */ - passwordHash?: string | null /* 密码加密字符 */ - passwordSalt?: string | null /* 密码加密盐值 */ - phoneNumber?: string | null /* 手机号 */ - realName?: string | null /* 真实姓名 */ - sex?: number /* 性别(1.男 2.女) */ - status?: number /* 状态 */ - type?: number /* 用户类型 */ - userName?: string | null /* 用户名 */ + /** + * @description 企业Id + */ + enterpriseId?: string | null + /** + * @description 组织架构Id + */ + orgId?: string | null + /** + * @description 邮箱 + */ + email?: string | null + /** + * @description 头像 + */ + headPortraitCode?: string | null + /** + * @description 身份证 + */ + idCard?: string | null + /** + * @description 密码加密字符 + */ + passwordHash?: string | null + /** + * @description 密码加密盐值 + */ + passwordSalt?: string | null + /** + * @description 手机号 + */ + phoneNumber?: string | null + /** + * @description 真实姓名 + */ + realName?: string | null + /** + * @description 性别(1.男 2.女) + */ + sex?: number + /** + * @description 状态 + */ + status?: number + /** + * @description 用户类型 + */ + type?: number + /** + * @description 用户名 + */ + userName?: string | null organizations?: Organizations enterprise?: Enterprise - roles?: Array - claims?: Array /* 用户身份声明 */ + roles?: Array | null + /** + * @description 用户身份声明 + */ + claims?: Array | null } /** @@ -769,13 +1528,34 @@ export interface Roles { lastModifierUserId?: string | null lastModifierUserName?: string | null isDeleted?: boolean - code?: string | null /* 枚举编码 */ - remarks?: string | null /* 备注 */ - roleName?: string | null /* 角色名/组名 */ - sortId?: number /* 排序值 */ - menus?: Array

/* Menus导航属性 */ - users?: Array /* Users导航属性 */ - buttons?: Array