Merge pull request #243 from ferdikoomen/feature/async-await

- Creating async await compatible version
This commit is contained in:
Ferdi Koomen 2020-05-30 15:26:26 +02:00 committed by GitHub
commit 760e1bd72c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 619 additions and 440 deletions

View File

@ -2,7 +2,14 @@
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-typescript'
]
[
'@babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
'@babel/preset-typescript',
],
};

View File

@ -32,5 +32,12 @@ if (OpenAPI) {
exportServices: program.exportServices,
exportModels: program.exportModels,
exportSchemas: program.exportSchemas,
});
})
.then(() => {
process.exit(0);
})
.catch(error => {
console.error(error);
process.exit(1);
});
}

View File

@ -1,6 +1,6 @@
{
"name": "openapi-typescript-codegen",
"version": "0.3.1",
"version": "0.4.0",
"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",

View File

@ -50,7 +50,7 @@ export default {
file: './dist/index.js',
format: 'cjs',
},
external: ['fs', 'os', ...external],
external: ['fs', 'os', 'util', ...external],
plugins: [
handlebarsPlugin(),
typescript({

View File

@ -1,8 +1,8 @@
import * as OpenAPI from '.';
describe('index', () => {
it('parses v2 without issues', () => {
OpenAPI.generate({
it('parses v2 without issues', async () => {
await OpenAPI.generate({
input: './test/mock/v2/spec.json',
output: './test/result/v2/',
useOptions: true,
@ -11,8 +11,8 @@ describe('index', () => {
});
});
it('parses v3 without issues', () => {
OpenAPI.generate({
it('parses v3 without issues', async () => {
await OpenAPI.generate({
input: './test/mock/v3/spec.json',
output: './test/result/v3/',
useOptions: true,

View File

@ -4,7 +4,7 @@ import { getOpenApiSpec } from './utils/getOpenApiSpec';
import { getOpenApiVersion, OpenApiVersion } from './utils/getOpenApiVersion';
import { isString } from './utils/isString';
import { postProcessClient } from './utils/postProcessClient';
import { readHandlebarsTemplates } from './utils/readHandlebarsTemplates';
import { registerHandlebarsTemplates } from './utils/registerHandlebarsTemplates';
import { writeClient } from './utils/writeClient';
export enum HttpClient {
@ -40,7 +40,7 @@ export interface Options {
* @param exportSchemas: Generate schemas.
* @param write Write the files to disk (true or false).
*/
export function generate({
export async function generate({
input,
output,
httpClient = HttpClient.FETCH,
@ -51,35 +51,30 @@ export function generate({
exportModels = true,
exportSchemas = false,
write = true,
}: Options): void {
try {
// Load the specification, read the OpenAPI version and load the
// handlebar templates for the given language
const openApi = isString(input) ? getOpenApiSpec(input) : input;
const openApiVersion = getOpenApiVersion(openApi);
const templates = readHandlebarsTemplates();
}: Options): Promise<void> {
// Load the specification, read the OpenAPI version and load the
// handlebar templates for the given language
const openApi = isString(input) ? await getOpenApiSpec(input) : input;
const openApiVersion = getOpenApiVersion(openApi);
const templates = registerHandlebarsTemplates();
switch (openApiVersion) {
case OpenApiVersion.V2: {
const client = parseV2(openApi);
const clientFinal = postProcessClient(client, useUnionTypes);
if (write) {
writeClient(clientFinal, templates, output, httpClient, useOptions, exportCore, exportServices, exportModels, exportSchemas);
}
break;
}
case OpenApiVersion.V3: {
const client = parseV3(openApi);
const clientFinal = postProcessClient(client, useUnionTypes);
if (write) {
writeClient(clientFinal, templates, output, httpClient, useOptions, exportCore, exportServices, exportModels, exportSchemas);
}
break;
switch (openApiVersion) {
case OpenApiVersion.V2: {
const client = parseV2(openApi);
const clientFinal = postProcessClient(client, useUnionTypes);
if (write) {
await writeClient(clientFinal, templates, output, httpClient, useOptions, exportCore, exportServices, exportModels, exportSchemas);
}
break;
}
case OpenApiVersion.V3: {
const client = parseV3(openApi);
const clientFinal = postProcessClient(client, useUnionTypes);
if (write) {
await writeClient(clientFinal, templates, output, httpClient, useOptions, exportCore, exportServices, exportModels, exportSchemas);
}
break;
}
} catch (e) {
console.error(e);
process.exit(1);
}
}

View File

@ -0,0 +1,33 @@
import { getRef } from './getRef';
describe('getRef', () => {
it('should produce correct result', () => {
expect(
getRef(
{
swagger: '2.0',
info: {
title: 'dummy',
version: '1.0',
},
host: 'localhost:8080',
basePath: '/api',
schemes: ['http', 'https'],
paths: {},
definitions: {
Example: {
description: 'This is an Example model ',
type: 'integer',
},
},
},
{
$ref: '#/definitions/Example',
}
)
).toEqual({
description: 'This is an Example model ',
type: 'integer',
});
});
});

View File

@ -19,7 +19,7 @@ export interface OpenApiComponents {
examples?: Dictionary<OpenApiExample>;
requestBodies?: Dictionary<OpenApiRequestBody>;
headers?: Dictionary<OpenApiHeader>;
securitySchemes: Dictionary<OpenApiSecurityScheme>;
securitySchemes?: Dictionary<OpenApiSecurityScheme>;
links?: Dictionary<OpenApiLink>;
callbacks?: Dictionary<OpenApiCallback>;
}

View File

@ -1,13 +0,0 @@
import { Enum } from '../../../client/interfaces/Enum';
export function getEnumValues(enumerators: Enum[]): string[] {
// Fetch values from the symbols, just to be sure we filter out
// any double values and finally we sort them to make them easier
// to read when we use them in our generated code.
return enumerators
.map(enumerator => enumerator.value)
.filter((enumerator, index, arr) => {
return arr.indexOf(enumerator) === index;
})
.sort();
}

View File

@ -0,0 +1,37 @@
import { getRef } from './getRef';
describe('getRef', () => {
it('should produce correct result', () => {
expect(
getRef(
{
openapi: '3.0',
info: {
title: 'dummy',
version: '1.0',
},
paths: {},
servers: [
{
url: 'https://localhost:8080/api',
},
],
components: {
schemas: {
Example: {
description: 'This is an Example model ',
type: 'integer',
},
},
},
},
{
$ref: '#/components/schemas/Example',
}
)
).toEqual({
description: 'This is an Example model ',
type: 'integer',
});
});
});

View File

@ -4,7 +4,7 @@ import { OpenApiReference } from '../interfaces/OpenApiReference';
export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
if (item.$ref) {
// Fetch the paths to the definitions, this converts:
// "#/definitions/Form" to ["definitions", "Form"]
// "#/components/schemas/Form" to ["components", "schemas", "Form"]
const paths = item.$ref
.replace(/^#/g, '')
.split('/')

View File

@ -0,0 +1,6 @@
export const readFile = jest.fn();
export const writeFile = jest.fn();
export const copyFile = jest.fn();
export const exists = jest.fn();
export const rmdir = jest.fn();
export const mkdir = jest.fn();

25
src/utils/fileSystem.ts Normal file
View File

@ -0,0 +1,25 @@
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import rimraf from 'rimraf';
import * as util from 'util';
// Wrapped file system calls
export const readFile = util.promisify(fs.readFile);
export const writeFile = util.promisify(fs.writeFile);
export const copyFile = util.promisify(fs.copyFile);
export const exists = util.promisify(fs.exists);
// Re-export from mkdirp to make mocking easier
export const mkdir = mkdirp;
// Promisified version of rimraf
export const rmdir = (path: string) =>
new Promise((resolve, reject) => {
rimraf(path, (error: Error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});

View File

@ -0,0 +1,9 @@
import { flatMap } from './flatMap';
describe('flatMap', () => {
it('should produce correct result', () => {
expect(flatMap([1, 2, 3], i => [i])).toEqual([1, 2, 3]);
expect(flatMap([1, 2, 3], i => [i + 1])).toEqual([2, 3, 4]);
expect(flatMap([1, 2, 3], () => [1])).toEqual([1, 1, 1]);
});
});

View File

@ -4,7 +4,7 @@
*/
export function flatMap<U, T>(array: T[], callback: (value: T, index: number, array: T[]) => U[]): U[] {
const result: U[] = [];
array.map(callback).forEach(arr => {
array.map<U[]>(callback).forEach(arr => {
result.push(...arr);
});
return result;

40
src/utils/format.spec.ts Normal file
View File

@ -0,0 +1,40 @@
import { format } from './format';
const input1 = `{ foo: true }`;
const output1 = `{ foo: true }`;
const input2 = `{ foo: true, bar: 123 }`;
const output2 = `{ foo: true, bar: 123 }`;
const input3 = `{
foo: true,
bar: 123
}`;
const output3 = `{
foo: true,
bar: 123
}`;
const input4 = `{
\t\t\t\tfoo: true,
\t\t\t\tbar: 123
}`;
const output4 = `{
foo: true,
bar: 123
}`;
describe('format', () => {
it('should produce correct result', () => {
expect(format(``)).toEqual('');
expect(format(`{}`)).toEqual('{}');
expect(format(input1)).toEqual(output1);
expect(format(input2)).toEqual(output2);
expect(format(input3)).toEqual(output3);
expect(format(input4)).toEqual(output4);
});
});

View File

@ -1,24 +1,27 @@
import * as fs from 'fs';
import { exists, readFile } from './fileSystem';
import { getOpenApiSpec } from './getOpenApiSpec';
jest.mock('fs');
jest.mock('./fileSystem');
const fsExistsSync = fs.existsSync as jest.MockedFunction<typeof fs.existsSync>;
const fsReadFileSync = fs.readFileSync as jest.MockedFunction<typeof fs.readFileSync>;
const existsMocked = exists as jest.MockedFunction<typeof exists>;
const readFileMocked = readFile as jest.MockedFunction<typeof readFile>;
function mockPromise<T>(value: T): Promise<T> {
return new Promise<T>(resolve => resolve(value));
}
describe('getOpenApiSpec', () => {
it('should read the json file', () => {
fsExistsSync.mockReturnValue(true);
fsReadFileSync.mockReturnValue('{"message": "Hello World!"}');
const spec = getOpenApiSpec('spec.json');
it('should read the json file', async () => {
existsMocked.mockReturnValue(mockPromise(true));
readFileMocked.mockReturnValue(mockPromise('{"message": "Hello World!"}'));
const spec = await getOpenApiSpec('spec.json');
expect(spec.message).toEqual('Hello World!');
});
it('should read the yaml file', () => {
fsExistsSync.mockReturnValue(true);
fsReadFileSync.mockReturnValue('message: "Hello World!"');
const spec = getOpenApiSpec('spec.yaml');
it('should read the yaml file', async () => {
existsMocked.mockReturnValue(mockPromise(true));
readFileMocked.mockReturnValue(mockPromise('message: "Hello World!"'));
const spec = await getOpenApiSpec('spec.yaml');
expect(spec.message).toEqual('Hello World!');
});
});

View File

@ -1,15 +1,18 @@
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import * as path from 'path';
import { exists, readFile } from './fileSystem';
/**
* Check if given file exists and try to read the content as string.
* @param filePath
*/
function read(filePath: string): string {
if (fs.existsSync(filePath)) {
async function read(filePath: string): Promise<string> {
const fileExists = await exists(filePath);
if (fileExists) {
try {
return fs.readFileSync(filePath, 'utf8').toString();
const content = await readFile(filePath, 'utf8');
return content.toString();
} catch (e) {
throw new Error(`Could not read OpenApi spec: "${filePath}"`);
}
@ -23,10 +26,10 @@ function read(filePath: string): string {
* on parsing the file as JSON.
* @param input
*/
export function getOpenApiSpec(input: string): any {
export async function getOpenApiSpec(input: string): Promise<any> {
const file = path.resolve(process.cwd(), input);
const extname = path.extname(file).toLowerCase();
const content = read(file);
const content = await read(file);
switch (extname) {
case '.yml':
case '.yaml':

View File

@ -0,0 +1,13 @@
import { isString } from './isString';
describe('isString', () => {
it('should produce correct result', () => {
expect(isString('foo')).toBeTruthy();
expect(isString('123')).toBeTruthy();
expect(isString('-1')).toBeTruthy();
expect(isString('')).toBeTruthy();
expect(isString(null)).toBeFalsy();
expect(isString(undefined)).toBeFalsy();
expect(isString({})).toBeFalsy();
});
});

View File

@ -41,7 +41,7 @@ export interface Templates {
* Read all the Handlebar templates that we need and return on wrapper object
* so we can easily access the templates in out generator / write functions.
*/
export function readHandlebarsTemplates(): Templates {
export function registerHandlebarsTemplates(): Templates {
registerHandlebarHelpers();
const templates: Templates = {

View File

@ -1,22 +1,13 @@
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import * as rimraf from 'rimraf';
import { Client } from '../client/interfaces/Client';
import { HttpClient } from '../index';
import { Templates } from './readHandlebarsTemplates';
import { mkdir, rmdir, writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClient } from './writeClient';
jest.mock('rimraf');
jest.mock('mkdirp');
jest.mock('fs');
const rimrafSync = mkdirp.sync as jest.MockedFunction<typeof mkdirp.sync>;
const mkdirpSync = rimraf.sync as jest.MockedFunction<typeof rimraf.sync>;
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
jest.mock('./fileSystem');
describe('writeClient', () => {
it('should write to filesystem', () => {
it('should write to filesystem', async () => {
const client: Client = {
server: 'http://localhost:8080',
version: 'v1',
@ -32,10 +23,10 @@ describe('writeClient', () => {
settings: () => 'dummy',
};
writeClient(client, templates, '/', HttpClient.FETCH, false, true, true, true, true);
await writeClient(client, templates, '/', HttpClient.FETCH, false, true, true, true, true);
expect(rimrafSync).toBeCalled();
expect(mkdirpSync).toBeCalled();
expect(fsWriteFileSync).toBeCalled();
expect(rmdir).toBeCalled();
expect(mkdir).toBeCalled();
expect(writeFile).toBeCalled();
});
});

View File

@ -1,19 +1,17 @@
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import * as path from 'path';
import * as rimraf from 'rimraf';
import { Client } from '../client/interfaces/Client';
import { HttpClient } from '../index';
import { Templates } from './readHandlebarsTemplates';
import { copyFile, mkdir, rmdir } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientIndex } from './writeClientIndex';
import { writeClientModels } from './writeClientModels';
import { writeClientSchemas } from './writeClientSchemas';
import { writeClientServices } from './writeClientServices';
import { writeClientSettings } from './writeClientSettings';
function copySupportFile(filePath: string, outputPath: string): void {
fs.copyFileSync(path.resolve(__dirname, `../src/templates/${filePath}`), path.resolve(outputPath, filePath));
async function copySupportFile(filePath: string, outputPath: string): Promise<void> {
await copyFile(path.resolve(__dirname, `../src/templates/${filePath}`), path.resolve(outputPath, filePath));
}
/**
@ -28,7 +26,7 @@ function copySupportFile(filePath: string, outputPath: string): void {
* @param exportModels: Generate models.
* @param exportSchemas: Generate schemas.
*/
export function writeClient(
export async function writeClient(
client: Client,
templates: Templates,
output: string,
@ -38,7 +36,7 @@ export function writeClient(
exportServices: boolean,
exportModels: boolean,
exportSchemas: boolean
): void {
): Promise<void> {
const outputPath = path.resolve(process.cwd(), output);
const outputPathCore = path.resolve(outputPath, 'core');
const outputPathModels = path.resolve(outputPath, 'models');
@ -46,38 +44,38 @@ export function writeClient(
const outputPathServices = path.resolve(outputPath, 'services');
// Clean output directory
rimraf.sync(outputPath);
mkdirp.sync(outputPath);
await rmdir(outputPath);
await mkdir(outputPath);
if (exportCore) {
mkdirp.sync(outputPathCore);
copySupportFile('core/ApiError.ts', outputPath);
copySupportFile('core/getFormData.ts', outputPath);
copySupportFile('core/getQueryString.ts', outputPath);
copySupportFile('core/isSuccess.ts', outputPath);
copySupportFile('core/request.ts', outputPath);
copySupportFile('core/RequestOptions.ts', outputPath);
copySupportFile('core/requestUsingFetch.ts', outputPath);
copySupportFile('core/requestUsingXHR.ts', outputPath);
copySupportFile('core/Result.ts', outputPath);
await mkdir(outputPathCore);
await copySupportFile('core/ApiError.ts', outputPath);
await copySupportFile('core/getFormData.ts', outputPath);
await copySupportFile('core/getQueryString.ts', outputPath);
await copySupportFile('core/isSuccess.ts', outputPath);
await copySupportFile('core/request.ts', outputPath);
await copySupportFile('core/RequestOptions.ts', outputPath);
await copySupportFile('core/requestUsingFetch.ts', outputPath);
await copySupportFile('core/requestUsingXHR.ts', outputPath);
await copySupportFile('core/Result.ts', outputPath);
}
if (exportServices) {
mkdirp.sync(outputPathServices);
writeClientSettings(client, templates, outputPathCore, httpClient);
writeClientServices(client.services, templates, outputPathServices, useOptions);
await mkdir(outputPathServices);
await writeClientSettings(client, templates, outputPathCore, httpClient);
await writeClientServices(client.services, templates, outputPathServices, useOptions);
}
if (exportSchemas) {
mkdirp.sync(outputPathSchemas);
writeClientSchemas(client.models, templates, outputPathSchemas);
await mkdir(outputPathSchemas);
await writeClientSchemas(client.models, templates, outputPathSchemas);
}
if (exportModels) {
mkdirp.sync(outputPathModels);
copySupportFile('models/Dictionary.ts', outputPath);
writeClientModels(client.models, templates, outputPathModels);
await mkdir(outputPathModels);
await copySupportFile('models/Dictionary.ts', outputPath);
await writeClientModels(client.models, templates, outputPathModels);
}
writeClientIndex(client, templates, outputPath, exportCore, exportModels, exportServices, exportSchemas);
await writeClientIndex(client, templates, outputPath, exportCore, exportModels, exportServices, exportSchemas);
}

View File

@ -1,15 +1,12 @@
import * as fs from 'fs';
import { Client } from '../client/interfaces/Client';
import { Templates } from './readHandlebarsTemplates';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientIndex } from './writeClientIndex';
jest.mock('fs');
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
jest.mock('./fileSystem');
describe('writeClientIndex', () => {
it('should write to filesystem', () => {
it('should write to filesystem', async () => {
const client: Client = {
server: 'http://localhost:8080',
version: '1.0',
@ -25,8 +22,8 @@ describe('writeClientIndex', () => {
settings: () => 'dummy',
};
writeClientIndex(client, templates, '/', true, true, true, true);
await writeClientIndex(client, templates, '/', true, true, true, true);
expect(fsWriteFileSync).toBeCalledWith('/index.ts', 'dummy');
expect(writeFile).toBeCalledWith('/index.ts', 'dummy');
});
});

View File

@ -1,10 +1,10 @@
import * as fs from 'fs';
import * as path from 'path';
import { Client } from '../client/interfaces/Client';
import { writeFile } from './fileSystem';
import { getModelNames } from './getModelNames';
import { getServiceNames } from './getServiceNames';
import { Templates } from './readHandlebarsTemplates';
import { Templates } from './registerHandlebarsTemplates';
/**
* Generate the OpenAPI client index file using the Handlebar template and write it to disk.
@ -18,8 +18,16 @@ import { Templates } from './readHandlebarsTemplates';
* @param exportModels: Generate models.
* @param exportSchemas: Generate schemas.
*/
export function writeClientIndex(client: Client, templates: Templates, outputPath: string, exportCore: boolean, exportServices: boolean, exportModels: boolean, exportSchemas: boolean): void {
fs.writeFileSync(
export async function writeClientIndex(
client: Client,
templates: Templates,
outputPath: string,
exportCore: boolean,
exportServices: boolean,
exportModels: boolean,
exportSchemas: boolean
): Promise<void> {
await writeFile(
path.resolve(outputPath, 'index.ts'),
templates.index({
exportCore,

View File

@ -1,15 +1,12 @@
import * as fs from 'fs';
import { Model } from '../client/interfaces/Model';
import { Templates } from './readHandlebarsTemplates';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientModels } from './writeClientModels';
jest.mock('fs');
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
jest.mock('./fileSystem');
describe('writeClientModels', () => {
it('should write to filesystem', () => {
it('should write to filesystem', async () => {
const models: Model[] = [
{
export: 'interface',
@ -39,8 +36,8 @@ describe('writeClientModels', () => {
settings: () => 'dummy',
};
writeClientModels(models, templates, '/');
await writeClientModels(models, templates, '/');
expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy');
expect(writeFile).toBeCalledWith('/Item.ts', 'dummy');
});
});

View File

@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';
import { Model } from '../client/interfaces/Model';
import { writeFile } from './fileSystem';
import { format } from './format';
import { Templates } from './readHandlebarsTemplates';
import { Templates } from './registerHandlebarsTemplates';
/**
* Generate Models using the Handlebar template and write to disk.
@ -11,10 +11,10 @@ import { Templates } from './readHandlebarsTemplates';
* @param templates The loaded handlebar templates.
* @param outputPath Directory to write the generated files to.
*/
export function writeClientModels(models: Model[], templates: Templates, outputPath: string): void {
models.forEach(model => {
export async function writeClientModels(models: Model[], templates: Templates, outputPath: string): Promise<void> {
for (const model of models) {
const file = path.resolve(outputPath, `${model.name}.ts`);
const templateResult = templates.model(model);
fs.writeFileSync(file, format(templateResult));
});
await writeFile(file, format(templateResult));
}
}

View File

@ -1,15 +1,12 @@
import * as fs from 'fs';
import { Model } from '../client/interfaces/Model';
import { Templates } from './readHandlebarsTemplates';
import { writeClientModels } from './writeClientModels';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientSchemas } from './writeClientSchemas';
jest.mock('fs');
jest.mock('./fileSystem');
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
describe('writeClientModels', () => {
it('should write to filesystem', () => {
describe('writeClientSchemas', () => {
it('should write to filesystem', async () => {
const models: Model[] = [
{
export: 'interface',
@ -39,8 +36,8 @@ describe('writeClientModels', () => {
settings: () => 'dummy',
};
writeClientModels(models, templates, '/');
await writeClientSchemas(models, templates, '/');
expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy');
expect(writeFile).toBeCalledWith('/$Item.ts', 'dummy');
});
});

View File

@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';
import { Model } from '../client/interfaces/Model';
import { writeFile } from './fileSystem';
import { format } from './format';
import { Templates } from './readHandlebarsTemplates';
import { Templates } from './registerHandlebarsTemplates';
/**
* Generate Schemas using the Handlebar template and write to disk.
@ -11,10 +11,10 @@ import { Templates } from './readHandlebarsTemplates';
* @param templates The loaded handlebar templates.
* @param outputPath Directory to write the generated files to.
*/
export function writeClientSchemas(models: Model[], templates: Templates, outputPath: string): void {
models.forEach(model => {
export async function writeClientSchemas(models: Model[], templates: Templates, outputPath: string): Promise<void> {
for (const model of models) {
const file = path.resolve(outputPath, `$${model.name}.ts`);
const templateResult = templates.schema(model);
fs.writeFileSync(file, format(templateResult));
});
await writeFile(file, format(templateResult));
}
}

View File

@ -1,15 +1,12 @@
import * as fs from 'fs';
import { Service } from '../client/interfaces/Service';
import { Templates } from './readHandlebarsTemplates';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientServices } from './writeClientServices';
jest.mock('fs');
const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction<typeof fs.writeFileSync>;
jest.mock('./fileSystem');
describe('writeClientServices', () => {
it('should write to filesystem', () => {
it('should write to filesystem', async () => {
const services: Service[] = [
{
name: 'Item',
@ -26,8 +23,8 @@ describe('writeClientServices', () => {
settings: () => 'dummy',
};
writeClientServices(services, templates, '/', false);
await writeClientServices(services, templates, '/', false);
expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy');
expect(writeFile).toBeCalledWith('/Item.ts', 'dummy');
});
});

View File

@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';
import { Service } from '../client/interfaces/Service';
import { writeFile } from './fileSystem';
import { format } from './format';
import { Templates } from './readHandlebarsTemplates';
import { Templates } from './registerHandlebarsTemplates';
/**
* Generate Services using the Handlebar template and write to disk.
@ -12,13 +12,13 @@ import { Templates } from './readHandlebarsTemplates';
* @param outputPath Directory to write the generated files to.
* @param useOptions Use options or arguments functions.
*/
export function writeClientServices(services: Service[], templates: Templates, outputPath: string, useOptions: boolean): void {
services.forEach(service => {
export async function writeClientServices(services: Service[], templates: Templates, outputPath: string, useOptions: boolean): Promise<void> {
for (const service of services) {
const file = path.resolve(outputPath, `${service.name}.ts`);
const templateResult = templates.service({
...service,
useOptions,
});
fs.writeFileSync(file, format(templateResult));
});
await writeFile(file, format(templateResult));
}
}

View File

@ -0,0 +1,30 @@
import { Client } from '../client/interfaces/Client';
import { HttpClient } from '../index';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
import { writeClientSettings } from './writeClientSettings';
jest.mock('./fileSystem');
describe('writeClientSettings', () => {
it('should write to filesystem', async () => {
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',
};
await writeClientSettings(client, templates, '/', HttpClient.FETCH);
expect(writeFile).toBeCalledWith('/OpenAPI.ts', 'dummy');
});
});

View File

@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';
import { Client } from '../client/interfaces/Client';
import { HttpClient } from '../index';
import { Templates } from './readHandlebarsTemplates';
import { writeFile } from './fileSystem';
import { Templates } from './registerHandlebarsTemplates';
/**
* Generate OpenAPI configuration file "OpenAPI.ts"
@ -12,8 +12,8 @@ import { Templates } from './readHandlebarsTemplates';
* @param outputPath Directory to write the generated files to.
* @param httpClient The selected httpClient (fetch or XHR).
*/
export function writeClientSettings(client: Client, templates: Templates, outputPath: string, httpClient: HttpClient): void {
fs.writeFileSync(
export async function writeClientSettings(client: Client, templates: Templates, outputPath: string, httpClient: HttpClient): Promise<void> {
await writeFile(
path.resolve(outputPath, 'OpenAPI.ts'),
templates.settings({
httpClient,

File diff suppressed because it is too large Load Diff

View File

@ -20,29 +20,33 @@ function compile(dir) {
compiler.emit();
}
console.time('generate');
async function run() {
console.time('generate');
OpenAPI.generate({
input: './test/mock/v2/spec.json',
output: './test/result/v2/',
httpClient: OpenAPI.HttpClient.FETCH,
useOptions: true,
useUnionTypes: true,
exportSchemas: true,
exportServices: true
});
await OpenAPI.generate({
input: './test/mock/v2/spec.json',
output: './test/result/v2/',
httpClient: OpenAPI.HttpClient.FETCH,
useOptions: true,
useUnionTypes: true,
exportSchemas: true,
exportServices: true,
});
OpenAPI.generate({
input: './test/mock/v3/spec.json',
output: './test/result/v3/',
httpClient: OpenAPI.HttpClient.FETCH,
useOptions: true,
useUnionTypes: true,
exportSchemas: true,
exportServices: true
});
await OpenAPI.generate({
input: './test/mock/v3/spec.json',
output: './test/result/v3/',
httpClient: OpenAPI.HttpClient.FETCH,
useOptions: true,
useUnionTypes: true,
exportSchemas: true,
exportServices: true,
});
console.timeEnd('generate');
console.timeEnd('generate');
compile('./test/result/v2/');
compile('./test/result/v3/');
compile('./test/result/v2/');
compile('./test/result/v3/');
}
run();

View File

@ -4,11 +4,9 @@ const OpenAPI = require('../dist');
const glob = require('glob');
const fs = require('fs');
describe('generation', () => {
describe('v2', () => {
OpenAPI.generate({
describe('v2', () => {
it('should generate', async () => {
await OpenAPI.generate({
input: './test/mock/v2/spec.json',
output: './test/result/v2/',
httpClient: OpenAPI.HttpClient.FETCH,
@ -20,18 +18,18 @@ describe('generation', () => {
exportServices: true,
});
test.each(glob
.sync('./test/result/v2/**/*.ts')
.map(file => [file])
)('file(%s)', file => {
const files = glob.sync('./test/result/v2/**/*.ts');
files.forEach(file => {
const content = fs.readFileSync(file, 'utf8').toString();
expect(content).toMatchSnapshot(file);
});
});
});
describe('v3', () => {
OpenAPI.generate({
describe('v3', () => {
it('should generate', async () => {
await OpenAPI.generate({
input: './test/mock/v3/spec.json',
output: './test/result/v3/',
httpClient: OpenAPI.HttpClient.FETCH,
@ -43,14 +41,11 @@ describe('generation', () => {
exportServices: true,
});
test.each(glob
.sync('./test/result/v3/**/*.ts')
.map(file => [file])
)('file(%s)', file => {
const files = glob.sync('./test/result/v3/**/*.ts');
files.forEach(file => {
const content = fs.readFileSync(file, 'utf8').toString();
expect(content).toMatchSnapshot(file);
});
});
});

View File

@ -3,10 +3,10 @@
"compilerOptions": {
"outDir": "./dist",
"target": "es6",
"module": "es6",
"target": "ES2017",
"module": "ES6",
"moduleResolution": "node",
"lib": ["es6", "dom"],
"lib": ["ES2017"],
"types": ["node", "jest"],
"typeRoots": ["node_modules/@types"],
"declaration": false,
@ -19,9 +19,9 @@
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"importHelpers": false,
"removeComments": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true
},
"files": [