mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Added flag to enable the optional generation of schemas and services
This commit is contained in:
parent
ca2174c64b
commit
06214f55ac
@ -2,6 +2,6 @@
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 500,
|
||||
"printWidth": 200,
|
||||
"tabWidth": 4
|
||||
}
|
||||
|
||||
136
README.md
136
README.md
@ -56,7 +56,7 @@ const OpenAPI = require('openapi-typescript-codegen');
|
||||
|
||||
OpenAPI.generate({
|
||||
input: './api/openapi.json',
|
||||
output: './dist'
|
||||
output: './generated'
|
||||
});
|
||||
```
|
||||
|
||||
@ -64,19 +64,20 @@ Or by providing the JSON directly:
|
||||
|
||||
```javascript
|
||||
const OpenAPI = require('openapi-typescript-codegen');
|
||||
|
||||
const spec = require('./api/openapi.json');
|
||||
|
||||
OpenAPI.generate({
|
||||
input: spec,
|
||||
output: './dist'
|
||||
output: './generated'
|
||||
});
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Argument-style vs. Object-style
|
||||
There's no [named parameter](https://en.wikipedia.org/wiki/Named_parameter) in JS/TS, because of that,
|
||||
we offer an option `--useOptions` to generate code in two different styles.
|
||||
There's no [named parameter](https://en.wikipedia.org/wiki/Named_parameter) in Javascript or Typescript, because of
|
||||
that, we offer the flag `--useOptions` to generate code in two different styles.
|
||||
|
||||
Argument-style:
|
||||
```typescript
|
||||
@ -84,24 +85,22 @@ function createUser(name: string, password: string, type?: string, address?: str
|
||||
// ...
|
||||
}
|
||||
|
||||
// usage
|
||||
// Usage
|
||||
createUser('Jack', '123456', undefined, 'NY US');
|
||||
```
|
||||
|
||||
Object-style:
|
||||
```typescript
|
||||
interface CreateUserOptions {
|
||||
function createUser({ name, password, type, address }: {
|
||||
name: string,
|
||||
password: string,
|
||||
type?: string
|
||||
address?: string
|
||||
}
|
||||
|
||||
function createUser({ name, password, type, address }: CreateUserOptions) {
|
||||
}) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// usage
|
||||
// Usage
|
||||
createUser({
|
||||
name: 'Jack',
|
||||
password: '123456',
|
||||
@ -109,6 +108,118 @@ createUser({
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Runtime schemas
|
||||
By default the OpenAPI generator only exports interfaces for your models. These interfaces will help you during
|
||||
development, but will not be available in javascript during runtime. However Swagger allows you to define properties
|
||||
that can be useful during runtime, for instance: `maxLength` of a string or a `pattern` to match, etc. Let's say
|
||||
we have the following model:
|
||||
|
||||
```json
|
||||
{
|
||||
"MyModel": {
|
||||
"required": [
|
||||
"key",
|
||||
"name"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-zA-Z0-9_]*$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 255,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"readOnly": true
|
||||
},
|
||||
"modified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will generate the following interface:
|
||||
|
||||
```typescript
|
||||
export interface ModelWithPattern {
|
||||
key: string;
|
||||
name: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly modified?: string;
|
||||
}
|
||||
```
|
||||
|
||||
The interface does not contain any properties like `maxLength` or `pattern`. However they could be useful
|
||||
if we wanted to create some form where a user could create such a model. In that form you would iterate
|
||||
over the properties to render form fields based on their type and validate the input based on the `maxLength`
|
||||
or `pattern` property. This requires us to have this information somewhere... For this we can use the
|
||||
flag `--exportSchemas` to generate a runtime model next to the normal interface:
|
||||
|
||||
```typescript
|
||||
export const $ModelWithPattern = {
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 64,
|
||||
pattern: '^[a-zA-Z0-9_]*$',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
isReadOnly: true,
|
||||
},
|
||||
modified: {
|
||||
type: 'string',
|
||||
isReadOnly: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
These runtime object are prefixed with a `$` character and expose all the interesting attributes of a model
|
||||
and it's properties. We can now use this object to generate the form:
|
||||
|
||||
```typescript jsx
|
||||
import { $ModelWithPattern } from './generated';
|
||||
|
||||
// Some pseudo code to iterate over the properties and return a form field
|
||||
// the form field could be some abstract component that renders the correct
|
||||
// field type and validation rules based on the given input.
|
||||
const formFields = Object.entries($ModelWithPattern.properties).map(([key, value]) => (
|
||||
<FormField
|
||||
name={key}
|
||||
type={value.type}
|
||||
format={value.format}
|
||||
maxLength={value.maxLength}
|
||||
pattern={value.pattern}
|
||||
isReadOnly={value.isReadOnly}
|
||||
/>
|
||||
));
|
||||
|
||||
const MyForm = () => (
|
||||
<form>
|
||||
{formFields}
|
||||
</form>
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Enum with custom names and descriptions
|
||||
You can use `x-enum-varnames` and `x-enum-descriptions` in your spec to generate enum with custom names and descriptions.
|
||||
It's not in official [spec](https://github.com/OAI/OpenAPI-Specification/issues/681) yet. But its a supported extension
|
||||
@ -159,6 +270,7 @@ The OpenAPI generator supports Bearer Token authorization. In order to enable th
|
||||
of tokens in each request you can set the token using the global OpenAPI configuration:
|
||||
|
||||
```typescript
|
||||
import { OpenAPI } from './'
|
||||
OpenAPI.TOKEN = 'some-bearer-token'
|
||||
import { OpenAPI } from './generated';
|
||||
|
||||
OpenAPI.TOKEN = 'some-bearer-token';
|
||||
```
|
||||
|
||||
@ -13,6 +13,8 @@ program
|
||||
.option('--client [value]', 'HTTP client to generate [fetch, xhr]', 'fetch')
|
||||
.option('--useOptions', 'Use options vs arguments style functions', false)
|
||||
.option('--useUnionTypes', 'Use inclusive union types', false)
|
||||
.option('--exportServices', 'Generate services', true)
|
||||
.option('--exportSchemas', 'Generate schemas', false)
|
||||
.parse(process.argv);
|
||||
|
||||
const OpenAPI = require(path.resolve(__dirname, '../dist/index.js'));
|
||||
@ -23,6 +25,8 @@ if (OpenAPI) {
|
||||
output: program.output,
|
||||
httpClient: program.client,
|
||||
useOptions: program.useOptions,
|
||||
useUnionTypes: program.useUnionTypes
|
||||
useUnionTypes: program.useUnionTypes,
|
||||
exportServices: program.exportServices,
|
||||
exportSchemas: program.exportSchemas,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openapi-typescript-codegen",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.5",
|
||||
"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",
|
||||
@ -63,7 +63,6 @@
|
||||
"dependencies": {
|
||||
"camelcase": "5.3.1",
|
||||
"commander": "4.1.1",
|
||||
"glob": "7.1.6",
|
||||
"handlebars": "4.7.3",
|
||||
"js-yaml": "3.13.1",
|
||||
"mkdirp": "1.0.3",
|
||||
@ -86,6 +85,7 @@
|
||||
"eslint-config-prettier": "6.10.0",
|
||||
"eslint-plugin-prettier": "3.1.2",
|
||||
"eslint-plugin-simple-import-sort": "5.0.1",
|
||||
"glob": "7.1.6",
|
||||
"jest": "25.1.0",
|
||||
"jest-cli": "25.1.0",
|
||||
"prettier": "1.19.1",
|
||||
|
||||
10
src/index.ts
10
src/index.ts
@ -18,6 +18,8 @@ export interface Options {
|
||||
httpClient?: HttpClient;
|
||||
useOptions?: boolean;
|
||||
useUnionTypes?: boolean;
|
||||
exportServices?: boolean;
|
||||
exportSchemas?: boolean;
|
||||
write?: boolean;
|
||||
}
|
||||
|
||||
@ -30,9 +32,11 @@ export interface Options {
|
||||
* @param httpClient The selected httpClient (fetch or XHR).
|
||||
* @param useOptions Use options or arguments functions.
|
||||
* @param useUnionTypes Use inclusive union types.
|
||||
* @param exportServices: Generate services.
|
||||
* @param exportSchemas: Generate schemas.
|
||||
* @param write Write the files to disk (true or false).
|
||||
*/
|
||||
export function generate({ input, output, httpClient = HttpClient.FETCH, useOptions = false, useUnionTypes = false, write = true }: Options): void {
|
||||
export function generate({ input, output, httpClient = HttpClient.FETCH, useOptions = false, useUnionTypes = false, exportServices = true, exportSchemas = false, write = true }: Options): void {
|
||||
try {
|
||||
// Load the specification, read the OpenAPI version and load the
|
||||
// handlebar templates for the given language
|
||||
@ -45,7 +49,7 @@ export function generate({ input, output, httpClient = HttpClient.FETCH, useOpti
|
||||
const client = parseV2(openApi);
|
||||
const clientFinal = postProcessClient(client, useUnionTypes);
|
||||
if (write) {
|
||||
writeClient(clientFinal, templates, output, httpClient, useOptions);
|
||||
writeClient(clientFinal, templates, output, httpClient, useOptions, exportServices, exportSchemas);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -54,7 +58,7 @@ export function generate({ input, output, httpClient = HttpClient.FETCH, useOpti
|
||||
const client = parseV3(openApi);
|
||||
const clientFinal = postProcessClient(client, useUnionTypes);
|
||||
if (write) {
|
||||
writeClient(clientFinal, templates, output, httpClient, useOptions);
|
||||
writeClient(clientFinal, templates, output, httpClient, useOptions, exportServices, exportSchemas);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import { Result } from './Result';
|
||||
* @param response Response object from fetch
|
||||
*/
|
||||
async function parseBody(response: Response): Promise<any> {
|
||||
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
|
||||
@ -28,7 +28,6 @@ function parseBody(xhr: XMLHttpRequest): any {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -2,25 +2,31 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
{{#if exportServices}}
|
||||
|
||||
export { ApiError } from './core/ApiError';
|
||||
export { isSuccess } from './core/isSuccess';
|
||||
export { OpenAPI } from './core/OpenAPI';
|
||||
{{/if}}
|
||||
{{#if models}}
|
||||
|
||||
{{#each models}}
|
||||
export { {{{this}}} } from './models/{{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if exportSchemas}}
|
||||
{{#if models}}
|
||||
|
||||
{{#each models}}
|
||||
export { ${{{this}}} } from './schemas/${{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if exportServices}}
|
||||
{{#if services}}
|
||||
|
||||
{{#each services}}
|
||||
export { {{{this}}} } from './services/{{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
@ -6,26 +6,19 @@ import * as Handlebars from 'handlebars';
|
||||
* @param filePath
|
||||
*/
|
||||
export function readHandlebarsTemplate(filePath: string): Handlebars.TemplateDelegate {
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
const template = fs
|
||||
.readFileSync(filePath, 'utf8')
|
||||
.toString()
|
||||
.trim();
|
||||
const template = fs
|
||||
.readFileSync(filePath, 'utf8')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return Handlebars.compile(template, {
|
||||
strict: true,
|
||||
noEscape: true,
|
||||
preventIndent: true,
|
||||
knownHelpersOnly: true,
|
||||
knownHelpers: {
|
||||
equals: true,
|
||||
notEquals: true,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`Could not compile Handlebar template: "${filePath}"`);
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find Handlebar template: "${filePath}"`);
|
||||
return Handlebars.compile(template, {
|
||||
strict: true,
|
||||
noEscape: true,
|
||||
preventIndent: true,
|
||||
knownHelpersOnly: true,
|
||||
knownHelpers: {
|
||||
equals: true,
|
||||
notEquals: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,20 +1,16 @@
|
||||
import * as fs from 'fs';
|
||||
import * as glob from 'glob';
|
||||
|
||||
import { readHandlebarsTemplates } from './readHandlebarsTemplates';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('glob');
|
||||
|
||||
const fsExistsSync = fs.existsSync as jest.MockedFunction<typeof fs.existsSync>;
|
||||
const fsReadFileSync = fs.readFileSync as jest.MockedFunction<typeof fs.readFileSync>;
|
||||
const globSync = glob.sync as jest.MockedFunction<typeof glob.sync>;
|
||||
|
||||
describe('readHandlebarsTemplates', () => {
|
||||
it('should read the templates', () => {
|
||||
fsExistsSync.mockReturnValue(true);
|
||||
fsReadFileSync.mockReturnValue('{{{message}}}');
|
||||
globSync.mockReturnValue([]);
|
||||
|
||||
const template = readHandlebarsTemplates();
|
||||
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import * as glob from 'glob';
|
||||
import * as Handlebars from 'handlebars';
|
||||
import * as path from 'path';
|
||||
|
||||
import { readHandlebarsTemplate } from './readHandlebarsTemplate';
|
||||
import { registerHandlebarHelpers } from './registerHandlebarHelpers';
|
||||
|
||||
function resolveTemplate(filePath: string): string {
|
||||
return path.resolve(__dirname, `../../src/templates/${filePath}`);
|
||||
}
|
||||
|
||||
export interface Templates {
|
||||
index: Handlebars.TemplateDelegate;
|
||||
model: Handlebars.TemplateDelegate;
|
||||
@ -18,27 +21,47 @@ export interface Templates {
|
||||
* so we can easily access the templates in out generator / write functions.
|
||||
*/
|
||||
export function readHandlebarsTemplates(): Templates {
|
||||
try {
|
||||
registerHandlebarHelpers();
|
||||
registerHandlebarHelpers();
|
||||
|
||||
const templates: Templates = {
|
||||
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`)),
|
||||
};
|
||||
const templates: Templates = {
|
||||
index: readHandlebarsTemplate(resolveTemplate('index.hbs')),
|
||||
model: readHandlebarsTemplate(resolveTemplate('model.hbs')),
|
||||
schema: readHandlebarsTemplate(resolveTemplate('schema.hbs')),
|
||||
service: readHandlebarsTemplate(resolveTemplate('service.hbs')),
|
||||
settings: readHandlebarsTemplate(resolveTemplate('core/OpenAPI.hbs')),
|
||||
};
|
||||
|
||||
const partials = path.resolve(__dirname, `../../src/templates/partials`);
|
||||
const partialsFiles = glob.sync('*.hbs', { cwd: partials });
|
||||
partialsFiles.forEach(partial => {
|
||||
const templateName = path.basename(partial, '.hbs');
|
||||
const template = readHandlebarsTemplate(path.resolve(partials, partial));
|
||||
Handlebars.registerPartial(templateName, template);
|
||||
});
|
||||
const partials = [
|
||||
'exportEnum.hbs',
|
||||
'exportInterface.hbs',
|
||||
'exportType.hbs',
|
||||
'extends.hbs',
|
||||
'isNullable.hbs',
|
||||
'isReadOnly.hbs',
|
||||
'isRequired.hbs',
|
||||
'parameters.hbs',
|
||||
'result.hbs',
|
||||
'schema.hbs',
|
||||
'schemaArray.hbs',
|
||||
'schemaDictionary.hbs',
|
||||
'schemaEnum.hbs',
|
||||
'schemaGeneric.hbs',
|
||||
'schemaInterface.hbs',
|
||||
'type.hbs',
|
||||
'typeArray.hbs',
|
||||
'typeDictionary.hbs',
|
||||
'typeEnum.hbs',
|
||||
'typeGeneric.hbs',
|
||||
'typeInterface.hbs',
|
||||
'typeReference.hbs',
|
||||
];
|
||||
|
||||
return templates;
|
||||
} catch (e) {
|
||||
throw new Error(`Could not read Handlebar templates`);
|
||||
}
|
||||
partials.forEach(partial => {
|
||||
const templatePath = resolveTemplate(`partials/${partial}`);
|
||||
const templateName = path.basename(partial, '.hbs');
|
||||
const template = readHandlebarsTemplate(templatePath);
|
||||
Handlebars.registerPartial(templateName, template);
|
||||
});
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import * as glob from 'glob';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
@ -11,12 +10,10 @@ import { writeClient } from './writeClient';
|
||||
jest.mock('rimraf');
|
||||
jest.mock('mkdirp');
|
||||
jest.mock('fs');
|
||||
jest.mock('glob');
|
||||
|
||||
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>;
|
||||
const globSync = glob.sync as jest.MockedFunction<typeof glob.sync>;
|
||||
|
||||
describe('writeClient', () => {
|
||||
it('should write to filesystem', () => {
|
||||
@ -35,13 +32,10 @@ describe('writeClient', () => {
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
globSync.mockReturnValue([]);
|
||||
|
||||
writeClient(client, templates, '/', HttpClient.FETCH, false);
|
||||
writeClient(client, templates, '/', HttpClient.FETCH, false, true, true);
|
||||
|
||||
expect(rimrafSync).toBeCalled();
|
||||
expect(mkdirpSync).toBeCalled();
|
||||
expect(fsWriteFileSync).toBeCalled();
|
||||
expect(globSync).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import * as glob from 'glob';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import * as path from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
@ -13,6 +12,10 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write our OpenAPI client, using the given templates at the given output path.
|
||||
* @param client Client object with all the models, services, etc.
|
||||
@ -20,8 +23,10 @@ import { writeClientSettings } from './writeClientSettings';
|
||||
* @param output Directory to write the generated files to.
|
||||
* @param httpClient The selected httpClient (fetch or XHR).
|
||||
* @param useOptions Use options or arguments functions.
|
||||
* @param exportServices: Generate services.
|
||||
* @param exportSchemas: Generate schemas.
|
||||
*/
|
||||
export function writeClient(client: Client, templates: Templates, output: string, httpClient: HttpClient, useOptions: boolean): void {
|
||||
export function writeClient(client: Client, templates: Templates, output: string, httpClient: HttpClient, useOptions: boolean, exportServices: boolean, exportSchemas: boolean): void {
|
||||
const outputPath = path.resolve(process.cwd(), output);
|
||||
const outputPathCore = path.resolve(outputPath, 'core');
|
||||
const outputPathModels = path.resolve(outputPath, 'models');
|
||||
@ -29,37 +34,35 @@ export function writeClient(client: Client, templates: Templates, output: string
|
||||
const outputPathServices = path.resolve(outputPath, 'services');
|
||||
|
||||
// Clean output directory
|
||||
try {
|
||||
rimraf.sync(outputPath);
|
||||
} catch (e) {
|
||||
throw new Error('Could not clean output directory');
|
||||
}
|
||||
rimraf.sync(outputPath);
|
||||
mkdirp.sync(outputPath);
|
||||
|
||||
// Create new directories
|
||||
try {
|
||||
mkdirp.sync(outputPath);
|
||||
if (exportServices) {
|
||||
mkdirp.sync(outputPathCore);
|
||||
mkdirp.sync(outputPathModels);
|
||||
mkdirp.sync(outputPathSchemas);
|
||||
mkdirp.sync(outputPathServices);
|
||||
} catch (e) {
|
||||
throw new Error('Could not create output directories');
|
||||
|
||||
copySupportFile('core/ApiError.ts', outputPath);
|
||||
copySupportFile('core/getFormData.ts', outputPath);
|
||||
copySupportFile('core/getQueryString.ts', outputPath);
|
||||
copySupportFile('core/isSuccess.ts', outputPath);
|
||||
copySupportFile('core/OpenAPI.hbs', 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);
|
||||
|
||||
writeClientServices(client.services, templates, outputPathServices, useOptions);
|
||||
writeClientSettings(client, templates, outputPathCore, httpClient);
|
||||
}
|
||||
|
||||
// Copy all support files
|
||||
const supportFiles = path.resolve(__dirname, '../../src/templates/');
|
||||
const supportFilesList = glob.sync('**/*.ts', { cwd: supportFiles });
|
||||
supportFilesList.forEach(file => {
|
||||
fs.copyFileSync(
|
||||
path.resolve(supportFiles, file), // From input path
|
||||
path.resolve(outputPath, file) // To output path
|
||||
);
|
||||
});
|
||||
if (exportSchemas) {
|
||||
mkdirp.sync(outputPathSchemas);
|
||||
writeClientSchemas(client.models, templates, outputPathSchemas);
|
||||
}
|
||||
|
||||
// Write the client files
|
||||
mkdirp.sync(outputPathModels);
|
||||
copySupportFile('models/Dictionary.ts', outputPath);
|
||||
writeClientModels(client.models, templates, outputPathModels);
|
||||
writeClientSchemas(client.models, templates, outputPathSchemas);
|
||||
writeClientServices(client.services, templates, outputPathServices, useOptions);
|
||||
writeClientSettings(client, templates, outputPathCore, httpClient);
|
||||
writeClientIndex(client, templates, outputPath);
|
||||
writeClientIndex(client, templates, outputPath, exportServices, exportSchemas);
|
||||
}
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
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', () => {
|
||||
@ -28,9 +25,7 @@ describe('writeClientIndex', () => {
|
||||
settings: () => 'dummy',
|
||||
};
|
||||
|
||||
globSync.mockReturnValue([]);
|
||||
|
||||
writeClientIndex(client, templates, '/');
|
||||
writeClientIndex(client, templates, '/', true, true);
|
||||
|
||||
expect(fsWriteFileSync).toBeCalledWith('/index.ts', 'dummy');
|
||||
});
|
||||
|
||||
@ -13,19 +13,19 @@ import { Templates } from './readHandlebarsTemplates';
|
||||
* @param client Client object, containing, models, schemas and services.
|
||||
* @param templates The loaded handlebar templates.
|
||||
* @param outputPath Directory to write the generated files to.
|
||||
* @param exportServices: Generate services.
|
||||
* @param exportSchemas: Generate schemas.
|
||||
*/
|
||||
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`);
|
||||
}
|
||||
export function writeClientIndex(client: Client, templates: Templates, outputPath: string, exportServices: boolean, exportSchemas: boolean): void {
|
||||
fs.writeFileSync(
|
||||
path.resolve(outputPath, 'index.ts'),
|
||||
templates.index({
|
||||
exportServices,
|
||||
exportSchemas,
|
||||
server: client.server,
|
||||
version: client.version,
|
||||
models: getModelNames(client.models),
|
||||
services: getServiceNames(client.services),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -294,7 +294,6 @@ import { Result } from './Result';
|
||||
* @param response Response object from fetch
|
||||
*/
|
||||
async function parseBody(response: Response): Promise<any> {
|
||||
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
@ -368,7 +367,6 @@ function parseBody(xhr: XMLHttpRequest): any {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -461,6 +459,7 @@ export { ModelWithLink } from './models/ModelWithLink';
|
||||
export { ModelWithNestedEnums } from './models/ModelWithNestedEnums';
|
||||
export { ModelWithNestedProperties } from './models/ModelWithNestedProperties';
|
||||
export { ModelWithOrderedProperties } from './models/ModelWithOrderedProperties';
|
||||
export { ModelWithPattern } from './models/ModelWithPattern';
|
||||
export { ModelWithProperties } from './models/ModelWithProperties';
|
||||
export { ModelWithReference } from './models/ModelWithReference';
|
||||
export { ModelWithString } from './models/ModelWithString';
|
||||
@ -502,6 +501,7 @@ export { $ModelWithLink } from './schemas/$ModelWithLink';
|
||||
export { $ModelWithNestedEnums } from './schemas/$ModelWithNestedEnums';
|
||||
export { $ModelWithNestedProperties } from './schemas/$ModelWithNestedProperties';
|
||||
export { $ModelWithOrderedProperties } from './schemas/$ModelWithOrderedProperties';
|
||||
export { $ModelWithPattern } from './schemas/$ModelWithPattern';
|
||||
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
|
||||
export { $ModelWithReference } from './schemas/$ModelWithReference';
|
||||
export { $ModelWithString } from './schemas/$ModelWithString';
|
||||
@ -1090,6 +1090,25 @@ export interface ModelWithOrderedProperties {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`generation v2 file(./test/result/v2/models/ModelWithPattern.ts): ./test/result/v2/models/ModelWithPattern.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
|
||||
/**
|
||||
* This is a model that contains a some patterns
|
||||
*/
|
||||
export interface ModelWithPattern {
|
||||
key: string;
|
||||
name: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly modified?: string;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`generation v2 file(./test/result/v2/models/ModelWithProperties.ts): ./test/result/v2/models/ModelWithProperties.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
@ -1690,6 +1709,38 @@ export const $ModelWithOrderedProperties = {
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`generation v2 file(./test/result/v2/schemas/$ModelWithPattern.ts): ./test/result/v2/schemas/$ModelWithPattern.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export const $ModelWithPattern = {
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 64,
|
||||
pattern: '^[a-zA-Z0-9_]*$',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
isReadOnly: true,
|
||||
},
|
||||
modified: {
|
||||
type: 'string',
|
||||
isReadOnly: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`generation v2 file(./test/result/v2/schemas/$ModelWithProperties.ts): ./test/result/v2/schemas/$ModelWithProperties.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
@ -2647,7 +2698,6 @@ import { Result } from './Result';
|
||||
* @param response Response object from fetch
|
||||
*/
|
||||
async function parseBody(response: Response): Promise<any> {
|
||||
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
@ -2721,7 +2771,6 @@ function parseBody(xhr: XMLHttpRequest): any {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2816,6 +2865,7 @@ export { ModelWithNestedEnums } from './models/ModelWithNestedEnums';
|
||||
export { ModelWithNestedProperties } from './models/ModelWithNestedProperties';
|
||||
export { ModelWithOneOf } from './models/ModelWithOneOf';
|
||||
export { ModelWithOrderedProperties } from './models/ModelWithOrderedProperties';
|
||||
export { ModelWithPattern } from './models/ModelWithPattern';
|
||||
export { ModelWithProperties } from './models/ModelWithProperties';
|
||||
export { ModelWithReference } from './models/ModelWithReference';
|
||||
export { ModelWithString } from './models/ModelWithString';
|
||||
@ -2859,6 +2909,7 @@ export { $ModelWithNestedEnums } from './schemas/$ModelWithNestedEnums';
|
||||
export { $ModelWithNestedProperties } from './schemas/$ModelWithNestedProperties';
|
||||
export { $ModelWithOneOf } from './schemas/$ModelWithOneOf';
|
||||
export { $ModelWithOrderedProperties } from './schemas/$ModelWithOrderedProperties';
|
||||
export { $ModelWithPattern } from './schemas/$ModelWithPattern';
|
||||
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
|
||||
export { $ModelWithReference } from './schemas/$ModelWithReference';
|
||||
export { $ModelWithString } from './schemas/$ModelWithString';
|
||||
@ -3489,6 +3540,25 @@ export interface ModelWithOrderedProperties {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`generation v3 file(./test/result/v3/models/ModelWithPattern.ts): ./test/result/v3/models/ModelWithPattern.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
|
||||
/**
|
||||
* This is a model that contains a some patterns
|
||||
*/
|
||||
export interface ModelWithPattern {
|
||||
key: string;
|
||||
name: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly modified?: string;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`generation v3 file(./test/result/v3/models/ModelWithProperties.ts): ./test/result/v3/models/ModelWithProperties.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
@ -4123,6 +4193,38 @@ export const $ModelWithOrderedProperties = {
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`generation v3 file(./test/result/v3/schemas/$ModelWithPattern.ts): ./test/result/v3/schemas/$ModelWithPattern.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
export const $ModelWithPattern = {
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 64,
|
||||
pattern: '^[a-zA-Z0-9_]*$',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
isReadOnly: true,
|
||||
},
|
||||
modified: {
|
||||
type: 'string',
|
||||
isReadOnly: true,
|
||||
format: 'date-time',
|
||||
},
|
||||
},
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`generation v3 file(./test/result/v3/schemas/$ModelWithProperties.ts): ./test/result/v3/schemas/$ModelWithProperties.ts 1`] = `
|
||||
"/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
|
||||
@ -24,6 +24,8 @@ OpenAPI.generate({
|
||||
httpClient: OpenAPI.HttpClient.FETCH,
|
||||
useOptions: false,
|
||||
useUnionTypes: false,
|
||||
exportSchemas: true,
|
||||
exportServices: true,
|
||||
});
|
||||
|
||||
OpenAPI.generate({
|
||||
@ -32,6 +34,8 @@ OpenAPI.generate({
|
||||
httpClient: OpenAPI.HttpClient.FETCH,
|
||||
useOptions: false,
|
||||
useUnionTypes: false,
|
||||
exportSchemas: true,
|
||||
exportServices: true,
|
||||
});
|
||||
|
||||
compile('./test/result/v2/');
|
||||
|
||||
@ -12,6 +12,8 @@ describe('generation', () => {
|
||||
httpClient: OpenAPI.HttpClient.FETCH,
|
||||
useOptions: false,
|
||||
useUnionTypes: false,
|
||||
exportSchemas: true,
|
||||
exportServices: true,
|
||||
});
|
||||
|
||||
test.each(glob
|
||||
@ -31,6 +33,8 @@ describe('generation', () => {
|
||||
httpClient: OpenAPI.HttpClient.FETCH,
|
||||
useOptions: false,
|
||||
useUnionTypes: false,
|
||||
exportSchemas: true,
|
||||
exportServices: true,
|
||||
});
|
||||
|
||||
test.each(glob
|
||||
|
||||
@ -998,6 +998,34 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ModelWithPattern": {
|
||||
"description": "This is a model that contains a some patterns",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-zA-Z0-9_]*$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 255,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"readOnly": true
|
||||
},
|
||||
"modified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1482,6 +1482,34 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ModelWithPattern": {
|
||||
"description": "This is a model that contains a some patterns",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-zA-Z0-9_]*$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 255,
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"readOnly": true
|
||||
},
|
||||
"modified": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user