diff --git a/.prettierrc.json b/.prettierrc.json index 681dbab3..3f00fc05 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,6 +2,6 @@ "semi": true, "singleQuote": true, "trailingComma": "es5", - "printWidth": 500, + "printWidth": 200, "tabWidth": 4 } diff --git a/README.md b/README.md index 338e40c2..8dcc860f 100644 --- a/README.md +++ b/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]) => ( + +)); + +const MyForm = () => ( +
+ {formFields} +
+); + +``` + + ### 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'; ``` diff --git a/bin/index.js b/bin/index.js index d8e26a0f..dc7cab8f 100755 --- a/bin/index.js +++ b/bin/index.js @@ -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, }); } diff --git a/package.json b/package.json index e3535ce0..f262acda 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/index.ts b/src/index.ts index f02f33be..a22881ad 100644 --- a/src/index.ts +++ b/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; } diff --git a/src/templates/core/requestUsingFetch.ts b/src/templates/core/requestUsingFetch.ts index 637285a6..a79714c6 100644 --- a/src/templates/core/requestUsingFetch.ts +++ b/src/templates/core/requestUsingFetch.ts @@ -12,7 +12,6 @@ import { Result } from './Result'; * @param response Response object from fetch */ async function parseBody(response: Response): Promise { - try { const contentType = response.headers.get('Content-Type'); if (contentType) { diff --git a/src/templates/core/requestUsingXHR.ts b/src/templates/core/requestUsingXHR.ts index f0c9b5e1..a3445740 100644 --- a/src/templates/core/requestUsingXHR.ts +++ b/src/templates/core/requestUsingXHR.ts @@ -28,7 +28,6 @@ function parseBody(xhr: XMLHttpRequest): any { } catch (e) { console.error(e); } - return null; } diff --git a/src/templates/index.hbs b/src/templates/index.hbs index a7b6032d..1377cdab 100644 --- a/src/templates/index.hbs +++ b/src/templates/index.hbs @@ -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}} diff --git a/src/utils/readHandlebarsTemplate.ts b/src/utils/readHandlebarsTemplate.ts index 31c2b0da..9249965b 100644 --- a/src/utils/readHandlebarsTemplate.ts +++ b/src/utils/readHandlebarsTemplate.ts @@ -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, + }, + }); } diff --git a/src/utils/readHandlebarsTemplates.spec.ts b/src/utils/readHandlebarsTemplates.spec.ts index 37f49037..d02a3ca1 100644 --- a/src/utils/readHandlebarsTemplates.spec.ts +++ b/src/utils/readHandlebarsTemplates.spec.ts @@ -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; const fsReadFileSync = fs.readFileSync as jest.MockedFunction; -const globSync = glob.sync as jest.MockedFunction; describe('readHandlebarsTemplates', () => { it('should read the templates', () => { fsExistsSync.mockReturnValue(true); fsReadFileSync.mockReturnValue('{{{message}}}'); - globSync.mockReturnValue([]); const template = readHandlebarsTemplates(); diff --git a/src/utils/readHandlebarsTemplates.ts b/src/utils/readHandlebarsTemplates.ts index 4f573ef9..775b6b09 100644 --- a/src/utils/readHandlebarsTemplates.ts +++ b/src/utils/readHandlebarsTemplates.ts @@ -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; } diff --git a/src/utils/writeClient.spec.ts b/src/utils/writeClient.spec.ts index 6d81aee7..6f3f62d2 100644 --- a/src/utils/writeClient.spec.ts +++ b/src/utils/writeClient.spec.ts @@ -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; const mkdirpSync = rimraf.sync as jest.MockedFunction; const fsWriteFileSync = fs.writeFileSync as jest.MockedFunction; -const globSync = glob.sync as jest.MockedFunction; 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(); }); }); diff --git a/src/utils/writeClient.ts b/src/utils/writeClient.ts index da17f36b..ee7592cd 100644 --- a/src/utils/writeClient.ts +++ b/src/utils/writeClient.ts @@ -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); } diff --git a/src/utils/writeClientIndex.spec.ts b/src/utils/writeClientIndex.spec.ts index 2cecb881..38f4a86b 100644 --- a/src/utils/writeClientIndex.spec.ts +++ b/src/utils/writeClientIndex.spec.ts @@ -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; -const globSync = glob.sync as jest.MockedFunction; 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'); }); diff --git a/src/utils/writeClientIndex.ts b/src/utils/writeClientIndex.ts index 86e872c8..49e8823e 100644 --- a/src/utils/writeClientIndex.ts +++ b/src/utils/writeClientIndex.ts @@ -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), + }) + ); } diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.js.snap index ba5c0d6f..04bd028e 100644 --- a/test/__snapshots__/index.spec.js.snap +++ b/test/__snapshots__/index.spec.js.snap @@ -294,7 +294,6 @@ import { Result } from './Result'; * @param response Response object from fetch */ async function parseBody(response: Response): Promise { - 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 { - 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 */ diff --git a/test/index.js b/test/index.js index 3e37a8db..f1127c38 100644 --- a/test/index.js +++ b/test/index.js @@ -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/'); diff --git a/test/index.spec.js b/test/index.spec.js index 944bb2d1..c1e2b1d0 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -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 diff --git a/test/mock/v2/spec.json b/test/mock/v2/spec.json index dc2ed535..2f8d40a5 100644 --- a/test/mock/v2/spec.json +++ b/test/mock/v2/spec.json @@ -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 + } + } } } } diff --git a/test/mock/v3/spec.json b/test/mock/v3/spec.json index 0d7f2827..be5d08d3 100644 --- a/test/mock/v3/spec.json +++ b/test/mock/v3/spec.json @@ -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 + } + } } } }