diff --git a/src/openApi/v2/parser/getArrayType.ts b/src/openApi/v2/parser/getArrayType.ts deleted file mode 100644 index 5d71928a..00000000 --- a/src/openApi/v2/parser/getArrayType.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { getType } from './getType'; -import { PrimaryType } from './constants'; -import { OpenApiItems } from '../interfaces/OpenApiItems'; - -export interface ArrayType { - type: string; - base: string; - template?: string; - default?: any; - imports: string[]; -} - -export function getArrayType(items: OpenApiItems): ArrayType { - const result: ArrayType = { - type: PrimaryType.OBJECT, - base: PrimaryType.OBJECT, - default: items.default, - imports: [], - }; - - // If the parameter has a type than it can be a basic or generic type. - if (items.type) { - const itemsType = getType(items.type); - result.type = itemsType.type; - result.base = itemsType.base; - result.template = itemsType.template; - result.imports.push(...itemsType.imports); - - // If the parameter is an Array type, we check for the child type, - // so we can create a typed array, otherwise this will be a "any[]". - if (items.type === 'array' && items.items) { - const arrayType = getArrayType(items.items); - result.type = `${arrayType.type}[]`; - result.base = arrayType.base; - result.template = arrayType.template; - result.imports.push(...arrayType.imports); - } - } - - // if (items.enum) { - // result.type = getEnumType(items.enum, true); - // result.base = 'string'; - // result.imports = []; - // } - - return result; -} diff --git a/src/openApi/v2/parser/getModel.ts b/src/openApi/v2/parser/getModel.ts index ea7be48c..dc7d808f 100644 --- a/src/openApi/v2/parser/getModel.ts +++ b/src/openApi/v2/parser/getModel.ts @@ -100,35 +100,36 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri return result; } - if (definition.allOf) { - definition.allOf.forEach(parent => { - if (parent.$ref) { - const parentRef = getType(parent.$ref); - result.extends.push(parentRef.type); - result.imports.push(parentRef.base); - } - if (parent.type === 'object' && parent.properties) { - const properties = getModelProperties(openApi, parent); - properties.forEach(property => { - result.properties.push(property); - result.imports.push(...property.imports); - }); - } - }); + if (definition.type === 'object') { result.export = 'interface'; result.type = PrimaryType.OBJECT; result.base = PrimaryType.OBJECT; - } - if (definition.type === 'object' && definition.properties) { - const properties = getModelProperties(openApi, definition); - properties.forEach(property => { - result.properties.push(property); - result.imports.push(...property.imports); - }); - result.export = 'interface'; - result.type = PrimaryType.OBJECT; - result.base = PrimaryType.OBJECT; + if (definition.allOf) { + definition.allOf.forEach(parent => { + if (parent.$ref) { + const parentRef = getType(parent.$ref); + result.extends.push(parentRef.type); + result.imports.push(parentRef.base); + } + if (parent.type === 'object' && parent.properties) { + const properties = getModelProperties(openApi, parent); + properties.forEach(property => { + result.properties.push(property); + result.imports.push(...property.imports); + }); + } + }); + } + + if (definition.properties) { + const properties = getModelProperties(openApi, definition); + properties.forEach(property => { + result.properties.push(property); + result.imports.push(...property.imports); + }); + } + return result; } diff --git a/src/openApi/v2/parser/getSchemaReference.ts b/src/openApi/v2/parser/getSchemaReference.ts deleted file mode 100644 index 5420b87d..00000000 --- a/src/openApi/v2/parser/getSchemaReference.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { OpenApiSchema } from '../interfaces/OpenApiSchema'; -import { OpenApi } from '../interfaces/OpenApi'; -import { PrimaryType } from './constants'; - -export interface SchemaReference { - type: string; - base: string; - template: string | null; - imports: string[]; -} - -export function getSchemaReference(openApi: OpenApi, schema: OpenApiSchema): SchemaReference { - const result: SchemaReference = { - type: PrimaryType.OBJECT, - base: PrimaryType.OBJECT, - template: null, - imports: [], - }; - - if (schema.$ref) { - // const itemSchemaType: Type = getType(schema.$ref); - // result.type = itemSchemaType.type; - // result.base = itemSchemaType.base; - // result.template = itemSchemaType.template; - // result.imports.push(...itemSchemaType.imports); - } else { - // const item: OpenApiSchema = getRef(openApi, schema); - // const itemSchema: Schema = getSchema(openApi, item, 'unknown'); - // result.type = itemSchema.type; - // result.base = itemSchema.base; - // result.template = itemSchema.template; - // result.imports.push(...itemSchema.imports); - } - - return result; -} diff --git a/src/openApi/v2/parser/getTypeFromProperties.ts b/src/openApi/v2/parser/getTypeFromProperties.ts deleted file mode 100644 index 770b551d..00000000 --- a/src/openApi/v2/parser/getTypeFromProperties.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EOL } from 'os'; -import { Model } from '../../../client/interfaces/Model'; - -export function getTypeFromProperties(properties: Model[]): string { - return [ - `{`, - ...properties.map(property => { - let type = ''; - type = `${type}${property.readOnly ? 'readonly ' : ''}`; - type = `${type}${property.name}`; - type = `${type}${property.required ? '' : '?'}`; - type = `${type}: ${property.type}`; - type = `${type}${property.nullable ? ' | null' : ''}`; - return `${type},`; - }), - `}`, - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getValidationForArray.ts b/src/openApi/v2/parser/getValidationForArray.ts deleted file mode 100644 index 48e8ec44..00000000 --- a/src/openApi/v2/parser/getValidationForArray.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Model } from '../../../client/interfaces/Model'; - -export function getValidationForArray(name: string, model: Model): string { - return `yup.array<${name ? name : 'any'}>().of(${model.validation ? model.validation : 'yup.mixed()'})`; -} diff --git a/src/openApi/v2/parser/getValidationForArrayRef.ts b/src/openApi/v2/parser/getValidationForArrayRef.ts deleted file mode 100644 index d71a2bb6..00000000 --- a/src/openApi/v2/parser/getValidationForArrayRef.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Type } from '../../../client/interfaces/Type'; - -export function getValidationForArrayRef(ref: Type): string { - return `yup.array<${ref.type}>().of(${ref.base}.schema)`; -} diff --git a/src/openApi/v2/parser/getValidationForDictionary.ts b/src/openApi/v2/parser/getValidationForDictionary.ts deleted file mode 100644 index 17f077c2..00000000 --- a/src/openApi/v2/parser/getValidationForDictionary.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Model } from '../../../client/interfaces/Model'; -import { EOL } from 'os'; - -export function getValidationForDictionary(name: string, model: Model): string { - /* prettier-ignore */ - return [ - `yup.lazy>(value =>`, - `yup.object>().shape(`, - `Object.entries(value).reduce((obj, item) => ({`, - `...obj,`, - `[item[0]]: ${model.validation ? model.validation : 'yup.mixed()'},`, - `}), {})`, - `)`, - `)` - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getValidationForDictionaryRef.ts b/src/openApi/v2/parser/getValidationForDictionaryRef.ts deleted file mode 100644 index 85705e27..00000000 --- a/src/openApi/v2/parser/getValidationForDictionaryRef.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Type } from '../../../client/interfaces/Type'; -import { EOL } from 'os'; - -export function getValidationForDictionaryRef(type: Type): string { - /* prettier-ignore */ - return [ - `yup.lazy>(value =>`, - `yup.object>().shape(`, - `Object.entries(value).reduce((obj, item) => ({`, - `...obj,`, - `[item[0]]: ${type.base}.schema,`, - `}), {})`, - `)`, - `)` - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getValidationForEnum.ts b/src/openApi/v2/parser/getValidationForEnum.ts deleted file mode 100644 index 485f43ea..00000000 --- a/src/openApi/v2/parser/getValidationForEnum.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Enum } from '../../../client/interfaces/Enum'; -import { EOL } from 'os'; - -export function getValidationForEnum(name: string, enumerators: Enum[]): string { - return [ - `yup.mixed${name ? `<${name}>` : ''}().oneOf([`, - ...enumerators.map(enumerator => { - if (name) { - return `${name}.${enumerator.name},`; - } else { - return `${enumerator.value},`; - } - }), - `])`, - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getValidationForProperties.ts b/src/openApi/v2/parser/getValidationForProperties.ts deleted file mode 100644 index 88ce8cca..00000000 --- a/src/openApi/v2/parser/getValidationForProperties.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EOL } from 'os'; -import { Model } from '../../../client/interfaces/Model'; - -export function getValidationForProperties(name: string, model: Model): string { - return [ - ...model.extends.map(extend => `${extend}.schema.concat(`), - `yup.object${name ? `<${name}>` : ''}().shape({`, - ...model.properties.map(property => { - let validation = ''; - validation = `${validation}${property.name}: yup.lazy(() => ${property.validation}.default(undefined))`; - validation = `${validation}${property.required ? '.required()' : ''}`; - validation = `${validation}${property.nullable ? '.nullable()' : ''}`; - return `${validation},`; - }), - `}).noUnknown()`, - ...model.extends.map(() => `)`), - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getValidationForRef.ts b/src/openApi/v2/parser/getValidationForRef.ts deleted file mode 100644 index 74083c7f..00000000 --- a/src/openApi/v2/parser/getValidationForRef.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Type } from '../../../client/interfaces/Type'; - -export function getValidationForRef(ref: Type): string { - return `${ref.base}.schema`; -} diff --git a/src/openApi/v2/parser/getValidationForType.ts b/src/openApi/v2/parser/getValidationForType.ts deleted file mode 100644 index 401121c0..00000000 --- a/src/openApi/v2/parser/getValidationForType.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PrimaryType } from './constants'; -import { Type } from '../../../client/interfaces/Type'; - -export function getValidationForType(type: Type): string { - let validation = `yup.mixed<${type.type}>()`; - switch (type.type) { - case PrimaryType.BOOLEAN: - validation = `yup.boolean()`; - break; - case PrimaryType.NUMBER: - validation = `yup.number()`; - break; - case PrimaryType.STRING: - validation = `yup.string()`; - break; - } - return validation; -} diff --git a/src/templates/typescript/exportArray.hbs b/src/templates/typescript/exportArray.hbs index 61c58e49..509d7c21 100644 --- a/src/templates/typescript/exportArray.hbs +++ b/src/templates/typescript/exportArray.hbs @@ -7,13 +7,16 @@ export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; export namespace {{{name}}} { - export const schema = {{>validation}}; +{{#indent}} +export const schema = {{>validation}}; + +export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} - export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { - return schema.validateSync(value, { strict: true }); - } } diff --git a/src/templates/typescript/exportDictionary.hbs b/src/templates/typescript/exportDictionary.hbs index 61c58e49..509d7c21 100644 --- a/src/templates/typescript/exportDictionary.hbs +++ b/src/templates/typescript/exportDictionary.hbs @@ -7,13 +7,16 @@ export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; export namespace {{{name}}} { - export const schema = {{>validation}}; +{{#indent}} +export const schema = {{>validation}}; + +export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} - export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { - return schema.validateSync(value, { strict: true }); - } } diff --git a/src/templates/typescript/exportEnum.hbs b/src/templates/typescript/exportEnum.hbs index b5e75ade..2cef9a1f 100644 --- a/src/templates/typescript/exportEnum.hbs +++ b/src/templates/typescript/exportEnum.hbs @@ -11,13 +11,16 @@ export enum {{{name}}} { export namespace {{{name}}} { - export const schema = {{>validation}}; +{{#indent}} +export const schema = {{>validation}}; + +export function validate(value: any): Promise<{{{name}}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} - export function validate(value: any): Promise<{{{name}}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}} { - return schema.validateSync(value, { strict: true }); - } } diff --git a/src/templates/typescript/exportGeneric.hbs b/src/templates/typescript/exportGeneric.hbs index 61c58e49..509d7c21 100644 --- a/src/templates/typescript/exportGeneric.hbs +++ b/src/templates/typescript/exportGeneric.hbs @@ -7,13 +7,16 @@ export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; export namespace {{{name}}} { - export const schema = {{>validation}}; +{{#indent}} +export const schema = {{>validation}}; + +export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} - export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { - return schema.validateSync(value, { strict: true }); - } } diff --git a/src/templates/typescript/exportInterface.hbs b/src/templates/typescript/exportInterface.hbs index bc385cd9..e80560d3 100644 --- a/src/templates/typescript/exportInterface.hbs +++ b/src/templates/typescript/exportInterface.hbs @@ -3,39 +3,50 @@ * {{{description}}} */ {{/if}} -export interface {{{name}}}{{{template}}}{{#if extends}} extends{{#each extends}} {{{this}}}{{/each}}{{/if}} { - {{#each properties}} - {{#if description}} - /** - * {{{description}}} - */ - {{/if}} - {{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type}}{{#if nullable}} | null{{/if}}; - {{/each}} +export interface {{{name}}}{{{template}}}{{#if extends}} extends {{#each extends}}{{{this}}}{{#unless @last}}, {{/unless}}{{/each}}{{/if}} { + +{{#indent}} +{{#each properties}} +{{#if description}} +/** + * {{{description}}} + */ +{{/if}} +{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type}}{{#if nullable}} | null{{/if}}; +{{/each}} +{{/indent}} + } export namespace {{{name}}} { - {{#each enums}} - {{#if description}} - /** - * {{{description}}} - */ - {{/if}} - export enum {{{name}}} { - {{#each values}} - {{{name}}} = {{{value}}}, - {{/each}} - } - +{{#indent}} +{{#each enums}} +{{#if description}} +/** + * {{{description}}} + */ +{{/if}} +export enum {{{name}}} { + {{#each values}} + {{{name}}} = {{{value}}}, {{/each}} - export const schema = {{>validation}}; - - export function validate(value: any): Promise<{{{name}}}{{{template}}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}}{{{template}}} { - return schema.validateSync(value, { strict: true }); - } +} + +{{/each}} +export const schema: yup.ObjectSchema<{{{name}}}{{{template}}}> = ( +{{#indent}} +{{>validation}} +{{/indent}} +/**/); + +export function validate(value: any): Promise<{{{name}}}{{{template}}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}}{{{template}}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} + } diff --git a/src/templates/typescript/exportReference.hbs b/src/templates/typescript/exportReference.hbs index 1f71e092..621f0ffa 100644 --- a/src/templates/typescript/exportReference.hbs +++ b/src/templates/typescript/exportReference.hbs @@ -2,13 +2,16 @@ export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; export namespace {{{name}}} { - export const schema = {{>validation}}; +{{#indent}} +export const schema = {{>validation}}; + +export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + return schema.validate(value, { strict: true }); +} + +export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + return schema.validateSync(value, { strict: true }); +} +{{/indent}} - export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { - return schema.validate(value, { strict: true }); - } - - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { - return schema.validateSync(value, { strict: true }); - } } diff --git a/src/templates/typescript/model.hbs b/src/templates/typescript/model.hbs index 770cda77..ce300c7b 100644 --- a/src/templates/typescript/model.hbs +++ b/src/templates/typescript/model.hbs @@ -2,8 +2,8 @@ /* tslint:disable */ /* eslint-disable */ /* prettier-ignore */ -{{#if imports}} +{{#if imports}} {{#each imports}} import { {{{this}}} } from '../models/{{{this}}}'; {{/each}} diff --git a/src/templates/typescript/typeForInterface.hbs b/src/templates/typescript/typeForInterface.hbs index ae9413a7..98034312 100644 --- a/src/templates/typescript/typeForInterface.hbs +++ b/src/templates/typescript/typeForInterface.hbs @@ -1,12 +1,12 @@ { - {{#each properties}} - {{#each properties}} - {{#if description}} - /** - * {{{description}}} - */ - {{/if}} - {{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type}}{{#if nullable}} | null{{/if}}, - {{/each}} - {{/each}} -} +{{#indent}} +{{#each properties}} +{{#if description}} +/** + * {{{description}}} + */ +{{/if}} +{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type}}{{#if nullable}} | null{{/if}}, +{{/each}} +{{/indent}} +/**/} diff --git a/src/templates/typescript/validationForDictionary.hbs b/src/templates/typescript/validationForDictionary.hbs index 0ab8fcf7..69957acf 100644 --- a/src/templates/typescript/validationForDictionary.hbs +++ b/src/templates/typescript/validationForDictionary.hbs @@ -1,19 +1,31 @@ {{~#if link~}} yup.lazytype link}}>>(value => - yup.objecttype link}}>>().shape( - Object.entries(value).reduce((obj, item) => ({ - ...obj, - [item[0]]: {{>validation link}}, - }), {}) - ) -) +{{#indent}} +yup.objecttype link}}>>().shape( +{{#indent}} +Object.entries(value).reduce((obj, item) => ({ +{{#indent}} +...obj, +[item[0]]: {{>validation link}}, +{{/indent}} +/**/}), {}) +{{/indent}} +/**/) +{{/indent}} +/**/) {{~else~}} yup.lazy>(value => - yup.object>().shape( - Object.entries(value).reduce((obj, item) => ({ - ...obj, - [item[0]]: {{{base}}}.schema, - }), {}) - ) -) +{{#indent}} +yup.object>().shape( +{{#indent}} +Object.entries(value).reduce((obj, item) => ({ +{{#indent}} +...obj, +[item[0]]: {{{base}}}.schema, +{{/indent}} +/**/}), {}) +{{/indent}} +/**/) +{{/indent}} +/**/) {{~/if~}} diff --git a/src/templates/typescript/validationForInterface.hbs b/src/templates/typescript/validationForInterface.hbs index 224a3521..3e7e7776 100644 --- a/src/templates/typescript/validationForInterface.hbs +++ b/src/templates/typescript/validationForInterface.hbs @@ -1,15 +1,17 @@ -{{~#if extends~}} +{{#if extends}} {{#each extends}} {{{this}}}.schema.concat( {{/each}} -{{~/if~}} +{{/if}} yup.object{{#if name}}<{{{name}}}>{{/if}}().shape({ - {{#each properties}} - {{{name}}}: yup.lazy(() => {{>validation}}.default(undefined)){{#if required}}.required(){{/if}}{{#if nullable}}.nullable(){{/if}}, - {{/each}} -}).noUnknown() -{{~#if extends~}} +{{#indent}} +{{#each properties}} +{{{name}}}: yup.lazy(() => {{>validation}}.default(undefined)){{#if required}}.required(){{/if}}{{#if nullable}}.nullable(){{/if}}, +{{/each}} +{{/indent}} +/**/}).noUnknown() +{{#if extends}} {{#each extends}} ) {{/each}} -{{~/if~}} +{{/if}} diff --git a/src/utils/exportModel.ts b/src/utils/exportModel.ts index 3fed30bf..8c49c12b 100644 --- a/src/utils/exportModel.ts +++ b/src/utils/exportModel.ts @@ -15,18 +15,12 @@ export function exportModel(model: Model): Model { const nameB = b.toLowerCase(); return nameA.localeCompare(nameB); }), - properties: model.properties - // .map(property => exportModel(property)) - // .filter((property, index, arr) => { - // return arr.findIndex(item => item.name === property.name) === index; - // }) - .sort((a, b) => { - const nameA = a.name.toLowerCase(); - const nameB = b.name.toLowerCase(); - return nameA.localeCompare(nameB); - }), + properties: model.properties.sort((a, b) => { + const nameA = a.name.toLowerCase(); + const nameB = b.name.toLowerCase(); + return nameA.localeCompare(nameB); + }), enums: model.enums - .map(property => exportModel(property)) .filter((property, index, arr) => { return arr.findIndex(item => item.name === property.name) === index; }) diff --git a/src/utils/fixIndentation.ts b/src/utils/fixIndentation.ts new file mode 100644 index 00000000..d8b89db7 --- /dev/null +++ b/src/utils/fixIndentation.ts @@ -0,0 +1,10 @@ +// Replace " /**/}" with "}" +// Replace " /**/)" with ")" +// Replace " /**/]" with "]" + +export function fixIndentation(s: string): string { + return s + .replace(/\s{4}\/\*\*\/\}/g, '}') + .replace(/\s{4}\/\*\*\/\)/g, ')') + .replace(/\s{4}\/\*\*\/\]/g, ']'); +} diff --git a/src/utils/getModelNames.spec.ts b/src/utils/getModelNames.spec.ts index e7a22d15..473a2933 100644 --- a/src/utils/getModelNames.spec.ts +++ b/src/utils/getModelNames.spec.ts @@ -5,6 +5,7 @@ describe('getModelNames', () => { it('should return sorted list', () => { const models = new Map(); models.set('John', { + export: 'interface', name: 'John', type: 'John', base: 'John', @@ -19,9 +20,9 @@ describe('getModelNames', () => { enum: [], enums: [], properties: [], - validation: null, }); models.set('Jane', { + export: 'interface', name: 'Jane', type: 'Jane', base: 'Jane', @@ -36,9 +37,9 @@ describe('getModelNames', () => { enum: [], enums: [], properties: [], - validation: null, }); models.set('Doe', { + export: 'interface', name: 'Doe', type: 'Doe', base: 'Doe', @@ -53,7 +54,6 @@ describe('getModelNames', () => { enum: [], enums: [], properties: [], - validation: null, }); expect(getModelNames(new Map())).toEqual([]); diff --git a/src/utils/readHandlebarsTemplate.ts b/src/utils/readHandlebarsTemplate.ts index cd7e894d..2498e9d1 100644 --- a/src/utils/readHandlebarsTemplate.ts +++ b/src/utils/readHandlebarsTemplate.ts @@ -1,21 +1,24 @@ import * as fs from 'fs'; -import * as handlebars from 'handlebars'; +import * as Handlebars from 'handlebars'; /** * Read and compile the Handlebars template. * @param filePath */ -export function readHandlebarsTemplate(filePath: string): handlebars.TemplateDelegate { +export function readHandlebarsTemplate(filePath: string): Handlebars.TemplateDelegate { if (fs.existsSync(filePath)) { try { const template = fs .readFileSync(filePath, 'utf8') .toString() .trim(); - return handlebars.compile(template, { + return Handlebars.compile(template, { strict: true, + noEscape: true, + preventIndent: true, knownHelpersOnly: true, knownHelpers: { + indent: true, eq: true, }, }); diff --git a/src/utils/readHandlebarsTemplates.ts b/src/utils/readHandlebarsTemplates.ts index 0000300b..18555d83 100644 --- a/src/utils/readHandlebarsTemplates.ts +++ b/src/utils/readHandlebarsTemplates.ts @@ -1,32 +1,13 @@ -import * as handlebars from 'handlebars'; +import * as Handlebars from 'handlebars'; import { readHandlebarsTemplate } from './readHandlebarsTemplate'; import { Language } from '../index'; import * as path from 'path'; +import { registerHandlebarHelpers } from './registerHandlebarHelpers'; export interface Templates { - index: handlebars.TemplateDelegate; - model: handlebars.TemplateDelegate; - service: handlebars.TemplateDelegate; - exportGeneric: handlebars.TemplateDelegate; - exportReference: handlebars.TemplateDelegate; - exportInterface: handlebars.TemplateDelegate; - exportEnum: handlebars.TemplateDelegate; - exportDictionary: handlebars.TemplateDelegate; - exportArray: handlebars.TemplateDelegate; - validation: handlebars.TemplateDelegate; - validationForGeneric: handlebars.TemplateDelegate; - validationForReference: handlebars.TemplateDelegate; - validationForEnum: handlebars.TemplateDelegate; - validationForInterface: handlebars.TemplateDelegate; - validationForDictionary: handlebars.TemplateDelegate; - validationForArray: handlebars.TemplateDelegate; - type: handlebars.TemplateDelegate; - typeForArray: handlebars.TemplateDelegate; - typeForDictionary: handlebars.TemplateDelegate; - typeForEnum: handlebars.TemplateDelegate; - typeForInterface: handlebars.TemplateDelegate; - typeForReference: handlebars.TemplateDelegate; - typeForGeneric: handlebars.TemplateDelegate; + index: Handlebars.TemplateDelegate; + model: Handlebars.TemplateDelegate; + service: Handlebars.TemplateDelegate; } /** @@ -35,18 +16,14 @@ export interface Templates { * @param language The language we need to generate (Typescript or Javascript). */ export function readHandlebarsTemplates(language: Language): Templates { - handlebars.registerHelper('eq', function(a: string, b: string, options: handlebars.HelperOptions): string { - // eslint-disable - // prettier-ignore - // @ts-ignore - return a === b ? options.fn(this) : options.inverse(this); - }); - try { - return { + registerHandlebarHelpers(); + const templates: Templates = { index: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/index.hbs`)), model: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/model.hbs`)), service: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/service.hbs`)), + }; + Handlebars.registerPartial({ exportGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportGeneric.hbs`)), exportReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportReference.hbs`)), exportInterface: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportInterface.hbs`)), @@ -67,7 +44,8 @@ export function readHandlebarsTemplates(language: Language): Templates { typeForInterface: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForInterface.hbs`)), typeForReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForReference.hbs`)), typeForGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForGeneric.hbs`)), - }; + }); + return templates; } catch (e) { throw e; } diff --git a/src/utils/registerHandlebarHelpers.ts b/src/utils/registerHandlebarHelpers.ts new file mode 100644 index 00000000..2291a5d8 --- /dev/null +++ b/src/utils/registerHandlebarHelpers.ts @@ -0,0 +1,21 @@ +import * as Handlebars from 'handlebars'; +import { EOL } from 'os'; + +export function registerHandlebarHelpers(): void { + Handlebars.registerHelper('indent', function(options: Handlebars.HelperOptions): string { + // eslint-disable + // prettier-ignore + // @ts-ignore + return options.fn(this) + .split(EOL) + .map(line => ` ${line}`) + .join(EOL) + }); + + Handlebars.registerHelper('eq', function(a: string, b: string, options: Handlebars.HelperOptions): string { + // eslint-disable + // prettier-ignore + // @ts-ignore + return a === b ? options.fn(this) : options.inverse(this); + }); +} diff --git a/src/utils/writeClient.spec.ts b/src/utils/writeClient.spec.ts index 76fc3e59..9f0a4cc5 100644 --- a/src/utils/writeClient.spec.ts +++ b/src/utils/writeClient.spec.ts @@ -31,7 +31,6 @@ describe('writeClient', () => { const templates: Templates = { index: () => 'dummy', model: () => 'dummy', - interface: () => 'dummy', service: () => 'dummy', }; diff --git a/src/utils/writeClientIndex.spec.ts b/src/utils/writeClientIndex.spec.ts index ed491b08..2ce68bb7 100644 --- a/src/utils/writeClientIndex.spec.ts +++ b/src/utils/writeClientIndex.spec.ts @@ -21,12 +21,7 @@ describe('writeClientIndex', () => { const templates: Templates = { index: () => 'dummy', model: () => 'dummy', - exportInterface: () => 'dummy', - exportEnum: () => 'dummy', - exportType: () => 'dummy', service: () => 'dummy', - validation: () => 'dummy', - type: () => 'dummy', }; writeClientIndex(client, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/index.ts', 'dummy'); diff --git a/src/utils/writeClientModels.spec.ts b/src/utils/writeClientModels.spec.ts index d73b7d27..740e44bf 100644 --- a/src/utils/writeClientModels.spec.ts +++ b/src/utils/writeClientModels.spec.ts @@ -12,6 +12,7 @@ describe('writeClientModels', () => { it('should write to filesystem', () => { const models = new Map(); models.set('Item', { + export: 'interface', name: 'Item', type: 'Item', base: 'Item', @@ -26,17 +27,11 @@ describe('writeClientModels', () => { enum: [], enums: [], properties: [], - validation: null, }); const templates: Templates = { index: () => 'dummy', model: () => 'dummy', - exportInterface: () => 'dummy', - exportEnum: () => 'dummy', - exportType: () => 'dummy', service: () => 'dummy', - validation: () => 'dummy', - type: () => 'dummy', }; writeClientModels(models, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy'); diff --git a/src/utils/writeClientModels.ts b/src/utils/writeClientModels.ts index a89d3402..7379785a 100644 --- a/src/utils/writeClientModels.ts +++ b/src/utils/writeClientModels.ts @@ -5,6 +5,7 @@ import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportModel } from './exportModel'; import { Templates } from './readHandlebarsTemplates'; +import { fixIndentation } from './fixIndentation'; /** * Generate Models using the Handlebar template and write to disk. @@ -18,31 +19,8 @@ export function writeClientModels(models: Map, language: Language const fileName = getFileName(model.name, language); try { const templateData = exportModel(model); - const templateResult = templates.model(templateData, { - partials: { - exportGeneric: templates.exportGeneric, - exportReference: templates.exportReference, - exportInterface: templates.exportInterface, - exportEnum: templates.exportEnum, - exportDictionary: templates.exportDictionary, - exportArray: templates.exportArray, - validation: templates.validation, - validationForGeneric: templates.validationForGeneric, - validationForReference: templates.validationForReference, - validationForEnum: templates.validationForEnum, - validationForInterface: templates.validationForInterface, - validationForDictionary: templates.validationForDictionary, - validationForArray: templates.validationForArray, - type: templates.type, - typeForArray: templates.typeForArray, - typeForDictionary: templates.typeForDictionary, - typeForEnum: templates.typeForEnum, - typeForInterface: templates.typeForInterface, - typeForReference: templates.typeForReference, - typeForGeneric: templates.typeForGeneric, - }, - }); - fs.writeFileSync(path.resolve(outputPath, fileName), templateResult); + const templateResult = templates.model(templateData); + fs.writeFileSync(path.resolve(outputPath, fileName), fixIndentation(templateResult)); } catch (e) { throw new Error(`Could not write model: "${fileName}"`); } diff --git a/src/utils/writeClientServices.spec.ts b/src/utils/writeClientServices.spec.ts index c5aa2678..6b3a8833 100644 --- a/src/utils/writeClientServices.spec.ts +++ b/src/utils/writeClientServices.spec.ts @@ -19,12 +19,7 @@ describe('writeClientServices', () => { const templates: Templates = { index: () => 'dummy', model: () => 'dummy', - exportInterface: () => 'dummy', - exportEnum: () => 'dummy', - exportType: () => 'dummy', service: () => 'dummy', - validation: () => 'dummy', - type: () => 'dummy', }; writeClientServices(services, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy'); diff --git a/src/utils/writeClientServices.ts b/src/utils/writeClientServices.ts index 21e95a9e..3d705f0b 100644 --- a/src/utils/writeClientServices.ts +++ b/src/utils/writeClientServices.ts @@ -5,6 +5,7 @@ import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportService } from './exportService'; import { Templates } from './readHandlebarsTemplates'; +import { fixIndentation } from './fixIndentation'; /** * Generate Services using the Handlebar template and write to disk. @@ -19,7 +20,7 @@ export function writeClientServices(services: Map, language: La try { const templateData = exportService(service); const templateResult = templates.model(templateData); - fs.writeFileSync(path.resolve(outputPath, fileName), templateResult); + fs.writeFileSync(path.resolve(outputPath, fileName), fixIndentation(templateResult)); } catch (e) { throw new Error(`Could not write service: "${fileName}"`); } diff --git a/test/mock/v2/spec.json b/test/mock/v2/spec.json index 6149738d..d5a9cd93 100644 --- a/test/mock/v2/spec.json +++ b/test/mock/v2/spec.json @@ -80,6 +80,16 @@ "$ref": "#/definitions/ModelWithString" } }, + "ArrayWithArray": { + "description": "This is a simple array containing an array", + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/ModelWithString" + } + } + }, "ArrayWithProperties": { "description": "This is a simple array with properties", "type": "array", @@ -119,6 +129,16 @@ } } }, + "DictionaryWithDictionary": { + "description": "This is a string dictionary", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, "DictionaryWithProperties": { "description": "This is a complex dictionary", "type": "object", @@ -197,7 +217,7 @@ } } }, - "ModelWithModelArray": { + "ModelWithArray": { "description": "This is a model with one property containing an array", "type": "object", "properties": { @@ -209,6 +229,18 @@ } } }, + "ModelWithDictionary": { + "description": "This is a model with one property containing a dictionary", + "type": "object", + "properties": { + "prop": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "ModelLink": { "description": "This is a model that can have a template??", "type": "object", @@ -309,6 +341,9 @@ "description": "This is a model that extends another model", "type": "object", "allOf": [ + { + "$ref": "#/definitions/ModelWithString" + }, { "$ref": "#/definitions/ModelThatExtends" }, diff --git a/test/mock/v2/spec.json2 b/test/mock/v2/spec.json2 new file mode 100644 index 00000000..d159be1e --- /dev/null +++ b/test/mock/v2/spec.json2 @@ -0,0 +1,361 @@ +{ + "swagger": "2.0", + "info": { + "version": "v9.0", + "title": "swagger" + }, + "host": "localhost:8080", + "basePath": "/api", + "schemes": [ + "http" + ], + "paths": { + }, + "definitions": { + "SimpleInteger": { + "description": "This is a simple number", + "type": "integer" + }, + "SimpleBoolean": { + "description": "This is a simple boolean", + "type": "boolean" + }, + "SimpleString": { + "description": "This is a simple string", + "type": "string" + }, + "SimpleFile": { + "description": "This is a simple file", + "type": "File" + }, + "SimpleReference": { + "description": "This is a simple reference", + "$ref": "#/definitions/ModelWithString" + }, + "EnumWithStrings": { + "description": "This is a simple enum with strings", + "enum": [ + "Success", + "Warning", + "Error" + ] + }, + "EnumWithNumbers": { + "description": "This is a simple enum with numbers", + "enum": [ + 1, + 2, + 3 + ] + }, + "EnumFromDescription": { + "description": "Success=1,Warning=2,Error=3", + "type": "int" + }, + "ArrayWithNumbers": { + "description": "This is a simple array with numbers", + "type": "array", + "items": { + "type": "integer" + } + }, + "ArrayWithBooleans": { + "description": "This is a simple array with booleans", + "type": "array", + "items": { + "type": "boolean" + } + }, + "ArrayWithStrings": { + "description": "This is a simple array with strings", + "type": "array", + "items": { + "type": "string" + } + }, + "ArrayWithReferences": { + "description": "This is a simple array with references", + "type": "array", + "items": { + "$ref": "#/definitions/ModelWithString" + } + }, + "ArrayWithArray": { + "description": "This is a simple array containing an array", + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/ModelWithString" + } + } + }, + "ArrayWithProperties": { + "description": "This is a simple array with properties", + "type": "array", + "items": { + "type": "object", + "properties": { + "foo": { + "type": "string" + }, + "bar": { + "type": "string" + } + } + } + }, + "DictionaryWithString": { + "description": "This is a string dictionary", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "DictionaryWithReference": { + "description": "This is a string reference", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ModelWithString" + } + }, + "DictionaryWithArray": { + "description": "This is a complex dictionary", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/ModelWithString" + } + } + }, + "DictionaryWithDictionary": { + "description": "This is a string dictionary", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "DictionaryWithProperties": { + "description": "This is a complex dictionary", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "foo": { + "type": "string" + }, + "bar": { + "type": "string" + } + } + } + }, + "ModelWithInteger": { + "description": "This is a model with one number property", + "type": "object", + "properties": { + "prop": { + "description": "This is a simple number property", + "type": "integer" + } + } + }, + "ModelWithBoolean": { + "description": "This is a model with one boolean property", + "type": "object", + "properties": { + "prop": { + "description": "This is a simple boolean property", + "type": "boolean" + } + } + }, + "ModelWithString": { + "description": "This is a model with one string property", + "type": "object", + "properties": { + "prop": { + "description": "This is a simple string property", + "type": "string" + } + } + }, + "ModelWithEnum": { + "description": "This is a model with one enum", + "type": "object", + "properties": { + "prop": { + "description": "This is a simple enum with strings", + "enum": [ + "Success", + "Warning", + "Error" + ] + } + } + }, + "ModelWithEnumFromDescription": { + "description": "This is a model with one enum", + "type": "object", + "properties": { + "prop": { + "type": "integer", + "description": "Success=1,Warning=2,Error=3" + } + } + }, + "ModelWithReference": { + "description": "This is a model with one property containing a reference", + "type": "object", + "properties": { + "prop": { + "$ref": "#/definitions/ModelWithString" + } + } + }, + "ModelWithArray": { + "description": "This is a model with one property containing an array", + "type": "object", + "properties": { + "prop": { + "type": "array", + "items": { + "$ref": "#/definitions/ModelWithString" + } + } + } + }, + "ModelWithDictionary": { + "description": "This is a model with one property containing a dictionary", + "type": "object", + "properties": { + "prop": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "ModelLink": { + "description": "This is a model that can have a template??", + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "ModelWithLink": { + "description": "This is a model that can have a template??", + "type": "object", + "properties": { + "prop": { + "$ref": "#/definitions/ModelLink[ModelWithString]" + } + } + }, + "ModelWithCircularReference": { + "description": "This is a model with one property containing a circular reference", + "type": "object", + "properties": { + "prop": { + "$ref": "#/definitions/ModelWithCircularReference" + } + } + }, + "ModelWithNestedProperties": { + "description": "This is a model with one nested property", + "type": "object", + "properties": { + "first": { + "type": "object", + "properties": { + "second": { + "type": "object", + "properties": { + "third": { + "type": "string" + } + } + } + } + } + } + }, + "ModelWithDuplicateProperties": { + "description": "This is a model with duplicated properties", + "type": "object", + "properties": { + "prop": { + "$ref": "#/definitions/ModelWithString" + }, + "prop": { + "$ref": "#/definitions/ModelWithString" + }, + "prop": { + "$ref": "#/definitions/ModelWithString" + } + } + }, + "ModelWithDuplicateImports": { + "description": "This is a model with duplicated imports", + "type": "object", + "properties": { + "propA": { + "$ref": "#/definitions/ModelWithString" + }, + "propB": { + "$ref": "#/definitions/ModelWithString" + }, + "propC": { + "$ref": "#/definitions/ModelWithString" + } + } + }, + "ModelThatExtends": { + "description": "This is a model that extends another model", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/ModelWithString" + }, + { + "type": "object", + "properties": { + "propExtendsA": { + "type": "string" + }, + "propExtendsB": { + "$ref": "#/definitions/ModelWithString" + } + } + } + ] + }, + "ModelThatExtendsExtends": { + "description": "This is a model that extends another model", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/ModelThatExtends" + }, + { + "type": "object", + "properties": { + "propExtendsC": { + "type": "string" + }, + "propExtendsD": { + "$ref": "#/definitions/ModelWithString" + } + } + } + ] + } + } +}