├── .eslintignore ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── Dockerfile ├── README.md ├── docker-compose.yml ├── package.json ├── pnpm-lock.yaml ├── src ├── cli.ts ├── index.ts ├── log.ts ├── mockGenerator.ts ├── openAPIParserMock │ ├── index.ts │ ├── primitives.ts │ └── utils.ts ├── serviceGenerator.ts └── util.ts ├── swagger.json ├── templates ├── interface.njk ├── serviceController.njk └── serviceIndex.njk ├── test ├── apispe │ └── api │ │ ├── api0.ts │ │ ├── index.ts │ │ └── typings.d.ts ├── example-files │ ├── apispec_1.json │ ├── swagger-custom-hook.json │ ├── swagger-empty.json │ ├── swagger-file-convert.json │ ├── swagger-get-method-params-convert-obj.json │ ├── swagger-schema-contain-blank-symbol.json │ └── swgger3.0.1.json ├── genSwagger.js ├── java-api.json ├── morse-api.json ├── oc-swagger.json ├── openapi.json ├── swagger-allof.json ├── test-allof-api.json ├── test.js └── test.locale.js └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | /lambda/ 2 | /scripts 3 | /config 4 | .history 5 | public 6 | dist 7 | .umi 8 | mock 9 | servers -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | package.json 3 | .umi 4 | .umi-production 5 | /dist 6 | .dockerignore 7 | .DS_Store 8 | .eslintignore 9 | *.png 10 | *.toml 11 | docker 12 | .editorconfig 13 | Dockerfile* 14 | .gitignore 15 | .prettierignore 16 | LICENSE 17 | .eslintcache 18 | *.lock 19 | yarn-error.log 20 | .history 21 | CNAME 22 | /build 23 | /public -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'all', 4 | printWidth: 100, 5 | proseWrap: 'never', 6 | endOfLine: 'lf', 7 | overrides: [ 8 | { 9 | files: '.prettierrc', 10 | options: { 11 | parser: 'json', 12 | }, 13 | }, 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | RUN npm install pnpm@^8.15.7 -g 3 | RUN pnpm config set store-dir /path/to/.pnpm-store 4 | WORKDIR /code 5 | EXPOSE 8080 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAPI to TypeScript Generator (OpenAPI TypeScript 生成器) 2 | 3 | [![GitHub Repo stars](https://img.shields.io/github/stars/chenshuai2144/openapi2typescript?style=social)](https://github.com/chenshuai2144/openapi2typescript) [![npm (scoped)](https://img.shields.io/npm/v/@umijs/openapi)](https://www.npmjs.com/package/@umijs/openapi) ![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/chenshuai2144/openapi2typescript?include_prereleases) 4 | 5 | [English](#english) | [中文](#chinese) 6 | 7 | 8 | ## English 9 | 10 | ### Introduction 11 | A powerful tool that generates TypeScript request code from [OpenAPI 3.0](https://swagger.io/blog/news/whats-new-in-openapi-3-0/) documentation. If you're using [umi](https://umijs.org), you might want to check out [@umijs/plugin-openapi](https://www.npmjs.com/package/@umijs/plugin-openapi) plugin instead. 12 | 13 | ### Installation 14 | 15 | ```bash 16 | npm i --save-dev @umijs/openapi 17 | # or 18 | pnpm add -D @umijs/openapi 19 | # or 20 | yarn add -D @umijs/openapi 21 | ``` 22 | 23 | ### Usage 24 | 25 | 1. Create a configuration file in your project root directory. You can name it either `openapi2ts.config.ts` or `.openapi2tsrc.ts`: 26 | 27 | ```typescript 28 | export default { 29 | schemaPath: 'http://petstore.swagger.io/v2/swagger.json', 30 | serversPath: './servers', 31 | } 32 | ``` 33 | 34 | For multiple API sources, you can use an array configuration: 35 | 36 | ```typescript 37 | export default [ 38 | { 39 | schemaPath: 'http://app.swagger.io/v2/swagger.json', 40 | serversPath: './servers/app', 41 | }, 42 | { 43 | schemaPath: 'http://auth.swagger.io/v2/swagger.json', 44 | serversPath: './servers/auth', 45 | } 46 | ] 47 | ``` 48 | 49 | 2. Add the generation script to your `package.json`: 50 | 51 | ```json 52 | { 53 | "scripts": { 54 | "openapi2ts": "openapi2ts" 55 | } 56 | } 57 | ``` 58 | 59 | 3. Generate the API code: 60 | 61 | ```bash 62 | npm run openapi2ts 63 | ``` 64 | 65 | ### Configuration Options 66 | 67 | | Property | Required | Description | Type | Default | 68 | |----------|----------|-------------|------|---------| 69 | | requestLibPath | No | Custom request method path | string | - | 70 | | requestOptionsType | No | Custom request options type | string | {[key: string]: any} | 71 | | requestImportStatement | No | Custom request import statement | string | - | 72 | | apiPrefix | No | API prefix | string | - | 73 | | serversPath | No | Output directory path | string | - | 74 | | schemaPath | No | Swagger 2.0 or OpenAPI 3.0 URL | string | - | 75 | | projectName | No | Project name | string | - | 76 | | authorization | No | Documentation authentication token | string | - | 77 | | namespace | No | Namespace name | string | API | 78 | | mockFolder | No | Mock directory | string | - | 79 | | enumStyle | No | Enum style | string-literal \| enum | string-literal | 80 | | nullable | No | Use null instead of optional | boolean | false | 81 | | dataFields | No | Data fields in response | string[] | - | 82 | | isCamelCase | No | Use camelCase for files and functions | boolean | true | 83 | | declareType | No | Interface declaration type | type/interface | type | 84 | 85 | ### Custom Hooks 86 | 87 | | Property | Type | Description | 88 | |----------|------|-------------| 89 | | afterOpenApiDataInited | (openAPIData: OpenAPIObject) => OpenAPIObject | Hook after OpenAPI data initialization | 90 | | customFunctionName | (data: APIDataType) => string | Custom request function name | 91 | | customTypeName | (data: APIDataType) => string | Custom type name | 92 | | customClassName | (tagName: string) => string | Custom class name | 93 | | customType | (schemaObject, namespace, originGetType) => string | Custom type getter | 94 | | customFileNames | (operationObject, apiPath, _apiMethod) => string[] | Custom file name generator | 95 | 96 | 97 | ## 中文 98 | 99 | ### 介绍 100 | 一个强大的工具,可以根据 [OpenAPI 3.0](https://swagger.io/blog/news/whats-new-in-openapi-3-0/) 文档生成 TypeScript 请求代码。如果你使用 [umi](https://umijs.org),可以考虑使用 [@umijs/plugin-openapi](https://www.npmjs.com/package/@umijs/plugin-openapi) 插件。 101 | 102 | ### 安装 103 | 104 | ```bash 105 | npm i --save-dev @umijs/openapi 106 | # 或者 107 | pnpm add -D @umijs/openapi 108 | # 或者 109 | yarn add -D @umijs/openapi 110 | ``` 111 | 112 | ### 使用方法 113 | 114 | 1. 在项目根目录创建配置文件,可以命名为 `openapi2ts.config.ts` 或 `.openapi2tsrc.ts`: 115 | 116 | ```typescript 117 | export default { 118 | schemaPath: 'http://petstore.swagger.io/v2/swagger.json', 119 | serversPath: './servers', 120 | } 121 | ``` 122 | 123 | 如果需要处理多个 API 源,可以使用数组配置: 124 | 125 | ```typescript 126 | export default [ 127 | { 128 | schemaPath: 'http://app.swagger.io/v2/swagger.json', 129 | serversPath: './servers/app', 130 | }, 131 | { 132 | schemaPath: 'http://auth.swagger.io/v2/swagger.json', 133 | serversPath: './servers/auth', 134 | } 135 | ] 136 | ``` 137 | 138 | 2. 在 `package.json` 中添加生成脚本: 139 | 140 | ```json 141 | { 142 | "scripts": { 143 | "openapi2ts": "openapi2ts" 144 | } 145 | } 146 | ``` 147 | 148 | 3. 生成 API 代码: 149 | 150 | ```bash 151 | npm run openapi2ts 152 | ``` 153 | 154 | ### 配置选项 155 | 156 | | 属性 | 必填 | 说明 | 类型 | 默认值 | 157 | |------|------|------|------|--------| 158 | | requestLibPath | 否 | 自定义请求方法路径 | string | - | 159 | | requestOptionsType | 否 | 自定义请求方法 options 参数类型 | string | {[key: string]: any} | 160 | | requestImportStatement | 否 | 自定义请求方法表达式 | string | - | 161 | | apiPrefix | 否 | API 前缀 | string | - | 162 | | serversPath | 否 | 生成文件夹的路径 | string | - | 163 | | schemaPath | 否 | Swagger 2.0 或 OpenAPI 3.0 的地址 | string | - | 164 | | projectName | 否 | 项目名称 | string | - | 165 | | authorization | 否 | 文档登录凭证 | string | - | 166 | | namespace | 否 | 命名空间名称 | string | API | 167 | | mockFolder | 否 | mock 目录 | string | - | 168 | | enumStyle | 否 | 枚举样式 | string-literal \| enum | string-literal | 169 | | nullable | 否 | 使用 null 代替可选 | boolean | false | 170 | | dataFields | 否 | response 中数据字段 | string[] | - | 171 | | isCamelCase | 否 | 小驼峰命名文件和请求函数 | boolean | true | 172 | | declareType | 否 | interface 声明类型 | type/interface | type | 173 | 174 | ### 自定义钩子 175 | 176 | | 属性 | 类型 | 说明 | 177 | |------|------|------| 178 | | afterOpenApiDataInited | (openAPIData: OpenAPIObject) => OpenAPIObject | OpenAPI 数据初始化后的钩子 | 179 | | customFunctionName | (data: APIDataType) => string | 自定义请求方法函数名称 | 180 | | customTypeName | (data: APIDataType) => string | 自定义类型名称 | 181 | | customClassName | (tagName: string) => string | 自定义类名 | 182 | | customType | (schemaObject, namespace, originGetType) => string | 自定义获取类型 | 183 | | customFileNames | (operationObject, apiPath, _apiMethod) => string[] | 自定义生成文件名 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | openapi: 5 | build: . 6 | volumes: 7 | - .:/code 8 | stdin_open: true 9 | tty: true 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@umijs/openapi", 3 | "version": "1.13.15", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+git@github.com:chenshuai2144/openapi2typescript.git" 8 | }, 9 | "license": "MIT", 10 | "author": "chenshuai2144/kobe", 11 | "main": "dist/index.js", 12 | "bin": { 13 | "openapi2ts": "dist/cli.js" 14 | }, 15 | "files": [ 16 | "dist", 17 | "templates" 18 | ], 19 | "scripts": { 20 | "build": "tsc", 21 | "localConvert4Project": "rm -rf ./test/servers/ ./test/file-servers/ && npm run build && cd ./test && node ./test.js && cd .. && rm -rf /Users/fd/wj/psp-web-pro/src/services/wj && mv ./test/servers/api/ /Users/fd/wj/psp-web-pro/src/services/wj", 22 | "prepublishOnly": "npm run build && np --no-cleanup --yolo --no-publish --any-branch", 23 | "start": "tsc -w", 24 | "test": "rm -rf ./test/servers/ ./test/servers-allof/ ./test/file-servers/ && npm run build && cd ./test && node ./test.js && cd ..", 25 | "test:windows": "rimraf ./test/servers/ ./test/servers-allof/ ./test/file-servers/ && npm run build && cd ./test && ts-node ./test.js --project tsconfig.json && cd .." 26 | }, 27 | "dependencies": { 28 | "chalk": "^4.1.2", 29 | "cosmiconfig": "^9.0.0", 30 | "dayjs": "^1.10.3", 31 | "glob": "^7.1.6", 32 | "lodash": "^4.17.21", 33 | "memoizee": "^0.4.15", 34 | "mock.js": "^0.2.0", 35 | "mockjs": "^1.1.0", 36 | "node-fetch": "^2.6.1", 37 | "number-to-words": "^1.2.4", 38 | "nunjucks": "^3.2.2", 39 | "openapi3-ts": "^2.0.1", 40 | "prettier": "^2.2.1", 41 | "reserved-words": "^0.1.2", 42 | "rimraf": "^3.0.2", 43 | "swagger2openapi": "^7.0.4", 44 | "tiny-pinyin": "^1.3.2" 45 | }, 46 | "devDependencies": { 47 | "@types/express": "^4.17.11", 48 | "@types/node": "^14.14.22", 49 | "@types/node-fetch": "^2.6.12", 50 | "@types/number-to-words": "^1.2.3", 51 | "@types/swagger2openapi": "^7.0.4", 52 | "np": "^7.2.0", 53 | "ts-node": "^10.9.2", 54 | "tslib": "^2.6.0", 55 | "typescript": "^4.1.3" 56 | }, 57 | "packageManager": "pnpm@9.13.2+sha512.88c9c3864450350e65a33587ab801acf946d7c814ed1134da4a924f6df5a2120fd36b46aab68f7cd1d413149112d53c7db3a4136624cfd00ff1846a0c6cef48a" 58 | } 59 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import chalk from 'chalk'; 4 | import { cosmiconfigSync } from 'cosmiconfig'; 5 | import { generateService, GenerateServiceProps } from './index'; 6 | 7 | const explorerSync = cosmiconfigSync('openapi2ts'); 8 | const searchedFor = explorerSync.search(); 9 | 10 | async function run() { 11 | try { 12 | if (searchedFor?.config) { 13 | const configs: GenerateServiceProps[] = Array.isArray(searchedFor.config) 14 | ? searchedFor.config 15 | : [searchedFor.config]; 16 | 17 | for (const config of configs) { 18 | await generateService(config); 19 | } 20 | } else { 21 | throw new Error('config is not found'); 22 | } 23 | } catch (error) { 24 | console.log(chalk.red(error)); 25 | } 26 | } 27 | 28 | run(); 29 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | /* eslint-disable import/no-dynamic-require */ 3 | import http from 'http'; 4 | import https from 'https'; 5 | import fetch from 'node-fetch'; 6 | import type { OpenAPIObject, OperationObject, SchemaObject } from 'openapi3-ts'; 7 | import converter from 'swagger2openapi'; 8 | import Log from './log'; 9 | import { mockGenerator } from './mockGenerator'; 10 | import { ServiceGenerator } from './serviceGenerator'; 11 | import type { APIDataType } from './serviceGenerator'; 12 | 13 | const getImportStatement = (requestLibPath: string) => { 14 | if (requestLibPath && requestLibPath.startsWith('import')) { 15 | return requestLibPath; 16 | } 17 | if (requestLibPath) { 18 | return `import request from '${requestLibPath}'`; 19 | } 20 | return `import { request } from "umi"`; 21 | }; 22 | 23 | export type GenerateServiceProps = { 24 | requestLibPath?: string; 25 | requestOptionsType?: string; 26 | requestImportStatement?: string; 27 | // interface 类型声明方式, 满足某些团队的开发规范 28 | declareType?: 'type' | 'interface'; 29 | /** 30 | * api 的前缀 31 | */ 32 | apiPrefix?: 33 | | string 34 | | ((params: { 35 | path: string; 36 | method: string; 37 | namespace: string; 38 | functionName: string; 39 | autoExclude?: boolean; 40 | }) => string); 41 | /** 42 | * 生成的文件夹的路径 43 | */ 44 | serversPath?: string; 45 | /** 46 | * Swagger 2.0 或 OpenAPI 3.0 的地址 47 | */ 48 | schemaPath?: string; 49 | /** 50 | * 项目名称 51 | */ 52 | projectName?: string; 53 | /** 54 | * 文档登录凭证 55 | */ 56 | authorization?: string; 57 | 58 | hook?: { 59 | /** change open api data after constructor */ 60 | afterOpenApiDataInited?: (openAPIData: OpenAPIObject) => OpenAPIObject; 61 | 62 | /** 自定义函数名称 */ 63 | customFunctionName?: (data: APIDataType) => string; 64 | /** 自定义类型名称 */ 65 | customTypeName?: (data: APIDataType) => string; 66 | /** 自定义 options 默认值 */ 67 | customOptionsDefaultValue?: (data: OperationObject) => Record | undefined; 68 | /** 自定义类名 */ 69 | customClassName?: (tagName: string) => string; 70 | 71 | /** 72 | * 自定义获取type hook 73 | * 返回非字符串将使用默认方法获取type 74 | * @example set number to string 75 | * function customType(schemaObject,namespace){ 76 | * if(schemaObject.type==='number' && !schemaObject.format){ 77 | * return 'BigDecimalString'; 78 | * } 79 | * } 80 | */ 81 | customType?: ( 82 | schemaObject: SchemaObject | undefined, 83 | namespace: string, 84 | originGetType: (schemaObject: SchemaObject | undefined, namespace: string) => string, 85 | ) => string; 86 | 87 | /** 88 | * 自定义生成文件名,可返回多个,表示生成多个文件 89 | * 返回为空,则使用默认的获取方法获取 90 | * @example 使用operationId生成文件名 91 | * function customFileNames(operationObject,apiPath){ 92 | * const operationId=operationObject.operationId; 93 | * if (!operationId) { 94 | * console.warn('[Warning] no operationId', apiPath); 95 | * return; 96 | * } 97 | * const res = operationId.split('_'); 98 | * if (res.length > 1) { 99 | * res.shift(); 100 | * if (res.length > 2) { 101 | * console.warn('[Warning] operationId has more than 2 part', apiPath); 102 | * } 103 | * return [res.join('_')]; 104 | * } else { 105 | * const controllerName = (res || [])[0]; 106 | * if (controllerName) { 107 | * return [controllerName]; 108 | * } 109 | * return; 110 | * } 111 | * } 112 | */ 113 | customFileNames?: ( 114 | operationObject: OperationObject, 115 | apiPath: string, 116 | _apiMethod: string, 117 | ) => string[]; 118 | }; 119 | namespace?: string; 120 | 121 | /** 122 | * 默认为false,true时使用null代替可选 123 | */ 124 | nullable?: boolean; 125 | 126 | mockFolder?: string; 127 | /** 128 | * 模板文件的文件路径 129 | */ 130 | templatesFolder?: string; 131 | 132 | /** 133 | * 枚举样式 134 | */ 135 | enumStyle?: 'string-literal' | 'enum'; 136 | 137 | /** 138 | * response中数据字段 139 | * example: ['result', 'res'] 140 | */ 141 | dataFields?: string[]; 142 | 143 | /** 144 | * 模板文件、请求函数采用小驼峰命名 145 | */ 146 | isCamelCase?: boolean; 147 | /** 148 | * mock配置 149 | */ 150 | mockConfig?: { 151 | /** 152 | * msw类型mock文件格式. 直接返回对象 153 | * 举例: 154 | * // @ts-ignore 155 | 156 | export default { 157 | 'DELETE /mydata/delete': { message: { message: 'Mydata successfully deleted' } }, 158 | }; 159 | 160 | 161 | 原文件: 162 | // @ts-ignore 163 | import { Request, Response } from 'express'; 164 | 165 | export default { 166 | 'DELETE /mydata/delete': (req: Request, res: Response) => { 167 | res.status(200).send({ message: { message: 'Mydata successfully deleted' } }); 168 | }, 169 | }; 170 | */ 171 | msw?: boolean; 172 | }; 173 | }; 174 | 175 | const converterSwaggerToOpenApi = (swagger: any) => { 176 | if (!swagger.swagger) { 177 | return swagger; 178 | } 179 | return new Promise((resolve, reject) => { 180 | converter.convertObj(swagger, {}, (err, options) => { 181 | Log(['💺 将 Swagger 转化为 openAPI']); 182 | if (err) { 183 | reject(err); 184 | return; 185 | } 186 | resolve(options.openapi); 187 | }); 188 | }); 189 | }; 190 | 191 | export const getSchema = async (schemaPath: string, authorization?: string) => { 192 | if (schemaPath.startsWith('http')) { 193 | const protocol = schemaPath.startsWith('https:') ? https : http; 194 | try { 195 | const agent = new protocol.Agent({ 196 | rejectUnauthorized: false, 197 | }); 198 | const headers = { 199 | authorization, 200 | }; 201 | const json = await fetch(schemaPath, { agent, headers: authorization? headers: {} }).then((rest) => rest.json()); 202 | return json; 203 | } catch (error) { 204 | // eslint-disable-next-line no-console 205 | console.log('fetch openapi error:', error); 206 | } 207 | return null; 208 | } 209 | if (require.cache[schemaPath]) { 210 | delete require.cache[schemaPath]; 211 | } 212 | const schema = require(schemaPath); 213 | return schema; 214 | }; 215 | 216 | const getOpenAPIConfig = async (schemaPath: string, authorization?: string) => { 217 | const schema = await getSchema(schemaPath, authorization); 218 | if (!schema) { 219 | return null; 220 | } 221 | const openAPI = await converterSwaggerToOpenApi(schema); 222 | return openAPI; 223 | }; 224 | 225 | // 从 appName 生成 service 数据 226 | export const generateService = async ({ 227 | authorization, 228 | requestLibPath, 229 | schemaPath, 230 | mockFolder, 231 | nullable = false, 232 | requestOptionsType = '{[key: string]: any}', 233 | ...rest 234 | }: GenerateServiceProps) => { 235 | const openAPI = await getOpenAPIConfig(schemaPath, authorization); 236 | const requestImportStatement = getImportStatement(requestLibPath); 237 | const serviceGenerator = new ServiceGenerator( 238 | { 239 | namespace: 'API', 240 | requestOptionsType, 241 | requestImportStatement, 242 | enumStyle: 'string-literal', 243 | nullable, 244 | isCamelCase: true, 245 | mockConfig: {}, 246 | ...rest, 247 | }, 248 | openAPI, 249 | ); 250 | serviceGenerator.genFile(); 251 | 252 | if (mockFolder) { 253 | await mockGenerator({ 254 | openAPI, 255 | mockFolder: mockFolder || './mocks/', 256 | mockConfig: rest.mockConfig || {}, 257 | }); 258 | } 259 | }; 260 | -------------------------------------------------------------------------------- /src/log.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | // eslint-disable-next-line no-console 4 | const Log = (...rest) => console.log(`${chalk.blue('[openAPI]')}: ${rest.join('\n')}`); 5 | 6 | export default Log; 7 | -------------------------------------------------------------------------------- /src/mockGenerator.ts: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs'; 2 | import fs from 'fs'; 3 | import { prettierFile, writeFile } from './util'; 4 | import { dirname, join } from 'path'; 5 | import OpenAPIParserMock from './openAPIParserMock/index'; 6 | import Log from './log'; 7 | import pinyin from 'tiny-pinyin'; 8 | import { GenerateServiceProps } from './index'; 9 | Mock.Random.extend({ 10 | country() { 11 | const data = [ 12 | '阿根廷', 13 | '澳大利亚', 14 | '巴西', 15 | '加拿大', 16 | '中国', 17 | '法国', 18 | '德国', 19 | '印度', 20 | '印度尼西亚', 21 | '意大利', 22 | '日本', 23 | '韩国', 24 | '墨西哥', 25 | '俄罗斯', 26 | '沙特阿拉伯', 27 | '南非', 28 | '土耳其', 29 | '英国', 30 | '美国', 31 | ]; 32 | const id = (Math.random() * data.length).toFixed(); 33 | return data[id]; 34 | }, 35 | phone() { 36 | const phonepreFix = ['111', '112', '114']; // 自己写前缀哈 37 | return this.pick(phonepreFix) + Mock.mock(/\d{8}/); // Number() 38 | }, 39 | status() { 40 | const status = ['success', 'error', 'default', 'processing', 'warning']; 41 | return status[(Math.random() * 4).toFixed(0)]; 42 | }, 43 | authority() { 44 | const status = ['admin', 'user', 'guest']; 45 | return status[(Math.random() * status.length).toFixed(0)]; 46 | }, 47 | avatar() { 48 | const avatar = [ 49 | 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', 50 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', 51 | 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png', 52 | 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', 53 | 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', 54 | 'https://avatars0.githubusercontent.com/u/507615?s=40&v=4', 55 | 'https://avatars1.githubusercontent.com/u/8186664?s=40&v=4', 56 | ]; 57 | const id = (Math.random() * avatar.length).toFixed(); 58 | return avatar[id]; 59 | }, 60 | group() { 61 | const data = ['体验技术部', '创新科技组', '前端 6 组', '区块链平台部', '服务技术部']; 62 | const id = (Math.random() * data.length).toFixed(); 63 | return data[id]; 64 | }, 65 | label() { 66 | const label = [ 67 | '很有想法的', 68 | '小清新', 69 | '傻白甜', 70 | '阳光少年', 71 | '大咖', 72 | '健身达人', 73 | '程序员', 74 | '算法工程师', 75 | '川妹子', 76 | '名望程序员', 77 | '大长腿', 78 | '海纳百川', 79 | '专注设计', 80 | '爱好广泛', 81 | 'IT 互联网', 82 | ]; 83 | const id = (Math.random() * label.length).toFixed(); 84 | return label[id]; 85 | }, 86 | href() { 87 | const href = [ 88 | 'https://preview.pro.ant.design/dashboard/analysis', 89 | 'https://ant.design', 90 | 'https://procomponents.ant.design/', 91 | 'https://umijs.org/', 92 | 'https://github.com/umijs/dumi', 93 | ]; 94 | const id = (Math.random() * href.length).toFixed(); 95 | return href[id]; 96 | }, 97 | }); 98 | 99 | const genMockData = (example: string) => { 100 | if (!example) { 101 | return {}; 102 | } 103 | 104 | if (typeof example === 'string') { 105 | return Mock.mock(example); 106 | } 107 | 108 | if (Array.isArray(example)) { 109 | return Mock.mock(example); 110 | } 111 | 112 | return Object.keys(example) 113 | .map((name) => { 114 | return { 115 | [name]: Mock.mock(example[name]), 116 | }; 117 | }) 118 | .reduce((pre, next) => { 119 | return { 120 | ...pre, 121 | ...next, 122 | }; 123 | }, {}); 124 | }; 125 | 126 | const genByTemp = ({ 127 | method, 128 | path, 129 | parameters, 130 | status, 131 | data, 132 | mockConfig, 133 | }: { 134 | method: string; 135 | path: string; 136 | parameters: { 137 | name: string; 138 | in: string; 139 | description: string; 140 | required: boolean; 141 | schema: { type: string }; 142 | example: string; 143 | }[]; 144 | status: string; 145 | data: string; 146 | mockConfig: GenerateServiceProps['mockConfig']; 147 | }) => { 148 | if (!['get', 'put', 'post', 'delete', 'patch'].includes(method.toLocaleLowerCase())) { 149 | return ''; 150 | } 151 | 152 | let securityPath = path; 153 | parameters?.forEach((item) => { 154 | if (item.in === 'path') { 155 | securityPath = securityPath.replace(`{${item.name}}`, `:${item.name}`); 156 | } 157 | }); 158 | 159 | if (mockConfig.msw) { 160 | return `'${method.toUpperCase()} ${securityPath}': ${data}`; 161 | } 162 | 163 | return `'${method.toUpperCase()} ${securityPath}': (req: Request, res: Response) => { 164 | res.status(${status}).send(${data}); 165 | }`; 166 | }; 167 | 168 | const genMockFiles = (mockFunction: string[], mockConfig: GenerateServiceProps['mockConfig']) => { 169 | return prettierFile(` 170 | // @ts-ignore 171 | ${mockConfig.msw ? '' : "import { Request, Response } from 'express'"} 172 | 173 | 174 | export default { 175 | ${mockFunction.join('\n,')} 176 | }`)[0]; 177 | }; 178 | export type genMockDataServerConfig = { 179 | openAPI: any; 180 | mockFolder: string; 181 | mockConfig: GenerateServiceProps['mockConfig']; 182 | }; 183 | 184 | const mockGenerator = async ({ openAPI, mockFolder, mockConfig }: genMockDataServerConfig) => { 185 | const openAPParse = new OpenAPIParserMock(openAPI); 186 | const docs = openAPParse.parser(); 187 | const pathList = Object.keys(docs.paths); 188 | const { paths } = docs; 189 | const mockActionsObj = {}; 190 | pathList.forEach((path) => { 191 | const pathConfig = paths[path]; 192 | Object.keys(pathConfig).forEach((method) => { 193 | const methodConfig = pathConfig[method]; 194 | if (methodConfig) { 195 | let conte = ( 196 | methodConfig.operationId || 197 | methodConfig?.tags?.join('/') || 198 | path.replace('/', '').split('/')[1] 199 | )?.replace(/[^\w^\s^\u4e00-\u9fa5]/gi, ''); 200 | if (/[\u3220-\uFA29]/.test(conte)) { 201 | conte = pinyin.convertToPinyin(conte, '', true); 202 | } 203 | if (!conte) { 204 | return; 205 | } 206 | const data = genMockData(methodConfig.responses?.['200']?.example); 207 | if (!mockActionsObj[conte]) { 208 | mockActionsObj[conte] = []; 209 | } 210 | const tempFile = genByTemp({ 211 | method, 212 | path, 213 | parameters: methodConfig.parameters, 214 | status: '200', 215 | data: JSON.stringify(data), 216 | mockConfig, 217 | }); 218 | if (tempFile) { 219 | mockActionsObj[conte].push(tempFile); 220 | } 221 | } 222 | }); 223 | }); 224 | Object.keys(mockActionsObj).forEach((file) => { 225 | if (!file || file === 'undefined') { 226 | return; 227 | } 228 | if (file.includes('/')) { 229 | const dirName = dirname(join(mockFolder, `${file}.ts`)); 230 | if (!fs.existsSync(dirName)) { 231 | fs.mkdirSync(dirName); 232 | } 233 | } 234 | writeFile(mockFolder, `${file}.ts`, genMockFiles(mockActionsObj[file], mockConfig)); 235 | }); 236 | Log('✅ 生成 mock 文件成功'); 237 | }; 238 | 239 | export { mockGenerator }; 240 | -------------------------------------------------------------------------------- /src/openAPIParserMock/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-continue */ 2 | /* eslint-disable guard-for-in */ 3 | /* eslint-disable no-restricted-syntax */ 4 | import memoizee from 'memoizee'; 5 | 6 | import * as utils from './utils'; 7 | import primitives from './primitives'; 8 | 9 | const getDateByName = (name: string[] | string, parentsKey?: string[]) => { 10 | if (!name || name.length < 1) { 11 | return 'string'; 12 | } 13 | if (Array.isArray(name)) { 14 | return getDateByName([...name].pop(), name); 15 | } 16 | if (['nickname', 'name'].includes(name)) { 17 | return 'cname'; 18 | } 19 | if (['owner', 'firstName', 'lastName', 'username'].includes(name)) { 20 | return 'name'; 21 | } 22 | if (['avatar'].includes(name)) { 23 | return 'avatar'; 24 | } 25 | 26 | if (['group'].includes(name)) { 27 | return 'group'; 28 | } 29 | 30 | if (name.toLocaleLowerCase().endsWith('id')) { 31 | return 'uuid'; 32 | } 33 | 34 | if ( 35 | name.toLocaleLowerCase().endsWith('type') || 36 | name.toLocaleLowerCase().endsWith('key') || 37 | ['key'].includes(name) 38 | ) { 39 | return 'id'; 40 | } 41 | 42 | if (name.toLocaleLowerCase().endsWith('label') || ['label'].includes(name)) { 43 | const newParents = [...parentsKey]; 44 | newParents.pop(); 45 | const newType = getDateByName(newParents); 46 | if (newType !== 'string' && newType !== 'csentence') { 47 | return newType; 48 | } 49 | 50 | return 'label'; 51 | } 52 | 53 | if (['email'].includes(name)) { 54 | return 'email'; 55 | } 56 | if (['password'].includes(name)) { 57 | return 'string(16)'; 58 | } 59 | if (['phone'].includes(name)) { 60 | return 'phone'; 61 | } 62 | if (['province'].includes(name)) { 63 | return 'province'; 64 | } 65 | if (['city'].includes(name)) { 66 | return 'city'; 67 | } 68 | if (['addr', 'address'].includes(name)) { 69 | return 'county'; 70 | } 71 | 72 | if (['country'].includes(name)) { 73 | return 'country'; 74 | } 75 | 76 | if ( 77 | ['url', 'imageUrl', 'href'].includes(name) || 78 | name.toLocaleLowerCase().endsWith('url') || 79 | name.toLocaleLowerCase().endsWith('urls') || 80 | name.toLocaleLowerCase().endsWith('image') || 81 | name.toLocaleLowerCase().endsWith('link') 82 | ) { 83 | return 'href'; 84 | } 85 | 86 | if (name.toLocaleLowerCase().endsWith('errorcode')) { 87 | return 'errorCode'; 88 | } 89 | 90 | if ( 91 | ['type', 'status'].includes(name) || 92 | name.toLocaleLowerCase().endsWith('status') || 93 | name.toLocaleLowerCase().endsWith('type') 94 | ) { 95 | return 'status'; 96 | } 97 | 98 | if (name.toLocaleLowerCase().endsWith('authority')) { 99 | return 'authority'; 100 | } 101 | 102 | return 'csentence'; 103 | }; 104 | 105 | function primitive(schemaParams, propsName) { 106 | const schema = utils.objectify(schemaParams); 107 | const { type, format } = schema; 108 | const value = primitives[`${type}_${format || getDateByName(propsName)}`] || primitives[type]; 109 | 110 | if (typeof schema.example === 'undefined') { 111 | return value || `Unknown Type: ${schema.type}`; 112 | } 113 | return schema.example; 114 | } 115 | 116 | class OpenAPIGeneratorMockJs { 117 | protected openAPI: any; 118 | constructor(openAPI) { 119 | this.openAPI = openAPI; 120 | this.sampleFromSchema = memoizee(this.sampleFromSchema); 121 | } 122 | 123 | sampleFromSchema = (schema: any, propsName?: string[], schemaSet: Set = new Set()) => { 124 | let schemaRef = schema?.$ref; 125 | 126 | if (schemaRef) { 127 | // 如果之前已经使用过该引用结构,直接返回null,不然会陷入无限递归的情况 128 | if (schemaSet.has(schemaRef)) { 129 | return null; 130 | } else { 131 | schemaSet.add(schemaRef); 132 | } 133 | } 134 | 135 | const localSchema = schemaRef 136 | ? utils.get(this.openAPI, schemaRef.replace('#/', '').split('/')) 137 | : utils.objectify(schema); 138 | 139 | let { type } = localSchema; 140 | const { properties, additionalProperties, items, anyOf, oneOf, allOf } = localSchema; 141 | 142 | if (allOf) { 143 | let obj = {}; 144 | allOf.forEach((item) => { 145 | const newObj = this.sampleFromSchema(item, propsName, new Set(schemaSet)); 146 | obj = { 147 | ...obj, 148 | ...newObj, 149 | }; 150 | }); 151 | return obj; 152 | } 153 | 154 | if (!type) { 155 | if (properties) { 156 | type = 'object'; 157 | } else if (items) { 158 | type = 'array'; 159 | } else if (anyOf || oneOf) { 160 | type = 'union'; 161 | } else { 162 | return null; 163 | } 164 | } 165 | 166 | if (type === 'null') { 167 | return null; 168 | } 169 | 170 | if (type === 'object') { 171 | const props = utils.objectify(properties); 172 | const obj: Record = {}; 173 | for (const name in props) { 174 | obj[name] = this.sampleFromSchema( 175 | props[name], 176 | [...(propsName || []), name], 177 | new Set(schemaSet), 178 | ); 179 | } 180 | 181 | if (additionalProperties === true) { 182 | obj.additionalProp1 = {}; 183 | return obj; 184 | } 185 | if (additionalProperties) { 186 | const additionalProps = utils.objectify(additionalProperties); 187 | const additionalPropVal = this.sampleFromSchema( 188 | additionalProps, 189 | propsName, 190 | new Set(schemaSet), 191 | ); 192 | 193 | for (let i = 1; i < 4; i += 1) { 194 | obj[`additionalProp${i}`] = additionalPropVal; 195 | } 196 | } 197 | return obj; 198 | } 199 | 200 | if (type === 'array') { 201 | const item = this.sampleFromSchema(items, propsName, new Set(schemaSet)); 202 | return new Array(parseInt((Math.random() * 20).toFixed(0), 10)).fill(item); 203 | } 204 | 205 | if (type === 'union') { 206 | const subschemas = anyOf || oneOf; 207 | const subschemas_length = (subschemas && subschemas.length) || 0; 208 | if (subschemas_length) { 209 | const index = utils.getRandomInt(0, subschemas_length); 210 | const obj = this.sampleFromSchema(subschemas[index], propsName, new Set(schemaSet)); 211 | return obj; 212 | } 213 | } 214 | 215 | if (localSchema.enum) { 216 | if (localSchema.default) return localSchema.default; 217 | return utils.normalizeArray(localSchema.enum)[0]; 218 | } 219 | 220 | if (type === 'file') { 221 | return null; 222 | } 223 | return primitive(localSchema, propsName); 224 | }; 225 | 226 | parser = () => { 227 | const openAPI = { 228 | ...this.openAPI, 229 | }; 230 | for (const path in openAPI.paths) { 231 | for (const method in openAPI.paths[path]) { 232 | const api = openAPI.paths[path][method]; 233 | for (const code in api.responses) { 234 | const response = api.responses[code]; 235 | 236 | const keys = Object.keys(response.content || {}); 237 | if (keys.length) { 238 | let key: string; 239 | 240 | if (keys.includes('application/json')) { 241 | key = 'application/json'; 242 | } else if (keys.includes('*/*')) { 243 | key = '*/*'; 244 | } else { 245 | key = keys[0]; 246 | } 247 | 248 | const schema = utils.inferSchema(response.content[key]); 249 | 250 | if (schema) { 251 | response.example = schema ? this.sampleFromSchema(schema) : null; 252 | } 253 | } 254 | } 255 | if (!api.parameters) continue; 256 | for (const parameter of api.parameters) { 257 | const schema = utils.inferSchema(parameter); 258 | parameter.example = schema ? this.sampleFromSchema(schema) : null; 259 | } 260 | } 261 | } 262 | return openAPI; 263 | }; 264 | } 265 | 266 | export default OpenAPIGeneratorMockJs; 267 | -------------------------------------------------------------------------------- /src/openAPIParserMock/primitives.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | string: '@string', 3 | string_email: '@email', 4 | string_phone: '@phone', 5 | string_username: '@cname', 6 | string_url: '@url', 7 | string_uuid: '@guid', 8 | string_id: '@increment', 9 | string_status: '@status', 10 | string_authority: '@authority', 11 | string_label: '@label', 12 | string_group: '@group', 13 | string_csentence: '@csentence', 14 | string_cname: '@cname', 15 | string_name: '@last', 16 | string_errorCode: '@increment', 17 | string_country: '@country', 18 | string_county: '@county(true)', 19 | string_province: '@province', 20 | string_city: '@city', 21 | string_avatar: '@avatar', 22 | string_href: '@href', 23 | 'string_string(16)': '@string(16)', 24 | 'string_date-time': '@datetime', 25 | string_date: '@date', 26 | number: '@integer(60, 100)', 27 | number_float: '@float(60, 100, 3, 5)', 28 | integer: '@integer(60, 100)', 29 | boolean: '@boolean', 30 | }; 31 | -------------------------------------------------------------------------------- /src/openAPIParserMock/utils.ts: -------------------------------------------------------------------------------- 1 | function isObject(obj) { 2 | return !!obj && typeof obj === 'object'; 3 | } 4 | 5 | function objectify(thing) { 6 | if (!isObject(thing)) return {}; 7 | return thing; 8 | } 9 | 10 | function get(entity: any, path: (string | number)[]) { 11 | let current = entity; 12 | 13 | for (let i = 0; i < path.length; i += 1) { 14 | if (current === null || current === undefined) { 15 | return undefined; 16 | } 17 | 18 | current = current[path[i]]; 19 | } 20 | 21 | return current; 22 | } 23 | 24 | function normalizeArray(arr) { 25 | if (Array.isArray(arr)) return arr; 26 | return [arr]; 27 | } 28 | 29 | function isFunc(thing) { 30 | return typeof thing === 'function'; 31 | } 32 | 33 | function inferSchema(thing) { 34 | if (thing.schema) { 35 | return thing.schema; 36 | } 37 | 38 | if (thing.properties) { 39 | return { 40 | ...thing, 41 | type: 'object', 42 | }; 43 | } 44 | 45 | return thing; 46 | } 47 | 48 | function getRandomInt(min, max) { 49 | const minCeiled = Math.ceil(min); 50 | const maxFloored = Math.floor(max); 51 | return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); 52 | } 53 | 54 | export { isObject, get, objectify, isFunc, inferSchema, normalizeArray, getRandomInt }; 55 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable guard-for-in */ 2 | /* eslint-disable no-restricted-syntax */ 3 | /* eslint-disable no-lonely-if */ 4 | /* eslint-disable no-param-reassign */ 5 | import path from 'path'; 6 | import fs from 'fs'; 7 | import { camelCase, upperFirst } from 'lodash'; 8 | 9 | export const getAbsolutePath = (filePath: string) => { 10 | if (filePath && !path.isAbsolute(filePath)) { 11 | return path.join(process.cwd(), filePath); 12 | } 13 | return filePath; 14 | }; 15 | 16 | export const mkdir = (dir: string) => { 17 | if (!fs.existsSync(dir)) { 18 | mkdir(path.dirname(dir)); 19 | fs.mkdirSync(dir); 20 | } 21 | }; 22 | 23 | export const prettierFile = (content: string): [string, boolean] => { 24 | let result = content; 25 | let hasError = false; 26 | try { 27 | const prettier = require('prettier'); 28 | 29 | const prettierOptions = prettier.resolveConfig.sync(process.cwd()); 30 | result = prettier.format(content, { 31 | parser: 'typescript', 32 | ...prettierOptions, 33 | }); 34 | } catch (error) { 35 | hasError = true; 36 | } 37 | return [result, hasError]; 38 | }; 39 | 40 | export const writeFile = (folderPath: string, fileName: string, content: string) => { 41 | const filePath = path.join(folderPath, fileName); 42 | mkdir(path.dirname(filePath)); 43 | const [prettierContent, hasError] = prettierFile(content); 44 | fs.writeFileSync(filePath, prettierContent, { 45 | encoding: 'utf8', 46 | }); 47 | return hasError; 48 | }; 49 | 50 | export const getTagName = (name: string) => { 51 | const result = name.split('.'); 52 | // 数据源中的 tag 等同于全量的 op API 名,确定为 4-5 段,如上格式 53 | // 取中间的 1-2 字段作为 tag,作为 serviceController 创建目录的依据 54 | if (result.length === 4) { 55 | return result[2]; 56 | } 57 | if (result.length === 5) { 58 | return result[2] + upperFirst(result[3]); 59 | } 60 | return name; 61 | }; 62 | 63 | /** 64 | * 根据当前的数据源类型,对请求回来的 apiInfo 进行格式化 65 | * 如果是 op 数据源,对 tags 以及 path 中的 tags 进行处理 66 | * - before: 前缀(产品集.产品码) + 操作对象(必填)+ 子操作对象(可选)+ 动作(必填) 67 | * - after: 操作对象(必填)+ 子操作对象(可选) ==> 驼峰 68 | */ 69 | export const formatApiInfo = (apiInfo: Record): any => { 70 | if ( 71 | !( 72 | apiInfo && 73 | apiInfo.schema.info && 74 | apiInfo.schema.info.extensions && 75 | apiInfo.schema.info.extensions['x-antTech-description'] 76 | ) 77 | ) { 78 | // 非 op 数据源,直接返回 79 | return apiInfo; 80 | } 81 | 82 | apiInfo.schema.tags = apiInfo.schema.tags.map((item: Record) => { 83 | return { 84 | ...item, 85 | name: getTagName(item.name), 86 | }; 87 | }); 88 | 89 | for (const child_path in apiInfo.schema.paths) { 90 | apiInfo.schema.paths[child_path].post.tags = apiInfo.schema.paths[ 91 | child_path 92 | ].post.tags.map((tag: string) => getTagName(tag)); 93 | } 94 | 95 | return apiInfo; 96 | }; 97 | 98 | type serviceParam = { 99 | title: string; 100 | type: string; 101 | description: string; 102 | default: string; 103 | [key: string]: any; 104 | }; 105 | 106 | type serviceParams = Record; 107 | /** 108 | * 一方化场景下,由于 onex 会对请求的响应做处理 109 | * 1. 将 Response & Request 中的参数字段会变更为小驼峰写法 110 | * onex 相关代码 : http://gitlab.alipay-inc.com/one-console/sdk/blob/master/src/request.ts#L110 111 | * 2. 另外要注意: 112 | * op 返回的数据,请求参数的类型格式 需要做额外的处理 113 | * - (name) key.n, (type) string ==> key: string [] 114 | * - (name) key.m, (type) string ===> key: string [] 115 | * - (name) key.key1 , (type) string ==> key: {key1:string} 116 | * - (name) key.n.key1 ,(type) string => key:{ key1 :string}[] 117 | * - (name) key.n.key1.m,(type) string ==> key:{key1: string[]}[] 118 | */ 119 | export function formatParamsForYFH( 120 | params: serviceParams, 121 | paramsObject: serviceParams = {}, 122 | ): serviceParams { 123 | Object.keys(params).forEach((name) => { 124 | const prop = params[name]; 125 | let key = name; 126 | const nameList = name.split('.'); 127 | const nameListLength = nameList.length; 128 | 129 | if (nameListLength === 1) { 130 | // 正常的 key 131 | paramsObject[key] = { ...prop }; 132 | } else if (nameListLength === 2 && nameList[1] !== 'n' && nameList[1] !== 'm') { 133 | const [childKey] = nameList; 134 | // key.child_key 135 | const key_child_key = camelCase(nameList[1]); 136 | paramsObject[childKey] = combineParams(childKey, key_child_key, prop, paramsObject); 137 | } else { 138 | // key.n.child_key 139 | if (nameList[nameListLength - 2] === 'n' || nameList[nameListLength - 2] === 'm') { 140 | const child_key = camelCase(nameList.pop()); 141 | nameList.pop(); 142 | key = nameList.join('.'); 143 | paramsObject[key] = combineParams(key, child_key, prop, paramsObject, '.n.key'); 144 | } else { 145 | const child_key = camelCase(nameList.pop()); 146 | key = nameList.join('.'); 147 | 148 | // .key.n 149 | if (child_key === 'n' || child_key === 'm') { 150 | // .n.key.m 151 | if (nameList[nameList.length - 2] === 'n' || nameList[nameList.length - 2] === 'm') { 152 | const child_child_key = camelCase(nameList.pop()); 153 | nameList.pop(); 154 | key = nameList.join('.'); 155 | paramsObject[key] = combineParams(key, child_child_key, prop, paramsObject, '.n.key.m'); 156 | } else { 157 | prop.type = `${prop.type}[]`; 158 | paramsObject[key] = { ...prop }; 159 | } 160 | } else { 161 | paramsObject[key] = combineParams(key, child_key, prop, paramsObject); 162 | } 163 | } 164 | } 165 | 166 | paramsObject[key].name = camelCase(key); 167 | }); 168 | 169 | const hasInvoke = Object.keys(paramsObject).filter((param) => param.includes('.')).length > 0; 170 | 171 | if (hasInvoke) { 172 | // 递归 173 | return formatParamsForYFH(paramsObject); 174 | } 175 | return paramsObject; 176 | } 177 | 178 | function combineParams( 179 | key: string, 180 | child_key: string, 181 | prop: serviceParam, 182 | paramsObject: serviceParams, 183 | type?: string, 184 | ): serviceParam { 185 | const typeSuffix = type === '.n.key.m' ? '[]' : ''; 186 | const keySuffix = type === '.n.key' || type === '.n.key.m' ? '[]' : ''; 187 | if (paramsObject[key]) { 188 | const child_type = `{${child_key}:${prop.type}${typeSuffix}, ${paramsObject[key].type.slice( 189 | 1, 190 | )}`; 191 | paramsObject[key] = { 192 | ...paramsObject[key], 193 | type: child_type, 194 | }; 195 | } else { 196 | paramsObject[key] = { 197 | ...prop, 198 | type: `{${child_key}:${prop.type} 199 | }${keySuffix}`, 200 | }; 201 | } 202 | 203 | return paramsObject[key]; 204 | } 205 | 206 | export const stripDot = (str: string) => { 207 | return str.replace(/[-_ .](\w)/g, (_all, letter) => letter.toUpperCase()); 208 | }; 209 | -------------------------------------------------------------------------------- /swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", 5 | "version": "1.0.0", 6 | "title": "Swagger Petstore", 7 | "termsOfService": "http://swagger.io/terms/", 8 | "contact": { 9 | "email": "apiteam@swagger.io" 10 | }, 11 | "license": { 12 | "name": "Apache 2.0", 13 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 14 | } 15 | }, 16 | "host": "petstore.swagger.io", 17 | "basePath": "/v2", 18 | "tags": [ 19 | { 20 | "name": "pet", 21 | "description": "Everything about your Pets", 22 | "externalDocs": { 23 | "description": "Find out more", 24 | "url": "http://swagger.io" 25 | } 26 | }, 27 | { 28 | "name": "store", 29 | "description": "Access to Petstore orders" 30 | }, 31 | { 32 | "name": "user", 33 | "description": "Operations about user", 34 | "externalDocs": { 35 | "description": "Find out more about our store", 36 | "url": "http://swagger.io" 37 | } 38 | } 39 | ], 40 | "schemes": ["https", "http"], 41 | "paths": { 42 | "/pet": { 43 | "post": { 44 | "tags": ["pet"], 45 | "summary": "Add a new pet to the store", 46 | "description": "", 47 | "operationId": "addPet", 48 | "consumes": ["application/json", "application/xml"], 49 | "produces": ["application/xml", "application/json"], 50 | "parameters": [ 51 | { 52 | "in": "body", 53 | "name": "body", 54 | "description": "Pet object that needs to be added to the store", 55 | "required": true, 56 | "schema": { 57 | "$ref": "#/definitions/Pet" 58 | } 59 | } 60 | ], 61 | "responses": { 62 | "405": { 63 | "description": "Invalid input" 64 | } 65 | }, 66 | "security": [ 67 | { 68 | "petstore_auth": ["write:pets", "read:pets"] 69 | } 70 | ] 71 | }, 72 | "put": { 73 | "tags": ["pet"], 74 | "summary": "Update an existing pet", 75 | "description": "", 76 | "operationId": "updatePet", 77 | "consumes": ["application/json", "application/xml"], 78 | "produces": ["application/xml", "application/json"], 79 | "parameters": [ 80 | { 81 | "in": "body", 82 | "name": "body", 83 | "description": "Pet object that needs to be added to the store", 84 | "required": true, 85 | "schema": { 86 | "$ref": "#/definitions/Pet" 87 | } 88 | } 89 | ], 90 | "responses": { 91 | "400": { 92 | "description": "Invalid ID supplied" 93 | }, 94 | "404": { 95 | "description": "Pet not found" 96 | }, 97 | "405": { 98 | "description": "Validation exception" 99 | } 100 | }, 101 | "security": [ 102 | { 103 | "petstore_auth": ["write:pets", "read:pets"] 104 | } 105 | ] 106 | } 107 | }, 108 | "/pet/findByStatus": { 109 | "get": { 110 | "tags": ["pet"], 111 | "summary": "Finds Pets by status", 112 | "description": "Multiple status values can be provided with comma separated strings", 113 | "operationId": "findPetsByStatus", 114 | "produces": ["application/xml", "application/json"], 115 | "parameters": [ 116 | { 117 | "name": "status", 118 | "in": "query", 119 | "description": "Status values that need to be considered for filter", 120 | "required": true, 121 | "type": "array", 122 | "items": { 123 | "type": "string", 124 | "enum": ["available", "pending", "sold"], 125 | "default": "available" 126 | }, 127 | "collectionFormat": "multi" 128 | } 129 | ], 130 | "responses": { 131 | "200": { 132 | "description": "successful operation", 133 | "schema": { 134 | "type": "array", 135 | "items": { 136 | "$ref": "#/definitions/Pet" 137 | } 138 | } 139 | }, 140 | "400": { 141 | "description": "Invalid status value" 142 | } 143 | }, 144 | "security": [ 145 | { 146 | "petstore_auth": ["write:pets", "read:pets"] 147 | } 148 | ] 149 | } 150 | }, 151 | "/pet/findByTags": { 152 | "get": { 153 | "tags": ["pet"], 154 | "summary": "Finds Pets by tags", 155 | "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", 156 | "operationId": "findPetsByTags", 157 | "produces": ["application/xml", "application/json"], 158 | "parameters": [ 159 | { 160 | "name": "tags", 161 | "in": "query", 162 | "description": "Tags to filter by", 163 | "required": true, 164 | "type": "array", 165 | "items": { 166 | "type": "string" 167 | }, 168 | "collectionFormat": "multi" 169 | } 170 | ], 171 | "responses": { 172 | "200": { 173 | "description": "successful operation", 174 | "schema": { 175 | "type": "array", 176 | "items": { 177 | "$ref": "#/definitions/Pet" 178 | } 179 | } 180 | }, 181 | "400": { 182 | "description": "Invalid tag value" 183 | } 184 | }, 185 | "security": [ 186 | { 187 | "petstore_auth": ["write:pets", "read:pets"] 188 | } 189 | ], 190 | "deprecated": true 191 | } 192 | }, 193 | "/pet/{petId}": { 194 | "get": { 195 | "tags": ["pet"], 196 | "summary": "Find pet by ID", 197 | "description": "Returns a single pet", 198 | "operationId": "getPetById", 199 | "produces": ["application/xml", "application/json"], 200 | "parameters": [ 201 | { 202 | "name": "petId", 203 | "in": "path", 204 | "description": "ID of pet to return", 205 | "required": true, 206 | "type": "integer", 207 | "format": "int64" 208 | } 209 | ], 210 | "responses": { 211 | "200": { 212 | "description": "successful operation", 213 | "schema": { 214 | "$ref": "#/definitions/Pet" 215 | } 216 | }, 217 | "400": { 218 | "description": "Invalid ID supplied" 219 | }, 220 | "404": { 221 | "description": "Pet not found" 222 | } 223 | }, 224 | "security": [ 225 | { 226 | "api_key": [] 227 | } 228 | ] 229 | }, 230 | "post": { 231 | "tags": ["pet"], 232 | "summary": "Updates a pet in the store with form data", 233 | "description": "", 234 | "operationId": "updatePetWithForm", 235 | "consumes": ["application/x-www-form-urlencoded"], 236 | "produces": ["application/xml", "application/json"], 237 | "parameters": [ 238 | { 239 | "name": "petId", 240 | "in": "path", 241 | "description": "ID of pet that needs to be updated", 242 | "required": true, 243 | "type": "integer", 244 | "format": "int64" 245 | }, 246 | { 247 | "name": "name", 248 | "in": "formData", 249 | "description": "Updated name of the pet", 250 | "required": false, 251 | "type": "string" 252 | }, 253 | { 254 | "name": "status", 255 | "in": "formData", 256 | "description": "Updated status of the pet", 257 | "required": false, 258 | "type": "string" 259 | } 260 | ], 261 | "responses": { 262 | "405": { 263 | "description": "Invalid input" 264 | } 265 | }, 266 | "security": [ 267 | { 268 | "petstore_auth": ["write:pets", "read:pets"] 269 | } 270 | ] 271 | }, 272 | "delete": { 273 | "tags": ["pet"], 274 | "summary": "Deletes a pet", 275 | "description": "", 276 | "operationId": "deletePet", 277 | "produces": ["application/xml", "application/json"], 278 | "parameters": [ 279 | { 280 | "name": "api_key", 281 | "in": "header", 282 | "required": false, 283 | "type": "string" 284 | }, 285 | { 286 | "name": "petId", 287 | "in": "path", 288 | "description": "Pet id to delete", 289 | "required": true, 290 | "type": "integer", 291 | "format": "int64" 292 | } 293 | ], 294 | "responses": { 295 | "400": { 296 | "description": "Invalid ID supplied" 297 | }, 298 | "404": { 299 | "description": "Pet not found" 300 | } 301 | }, 302 | "security": [ 303 | { 304 | "petstore_auth": ["write:pets", "read:pets"] 305 | } 306 | ] 307 | } 308 | }, 309 | "/pet/{petId}/uploadImage": { 310 | "post": { 311 | "tags": ["pet"], 312 | "summary": "uploads an image", 313 | "description": "", 314 | "operationId": "uploadFile", 315 | "consumes": ["multipart/form-data"], 316 | "produces": ["application/json"], 317 | "parameters": [ 318 | { 319 | "name": "petId", 320 | "in": "path", 321 | "description": "ID of pet to update", 322 | "required": true, 323 | "type": "integer", 324 | "format": "int64" 325 | }, 326 | { 327 | "name": "additionalMetadata", 328 | "in": "formData", 329 | "description": "Additional data to pass to server", 330 | "required": false, 331 | "type": "string" 332 | }, 333 | { 334 | "name": "file", 335 | "in": "formData", 336 | "description": "file to upload", 337 | "required": false, 338 | "type": "file" 339 | } 340 | ], 341 | "responses": { 342 | "200": { 343 | "description": "successful operation", 344 | "schema": { 345 | "$ref": "#/definitions/ApiResponse" 346 | } 347 | } 348 | }, 349 | "security": [ 350 | { 351 | "petstore_auth": ["write:pets", "read:pets"] 352 | } 353 | ] 354 | } 355 | }, 356 | "/store/inventory": { 357 | "get": { 358 | "tags": ["store"], 359 | "summary": "Returns pet inventories by status", 360 | "description": "Returns a map of status codes to quantities", 361 | "operationId": "getInventory", 362 | "produces": ["application/json"], 363 | "parameters": [], 364 | "responses": { 365 | "200": { 366 | "description": "successful operation", 367 | "schema": { 368 | "type": "object", 369 | "additionalProperties": { 370 | "type": "integer", 371 | "format": "int32" 372 | } 373 | } 374 | } 375 | }, 376 | "security": [ 377 | { 378 | "api_key": [] 379 | } 380 | ] 381 | } 382 | }, 383 | "/store/order": { 384 | "post": { 385 | "tags": ["store"], 386 | "summary": "Place an order for a pet", 387 | "description": "", 388 | "operationId": "placeOrder", 389 | "produces": ["application/xml", "application/json"], 390 | "parameters": [ 391 | { 392 | "in": "body", 393 | "name": "body", 394 | "description": "order placed for purchasing the pet", 395 | "required": true, 396 | "schema": { 397 | "$ref": "#/definitions/Order" 398 | } 399 | } 400 | ], 401 | "responses": { 402 | "200": { 403 | "description": "successful operation", 404 | "schema": { 405 | "$ref": "#/definitions/Order" 406 | } 407 | }, 408 | "400": { 409 | "description": "Invalid Order" 410 | } 411 | } 412 | } 413 | }, 414 | "/store/order/{orderId}": { 415 | "get": { 416 | "tags": ["store"], 417 | "summary": "Find purchase order by ID", 418 | "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", 419 | "operationId": "getOrderById", 420 | "produces": ["application/xml", "application/json"], 421 | "parameters": [ 422 | { 423 | "name": "orderId", 424 | "in": "path", 425 | "description": "ID of pet that needs to be fetched", 426 | "required": true, 427 | "type": "integer", 428 | "maximum": 10, 429 | "minimum": 1, 430 | "format": "int64" 431 | } 432 | ], 433 | "responses": { 434 | "200": { 435 | "description": "successful operation", 436 | "schema": { 437 | "$ref": "#/definitions/Order" 438 | } 439 | }, 440 | "400": { 441 | "description": "Invalid ID supplied" 442 | }, 443 | "404": { 444 | "description": "Order not found" 445 | } 446 | } 447 | }, 448 | "delete": { 449 | "tags": ["store"], 450 | "summary": "Delete purchase order by ID", 451 | "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", 452 | "operationId": "deleteOrder", 453 | "produces": ["application/xml", "application/json"], 454 | "parameters": [ 455 | { 456 | "name": "orderId", 457 | "in": "path", 458 | "description": "ID of the order that needs to be deleted", 459 | "required": true, 460 | "type": "integer", 461 | "minimum": 1, 462 | "format": "int64" 463 | } 464 | ], 465 | "responses": { 466 | "400": { 467 | "description": "Invalid ID supplied" 468 | }, 469 | "404": { 470 | "description": "Order not found" 471 | } 472 | } 473 | } 474 | }, 475 | "/user": { 476 | "post": { 477 | "tags": ["user"], 478 | "summary": "Create user", 479 | "description": "This can only be done by the logged in user.", 480 | "operationId": "createUser", 481 | "produces": ["application/xml", "application/json"], 482 | "parameters": [ 483 | { 484 | "in": "body", 485 | "name": "body", 486 | "description": "Created user object", 487 | "required": true, 488 | "schema": { 489 | "$ref": "#/definitions/User" 490 | } 491 | } 492 | ], 493 | "responses": { 494 | "default": { 495 | "description": "successful operation" 496 | } 497 | } 498 | } 499 | }, 500 | "/user/createWithArray": { 501 | "post": { 502 | "tags": ["user"], 503 | "summary": "Creates list of users with given input array", 504 | "description": "", 505 | "operationId": "createUsersWithArrayInput", 506 | "produces": ["application/xml", "application/json"], 507 | "parameters": [ 508 | { 509 | "in": "body", 510 | "name": "body", 511 | "description": "List of user object", 512 | "required": true, 513 | "schema": { 514 | "type": "array", 515 | "items": { 516 | "$ref": "#/definitions/User" 517 | } 518 | } 519 | } 520 | ], 521 | "responses": { 522 | "default": { 523 | "description": "successful operation" 524 | } 525 | } 526 | } 527 | }, 528 | "/user/createWithList": { 529 | "post": { 530 | "tags": ["user"], 531 | "summary": "Creates list of users with given input array", 532 | "description": "", 533 | "operationId": "createUsersWithListInput", 534 | "produces": ["application/xml", "application/json"], 535 | "parameters": [ 536 | { 537 | "in": "body", 538 | "name": "body", 539 | "description": "List of user object", 540 | "required": true, 541 | "schema": { 542 | "type": "array", 543 | "items": { 544 | "$ref": "#/definitions/User" 545 | } 546 | } 547 | } 548 | ], 549 | "responses": { 550 | "default": { 551 | "description": "successful operation" 552 | } 553 | } 554 | } 555 | }, 556 | "/user/login": { 557 | "get": { 558 | "tags": ["user"], 559 | "summary": "Logs user into the system", 560 | "description": "", 561 | "operationId": "loginUser", 562 | "produces": ["application/xml", "application/json"], 563 | "parameters": [ 564 | { 565 | "name": "username", 566 | "in": "query", 567 | "description": "The user name for login", 568 | "required": true, 569 | "type": "string" 570 | }, 571 | { 572 | "name": "password", 573 | "in": "query", 574 | "description": "The password for login in clear text", 575 | "required": true, 576 | "type": "string" 577 | } 578 | ], 579 | "responses": { 580 | "200": { 581 | "description": "successful operation", 582 | "schema": { 583 | "type": "string" 584 | }, 585 | "headers": { 586 | "X-Rate-Limit": { 587 | "type": "integer", 588 | "format": "int32", 589 | "description": "calls per hour allowed by the user" 590 | }, 591 | "X-Expires-After": { 592 | "type": "string", 593 | "format": "date-time", 594 | "description": "date in UTC when token expires" 595 | } 596 | } 597 | }, 598 | "400": { 599 | "description": "Invalid username/password supplied" 600 | } 601 | } 602 | } 603 | }, 604 | "/user/logout": { 605 | "get": { 606 | "tags": ["user"], 607 | "summary": "Logs out current logged in user session", 608 | "description": "", 609 | "operationId": "logoutUser", 610 | "produces": ["application/xml", "application/json"], 611 | "parameters": [], 612 | "responses": { 613 | "default": { 614 | "description": "successful operation" 615 | } 616 | } 617 | } 618 | }, 619 | "/user/{username}": { 620 | "get": { 621 | "tags": ["user"], 622 | "summary": "Get user by user name", 623 | "description": "", 624 | "operationId": "getUserByName", 625 | "produces": ["application/xml", "application/json"], 626 | "parameters": [ 627 | { 628 | "name": "username", 629 | "in": "path", 630 | "description": "The name that needs to be fetched. Use user1 for testing. ", 631 | "required": true, 632 | "type": "string" 633 | } 634 | ], 635 | "responses": { 636 | "200": { 637 | "description": "successful operation", 638 | "schema": { 639 | "$ref": "#/definitions/User" 640 | } 641 | }, 642 | "400": { 643 | "description": "Invalid username supplied" 644 | }, 645 | "404": { 646 | "description": "User not found" 647 | } 648 | } 649 | }, 650 | "put": { 651 | "tags": ["user"], 652 | "summary": "Updated user", 653 | "description": "This can only be done by the logged in user.", 654 | "operationId": "updateUser", 655 | "produces": ["application/xml", "application/json"], 656 | "parameters": [ 657 | { 658 | "name": "username", 659 | "in": "path", 660 | "description": "name that need to be updated", 661 | "required": true, 662 | "type": "string" 663 | }, 664 | { 665 | "in": "body", 666 | "name": "body", 667 | "description": "Updated user object", 668 | "required": true, 669 | "schema": { 670 | "$ref": "#/definitions/User" 671 | } 672 | } 673 | ], 674 | "responses": { 675 | "400": { 676 | "description": "Invalid user supplied" 677 | }, 678 | "404": { 679 | "description": "User not found" 680 | } 681 | } 682 | }, 683 | "delete": { 684 | "tags": ["user"], 685 | "summary": "Delete user", 686 | "description": "This can only be done by the logged in user.", 687 | "operationId": "deleteUser", 688 | "produces": ["application/xml", "application/json"], 689 | "parameters": [ 690 | { 691 | "name": "username", 692 | "in": "path", 693 | "description": "The name that needs to be deleted", 694 | "required": true, 695 | "type": "string" 696 | } 697 | ], 698 | "responses": { 699 | "400": { 700 | "description": "Invalid username supplied" 701 | }, 702 | "404": { 703 | "description": "User not found" 704 | } 705 | } 706 | } 707 | } 708 | }, 709 | "securityDefinitions": { 710 | "petstore_auth": { 711 | "type": "oauth2", 712 | "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", 713 | "flow": "implicit", 714 | "scopes": { 715 | "write:pets": "modify pets in your account", 716 | "read:pets": "read your pets" 717 | } 718 | }, 719 | "api_key": { 720 | "type": "apiKey", 721 | "name": "api_key", 722 | "in": "header" 723 | } 724 | }, 725 | "definitions": { 726 | "Order": { 727 | "type": "object", 728 | "properties": { 729 | "id": { 730 | "type": "integer", 731 | "format": "int64" 732 | }, 733 | "petId": { 734 | "type": "integer", 735 | "format": "int64" 736 | }, 737 | "quantity": { 738 | "type": "integer", 739 | "format": "int32" 740 | }, 741 | "shipDate": { 742 | "type": "string", 743 | "format": "date-time" 744 | }, 745 | "status": { 746 | "type": "string", 747 | "description": "Order Status", 748 | "enum": ["placed", "approved", "delivered"] 749 | }, 750 | "complete": { 751 | "type": "boolean", 752 | "default": false 753 | } 754 | }, 755 | "xml": { 756 | "name": "Order" 757 | } 758 | }, 759 | "Category": { 760 | "type": "object", 761 | "properties": { 762 | "id": { 763 | "type": "integer", 764 | "format": "int64" 765 | }, 766 | "name": { 767 | "type": "string" 768 | } 769 | }, 770 | "xml": { 771 | "name": "Category" 772 | } 773 | }, 774 | "User": { 775 | "type": "object", 776 | "properties": { 777 | "id": { 778 | "type": "integer", 779 | "format": "int64" 780 | }, 781 | "username": { 782 | "type": "string" 783 | }, 784 | "firstName": { 785 | "type": "string" 786 | }, 787 | "lastName": { 788 | "type": "string" 789 | }, 790 | "email": { 791 | "type": "string" 792 | }, 793 | "password": { 794 | "type": "string" 795 | }, 796 | "phone": { 797 | "type": "string" 798 | }, 799 | "userStatus": { 800 | "type": "integer", 801 | "format": "int32", 802 | "description": "User Status" 803 | } 804 | }, 805 | "xml": { 806 | "name": "User" 807 | } 808 | }, 809 | "Tag": { 810 | "type": "object", 811 | "properties": { 812 | "id": { 813 | "type": "integer", 814 | "format": "int64" 815 | }, 816 | "name": { 817 | "type": "string" 818 | } 819 | }, 820 | "xml": { 821 | "name": "Tag" 822 | } 823 | }, 824 | "Pet": { 825 | "type": "object", 826 | "required": ["name", "photoUrls"], 827 | "properties": { 828 | "id": { 829 | "type": "integer", 830 | "format": "int64" 831 | }, 832 | "category": { 833 | "$ref": "#/definitions/Category" 834 | }, 835 | "name": { 836 | "type": "string", 837 | "example": "doggie" 838 | }, 839 | "photoUrls": { 840 | "type": "array", 841 | "xml": { 842 | "name": "photoUrl", 843 | "wrapped": true 844 | }, 845 | "items": { 846 | "type": "string" 847 | } 848 | }, 849 | "tags": { 850 | "type": "array", 851 | "xml": { 852 | "name": "tag", 853 | "wrapped": true 854 | }, 855 | "items": { 856 | "$ref": "#/definitions/Tag" 857 | } 858 | }, 859 | "status": { 860 | "type": "string", 861 | "description": "pet status in the store", 862 | "enum": ["available", "pending", "sold"] 863 | } 864 | }, 865 | "xml": { 866 | "name": "Pet" 867 | } 868 | }, 869 | "ApiResponse": { 870 | "type": "object", 871 | "properties": { 872 | "code": { 873 | "type": "integer", 874 | "format": "int32" 875 | }, 876 | "type": { 877 | "type": "string" 878 | }, 879 | "message": { 880 | "type": "string" 881 | } 882 | } 883 | } 884 | }, 885 | "externalDocs": { 886 | "description": "Find out more about Swagger", 887 | "url": "http://swagger.io" 888 | } 889 | } 890 | -------------------------------------------------------------------------------- /templates/interface.njk: -------------------------------------------------------------------------------- 1 | declare namespace {{ namespace }} { 2 | {% for type in list -%} 3 | {%- if type.props.length %} 4 | {%- if type.isEnum %} 5 | enum {{ type.typeName | safe }} 6 | {%- else %} 7 | {{ declareType }} {{ type.typeName | safe }} {{ equalSymbol }} 8 | {%- endif %} 9 | {%- for prop in type.props %} 10 | {%- if prop.length > 1 %} 11 | { 12 | {%- endif %} 13 | {%- if prop.length == 1 %} 14 | {%- if not prop[0].$ref or prop[0].name %} 15 | { 16 | {%- endif %} 17 | {%- endif %} 18 | {%- for p in prop %} 19 | {%- if p.desc %} 20 | /** {{ p.desc }} */ 21 | {%- endif %} 22 | {%- if p["$ref"] and not p.name %} 23 | // {{ p.$ref }} 24 | {{ p.type | safe }} 25 | {%- else %} 26 | {%- if nullable %} 27 | '{{ p.name }}': {{ p.type | safe }}{{'' if p.required else '| null'}}; 28 | {%- else %} 29 | '{{ p.name }}'{{ '' if p.required else '?' }}: {{ p.type | safe }}; 30 | {%- endif %} 31 | {%- endif %} 32 | {%- endfor %} 33 | {%- if prop.length > 1 %} 34 | } 35 | {%- endif %} 36 | {%- if prop.length == 1 %} 37 | {%- if not prop[0].$ref or prop[0].name %} 38 | } 39 | {%- endif %} 40 | {%- endif %} 41 | {%- if prop.length == 0 %} 42 | {} 43 | {%- endif %} 44 | {{ '' if loop.last === true else ' & ' }} 45 | {%- endfor %} 46 | {%- else %} 47 | {%- if type.isEnum %} 48 | enum {{ type.typeName | safe }} {{ type.type }}; 49 | {%- else %} 50 | type {{ type.typeName | safe }} = {{ type.type }}; 51 | {%- endif %} 52 | {%- endif %} 53 | {% endfor %} 54 | } 55 | -------------------------------------------------------------------------------- /templates/serviceController.njk: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | /* eslint-disable */ 3 | {{ requestImportStatement }} 4 | 5 | {% for api in list -%} 6 | /** {{ api.desc if api.desc else '此处后端没有提供注释' }} {{api.method | upper}} {{ api.pathInComment | safe }} */ 7 | export async function {{ api.functionName }}( 8 | {%- if api.params and api.hasParams %} 9 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) 10 | params 11 | {%- if genType === "ts" -%} 12 | : {{namespace}}.{{api.typeName}} 13 | {# header 入参 -#} 14 | {% if api.params.header -%} 15 | & { // header 16 | {% for param in api.params.header -%} 17 | {% if param.description -%} 18 | /** {{ param.description }} */ 19 | {% endif -%} 20 | '{{ param.name }}' 21 | {{- "?" if not param.required }} 22 | {{- (": " + param.type + ";") | safe }} 23 | {% endfor -%} 24 | } 25 | {%- endif -%} 26 | {%- endif -%} 27 | {%- if api.hasParams -%} 28 | {{ "," if api.body or api.file}} 29 | {%- endif -%} 30 | {%- endif -%} 31 | {%- if api.body -%} 32 | body 33 | {%- if genType === "ts" -%} 34 | : {% if api.body.propertiesList %}{ 35 | {%- for prop in api.body.propertiesList %} 36 | {% if prop.schema.description -%} 37 | /** {{ prop.schema.description }} */ 38 | {% endif -%} 39 | {{ prop.key }}{{ "?" if not prop.schema.required }}: {{ prop.schema.type }}, 40 | {%- endfor %} 41 | } 42 | {%- else -%} 43 | {{ api.body.type }} 44 | {%- endif -%} 45 | {%- endif -%} 46 | {{ "," if api.file }} 47 | {%- endif %} 48 | {%- if api.file -%} 49 | {%- for file in api.file -%} 50 | {{file.title | safe}} 51 | {%- if genType === "ts" -%} 52 | {{- "?" if not api.file.required -}} 53 | : File {{ "[]" if file.multiple }} 54 | {%- endif -%} 55 | {{"," if not loop.last }} 56 | {%- endfor -%} 57 | {%- endif -%} 58 | {{ "," if api.body or api.hasParams or api.file }} 59 | options {{ ("?: " + requestOptionsType) if genType === "ts" }} 60 | ) { 61 | {% if api.params and api.params.path -%} 62 | const { {% for param in api.params.path %}'{{ param.name }}': {{ param.alias }}, {% endfor %} 63 | {% if api.params.path -%} 64 | ...queryParams 65 | {% endif -%} 66 | } = params; 67 | {% endif -%} 68 | {% if api.hasFormData -%} 69 | const formData = new FormData(); 70 | {% if api.file -%} 71 | {% for file in api.file %} 72 | if({{file.title | safe}}){ 73 | {% if file.multiple %} 74 | {{file.title | safe}}.forEach(f => formData.append('{{file.title | safe}}', f || '')); 75 | {% else %} 76 | formData.append('{{file.title | safe}}', {{file.title | safe}}) 77 | {% endif %} 78 | } 79 | {% endfor %} 80 | {%- endif -%} 81 | {% if api.body %} 82 | Object.keys(body).forEach(ele => { 83 | {% if genType === "ts" %} 84 | const item = (body as any)[ele]; 85 | {% else %} 86 | const item = body[ele]; 87 | {% endif %} 88 | if (item !== undefined && item !== null) { 89 | {% if genType === "ts" %} 90 | if (typeof item === 'object' && !(item instanceof File)) { 91 | if (item instanceof Array) { 92 | item.forEach((f) => formData.append(ele, f || '')); 93 | } else { 94 | formData.append(ele, JSON.stringify(item)); 95 | } 96 | } else { 97 | formData.append(ele, item); 98 | } 99 | {% else %} 100 | formData.append(ele, typeof item === 'object' ? JSON.stringify(item) : item); 101 | {% endif %} 102 | } 103 | }); 104 | {% endif %} 105 | {% endif -%} 106 | 107 | {% if api.hasPathVariables or api.hasApiPrefix -%} 108 | return request{{ ("<" + api.response.type + ">") | safe if genType === "ts" }}(`{{ api.path | safe }}`, { 109 | {% else -%} 110 | return request{{ ("<" + api.response.type + ">") | safe if genType === "ts" }}('{{ api.path }}', { 111 | {% endif -%} 112 | method: '{{ api.method | upper }}', 113 | {%- if api.hasHeader and api.body.mediaType not in ["multipart/form-data"]%} 114 | headers: { 115 | {%- if api.body.mediaType %} 116 | 'Content-Type': '{{ api.body.mediaType | safe }}', 117 | {%- endif %} 118 | }, 119 | {%- endif %} 120 | {%- if api.params and api.hasParams %} 121 | params: { 122 | {%- for query in api.params.query %} 123 | {% if query.schema.default -%} 124 | // {{query.name | safe}} has a default value: {{ query.schema.default | safe }} 125 | '{{query.name | safe}}': '{{query.schema.default | safe}}', 126 | {%- endif -%} 127 | {%- endfor -%} 128 | ...{{ 'queryParams' if api.params and api.params.path else 'params' }}, 129 | {%- for query in api.params.query %} 130 | {%- if query.isComplexType %} 131 | '{{query.name | safe}}': undefined, 132 | ...{{ 'queryParams' if api.params and api.params.path else 'params' }}['{{query.name | safe}}'], 133 | {%- endif %} 134 | {%- endfor -%} 135 | }, 136 | {%- endif %} 137 | {%- if api.hasFormData %} 138 | data: formData, 139 | {%- if api.body.mediaType === "multipart/form-data" %} 140 | requestType: 'form', 141 | {%- endif %} 142 | {%- else %} 143 | {%- if api.body %} 144 | data: body, 145 | {%- endif %} 146 | {%- endif %} 147 | ...(options || {{api.options | dump}}), 148 | }); 149 | } 150 | 151 | {% endfor -%} 152 | -------------------------------------------------------------------------------- /templates/serviceIndex.njk: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | /* eslint-disable */ 3 | // API 更新时间:{{ apiResourceModifyTime }} 4 | // API 唯一标识:{{ apiResourceId }} 5 | {% for api in list -%} 6 | import * as {{ api.controllerName }} from './{{ api.fileName }}' 7 | {% endfor -%} 8 | 9 | 10 | export default { 11 | {% for api in list -%} 12 | {{ api.controllerName }}, 13 | {% endfor -%} 14 | } 15 | -------------------------------------------------------------------------------- /test/apispe/api/api0.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | /* eslint-disable */ 3 | import { request } from 'umi'; 4 | 5 | /** Run Template API POST /agent/runtemplate */ 6 | export async function postAgentRuntemplate(options?: { [key: string]: any }) { 7 | return request<{ message?: any }>('/agent/runtemplate', { 8 | method: 'POST', 9 | ...(options || {}), 10 | }); 11 | } 12 | 13 | /** Active Licence API POST /licence/active */ 14 | export async function postLicenceActive(body: {}, file?: File, options?: { [key: string]: any }) { 15 | const formData = new FormData(); 16 | 17 | if (file) { 18 | formData.append('file', file); 19 | } 20 | 21 | Object.keys(body).forEach((ele) => { 22 | const item = (body as any)[ele]; 23 | 24 | if (item !== undefined && item !== null) { 25 | if (typeof item === 'object' && !(item instanceof File)) { 26 | if (item instanceof Array) { 27 | item.forEach((f) => formData.append(ele, f || '')); 28 | } else { 29 | formData.append(ele, JSON.stringify(item)); 30 | } 31 | } else { 32 | formData.append(ele, item); 33 | } 34 | } 35 | }); 36 | 37 | return request<{ message?: any }>('/licence/active', { 38 | method: 'POST', 39 | data: formData, 40 | requestType: 'form', 41 | ...(options || {}), 42 | }); 43 | } 44 | 45 | /** Show Licence API GET /licence/info */ 46 | export async function getLicenceInfo(options?: { [key: string]: any }) { 47 | return request<{ message?: any }>('/licence/info', { 48 | method: 'GET', 49 | ...(options || {}), 50 | }); 51 | } 52 | 53 | /** Show machine code API GET /licence/machinecode */ 54 | export async function getLicenceMachinecode(options?: { [key: string]: any }) { 55 | return request<{ message?: any }>('/licence/machinecode', { 56 | method: 'GET', 57 | ...(options || {}), 58 | }); 59 | } 60 | 61 | /** Licence Test API GET /licence/test */ 62 | export async function getLicenceTest(options?: { [key: string]: any }) { 63 | return request<{ message?: any }>('/licence/test', { 64 | method: 'GET', 65 | ...(options || {}), 66 | }); 67 | } 68 | 69 | /** Create one Mydata for user (Folders,Files, & APIDataFeeds) POST /mydata/create */ 70 | export async function postMydataCreate( 71 | body: { 72 | /** The type of data to create ('folder', 'file', 'api_data_feed'). */ 73 | type: string; 74 | /** Name of the folder or API data feed. Required for 'folder' and 'api_data_feed'. */ 75 | name?: string; 76 | /** Name of the folder to upload files into. Required for 'file'. */ 77 | folder_name?: string; 78 | /** Permission setting for the folder. Defaults to 'private' if not specified. */ 79 | permission?: string; 80 | /** Boolean flag to set the folder as searchable. */ 81 | is_searchable?: boolean; 82 | /** API code for the API data feed. Required for 'api_data_feed'. */ 83 | api_code?: string; 84 | }, 85 | options?: { [key: string]: any }, 86 | ) { 87 | return request<{ message?: any }>('/mydata/create', { 88 | method: 'POST', 89 | headers: { 90 | 'Content-Type': 'application/x-www-form-urlencoded', 91 | }, 92 | data: body, 93 | ...(options || {}), 94 | }); 95 | } 96 | 97 | /** Delete one Mydata for user (Folders,Files, & APIDataFeeds) DELETE /mydata/delete */ 98 | export async function deleteMydataOpenApiDelete(options?: { [key: string]: any }) { 99 | return request<{ message?: any }>('/mydata/delete', { 100 | method: 'DELETE', 101 | ...(options || {}), 102 | }); 103 | } 104 | 105 | /** List all mydata (Folders,Files, & APIDataFeeds) GET /mydata/list */ 106 | export async function getMydataList(options?: { [key: string]: any }) { 107 | return request<{ message?: any }>('/mydata/list', { 108 | method: 'GET', 109 | ...(options || {}), 110 | }); 111 | } 112 | 113 | /** Search one Mydata for its name or tags. GET /mydata/search */ 114 | export async function getMydataSearch( 115 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) 116 | params: API.getMydataSearchParams, 117 | options?: { [key: string]: any }, 118 | ) { 119 | return request('/mydata/search', { 120 | method: 'GET', 121 | params: { 122 | ...params, 123 | }, 124 | ...(options || {}), 125 | }); 126 | } 127 | 128 | /** Update one Mydata for user (Folders,Files, & APIDataFeeds) GET /mydata/update */ 129 | export async function getMydataUpdate(options?: { [key: string]: any }) { 130 | return request<{ message?: any }>('/mydata/update', { 131 | method: 'GET', 132 | ...(options || {}), 133 | }); 134 | } 135 | 136 | /** Update one Mydata for user (Folders,Files, & APIDataFeeds) POST /mydata/update */ 137 | export async function postMydataUpdate(options?: { [key: string]: any }) { 138 | return request<{ message?: any }>('/mydata/update', { 139 | method: 'POST', 140 | ...(options || {}), 141 | }); 142 | } 143 | -------------------------------------------------------------------------------- /test/apispe/api/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | /* eslint-disable */ 3 | // API 更新时间: 4 | // API 唯一标识: 5 | import * as api0 from './api0'; 6 | export default { 7 | api0, 8 | }; 9 | -------------------------------------------------------------------------------- /test/apispe/api/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace API {} 2 | -------------------------------------------------------------------------------- /test/example-files/apispec_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": {}, 3 | "info": { 4 | "description": "API for InvRet", 5 | "title": "InvRet API", 6 | "version": "0.3.0" 7 | }, 8 | "paths": { 9 | "/agent/runtemplate": { 10 | "post": { 11 | "responses": { 12 | "200": { 13 | "content": { 14 | "application/json": { 15 | "schema": { 16 | "properties": { 17 | "message": { 18 | "example": { 19 | "message": "Module successfully launched" 20 | }, 21 | "type": "text" 22 | } 23 | }, 24 | "type": "object" 25 | } 26 | } 27 | }, 28 | "description": "Run Template" 29 | } 30 | }, 31 | "summary": "Run Template API" 32 | } 33 | }, 34 | "/licence/active": { 35 | "post": { 36 | "consumes": ["multipart/form-data"], 37 | "parameters": [ 38 | { 39 | "description": "The file to upload", 40 | "in": "formData", 41 | "name": "file", 42 | "required": true, 43 | "type": "file" 44 | } 45 | ], 46 | "responses": { 47 | "200": { 48 | "content": { 49 | "application/json": { 50 | "schema": { 51 | "properties": { 52 | "message": { 53 | "example": { 54 | "Expired": "2024-05-15 12:45:32", 55 | "HasFeature1:": "True", 56 | "HasFeature2:": "True", 57 | "HasFeature3:": "False", 58 | "UserCount:": 10 59 | }, 60 | "type": "text" 61 | } 62 | }, 63 | "type": "object" 64 | } 65 | } 66 | }, 67 | "description": "Active licence info" 68 | } 69 | }, 70 | "summary": "Active Licence API" 71 | } 72 | }, 73 | "/licence/info": { 74 | "get": { 75 | "responses": { 76 | "200": { 77 | "content": { 78 | "application/json": { 79 | "schema": { 80 | "properties": { 81 | "message": { 82 | "example": { 83 | "Expired": "2024-05-15 12:45:32", 84 | "HasFeature1:": "True", 85 | "HasFeature2:": "True", 86 | "HasFeature3:": "False", 87 | "UserCount:": 10 88 | }, 89 | "type": "text" 90 | } 91 | }, 92 | "type": "object" 93 | } 94 | } 95 | }, 96 | "description": "Show Licence info" 97 | } 98 | }, 99 | "summary": "Show Licence API" 100 | } 101 | }, 102 | "/licence/machinecode": { 103 | "get": { 104 | "responses": { 105 | "200": { 106 | "content": { 107 | "application/json": { 108 | "schema": { 109 | "properties": { 110 | "message": { 111 | "example": { 112 | "MachineCode": "632d813473d18c3d2b1a9f9e07b19750394aab4090c63ad3609b2f1ba69bb656" 113 | }, 114 | "type": "text" 115 | } 116 | }, 117 | "type": "object" 118 | } 119 | } 120 | }, 121 | "description": "Show machine code info" 122 | } 123 | }, 124 | "summary": "Show machine code API" 125 | } 126 | }, 127 | "/licence/test": { 128 | "get": { 129 | "responses": { 130 | "200": { 131 | "content": { 132 | "application/json": { 133 | "schema": { 134 | "properties": { 135 | "message": { 136 | "example": { 137 | "message": "ok!" 138 | }, 139 | "type": "text" 140 | } 141 | }, 142 | "type": "object" 143 | } 144 | } 145 | }, 146 | "description": "Test licence info" 147 | } 148 | }, 149 | "summary": "Licence Test API" 150 | } 151 | }, 152 | "/mydata/create": { 153 | "post": { 154 | "parameters": [ 155 | { 156 | "description": "The type of data to create ('folder', 'file', 'api_data_feed').", 157 | "in": "formData", 158 | "name": "type", 159 | "required": true, 160 | "type": "string" 161 | }, 162 | { 163 | "description": "Name of the folder or API data feed. Required for 'folder' and 'api_data_feed'.", 164 | "in": "formData", 165 | "name": "name", 166 | "required": false, 167 | "type": "string" 168 | }, 169 | { 170 | "description": "File or files to upload. Required for 'file' type.", 171 | "in": "formData", 172 | "name": "file", 173 | "required": false, 174 | "type": "file" 175 | }, 176 | { 177 | "description": "Name of the folder to upload files into. Required for 'file'.", 178 | "in": "formData", 179 | "name": "folder_name", 180 | "required": false, 181 | "type": "string" 182 | }, 183 | { 184 | "description": "Permission setting for the folder. Defaults to 'private' if not specified.", 185 | "in": "formData", 186 | "name": "permission", 187 | "required": false, 188 | "type": "string" 189 | }, 190 | { 191 | "description": "Boolean flag to set the folder as searchable.", 192 | "in": "formData", 193 | "name": "is_searchable", 194 | "required": false, 195 | "type": "boolean" 196 | }, 197 | { 198 | "description": "API code for the API data feed. Required for 'api_data_feed'.", 199 | "in": "formData", 200 | "name": "api_code", 201 | "required": false, 202 | "type": "string" 203 | } 204 | ], 205 | "responses": { 206 | "200": { 207 | "content": { 208 | "application/json": { 209 | "schema": { 210 | "properties": { 211 | "message": { 212 | "example": { 213 | "message": "Mydata successfully created" 214 | }, 215 | "type": "text" 216 | } 217 | }, 218 | "type": "object" 219 | } 220 | } 221 | }, 222 | "description": "Create Mydata" 223 | } 224 | }, 225 | "summary": "Create one Mydata for user (Folders,Files, & APIDataFeeds)" 226 | } 227 | }, 228 | "/mydata/delete": { 229 | "delete": { 230 | "responses": { 231 | "200": { 232 | "content": { 233 | "application/json": { 234 | "schema": { 235 | "properties": { 236 | "message": { 237 | "example": { 238 | "message": "Mydata successfully deleted" 239 | }, 240 | "type": "text" 241 | } 242 | }, 243 | "type": "object" 244 | } 245 | } 246 | }, 247 | "description": "Delete Mydata" 248 | } 249 | }, 250 | "summary": "Delete one Mydata for user (Folders,Files, & APIDataFeeds)" 251 | } 252 | }, 253 | "/mydata/list": { 254 | "get": { 255 | "responses": { 256 | "200": { 257 | "content": { 258 | "application/json": { 259 | "schema": { 260 | "properties": { 261 | "message": { 262 | "example": { 263 | "message": "Module successfully launched" 264 | }, 265 | "type": "text" 266 | } 267 | }, 268 | "type": "object" 269 | } 270 | } 271 | }, 272 | "description": "List Mydata" 273 | } 274 | }, 275 | "summary": "List all mydata (Folders,Files, & APIDataFeeds)" 276 | } 277 | }, 278 | "/mydata/search": { 279 | "get": { 280 | "parameters": [ 281 | { 282 | "description": "The search keyword to look for in MyData names or tags.", 283 | "in": "query", 284 | "name": "query", 285 | "required": true, 286 | "type": "string" 287 | }, 288 | { 289 | "description": "Type for the keyword. 'folder' or 'file'", 290 | "in": "query", 291 | "name": "type", 292 | "required": true, 293 | "type": "string" 294 | } 295 | ], 296 | "responses": { 297 | "200": { 298 | "description": "An array of MyData entries that match the search criteria.", 299 | "items": { 300 | "id": { 301 | "example": 1, 302 | "format": "int64", 303 | "type": "integer" 304 | }, 305 | "name": { 306 | "example": "Sample Data", 307 | "type": "string" 308 | }, 309 | "properties": null, 310 | "tags": { 311 | "example": "example tag", 312 | "items": null, 313 | "type": "string" 314 | }, 315 | "type": "object" 316 | }, 317 | "schema": null, 318 | "type": "array" 319 | } 320 | }, 321 | "summary": "Search one Mydata for its name or tags." 322 | } 323 | }, 324 | "/mydata/update": { 325 | "get": { 326 | "responses": { 327 | "200": { 328 | "content": { 329 | "application/json": { 330 | "schema": { 331 | "properties": { 332 | "message": { 333 | "example": { 334 | "message": "Mydata successfully updated" 335 | }, 336 | "type": "text" 337 | } 338 | }, 339 | "type": "object" 340 | } 341 | } 342 | }, 343 | "description": "Update Mydata" 344 | } 345 | }, 346 | "summary": "Update one Mydata for user (Folders,Files, & APIDataFeeds)" 347 | }, 348 | "post": { 349 | "responses": { 350 | "200": { 351 | "content": { 352 | "application/json": { 353 | "schema": { 354 | "properties": { 355 | "message": { 356 | "example": { 357 | "message": "Mydata successfully updated" 358 | }, 359 | "type": "text" 360 | } 361 | }, 362 | "type": "object" 363 | } 364 | } 365 | }, 366 | "description": "Update Mydata" 367 | } 368 | }, 369 | "summary": "Update one Mydata for user (Folders,Files, & APIDataFeeds)" 370 | } 371 | } 372 | }, 373 | "swagger": "2.0" 374 | } 375 | -------------------------------------------------------------------------------- /test/example-files/swagger-custom-hook.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.3", 3 | "info": { 4 | "version": "1.0.20210224155647", 5 | "description": "custom-hook-swagger", 6 | "title": "custom-hook-test" 7 | }, 8 | "servers": [ 9 | { 10 | "url": "http://127.0.0.1:9421", 11 | "description": "Inferred Url" 12 | } 13 | ], 14 | "tags": [ 15 | { 16 | "name": "帐号 租户帐号 UserAccount" 17 | }, 18 | { 19 | "name": "帐号 验证码 Captcha" 20 | } 21 | ], 22 | "paths": { 23 | "/api/v1/captcha/img/check.json": { 24 | "post": { 25 | "tags": [ 26 | "帐号 验证码 Captcha" 27 | ], 28 | "summary": "验证图片验证码是否正确", 29 | "operationId": "checkImgCaptchaUsingPOST", 30 | "requestBody": { 31 | "content": { 32 | "application/json": { 33 | "schema": { 34 | "$ref": "#/components/schemas/CheckImgCaptchaReq" 35 | } 36 | } 37 | } 38 | }, 39 | "responses": { 40 | "200": { 41 | "description": "OK", 42 | "content": { 43 | "*/*": { 44 | "schema": { 45 | "$ref": "#/components/schemas/ApiRes«CaptchaTokenVO»" 46 | } 47 | } 48 | } 49 | }, 50 | "201": { 51 | "description": "Created" 52 | }, 53 | "401": { 54 | "description": "Unauthorized" 55 | }, 56 | "403": { 57 | "description": "Forbidden" 58 | }, 59 | "404": { 60 | "description": "Not Found" 61 | } 62 | } 63 | } 64 | }, 65 | "/api/v1/captcha/img/get.json": { 66 | "post": { 67 | "tags": [ 68 | "帐号 验证码 Captcha" 69 | ], 70 | "summary": "获取图片验证码", 71 | "operationId": "getImageCaptchaUsingPOST", 72 | "requestBody": { 73 | "content": { 74 | "application/json": { 75 | "schema": { 76 | "$ref": "#/components/schemas/ImgCaptchaReq" 77 | } 78 | } 79 | } 80 | }, 81 | "responses": { 82 | "200": { 83 | "description": "OK", 84 | "content": { 85 | "*/*": { 86 | "schema": { 87 | "$ref": "#/components/schemas/ApiRes«ImgCaptchaDTO»" 88 | } 89 | } 90 | } 91 | }, 92 | "201": { 93 | "description": "Created" 94 | }, 95 | "401": { 96 | "description": "Unauthorized" 97 | }, 98 | "403": { 99 | "description": "Forbidden" 100 | }, 101 | "404": { 102 | "description": "Not Found" 103 | } 104 | } 105 | } 106 | }, 107 | "/api/v1/user/account/login.json": { 108 | "post": { 109 | "tags": [ 110 | "帐号 租户帐号 UserAccount" 111 | ], 112 | "summary": "登录 - 帐密登录", 113 | "description":"参数:account", 114 | "operationId": "accountLoginUsingPOST", 115 | "requestBody": { 116 | "content": { 117 | "application/json": { 118 | "schema": { 119 | "$ref": "#/components/schemas/AccountLoginReq" 120 | } 121 | } 122 | } 123 | }, 124 | "responses": { 125 | "default":{ 126 | "description":"true代表登录成功,否则为提示信息", 127 | "content":{"*/*":{"schema":{"type":"string"}}} 128 | }, 129 | "200": { 130 | "description": "OK", 131 | "content": { 132 | "*/*": { 133 | "schema": { 134 | "$ref": "#/components/schemas/ApiRes«TokenDTO»" 135 | } 136 | } 137 | } 138 | }, 139 | "201": { 140 | "description": "Created" 141 | }, 142 | "401": { 143 | "description": "Unauthorized" 144 | }, 145 | "403": { 146 | "description": "Forbidden" 147 | }, 148 | "404": { 149 | "description": "Not Found" 150 | } 151 | } 152 | } 153 | }, 154 | "/api/v1/user/account/loginIsNeedCaptcha.json": { 155 | "post": { 156 | "tags": [ 157 | "帐号 租户帐号 UserAccount" 158 | ], 159 | "summary": "登录 - 帐密登录是否需验证码验证", 160 | "operationId": "loginIsNeedCaptchaUsingPOST", 161 | "requestBody": { 162 | "content": { 163 | "application/json": { 164 | "schema": { 165 | "$ref": "#/components/schemas/LoginIsNeedCaptchaReq" 166 | } 167 | } 168 | } 169 | }, 170 | "responses": { 171 | "200": { 172 | "description": "OK", 173 | "content": { 174 | "*/*": { 175 | "schema": { 176 | "$ref": "#/components/schemas/ApiRes«boolean»" 177 | } 178 | } 179 | } 180 | }, 181 | "201": { 182 | "description": "Created" 183 | }, 184 | "401": { 185 | "description": "Unauthorized" 186 | }, 187 | "403": { 188 | "description": "Forbidden" 189 | }, 190 | "404": { 191 | "description": "Not Found" 192 | } 193 | } 194 | } 195 | }, 196 | "/api/v1/user/account/register.json": { 197 | "post": { 198 | "tags": [ 199 | "帐号 租户帐号 UserAccount" 200 | ], 201 | "summary": "注册 - 帐密方式", 202 | "description": "图片验证码验证方式二选一:ID + token || ID + 验证码", 203 | "operationId": "accountRegisterUsingPOST", 204 | "requestBody": { 205 | "content": { 206 | "application/json": { 207 | "schema": { 208 | "$ref": "#/components/schemas/AccountRegisterReq" 209 | } 210 | } 211 | } 212 | }, 213 | "responses": { 214 | "200": { 215 | "description": "OK", 216 | "content": { 217 | "*/*": { 218 | "schema": { 219 | "$ref": "#/components/schemas/ApiRes«TokenDTO»" 220 | } 221 | } 222 | } 223 | }, 224 | "201": { 225 | "description": "Created" 226 | }, 227 | "401": { 228 | "description": "Unauthorized" 229 | }, 230 | "403": { 231 | "description": "Forbidden" 232 | }, 233 | "404": { 234 | "description": "Not Found" 235 | } 236 | } 237 | } 238 | }, 239 | "/api/v1/user/edit/pwd.json": { 240 | "post": { 241 | "tags": [ 242 | "帐号 租户帐号 UserAccount" 243 | ], 244 | "summary": "修改密码 - 原密码方式", 245 | "operationId": "editPasswordUsingPOST", 246 | "requestBody": { 247 | "content": { 248 | "application/json": { 249 | "schema": { 250 | "$ref": "#/components/schemas/EditPasswordReq" 251 | } 252 | } 253 | } 254 | }, 255 | "responses": { 256 | "200": { 257 | "description": "OK", 258 | "content": { 259 | "*/*": { 260 | "schema": { 261 | "$ref": "#/components/schemas/ApiRes«TokenDTO»" 262 | } 263 | } 264 | } 265 | }, 266 | "201": { 267 | "description": "Created" 268 | }, 269 | "401": { 270 | "description": "Unauthorized" 271 | }, 272 | "403": { 273 | "description": "Forbidden" 274 | }, 275 | "404": { 276 | "description": "Not Found" 277 | } 278 | } 279 | } 280 | }, 281 | "/api/v1/user/forgot/pwd/sms.json": { 282 | "post": { 283 | "tags": [ 284 | "帐号 租户帐号 UserAccount" 285 | ], 286 | "summary": "忘记密码 - 手机号方式", 287 | "operationId": "forgotPasswordUsingPOST", 288 | "requestBody": { 289 | "content": { 290 | "application/json": { 291 | "schema": { 292 | "$ref": "#/components/schemas/ApiReq" 293 | } 294 | } 295 | } 296 | }, 297 | "responses": { 298 | "200": { 299 | "description": "OK", 300 | "content": { 301 | "*/*": { 302 | "schema": { 303 | "$ref": "#/components/schemas/ApiRes«TokenDTO»" 304 | } 305 | } 306 | } 307 | }, 308 | "201": { 309 | "description": "Created" 310 | }, 311 | "401": { 312 | "description": "Unauthorized" 313 | }, 314 | "403": { 315 | "description": "Forbidden" 316 | }, 317 | "404": { 318 | "description": "Not Found" 319 | } 320 | } 321 | } 322 | } 323 | }, 324 | "components": { 325 | "schemas": { 326 | "AccountLoginReq": { 327 | "title": "AccountLoginReq", 328 | "required": [ 329 | "password", 330 | "username" 331 | ], 332 | "type": "object", 333 | "properties": { 334 | "captcha": { 335 | "type": "string", 336 | "description": "验证码" 337 | }, 338 | "captchaId": { 339 | "type": "integer", 340 | "description": "验证码ID", 341 | "format": "int64" 342 | }, 343 | "captchaToken": { 344 | "type": "string", 345 | "description": "验证令牌" 346 | }, 347 | "password": { 348 | "type": "string", 349 | "description": "密码(客户端使用 “墙]・▽・)ノ 嗨,美女+密码” 的MD5值提交,32位小写)", 350 | "example": "123456" 351 | }, 352 | "username": { 353 | "type": "string", 354 | "description": "用户名", 355 | "example": "fastdev" 356 | } 357 | } 358 | }, 359 | "AccountRegisterReq": { 360 | "title": "AccountRegisterReq", 361 | "required": [ 362 | "captchaId", 363 | "password", 364 | "username" 365 | ], 366 | "type": "object", 367 | "properties": { 368 | "captcha": { 369 | "type": "string", 370 | "description": "验证码" 371 | }, 372 | "captchaId": { 373 | "type": "integer", 374 | "description": "验证码ID", 375 | "format": "int64" 376 | }, 377 | "captchaToken": { 378 | "type": "string", 379 | "description": "验证令牌" 380 | }, 381 | "password": { 382 | "type": "string", 383 | "description": "密码(输入的密码,需要检测密码是否合法)", 384 | "example": "123456" 385 | }, 386 | "username": { 387 | "type": "string", 388 | "description": "用户名", 389 | "example": "fastdev" 390 | } 391 | } 392 | }, 393 | "ApiReq": { 394 | "title": "ApiReq", 395 | "type": "object" 396 | }, 397 | "ApiRes«CaptchaTokenVO»": { 398 | "title": "ApiRes«CaptchaTokenVO»", 399 | "required": [ 400 | "code", 401 | "msg", 402 | "showType" 403 | ], 404 | "type": "object", 405 | "properties": { 406 | "code": { 407 | "type": "integer", 408 | "description": "状态码;大于零为处理成功的不同状态,小于零的状态则视为失败的情况", 409 | "format": "int32", 410 | "example": 200 411 | }, 412 | "data": { 413 | "description": "相应数据", 414 | "$ref": "#/components/schemas/CaptchaTokenVO", 415 | "example": "{}" 416 | }, 417 | "msg": { 418 | "type": "string", 419 | "description": "提示消息", 420 | "example": "请求成功" 421 | }, 422 | "showType": { 423 | "type": "string", 424 | "description": "消息提示方式", 425 | "example": "0", 426 | "enum": [ 427 | "DIALOG", 428 | "ERROR", 429 | "NOTIFICATION", 430 | "PAGE", 431 | "SLIENT", 432 | "SUCCESS", 433 | "UNUSED_6", 434 | "UNUSED_7", 435 | "UNUSED_8", 436 | "WARN" 437 | ] 438 | } 439 | } 440 | }, 441 | "ApiRes«ImgCaptchaDTO»": { 442 | "title": "ApiRes«ImgCaptchaDTO»", 443 | "required": [ 444 | "code", 445 | "msg", 446 | "showType" 447 | ], 448 | "type": "object", 449 | "properties": { 450 | "code": { 451 | "type": "integer", 452 | "description": "状态码;大于零为处理成功的不同状态,小于零的状态则视为失败的情况", 453 | "format": "int32", 454 | "example": 200 455 | }, 456 | "data": { 457 | "description": "相应数据", 458 | "$ref": "#/components/schemas/ImgCaptchaDTO", 459 | "example": "{}" 460 | }, 461 | "msg": { 462 | "type": "string", 463 | "description": "提示消息", 464 | "example": "请求成功" 465 | }, 466 | "showType": { 467 | "type": "string", 468 | "description": "消息提示方式", 469 | "example": "0", 470 | "enum": [ 471 | "DIALOG", 472 | "ERROR", 473 | "NOTIFICATION", 474 | "PAGE", 475 | "SLIENT", 476 | "SUCCESS", 477 | "UNUSED_6", 478 | "UNUSED_7", 479 | "UNUSED_8", 480 | "WARN" 481 | ] 482 | } 483 | } 484 | }, 485 | "ApiRes«TokenDTO»": { 486 | "title": "ApiRes«TokenDTO»", 487 | "required": [ 488 | "code", 489 | "msg", 490 | "showType" 491 | ], 492 | "type": "object", 493 | "properties": { 494 | "code": { 495 | "type": "integer", 496 | "description": "状态码;大于零为处理成功的不同状态,小于零的状态则视为失败的情况", 497 | "format": "int32", 498 | "example": 200 499 | }, 500 | "data": { 501 | "description": "相应数据", 502 | "$ref": "#/components/schemas/TokenDTO", 503 | "example": "{}" 504 | }, 505 | "msg": { 506 | "type": "string", 507 | "description": "提示消息", 508 | "example": "请求成功" 509 | }, 510 | "showType": { 511 | "type": "string", 512 | "description": "消息提示方式", 513 | "example": "0", 514 | "enum": [ 515 | "DIALOG", 516 | "ERROR", 517 | "NOTIFICATION", 518 | "PAGE", 519 | "SLIENT", 520 | "SUCCESS", 521 | "UNUSED_6", 522 | "UNUSED_7", 523 | "UNUSED_8", 524 | "WARN" 525 | ] 526 | } 527 | } 528 | }, 529 | "ApiRes«boolean»": { 530 | "title": "ApiRes«boolean»", 531 | "required": [ 532 | "code", 533 | "msg", 534 | "showType" 535 | ], 536 | "type": "object", 537 | "properties": { 538 | "code": { 539 | "type": "integer", 540 | "description": "状态码;大于零为处理成功的不同状态,小于零的状态则视为失败的情况", 541 | "format": "int32", 542 | "example": 200 543 | }, 544 | "data": { 545 | "type": "boolean", 546 | "description": "相应数据", 547 | "example": false 548 | }, 549 | "msg": { 550 | "type": "string", 551 | "description": "提示消息", 552 | "example": "请求成功" 553 | }, 554 | "showType": { 555 | "type": "string", 556 | "description": "消息提示方式", 557 | "example": "0", 558 | "enum": [ 559 | "DIALOG", 560 | "ERROR", 561 | "NOTIFICATION", 562 | "PAGE", 563 | "SLIENT", 564 | "SUCCESS", 565 | "UNUSED_6", 566 | "UNUSED_7", 567 | "UNUSED_8", 568 | "WARN" 569 | ] 570 | } 571 | } 572 | }, 573 | "CaptchaTokenVO": { 574 | "title": "CaptchaTokenVO", 575 | "required": [ 576 | "token" 577 | ], 578 | "type": "object", 579 | "properties": { 580 | "token": { 581 | "type": "string", 582 | "description": "验证令牌" 583 | } 584 | } 585 | }, 586 | "CheckImgCaptchaReq": { 587 | "title": "CheckImgCaptchaReq", 588 | "required": [ 589 | "captcha", 590 | "id", 591 | "type" 592 | ], 593 | "type": "object", 594 | "properties": { 595 | "captcha": { 596 | "type": "string", 597 | "description": "验证码" 598 | }, 599 | "id": { 600 | "type": "integer", 601 | "description": "验证码ID", 602 | "format": "int64" 603 | }, 604 | "type": { 605 | "type": "string", 606 | "description": "验证码类型,1->注册,2->登录,3->忘记密码,4->修改密码", 607 | "enum": [ 608 | "EDIT_PASSWORD", 609 | "FORGOT_PASSWORD", 610 | "LOGIN", 611 | "REGISTER" 612 | ] 613 | } 614 | } 615 | }, 616 | "EditPasswordReq": { 617 | "title": "EditPasswordReq", 618 | "required": [ 619 | "newPassword", 620 | "password", 621 | "token", 622 | "uid" 623 | ], 624 | "type": "object", 625 | "properties": { 626 | "newPassword": { 627 | "type": "string", 628 | "description": "新密码" 629 | }, 630 | "password": { 631 | "type": "string", 632 | "description": "当前的密码" 633 | }, 634 | "token": { 635 | "type": "string", 636 | "description": "用户通行令牌" 637 | }, 638 | "uid": { 639 | "type": "integer", 640 | "description": "用户ID", 641 | "format": "int64" 642 | } 643 | } 644 | }, 645 | "ImgCaptchaDTO": { 646 | "title": "ImgCaptchaDTO", 647 | "required": [ 648 | "id", 649 | "url" 650 | ], 651 | "type": "object", 652 | "properties": { 653 | "id": { 654 | "type": "integer", 655 | "description": "验证码ID", 656 | "format": "int64" 657 | }, 658 | "url": { 659 | "type": "string", 660 | "description": "图片验证码URL" 661 | } 662 | } 663 | }, 664 | "ImgCaptchaReq": { 665 | "title": "ImgCaptchaReq", 666 | "required": [ 667 | "type" 668 | ], 669 | "type": "object", 670 | "properties": { 671 | "type": { 672 | "type": "string", 673 | "description": "验证码类型", 674 | "enum": [ 675 | "EDIT_PASSWORD", 676 | "FORGOT_PASSWORD", 677 | "LOGIN", 678 | "REGISTER" 679 | ] 680 | } 681 | } 682 | }, 683 | "LoginIsNeedCaptchaReq": { 684 | "title": "LoginIsNeedCaptchaReq", 685 | "required": [ 686 | "username" 687 | ], 688 | "type": "object", 689 | "properties": { 690 | "username": { 691 | "type": "string", 692 | "description": "用户名", 693 | "example": "fastdev" 694 | } 695 | } 696 | }, 697 | "TokenDTO": { 698 | "title": "TokenDTO", 699 | "required": [ 700 | "accessToken", 701 | "refreshToken", 702 | "userId" 703 | ], 704 | "type": "object", 705 | "properties": { 706 | "accessToken": { 707 | "type": "string", 708 | "description": "通行令牌" 709 | }, 710 | "refreshToken": { 711 | "type": "string", 712 | "description": "刷新令牌" 713 | }, 714 | "userId": { 715 | "type": "integer", 716 | "description": "用户ID", 717 | "format": "int64" 718 | } 719 | } 720 | } 721 | } 722 | } 723 | } 724 | -------------------------------------------------------------------------------- /test/example-files/swagger-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "未定义接口", 5 | "description": "接口文档描述", 6 | "contact": {}, 7 | "license": {}, 8 | "version": "v1.0" 9 | }, 10 | "paths": {}, 11 | "components": {}, 12 | "x-openapi": { 13 | "x-setting": { 14 | "customCode": 200, 15 | "language": "zh-CN", 16 | "enableSwaggerModels": true, 17 | "swaggerModelName": "实体类列表", 18 | "enableReloadCacheParameter": false, 19 | "enableAfterScript": true, 20 | "enableDocumentManage": true, 21 | "enableVersion": false, 22 | "enableRequestCache": true, 23 | "enableFilterMultipartApis": false, 24 | "enableFilterMultipartApiMethodType": "POST", 25 | "enableHost": false, 26 | "enableHostText": "", 27 | "enableDynamicParameter": true, 28 | "enableDebug": true, 29 | "enableFooter": true, 30 | "enableFooterCustom": false, 31 | "enableSearch": true, 32 | "enableOpenApi": true, 33 | "enableHomeCustom": false, 34 | "enableGroup": true, 35 | "enableResponseCode": true 36 | }, 37 | "x-markdownFiles": [] 38 | } 39 | } -------------------------------------------------------------------------------- /test/example-files/swagger-file-convert.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "file swagger", 5 | "version": "1.0.0" 6 | }, 7 | "basePath": "/", 8 | "tags": [ 9 | { 10 | "name": "file-controller", 11 | "description": "FileController" 12 | } 13 | ], 14 | "paths": { 15 | "/webapi/clear/importNavReCheckFile": { 16 | "post": { 17 | "tags": [ 18 | "file-controller" 19 | ], 20 | "summary": "导入文件", 21 | "description": "导入文件", 22 | "operationId": "importNavReCheckFileUsingPOST", 23 | "consumes": [ 24 | "multipart/form-data" 25 | ], 26 | "produces": [ 27 | "*/*" 28 | ], 29 | "parameters": [ 30 | { 31 | "name": "file", 32 | "in": "formData", 33 | "description": "file", 34 | "required": true, 35 | "type": "file" 36 | } 37 | ], 38 | "responses": { 39 | "200": { 40 | "description": "结果", 41 | "schema": { 42 | "originalRef": "Response«string»", 43 | "$ref": "#/definitions/Response«string»" 44 | } 45 | }, 46 | "201": { 47 | "description": "Created" 48 | }, 49 | "401": { 50 | "description": "Unauthorized" 51 | }, 52 | "403": { 53 | "description": "Forbidden" 54 | }, 55 | "404": { 56 | "description": "Not Found" 57 | } 58 | }, 59 | "deprecated": false 60 | } 61 | } 62 | }, 63 | "definitions": { 64 | "Response«string»": { 65 | "type": "object", 66 | "properties": { 67 | "msg": { 68 | "type": "string" 69 | }, 70 | "result": { 71 | "type": "string" 72 | }, 73 | "resultCode": { 74 | "type": "string" 75 | }, 76 | "success": { 77 | "type": "boolean" 78 | } 79 | }, 80 | "title": "Response«string»" 81 | }, 82 | "中文测试1": { 83 | "type": "object", 84 | "properties": { 85 | "msg": { 86 | "type": "string" 87 | }, 88 | "result": { 89 | "type": "string" 90 | }, 91 | "resultCode": { 92 | "type": "string" 93 | }, 94 | "success": { 95 | "type": "boolean" 96 | } 97 | }, 98 | "title": "Response«string»" 99 | }, 100 | "中文测试2": { 101 | "type": "object", 102 | "properties": { 103 | "msg": { 104 | "type": "string" 105 | }, 106 | "result": { 107 | "type": "string" 108 | }, 109 | "resultCode": { 110 | "type": "string" 111 | }, 112 | "success": { 113 | "type": "boolean" 114 | } 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /test/example-files/swagger-schema-contain-blank-symbol.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "Swagger Petstore", 5 | "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", 6 | "termsOfService": "http://swagger.io/terms/", 7 | "contact": { 8 | "email": "apiteam@swagger.io" 9 | }, 10 | "license": { 11 | "name": "Apache 2.0", 12 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 13 | }, 14 | "version": "1.0.0" 15 | }, 16 | "externalDocs": { 17 | "description": "Find out more about Swagger", 18 | "url": "http://swagger.io" 19 | }, 20 | "servers": [ 21 | { 22 | "url": "https://petstore.swagger.io/v2" 23 | }, 24 | { 25 | "url": "http://petstore.swagger.io/v2" 26 | } 27 | ], 28 | "tags": [ 29 | { 30 | "name": "pet", 31 | "description": "Everything about your Pets", 32 | "externalDocs": { 33 | "description": "Find out more", 34 | "url": "http://swagger.io" 35 | } 36 | }, 37 | { 38 | "name": "store", 39 | "description": "Access to Petstore orders" 40 | }, 41 | { 42 | "name": "user", 43 | "description": "Operations about user", 44 | "externalDocs": { 45 | "description": "Find out more about our store", 46 | "url": "http://swagger.io" 47 | } 48 | } 49 | ], 50 | "paths": { 51 | "/electricity/classification/time-sharing-electricity": { 52 | "post": { 53 | "tags": ["分项用电模块{6C76B49E435F7713E0531F0B10AC7013}"], 54 | "summary": "分时电量查询", 55 | "operationId": "timeSharingElectricityUsingPOST", 56 | "requestBody": { 57 | "content": { 58 | "application/json": { 59 | "schema": { 60 | "$ref": "#/components/schemas/分项、设备 自然时间 统计查询 dto" 61 | } 62 | } 63 | } 64 | }, 65 | "responses": { 66 | "200": { 67 | "description": "OK", 68 | "content": { 69 | "*/*": { 70 | "schema": { 71 | "$ref": "#/components/schemas/StatisticsQueryVo" 72 | } 73 | } 74 | } 75 | }, 76 | "201": { 77 | "description": "Created" 78 | }, 79 | "401": { 80 | "description": "Unauthorized" 81 | }, 82 | "403": { 83 | "description": "Forbidden" 84 | }, 85 | "404": { 86 | "description": "Not Found" 87 | } 88 | } 89 | } 90 | } 91 | }, 92 | "components": { 93 | "schemas": { 94 | "分项、设备 自然时间 统计查询 dto": { 95 | "title": "分项、设备 自然时间 统计查询 dto", 96 | "type": "object", 97 | "properties": { 98 | "businessType": { 99 | "type": "string", 100 | "description": "业务类型", 101 | "enum": ["CLASSIFICATION", "DEVICE", "INSTRUMENT"] 102 | }, 103 | "endTime": { 104 | "type": "string", 105 | "description": "结束时间", 106 | "format": "date-time" 107 | }, 108 | "energyType": { 109 | "type": "string", 110 | "description": "能源类型" 111 | }, 112 | "ids": { 113 | "type": "array", 114 | "description": "根据业务类型传勾选id", 115 | "items": { 116 | "type": "string" 117 | } 118 | }, 119 | "startTime": { 120 | "type": "string", 121 | "description": "开始时间", 122 | "format": "date-time" 123 | }, 124 | "timeInterval": { 125 | "type": "string", 126 | "description": "时间粒度", 127 | "enum": ["DAY", "HALFHOUR", "HOUR", "MINUTE", "MONTH", "QUARTERHOUR", "YEAR"] 128 | }, 129 | "timeQueryType": { 130 | "type": "string", 131 | "description": "查询类型 自然时间-NATURAL,班制时间-SHIFT", 132 | "enum": ["NATURAL", "SHIFT"] 133 | }, 134 | "vsEndTime": { 135 | "type": "string", 136 | "description": "对比结束时间", 137 | "format": "date-time" 138 | }, 139 | "vsStartTime": { 140 | "type": "string", 141 | "description": "对比开始时间", 142 | "format": "date-time" 143 | }, 144 | "vsType": { 145 | "type": "string", 146 | "description": "比较类型", 147 | "enum": ["CLASSIFICATION", "DEVICE", "TIME"] 148 | } 149 | } 150 | }, 151 | "ApiResponse": { 152 | "type": "object", 153 | "properties": { 154 | "code": { 155 | "type": "integer", 156 | "format": "int32" 157 | }, 158 | "type": { 159 | "type": "string" 160 | }, 161 | "message": { 162 | "type": "string" 163 | } 164 | } 165 | } 166 | }, 167 | "securitySchemes": { 168 | "petstore_auth": { 169 | "type": "oauth2", 170 | "flows": { 171 | "implicit": { 172 | "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", 173 | "scopes": { 174 | "write:pets": "modify pets in your account", 175 | "read:pets": "read your pets" 176 | } 177 | } 178 | } 179 | }, 180 | "api_key": { 181 | "type": "apiKey", 182 | "name": "api_key", 183 | "in": "header" 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /test/example-files/swgger3.0.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "test API", 5 | "description": "test API ", 6 | "version": "v1.0.0" 7 | }, 8 | "tags": [ 9 | { 10 | "name": "SysOperlog", 11 | "description": "日志管理" 12 | } 13 | ], 14 | "paths": { 15 | "/LandIA/sysOperlog/listOperlog": { 16 | "get": { 17 | "tags": [ 18 | "SysOperlog" 19 | ], 20 | "summary": "操作日志接口", 21 | "operationId": "get_test1", 22 | "parameters": [ 23 | { 24 | "name": "startTime", 25 | "in": "query", 26 | "description": "起始时间", 27 | "required": false, 28 | "schema": { 29 | "type": "integer", 30 | "format": "int64" 31 | } 32 | }, 33 | { 34 | "name": "endTime", 35 | "in": "query", 36 | "description": "结束时间", 37 | "required": false, 38 | "schema": { 39 | "type": "integer", 40 | "format": "int64" 41 | } 42 | }, 43 | { 44 | "name": "userId", 45 | "in": "query", 46 | "description": "用户ID", 47 | "required": false, 48 | "schema": { 49 | "type": "string" 50 | } 51 | }, 52 | { 53 | "name": "pageSize", 54 | "in": "query", 55 | "description": "分页页容量", 56 | "required": false, 57 | "schema": { 58 | "type": "integer", 59 | "format": "int32" 60 | } 61 | }, 62 | { 63 | "name": "pageIndex", 64 | "in": "query", 65 | "description": "分页页序号", 66 | "required": false, 67 | "schema": { 68 | "type": "integer", 69 | "format": "int32" 70 | } 71 | }, 72 | { 73 | "name": "reqSerial", 74 | "in": "header", 75 | "description": "请求流水号", 76 | "required": true, 77 | "schema": { 78 | "type": "string" 79 | } 80 | }, 81 | { 82 | "name": "Authorization", 83 | "in": "header", 84 | "description": "请求授权码", 85 | "required": false, 86 | "schema": { 87 | "type": "string" 88 | } 89 | } 90 | ], 91 | "responses": { 92 | "200": { 93 | "description": "OK", 94 | "content": { 95 | "*/*": { 96 | "schema": { 97 | "$ref": "#/components/schemas/F2BResponseGetListOperlogRespPost" 98 | } 99 | } 100 | } 101 | } 102 | } 103 | }, 104 | "post": { 105 | "tags": [ 106 | "SysOperlog" 107 | ], 108 | "summary": "操作日志接口2", 109 | "operationId": "post_test2", 110 | "parameters": [ 111 | { 112 | "name": "Authorization", 113 | "in": "header", 114 | "description": "请求授权码", 115 | "required": false, 116 | "schema": { 117 | "type": "string" 118 | } 119 | } 120 | ], 121 | "responses": { 122 | "200": { 123 | "description": "OK", 124 | "content": { 125 | "*/*": { 126 | "schema": { 127 | "$ref": "#/components/schemas/F2BResponseGetListOperlogRespPost" 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | }, 135 | "/admin-api/system/oauth2/user/update": { 136 | "get": { 137 | "description": "New endpoint", 138 | "responses": { 139 | "200": { 140 | "description": "New response", 141 | "content": { 142 | "application/json": { 143 | "schema": { 144 | "": "" 145 | } 146 | } 147 | } 148 | } 149 | } 150 | }, 151 | "put": { 152 | "tags": [ 153 | "管理后台 - OAuth2.0 用户" 154 | ], 155 | "summary": "更新用户基本信息", 156 | "operationId": "updateUserInfo", 157 | "parameters": [ 158 | { 159 | "name": "AUTH_ID", 160 | "in": "header", 161 | "description": "Session Token", 162 | "schema": { 163 | "type": "string", 164 | "description": "session-id", 165 | "default": "1" 166 | } 167 | }, 168 | { 169 | "name": "tenant-id", 170 | "in": "header", 171 | "description": "租户编号", 172 | "schema": { 173 | "type": "integer", 174 | "description": "租户编号", 175 | "format": "int32", 176 | "default": 1 177 | } 178 | }, 179 | { 180 | "name": "Authorization", 181 | "in": "header", 182 | "description": "认证 Token", 183 | "schema": { 184 | "type": "string", 185 | "description": "认证 Token", 186 | "default": "Best test1" 187 | } 188 | } 189 | ], 190 | "requestBody": { 191 | "content": { 192 | "application/json": { 193 | "schema": { 194 | "$ref": "#/components/schemas/OAuth2UserUpdateReqVO" 195 | } 196 | } 197 | }, 198 | "required": true 199 | }, 200 | "responses": { 201 | "200": { 202 | "description": "OK", 203 | "content": { 204 | "*/*": { 205 | "schema": { 206 | "$ref": "#/components/schemas/CommonResultBoolean" 207 | } 208 | } 209 | } 210 | } 211 | } 212 | } 213 | } 214 | }, 215 | "components": { 216 | "schemas": { 217 | "F2BResponseGetListOperlogRespPost": { 218 | "type": "object", 219 | "properties": { 220 | "bizContent": { 221 | "$ref": "#/components/schemas/GetListOperlogRespPost" 222 | }, 223 | "bizCode": { 224 | "type": "string" 225 | }, 226 | "bizMsg": { 227 | "type": "string" 228 | }, 229 | "sysCode": { 230 | "type": "string" 231 | }, 232 | "sysMsg": { 233 | "type": "string" 234 | }, 235 | "reqSerial": { 236 | "type": "string" 237 | } 238 | } 239 | }, 240 | "GetListOperlogRespPost": { 241 | "type": "object", 242 | "properties": { 243 | "total": { 244 | "type": "integer", 245 | "format": "int64" 246 | }, 247 | "rows": { 248 | "type": "array", 249 | "items": { 250 | "$ref": "#/components/schemas/SysCommonLogPojo" 251 | } 252 | } 253 | } 254 | }, 255 | "CommonResultBoolean": { 256 | "type": "object", 257 | "properties": { 258 | "code": { 259 | "type": "integer", 260 | "format": "int32" 261 | }, 262 | "data": { 263 | "type": "boolean" 264 | }, 265 | "msg": { 266 | "type": "string" 267 | } 268 | } 269 | }, 270 | "OAuth2UserUpdateReqVO": { 271 | "required": [ 272 | "nickname" 273 | ], 274 | "type": "object", 275 | "properties": { 276 | "nickname": { 277 | "maxLength": 30, 278 | "minLength": 0, 279 | "type": "string", 280 | "description": "用户昵称", 281 | "example": "芋艿" 282 | }, 283 | "email": { 284 | "maxLength": 50, 285 | "minLength": 0, 286 | "type": "string", 287 | "description": "用户邮箱", 288 | "example": "platform@iocoder.cn" 289 | }, 290 | "mobile": { 291 | "type": "string", 292 | "description": "手机号码", 293 | "example": "15601691300" 294 | }, 295 | "sex": { 296 | "type": "integer", 297 | "description": "用户性别,参见 SexEnum 枚举类", 298 | "format": "int32", 299 | "example": 1 300 | } 301 | }, 302 | "description": "管理后台 - OAuth2 更新用户基本信息 Request VO" 303 | } 304 | } 305 | } 306 | } -------------------------------------------------------------------------------- /test/genSwagger.js: -------------------------------------------------------------------------------- 1 | const openAPI = require('../dist/index'); 2 | 3 | openAPI.generateService({ 4 | schemaPath: './swagger.json', 5 | serversPath: './servers', 6 | declareType: 'interface', 7 | }); 8 | -------------------------------------------------------------------------------- /test/java-api.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.3", 3 | "info": { 4 | "title": "Api Documentation", 5 | "description": "Api Documentation", 6 | "termsOfService": "urn:tos", 7 | "contact": {}, 8 | "license": { 9 | "name": "Apache 2.0", 10 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 11 | }, 12 | "version": "1.0" 13 | }, 14 | "servers": [ 15 | { 16 | "url": "http://localhost:8080", 17 | "description": "Inferred Url" 18 | } 19 | ], 20 | "tags": [ 21 | { 22 | "name": "flow-controller", 23 | "description": "Flow Controller" 24 | } 25 | ], 26 | "paths": { 27 | "/flow/flows": { 28 | "get": { 29 | "tags": ["flow-controller"], 30 | "summary": "flows", 31 | "operationId": "flowsUsingGET_2", 32 | "parameters": [ 33 | { 34 | "name": "arguments", 35 | "in": "query", 36 | "required": false, 37 | "style": "form", 38 | "schema": { 39 | "type": "string" 40 | } 41 | }, 42 | { 43 | "name": "createDate", 44 | "in": "query", 45 | "required": false, 46 | "style": "form", 47 | "schema": { 48 | "type": "string", 49 | "format": "date-time" 50 | } 51 | }, 52 | { 53 | "name": "createUserId", 54 | "in": "query", 55 | "required": false, 56 | "style": "form", 57 | "schema": { 58 | "type": "integer", 59 | "format": "int64" 60 | } 61 | }, 62 | { 63 | "name": "edge", 64 | "in": "query", 65 | "required": false, 66 | "style": "form", 67 | "schema": { 68 | "type": "string" 69 | } 70 | }, 71 | { 72 | "name": "id", 73 | "in": "query", 74 | "required": false, 75 | "style": "form", 76 | "schema": { 77 | "type": "integer", 78 | "format": "int64" 79 | } 80 | }, 81 | { 82 | "name": "name", 83 | "in": "query", 84 | "required": false, 85 | "style": "form", 86 | "schema": { 87 | "type": "string" 88 | } 89 | }, 90 | { 91 | "name": "node", 92 | "in": "query", 93 | "required": false, 94 | "style": "form", 95 | "schema": { 96 | "type": "string" 97 | } 98 | }, 99 | { 100 | "name": "note", 101 | "in": "query", 102 | "required": false, 103 | "style": "form", 104 | "schema": { 105 | "type": "string" 106 | } 107 | }, 108 | { 109 | "name": "statusCode", 110 | "in": "query", 111 | "required": false, 112 | "style": "form", 113 | "schema": { 114 | "type": "integer", 115 | "format": "int32" 116 | } 117 | } 118 | ], 119 | "responses": { 120 | "200": { 121 | "description": "OK", 122 | "content": { 123 | "*/*": { 124 | "schema": { 125 | "$ref": "#/components/schemas/Result«List«ConfigFlow»»" 126 | } 127 | } 128 | } 129 | }, 130 | "401": { 131 | "description": "Unauthorized" 132 | }, 133 | "403": { 134 | "description": "Forbidden" 135 | }, 136 | "404": { 137 | "description": "Not Found" 138 | } 139 | } 140 | } 141 | } 142 | }, 143 | "components": { 144 | "schemas": { 145 | "Result«List«ConfigFlow»»": { 146 | "title": "Result«List«ConfigFlow»»", 147 | "type": "object", 148 | "properties": { 149 | "data": { 150 | "type": "array", 151 | "items": { 152 | "$ref": "#/components/schemas/ConfigFlow" 153 | } 154 | }, 155 | "errorCode": { 156 | "type": "string" 157 | }, 158 | "errorMessage": { 159 | "type": "string" 160 | }, 161 | "host": { 162 | "type": "string" 163 | }, 164 | "showType": { 165 | "$ref": "#/components/schemas/ShowType" 166 | }, 167 | "success": { 168 | "type": "boolean" 169 | }, 170 | "traceId": { 171 | "type": "string" 172 | } 173 | } 174 | }, 175 | "ShowType": { 176 | "title": "ShowType", 177 | "type": "object", 178 | "properties": { 179 | "type": { 180 | "type": "integer", 181 | "format": "int32" 182 | } 183 | } 184 | }, 185 | "ConfigFlow": { 186 | "title": "ConfigFlow", 187 | "type": "object", 188 | "properties": { 189 | "id": { 190 | "type": "integer", 191 | "format": "int64" 192 | }, 193 | "name": { 194 | "type": "string" 195 | } 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /test/morse-api.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": "1.0.20210224155647", 4 | "description": "morse_restful_api", 5 | "title": "morse_product" 6 | }, 7 | "paths": { 8 | "/manager/user/addUser.json": { 9 | "post": { 10 | "responses": { 11 | "200": { 12 | "description": "Success" 13 | } 14 | }, 15 | "parameters": [ 16 | { 17 | "required": true, 18 | "type": "string", 19 | "description": "\u7528\u6237\u540d", 20 | "name": "username", 21 | "in": "query" 22 | }, 23 | { 24 | "required": true, 25 | "type": "string", 26 | "description": "\u5bc6\u7801", 27 | "name": "passwd", 28 | "in": "query" 29 | }, 30 | { 31 | "type": "string", 32 | "description": "\u90ae\u7bb1", 33 | "name": "email", 34 | "in": "query" 35 | }, 36 | { 37 | "type": "string", 38 | "description": "\u624b\u673a\u53f7", 39 | "name": "mobile", 40 | "in": "query" 41 | } 42 | ], 43 | "tags": ["manager/user"], 44 | "description": ":return:", 45 | "summary": "\u6dfb\u52a0\u7528\u6237\u4fe1\u606f", 46 | "operationId": "post_user_add" 47 | } 48 | }, 49 | "/manager/user/authorization.json": { 50 | "post": { 51 | "tags": ["manager/user"], 52 | "responses": { 53 | "200": { 54 | "description": "Success" 55 | } 56 | }, 57 | "operationId": "post_user_authorization" 58 | } 59 | }, 60 | "/manager/user/delUser.json": { 61 | "post": { 62 | "tags": ["manager/user"], 63 | "responses": { 64 | "200": { 65 | "description": "Success" 66 | } 67 | }, 68 | "parameters": [ 69 | { 70 | "required": true, 71 | "type": "string", 72 | "description": "\u81ea\u589eid", 73 | "name": "id", 74 | "in": "query" 75 | } 76 | ], 77 | "operationId": "post_user_del" 78 | } 79 | }, 80 | "/manager/user/importUser.json": { 81 | "post": { 82 | "tags": ["manager/user"], 83 | "responses": { 84 | "200": { 85 | "description": "Success" 86 | } 87 | }, 88 | "operationId": "post_user_import" 89 | } 90 | }, 91 | "/manager/user/queryAuthcodes.json": { 92 | "get": { 93 | "tags": ["manager/user"], 94 | "responses": { 95 | "200": { 96 | "description": "Success" 97 | } 98 | }, 99 | "operationId": "get_query_authcodes" 100 | } 101 | }, 102 | "/manager/user/queryUserInfo.json": { 103 | "get": { 104 | "tags": ["manager/user"], 105 | "responses": { 106 | "200": { 107 | "description": "Success" 108 | } 109 | }, 110 | "parameters": [ 111 | { 112 | "required": true, 113 | "type": "string", 114 | "description": "\u81ea\u589e", 115 | "name": "id", 116 | "in": "query" 117 | } 118 | ], 119 | "operationId": "get_user_info" 120 | } 121 | }, 122 | "/manager/user/queryUsers.json": { 123 | "get": { 124 | "tags": ["manager/user"], 125 | "responses": { 126 | "200": { 127 | "description": "Success" 128 | } 129 | }, 130 | "parameters": [ 131 | { 132 | "type": "string", 133 | "description": "\u7528\u6237\u7c7b\u578b\uff0cadmin/operator/None", 134 | "name": "user_type", 135 | "in": "query" 136 | }, 137 | { 138 | "type": "string", 139 | "description": "\u5173\u952e\u8bcd", 140 | "name": "key_words", 141 | "in": "query" 142 | }, 143 | { 144 | "type": "string", 145 | "description": "\u7b2c\u51e0\u6761", 146 | "name": "page_num", 147 | "in": "query" 148 | }, 149 | { 150 | "type": "string", 151 | "description": "\u4e00\u9875\u51e0\u6761", 152 | "name": "page_size", 153 | "in": "query" 154 | } 155 | ], 156 | "operationId": "get_users_query" 157 | } 158 | }, 159 | "/manager/user/randomPassword.json": { 160 | "get": { 161 | "tags": ["manager/user"], 162 | "responses": { 163 | "200": { 164 | "description": "Success" 165 | } 166 | }, 167 | "operationId": "get_random_password" 168 | } 169 | }, 170 | "/manager/user/updateUser.json": { 171 | "post": { 172 | "responses": { 173 | "200": { 174 | "description": "Success" 175 | } 176 | }, 177 | "parameters": [ 178 | { 179 | "required": true, 180 | "type": "string", 181 | "description": "id", 182 | "name": "id", 183 | "in": "query" 184 | }, 185 | { 186 | "required": true, 187 | "type": "string", 188 | "description": "\u7528\u6237\u540d", 189 | "name": "username", 190 | "in": "query" 191 | }, 192 | { 193 | "required": true, 194 | "type": "string", 195 | "description": "\u5bc6\u7801", 196 | "name": "passwd", 197 | "in": "query" 198 | }, 199 | { 200 | "type": "string", 201 | "description": "\u90ae\u7bb1", 202 | "name": "email", 203 | "in": "query" 204 | }, 205 | { 206 | "type": "string", 207 | "description": "\u624b\u673a\u53f7", 208 | "name": "mobile", 209 | "in": "query" 210 | } 211 | ], 212 | "tags": ["manager/user"], 213 | "description": ":return:", 214 | "summary": "\u7f16\u8f91\u7528\u6237\u4fe1\u606f", 215 | "operationId": "post_userupdate" 216 | } 217 | } 218 | }, 219 | "responses": { 220 | "MaskError": { 221 | "description": "When any error occurs on mask" 222 | }, 223 | "ParseError": { 224 | "description": "When a mask can't be parsed" 225 | } 226 | }, 227 | "tags": [ 228 | { 229 | "name": "manager/user", 230 | "description": "\u7528\u6237\u6a21\u5757" 231 | } 232 | ], 233 | "basePath": "/", 234 | "produces": ["application/json"], 235 | "swagger": "2.0", 236 | "consumes": ["application/json"] 237 | } 238 | -------------------------------------------------------------------------------- /test/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "Swagger Petstore", 5 | "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", 6 | "termsOfService": "http://swagger.io/terms/", 7 | "contact": { 8 | "email": "apiteam@swagger.io" 9 | }, 10 | "license": { 11 | "name": "Apache 2.0", 12 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 13 | }, 14 | "version": "1.0.0" 15 | }, 16 | "externalDocs": { 17 | "description": "Find out more about Swagger", 18 | "url": "http://swagger.io" 19 | }, 20 | "servers": [ 21 | { 22 | "url": "https://petstore.swagger.io/v2" 23 | }, 24 | { 25 | "url": "http://petstore.swagger.io/v2" 26 | } 27 | ], 28 | "tags": [ 29 | { 30 | "name": "pet", 31 | "description": "Everything about your Pets", 32 | "externalDocs": { 33 | "description": "Find out more", 34 | "url": "http://swagger.io" 35 | } 36 | }, 37 | { 38 | "name": "store", 39 | "description": "Access to Petstore orders" 40 | }, 41 | { 42 | "name": "user", 43 | "description": "Operations about user", 44 | "externalDocs": { 45 | "description": "Find out more about our store", 46 | "url": "http://swagger.io" 47 | } 48 | } 49 | ], 50 | "paths": { 51 | "/pet": { 52 | "put": { 53 | "tags": ["pet"], 54 | "summary": "Update an existing pet", 55 | "operationId": "updatePet", 56 | "requestBody": { 57 | "description": "Pet object that needs to be added to the store", 58 | "content": { 59 | "application/json": { 60 | "schema": { 61 | "$ref": "#/components/schemas/Pet" 62 | } 63 | }, 64 | "application/xml": { 65 | "schema": { 66 | "$ref": "#/components/schemas/Pet" 67 | } 68 | } 69 | }, 70 | "required": true 71 | }, 72 | "responses": { 73 | "400": { 74 | "description": "Invalid ID supplied", 75 | "content": {} 76 | }, 77 | "404": { 78 | "description": "Pet not found", 79 | "content": {} 80 | }, 81 | "405": { 82 | "description": "Validation exception", 83 | "content": {} 84 | } 85 | }, 86 | "security": [ 87 | { 88 | "petstore_auth": ["write:pets", "read:pets"] 89 | } 90 | ], 91 | "x-codegen-request-body-name": "body" 92 | }, 93 | "post": { 94 | "tags": ["pet"], 95 | "summary": "Add a new pet to the store", 96 | "operationId": "addPet", 97 | "requestBody": { 98 | "description": "Pet object that needs to be added to the store", 99 | "content": { 100 | "application/json": { 101 | "schema": { 102 | "$ref": "#/components/schemas/Pet" 103 | } 104 | }, 105 | "application/xml": { 106 | "schema": { 107 | "$ref": "#/components/schemas/Pet" 108 | } 109 | } 110 | }, 111 | "required": true 112 | }, 113 | "responses": { 114 | "405": { 115 | "description": "Invalid input", 116 | "content": {} 117 | } 118 | }, 119 | "security": [ 120 | { 121 | "petstore_auth": ["write:pets", "read:pets"] 122 | } 123 | ], 124 | "x-codegen-request-body-name": "body" 125 | } 126 | }, 127 | "/pet/findByStatus": { 128 | "get": { 129 | "tags": ["pet"], 130 | "summary": "Finds Pets by status", 131 | "description": "Multiple status values can be provided with comma separated strings", 132 | "operationId": "findPetsByStatus", 133 | "parameters": [ 134 | { 135 | "name": "status", 136 | "in": "query", 137 | "description": "Status values that need to be considered for filter", 138 | "required": true, 139 | "style": "form", 140 | "explode": true, 141 | "schema": { 142 | "type": "array", 143 | "items": { 144 | "type": "string", 145 | "default": "available", 146 | "enum": ["available", "pending", "sold"] 147 | } 148 | } 149 | } 150 | ], 151 | "responses": { 152 | "200": { 153 | "description": "successful operation", 154 | "content": { 155 | "application/xml": { 156 | "schema": { 157 | "type": "array", 158 | "items": { 159 | "$ref": "#/components/schemas/Pet" 160 | } 161 | } 162 | }, 163 | "application/json": { 164 | "schema": { 165 | "type": "array", 166 | "items": { 167 | "$ref": "#/components/schemas/Pet" 168 | } 169 | } 170 | } 171 | } 172 | }, 173 | "400": { 174 | "description": "Invalid status value", 175 | "content": {} 176 | } 177 | }, 178 | "security": [ 179 | { 180 | "petstore_auth": ["write:pets", "read:pets"] 181 | } 182 | ] 183 | } 184 | }, 185 | "/pet/findByTags": { 186 | "get": { 187 | "tags": ["pet"], 188 | "summary": "Finds Pets by tags", 189 | "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", 190 | "operationId": "findPetsByTags", 191 | "parameters": [ 192 | { 193 | "name": "tags", 194 | "in": "query", 195 | "description": "Tags to filter by", 196 | "required": true, 197 | "style": "form", 198 | "explode": true, 199 | "schema": { 200 | "type": "array", 201 | "items": { 202 | "type": "string" 203 | } 204 | } 205 | } 206 | ], 207 | "responses": { 208 | "200": { 209 | "description": "successful operation", 210 | "content": { 211 | "application/xml": { 212 | "schema": { 213 | "type": "array", 214 | "items": { 215 | "$ref": "#/components/schemas/Pet" 216 | } 217 | } 218 | }, 219 | "application/json": { 220 | "schema": { 221 | "type": "array", 222 | "items": { 223 | "$ref": "#/components/schemas/Pet" 224 | } 225 | } 226 | } 227 | } 228 | }, 229 | "400": { 230 | "description": "Invalid tag value", 231 | "content": {} 232 | } 233 | }, 234 | "deprecated": true, 235 | "security": [ 236 | { 237 | "petstore_auth": ["write:pets", "read:pets"] 238 | } 239 | ] 240 | } 241 | }, 242 | "/pet/{petId}": { 243 | "get": { 244 | "tags": ["pet"], 245 | "summary": "Find pet by ID", 246 | "description": "Returns a single pet", 247 | "operationId": "getPetById", 248 | "parameters": [ 249 | { 250 | "name": "petId", 251 | "in": "path", 252 | "description": "ID of pet to return", 253 | "required": true, 254 | "schema": { 255 | "type": "integer", 256 | "format": "int64" 257 | } 258 | } 259 | ], 260 | "responses": { 261 | "200": { 262 | "description": "successful operation", 263 | "content": { 264 | "application/xml": { 265 | "schema": { 266 | "$ref": "#/components/schemas/Pet" 267 | } 268 | }, 269 | "application/json": { 270 | "schema": { 271 | "$ref": "#/components/schemas/Pet" 272 | } 273 | } 274 | } 275 | }, 276 | "400": { 277 | "description": "Invalid ID supplied", 278 | "content": {} 279 | }, 280 | "404": { 281 | "description": "Pet not found", 282 | "content": {} 283 | } 284 | }, 285 | "security": [ 286 | { 287 | "api_key": [] 288 | } 289 | ] 290 | }, 291 | "post": { 292 | "tags": ["pet"], 293 | "summary": "Updates a pet in the store with form data", 294 | "operationId": "updatePetWithForm", 295 | "parameters": [ 296 | { 297 | "name": "petId", 298 | "in": "path", 299 | "description": "ID of pet that needs to be updated", 300 | "required": true, 301 | "schema": { 302 | "type": "integer", 303 | "format": "int64" 304 | } 305 | } 306 | ], 307 | "requestBody": { 308 | "content": { 309 | "application/x-www-form-urlencoded": { 310 | "schema": { 311 | "properties": { 312 | "name": { 313 | "type": "string", 314 | "description": "Updated name of the pet" 315 | }, 316 | "status": { 317 | "type": "string", 318 | "description": "Updated status of the pet" 319 | } 320 | } 321 | } 322 | } 323 | } 324 | }, 325 | "responses": { 326 | "405": { 327 | "description": "Invalid input", 328 | "content": {} 329 | } 330 | }, 331 | "security": [ 332 | { 333 | "petstore_auth": ["write:pets", "read:pets"] 334 | } 335 | ] 336 | }, 337 | "delete": { 338 | "tags": ["pet"], 339 | "summary": "Deletes a pet", 340 | "operationId": "deletePet", 341 | "parameters": [ 342 | { 343 | "name": "api_key", 344 | "in": "header", 345 | "schema": { 346 | "type": "string" 347 | } 348 | }, 349 | { 350 | "name": "petId", 351 | "in": "path", 352 | "description": "Pet id to delete", 353 | "required": true, 354 | "schema": { 355 | "type": "integer", 356 | "format": "int64" 357 | } 358 | } 359 | ], 360 | "responses": { 361 | "400": { 362 | "description": "Invalid ID supplied", 363 | "content": {} 364 | }, 365 | "404": { 366 | "description": "Pet not found", 367 | "content": {} 368 | } 369 | }, 370 | "security": [ 371 | { 372 | "petstore_auth": ["write:pets", "read:pets"] 373 | } 374 | ] 375 | } 376 | }, 377 | "/pet/{petId}/uploadImage": { 378 | "post": { 379 | "tags": ["pet"], 380 | "summary": "uploads an image", 381 | "operationId": "uploadFile", 382 | "parameters": [ 383 | { 384 | "name": "petId", 385 | "in": "path", 386 | "description": "ID of pet to update", 387 | "required": true, 388 | "schema": { 389 | "type": "integer", 390 | "format": "int64" 391 | } 392 | } 393 | ], 394 | "requestBody": { 395 | "content": { 396 | "multipart/form-data": { 397 | "schema": { 398 | "properties": { 399 | "additionalMetadata": { 400 | "type": "string", 401 | "description": "Additional data to pass to server" 402 | }, 403 | "file": { 404 | "type": "string", 405 | "description": "file to upload", 406 | "format": "binary" 407 | } 408 | } 409 | } 410 | } 411 | } 412 | }, 413 | "responses": { 414 | "200": { 415 | "description": "successful operation", 416 | "content": { 417 | "application/json": { 418 | "schema": { 419 | "$ref": "#/components/schemas/ApiResponse" 420 | } 421 | } 422 | } 423 | } 424 | }, 425 | "security": [ 426 | { 427 | "petstore_auth": ["write:pets", "read:pets"] 428 | } 429 | ] 430 | } 431 | }, 432 | "/store/inventory": { 433 | "get": { 434 | "tags": ["store"], 435 | "summary": "Returns pet inventories by status", 436 | "description": "Returns a map of status codes to quantities", 437 | "operationId": "getInventory", 438 | "responses": { 439 | "200": { 440 | "description": "successful operation", 441 | "content": { 442 | "application/json": { 443 | "schema": { 444 | "type": "object", 445 | "additionalProperties": { 446 | "type": "integer", 447 | "format": "int32" 448 | } 449 | } 450 | } 451 | } 452 | } 453 | }, 454 | "security": [ 455 | { 456 | "api_key": [] 457 | } 458 | ] 459 | } 460 | }, 461 | "/store/order": { 462 | "post": { 463 | "tags": ["store"], 464 | "summary": "Place an order for a pet", 465 | "operationId": "placeOrder", 466 | "requestBody": { 467 | "description": "order placed for purchasing the pet", 468 | "content": { 469 | "*/*": { 470 | "schema": { 471 | "$ref": "#/components/schemas/Order" 472 | } 473 | } 474 | }, 475 | "required": true 476 | }, 477 | "responses": { 478 | "200": { 479 | "description": "successful operation", 480 | "content": { 481 | "application/xml": { 482 | "schema": { 483 | "$ref": "#/components/schemas/Order" 484 | } 485 | }, 486 | "application/json": { 487 | "schema": { 488 | "$ref": "#/components/schemas/Order" 489 | } 490 | } 491 | } 492 | }, 493 | "400": { 494 | "description": "Invalid Order", 495 | "content": {} 496 | } 497 | }, 498 | "x-codegen-request-body-name": "body" 499 | } 500 | }, 501 | "/store/order/{orderId}": { 502 | "get": { 503 | "tags": ["store"], 504 | "summary": "Find purchase order by ID", 505 | "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", 506 | "operationId": "getOrderById", 507 | "parameters": [ 508 | { 509 | "name": "orderId", 510 | "in": "path", 511 | "description": "ID of pet that needs to be fetched", 512 | "required": true, 513 | "schema": { 514 | "maximum": 10, 515 | "minimum": 1, 516 | "type": "integer", 517 | "format": "int64" 518 | } 519 | } 520 | ], 521 | "responses": { 522 | "200": { 523 | "description": "successful operation", 524 | "content": { 525 | "application/xml": { 526 | "schema": { 527 | "$ref": "#/components/schemas/Order" 528 | } 529 | }, 530 | "application/json": { 531 | "schema": { 532 | "$ref": "#/components/schemas/Order" 533 | } 534 | } 535 | } 536 | }, 537 | "400": { 538 | "description": "Invalid ID supplied", 539 | "content": {} 540 | }, 541 | "404": { 542 | "description": "Order not found", 543 | "content": {} 544 | } 545 | } 546 | }, 547 | "delete": { 548 | "tags": ["store"], 549 | "summary": "Delete purchase order by ID", 550 | "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", 551 | "operationId": "deleteOrder", 552 | "parameters": [ 553 | { 554 | "name": "orderId", 555 | "in": "path", 556 | "description": "ID of the order that needs to be deleted", 557 | "required": true, 558 | "schema": { 559 | "minimum": 1, 560 | "type": "integer", 561 | "format": "int64" 562 | } 563 | } 564 | ], 565 | "responses": { 566 | "400": { 567 | "description": "Invalid ID supplied", 568 | "content": {} 569 | }, 570 | "404": { 571 | "description": "Order not found", 572 | "content": {} 573 | } 574 | } 575 | } 576 | }, 577 | "/user": { 578 | "post": { 579 | "tags": ["user"], 580 | "summary": "Create user", 581 | "description": "This can only be done by the logged in user.", 582 | "operationId": "createUser", 583 | "requestBody": { 584 | "description": "Created user object", 585 | "content": { 586 | "*/*": { 587 | "schema": { 588 | "$ref": "#/components/schemas/User" 589 | } 590 | } 591 | }, 592 | "required": true 593 | }, 594 | "responses": { 595 | "default": { 596 | "description": "successful operation", 597 | "content": {} 598 | } 599 | }, 600 | "x-codegen-request-body-name": "body" 601 | } 602 | }, 603 | "/user/createWithArray": { 604 | "post": { 605 | "tags": ["user"], 606 | "summary": "Creates list of users with given input array", 607 | "operationId": "createUsersWithArrayInput", 608 | "requestBody": { 609 | "description": "List of user object", 610 | "content": { 611 | "*/*": { 612 | "schema": { 613 | "type": "array", 614 | "items": { 615 | "$ref": "#/components/schemas/User" 616 | } 617 | } 618 | } 619 | }, 620 | "required": true 621 | }, 622 | "responses": { 623 | "default": { 624 | "description": "successful operation", 625 | "content": {} 626 | } 627 | }, 628 | "x-codegen-request-body-name": "body" 629 | } 630 | }, 631 | "/user/createWithList": { 632 | "post": { 633 | "tags": ["user"], 634 | "summary": "Creates list of users with given input array", 635 | "operationId": "createUsersWithListInput", 636 | "requestBody": { 637 | "description": "List of user object", 638 | "content": { 639 | "*/*": { 640 | "schema": { 641 | "type": "array", 642 | "items": { 643 | "$ref": "#/components/schemas/User" 644 | } 645 | } 646 | } 647 | }, 648 | "required": true 649 | }, 650 | "responses": { 651 | "default": { 652 | "description": "successful operation", 653 | "content": {} 654 | } 655 | }, 656 | "x-codegen-request-body-name": "body" 657 | } 658 | }, 659 | "/user/login": { 660 | "get": { 661 | "tags": ["user"], 662 | "summary": "Logs user into the system", 663 | "operationId": "loginUser", 664 | "parameters": [ 665 | { 666 | "name": "username", 667 | "in": "query", 668 | "description": "The user name for login", 669 | "required": true, 670 | "schema": { 671 | "type": "string" 672 | } 673 | }, 674 | { 675 | "name": "password", 676 | "in": "query", 677 | "description": "The password for login in clear text", 678 | "required": true, 679 | "schema": { 680 | "type": "string" 681 | } 682 | } 683 | ], 684 | "responses": { 685 | "200": { 686 | "description": "successful operation", 687 | "headers": { 688 | "X-Rate-Limit": { 689 | "description": "calls per hour allowed by the user", 690 | "schema": { 691 | "type": "integer", 692 | "format": "int32" 693 | } 694 | }, 695 | "X-Expires-After": { 696 | "description": "date in UTC when token expires", 697 | "schema": { 698 | "type": "string", 699 | "format": "date-time" 700 | } 701 | } 702 | }, 703 | "content": { 704 | "application/xml": { 705 | "schema": { 706 | "type": "string" 707 | } 708 | }, 709 | "application/json": { 710 | "schema": { 711 | "type": "string" 712 | } 713 | } 714 | } 715 | }, 716 | "400": { 717 | "description": "Invalid username/password supplied", 718 | "content": {} 719 | } 720 | } 721 | } 722 | }, 723 | "/user/logout": { 724 | "get": { 725 | "tags": ["user"], 726 | "summary": "Logs out current logged in user session", 727 | "operationId": "logoutUser", 728 | "responses": { 729 | "default": { 730 | "description": "successful operation", 731 | "content": {} 732 | } 733 | } 734 | } 735 | }, 736 | "/user/{username}": { 737 | "get": { 738 | "tags": ["user"], 739 | "summary": "Get user by user name", 740 | "operationId": "getUserByName", 741 | "parameters": [ 742 | { 743 | "name": "username", 744 | "in": "path", 745 | "description": "The name that needs to be fetched. Use user1 for testing. ", 746 | "required": true, 747 | "schema": { 748 | "type": "string" 749 | } 750 | } 751 | ], 752 | "responses": { 753 | "200": { 754 | "description": "successful operation", 755 | "content": { 756 | "application/xml": { 757 | "schema": { 758 | "$ref": "#/components/schemas/User" 759 | } 760 | }, 761 | "application/json": { 762 | "schema": { 763 | "$ref": "#/components/schemas/User" 764 | } 765 | } 766 | } 767 | }, 768 | "400": { 769 | "description": "Invalid username supplied", 770 | "content": {} 771 | }, 772 | "404": { 773 | "description": "User not found", 774 | "content": {} 775 | } 776 | } 777 | }, 778 | "put": { 779 | "tags": ["user"], 780 | "summary": "Updated user", 781 | "description": "This can only be done by the logged in user.", 782 | "operationId": "updateUser", 783 | "parameters": [ 784 | { 785 | "name": "username", 786 | "in": "path", 787 | "description": "name that need to be updated", 788 | "required": true, 789 | "schema": { 790 | "type": "string" 791 | } 792 | } 793 | ], 794 | "requestBody": { 795 | "description": "Updated user object", 796 | "content": { 797 | "*/*": { 798 | "schema": { 799 | "$ref": "#/components/schemas/User" 800 | } 801 | } 802 | }, 803 | "required": true 804 | }, 805 | "responses": { 806 | "400": { 807 | "description": "Invalid user supplied", 808 | "content": {} 809 | }, 810 | "404": { 811 | "description": "User not found", 812 | "content": {} 813 | } 814 | }, 815 | "x-codegen-request-body-name": "body" 816 | }, 817 | "delete": { 818 | "tags": ["user"], 819 | "summary": "Delete user", 820 | "description": "This can only be done by the logged in user.", 821 | "operationId": "deleteUser", 822 | "parameters": [ 823 | { 824 | "name": "username", 825 | "in": "path", 826 | "description": "The name that needs to be deleted", 827 | "required": true, 828 | "schema": { 829 | "type": "string" 830 | } 831 | } 832 | ], 833 | "responses": { 834 | "400": { 835 | "description": "Invalid username supplied", 836 | "content": {} 837 | }, 838 | "404": { 839 | "description": "User not found", 840 | "content": {} 841 | } 842 | } 843 | } 844 | } 845 | }, 846 | "components": { 847 | "schemas": { 848 | "Order": { 849 | "type": "object", 850 | "properties": { 851 | "id": { 852 | "type": "integer", 853 | "format": "int64" 854 | }, 855 | "petId": { 856 | "type": "integer", 857 | "format": "int64" 858 | }, 859 | "quantity": { 860 | "type": "integer", 861 | "format": "int32" 862 | }, 863 | "shipDate": { 864 | "type": "string", 865 | "format": "date-time" 866 | }, 867 | "status": { 868 | "type": "string", 869 | "description": "Order Status", 870 | "enum": ["placed", "approved", "delivered"] 871 | }, 872 | "complete": { 873 | "type": "boolean", 874 | "default": false 875 | } 876 | }, 877 | "xml": { 878 | "name": "Order" 879 | } 880 | }, 881 | "Category": { 882 | "type": "object", 883 | "properties": { 884 | "id": { 885 | "type": "integer", 886 | "format": "int64" 887 | }, 888 | "name": { 889 | "type": "string" 890 | } 891 | }, 892 | "xml": { 893 | "name": "Category" 894 | } 895 | }, 896 | "User": { 897 | "type": "object", 898 | "properties": { 899 | "id": { 900 | "type": "integer", 901 | "format": "int64" 902 | }, 903 | "username": { 904 | "type": "string" 905 | }, 906 | "firstName": { 907 | "type": "string" 908 | }, 909 | "lastName": { 910 | "type": "string" 911 | }, 912 | "email": { 913 | "type": "string" 914 | }, 915 | "password": { 916 | "type": "string" 917 | }, 918 | "phone": { 919 | "type": "string" 920 | }, 921 | "userStatus": { 922 | "type": "integer", 923 | "description": "User Status", 924 | "format": "int32" 925 | } 926 | }, 927 | "xml": { 928 | "name": "User" 929 | } 930 | }, 931 | "Tag": { 932 | "type": "object", 933 | "properties": { 934 | "id": { 935 | "type": "integer", 936 | "format": "int64" 937 | }, 938 | "name": { 939 | "type": "string" 940 | } 941 | }, 942 | "xml": { 943 | "name": "Tag" 944 | } 945 | }, 946 | "Pet": { 947 | "required": ["name", "photoUrls"], 948 | "type": "object", 949 | "properties": { 950 | "id": { 951 | "type": "integer", 952 | "format": "int64" 953 | }, 954 | "category": { 955 | "$ref": "#/components/schemas/Category" 956 | }, 957 | "name": { 958 | "type": "string", 959 | "example": "doggie" 960 | }, 961 | "photoUrls": { 962 | "type": "array", 963 | "xml": { 964 | "name": "photoUrl", 965 | "wrapped": true 966 | }, 967 | "items": { 968 | "type": "string" 969 | } 970 | }, 971 | "tags": { 972 | "type": "array", 973 | "xml": { 974 | "name": "tag", 975 | "wrapped": true 976 | }, 977 | "items": { 978 | "$ref": "#/components/schemas/Tag" 979 | } 980 | }, 981 | "status": { 982 | "type": "string", 983 | "description": "pet status in the store", 984 | "enum": ["available", "pending", "sold"] 985 | } 986 | }, 987 | "xml": { 988 | "name": "Pet" 989 | } 990 | }, 991 | "ApiResponse": { 992 | "type": "object", 993 | "properties": { 994 | "code": { 995 | "type": "integer", 996 | "format": "int32" 997 | }, 998 | "type": { 999 | "type": "string" 1000 | }, 1001 | "message": { 1002 | "type": "string" 1003 | } 1004 | } 1005 | } 1006 | }, 1007 | "securitySchemes": { 1008 | "petstore_auth": { 1009 | "type": "oauth2", 1010 | "flows": { 1011 | "implicit": { 1012 | "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", 1013 | "scopes": { 1014 | "write:pets": "modify pets in your account", 1015 | "read:pets": "read your pets" 1016 | } 1017 | } 1018 | } 1019 | }, 1020 | "api_key": { 1021 | "type": "apiKey", 1022 | "name": "api_key", 1023 | "in": "header" 1024 | } 1025 | } 1026 | } 1027 | } 1028 | -------------------------------------------------------------------------------- /test/swagger-allof.json: -------------------------------------------------------------------------------- 1 | {"basePath":"/","definitions":{"area.Area":{"properties":{"children":{"$ref":"#/definitions/area.Areas"},"id":{"type":"string"},"name":{"type":"string"},"pid":{"type":"string"},"type":{"type":"string"}},"type":"object"},"area.Areas":{"items":{"$ref":"#/definitions/area.Area"},"type":"array"},"model.AdminCommonArg":{"properties":{"id":{"description":"成员id","type":"string"}},"type":"object"},"model.AdminCreateArg":{"properties":{"name":{"description":"成员姓名","type":"string"},"number":{"description":"成员手机号","type":"string"},"organization_id":{"description":"成员所属机构","type":"string"},"role":{"description":"成员角色","type":"string"}},"type":"object"},"model.AdminCreateRet":{"properties":{"id":{"type":"string"},"password":{"type":"string"}},"type":"object"},"model.AdminInfo":{"properties":{"created_at":{"type":"string"},"enabled":{"type":"boolean"},"id":{"type":"string"},"name":{"type":"string"},"number":{"type":"string"},"organization":{"$ref":"#/definitions/model.OrganizationInfo"},"organization_id":{"type":"string"},"permissions":{"items":{"type":"string"},"type":"array"},"role":{"type":"string"},"updated_at":{"type":"string"},"visited_at":{"type":"string"}},"type":"object"},"model.AdminLoginArg":{"properties":{"number":{"description":"手机号","type":"string"},"password":{"description":"密码","type":"string"}},"type":"object"},"model.AdminPermission":{"properties":{"cate":{"type":"string"},"permissions":{"items":{"$ref":"#/definitions/model.Dict"},"type":"array"}},"type":"object"},"model.AdminSetPermissionsArg":{"properties":{"id":{"description":"成员id","type":"string"},"permissions":{"description":"成员权限","items":{"type":"string"},"type":"array"},"role":{"description":"成员角色","type":"string"}},"type":"object"},"model.AdminToggleArg":{"properties":{"enabled":{"description":"成员状态","type":"boolean"},"id":{"description":"成员id","type":"string"}},"type":"object"},"model.AdminUpdateArg":{"properties":{"id":{"description":"成员id","type":"string"},"name":{"description":"成员姓名","type":"string"},"number":{"description":"成员手机号","type":"string"}},"type":"object"},"model.Dict":{"properties":{"hidden":{"type":"boolean"},"key":{"type":"string"},"value":{"type":"string"}},"type":"object"},"model.OrganizationAllInfo":{"properties":{"children":{"items":{"$ref":"#/definitions/model.OrganizationAllInfo"},"type":"array"},"enabled":{"type":"boolean"},"id":{"type":"string"},"name":{"type":"string"},"pid":{"type":"string"}},"type":"object"},"model.OrganizationCommonArg":{"properties":{"id":{"description":"机构id","type":"string"}},"type":"object"},"model.OrganizationCreateArg":{"properties":{"address":{"description":"机构地址","type":"string"},"admin_name":{"description":"负责人姓名","type":"string"},"admin_number":{"description":"负责人手机号","type":"string"},"area":{"description":"机构地区","items":{"type":"string"},"type":"array"},"modules":{"description":"开通模块","items":{"type":"string"},"type":"array"},"name":{"description":"机构名称","type":"string"},"pid":{"description":"上级机构id","type":"string"},"type":{"description":"机构类别","type":"string"}},"type":"object"},"model.OrganizationCreateRet":{"properties":{"admin":{"$ref":"#/definitions/model.AdminCreateRet"},"id":{"type":"string"}},"type":"object"},"model.OrganizationInfo":{"properties":{"address":{"type":"string"},"area":{"items":{"type":"string"},"type":"array"},"created_at":{"type":"string"},"enabled":{"type":"boolean"},"id":{"type":"string"},"modules":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"pid":{"type":"string"},"type":{"type":"string"},"updated_at":{"type":"string"}},"type":"object"},"model.OrganizationMoveArg":{"properties":{"id":{"description":"机构id","type":"string"},"pid":{"description":"上级机构id","type":"string"}},"type":"object"},"model.OrganizationToggleArg":{"properties":{"enabled":{"description":"机构状态","type":"boolean"},"id":{"description":"机构id","type":"string"}},"type":"object"},"model.OrganizationUpdateArg":{"properties":{"address":{"description":"机构地址","type":"string"},"area":{"description":"机构地区","items":{"type":"string"},"type":"array"},"id":{"description":"机构id","type":"string"},"modules":{"description":"开通模块","items":{"type":"string"},"type":"array"},"name":{"description":"机构名称","type":"string"},"type":{"description":"机构类别","type":"string"}},"type":"object"},"model.UtilDataRet":{"properties":{"admin_permissions":{"items":{"$ref":"#/definitions/model.AdminPermission"},"type":"array"},"admin_role_permissions":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"admin_roles":{"items":{"$ref":"#/definitions/model.Dict"},"type":"array"},"organization_modules":{"items":{"$ref":"#/definitions/model.Dict"},"type":"array"},"organization_types":{"items":{"$ref":"#/definitions/model.Dict"},"type":"array"}},"type":"object"},"response.Body":{"properties":{"code":{"type":"integer"},"data":{"type":"object"},"msg":{"type":"string"}},"type":"object"}},"host":"psp.wangritian.com","info":{"contact":{},"title":"社心平台测试服","version":"1.0.0"},"paths":{"/admin/admin/create":{"post":{"operationId":"admin_create","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminCreateArg"}}],"responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.AdminCreateRet"}},"type":"object"}]}}},"summary":"成员创建","tags":["成员管理"]}},"/admin/admin/info":{"post":{"operationId":"admin_info","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminCommonArg"}}],"responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.AdminInfo"}},"type":"object"}]}}},"summary":"成员信息","tags":["成员管理"]}},"/admin/admin/setPermissions":{"post":{"operationId":"admin_set_permissions","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminSetPermissionsArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"成员权限设置","tags":["成员管理"]}},"/admin/admin/toggle":{"post":{"operationId":"admin_toggle","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminToggleArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"启用/禁用成员","tags":["成员管理"]}},"/admin/admin/update":{"post":{"operationId":"admin_update","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminUpdateArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"成员编辑","tags":["成员管理"]}},"/admin/open/login":{"post":{"operationId":"open_login","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.AdminLoginArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"管理员登录","tags":["开放接口"]}},"/admin/organization/all":{"post":{"operationId":"organization_all","responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"items":{"$ref":"#/definitions/model.OrganizationAllInfo"},"type":"array"}},"type":"object"}]}}},"summary":"机构树形列表","tags":["机构管理"]}},"/admin/organization/create":{"post":{"operationId":"organization_create","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationCreateArg"}}],"responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.OrganizationCreateRet"}},"type":"object"}]}}},"summary":"机构创建","tags":["机构管理"]}},"/admin/organization/delete":{"post":{"operationId":"organization_delete","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationCommonArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"机构删除","tags":["机构管理"]}},"/admin/organization/info":{"post":{"operationId":"organization_info","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationCommonArg"}}],"responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.OrganizationInfo"}},"type":"object"}]}}},"summary":"机构详情","tags":["机构管理"]}},"/admin/organization/move":{"post":{"operationId":"organization_move","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationMoveArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"机构移动","tags":["机构管理"]}},"/admin/organization/toggle":{"post":{"operationId":"organization_toggle","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationToggleArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"机构启用/禁用","tags":["机构管理"]}},"/admin/organization/update":{"post":{"operationId":"organization_update","parameters":[{"description":"_","in":"body","name":"_","schema":{"$ref":"#/definitions/model.OrganizationUpdateArg"}}],"responses":{"200":{"description":"_","schema":{"$ref":"#/definitions/response.Body"}}},"summary":"机构编辑","tags":["机构管理"]}},"/admin/util/admin":{"post":{"operationId":"util_admin","responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.AdminInfo"}},"type":"object"}]}}},"summary":"登录信息","tags":["系统"]}},"/admin/util/area":{"post":{"operationId":"util_area","responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"items":{"$ref":"#/definitions/area.Area"},"type":"array"}},"type":"object"}]}}},"summary":"地区数据","tags":["系统"]}},"/admin/util/data":{"post":{"operationId":"util_data","responses":{"200":{"description":"_","schema":{"allOf":[{"$ref":"#/definitions/response.Body"},{"properties":{"data":{"$ref":"#/definitions/model.UtilDataRet"}},"type":"object"}]}}},"summary":"字典数据","tags":["系统"]}}},"swagger":"2.0"} -------------------------------------------------------------------------------- /test/test-allof-api.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "Batch": { 5 | "allOf": [ 6 | { 7 | "$ref": "#/components/schemas/Response" 8 | }, 9 | { 10 | "$ref": "#/components/schemas/Batch_allOf" 11 | } 12 | ] 13 | }, 14 | "BatchList": { 15 | "allOf": [ 16 | { 17 | "$ref": "#/components/schemas/Response" 18 | }, 19 | { 20 | "properties": { 21 | "data": { 22 | "items": { 23 | "$ref": "#/components/schemas/Batch" 24 | }, 25 | "type": "array" 26 | } 27 | }, 28 | "type": "object" 29 | } 30 | ] 31 | }, 32 | "Batch_allOf": { 33 | "properties": { 34 | "data": { 35 | "type": "object" 36 | } 37 | }, 38 | "type": "object" 39 | }, 40 | "Response": { 41 | "description": "所有API的返回数据,如果业务处理失败,success必然是false,\n如果业务处理成功,success为True,并添加data字段携带需要的数据\n", 42 | "properties": { 43 | "errorCode": { 44 | "description": "业务约定的错误码", 45 | "type": "string" 46 | }, 47 | "errorMessage": { 48 | "description": "业务上的错误信息", 49 | "type": "string" 50 | }, 51 | "success": { 52 | "description": "业务上的请求是否成功", 53 | "type": "boolean" 54 | } 55 | }, 56 | "required": [ 57 | "success" 58 | ], 59 | "type": "object" 60 | } 61 | } 62 | }, 63 | "info": { 64 | "title": "Test allOf API", 65 | "version": "1.0.0" 66 | }, 67 | "openapi": "3.0.1", 68 | "paths": {}, 69 | "servers": [ 70 | { 71 | "url": "http://localhost:1234/" 72 | } 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | 5 | const openAPI = require('../dist/index'); 6 | 7 | const gen = async () => { 8 | await openAPI.generateService({ 9 | schemaPath: `${__dirname}/example-files/swagger-empty.json`, 10 | serversPath: './servers/empty', 11 | }); 12 | 13 | await openAPI.generateService({ 14 | schemaPath: `${__dirname}/test-allof-api.json`, 15 | serversPath: './servers-allof', 16 | }); 17 | 18 | await openAPI.generateService({ 19 | schemaPath: `${__dirname}/example-files/swagger-get-method-params-convert-obj.json`, 20 | serversPath: './servers', 21 | }); 22 | 23 | await openAPI.generateService({ 24 | schemaPath: `${__dirname}/example-files/swagger-schema-contain-blank-symbol.json`, 25 | serversPath: './servers/blank-symbol-servers', 26 | }); 27 | 28 | await openAPI.generateService({ 29 | requestLibPath: "import request from '@/request';", 30 | schemaPath: `${__dirname}/example-files/swagger-custom-hook.json`, 31 | serversPath: './servers/custom', 32 | declareType: 'interface', 33 | hook: { 34 | // 自定义类名 35 | customClassName: (tagName) => { 36 | return /[A-Z].+/.exec(tagName); 37 | }, 38 | // 自定义函数名 39 | customFunctionName: (data) => { 40 | let funName = data.operationId ? data.operationId : ''; 41 | const suffix = 'Using'; 42 | if (funName.indexOf(suffix) != -1) { 43 | funName = funName.substring(0, funName.lastIndexOf(suffix)); 44 | } 45 | return funName; 46 | }, 47 | // 自定义类型名 48 | customTypeName: (data) => { 49 | const { operationId } = data; 50 | const funName = operationId ? operationId[0].toUpperCase() + operationId.substring(1) : ''; 51 | const tag = data?.tags?.[0]; 52 | 53 | return `${tag ? tag : ''}${funName}`; 54 | }, 55 | }, 56 | }); 57 | 58 | // 支持null类型作为默认值 59 | await openAPI.generateService({ 60 | schemaPath: `${__dirname}/example-files/swagger-get-method-params-convert-obj.json`, 61 | serversPath: './servers/support-null', 62 | nullable: true, 63 | }); 64 | 65 | // 正常命名文件和请求函数 66 | await openAPI.generateService({ 67 | schemaPath: `${__dirname}/example-files/swagger-get-method-params-convert-obj.json`, 68 | serversPath: './servers/name/normal', 69 | isCamelCase: false, 70 | }); 71 | 72 | // 小驼峰命名文件和请求函数 73 | await openAPI.generateService({ 74 | schemaPath: `${__dirname}/example-files/swagger-get-method-params-convert-obj.json`, 75 | serversPath: './servers/name/camel-case', 76 | isCamelCase: true, 77 | }); 78 | 79 | await openAPI.generateService({ 80 | schemaPath: `${__dirname}/example-files/swagger-file-convert.json`, 81 | serversPath: './file-servers', 82 | }); 83 | 84 | // check 文件生成 85 | const fileControllerStr = fs.readFileSync( 86 | path.join(__dirname, 'file-servers/api/fileController.ts'), 87 | 'utf8', 88 | ); 89 | assert(fileControllerStr.indexOf('!(item instanceof File)') > 0); 90 | assert(fileControllerStr.indexOf(`requestType: 'form',`) > 0); 91 | assert(fileControllerStr.indexOf('Content-Type') < 0); 92 | // await openAPI.generateService({ 93 | // // requestLibPath: "import request from '@/request';", 94 | // schemaPath: `http://82.157.33.9/swagger/swagger.json`, 95 | // serversPath: './servers', 96 | // }); 97 | // await openAPI.generateService({ 98 | // schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', 99 | // serversPath: './servers', 100 | // mockFolder: './mocks', 101 | // }); 102 | // await openAPI.generateService({ 103 | // schemaPath: 'http://petstore.swagger.io/v2/swagger.json', 104 | // serversPath: './servers', 105 | // mockFolder: './mocks', 106 | // }); 107 | // await openAPI.generateService({ 108 | // schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/LyDMjDyIhK/1611471979478-opa.json', 109 | // serversPath: './servers', 110 | // mockFolder: './mocks', 111 | // }); 112 | // await openAPI.generateService({ 113 | // schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/Zd7dLTHUjE/ant-design-pro.json', 114 | // serversPath: './servers', 115 | // mockFolder: './mocks', 116 | // }); 117 | // await openAPI.generateService({ 118 | // schemaPath: `${__dirname}/morse-api.json`, 119 | // serversPath: './servers', 120 | // mockFolder: './mocks', 121 | // }); 122 | // await openAPI.generateService({ 123 | // schemaPath: `${__dirname}/oc-swagger.json`, 124 | // serversPath: './servers', 125 | // mockFolder: './mocks', 126 | // }); 127 | // await openAPI.generateService({ 128 | // schemaPath: `${__dirname}/java-api.json`, 129 | // serversPath: './servers', 130 | // mockFolder: './mocks', 131 | // }); 132 | 133 | await openAPI.generateService({ 134 | schemaPath: `${__dirname}/example-files/apispec_1.json`, 135 | serversPath: './apispe', 136 | mockFolder: './mocks', 137 | mockConfig: { 138 | // msw: true, 139 | }, 140 | }); 141 | }; 142 | gen(); 143 | -------------------------------------------------------------------------------- /test/test.locale.js: -------------------------------------------------------------------------------- 1 | const openAPI = require('../dist/index'); 2 | 3 | openAPI.generateService({ 4 | schemaPath: './openapi.json', 5 | serversPath: './servers', 6 | }); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "src", 4 | "outDir": "dist", 5 | "target": "ES2015", 6 | "module": "CommonJS", 7 | "moduleResolution": "node", 8 | "importHelpers": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "skipLibCheck": true, 12 | "declaration": true 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules", "lib", "es", "dist", "**/__tests__", "**/__test__", "**/demo"] 16 | } 17 | --------------------------------------------------------------------------------