mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Working on validation
This commit is contained in:
parent
433de4437e
commit
fd0d909faa
@ -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
|
||||
|
||||
13
src/client/interfaces/Model.d.ts
vendored
13
src/client/interfaces/Model.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
||||
4
src/client/interfaces/Operation.d.ts
vendored
4
src/client/interfaces/Operation.d.ts
vendored
@ -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;
|
||||
|
||||
@ -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[];
|
||||
|
||||
@ -7,5 +7,5 @@ export interface OperationParameters {
|
||||
parametersQuery: OperationParameter[];
|
||||
parametersForm: OperationParameter[];
|
||||
parametersHeader: OperationParameter[];
|
||||
parametersBody?: OperationParameter;
|
||||
parametersBody: OperationParameter | null;
|
||||
}
|
||||
|
||||
2
src/client/interfaces/OperationResponse.d.ts
vendored
2
src/client/interfaces/OperationResponse.d.ts
vendored
@ -3,6 +3,6 @@ export interface OperationResponse {
|
||||
text: string;
|
||||
type: string;
|
||||
base: string;
|
||||
template?: string;
|
||||
template: string | null;
|
||||
imports: string[];
|
||||
}
|
||||
|
||||
2
src/client/interfaces/Type.d.ts
vendored
2
src/client/interfaces/Type.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
export interface Type {
|
||||
type: string;
|
||||
base: string;
|
||||
template?: string;
|
||||
template: string | null;
|
||||
imports: string[];
|
||||
}
|
||||
|
||||
6
src/client/interfaces/Validation.d.ts
vendored
6
src/client/interfaces/Validation.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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<string, PrimaryType>([
|
||||
['binary', PrimaryType.FILE],
|
||||
['any', PrimaryType.OBJECT],
|
||||
['object', PrimaryType.OBJECT],
|
||||
['list', PrimaryType.ARRAY],
|
||||
['array', PrimaryType.ARRAY],
|
||||
['boolean', PrimaryType.BOOLEAN],
|
||||
['byte', PrimaryType.NUMBER],
|
||||
['int', PrimaryType.NUMBER],
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -33,6 +33,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
|
||||
parametersQuery: [],
|
||||
parametersForm: [],
|
||||
parametersHeader: [],
|
||||
parametersBody: null,
|
||||
imports: [],
|
||||
errors: [],
|
||||
result: PrimaryType.VOID,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -17,6 +17,7 @@ export function getOperationParameters(openApi: OpenApi, parameters: OpenApiPara
|
||||
parametersQuery: [],
|
||||
parametersForm: [],
|
||||
parametersHeader: [],
|
||||
parametersBody: null,
|
||||
};
|
||||
|
||||
// Iterate over the parameters
|
||||
|
||||
@ -13,6 +13,7 @@ export function getOperationResponse(responses: OperationResponse[]): OperationR
|
||||
text: '',
|
||||
type: PrimaryType.OBJECT,
|
||||
base: PrimaryType.OBJECT,
|
||||
template: null,
|
||||
imports: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -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<string>');
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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<Model>".
|
||||
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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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:
|
||||
|
||||
20
src/templates/typescript/exportEnum.hbs
Normal file
20
src/templates/typescript/exportEnum.hbs
Normal file
@ -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}}
|
||||
38
src/templates/typescript/exportInterface.hbs
Normal file
38
src/templates/typescript/exportInterface.hbs
Normal file
@ -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}}
|
||||
}
|
||||
16
src/templates/typescript/exportType.hbs
Normal file
16
src/templates/typescript/exportType.hbs
Normal file
@ -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}}
|
||||
@ -18,4 +18,4 @@ export { {{{this}}} } from './models/{{{this}}}';
|
||||
{{#each services}}
|
||||
export { {{{this}}} } from './services/{{{this}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
@ -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}}
|
||||
@ -81,4 +81,4 @@ export class {{{name}}} {
|
||||
}
|
||||
|
||||
{{/each}}
|
||||
}
|
||||
}
|
||||
5
src/templates/typescript/type.hbs
Normal file
5
src/templates/typescript/type.hbs
Normal file
@ -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}}
|
||||
1
src/templates/typescript/validation.hbs
Normal file
1
src/templates/typescript/validation.hbs
Normal file
@ -0,0 +1 @@
|
||||
yup.mixed()
|
||||
@ -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();
|
||||
|
||||
@ -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<string, number>();
|
||||
return {
|
||||
...service,
|
||||
|
||||
@ -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<string, Model>())).toEqual([]);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -31,6 +31,7 @@ describe('writeClient', () => {
|
||||
const templates: Templates = {
|
||||
index: () => 'dummy',
|
||||
model: () => 'dummy',
|
||||
interface: () => 'dummy',
|
||||
service: () => 'dummy',
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<string, Model>(),
|
||||
services: new Map<string, Service>(),
|
||||
};
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@ -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<string, Model>, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
|
||||
export function writeClientModels(models: Map<string, Model>, 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}"`);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@ -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<string, Service>, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
|
||||
export function writeClientServices(services: Map<string, Service>, 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}"`);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user