diff --git a/.editorconfig b/.editorconfig index d741e40b..cc133d0c 100755 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,6 @@ root = true end_of_line = lf charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true +insert_final_newline = false indent_style = space indent_size = 4 diff --git a/src/client/interfaces/Model.d.ts b/src/client/interfaces/Model.d.ts index ff2d07ba..89beab3c 100644 --- a/src/client/interfaces/Model.d.ts +++ b/src/client/interfaces/Model.d.ts @@ -5,15 +5,16 @@ export interface Model { name: string; type: string; base: string; - template?: string; - description?: string; - readOnly?: boolean; - required?: boolean; - nullable?: boolean; + link: Model | null; + template: string | null; + description: string | null; + readOnly: boolean; + required: boolean; + nullable: boolean; imports: string[]; extends: string[]; enum: Enum[]; enums: Model[]; properties: Model[]; - validation?: Validation; + validation: Validation | null; } diff --git a/src/client/interfaces/Operation.d.ts b/src/client/interfaces/Operation.d.ts index ee1a5abc..59c91f10 100644 --- a/src/client/interfaces/Operation.d.ts +++ b/src/client/interfaces/Operation.d.ts @@ -4,8 +4,8 @@ import { OperationParameters } from './OperationParameters'; export interface Operation extends OperationParameters { service: string; name: string; - summary?: string; - description?: string; + summary: string | null; + description: string | null; deprecated: boolean; method: string; path: string; diff --git a/src/client/interfaces/OperationParameter.d.ts b/src/client/interfaces/OperationParameter.d.ts index 5cdfbd73..e2e5db91 100644 --- a/src/client/interfaces/OperationParameter.d.ts +++ b/src/client/interfaces/OperationParameter.d.ts @@ -4,9 +4,9 @@ export interface OperationParameter { name: string; type: string; base: string; - template?: string; - description?: string; - default?: any; + template: string | null; + description: string | null; + default: any; required: boolean; nullable: boolean; imports: string[]; diff --git a/src/client/interfaces/OperationParameters.d.ts b/src/client/interfaces/OperationParameters.d.ts index abff15ce..775f5197 100644 --- a/src/client/interfaces/OperationParameters.d.ts +++ b/src/client/interfaces/OperationParameters.d.ts @@ -7,5 +7,5 @@ export interface OperationParameters { parametersQuery: OperationParameter[]; parametersForm: OperationParameter[]; parametersHeader: OperationParameter[]; - parametersBody?: OperationParameter; + parametersBody: OperationParameter | null; } diff --git a/src/client/interfaces/OperationResponse.d.ts b/src/client/interfaces/OperationResponse.d.ts index e6d582ba..5c8d1f92 100644 --- a/src/client/interfaces/OperationResponse.d.ts +++ b/src/client/interfaces/OperationResponse.d.ts @@ -3,6 +3,6 @@ export interface OperationResponse { text: string; type: string; base: string; - template?: string; + template: string | null; imports: string[]; } diff --git a/src/client/interfaces/Type.d.ts b/src/client/interfaces/Type.d.ts index 18c1c23c..5445eea5 100644 --- a/src/client/interfaces/Type.d.ts +++ b/src/client/interfaces/Type.d.ts @@ -1,6 +1,6 @@ export interface Type { type: string; base: string; - template?: string; + template: string | null; imports: string[]; } diff --git a/src/client/interfaces/Validation.d.ts b/src/client/interfaces/Validation.d.ts index cf2cece6..cbec194d 100644 --- a/src/client/interfaces/Validation.d.ts +++ b/src/client/interfaces/Validation.d.ts @@ -1,6 +1,6 @@ export interface Validation { type: 'ref' | 'type' | 'enum' | 'array' | 'dictionary' | 'property' | 'model'; - childType?: string | null; - childBase?: string | null; - childValidation?: Validation; + childType: string | null; + childBase: string | null; + childValidation: Validation | null; } diff --git a/src/index.ts b/src/index.ts index 1ebbbe3d..721af00a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,12 @@ import * as path from 'path'; import { parse as parseV2 } from './openApi/v2'; import { parse as parseV3 } from './openApi/v3'; -import { readHandlebarsTemplates, Templates } from './utils/readHandlebarsTemplates'; +import { readHandlebarsTemplates } from './utils/readHandlebarsTemplates'; import { getOpenApiSpec } from './utils/getOpenApiSpec'; import { writeClient } from './utils/writeClient'; import * as os from 'os'; import * as chalk from 'chalk'; import { getOpenApiVersion, OpenApiVersion } from './utils/getOpenApiVersion'; -import { Client } from './client/interfaces/Client'; export enum Language { TYPESCRIPT = 'typescript', diff --git a/src/openApi/v2/parser/constants.ts b/src/openApi/v2/parser/constants.ts index f6daba75..04041dd7 100644 --- a/src/openApi/v2/parser/constants.ts +++ b/src/openApi/v2/parser/constants.ts @@ -1,7 +1,6 @@ export enum PrimaryType { FILE = 'File', OBJECT = 'any', - ARRAY = 'any[]', BOOLEAN = 'boolean', NUMBER = 'number', STRING = 'string', @@ -14,8 +13,6 @@ export const TYPE_MAPPINGS = new Map([ ['binary', PrimaryType.FILE], ['any', PrimaryType.OBJECT], ['object', PrimaryType.OBJECT], - ['list', PrimaryType.ARRAY], - ['array', PrimaryType.ARRAY], ['boolean', PrimaryType.BOOLEAN], ['byte', PrimaryType.NUMBER], ['int', PrimaryType.NUMBER], diff --git a/src/openApi/v2/parser/getComment.ts b/src/openApi/v2/parser/getComment.ts index e72da5a2..d9af7afa 100644 --- a/src/openApi/v2/parser/getComment.ts +++ b/src/openApi/v2/parser/getComment.ts @@ -1,6 +1,6 @@ import { EOL } from 'os'; -export function getComment(comment?: string): string | undefined { +export function getComment(comment?: string): string | null { if (comment) { return comment .split(/(\r\n|\n|\r)+/g) @@ -9,5 +9,5 @@ export function getComment(comment?: string): string | undefined { .join(EOL) .replace(/(\r\n|\n|\r)+/g, '$1 * '); } - return undefined; + return null; } diff --git a/src/openApi/v2/parser/getMappedType.spec.ts b/src/openApi/v2/parser/getMappedType.spec.ts index 23974975..1c9c89f0 100644 --- a/src/openApi/v2/parser/getMappedType.spec.ts +++ b/src/openApi/v2/parser/getMappedType.spec.ts @@ -3,8 +3,6 @@ import { getMappedType } from './getMappedType'; describe('getMappedType', () => { it('should map types to the basics', () => { expect(getMappedType('File')).toEqual('File'); - expect(getMappedType('Array')).toEqual('any[]'); - expect(getMappedType('List')).toEqual('any[]'); expect(getMappedType('String')).toEqual('string'); expect(getMappedType('date')).toEqual('string'); expect(getMappedType('date-time')).toEqual('string'); diff --git a/src/openApi/v2/parser/getModel.ts b/src/openApi/v2/parser/getModel.ts index 44fe7146..f01e7349 100644 --- a/src/openApi/v2/parser/getModel.ts +++ b/src/openApi/v2/parser/getModel.ts @@ -7,7 +7,6 @@ import { PrimaryType } from './constants'; import { getEnumType } from './getEnumType'; import { getEnum } from './getEnum'; import { getEnumFromDescription } from './getEnumFromDescription'; -import { getTypeFromProperties } from './getTypeFromProperties'; import { getModelProperties } from './getModelProperties'; export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: string = ''): Model { @@ -15,13 +14,18 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri name, type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, + template: null, + link: null, description: getComment(definition.description), - readOnly: definition.readOnly, + readOnly: definition.readOnly || false, + required: false, + nullable: false, imports: [], extends: [], enum: [], enums: [], properties: [], + validation: null, }; if (definition.$ref) { @@ -32,6 +36,9 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.imports.push(...definitionRef.imports); result.validation = { type: 'ref', + childType: null, + childBase: null, + childValidation: null, }; return result; } @@ -45,10 +52,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.enum.push(...enumerators); result.validation = { type: 'enum', + childType: null, + childBase: null, + childValidation: null, }; return result; } - return result; } // If the param is a enum then return the values as an inline type. @@ -60,10 +69,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.enum.push(...enumerators); result.validation = { type: 'enum', + childType: null, + childBase: null, + childValidation: null, }; return result; } - return result; } // If the schema is an Array type, we check for the child type, @@ -71,20 +82,22 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri if (definition.type === 'array' && definition.items) { if (definition.items.$ref) { const arrayItems = getType(definition.items.$ref); - result.type = `${arrayItems.type}[]`; - result.base = arrayItems.base; + result.type = `Array<${arrayItems.type}>`; + result.base = 'Array'; result.template = arrayItems.template; result.imports.push(...arrayItems.imports); result.validation = { type: 'array', childType: arrayItems.type, childBase: arrayItems.base, + childValidation: null, }; } else { const arrayItems = getModel(openApi, definition.items); - result.type = `${arrayItems.type}[]`; - result.base = arrayItems.base; + result.type = `Array<${arrayItems.type}>`; + result.base = 'Array'; result.template = arrayItems.template; + result.link = arrayItems; result.imports.push(...arrayItems.imports); result.validation = { type: 'array', @@ -110,12 +123,14 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri type: 'dictionary', childType: additionalProperties.type, childBase: additionalProperties.base, + childValidation: null, }; } else { const additionalProperties = getModel(openApi, definition.additionalProperties); result.type = `Dictionary<${additionalProperties.type}>`; result.base = 'Dictionary'; result.template = additionalProperties.type; + result.link = additionalProperties; result.imports.push(...additionalProperties.imports); result.validation = { type: 'dictionary', @@ -143,10 +158,13 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri }); } }); - result.type = getTypeFromProperties(result.properties); + result.type = PrimaryType.OBJECT; result.base = PrimaryType.OBJECT; result.validation = { type: 'model', + childType: null, + childBase: null, + childValidation: null, }; } @@ -156,10 +174,13 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.properties.push(property); result.imports.push(...property.imports); }); - result.type = getTypeFromProperties(result.properties); + result.type = PrimaryType.OBJECT; result.base = PrimaryType.OBJECT; result.validation = { type: 'model', + childType: null, + childBase: null, + childValidation: null, }; return result; } @@ -173,6 +194,9 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.imports.push(...definitionType.imports); result.validation = { type: 'type', + childType: null, + childBase: null, + childValidation: null, }; return result; } diff --git a/src/openApi/v2/parser/getModelProperties.ts b/src/openApi/v2/parser/getModelProperties.ts index 566568e6..352fc543 100644 --- a/src/openApi/v2/parser/getModelProperties.ts +++ b/src/openApi/v2/parser/getModelProperties.ts @@ -10,8 +10,8 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema): for (const propertyName in definition.properties) { if (definition.properties.hasOwnProperty(propertyName)) { const property = definition.properties[propertyName]; - const propertyRequired = definition.required && definition.required.includes(propertyName); - const propertyReadOnly = property.readOnly; + const propertyRequired = !!(definition.required && definition.required.includes(propertyName)); + const propertyReadOnly = !!property.readOnly; if (property.$ref) { const prop = getType(property.$ref); result.push({ @@ -19,9 +19,11 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema): type: prop.type, base: prop.base, template: prop.template, + link: null, description: getComment(property.description), readOnly: propertyReadOnly, required: propertyRequired, + nullable: false, imports: prop.imports, extends: [], enum: [], @@ -31,6 +33,7 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema): type: 'property', childType: prop.type, childBase: prop.base, + childValidation: null, }, }); } else { @@ -40,9 +43,11 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema): type: prop.type, base: prop.base, template: prop.template, + link: prop.link, description: getComment(property.description), readOnly: propertyReadOnly, required: propertyRequired, + nullable: false, imports: prop.imports, extends: prop.extends, enum: prop.enum, diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index 885b9267..21752df5 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -33,6 +33,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op: parametersQuery: [], parametersForm: [], parametersHeader: [], + parametersBody: null, imports: [], errors: [], result: PrimaryType.VOID, diff --git a/src/openApi/v2/parser/getOperationParameter.ts b/src/openApi/v2/parser/getOperationParameter.ts index 712db409..c8b6d14e 100644 --- a/src/openApi/v2/parser/getOperationParameter.ts +++ b/src/openApi/v2/parser/getOperationParameter.ts @@ -12,6 +12,7 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame name: getOperationParameterName(parameter.name), type: 'any', base: 'any', + template: null, description: getComment(parameter.description), default: parameter.default, required: parameter.required || false, diff --git a/src/openApi/v2/parser/getOperationParameters.ts b/src/openApi/v2/parser/getOperationParameters.ts index c71c1247..92eb292f 100644 --- a/src/openApi/v2/parser/getOperationParameters.ts +++ b/src/openApi/v2/parser/getOperationParameters.ts @@ -17,6 +17,7 @@ export function getOperationParameters(openApi: OpenApi, parameters: OpenApiPara parametersQuery: [], parametersForm: [], parametersHeader: [], + parametersBody: null, }; // Iterate over the parameters diff --git a/src/openApi/v2/parser/getOperationResponse.ts b/src/openApi/v2/parser/getOperationResponse.ts index 800facc3..9e018438 100644 --- a/src/openApi/v2/parser/getOperationResponse.ts +++ b/src/openApi/v2/parser/getOperationResponse.ts @@ -13,6 +13,7 @@ export function getOperationResponse(responses: OperationResponse[]): OperationR text: '', type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, + template: null, imports: [], }; } diff --git a/src/openApi/v2/parser/getType.spec.ts b/src/openApi/v2/parser/getType.spec.ts index df497783..e4bca943 100644 --- a/src/openApi/v2/parser/getType.spec.ts +++ b/src/openApi/v2/parser/getType.spec.ts @@ -5,7 +5,7 @@ describe('getType', () => { const type = getType('int'); expect(type.type).toEqual('number'); expect(type.base).toEqual('number'); - expect(type.template).toEqual(undefined); + expect(type.template).toEqual(null); expect(type.imports).toEqual([]); }); @@ -13,15 +13,15 @@ describe('getType', () => { const type = getType('String'); expect(type.type).toEqual('string'); expect(type.base).toEqual('string'); - expect(type.template).toEqual(undefined); + expect(type.template).toEqual(null); expect(type.imports).toEqual([]); }); it('should convert string array', () => { const type = getType('Array[String]'); - expect(type.type).toEqual('string[]'); + expect(type.type).toEqual('Array'); expect(type.base).toEqual('string'); - expect(type.template).toEqual(undefined); + expect(type.template).toEqual(null); expect(type.imports).toEqual([]); }); @@ -53,7 +53,7 @@ describe('getType', () => { const type = getType('#/definitions/Link', 'Link'); expect(type.type).toEqual('T'); expect(type.base).toEqual('T'); - expect(type.template).toEqual(undefined); + expect(type.template).toEqual(null); expect(type.imports).toEqual([]); }); }); diff --git a/src/openApi/v2/parser/getType.ts b/src/openApi/v2/parser/getType.ts index 8734f797..6caffb25 100644 --- a/src/openApi/v2/parser/getType.ts +++ b/src/openApi/v2/parser/getType.ts @@ -12,6 +12,7 @@ export function getType(value?: string, template?: string): Type { const result: Type = { type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, + template: null, imports: [], }; @@ -27,13 +28,12 @@ export function getType(value?: string, template?: string): Type { const match2 = getType(matches[2]); // If the first match is a generic array then construct a correct array type, - // for example the type "Array[Model]" becomes "Model[]". - if (match1.type === PrimaryType.ARRAY) { - result.type = `${match2.type}[]`; - result.base = `${match2.type}`; + // for example the type "Array[Model]" becomes "Array". + if (match1.type === 'Array') { + result.type = `Array<${match2.type}>`; + result.base = match2.type; match1.imports = []; } else if (match2.type === '') { - // Primary types like number[] or string[] result.type = match1.type; result.base = match1.type; result.template = match1.type; diff --git a/src/openApi/v2/parser/isPrimaryType.spec.ts b/src/openApi/v2/parser/isPrimaryType.spec.ts index 90edc5bf..ed3e1774 100644 --- a/src/openApi/v2/parser/isPrimaryType.spec.ts +++ b/src/openApi/v2/parser/isPrimaryType.spec.ts @@ -6,7 +6,6 @@ describe('isPrimaryType', () => { expect(isPrimaryType('boolean')).toBeTruthy(); expect(isPrimaryType('string')).toBeTruthy(); expect(isPrimaryType('any')).toBeTruthy(); - expect(isPrimaryType('any[]')).toBeTruthy(); expect(isPrimaryType('void')).toBeTruthy(); expect(isPrimaryType('null')).toBeTruthy(); expect(isPrimaryType('Array')).toBeFalsy(); diff --git a/src/openApi/v2/parser/isPrimaryType.ts b/src/openApi/v2/parser/isPrimaryType.ts index 6bba755c..dfb9e778 100644 --- a/src/openApi/v2/parser/isPrimaryType.ts +++ b/src/openApi/v2/parser/isPrimaryType.ts @@ -7,7 +7,6 @@ import { PrimaryType } from './constants'; export function isPrimaryType(type: string): type is PrimaryType { switch (type.toLowerCase()) { case PrimaryType.FILE: - case PrimaryType.ARRAY: case PrimaryType.OBJECT: case PrimaryType.BOOLEAN: case PrimaryType.NUMBER: diff --git a/src/templates/typescript/exportEnum.hbs b/src/templates/typescript/exportEnum.hbs new file mode 100644 index 00000000..36665ecf --- /dev/null +++ b/src/templates/typescript/exportEnum.hbs @@ -0,0 +1,20 @@ +export enum {{{name}}} { + {{#each symbols}} + {{{name}}} = {{{value}}}, + {{/each}} +} +{{#if validation}} + +export namespace {{{name}}} { + + 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 }); + } +} +{{/if}} \ No newline at end of file diff --git a/src/templates/typescript/exportInterface.hbs b/src/templates/typescript/exportInterface.hbs new file mode 100644 index 00000000..e1c2b49a --- /dev/null +++ b/src/templates/typescript/exportInterface.hbs @@ -0,0 +1,38 @@ +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 namespace {{{name}}} { + + {{#each enums}} + {{#if description}} + /** + * {{{description}}} + */ + {{/if}} + export enum {{{name}}} { + {{#each values}} + {{{name}}} = {{{value}}}, + {{/each}} + } + + {{/each}} + {{#if validation}} + 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 }); + } + {{/if}} +} \ No newline at end of file diff --git a/src/templates/typescript/exportType.hbs b/src/templates/typescript/exportType.hbs new file mode 100644 index 00000000..3478bcc6 --- /dev/null +++ b/src/templates/typescript/exportType.hbs @@ -0,0 +1,16 @@ +export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; +{{#if validation}} + +export namespace {{{name}}} { + + 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 }); + } +} +{{/if}} \ No newline at end of file diff --git a/src/templates/typescript/index.hbs b/src/templates/typescript/index.hbs index df73b249..0a412286 100644 --- a/src/templates/typescript/index.hbs +++ b/src/templates/typescript/index.hbs @@ -18,4 +18,4 @@ export { {{{this}}} } from './models/{{{this}}}'; {{#each services}} export { {{{this}}} } from './services/{{{this}}}'; {{/each}} -{{/if}} +{{/if}} \ No newline at end of file diff --git a/src/templates/typescript/model.hbs b/src/templates/typescript/model.hbs index d687a530..6100b5bf 100644 --- a/src/templates/typescript/model.hbs +++ b/src/templates/typescript/model.hbs @@ -17,83 +17,10 @@ import * as yup from 'yup'; * {{{description}}} */ {{/if}} -{{#if isInterface}} -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 namespace {{{name}}} { - - {{#each enums}} - {{#if description}} - /** - * {{{description}}} - */ - {{/if}} - export enum {{{name}}} { - {{#each values}} - {{{name}}} = {{{value}}}, - {{/each}} - } - - {{/each}} - {{#if validation}} - 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 }); - } - {{/if}} -} -{{/if}} -{{#if isType}} -export type {{{name}}} = {{{type}}}{{#if nullable}} | null{{/if}}; -{{#if validation}} - -export namespace {{{name}}} { - - 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 }); - } -} -{{/if}} -{{/if}} -{{#if isEnum}} -export enum {{{name}}} { - {{#each symbols}} - {{{name}}} = {{{value}}}, - {{/each}} -} -{{#if validation}} - -export namespace {{{name}}} { - - 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 }); - } -} -{{/if}} -{{/if}} +{{#if properties}} +{{> exportInterface}} +{{else if enum}} +{{> exportEnum}} +{{else}} +{{> exportType}} +{{/if}} \ No newline at end of file diff --git a/src/templates/typescript/service.hbs b/src/templates/typescript/service.hbs index adcc567e..27244d57 100644 --- a/src/templates/typescript/service.hbs +++ b/src/templates/typescript/service.hbs @@ -81,4 +81,4 @@ export class {{{name}}} { } {{/each}} -} +} \ No newline at end of file diff --git a/src/templates/typescript/type.hbs b/src/templates/typescript/type.hbs new file mode 100644 index 00000000..fbbe83b1 --- /dev/null +++ b/src/templates/typescript/type.hbs @@ -0,0 +1,5 @@ +{{#if properties}}{ +{{#each properties}} +{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type}}{{#if nullable}} | null{{/if}}, +{{/each}} +}{{else if link}}{{{base}}}<{{>type link}}>{{else}}{{{type}}}{{/if}} \ No newline at end of file diff --git a/src/templates/typescript/validation.hbs b/src/templates/typescript/validation.hbs new file mode 100644 index 00000000..64793b21 --- /dev/null +++ b/src/templates/typescript/validation.hbs @@ -0,0 +1 @@ +yup.mixed() \ No newline at end of file diff --git a/src/utils/exportModel.ts b/src/utils/exportModel.ts index c638c309..3fed30bf 100644 --- a/src/utils/exportModel.ts +++ b/src/utils/exportModel.ts @@ -1,6 +1,6 @@ import { Model } from '../client/interfaces/Model'; -export function exportModel(model: Model): any { +export function exportModel(model: Model): Model { return { ...model, imports: model.imports @@ -16,8 +16,17 @@ export function exportModel(model: Model): any { 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); + }), + enums: model.enums .map(property => exportModel(property)) - .filter(property => property.enum.length) .filter((property, index, arr) => { return arr.findIndex(item => item.name === property.name) === index; }) @@ -26,14 +35,9 @@ export function exportModel(model: Model): any { const nameB = b.name.toLowerCase(); return nameA.localeCompare(nameB); }), - enums: model.properties - .map(property => exportModel(property)) - .filter(property => !property.enum.length) - .filter((property, index, arr) => { - return arr.findIndex(item => item.name === property.name) === index; - }) - .filter((property, index, arr) => { - return arr.findIndex(item => item.name === property.name) === index; + enum: model.enum + .filter((enumerator, index, arr) => { + return arr.findIndex(item => item.name === enumerator.name) === index; }) .sort((a, b) => { const nameA = a.name.toLowerCase(); diff --git a/src/utils/exportService.ts b/src/utils/exportService.ts index 5882ebb3..c51f5061 100644 --- a/src/utils/exportService.ts +++ b/src/utils/exportService.ts @@ -1,6 +1,6 @@ import { Service } from '../client/interfaces/Service'; -export function exportService(service: Service): any { +export function exportService(service: Service): Service { const names = new Map(); return { ...service, diff --git a/src/utils/getModelNames.spec.ts b/src/utils/getModelNames.spec.ts index 432c3cc7..e7a22d15 100644 --- a/src/utils/getModelNames.spec.ts +++ b/src/utils/getModelNames.spec.ts @@ -8,37 +8,52 @@ describe('getModelNames', () => { name: 'John', type: 'John', base: 'John', + template: null, + link: null, + description: null, readOnly: false, required: false, nullable: false, imports: [], extends: [], enum: [], + enums: [], properties: [], + validation: null, }); models.set('Jane', { name: 'Jane', type: 'Jane', base: 'Jane', + template: null, + link: null, + description: null, readOnly: false, required: false, nullable: false, imports: [], extends: [], enum: [], + enums: [], properties: [], + validation: null, }); models.set('Doe', { name: 'Doe', type: 'Doe', base: 'Doe', + template: null, + link: null, + description: null, readOnly: false, required: false, nullable: false, imports: [], extends: [], enum: [], + enums: [], properties: [], + validation: null, }); expect(getModelNames(new Map())).toEqual([]); diff --git a/src/utils/readHandlebarsTemplates.ts b/src/utils/readHandlebarsTemplates.ts index 4b0a5569..f8370b81 100644 --- a/src/utils/readHandlebarsTemplates.ts +++ b/src/utils/readHandlebarsTemplates.ts @@ -6,7 +6,12 @@ import * as path from 'path'; export interface Templates { index: handlebars.TemplateDelegate; model: handlebars.TemplateDelegate; + exportInterface: handlebars.TemplateDelegate; + exportEnum: handlebars.TemplateDelegate; + exportType: handlebars.TemplateDelegate; service: handlebars.TemplateDelegate; + validation: handlebars.TemplateDelegate; + type: handlebars.TemplateDelegate; } /** @@ -17,13 +22,23 @@ export interface Templates { export function readHandlebarsTemplates(language: Language): Templates { const pathTemplateIndex = path.resolve(__dirname, `../../src/templates/${language}/index.hbs`); const pathTemplateModel = path.resolve(__dirname, `../../src/templates/${language}/model.hbs`); + const pathTemplateExportInterface = path.resolve(__dirname, `../../src/templates/${language}/exportInterface.hbs`); + const pathTemplateExportEnum = path.resolve(__dirname, `../../src/templates/${language}/exportEnum.hbs`); + const pathTemplateExportType = path.resolve(__dirname, `../../src/templates/${language}/exportType.hbs`); const pathTemplateService = path.resolve(__dirname, `../../src/templates/${language}/service.hbs`); + const pathTemplateValidation = path.resolve(__dirname, `../../src/templates/${language}/validation.hbs`); + const pathTemplateType = path.resolve(__dirname, `../../src/templates/${language}/type.hbs`); try { return { index: readHandlebarsTemplate(pathTemplateIndex), model: readHandlebarsTemplate(pathTemplateModel), + exportInterface: readHandlebarsTemplate(pathTemplateExportInterface), + exportEnum: readHandlebarsTemplate(pathTemplateExportEnum), + exportType: readHandlebarsTemplate(pathTemplateExportType), service: readHandlebarsTemplate(pathTemplateService), + validation: readHandlebarsTemplate(pathTemplateValidation), + type: readHandlebarsTemplate(pathTemplateType), }; } catch (e) { throw e; diff --git a/src/utils/writeClient.spec.ts b/src/utils/writeClient.spec.ts index 9f0a4cc5..76fc3e59 100644 --- a/src/utils/writeClient.spec.ts +++ b/src/utils/writeClient.spec.ts @@ -31,6 +31,7 @@ describe('writeClient', () => { const templates: Templates = { index: () => 'dummy', model: () => 'dummy', + interface: () => 'dummy', service: () => 'dummy', }; diff --git a/src/utils/writeClient.ts b/src/utils/writeClient.ts index 222e6d96..fa37966f 100644 --- a/src/utils/writeClient.ts +++ b/src/utils/writeClient.ts @@ -53,9 +53,9 @@ export function writeClient(client: Client, language: Language, templates: Templ // Write the client files try { - writeClientIndex(client, language, templates.index, outputPath); - writeClientModels(client.models, language, templates.model, outputPathModels); - writeClientServices(client.services, language, templates.service, outputPathServices); + writeClientIndex(client, language, templates, outputPath); + writeClientModels(client.models, language, templates, outputPathModels); + writeClientServices(client.services, language, templates, outputPathServices); } catch (e) { throw e; } diff --git a/src/utils/writeClientIndex.spec.ts b/src/utils/writeClientIndex.spec.ts index 67ac40ab..ed491b08 100644 --- a/src/utils/writeClientIndex.spec.ts +++ b/src/utils/writeClientIndex.spec.ts @@ -4,6 +4,7 @@ import { Client } from '../client/interfaces/Client'; import { Language } from '../index'; import { Model } from '../client/interfaces/Model'; import { Service } from '../client/interfaces/Service'; +import { Templates } from './readHandlebarsTemplates'; jest.mock('fs'); @@ -17,8 +18,17 @@ describe('writeClientIndex', () => { models: new Map(), services: new Map(), }; - const template = () => 'dummy'; - writeClientIndex(client, Language.TYPESCRIPT, template, '/'); + 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/writeClientIndex.ts b/src/utils/writeClientIndex.ts index c2b2913c..80a7e793 100644 --- a/src/utils/writeClientIndex.ts +++ b/src/utils/writeClientIndex.ts @@ -1,11 +1,11 @@ import { Client } from '../client/interfaces/Client'; -import * as handlebars from 'handlebars'; import * as fs from 'fs'; import * as path from 'path'; import { getModelNames } from './getModelNames'; import { getServiceNames } from './getServiceNames'; import { Language } from '../index'; import { getFileName } from './getFileName'; +import { Templates } from './readHandlebarsTemplates'; /** * Generate the OpenAPI client index file using the Handlebar template and write it to disk. @@ -13,15 +13,15 @@ import { getFileName } from './getFileName'; * library. But yuo can also import individual models and services directly. * @param client: Client object, containing, models, schemas and services. * @param language: The output language (Typescript or javascript). - * @param template: The template that is used to write the file. + * @param templates: The loaded handlebar templates. * @param outputPath: */ -export function writeClientIndex(client: Client, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void { +export function writeClientIndex(client: Client, language: Language, templates: Templates, outputPath: string): void { const fileName = getFileName('index', language); try { fs.writeFileSync( path.resolve(outputPath, fileName), - template({ + templates.index({ server: client.server, version: client.version, models: getModelNames(client.models), diff --git a/src/utils/writeClientModels.spec.ts b/src/utils/writeClientModels.spec.ts index d15de3f7..d73b7d27 100644 --- a/src/utils/writeClientModels.spec.ts +++ b/src/utils/writeClientModels.spec.ts @@ -2,6 +2,7 @@ import { writeClientModels } from './writeClientModels'; import * as fs from 'fs'; import { Model } from '../client/interfaces/Model'; import { Language } from '../index'; +import { Templates } from './readHandlebarsTemplates'; jest.mock('fs'); @@ -14,16 +15,30 @@ describe('writeClientModels', () => { name: 'Item', type: 'Item', base: 'Item', + template: null, + link: null, + description: null, readOnly: false, required: false, nullable: false, imports: [], extends: [], enum: [], + enums: [], properties: [], + validation: null, }); - const template = () => 'dummy'; - writeClientModels(models, Language.TYPESCRIPT, template, '/'); + 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 ad6f186c..e323b123 100644 --- a/src/utils/writeClientModels.ts +++ b/src/utils/writeClientModels.ts @@ -1,27 +1,35 @@ import * as fs from 'fs'; -import * as handlebars from 'handlebars'; import { Model } from '../client/interfaces/Model'; import * as path from 'path'; import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportModel } from './exportModel'; +import { Templates } from './readHandlebarsTemplates'; /** * Generate Models using the Handlebar template and write to disk. * @param models: Array of Models to write. * @param language: The output language (Typescript or javascript). - * @param template: The template that is used to write the file. + * @param templates: The loaded handlebar templates. * @param outputPath: */ -export function writeClientModels(models: Map, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void { +export function writeClientModels(models: Map, language: Language, templates: Templates, outputPath: string): void { models.forEach(model => { const fileName = getFileName(model.name, language); - try { - const templateData = exportModel(model); - const templateResult = template(templateData); - fs.writeFileSync(path.resolve(outputPath, fileName), templateResult); - } catch (e) { - throw new Error(`Could not write model: "${fileName}"`); - } + // try { + const templateData = exportModel(model); + const templateResult = templates.model(templateData, { + partials: { + exportInterface: templates.exportInterface, + exportEnum: templates.exportEnum, + exportType: templates.exportType, + validation: templates.validation, + type: templates.type, + }, + }); + fs.writeFileSync(path.resolve(outputPath, fileName), 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 9caa754b..c5aa2678 100644 --- a/src/utils/writeClientServices.spec.ts +++ b/src/utils/writeClientServices.spec.ts @@ -2,6 +2,7 @@ import { writeClientServices } from './writeClientServices'; import * as fs from 'fs'; import { Service } from '../client/interfaces/Service'; import { Language } from '../index'; +import { Templates } from './readHandlebarsTemplates'; jest.mock('fs'); @@ -15,8 +16,17 @@ describe('writeClientServices', () => { operations: [], imports: [], }); - const template = () => 'dummy'; - writeClientServices(services, Language.TYPESCRIPT, template, '/'); + 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 2fa7a883..21e95a9e 100644 --- a/src/utils/writeClientServices.ts +++ b/src/utils/writeClientServices.ts @@ -1,24 +1,24 @@ import * as fs from 'fs'; -import * as handlebars from 'handlebars'; import * as path from 'path'; import { Service } from '../client/interfaces/Service'; import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportService } from './exportService'; +import { Templates } from './readHandlebarsTemplates'; /** * Generate Services using the Handlebar template and write to disk. * @param services: Array of Services to write. * @param language: The output language (Typescript or javascript). - * @param template: The template that is used to write the file. + * @param templates: The loaded handlebar templates. * @param outputPath: */ -export function writeClientServices(services: Map, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void { +export function writeClientServices(services: Map, language: Language, templates: Templates, outputPath: string): void { services.forEach(service => { const fileName = getFileName(service.name, language); try { const templateData = exportService(service); - const templateResult = template(templateData); + const templateResult = templates.model(templateData); fs.writeFileSync(path.resolve(outputPath, fileName), templateResult); } catch (e) { throw new Error(`Could not write service: "${fileName}"`);