From acd1c2a355419f3848b58be7d10d89d9c234fdb7 Mon Sep 17 00:00:00 2001 From: Anton Koshkin <15276485+scink@users.noreply.github.com> Date: Thu, 13 Dec 2018 18:47:05 +0300 Subject: [PATCH] feat: add multiple file support (#6) * feat: add multiple file support Closes #2 BREAKING CHANGE: `pathToSpec` renamed to `pathsToSpec` and it is `string[]` now instead of `string` --- package-lock.json | 136 ++++++++++++++++++----- package.json | 2 + src/fileReader.ts | 3 + src/index.ts | 112 +++++++++++++------ src/language/typescript.ts | 27 ++++- src/swagger.ts | 32 +++--- test/index.ts | 4 +- test/specs/common.json | 182 ++++++++++++++++++++++++++++++ test/{ => specs}/swagger.json | 204 +++------------------------------- 9 files changed, 431 insertions(+), 271 deletions(-) create mode 100644 src/fileReader.ts create mode 100644 test/specs/common.json rename test/{ => specs}/swagger.json (84%) diff --git a/package-lock.json b/package-lock.json index e03878c..86cd960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,19 @@ "typelevel-ts": "^0.2.2" } }, + "@types/del": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/@types/del/-/del-3.0.1.tgz", + "integrity": "sha512-y6qRq6raBuu965clKgx6FHuiPu3oHdtmzMPXi8Uahsjdq1L6DL5fS/aY5/s71YwM7k6K1QIWvem5vNwlnNGIkQ==", + "requires": { + "@types/glob": "*" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, "@types/fs-extra": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz", @@ -88,6 +101,21 @@ "@types/node": "*" } }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, "@types/node": { "version": "10.5.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.3.tgz", @@ -199,6 +227,19 @@ "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -294,8 +335,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -377,7 +417,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -592,8 +631,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "configstore": { "version": "3.1.2", @@ -953,6 +991,19 @@ } } }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1254,8 +1305,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.4", @@ -1947,7 +1997,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1987,6 +2036,25 @@ "ini": "^1.3.4" } }, + "globby": { + "version": "6.1.0", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -2120,7 +2188,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2129,8 +2196,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -2333,11 +2399,23 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, "requires": { "path-is-inside": "^1.0.1" } @@ -2681,7 +2759,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2861,8 +2938,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -2917,7 +2993,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2970,6 +3045,11 @@ "p-limit": "^1.1.0" } }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==" + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -3025,14 +3105,12 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" }, "path-key": { "version": "2.0.1", @@ -3067,20 +3145,17 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -3378,6 +3453,14 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "^7.0.5" + } + }, "rxjs": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", @@ -4211,8 +4294,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.3.0", diff --git a/package.json b/package.json index 69857b5..493b216 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,10 @@ }, "dependencies": { "@devexperts/utils": "^0.12.6", + "@types/del": "^3.0.1", "@types/fs-extra": "^5.0.4", "@types/prettier": "^1.13.2", + "del": "^3.0.0", "fp-ts": "^1.10.0", "fs-extra": "^7.0.0", "io-ts": "^1.2.1", diff --git a/src/fileReader.ts b/src/fileReader.ts new file mode 100644 index 0000000..0a6286d --- /dev/null +++ b/src/fileReader.ts @@ -0,0 +1,3 @@ +import { TFileReader } from './index'; + +export const fromJSON: TFileReader = buffer => JSON.parse(buffer.toString()); diff --git a/src/index.ts b/src/index.ts index 91ed770..28ce5b9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,31 @@ -import { SwaggerObject } from './swagger'; +import { SwaggerObject, TSwaggerObject } from './swagger'; import * as prettier from 'prettier'; -import { map, write } from './fs'; +import { map, TFSEntity, write } from './fs'; import { TSerializer } from './utils'; import * as fs from 'fs-extra'; -import { fromNullable } from 'fp-ts/lib/Option'; +import { fromNullable, Option } from 'fp-ts/lib/Option'; import * as path from 'path'; import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; import { PathReporter } from 'io-ts/lib/PathReporter'; -import { last } from 'fp-ts/lib/Array'; +import { head, last } from 'fp-ts/lib/Array'; +import { Right } from 'fp-ts/lib/Either'; +import { ValidationError } from 'io-ts'; +import { serialize } from './language/typescript'; +import * as del from 'del'; const log = console.log.bind(console, '[SWAGGER-CODEGEN-TS]:'); +type TJSON = { + [key: string]: unknown; +}; + +export type TFileReader = (buffer: Buffer) => TJSON; + export type TGenerateOptions = { /** - * Path to json spec + * Paths to spec files */ - pathToSpec: string; + pathsToSpec: string[]; /** * Path to output directory (should be empty) */ @@ -28,42 +38,76 @@ export type TGenerateOptions = { * Path to prettier config */ pathToPrettierConfig?: string; + /** + * Buffer to JSON converter + * @param buffer - File Buffer + */ + fileReader: TFileReader; }; const cwd = process.cwd(); const resolvePath = (p: string) => (path.isAbsolute(p) ? p : path.resolve(cwd, p)); -export async function generate(options: TGenerateOptions): Promise { - const { serialize, pathToPrettierConfig } = options; - const out = resolvePath(options.out); - const pathToSpec = resolvePath(options.pathToSpec); - log('Reading spec from', pathToSpec); - const buffer = await fs.readFile(pathToSpec); - log('Parsing spec'); - const json = JSON.parse(buffer.toString()); - log('Decoding spec'); - const decoded = SwaggerObject.decode(json); - if (decoded.isLeft()) { - const report = PathReporter.report(decoded); - const lastReport = last(report); - log(lastReport.getOrElse('Invalid spec')); - ThrowReporter.report(decoded); - return; - } - log('Serializing spec'); - const serialized = serialize(path.basename(out), decoded.value); - log('Running prettier'); - const prettierConfig = fromNullable( +type TBuffer = { + buffer: Buffer; + fileName: string; +}; + +const read = async (pathToFile: string): Promise => { + const filePath = resolvePath(pathToFile); + return { + buffer: await fs.readFile(filePath), + fileName: path.basename(filePath), + }; +}; + +const serializeDecode = (serializer: TSerializer) => async ( + decoded: Right, + out: string, +): Promise => serializer(path.basename(out), decoded.value); + +const getPrettierConfig = async (pathToPrettierConfig?: string): Promise> => + fromNullable( await prettier.resolveConfig( fromNullable(pathToPrettierConfig) .map(resolvePath) .getOrElseL(() => path.resolve(__dirname, '../.prettierrc')), ), ); - const formatted = prettierConfig - .map(config => map(serialized, content => prettier.format(content, config))) - .getOrElse(serialized); - log('Writing to', out); - await write(path.dirname(out), formatted); - log('Done.'); -} + +const formatSerialized = (serialized: TFSEntity, prettierConfig: Option): TFSEntity => + prettierConfig.map(config => map(serialized, content => prettier.format(content, config))).getOrElse(serialized); + +const writeFormatted = (out: string, formatted: TFSEntity) => write(path.dirname(out), formatted); + +export const generate = async (options: TGenerateOptions): Promise => { + const out = resolvePath(options.out); + const isPathExist = await fs.pathExists(out); + + if (isPathExist) { + await del(out); + } + await fs.mkdirp(out); + const prettierConfig = await getPrettierConfig(options.pathToPrettierConfig); + const serializer = serializeDecode(serialize); + + for (const pathToFile of options.pathsToSpec) { + const pathToSpec = resolvePath(pathToFile); + const buffer = await read(pathToSpec); + const dirName = head(buffer.fileName.split('.')).getOrElse(buffer.fileName); + const apiOut = path.resolve(out, `./${dirName}`); + await fs.mkdir(apiOut); + const json = options.fileReader(buffer.buffer); + const decoded = SwaggerObject.decode(json); + if (decoded.isLeft()) { + const report = PathReporter.report(decoded); + const lastReport = last(report); + log(lastReport.getOrElse('Invalid spec')); + ThrowReporter.report(decoded); + return; + } + const seralized = await serializer(decoded, apiOut); + const formatted = formatSerialized(seralized, prettierConfig); + writeFormatted(apiOut, formatted); + } +}; diff --git a/src/language/typescript.ts b/src/language/typescript.ts index a79f21d..b5dc7dd 100644 --- a/src/language/typescript.ts +++ b/src/language/typescript.ts @@ -24,7 +24,7 @@ import { groupPathsByTag, TSerializer, } from '../utils'; -import { none, Option, some } from 'fp-ts/lib/Option'; +import { fromNullable, none, Option, some } from 'fp-ts/lib/Option'; import { getArrayMonoid, getRecordMonoid, monoidString, fold, monoidAny } from 'fp-ts/lib/Monoid'; import { decapitalize } from '@devexperts/utils/dist/string/string'; import { intercalate } from 'fp-ts/lib/Foldable2v'; @@ -135,8 +135,10 @@ const UTILS_DIRECTORY = 'utils'; const UTILS_FILENAME = 'utils'; const getRelativeRoot = (cwd: string) => path.relative(cwd, ROOT_DIRECTORY); -const getRelativeDefinitionPath = (cwd: string, definitionFileName: string): string => - `${getRelativeRoot(cwd)}/${DEFINITIONS_DIRECTORY}/${definitionFileName}`; +const getRelativeRefPath = (cwd: string, refBlockName: string, refFileName: string): string => + `${getRelativeRoot(cwd)}/${refBlockName}/${refFileName}`; +const getRelativeOutRefPath = (cwd: string, blockName: string, outFileName: string, refFileName: string): string => + `${getRelativeRoot(cwd)}/../${outFileName}/${blockName}/${refFileName}`; const getRelativeClientPath = (cwd: string): string => `${getRelativeRoot(cwd)}/${CLIENT_DIRECTORY}/${CLIENT_FILENAME}`; const getRelativeUtilsPath = (cwd: string): string => `${getRelativeRoot(cwd)}/${UTILS_DIRECTORY}/${UTILS_FILENAME}`; @@ -218,10 +220,25 @@ const serializePath = (url: string, item: TPathItemObject, rootName: string, cwd const serializeSchemaObject = (schema: TSchemaObject, rootName: string, cwd: string): TSerializedType => { switch (schema.type) { case undefined: { - const type = `${schema.$ref.replace(/^#\/definitions\//g, '')}`; + const $ref = schema.$ref; + const parts = fromNullable($ref.match(/^((.+)\/(.+)\.(.+))?#\/(.+)\/(.+)$/)); + + const defBlock = parts.mapNullable(parts => parts[5]); + const refFileName = parts.mapNullable(parts => parts[3]); + const safeType = parts.mapNullable(parts => parts[6]); + + if (safeType.isNone() || defBlock.isNone()) { + throw new Error(`Invalid $ref: ${$ref}`); + } + + const type = safeType.value; + const io = getIOName(type); const isRecursive = rootName === type || rootName === io; - const definitionFilePath = getRelativeDefinitionPath(cwd, type); + const definitionFilePath = refFileName.isSome() + ? getRelativeOutRefPath(cwd, defBlock.value, refFileName.value, type) + : getRelativeRefPath(cwd, defBlock.value, type); + return serializedType( type, io, diff --git a/src/swagger.ts b/src/swagger.ts index 9d38112..aed9636 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -733,36 +733,36 @@ export const TagObject: t.Type = t.type({ }); export type TSwaggerObject = { - swagger: string; - info: TInfoObject; - host: Option; basePath: Option; - schemes: Option; consumes: Option; - produces: Option; - paths: TPathsObject; definitions: Option; + externalDocs: Option; + host: Option; + info: TInfoObject; parameters: Option; + paths: TPathsObject; + produces: Option; responses: Option; - securityDefinitions: Option; + schemes: Option; security: Option; + securityDefinitions: Option; + swagger: string; tags: Option; - externalDocs: Option; }; export const SwaggerObject: t.Type = t.type({ - swagger: t.string, - info: InfoObject, - host: stringOption, basePath: stringOption, - schemes: stringArrayOption, consumes: stringArrayOption, - produces: stringArrayOption, - paths: PathsObject, definitions: createOptionFromNullable(DefinitionsObject), + externalDocs: createOptionFromNullable(ExternalDocumentationObject), + host: stringOption, + info: InfoObject, parameters: createOptionFromNullable(ParametersDefinitionsObject), + paths: PathsObject, + produces: stringArrayOption, responses: createOptionFromNullable(ResponsesDefinitionsObject), - securityDefinitions: createOptionFromNullable(SecurityDefinitionsObject), + schemes: stringArrayOption, security: createOptionFromNullable(t.array(SecurityRequirementObject)), + securityDefinitions: createOptionFromNullable(SecurityDefinitionsObject), + swagger: t.string, tags: createOptionFromNullable(t.array(TagObject)), - externalDocs: createOptionFromNullable(ExternalDocumentationObject), }); diff --git a/test/index.ts b/test/index.ts index bbf4a9f..6884bd8 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3,13 +3,15 @@ import * as path from 'path'; import { generate } from '../src'; import { serialize } from '../src/language/typescript'; +import { fromJSON } from '../src/fileReader'; const self = path.resolve(__dirname); generate({ - pathToSpec: path.resolve(self, './swagger.json'), + pathsToSpec: [path.resolve(self, './specs/swagger.json'), path.resolve(self, './specs/common.json')], out: path.resolve(self, './out'), serialize, + fileReader: fromJSON, }).catch(error => { console.error(error); process.exit(1); diff --git a/test/specs/common.json b/test/specs/common.json new file mode 100644 index 0000000..50b620c --- /dev/null +++ b/test/specs/common.json @@ -0,0 +1,182 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore" + }, + "paths": [], + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "EmptyObject": { + "type": "object" + } + } +} diff --git a/test/swagger.json b/test/specs/swagger.json similarity index 84% rename from test/swagger.json rename to test/specs/swagger.json index c6ecc45..5792efd 100644 --- a/test/swagger.json +++ b/test/specs/swagger.json @@ -41,6 +41,8 @@ "https", "http" ], + "parameters": { + }, "paths": { "/pet": { "post": { @@ -65,7 +67,7 @@ "description": "Pet object that needs to be added to the store", "required": true, "schema": { - "$ref": "#/definitions/Pet" + "$ref": "./common.json#/definitions/Pet" } } ], @@ -105,7 +107,7 @@ "description": "Pet object that needs to be added to the store", "required": true, "schema": { - "$ref": "#/definitions/Pet" + "$ref": "./common.json#/definitions/Pet" } } ], @@ -167,7 +169,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/Pet" + "$ref": "./common.json#/definitions/Pet" } } }, @@ -216,7 +218,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/Pet" + "$ref": "./common.json#/definitions/Pet" } } }, @@ -261,7 +263,7 @@ "200": { "description": "successful operation", "schema": { - "$ref": "#/definitions/Pet" + "$ref": "./common.json#/definitions/Pet" } }, "400": { @@ -416,7 +418,7 @@ "200": { "description": "successful operation", "schema": { - "$ref": "#/definitions/ApiResponse" + "$ref": "./common.json#/definitions/ApiResponse" } } }, @@ -480,7 +482,7 @@ "description": "order placed for purchasing the pet", "required": true, "schema": { - "$ref": "#/definitions/Order" + "$ref": "./common.json#/definitions/Order" } } ], @@ -488,7 +490,7 @@ "200": { "description": "successful operation", "schema": { - "$ref": "#/definitions/Order" + "$ref": "./common.json#/definitions/Order" } }, "400": { @@ -525,7 +527,7 @@ "200": { "description": "successful operation", "schema": { - "$ref": "#/definitions/Order" + "$ref": "./common.json#/definitions/Order" } }, "400": { @@ -587,7 +589,7 @@ "description": "Created user object", "required": true, "schema": { - "$ref": "#/definitions/User" + "$ref": "./common.json#/definitions/User" } } ], @@ -619,7 +621,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/User" + "$ref": "./common.json#/definitions/User" } } } @@ -652,7 +654,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/User" + "$ref": "./common.json#/definitions/User" } } } @@ -762,7 +764,7 @@ "200": { "description": "successful operation", "schema": { - "$ref": "#/definitions/User" + "$ref": "./common.json#/definitions/User" } }, "400": { @@ -798,7 +800,7 @@ "description": "Updated user object", "required": true, "schema": { - "$ref": "#/definitions/User" + "$ref": "./common.json#/definitions/User" } } ], @@ -907,180 +909,6 @@ "in": "header" } }, - "definitions": { - "Order": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "petId": { - "type": "integer", - "format": "int64" - }, - "quantity": { - "type": "integer", - "format": "int32" - }, - "shipDate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "description": "Order Status", - "enum": [ - "placed", - "approved", - "delivered" - ] - }, - "complete": { - "type": "boolean", - "default": false - } - }, - "xml": { - "name": "Order" - } - }, - "User": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "password": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "userStatus": { - "type": "integer", - "format": "int32", - "description": "User Status" - } - }, - "xml": { - "name": "User" - } - }, - "Category": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "xml": { - "name": "Category" - } - }, - "Tag": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "xml": { - "name": "Tag" - } - }, - "ApiResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "type": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, - "Pet": { - "type": "object", - "required": [ - "name", - "photoUrls" - ], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "category": { - "$ref": "#/definitions/Category" - }, - "name": { - "type": "string", - "example": "doggie" - }, - "photoUrls": { - "type": "array", - "xml": { - "name": "photoUrl", - "wrapped": true - }, - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "xml": { - "name": "tag", - "wrapped": true - }, - "items": { - "$ref": "#/definitions/Tag" - } - }, - "status": { - "type": "string", - "description": "pet status in the store", - "enum": [ - "available", - "pending", - "sold" - ] - } - }, - "xml": { - "name": "Pet" - } - }, - "EmptyObject" : { - "type" : "object" - } - }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io"