mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Simplified schema generation
This commit is contained in:
parent
9a1fa39ada
commit
4aa7f0ef67
@ -10,7 +10,7 @@ program
|
||||
.version(pkg.version)
|
||||
.option('--input [value]', 'Path to swagger specification', './spec.json')
|
||||
.option('--output [value]', 'Output directory', './generated')
|
||||
.option('--http-client [value]', 'HTTP client to generate [fetch, xhr]', 'fetch')
|
||||
.option('--client [value]', 'HTTP client to generate [fetch, xhr]', 'fetch')
|
||||
.parse(process.argv);
|
||||
|
||||
const SwaggerCodegen = require(path.resolve(__dirname, '../dist/index.js'));
|
||||
@ -19,6 +19,6 @@ if (SwaggerCodegen) {
|
||||
SwaggerCodegen.generate(
|
||||
program.input,
|
||||
program.output,
|
||||
program.httpClient
|
||||
program.client
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openapi-typescript-codegen",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.8",
|
||||
"description": "NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.",
|
||||
"author": "Ferdi Koomen",
|
||||
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
|
||||
|
||||
@ -18,7 +18,7 @@ export enum HttpClient {
|
||||
* service layer, etc.
|
||||
* @param input The relative location of the OpenAPI spec.
|
||||
* @param output The relative location of the output directory.
|
||||
* @param httpClient: The selected httpClient (fetch or XHR).
|
||||
* @param httpClient The selected httpClient (fetch or XHR).
|
||||
*/
|
||||
export function generate(input: string, output: string, httpClient: HttpClient = HttpClient.FETCH): void {
|
||||
const inputPath = path.resolve(process.cwd(), input);
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export type FieldSchema = {
|
||||
readonly type?: string;
|
||||
readonly isReadOnly?: boolean;
|
||||
readonly isRequired?: boolean;
|
||||
readonly isNullable?: boolean;
|
||||
readonly format?: string;
|
||||
readonly maximum?: number;
|
||||
readonly exclusiveMaximum?: boolean;
|
||||
readonly minimum?: number;
|
||||
readonly exclusiveMinimum?: boolean;
|
||||
readonly multipleOf?: number;
|
||||
readonly maxLength?: number;
|
||||
readonly minLength?: number;
|
||||
readonly pattern?: string;
|
||||
readonly maxItems?: number;
|
||||
readonly minItems?: number;
|
||||
readonly uniqueItems?: boolean;
|
||||
readonly maxProperties?: number;
|
||||
readonly minProperties?: number;
|
||||
}
|
||||
|
||||
export type ObjectSchema<T> = FieldSchema & {
|
||||
properties?: {
|
||||
readonly [K in keyof T]: Schema<T[K]>;
|
||||
}
|
||||
}
|
||||
|
||||
export type Schema<T> =
|
||||
T extends string ? FieldSchema :
|
||||
T extends number ? FieldSchema :
|
||||
T extends boolean ? FieldSchema :
|
||||
T extends File ? FieldSchema :
|
||||
T extends Blob ? FieldSchema :
|
||||
T extends Object ? ObjectSchema<T> :
|
||||
FieldSchema
|
||||
@ -1,17 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
import * as schemas from '../schemas';
|
||||
|
||||
/**
|
||||
* Get a schema object for a given model name.
|
||||
* @param model The model name to return the schema from.
|
||||
*/
|
||||
export function getSchema<K extends keyof typeof schemas, T>(model: K) {
|
||||
if (schemas.hasOwnProperty(model)) {
|
||||
return schemas[model];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
26
src/templates/index.hbs
Normal file
26
src/templates/index.hbs
Normal file
@ -0,0 +1,26 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export { ApiError } from './core/ApiError';
|
||||
export { isSuccess } from './core/isSuccess';
|
||||
export { OpenAPI } from './core/OpenAPI';
|
||||
{{#if models}}
|
||||
|
||||
{{#each models}}
|
||||
export { {{{this}}} } from './models/{{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if models}}
|
||||
|
||||
{{#each models}}
|
||||
export { ${{{this}}} } from './schemas/${{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if services}}
|
||||
|
||||
{{#each services}}
|
||||
export { {{{this}}} } from './services/{{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
@ -1,12 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export { ApiError } from './core/ApiError';
|
||||
export { getSchema } from './core/getSchema';
|
||||
export { isSuccess } from './core/isSuccess';
|
||||
export { OpenAPI } from './core/OpenAPI';
|
||||
export { FieldSchema, Schema, ObjectSchema } from './core/Schema';
|
||||
export * from './models/';
|
||||
export * from './services/';
|
||||
@ -14,5 +14,5 @@ import { {{{this}}} } from './{{{this}}}';
|
||||
{{else equals export 'enum'}}
|
||||
{{>exportEnum}}
|
||||
{{else}}
|
||||
{{>exportGeneric}}
|
||||
{{>exportType}}
|
||||
{{/equals}}
|
||||
@ -1,8 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
{{#each models}}
|
||||
export { {{{this}}} } from './{{{this}}}';
|
||||
{{/each}}
|
||||
@ -2,13 +2,11 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
{{#if extends}}
|
||||
|
||||
{{#each extends}}
|
||||
import { ${{{this}}} } from './${{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
import { Schema as __Schema } from '../core/Schema';
|
||||
import { {{{name}}} } from '../models/{{{name}}}';
|
||||
|
||||
export const ${{{name}}}: __Schema<{{{name}}}> = {{>schema}};
|
||||
export const ${{{name}}} = {{>schema}};
|
||||
@ -1,8 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export const $Dictionary = {
|
||||
type: 'Dictionary'
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
{{#each schemas}}
|
||||
export { ${{{this}}} as {{{this}}} } from './${{{this}}}';
|
||||
{{/each}}
|
||||
@ -1,8 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
{{#each services}}
|
||||
export { {{{this}}} } from './{{{this}}}';
|
||||
{{/each}}
|
||||
@ -1,6 +1,5 @@
|
||||
import * as fs from 'fs';
|
||||
import * as glob from 'glob';
|
||||
import { Language } from '../index';
|
||||
import { readHandlebarsTemplates } from './readHandlebarsTemplates';
|
||||
|
||||
jest.mock('fs');
|
||||
@ -19,15 +18,13 @@ describe('readHandlebarsTemplates', () => {
|
||||
const template = readHandlebarsTemplates();
|
||||
|
||||
expect(template).toBeDefined();
|
||||
expect(template.index).toBeDefined();
|
||||
expect(template.model).toBeDefined();
|
||||
expect(template.models).toBeDefined();
|
||||
expect(template.service).toBeDefined();
|
||||
expect(template.services).toBeDefined();
|
||||
expect(template.settings).toBeDefined();
|
||||
expect(template.index({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
expect(template.model({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
expect(template.models({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
expect(template.service({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
expect(template.services({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
expect(template.settings({ message: 'Hello World!' })).toEqual('Hello World!');
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,11 +5,9 @@ import { readHandlebarsTemplate } from './readHandlebarsTemplate';
|
||||
import { registerHandlebarHelpers } from './registerHandlebarHelpers';
|
||||
|
||||
export interface Templates {
|
||||
models: Handlebars.TemplateDelegate;
|
||||
index: Handlebars.TemplateDelegate;
|
||||
model: Handlebars.TemplateDelegate;
|
||||
schemas: Handlebars.TemplateDelegate;
|
||||
schema: Handlebars.TemplateDelegate;
|
||||
services: Handlebars.TemplateDelegate;
|
||||
service: Handlebars.TemplateDelegate;
|
||||
settings: Handlebars.TemplateDelegate;
|
||||
}
|
||||
@ -23,12 +21,10 @@ export function readHandlebarsTemplates(): Templates {
|
||||
registerHandlebarHelpers();
|
||||
|
||||
const templates: Templates = {
|
||||
models: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/models/index.hbs`)),
|
||||
model: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/models/model.hbs`)),
|
||||
schemas: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/schemas/index.hbs`)),
|
||||
schema: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/schemas/schema.hbs`)),
|
||||
services: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/services/index.hbs`)),
|
||||
service: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/services/service.hbs`)),
|
||||
index: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/index.hbs`)),
|
||||
model: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/model.hbs`)),
|
||||
schema: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/schema.hbs`)),
|
||||
service: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/service.hbs`)),
|
||||
settings: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/core/OpenAPI.hbs`)),
|
||||
};
|
||||
|
||||
|
||||
@ -27,12 +27,10 @@ describe('writeClient', () => {
|
||||
};
|
||||
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
models: () => 'dummy',
|
||||
schema: () => 'dummy',
|
||||
schemas: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
services: () => 'dummy',
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import * as rimraf from 'rimraf';
|
||||
import { Client } from '../client/interfaces/Client';
|
||||
import { HttpClient } from '../index';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { writeClientIndex } from './writeClientIndex';
|
||||
import { writeClientModels } from './writeClientModels';
|
||||
import { writeClientSchemas } from './writeClientSchemas';
|
||||
import { writeClientServices } from './writeClientServices';
|
||||
@ -53,8 +54,9 @@ export function writeClient(client: Client, httpClient: HttpClient, templates: T
|
||||
});
|
||||
|
||||
// Write the client files
|
||||
writeClientSettings(client, httpClient, templates, outputPathCore);
|
||||
writeClientModels(client.models, templates, outputPathModels);
|
||||
writeClientSchemas(client.models, templates, outputPathSchemas);
|
||||
writeClientServices(client.services, templates, outputPathServices);
|
||||
writeClientSettings(client, httpClient, templates, outputPathCore);
|
||||
writeClientIndex(client, templates, outputPath);
|
||||
}
|
||||
|
||||
36
src/utils/writeClientIndex.spec.ts
Normal file
36
src/utils/writeClientIndex.spec.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import * as fs from 'fs';
|
||||
import * as glob from 'glob';
|
||||
import { Client } from '../client/interfaces/Client';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { writeClientIndex } from './writeClientIndex';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('glob');
|
||||
|
||||
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
|
||||
const globSync = glob.sync as jest.MockedFunction<typeof glob.sync>;
|
||||
|
||||
describe('writeClientIndex', () => {
|
||||
it('should write to filesystem', () => {
|
||||
const client: Client = {
|
||||
server: 'http://localhost:8080',
|
||||
version: '1.0',
|
||||
models: [],
|
||||
services: [],
|
||||
};
|
||||
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
schema: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
globSync.mockReturnValue([]);
|
||||
|
||||
writeClientIndex(client, templates, '/');
|
||||
|
||||
expect(fsWriteFileSync).toBeCalledWith('/index.ts', 'dummy');
|
||||
});
|
||||
});
|
||||
30
src/utils/writeClientIndex.ts
Normal file
30
src/utils/writeClientIndex.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Client } from '../client/interfaces/Client';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { getModelNames } from './getModelNames';
|
||||
import { getServiceNames } from './getServiceNames';
|
||||
|
||||
/**
|
||||
* Generate the OpenAPI client index file using the Handlebar template and write it to disk.
|
||||
* The index file just contains all the exports you need to use the client as a standalone
|
||||
* library. But yuo can also import individual models and services directly.
|
||||
* @param client Client object, containing, models, schemas and services.
|
||||
* @param templates The loaded handlebar templates.
|
||||
* @param outputPath
|
||||
*/
|
||||
export function writeClientIndex(client: Client, templates: Templates, outputPath: string): void {
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
path.resolve(outputPath, 'index.ts'),
|
||||
templates.index({
|
||||
server: client.server,
|
||||
version: client.version,
|
||||
models: getModelNames(client.models),
|
||||
services: getServiceNames(client.services),
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Could not write index`);
|
||||
}
|
||||
}
|
||||
@ -30,12 +30,10 @@ describe('writeClientModels', () => {
|
||||
});
|
||||
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
models: () => 'dummy',
|
||||
schema: () => 'dummy',
|
||||
schemas: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
services: () => 'dummy',
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import { Model } from '../client/interfaces/Model';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { exportModel } from './exportModel';
|
||||
import { format } from './format';
|
||||
import { getModelNames } from './getModelNames';
|
||||
|
||||
/**
|
||||
* Generate Models using the Handlebar template and write to disk.
|
||||
@ -19,10 +18,4 @@ export function writeClientModels(models: Model[], templates: Templates, outputP
|
||||
const templateResult = templates.model(templateData);
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
});
|
||||
|
||||
const file = path.resolve(outputPath, 'index.ts');
|
||||
const templateResult = templates.models({
|
||||
models: getModelNames(models),
|
||||
});
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
}
|
||||
|
||||
@ -30,12 +30,10 @@ describe('writeClientModels', () => {
|
||||
});
|
||||
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
models: () => 'dummy',
|
||||
schema: () => 'dummy',
|
||||
schemas: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
services: () => 'dummy',
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import { Model } from '../client/interfaces/Model';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { exportModel } from './exportModel';
|
||||
import { format } from './format';
|
||||
import { getModelNames } from './getModelNames';
|
||||
|
||||
/**
|
||||
* Generate Schemas using the Handlebar template and write to disk.
|
||||
@ -19,10 +18,4 @@ export function writeClientSchemas(models: Model[], templates: Templates, output
|
||||
const templateResult = templates.schema(templateData);
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
});
|
||||
|
||||
const file = path.resolve(outputPath, 'index.ts');
|
||||
const templateResult = templates.schemas({
|
||||
schemas: getModelNames(models),
|
||||
});
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import { Language } from '../index';
|
||||
import { Service } from '../client/interfaces/Service';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { writeClientServices } from './writeClientServices';
|
||||
@ -18,12 +17,10 @@ describe('writeClientServices', () => {
|
||||
});
|
||||
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
models: () => 'dummy',
|
||||
schema: () => 'dummy',
|
||||
schemas: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
services: () => 'dummy',
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import { Service } from '../client/interfaces/Service';
|
||||
import { Templates } from './readHandlebarsTemplates';
|
||||
import { exportService } from './exportService';
|
||||
import { format } from './format';
|
||||
import { getServiceNames } from './getServiceNames';
|
||||
|
||||
/**
|
||||
* Generate Services using the Handlebar template and write to disk.
|
||||
@ -19,10 +18,4 @@ export function writeClientServices(services: Service[], templates: Templates, o
|
||||
const templateResult = templates.service(templateData);
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
});
|
||||
|
||||
const file = path.resolve(outputPath, 'index.ts');
|
||||
const templateResult = templates.services({
|
||||
services: getServiceNames(services),
|
||||
});
|
||||
fs.writeFileSync(file, format(templateResult));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user