diff --git a/README.md b/README.md index 047c9e40c..9cbdd6f9d 100644 --- a/README.md +++ b/README.md @@ -98,15 +98,18 @@ npx openapi-typescript schema.yaml #### CLI Options -| Option | Alias | Default | Description | -| :----------------------------- | :---- | :------: | :------------------------------------------------------------------------------------------------------ | -| `--output [location]` | `-o` | (stdout) | Where should the output file be saved? | -| `--auth [token]` | | | (optional) Provide an auth token to be passed along in the request (only if accessing a private schema) | -| `--immutable-types` | `-it` | `false` | (optional) Generates immutable types (readonly properties and readonly array) | -| `--additional-properties` | `-ap` | `false` | (optional) Allow arbitrary properties for all schema objects without `additionalProperties: false` | -| `--default-non-nullable` | | `false` | (optional) Treat schema objects with default values as non-nullable | -| `--prettier-config [location]` | `-c` | | (optional) Path to your custom Prettier configuration for output | -| `--raw-schema` | | `false` | Generate TS types from partial schema (e.g. having `components.schema` at the top level) | +| Option | Alias | Default | Description | +| :----------------------------- | :---- | :------: | :-------------------------------------------------------------------------------------------------------------------------------------- | +| `--output [location]` | `-o` | (stdout) | Where should the output file be saved? | +| `--auth [token]` | | | (optional) Provide an auth token to be passed along in the request (only if accessing a private schema) | +| `--immutable-types` | `-it` | `false` | (optional) Generates immutable types (readonly properties and readonly array) | +| `--additional-properties` | `-ap` | `false` | (optional) Allow arbitrary properties for all schema objects without `additionalProperties: false` | +| `--default-non-nullable` | | `false` | (optional) Treat schema objects with default values as non-nullable | +| `--prettier-config [location]` | `-c` | | (optional) Path to your custom Prettier configuration for output | +| `--raw-schema` | | `false` | Generate TS types from partial schema (e.g. having `components.schema` at the top level) | +| `--httpMethod` | `-m` | `GET` | (optional) Provide the HTTP Verb/Method for fetching a schema from a remote URL | +| `--headersObject` | `-h` | | (optional) Provide a JSON object as string of HTTP headers for remote schema request. This will take priority over `--header` | +| `--header` | `-x` | | (optional) Provide an array of or singular headers as an alternative to a JSON object. Each header must follow the `key: value` pattern | ### 🐢 Node diff --git a/bin/cli.js b/bin/cli.js index cbea4a0ad..0b81dde25 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -7,7 +7,7 @@ const path = require("path"); const glob = require("tiny-glob"); const { default: openapiTS } = require("../dist/cjs/index.js"); -let cli = meow( +const cli = meow( `Usage $ openapi-typescript [input] [options] @@ -15,6 +15,9 @@ Options --help display this --output, -o Specify output file (default: stdout) --auth (optional) Provide an authentication token for private URL + --headersObject, -h (optional) Provide a JSON object as string of HTTP headers for remote schema request + --header, -x (optional) Provide an array of or singular headers as an alternative to a JSON object. Each header must follow the key: value pattern + --httpMethod, -m (optional) Provide the HTTP Verb/Method for fetching a schema from a remote URL --immutable-types, -it (optional) Generates immutable types (readonly properties and readonly array) --additional-properties, -ap (optional) Allow arbitrary properties for all schema objects without "additionalProperties: false" --default-non-nullable (optional) If a schema object has a default value set, don’t mark it as nullable @@ -31,6 +34,20 @@ Options auth: { type: "string", }, + headersObject: { + type: "string", + alias: "h", + }, + header: { + type: "string", + alias: "x", + isMultiple: true, + }, + httpMethod: { + type: "string", + alias: "m", + default: "GET", + }, immutableTypes: { type: "boolean", alias: "it", @@ -69,6 +86,22 @@ function errorAndExit(errorMessage) { async function generateSchema(pathToSpec) { const output = cli.flags.output ? OUTPUT_FILE : OUTPUT_STDOUT; // FILE or STDOUT + // Parse incoming headers from CLI flags + let httpHeaders = {}; + // prefer --headersObject if specified + if (cli.flags.headersObject) { + httpHeaders = JSON.parse(cli.flags.headersObject); // note: this will generate a recognizable error for the user to act on + } + // otherwise, parse --header + else if (Array.isArray(cli.flags.header)) { + cli.flags.header.forEach((header) => { + const firstColon = header.indexOf(":"); + const k = header.substring(0, firstColon).trim(); + const v = header.substring(firstColon + 1).trim(); + httpHeaders[k] = v; + }); + } + // generate schema const result = await openapiTS(pathToSpec, { additionalProperties: cli.flags.additionalProperties, @@ -79,22 +112,24 @@ async function generateSchema(pathToSpec) { rawSchema: cli.flags.rawSchema, silent: output === OUTPUT_STDOUT, version: cli.flags.version, + httpHeaders, + httpMethod: cli.flags.httpMethod, }); // output if (output === OUTPUT_FILE) { - let outputFile = path.resolve(process.cwd(), cli.flags.output); // note: may be directory - const isDir = fs.existsSync(outputFile) && fs.lstatSync(outputFile).isDirectory(); + let outputFilePath = path.resolve(process.cwd(), cli.flags.output); // note: may be directory + const isDir = fs.existsSync(outputFilePath) && fs.lstatSync(outputFilePath).isDirectory(); if (isDir) { const filename = pathToSpec.replace(new RegExp(`${path.extname(pathToSpec)}$`), ".ts"); - outputFile = path.join(outputFile, filename); + outputFilePath = path.join(outputFilePath, filename); } - await fs.promises.writeFile(outputFile, result, "utf8"); + await fs.promises.writeFile(outputFilePath, result, "utf8"); const timeEnd = process.hrtime(timeStart); const time = timeEnd[0] + Math.round(timeEnd[1] / 1e6); - console.log(green(`šŸš€ ${pathToSpec} -> ${bold(outputFile)} [${time}ms]`)); + console.log(green(`šŸš€ ${pathToSpec} -> ${bold(outputFilePath)} [${time}ms]`)); } else { process.stdout.write(result); // if stdout, (still) don’t log anything to console! diff --git a/package-lock.json b/package-lock.json index b0e55e7f5..fdd46da39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "openapi-typescript": "bin/cli.js" }, "devDependencies": { - "@types/jest": "^27.0.0", + "@types/jest": "^27.0.2", "@types/js-yaml": "^4.0.1", "@types/mime": "^2.0.3", "@types/node-fetch": "^2.5.12", @@ -38,7 +38,8 @@ "typescript": "^4.4.3" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 12.0.0", + "npm": ">= 7.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 39bd77633..d823e0d7e 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,10 @@ "prepare": "npm run build", "pregenerate": "npm run build", "test": "npm run build && jest --no-cache", + "test:watch": "npm run build && jest --no-cache --watch", + "test:update": "npm run build && jest --no-cache --u", "test:coverage": "npm run build && jest --no-cache --coverage && codecov", + "test:coverage:local": "npm run build && jest --no-cache --collectCoverage", "typecheck": "tsc --noEmit --project tsconfig.esm.json", "version": "npm run build" }, @@ -66,7 +69,7 @@ "tiny-glob": "^0.2.9" }, "devDependencies": { - "@types/jest": "^27.0.0", + "@types/jest": "^27.0.2", "@types/js-yaml": "^4.0.1", "@types/mime": "^2.0.3", "@types/node-fetch": "^2.5.12", diff --git a/src/index.ts b/src/index.ts index 63bdf1b5f..97944ca46 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,9 +17,22 @@ export const WARNING_MESSAGE = `/** `; -export default async function openapiTS( +/** + * This function is the entry to the program and allows the user to pass in a remote schema and/or local schema. + * The URL or schema and headers can be passed in either programtically and/or via the CLI. + * Remote schemas are fetched from a server that supplies JSON or YAML format via an HTTP GET request. File based schemas + * are loaded in via file path, most commonly prefixed with the file:// format. Alternatively, the user can pass in + * OpenAPI2 or OpenAPI3 schema objects that can be parsed directly by the function without reading the file system. + * + * Function overloading is utilized for generating stronger types for our different schema types and option types. + * + * @param {string} schema Root Swagger Schema HTTP URL, File URL, and/or JSON or YAML schema + * @param {SwaggerToTSOptions} [options] Options to specify to the parsing system + * @return {Promise} {Promise} Parsed file schema + */ +async function openapiTS( schema: string | OpenAPI2 | OpenAPI3 | Record, - options: SwaggerToTSOptions = {} as any + options: SwaggerToTSOptions = {} as Partial ): Promise { const ctx: GlobalContext = { additionalProperties: options.additionalProperties || false, @@ -40,11 +53,15 @@ export default async function openapiTS( if (typeof schema === "string") { const schemaURL = resolveSchema(schema); if (options.silent === false) console.log(yellow(`šŸ”­ Loading spec from ${bold(schemaURL.href)}…`)); + await load(schemaURL, { ...ctx, schemas: allSchemas, rootURL: schemaURL, // as it crawls schemas recursively, it needs to know which is the root to resolve everything relative to + httpHeaders: options.httpHeaders, + httpMethod: options.httpMethod, }); + for (const k of Object.keys(allSchemas)) { if (k === schemaURL.href) { rootSchema = allSchemas[k]; @@ -53,7 +70,14 @@ export default async function openapiTS( } } } else { - await load(schema, { ...ctx, schemas: allSchemas, rootURL: new URL(VIRTUAL_JSON_URL) }); + await load(schema, { + ...ctx, + schemas: allSchemas, + rootURL: new URL(VIRTUAL_JSON_URL), + httpHeaders: options.httpHeaders, + httpMethod: options.httpMethod, + }); + for (const k of Object.keys(allSchemas)) { if (k === VIRTUAL_JSON_URL) { rootSchema = allSchemas[k]; @@ -108,3 +132,5 @@ export default async function openapiTS( } return prettier.format(output, prettierOptions); } + +export default openapiTS; diff --git a/src/load.ts b/src/load.ts index 6f32436c8..7df6c2344 100644 --- a/src/load.ts +++ b/src/load.ts @@ -5,7 +5,8 @@ import { URL } from "url"; import slash from "slash"; import mime from "mime"; import yaml from "js-yaml"; -import { GlobalContext } from "./types"; +import { red } from "kleur"; +import { GlobalContext, Headers } from "./types"; import { parseRef } from "./utils"; type PartialSchema = Record; // not a very accurate type, but this is easier to deal with before we know we’re dealing with a valid spec @@ -43,17 +44,52 @@ export function resolveSchema(url: string): URL { const localPath = path.isAbsolute(url) ? new URL("", `file://${slash(url)}`) : new URL(url, `file://${slash(process.cwd())}/`); // if absolute path is provided use that; otherwise search cwd\ + if (!fs.existsSync(localPath)) { throw new Error(`Could not locate ${url}`); } else if (fs.statSync(localPath).isDirectory()) { throw new Error(`${localPath} is a directory not a file`); } + return localPath; } +/** + * Accepts income HTTP headers and appends them to + * the fetch request for the schema. + * + * @param {HTTPHeaderMap} httpHeaders + * @return {Record} {Record} Final HTTP headers outcome. + */ +function parseHttpHeaders(httpHeaders: Record): Headers { + const finalHeaders: Record = {}; + + // Obtain the header key + for (const [k, v] of Object.entries(httpHeaders)) { + // If the value of the header is already a string, we can move on, otherwise we have to parse it + if (typeof v === "string") { + finalHeaders[k] = v; + } else { + try { + const stringVal = JSON.stringify(v); + finalHeaders[k] = stringVal; + } catch (err) { + /* istanbul ignore next */ + console.error( + red(`Cannot parse key: ${k} into JSON format. Continuing with the next HTTP header that is specified`) + ); + } + } + } + + return finalHeaders; +} + interface LoadOptions extends GlobalContext { rootURL: URL; schemas: SchemaMap; + httpHeaders?: Headers; + httpMethod?: string; } // temporary cache for load() @@ -65,7 +101,7 @@ export default async function load( options: LoadOptions ): Promise<{ [url: string]: PartialSchema }> { const isJSON = schema instanceof URL === false; // if this is dynamically-passed-in JSON, we’ll have to change a few things - let schemaID = isJSON ? new URL(VIRTUAL_JSON_URL).href : schema.href; + let schemaID = isJSON ? new URL(VIRTUAL_JSON_URL).href : (schema.href as string); const schemas = options.schemas; @@ -88,13 +124,20 @@ export default async function load( contentType = mime.getType(schemaID) || ""; } else { // load remote - const res = await fetch(schemaID, { - method: "GET", - headers: { - "User-Agent": "openapi-typescript", - ...(options.auth ? {} : { Authorization: options.auth }), - }, - }); + const headers: Headers = { + "User-Agent": "openapi-typescript", + }; + if (options.auth) headers.Authorizaton = options.auth; + + // Add custom parsed HTTP headers + if (options.httpHeaders) { + const parsedHeaders = parseHttpHeaders(options.httpHeaders); + for (const [k, v] of Object.entries(parsedHeaders)) { + headers[k] = v; + } + } + + const res = await fetch(schemaID, { method: options.httpMethod || "GET", headers }); contentType = res.headers.get("Content-Type") || ""; contents = await res.text(); } diff --git a/src/types.ts b/src/types.ts index 681d53eff..73f94cb60 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,8 @@ export interface OpenAPI3 { }; } +export type Headers = Record; + export interface HeaderObject { // note: this extends ParameterObject, minus "name" & "in" type?: string; // required @@ -134,6 +136,21 @@ export interface SwaggerToTSOptions { silent?: boolean; /** (optional) OpenAPI version. Must be present if parsing raw schema */ version?: number; + /** + * (optional) List of HTTP headers that will be sent with the fetch request to a remote schema. This is + * in addition to the authorization header. In some cases, servers require headers such as Accept: application/json + * or Accept: text/yaml to be sent in order to figure out how to properly fetch the OpenAPI/Swagger document as code. + * These headers will only be sent in the case that the schema URL protocol is of type http or https. + */ + httpHeaders?: Headers; + /** + * HTTP verb used to fetch the schema from a remote server. This is only applied + * when the schema is a string and has the http or https protocol present. By default, + * the request will use the HTTP GET method to fetch the schema from the server. + * + * @default {string} GET + */ + httpMethod?: string; } /** Context passed to all submodules */ diff --git a/tests/__snapshots__/empty-definitions.test.ts.snap b/tests/__snapshots__/empty-definitions.test.ts.snap new file mode 100644 index 000000000..4581f0891 --- /dev/null +++ b/tests/__snapshots__/empty-definitions.test.ts.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`allow empty definitions allow empty definitions 1`] = ` +"/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + \\"/pet\\": { + post: operations[\\"addPet\\"]; + }; +} + +export interface operations { + addPet: { + parameters: { + body: { + /** Pet object that needs to be added to the store */ + body: definitions[\\"Pet\\"]; + }; + }; + responses: { + /** Invalid input */ + 405: unknown; + }; + }; +} + +export interface external {} +" +`; + +exports[`allow empty definitions allow empty definitions 2`] = ` +"/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + readonly \\"/pet\\": { + readonly post: operations[\\"addPet\\"]; + }; +} + +export interface operations { + readonly addPet: { + readonly parameters: { + readonly body: { + /** Pet object that needs to be added to the store */ + readonly body: definitions[\\"Pet\\"]; + }; + }; + readonly responses: { + /** Invalid input */ + readonly 405: unknown; + }; + }; +} + +export interface external {} +" +`; diff --git a/tests/__snapshots__/formatter.test.ts.snap b/tests/__snapshots__/formatter.test.ts.snap new file mode 100644 index 000000000..39cfe03eb --- /dev/null +++ b/tests/__snapshots__/formatter.test.ts.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`formatter basic 1`] = ` +"/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths {} + +export interface components { + schemas: { + date: Date; + }; +} + +export interface operations {} + +export interface external {} +" +`; + +exports[`formatter hasObject 1`] = ` +"/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths {} + +export interface components { + schemas: { + calendar: { + dates?: { + start?: Date; + }; + }; + }; +} + +export interface operations {} + +export interface external {} +" +`; diff --git a/tests/bin/cli.test.ts b/tests/bin/cli.test.ts index c4d1365e2..7171e84e4 100644 --- a/tests/bin/cli.test.ts +++ b/tests/bin/cli.test.ts @@ -7,22 +7,24 @@ import { sanitizeLB } from "../test-utils"; // v3/index.test.ts. So this file is mainly for testing other flags. const cmd = `node ../../bin/cli.js`; +const cwd = __dirname; describe("cli", () => { it("--prettier-config (JSON)", async () => { execSync(`${cmd} specs/petstore.yaml -o generated/prettier-json.ts --prettier-config fixtures/.prettierrc`, { - cwd: __dirname, + cwd, }); const [generated, expected] = await Promise.all([ fs.promises.readFile(path.join(__dirname, "generated", "prettier-json.ts"), "utf8"), fs.promises.readFile(path.join(__dirname, "expected", "prettier-json.ts"), "utf8"), ]); + expect(generated).toBe(sanitizeLB(expected)); }); it("--prettier-config (.js)", async () => { execSync(`${cmd} specs/petstore.yaml -o generated/prettier-js.ts --prettier-config fixtures/prettier.config.js`, { - cwd: __dirname, + cwd, }); const [generated, expected] = await Promise.all([ fs.promises.readFile(path.join(__dirname, "generated", "prettier-js.ts"), "utf8"), @@ -33,12 +35,12 @@ describe("cli", () => { it("stdout", async () => { const expected = fs.readFileSync(path.join(__dirname, "expected", "stdout.ts"), "utf8"); - const generated = execSync(`${cmd} specs/petstore.yaml`, { cwd: __dirname }); + const generated = execSync(`${cmd} specs/petstore.yaml`, { cwd }); expect(generated.toString("utf8")).toBe(sanitizeLB(expected)); }); it("supports glob paths", async () => { - execSync(`${cmd} "specs/*.yaml" -o generated/`, { cwd: __dirname }); // Quotes are necessary because shells like zsh treats glob weirdly + execSync(`${cmd} "specs/*.yaml" -o generated/`, { cwd }); // Quotes are necessary because shells like zsh treats glob weirdly const [generatedPetstore, expectedPetstore, generatedManifold, expectedManifold] = await Promise.all([ fs.promises.readFile(path.join(__dirname, "generated", "specs", "petstore.ts"), "utf8"), fs.promises.readFile(path.join(__dirname, "expected", "petstore.ts"), "utf8"), @@ -48,4 +50,24 @@ describe("cli", () => { expect(generatedPetstore).toBe(sanitizeLB(expectedPetstore)); expect(generatedManifold).toBe(sanitizeLB(expectedManifold)); }); + + it("--header", async () => { + // note: we can’t check headers passed to fetch() without mocking (and overcomplicating/flake-ifying the tests). simply testing the parsing is the biggest win. + expect(() => + execSync( + `${cmd} https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v2/specs/manifold.yaml --header "x-openapi-format:json" --header "x-openapi-version:3.0.1"`, + { cwd } + ).toString("utf8") + ).not.toThrow(); + }); + + it.only("--headersObject", async () => { + // note: same as above—testing the parser is the biggest win; values can be tested trivially with manual debugging + expect(() => { + execSync( + `${cmd} https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v2/specs/manifold.yaml --headersObject "{\\"x-boolean\\":true, \\"x-number\\":3.0, \\"x-string\\": \\"openapi\\"}"`, + { cwd } + ); + }).not.toThrow(); + }); }); diff --git a/tests/empty-definitions.test.ts b/tests/empty-definitions.test.ts index b33a9478b..40e2f5d23 100644 --- a/tests/empty-definitions.test.ts +++ b/tests/empty-definitions.test.ts @@ -30,62 +30,8 @@ describe("allow empty definitions", () => { }, }; - expect(await openapiTS(schema as any, { version: 2 })).toBe(`/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths { - "/pet": { - post: operations["addPet"]; - }; -} - -export interface operations { - addPet: { - parameters: { - body: { - /** Pet object that needs to be added to the store */ - body: definitions["Pet"]; - }; - }; - responses: { - /** Invalid input */ - 405: unknown; - }; - }; -} - -export interface external {} -`); - - expect(await openapiTS(schema as any, { immutableTypes: true, version: 2 })).toBe(`/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths { - readonly "/pet": { - readonly post: operations["addPet"]; - }; -} - -export interface operations { - readonly addPet: { - readonly parameters: { - readonly body: { - /** Pet object that needs to be added to the store */ - readonly body: definitions["Pet"]; - }; - }; - readonly responses: { - /** Invalid input */ - readonly 405: unknown; - }; - }; -} - -export interface external {} -`); + // We are using snapshots to enforce consistency in string output and indentation + expect(await openapiTS(schema as any, { version: 2 })).toMatchSnapshot(); + expect(await openapiTS(schema as any, { immutableTypes: true, version: 2 })).toMatchSnapshot(); }); }); diff --git a/tests/formatter.test.ts b/tests/formatter.test.ts index 41bfe7d01..c4e1c56ce 100644 --- a/tests/formatter.test.ts +++ b/tests/formatter.test.ts @@ -13,6 +13,8 @@ describe("formatter", () => { }, }, }; + + // Ensures consistency and indentation in string output with snapshot. Please do NOT update snapshot unless value changes expect( await openapiTS(schema, { formatter(schemaObj) { @@ -23,23 +25,7 @@ describe("formatter", () => { }, version: 3, }) - ).toBe(`/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths {} - -export interface components { - schemas: { - date: Date; - }; -} - -export interface operations {} - -export interface external {} -`); + ).toMatchSnapshot(); }); it("hasObject", async () => { @@ -75,26 +61,6 @@ export interface external {} }, version: 3, }) - ).toBe(`/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths {} - -export interface components { - schemas: { - calendar: { - dates?: { - start?: Date; - }; - }; - }; -} - -export interface operations {} - -export interface external {} -`); + ).toMatchSnapshot(); }); }); diff --git a/tests/remote-schema/__snapshots__/remote-schema.test.ts.snap b/tests/remote-schema/__snapshots__/remote-schema.test.ts.snap new file mode 100644 index 000000000..f5804fd79 --- /dev/null +++ b/tests/remote-schema/__snapshots__/remote-schema.test.ts.snap @@ -0,0 +1,506 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`remote $refs resolves remote $refs 1`] = ` +"/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths {} + +export interface components { + schemas: { + /** this is a duplicate of subschema/remote1.yml */ + Circular: string; + Remote1: external[\\"subschema/remote1.yml\\"][\\"components\\"][\\"schemas\\"][\\"Remote1\\"]; + Pet: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + }; +} + +export interface operations {} + +export interface external { + \\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\": { + paths: { + \\"/pet\\": { + put: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"updatePet\\"]; + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"addPet\\"]; + }; + \\"/pet/findByStatus\\": { + /** Multiple status values can be provided with comma separated strings */ + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"findPetsByStatus\\"]; + }; + \\"/pet/findByTags\\": { + /** Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. */ + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"findPetsByTags\\"]; + }; + \\"/pet/{petId}\\": { + /** Returns a single pet */ + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"getPetById\\"]; + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"updatePetWithForm\\"]; + delete: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"deletePet\\"]; + }; + \\"/pet/{petId}/uploadImage\\": { + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"uploadFile\\"]; + }; + \\"/store/inventory\\": { + /** Returns a map of status codes to quantities */ + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"getInventory\\"]; + }; + \\"/store/order\\": { + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"placeOrder\\"]; + }; + \\"/store/order/{orderId}\\": { + /** For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions */ + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"getOrderById\\"]; + /** For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors */ + delete: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"deleteOrder\\"]; + }; + \\"/user\\": { + /** This can only be done by the logged in user. */ + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"createUser\\"]; + }; + \\"/user/createWithArray\\": { + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"createUsersWithArrayInput\\"]; + }; + \\"/user/createWithList\\": { + post: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"createUsersWithListInput\\"]; + }; + \\"/user/login\\": { + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"loginUser\\"]; + }; + \\"/user/logout\\": { + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"logoutUser\\"]; + }; + \\"/user/{username}\\": { + get: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"getUserByName\\"]; + /** This can only be done by the logged in user. */ + put: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"updateUser\\"]; + /** This can only be done by the logged in user. */ + delete: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"operations\\"][\\"deleteUser\\"]; + }; + }; + components: { + schemas: { + Order: { + id?: number; + petId?: number; + quantity?: number; + shipDate?: string; + /** Order Status */ + status?: \\"placed\\" | \\"approved\\" | \\"delivered\\"; + complete?: boolean; + }; + Category: { + id?: number; + name?: string; + }; + User: { + id?: number; + username?: string; + firstName?: string; + lastName?: string; + email?: string; + password?: string; + phone?: string; + /** User Status */ + userStatus?: number; + }; + Tag: { + id?: number; + name?: string; + }; + Pet: { + id?: number; + category?: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Category\\"]; + name: string; + photoUrls: string[]; + tags?: external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Tag\\"][]; + /** pet status in the store */ + status?: \\"available\\" | \\"pending\\" | \\"sold\\"; + }; + ApiResponse: { + code?: number; + type?: string; + message?: string; + }; + }; + }; + operations: { + updatePet: { + responses: { + /** Invalid ID supplied */ + 400: unknown; + /** Pet not found */ + 404: unknown; + /** Validation exception */ + 405: unknown; + }; + /** Pet object that needs to be added to the store */ + requestBody: { + content: { + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + }; + }; + }; + addPet: { + responses: { + /** Invalid input */ + 405: unknown; + }; + /** Pet object that needs to be added to the store */ + requestBody: { + content: { + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + }; + }; + }; + /** Multiple status values can be provided with comma separated strings */ + findPetsByStatus: { + parameters: { + query: { + /** Status values that need to be considered for filter */ + status: (\\"available\\" | \\"pending\\" | \\"sold\\")[]; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"][]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"][]; + }; + }; + /** Invalid status value */ + 400: unknown; + }; + }; + /** Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. */ + findPetsByTags: { + parameters: { + query: { + /** Tags to filter by */ + tags: string[]; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"][]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"][]; + }; + }; + /** Invalid tag value */ + 400: unknown; + }; + }; + /** Returns a single pet */ + getPetById: { + parameters: { + path: { + /** ID of pet to return */ + petId: number; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Pet\\"]; + }; + }; + /** Invalid ID supplied */ + 400: unknown; + /** Pet not found */ + 404: unknown; + }; + }; + updatePetWithForm: { + parameters: { + path: { + /** ID of pet that needs to be updated */ + petId: number; + }; + }; + responses: { + /** Invalid input */ + 405: unknown; + }; + requestBody: { + content: { + \\"application/x-www-form-urlencoded\\": { + /** Updated name of the pet */ + name?: string; + /** Updated status of the pet */ + status?: string; + }; + }; + }; + }; + deletePet: { + parameters: { + header: { + api_key?: string; + }; + path: { + /** Pet id to delete */ + petId: number; + }; + }; + responses: { + /** Invalid ID supplied */ + 400: unknown; + /** Pet not found */ + 404: unknown; + }; + }; + uploadFile: { + parameters: { + path: { + /** ID of pet to update */ + petId: number; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"ApiResponse\\"]; + }; + }; + }; + requestBody: { + content: { + \\"multipart/form-data\\": { + /** Additional data to pass to server */ + additionalMetadata?: string; + /** file to upload */ + file?: string; + }; + }; + }; + }; + /** Returns a map of status codes to quantities */ + getInventory: { + responses: { + /** successful operation */ + 200: { + content: { + \\"application/json\\": { [key: string]: number }; + }; + }; + }; + }; + placeOrder: { + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Order\\"]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Order\\"]; + }; + }; + /** Invalid Order */ + 400: unknown; + }; + /** order placed for purchasing the pet */ + requestBody: { + content: { + \\"*/*\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Order\\"]; + }; + }; + }; + /** For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions */ + getOrderById: { + parameters: { + path: { + /** ID of pet that needs to be fetched */ + orderId: number; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Order\\"]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"Order\\"]; + }; + }; + /** Invalid ID supplied */ + 400: unknown; + /** Order not found */ + 404: unknown; + }; + }; + /** For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors */ + deleteOrder: { + parameters: { + path: { + /** ID of the order that needs to be deleted */ + orderId: number; + }; + }; + responses: { + /** Invalid ID supplied */ + 400: unknown; + /** Order not found */ + 404: unknown; + }; + }; + /** This can only be done by the logged in user. */ + createUser: { + responses: { + /** successful operation */ + default: unknown; + }; + /** Created user object */ + requestBody: { + content: { + \\"*/*\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"]; + }; + }; + }; + createUsersWithArrayInput: { + responses: { + /** successful operation */ + default: unknown; + }; + /** List of user object */ + requestBody: { + content: { + \\"*/*\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"][]; + }; + }; + }; + createUsersWithListInput: { + responses: { + /** successful operation */ + default: unknown; + }; + /** List of user object */ + requestBody: { + content: { + \\"*/*\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"][]; + }; + }; + }; + loginUser: { + parameters: { + query: { + /** The user name for login */ + username: string; + /** The password for login in clear text */ + password: string; + }; + }; + responses: { + /** successful operation */ + 200: { + headers: { + /** calls per hour allowed by the user */ + \\"X-Rate-Limit\\"?: number; + /** date in UTC when token expires */ + \\"X-Expires-After\\"?: string; + }; + content: { + \\"application/xml\\": string; + \\"application/json\\": string; + }; + }; + /** Invalid username/password supplied */ + 400: unknown; + }; + }; + logoutUser: { + responses: { + /** successful operation */ + default: unknown; + }; + }; + getUserByName: { + parameters: { + path: { + /** The name that needs to be fetched. Use user1 for testing. */ + username: string; + }; + }; + responses: { + /** successful operation */ + 200: { + content: { + \\"application/xml\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"]; + \\"application/json\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"]; + }; + }; + /** Invalid username supplied */ + 400: unknown; + /** User not found */ + 404: unknown; + }; + }; + /** This can only be done by the logged in user. */ + updateUser: { + parameters: { + path: { + /** name that need to be updated */ + username: string; + }; + }; + responses: { + /** Invalid user supplied */ + 400: unknown; + /** User not found */ + 404: unknown; + }; + /** Updated user object */ + requestBody: { + content: { + \\"*/*\\": external[\\"https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml\\"][\\"components\\"][\\"schemas\\"][\\"User\\"]; + }; + }; + }; + /** This can only be done by the logged in user. */ + deleteUser: { + parameters: { + path: { + /** The name that needs to be deleted */ + username: string; + }; + }; + responses: { + /** Invalid username supplied */ + 400: unknown; + /** User not found */ + 404: unknown; + }; + }; + }; + }; + \\"subschema/remote1.yml\\": { + paths: {}; + components: { + schemas: { + /** this is a duplicate of spec.yml#components/schemas/Remote1 */ + Remote1: string; + Remote2: external[\\"subschema/remote2.yml\\"][\\"components\\"][\\"schemas\\"][\\"Remote2\\"]; + Circular: components[\\"schemas\\"][\\"Circular\\"]; + }; + }; + operations: {}; + }; + \\"subschema/remote2.yml\\": { + paths: {}; + components: { + schemas: { + Remote2: string; + }; + }; + operations: {}; + }; +} +" +`; diff --git a/tests/remote-schema/remote-schema.test.ts b/tests/remote-schema/remote-schema.test.ts index 07ccf3a20..aa73f020e 100644 --- a/tests/remote-schema/remote-schema.test.ts +++ b/tests/remote-schema/remote-schema.test.ts @@ -4,507 +4,8 @@ import openapiTS from "../../src/index"; describe("remote $refs", () => { it("resolves remote $refs", async () => { const types = await openapiTS(path.join(__dirname, "spec", "spec.yml")); - expect(types).toEqual(`/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ -export interface paths {} - -export interface components { - schemas: { - /** this is a duplicate of subschema/remote1.yml */ - Circular: string; - Remote1: external["subschema/remote1.yml"]["components"]["schemas"]["Remote1"]; - Pet: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - }; -} - -export interface operations {} - -export interface external { - "https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml": { - paths: { - "/pet": { - put: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["updatePet"]; - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["addPet"]; - }; - "/pet/findByStatus": { - /** Multiple status values can be provided with comma separated strings */ - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["findPetsByStatus"]; - }; - "/pet/findByTags": { - /** Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. */ - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["findPetsByTags"]; - }; - "/pet/{petId}": { - /** Returns a single pet */ - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["getPetById"]; - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["updatePetWithForm"]; - delete: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["deletePet"]; - }; - "/pet/{petId}/uploadImage": { - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["uploadFile"]; - }; - "/store/inventory": { - /** Returns a map of status codes to quantities */ - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["getInventory"]; - }; - "/store/order": { - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["placeOrder"]; - }; - "/store/order/{orderId}": { - /** For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions */ - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["getOrderById"]; - /** For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors */ - delete: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["deleteOrder"]; - }; - "/user": { - /** This can only be done by the logged in user. */ - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["createUser"]; - }; - "/user/createWithArray": { - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["createUsersWithArrayInput"]; - }; - "/user/createWithList": { - post: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["createUsersWithListInput"]; - }; - "/user/login": { - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["loginUser"]; - }; - "/user/logout": { - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["logoutUser"]; - }; - "/user/{username}": { - get: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["getUserByName"]; - /** This can only be done by the logged in user. */ - put: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["updateUser"]; - /** This can only be done by the logged in user. */ - delete: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["operations"]["deleteUser"]; - }; - }; - components: { - schemas: { - Order: { - id?: number; - petId?: number; - quantity?: number; - shipDate?: string; - /** Order Status */ - status?: "placed" | "approved" | "delivered"; - complete?: boolean; - }; - Category: { - id?: number; - name?: string; - }; - User: { - id?: number; - username?: string; - firstName?: string; - lastName?: string; - email?: string; - password?: string; - phone?: string; - /** User Status */ - userStatus?: number; - }; - Tag: { - id?: number; - name?: string; - }; - Pet: { - id?: number; - category?: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Category"]; - name: string; - photoUrls: string[]; - tags?: external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Tag"][]; - /** pet status in the store */ - status?: "available" | "pending" | "sold"; - }; - ApiResponse: { - code?: number; - type?: string; - message?: string; - }; - }; - }; - operations: { - updatePet: { - responses: { - /** Invalid ID supplied */ - 400: unknown; - /** Pet not found */ - 404: unknown; - /** Validation exception */ - 405: unknown; - }; - /** Pet object that needs to be added to the store */ - requestBody: { - content: { - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - }; - }; - }; - addPet: { - responses: { - /** Invalid input */ - 405: unknown; - }; - /** Pet object that needs to be added to the store */ - requestBody: { - content: { - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - }; - }; - }; - /** Multiple status values can be provided with comma separated strings */ - findPetsByStatus: { - parameters: { - query: { - /** Status values that need to be considered for filter */ - status: ("available" | "pending" | "sold")[]; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"][]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"][]; - }; - }; - /** Invalid status value */ - 400: unknown; - }; - }; - /** Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. */ - findPetsByTags: { - parameters: { - query: { - /** Tags to filter by */ - tags: string[]; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"][]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"][]; - }; - }; - /** Invalid tag value */ - 400: unknown; - }; - }; - /** Returns a single pet */ - getPetById: { - parameters: { - path: { - /** ID of pet to return */ - petId: number; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Pet"]; - }; - }; - /** Invalid ID supplied */ - 400: unknown; - /** Pet not found */ - 404: unknown; - }; - }; - updatePetWithForm: { - parameters: { - path: { - /** ID of pet that needs to be updated */ - petId: number; - }; - }; - responses: { - /** Invalid input */ - 405: unknown; - }; - requestBody: { - content: { - "application/x-www-form-urlencoded": { - /** Updated name of the pet */ - name?: string; - /** Updated status of the pet */ - status?: string; - }; - }; - }; - }; - deletePet: { - parameters: { - header: { - api_key?: string; - }; - path: { - /** Pet id to delete */ - petId: number; - }; - }; - responses: { - /** Invalid ID supplied */ - 400: unknown; - /** Pet not found */ - 404: unknown; - }; - }; - uploadFile: { - parameters: { - path: { - /** ID of pet to update */ - petId: number; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["ApiResponse"]; - }; - }; - }; - requestBody: { - content: { - "multipart/form-data": { - /** Additional data to pass to server */ - additionalMetadata?: string; - /** file to upload */ - file?: string; - }; - }; - }; - }; - /** Returns a map of status codes to quantities */ - getInventory: { - responses: { - /** successful operation */ - 200: { - content: { - "application/json": { [key: string]: number }; - }; - }; - }; - }; - placeOrder: { - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Order"]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Order"]; - }; - }; - /** Invalid Order */ - 400: unknown; - }; - /** order placed for purchasing the pet */ - requestBody: { - content: { - "*/*": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Order"]; - }; - }; - }; - /** For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions */ - getOrderById: { - parameters: { - path: { - /** ID of pet that needs to be fetched */ - orderId: number; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Order"]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["Order"]; - }; - }; - /** Invalid ID supplied */ - 400: unknown; - /** Order not found */ - 404: unknown; - }; - }; - /** For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors */ - deleteOrder: { - parameters: { - path: { - /** ID of the order that needs to be deleted */ - orderId: number; - }; - }; - responses: { - /** Invalid ID supplied */ - 400: unknown; - /** Order not found */ - 404: unknown; - }; - }; - /** This can only be done by the logged in user. */ - createUser: { - responses: { - /** successful operation */ - default: unknown; - }; - /** Created user object */ - requestBody: { - content: { - "*/*": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"]; - }; - }; - }; - createUsersWithArrayInput: { - responses: { - /** successful operation */ - default: unknown; - }; - /** List of user object */ - requestBody: { - content: { - "*/*": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"][]; - }; - }; - }; - createUsersWithListInput: { - responses: { - /** successful operation */ - default: unknown; - }; - /** List of user object */ - requestBody: { - content: { - "*/*": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"][]; - }; - }; - }; - loginUser: { - parameters: { - query: { - /** The user name for login */ - username: string; - /** The password for login in clear text */ - password: string; - }; - }; - responses: { - /** successful operation */ - 200: { - headers: { - /** calls per hour allowed by the user */ - "X-Rate-Limit"?: number; - /** date in UTC when token expires */ - "X-Expires-After"?: string; - }; - content: { - "application/xml": string; - "application/json": string; - }; - }; - /** Invalid username/password supplied */ - 400: unknown; - }; - }; - logoutUser: { - responses: { - /** successful operation */ - default: unknown; - }; - }; - getUserByName: { - parameters: { - path: { - /** The name that needs to be fetched. Use user1 for testing. */ - username: string; - }; - }; - responses: { - /** successful operation */ - 200: { - content: { - "application/xml": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"]; - "application/json": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"]; - }; - }; - /** Invalid username supplied */ - 400: unknown; - /** User not found */ - 404: unknown; - }; - }; - /** This can only be done by the logged in user. */ - updateUser: { - parameters: { - path: { - /** name that need to be updated */ - username: string; - }; - }; - responses: { - /** Invalid user supplied */ - 400: unknown; - /** User not found */ - 404: unknown; - }; - /** Updated user object */ - requestBody: { - content: { - "*/*": external["https://raw.githubusercontent.com/drwpow/openapi-typescript/main/tests/v3/specs/petstore.yaml"]["components"]["schemas"]["User"]; - }; - }; - }; - /** This can only be done by the logged in user. */ - deleteUser: { - parameters: { - path: { - /** The name that needs to be deleted */ - username: string; - }; - }; - responses: { - /** Invalid username supplied */ - 400: unknown; - /** User not found */ - 404: unknown; - }; - }; - }; - }; - "subschema/remote1.yml": { - paths: {}; - components: { - schemas: { - /** this is a duplicate of spec.yml#components/schemas/Remote1 */ - Remote1: string; - Remote2: external["subschema/remote2.yml"]["components"]["schemas"]["Remote2"]; - Circular: components["schemas"]["Circular"]; - }; - }; - operations: {}; - }; - "subschema/remote2.yml": { - paths: {}; - components: { - schemas: { - Remote2: string; - }; - }; - operations: {}; - }; -} -`); + // We can use a snapshot here to ensure indentation and consistency in the output string. Otherwise, please do NOT use snapshots + expect(types).toMatchSnapshot(); }); }); diff --git a/tests/utils/index.test.ts b/tests/utils/index.test.ts deleted file mode 100644 index 3986d7e7a..000000000 --- a/tests/utils/index.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { swaggerVersion, comment } from "../../src/utils"; - -describe("swaggerVersion", () => { - it("v2", () => { - expect(swaggerVersion({ swagger: "2.0" } as any)).toBe(2); - }); - it("v3", () => { - expect(swaggerVersion({ openapi: "3.0.1" } as any)).toBe(3); - }); - it("errs", () => { - expect(() => swaggerVersion({} as any)).toThrow(); - }); -}); - -describe("comment", () => { - it("escapes markdown in comments", () => { - const text = `Example markdown -**/some/url/path**`; - - expect(comment(text)).toBe( - `/** - * Example markdown - * **\\/some/url/path** - */ -` - ); - }); -});