- Added flag to enable the optional generation of schemas and services

This commit is contained in:
Ferdi Koomen 2020-03-08 10:47:39 +01:00
parent ca2174c64b
commit 06214f55ac
20 changed files with 420 additions and 126 deletions

View File

@ -2,6 +2,6 @@
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 500,
"printWidth": 200,
"tabWidth": 4
}

136
README.md
View File

@ -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';
```

View File

@ -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,
});
}

View File

@ -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",

View File

@ -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;
}

View File

@ -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) {

View File

@ -28,7 +28,6 @@ function parseBody(xhr: XMLHttpRequest): any {
} catch (e) {
console.error(e);
}
return null;
}

View File

@ -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}}

View File

@ -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,
},
});
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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();
});
});

View File

@ -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);
}

View File

@ -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');
});

View File

@ -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),
})
);
}

View File

@ -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 */

View File

@ -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/');

View File

@ -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

View File

@ -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
}
}
}
}
}

View File

@ -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
}
}
}
}
}