Skip to content

Support raw schemas in addition to Swagger/OpenAPI documents #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Options
--help display this
--output, -o specify output file
--prettier-config (optional) specify path to Prettier config file
--raw-schema (optional) Read from raw schema instead of document
--version (optional) Schema version (must be present for raw schemas)
`,
{
flags: {
Expand All @@ -25,6 +27,12 @@ Options
prettierConfig: {
type: "string",
},
rawSchema: {
type: "boolean"
},
version: {
type: "number"
}
},
}
);
Expand All @@ -48,6 +56,8 @@ const timeStart = process.hrtime();

const result = swaggerToTS(spec, {
prettierConfig: cli.flags.prettierConfig,
rawSchema: cli.flags.rawSchema,
version: cli.flags.version
});

// Write to file if specifying output
Expand Down
18 changes: 13 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import path from "path";
import prettier from "prettier";
import { swaggerVersion } from "./utils";
import { OpenAPI2, OpenAPI3, SwaggerToTSOptions } from "./types";
import {
OpenAPI2,
OpenAPI2Schemas,
OpenAPI3,
OpenAPI3Schemas,
SwaggerToTSOptions,
} from "./types";
import v2 from "./v2";
import v3 from "./v3";

Expand All @@ -14,19 +20,21 @@ export const WARNING_MESSAGE = `/**
`;

export default function swaggerToTS(
schema: OpenAPI2 | OpenAPI3,
schema: OpenAPI2 | OpenAPI2Schemas | OpenAPI3 | OpenAPI3Schemas,
options?: SwaggerToTSOptions
): string {
// generate types for V2 and V3
const version = swaggerVersion(schema);
const version =
(options && options.version) ||
swaggerVersion(schema as OpenAPI2 | OpenAPI3);
let output = `${WARNING_MESSAGE}`;
switch (version) {
case 2: {
output = output.concat(v2(schema as OpenAPI2, options));
output = output.concat(v2(schema as OpenAPI2 | OpenAPI2Schemas, options));
break;
}
case 3: {
output = output.concat(v3(schema as OpenAPI3, options));
output = output.concat(v3(schema as OpenAPI3 | OpenAPI3Schemas, options));
break;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/types/OpenAPI2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
* the parts that swagger-to-ts needs to know about.
*/

export interface OpenAPI2Schemas {
[key: string]: OpenAPI2SchemaObject;
}

export interface OpenAPI2 {
definitions?: { [key: string]: OpenAPI2SchemaObject };
definitions?: OpenAPI2Schemas;
swagger: string;
[key: string]: any; // handle other properties beyond swagger-to-ts’ concern
}
Expand Down
14 changes: 10 additions & 4 deletions src/types/OpenAPI3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@
* the parts that swagger-to-ts needs to know about.
*/

export interface OpenAPI3Schemas {
[key: string]: OpenAPI3SchemaObject | OpenAPI3Reference;
}

export interface OpenAPI3Components {
schemas: OpenAPI3Schemas;
responses?: OpenAPI3Schemas;
}

export interface OpenAPI3 {
openapi: string;
components: {
schemas: { [key: string]: OpenAPI3SchemaObject | OpenAPI3Reference };
responses?: { [key: string]: OpenAPI3SchemaObject | OpenAPI3Reference };
};
components: OpenAPI3Components;
[key: string]: any; // handle other properties beyond swagger-to-ts’ concern
}

Expand Down
4 changes: 4 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ export interface SwaggerToTSOptions {
schemaObject: OpenAPI2SchemaObject | OpenAPI3SchemaObject,
property: Property
) => Property;
/** (optional) Parsing input document as raw schema rather than OpenAPI document */
rawSchema?: boolean;
/** (optional) OpenAPI version. Must be present if parsing raw schema */
version?: number;
}
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ export function swaggerVersion(definition: OpenAPI2 | OpenAPI3): 2 | 3 {
}

/** Convert $ref to TS ref */
export function transformRef(ref: string): string {
const parts = ref.replace(/^#\//, "").split("/");
export function transformRef(ref: string, root = ""): string {
const parts = ref.replace(/^#\//, root).split("/");
return `${parts[0]}["${parts.slice(1).join('"]["')}"]`;
}

Expand Down
34 changes: 25 additions & 9 deletions src/v2.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import propertyMapper from "./property-mapper";
import { OpenAPI2, OpenAPI2SchemaObject, SwaggerToTSOptions } from "./types";
import {
OpenAPI2,
OpenAPI2SchemaObject,
OpenAPI2Schemas,
SwaggerToTSOptions,
} from "./types";
import {
comment,
nodeType,
Expand Down Expand Up @@ -29,25 +34,36 @@ export const PRIMITIVES: { [key: string]: "boolean" | "string" | "number" } = {
};

export default function generateTypesV2(
schema: OpenAPI2,
input: OpenAPI2 | OpenAPI2Schemas,
options?: SwaggerToTSOptions
): string {
if (!schema.definitions) {
throw new Error(
`⛔️ 'definitions' missing from schema https://swagger.io/specification/v2/#definitions-object`
);
const rawSchema = options && options.rawSchema;

let definitions: OpenAPI2Schemas;

if (rawSchema) {
definitions = input as OpenAPI2Schemas;
} else {
const document = input as OpenAPI2;

if (!document.definitions) {
throw new Error(
`⛔️ 'definitions' missing from schema https://swagger.io/specification/v2/#definitions-object`
);
}
definitions = document.definitions;
}

// propertyMapper
const propertyMapped = options
? propertyMapper(schema.definitions, options.propertyMapper)
: schema.definitions;
? propertyMapper(definitions, options.propertyMapper)
: definitions;

// type conversions
function transform(node: OpenAPI2SchemaObject): string {
switch (nodeType(node)) {
case "ref": {
return transformRef(node.$ref);
return transformRef(node.$ref, rawSchema ? "definitions/" : "");
}
case "string":
case "number":
Expand Down
48 changes: 34 additions & 14 deletions src/v3.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import propertyMapper from "./property-mapper";
import { OpenAPI3, OpenAPI3SchemaObject, SwaggerToTSOptions } from "./types";
import {
OpenAPI3,
OpenAPI3Components,
OpenAPI3SchemaObject,
OpenAPI3Schemas,
SwaggerToTSOptions,
} from "./types";
import {
comment,
nodeType,
Expand All @@ -24,25 +30,34 @@ export const PRIMITIVES: { [key: string]: "boolean" | "string" | "number" } = {
};

export default function generateTypesV3(
schema: OpenAPI3,
input: OpenAPI3 | OpenAPI3Schemas,
options?: SwaggerToTSOptions
): string {
if (!schema.components || !schema.components.schemas) {
throw new Error(
`⛔️ 'components' missing from schema https://swagger.io/specification`
);
const { rawSchema = false } = options || {};
let components: OpenAPI3Components;

if (rawSchema) {
components = { schemas: input };
} else {
components = (input as OpenAPI3).components;

if (!components || !components.schemas) {
throw new Error(
`⛔️ 'components' missing from schema https://swagger.io/specification`
);
}
}

// propertyMapper
const propertyMapped = options
? propertyMapper(schema.components.schemas, options.propertyMapper)
: schema.components.schemas;
? propertyMapper(components.schemas, options.propertyMapper)
: components.schemas;

// type converter
function transform(node: OpenAPI3SchemaObject): string {
switch (nodeType(node)) {
case "ref": {
return transformRef(node.$ref);
return transformRef(node.$ref, rawSchema ? "schemas/" : "");
}
case "string":
case "number":
Expand Down Expand Up @@ -139,17 +154,22 @@ export default function generateTypesV3(
return output;
}

if (rawSchema) {
const schemas = createKeys(propertyMapped, Object.keys(propertyMapped));

return `export interface schemas {
${schemas}
}`;
}

const schemas = `schemas: {
${createKeys(propertyMapped, Object.keys(propertyMapped))}
}`;

const responses = !schema.components.responses
const responses = !components.responses
? ``
: `responses: {
${createKeys(
schema.components.responses,
Object.keys(schema.components.responses)
)}
${createKeys(components.responses, Object.keys(components.responses))}
}`;

// note: make sure that base-level schemas are required
Expand Down