- Cleanup done, need to run through the test cases!

This commit is contained in:
Ferdi Koomen 2019-11-15 23:14:45 +01:00
parent 121ecd527f
commit e938bd9b5c
65 changed files with 547 additions and 289 deletions

View File

@ -1,7 +1,8 @@
import { EnumValue } from './EnumValue';
import { EnumSymbol } from './EnumSymbol';
export interface Enum {
name: string;
type: string;
values: EnumValue[];
symbols: EnumSymbol[];
validation: string | null;
}

View File

@ -1,4 +1,4 @@
export interface EnumValue {
export interface EnumSymbol {
name: string;
value: string;
}

View File

@ -1,6 +1,6 @@
import { ModelProperty } from './ModelProperty';
import { Enum } from './Enum';
import { EnumValue } from './EnumValue';
import { EnumSymbol } from './EnumSymbol';
export interface Model {
isInterface: boolean;
@ -12,9 +12,9 @@ export interface Model {
template: string | null;
validation: string | null;
description: string | null;
extends: string | null;
extends: string[];
imports: string[];
enums: Enum[];
values: EnumValue[];
symbols: EnumSymbol[];
properties: ModelProperty[];
}

View File

@ -4,6 +4,6 @@ export interface ModelProperty {
required: boolean;
nullable: boolean;
readOnly: boolean;
validation: string | null;
description: string | null;
validation: string | null;
}

View File

@ -4,9 +4,9 @@ import { OperationParameters } from './OperationParameters';
export interface Operation extends OperationParameters {
service: string;
name: string;
summary?: string;
description?: string;
deprecated?: boolean;
summary: string | null;
description: string | null;
deprecated: boolean;
method: string;
path: string;
errors: OperationError[];

View File

@ -29,8 +29,8 @@ export enum HttpClient {
* @param httpClient: The selected httpClient (fetch or XHR)
*/
export function generate(input: string, output: string, language: Language = Language.TYPESCRIPT, httpClient: HttpClient = HttpClient.FETCH): void {
const inputPath: string = path.resolve(process.cwd(), input);
const outputPath: string = path.resolve(process.cwd(), output);
const inputPath = path.resolve(process.cwd(), input);
const outputPath = path.resolve(process.cwd(), output);
console.log(chalk.bold.green('Generate:'));
console.log(chalk.grey(' Input:'), input);
@ -42,22 +42,22 @@ export function generate(input: string, output: string, language: Language = Lan
try {
// Load the specification, read the OpenAPI version and load the
// handlebar templates for the given language
const openApi: any = getOpenApiSpec(inputPath);
const openApiVersion: OpenApiVersion = getOpenApiVersion(openApi);
const templates: Templates = readHandlebarsTemplates(language);
const openApi = getOpenApiSpec(inputPath);
const openApiVersion = getOpenApiVersion(openApi);
const templates = readHandlebarsTemplates(language);
switch (language) {
case Language.JAVASCRIPT:
case Language.TYPESCRIPT:
// Generate and write version 2 client
if (openApiVersion === OpenApiVersion.V2) {
const clientV2: Client = parseV2(openApi);
const clientV2 = parseV2(openApi);
writeClient(clientV2, language, templates, outputPath);
}
// Generate and write version 3 client
if (openApiVersion === OpenApiVersion.V3) {
const clientV3: Client = parseV3(openApi);
const clientV3 = parseV3(openApi);
writeClient(clientV3, language, templates, outputPath);
}
}

View File

@ -7,6 +7,7 @@ import { OpenApiXml } from './OpenApiXml';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
*/
export interface OpenApiSchema {
$ref?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
title?: string;
description?: string;

View File

@ -1,6 +1,5 @@
import { getType } from './getType';
import { PrimaryType } from './constants';
import { Type } from '../../../client/interfaces/Type';
import { OpenApiItems } from '../interfaces/OpenApiItems';
export interface ArrayType {
@ -22,7 +21,7 @@ export function getArrayType(items: OpenApiItems): ArrayType {
// If the parameter has a type than it can be a basic or generic type.
if (items.type) {
const itemsType: Type = getType(items.type);
const itemsType = getType(items.type);
result.type = itemsType.type;
result.base = itemsType.base;
result.template = itemsType.template;
@ -31,7 +30,7 @@ export function getArrayType(items: OpenApiItems): ArrayType {
// 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: ArrayType = getArrayType(items.items);
const arrayType = getArrayType(items.items);
result.type = `${arrayType.type}[]`;
result.base = arrayType.base;
result.template = arrayType.template;

View File

@ -1,25 +1,12 @@
import { ModelEnumValue } from '../../../client/interfaces/ModelEnumValue';
import { Enum } from '../../../client/interfaces/Enum';
export function getModelEnum(values?: (string | number)[]): ModelEnumValue[] {
if (Array.isArray(values)) {
return values
.filter((value: string | number, index: number, arr: (string | number)[]) => {
return arr.indexOf(value) === index;
})
.map(
(value: string | number): ModelEnumValue => {
if (typeof value === 'number') {
return {
name: `NUM_${value}`,
value: String(value),
};
}
return {
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: `'${value}'`,
};
}
);
}
return [];
export function getModelEnum(): Enum {
const prop: Enum = {
name: '',
type: '',
values: [],
validation: null,
};
return prop;
}

View File

@ -0,0 +1,23 @@
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
export function getEnumSymbols(values?: (string | number)[]): EnumSymbol[] {
if (Array.isArray(values)) {
return values
.filter((value, index, arr) => {
return arr.indexOf(value) === index;
})
.map(value => {
if (typeof value === 'number') {
return {
name: `NUM_${value}`,
value: String(value),
};
}
return {
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: `'${value}'`,
};
});
}
return [];
}

View File

@ -1,16 +1,16 @@
import { ModelEnumValue } from '../../../client/interfaces/ModelEnumValue';
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
export function getModelEnumFromDescription(description: string): ModelEnumValue[] {
export function getEnumSymbolsFromDescription(description: string): EnumSymbol[] {
// Check if we can find this special format string:
// None=0,Something=1,AnotherThing=2
if (/^(\w+=[0-9]+,?)+$/g.test(description)) {
const matches: RegExpMatchArray | null = description.match(/(\w+=[0-9]+,?)/g);
const matches = description.match(/(\w+=[0-9]+,?)/g);
if (matches) {
// Grab the values from the description
const symbols: ModelEnumValue[] = [];
matches.forEach((match: string): void => {
const name: string = match.split('=')[0];
const value: number = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
const symbols: EnumSymbol[] = [];
matches.forEach(match => {
const name = match.split('=')[0];
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
if (name && Number.isInteger(value)) {
symbols.push({
name: name.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
@ -20,7 +20,7 @@ export function getModelEnumFromDescription(description: string): ModelEnumValue
});
// Filter out any duplicate names
return symbols.filter((symbol: ModelEnumValue, index: number, arr: ModelEnumValue[]): boolean => {
return symbols.filter((symbol, index, arr) => {
return arr.map(item => item.name).indexOf(symbol.name) === index;
});
}

View File

@ -1,15 +1,18 @@
export function getEnumType(symbols: ModelSymbol[], addParentheses = false): string {
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
import { getEnumValues } from './getEnumValues';
export function getEnumType(symbols: EnumSymbol[], addParentheses: boolean = false): string {
// Fetch values from the symbols, just to be sure we filter out
// any double values and finally we sort them to make them easier
// to read when we use them in our generated code.
const entries: string[] = getEnumValues(symbols);
const values = getEnumValues(symbols);
// Add grouping parentheses if needed. This can be handy if enum values
// are used in Arrays, so that you will get the following definition:
// const myArray: ('EnumValue1' | 'EnumValue2' | 'EnumValue3')[];
if (entries.length > 1 && addParentheses) {
return `(${entries.join(' | ')})`;
if (values.length > 1 && addParentheses) {
return `(${values.join(' | ')})`;
}
return entries.join(' | ');
return values.join(' | ');
}

View File

@ -1,11 +1,13 @@
export function getEnumValues(symbols: ModelSymbol[]): string[] {
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
export function getEnumValues(symbols: EnumSymbol[]): string[] {
// Fetch values from the symbols, just to be sure we filter out
// any double values and finally we sort them to make them easier
// to read when we use them in our generated code.
return symbols
.map(symbol => symbol.value)
.filter((value: string, index: number, arr: string[]): boolean => {
return arr.indexOf(value) === index;
.filter((symbol, index, arr) => {
return arr.indexOf(symbol) === index;
})
.sort();
}

View File

@ -4,7 +4,7 @@ import { PrimaryType, TYPE_MAPPINGS } from './constants';
* Get mapped type for given type to any basic Typescript/Javascript type.
*/
export function getMappedType(type: string): PrimaryType | string {
const mapped: string | undefined = TYPE_MAPPINGS.get(type.toLowerCase());
const mapped = TYPE_MAPPINGS.get(type.toLowerCase());
if (mapped) {
return mapped;
}

View File

@ -2,158 +2,204 @@ import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { getComment } from './getComment';
import { getType } from './getType';
import { Type } from '../../../client/interfaces/Type';
import { getEnumType } from './getEnumType';
import { PrimaryType } from './constants';
import { OpenApiReference } from '../interfaces/OpenApiReference';
import { getRef } from './getRef';
import { getEnumValues } from './getEnumValues';
import { Model } from '../../../client/interfaces/Model';
import { getValidationForRef } from './getValidationForRef';
import { getValidationForType } from './getValidationForType';
import { getValidationForArrayRef } from './getValidationForArrayRef';
import { getModelType } from './getModelType';
import { getModelValidation } from './getModelValidation';
import { getValidation } from './getValidation';
import { PrimaryType } from './constants';
import { getEnumType } from './getEnumType';
import { getEnumSymbols } from './getEnumSymbols';
import { getEnumValues } from './getEnumValues';
import { getEnumSymbolsFromDescription } from './getEnumSymbolsFromDescription';
export function getModel(openApi: OpenApi, schema: OpenApiSchema, name: string): Model {
export function getModel(openApi: OpenApi, definition: OpenApiSchema, definitionName: string = 'unknown'): Model {
const result: Model = {
name,
isInterface: false,
isType: false,
isEnum: false,
name: definitionName,
type: 'any',
base: 'any',
template: null,
validation: null,
description: getComment(schema.description),
extends: null,
description: getComment(definition.description),
extends: [],
imports: [],
enums: [],
symbols: [],
properties: [],
};
if (definition.$ref) {
const definitionRef = getType(definition.$ref);
result.isType = true;
result.type = definitionRef.type;
result.base = definitionRef.base;
result.template = definitionRef.template;
result.validation = getValidationForRef(definitionRef);
result.imports.push(...definitionRef.imports);
return result;
}
// If the param is a enum then return the values as an inline type.
if (schema.enum) {
const enumSymbols: ModelSymbol[] = getEnumSymbols(schema.enum);
if (definition.enum) {
const enumSymbols = getEnumSymbols(definition.enum);
if (enumSymbols.length) {
result.isEnum = true;
result.symbols = enumSymbols;
result.type = getEnumType(enumSymbols);
result.base = PrimaryType.STRING;
result.validation = `yup.mixed<${name}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
result.validation = `yup.mixed<${definitionName}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
return result;
}
return result;
}
// If the param is a enum then return the values as an inline type.
if (schema.type === 'int' && schema.description) {
const enumSymbols: ModelSymbol[] = getEnumSymbolsFromDescription(schema.description);
if (definition.type === 'int' && definition.description) {
const enumSymbols = getEnumSymbolsFromDescription(definition.description);
if (enumSymbols.length) {
result.isEnum = true;
result.symbols = enumSymbols;
result.type = getEnumType(enumSymbols);
result.base = PrimaryType.NUMBER;
result.validation = `yup.mixed<${name}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
result.validation = `yup.mixed<${definitionName}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
return result;
}
return result;
}
// If the schema is an Array type, we check for the child type,
// so we can create a typed array, otherwise this will be a "any[]".
if (schema.type === 'array' && schema.items) {
if (schema.items.$ref) {
const arrayType: Type = getType(schema.items.$ref);
result.imports.push(...arrayType.imports);
if (definition.type === 'array' && definition.items) {
if (definition.items.$ref) {
const arrayItemsRef = getType(definition.items.$ref);
result.imports.push(...arrayItemsRef.imports);
result.isType = true;
result.type = `${arrayType.type}[]`;
result.base = arrayType.base;
result.template = arrayType.template;
result.validation = `yup.array<${result.name}>().of(${result.base}.schema)`; // TODO: Simple strings!
result.imports.push(...arrayType.imports);
result.type = `${arrayItemsRef.type}[]`;
result.base = arrayItemsRef.base;
result.template = arrayItemsRef.template;
result.validation = getValidationForArrayRef(arrayItemsRef);
result.imports.push(...arrayItemsRef.imports);
} else {
const array: Schema = getSchema(openApi, schema.items, 'unkown');
const arrayType: string = getSchemaType(array);
const arrayItemsModel = getModel(openApi, definition.items);
result.isType = true;
result.type = `${arrayType}[]`;
result.base = arrayType;
result.template = null;
result.validation = `yup.array<${result.name}>().of(${result.base}.schema)`; // TODO: Simple strings!
result.imports.push(...array.imports);
result.type = `${arrayItemsModel.type}[]`;
result.base = arrayItemsModel.base;
result.template = arrayItemsModel.template;
// result.validation = getValidationForArray(array.validation || 'any');
result.imports.push(...arrayItemsModel.imports);
}
return result;
}
/*
// Check if this model extends other models
if (schema.allOf) {
schema.allOf.forEach((parent: OpenApiSchema & OpenApiReference): void => {
const parentSchema: SchemaReference = getSchemaReference(openApi, parent);
result.extends.push(parentSchema.type);
result.imports.push(parentSchema.base);
// Merge properties of other models
if (parent.properties) {
for (const propertyName in schema.properties) {
if (schema.properties.hasOwnProperty(propertyName)) {
const propertyRef: OpenApiSchema & OpenApiReference = schema.properties[propertyName];
const propertyRequired: boolean = (schema.required && schema.required.includes(propertyName)) || false;
const property: SchemaProperty = getSchemaProperty(openApi, propertyRef, propertyName, propertyRequired);
result.imports.push(...property.imports);
result.properties.set(propertyName, property);
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) {
for (const propertyName in parent.properties) {
if (parent.properties.hasOwnProperty(propertyName)) {
const property = parent.properties[propertyName];
const propertyRequired = !!(parent.required && parent.required.includes(propertyName));
const propertyReadOnly = !!property.readOnly;
if (property.$ref) {
const propertyRef = getType(property.$ref);
result.imports.push(...propertyRef.imports);
result.properties.push({
name: propertyName,
type: propertyRef.type,
required: propertyRequired,
nullable: false,
readOnly: propertyReadOnly,
description: property.description || null,
validation: getValidationForRef(propertyRef, propertyRequired),
});
} else {
const propertyModel = getModel(openApi, property);
result.imports.push(...propertyModel.imports);
result.properties.push({
name: propertyName,
type: propertyModel.type,
required: propertyRequired,
nullable: false,
readOnly: propertyReadOnly,
description: property.description || null,
validation: propertyModel.validation ? getValidation(propertyModel.validation, propertyRequired) : null,
});
}
}
}
}
});
}
*/
if (schema.type === 'object' && schema.properties) {
// Validation needs to also check extended schema!
// Check ModelThatExtends.ts
result.isInterface = true;
result.type = 'interface';
result.base = 'interface';
result.type = getModelType(result.properties);
result.validation = getModelValidation(definitionName, result.properties);
result.base = PrimaryType.OBJECT;
result.template = null;
}
for (const propertyName in schema.properties) {
if (schema.properties.hasOwnProperty(propertyName)) {
const propertyRequired: boolean = (schema.required && schema.required.includes(propertyName)) || false;
const propertyRef: OpenApiSchema & OpenApiReference = schema.properties[propertyName];
if (propertyRef.$ref) {
const propertyType: Type = getType(propertyRef.$ref);
result.imports.push(...propertyType.imports);
if (definition.type === 'object' && definition.properties) {
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;
if (property.$ref) {
const propertyRef = getType(property.$ref);
result.imports.push(...propertyRef.imports);
result.properties.push({
name: propertyName,
type: propertyType.type,
type: propertyRef.type,
required: propertyRequired,
nullable: false,
readOnly: false,
readOnly: propertyReadOnly,
description: property.description || null,
validation: getValidationForRef(propertyRef, propertyRequired),
});
} else {
const property: OpenApiSchema = getRef<OpenApiSchema>(openApi, propertyRef);
const propertySchema: Schema = getSchema(openApi, property, propertyName);
const propertyType: string = getSchemaType(propertySchema);
result.imports.push(...propertySchema.imports);
const propertyModel = getModel(openApi, property);
result.imports.push(...propertyModel.imports);
result.properties.push({
name: propertyName,
type: propertyType,
type: propertyModel.type,
required: propertyRequired,
nullable: false,
readOnly: property.readOnly || false,
description: property.description,
readOnly: propertyReadOnly,
description: property.description || null,
validation: propertyModel.validation ? getValidation(propertyModel.validation, propertyRequired) : null,
});
// TODO: This also needs a validation logic, maybe we can store that
// per schema and have them 'concatenate' on demand??
}
}
}
result.isInterface = true;
result.type = getModelType(result.properties);
result.validation = getModelValidation(definitionName, result.properties);
result.base = PrimaryType.OBJECT;
result.template = null;
return result;
}
// If the schema has a type than it can be a basic or generic type.
if (schema.type) {
const schemaType: Type = getType(schema.type);
if (definition.type !== 'object' && definition.type) {
const definitionType = getType(definition.type);
result.isType = true;
result.type = schemaType.type;
result.base = schemaType.base;
result.template = schemaType.template;
result.imports.push(...schemaType.imports);
result.type = definitionType.type;
result.base = definitionType.base;
result.template = definitionType.template;
result.validation = getValidationForType(definitionType);
result.imports.push(...definitionType.imports);
return result;
}

View File

@ -2,7 +2,7 @@ import { getModelTemplate } from './getModelTemplate';
describe('getModelTemplate', () => {
it('should return generic for template type', () => {
const template: string = getModelTemplate({
const template = getModelTemplate({
type: 'Link<Model>',
base: 'Link',
template: 'Model',
@ -12,7 +12,7 @@ describe('getModelTemplate', () => {
});
it('should return empty for primary type', () => {
const template: string = getModelTemplate({
const template = getModelTemplate({
type: 'string',
base: 'string',
template: null,

View File

@ -1,17 +1,12 @@
import { Model } from '../../../client/interfaces/Model';
import { EOL } from 'os';
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
// string
// array[test]
// array[{
// foo: string
// bar: string
// }]
export function getModelType(model: Model): string {
// if (schema.properties) {
// return schema.type
// }
if (model.type) {
return model.type;
}
return 'any';
export function getModelType(properties: ModelProperty[]): string {
return [
`{`,
...properties.map(property => {
return ` ${property.readOnly ? 'readonly ' : ''}${property.name}${property.required ? '' : '?'}: ${property.type},`;
}),
`}`,
].join(EOL);
}

View File

@ -0,0 +1,12 @@
import { EOL } from 'os';
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
export function getModelValidation(name: string, properties: ModelProperty[]): string {
return [
`yup.object().shape({`,
...properties.map(property => {
return ` ${property.name}: ${property.validation},`;
}),
`}).noUnknown()`,
].join(EOL);
}

View File

@ -1,14 +1,13 @@
import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { getModel } from './getModel';
export function getModels(openApi: OpenApi): Model[] {
const models: Model[] = [];
for (const definitionName in openApi.definitions) {
if (openApi.definitions.hasOwnProperty(definitionName)) {
const definition: OpenApiSchema = openApi.definitions[definitionName];
const definitionModel: Model = getModel(openApi, definition, definitionName);
const definition = openApi.definitions[definitionName];
const definitionModel = getModel(openApi, definition, definitionName);
models.push(definitionModel);
}
}

View File

@ -9,13 +9,14 @@ import { getComment } from './getComment';
import { getOperationResponses } from './getOperationResponses';
import { getOperationResponse } from './getOperationResponse';
import { getOperationErrors } from './getOperationErrors';
import { Operation } from '../../../client/interfaces/Operation';
export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation): Operation {
const serviceName: string = (op.tags && op.tags[0]) || 'Service';
const serviceClassName: string = getServiceClassName(serviceName);
const operationNameFallback: string = `${method}${serviceClassName}`;
const operationName: string = getOperationName(op.operationId || operationNameFallback);
const operationPath: string = getOperationPath(url);
const serviceName = (op.tags && op.tags[0]) || 'Service';
const serviceClassName = getServiceClassName(serviceName);
const operationNameFallback = `${method}${serviceClassName}`;
const operationName = getOperationName(op.operationId || operationNameFallback);
const operationPath = getOperationPath(url);
// Create a new operation object for this method.
const result: Operation = {
@ -23,7 +24,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
name: operationName,
summary: getComment(op.summary),
description: getComment(op.description),
deprecated: op.deprecated,
deprecated: op.deprecated || false,
method: method,
path: operationPath,
parameters: [],
@ -39,7 +40,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
// Parse the operation parameters (path, query, body, etc).
if (op.parameters) {
const parameters: OperationParameters = getOperationParameters(openApi, op.parameters);
const parameters = getOperationParameters(openApi, op.parameters);
result.imports.push(...parameters.imports);
result.parameters.push(...parameters.parameters);
result.parametersPath.push(...parameters.parametersPath);
@ -51,9 +52,9 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
// Parse the operation responses.
if (op.responses) {
const responses: OperationResponse[] = getOperationResponses(openApi, op.responses);
const response: OperationResponse = getOperationResponse(responses);
const errors: OperationError[] = getOperationErrors(responses);
const responses = getOperationResponses(openApi, op.responses);
const response = getOperationResponse(responses);
const errors = getOperationErrors(responses);
result.imports.push(...response.imports);
result.errors = errors;
result.result = response.type;

View File

@ -1,12 +1,13 @@
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
import { OperationError } from '../../../client/interfaces/OperationError';
export function getOperationErrors(responses: OperationResponse[]): OperationError[] {
return responses
.filter((response: OperationResponse): boolean => {
.filter(response => {
return response.code >= 300 && response.text !== undefined && response.text !== '';
})
.map(
(response: OperationResponse): OperationError => ({
code: response.code,
text: response.text,
})
);
.map(response => ({
code: response.code,
text: response.text,
}));
}

View File

@ -6,6 +6,6 @@ import camelCase from 'camelcase';
* the most popular Javascript and Typescript writing style.
*/
export function getOperationName(value: string): string {
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
return camelCase(clean);
}

View File

@ -1,15 +1,15 @@
import { OpenApiParameter } from '../interfaces/OpenApiParameter';
import { getType } from './getType';
import { Type } from '../../../client/interfaces/Type';
import { OpenApi } from '../interfaces/OpenApi';
import { getParameterName } from './getParameterName';
import { getComment } from './getComment';
import { getOperationParameterName } from './getOperationParameterName';
import { OperationParameter } from '../../../client/interfaces/OperationParameter';
export function getParameter(openApi: OpenApi, parameter: OpenApiParameter): Parameter {
const result: Parameter = {
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
const result: OperationParameter = {
in: parameter.in,
prop: parameter.name,
name: getParameterName(parameter.name),
name: getOperationParameterName(parameter.name),
type: 'any',
base: 'any',
template: null,
@ -22,7 +22,7 @@ export function getParameter(openApi: OpenApi, parameter: OpenApiParameter): Par
// If the parameter has a type than it can be a basic or generic type.
if (parameter.type) {
const parameterType: Type = getType(parameter.type);
const parameterType = getType(parameter.type);
result.type = parameterType.type;
result.base = parameterType.base;
result.template = parameterType.template;

View File

@ -4,7 +4,7 @@ import camelCase from 'camelcase';
* Replaces any invalid characters from a parameter name.
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
*/
export function getParameterName(value: string): string {
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
export function getOperationParameterName(value: string): string {
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
return camelCase(clean);
}

View File

@ -1,10 +1,12 @@
import { OpenApiParameter } from '../interfaces/OpenApiParameter';
import { OpenApiReference } from '../interfaces/OpenApiReference';
import { getParameter } from './getParameter';
import { OpenApi } from '../interfaces/OpenApi';
import { getRef } from './getRef';
import { OperationParameters } from '../../../client/interfaces/OperationParameters';
import { OperationParameter } from '../../../client/interfaces/OperationParameter';
import { getOperationParameter } from './getOperationParameter';
function sortByRequired(a: Parameter, b: Parameter): number {
function sortByRequired(a: OperationParameter, b: OperationParameter): number {
return a.required && !b.required ? -1 : !a.required && b.required ? 1 : 0;
}
@ -21,8 +23,8 @@ export function getOperationParameters(openApi: OpenApi, parameters: (OpenApiPar
// Iterate over the parameters
parameters.forEach(parameter => {
const paramRef: OpenApiParameter = getRef<OpenApiParameter>(openApi, parameter);
const param: Parameter = getParameter(openApi, paramRef);
const paramRef = getRef<OpenApiParameter>(openApi, parameter);
const param = getOperationParameter(openApi, paramRef);
// We ignore the "api-version" param, since we do not want to add this
// as the first / default parameter for each of the service calls.

View File

@ -1,4 +1,5 @@
import { PrimaryType } from './constants';
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
export function getOperationResponse(responses: OperationResponse[]): OperationResponse {
const response: OperationResponse = {
@ -11,7 +12,7 @@ export function getOperationResponse(responses: OperationResponse[]): OperationR
};
// Fetch the first valid (2XX range) response code and return that type.
const result: OperationResponse | undefined = responses.find(response => response.code && response.code >= 200 && response.code < 300);
const result = responses.find(response => response.code && response.code >= 200 && response.code < 300);
if (result) {
response.code = result.code;
response.text = result.text;

View File

@ -6,7 +6,7 @@ export function getOperationResponseCode(value: string | 'default'): number | nu
// Check if we can parse the code and return of successful.
if (/[0-9]+/g.test(value)) {
const code: number = parseInt(value);
const code = parseInt(value);
if (Number.isInteger(code)) {
return code;
}

View File

@ -1,9 +1,9 @@
import { OpenApiResponses } from '../interfaces/OpenApiResponses';
import { getOperationResponseCode } from './getOperationResponseCode';
import { OpenApiResponse } from '../interfaces/OpenApiResponse';
import { OpenApiReference } from '../interfaces/OpenApiReference';
import { getRef } from './getRef';
import { OpenApi } from '../interfaces/OpenApi';
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
export function getOperationResponses(openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] {
const results: OperationResponse[] = [];
@ -12,9 +12,9 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon
// status code and response message (if any).
for (const code in responses) {
if (responses.hasOwnProperty(code)) {
const responseOrReference: OpenApiResponse & OpenApiReference = responses[code];
const response: OpenApiResponse = getRef<OpenApiResponse>(openApi, responseOrReference);
const responseCode: number | null = getOperationResponseCode(code);
const responseOrReference = responses[code];
const response = getRef<OpenApiResponse>(openApi, responseOrReference);
const responseCode = getOperationResponseCode(code);
// If there is a response code then we check what data we get back,
// if there is no typed data, we just return <any> so the user is still

View File

@ -5,7 +5,7 @@ export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
if (item.$ref) {
// Fetch the paths to the definitions, this converts:
// "#/definitions/Form" to ["definitions", "Form"]
const paths: string[] = item.$ref
const paths = item.$ref
.replace(/^#/g, '')
.split('/')
.filter(item => item);

View File

@ -5,8 +5,8 @@ import { OpenApi } from '../interfaces/OpenApi';
* @param openApi
*/
export function getServer(openApi: OpenApi): string {
const scheme: string = (openApi.schemes && openApi.schemes[0]) || 'http';
const host: string | undefined = openApi.host;
const basePath: string = openApi.basePath || '';
const scheme = (openApi.schemes && openApi.schemes[0]) || 'http';
const host = openApi.host;
const basePath = openApi.basePath || '';
return host ? `${scheme}://${host}${basePath}` : basePath;
}

View File

@ -5,8 +5,8 @@ import camelCase from 'camelcase';
* the input string to PascalCase and appends the "Service" prefix if needed.
*/
export function getServiceClassName(value: string): string {
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
const name: string = camelCase(clean, { pascalCase: true });
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
const name = camelCase(clean, { pascalCase: true });
if (name && !name.endsWith('Service')) {
return `${name}Service`;
}

View File

@ -3,6 +3,6 @@
* This basically removes any "v" prefix from the version string.
* @param version
*/
export function getServiceVersion(version: string = '1.0'): string {
export function getServiceVersion(version = '1.0'): string {
return version.replace(/^v/gi, '');
}

View File

@ -1,16 +1,15 @@
import { Service } from '../../../client/interfaces/Service';
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiPath } from '../interfaces/OpenApiPath';
import { Method } from './constants';
/**
* Get the OpenAPI services
*/
export function getServices(openApi: OpenApi): Service[] {
const services: Map<string, Service> = new Map<string, Service>();
const services = new Map<string, Service>();
for (const url in openApi.paths) {
if (openApi.paths.hasOwnProperty(url)) {
const path: OpenApiPath = openApi.paths[url];
const path = openApi.paths[url];
for (const method in path) {
if (path.hasOwnProperty(method)) {
switch (method) {

View File

@ -17,15 +17,15 @@ export function getType(value: string | undefined, template: string | null = nul
};
// Remove definitions prefix and cleanup string.
const valueClean: string = stripNamespace(value || '');
const valueClean = stripNamespace(value || '');
// Check of we have an Array type or generic type, for instance: "Link[Model]".
if (/\[.*\]$/g.test(valueClean)) {
const matches: RegExpMatchArray | null = valueClean.match(/(.*?)\[(.*)\]$/);
const matches = valueClean.match(/(.*?)\[(.*)\]$/);
if (matches && matches.length) {
// Both of the types can be complex types so parse each of them.
const match1: Type = getType(matches[1]);
const match2: Type = getType(matches[2]);
const match1 = getType(matches[1]);
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[]".
@ -50,7 +50,7 @@ export function getType(value: string | undefined, template: string | null = nul
result.imports.push(...match2.imports);
}
} else if (hasMappedType(valueClean)) {
const mapped: string = getMappedType(valueClean);
const mapped = getMappedType(valueClean);
result.type = mapped;
result.base = mapped;
} else if (valueClean) {

View File

@ -0,0 +1,11 @@
export function getValidation(validation: string, required: boolean = false, nullable: boolean = false): string {
if (required) {
validation = `${validation}.required()`;
}
if (nullable) {
validation = `${validation}.nullable()`;
}
return validation;
}

View File

@ -0,0 +1,15 @@
import { Type } from '../../../client/interfaces/Type';
export function getValidationForArrayRef(ref: Type, required: boolean = false, nullable: boolean = false): string {
let validation = `yup.array<${ref.type}>().of(${ref.base}.schema)`;
if (required) {
validation = `${validation}.required()`;
}
if (nullable) {
validation = `${validation}.nullable()`;
}
return validation;
}

View File

@ -0,0 +1,28 @@
import { PrimaryType } from './constants';
import { Type } from '../../../client/interfaces/Type';
export function getValidationForArrayType(type: Type, required: boolean = false, nullable: boolean = false): string {
let validation = `yup.array<any>().of(yup.mixed())`;
switch (type.type) {
case PrimaryType.BOOLEAN:
validation = `yup.array<boolean>().of(yup.boolean())`;
break;
case PrimaryType.NUMBER:
validation = `yup.array<number>().of(yup.number())`;
break;
case PrimaryType.STRING:
validation = `yup.array<string>().of(yup.string())`;
break;
}
if (required) {
validation = `${validation}.required()`;
}
if (nullable) {
validation = `${validation}.nullable()`;
}
return validation;
}

View File

@ -0,0 +1,15 @@
import { Type } from '../../../client/interfaces/Type';
export function getValidationForRef(ref: Type, required = false, nullable = false): string {
let validation = `${ref.base}.schema`;
if (required) {
validation = `${validation}.required()`;
}
if (nullable) {
validation = `${validation}.nullable()`;
}
return validation;
}

View File

@ -0,0 +1,28 @@
import { PrimaryType } from './constants';
import { Type } from '../../../client/interfaces/Type';
export function getValidationForType(type: Type, required: boolean = false, nullable: boolean = false): 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;
}
if (required) {
validation = `${validation}.required()`;
}
if (nullable) {
validation = `${validation}.nullable()`;
}
return validation;
}

View File

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

View File

@ -7,6 +7,7 @@ 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:

View File

@ -1,12 +1,9 @@
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiServer } from '../interfaces/OpenApiServer';
import { Dictionary } from '../../../utils/types';
import { OpenApiServerVariable } from '../interfaces/OpenApiServerVariable';
export function getServer(openApi: OpenApi): string {
const server: OpenApiServer | undefined = openApi.servers && openApi.servers[0];
const variables: Dictionary<OpenApiServerVariable> = (server && server.variables) || {};
let url: string = (server && server.url) || '';
const server = openApi.servers && openApi.servers[0];
const variables = (server && server.variables) || {};
let url = (server && server.url) || '';
for (const variable in variables) {
if (variables.hasOwnProperty(variable)) {
url = url.replace(`{${variable}}`, variables[variable].default);

View File

@ -9,7 +9,7 @@
* @param params Key value based object.
*/
export function getFormData(params: { [key: string]: any }): FormData {
const formData: FormData = new FormData();
const formData = new FormData();
for (const key in params) {
if (typeof params[key] !== 'undefined') {
const value: any = params[key];

View File

@ -17,10 +17,10 @@ import { Result } from './Result';
*/
export async function request<T = any>(options: Readonly<RequestOptions>): Promise<Result<T>> {
// Create the request URL
let url: string = `${OpenAPI.BASE}${options.path}`;
let url = `${OpenAPI.BASE}${options.path}`;
// Create request headers
const headers: Headers = new Headers({
const headers = new Headers({
...options.headers,
Accept: 'application/json',
});

View File

@ -14,7 +14,7 @@ import { Result } from './Result';
*/
export async function requestUsingFetch<T = any>(url: string, request: Readonly<RequestInit>): Promise<Result<T>> {
// Fetch response using fetch API.
const response: Response = await fetch(url, request);
const response = await fetch(url, request);
// Create result object.
const result: Result = {
@ -28,7 +28,7 @@ export async function requestUsingFetch<T = any>(url: string, request: Readonly<
// Try to parse the content for any response status code.
// We check the "Content-Type" header to see if we need to parse the
// content as json or as plain text.
const contentType: string | null = response.headers.get('Content-Type');
const contentType = response.headers.get('Content-Type');
if (contentType) {
switch (contentType.toLowerCase()) {
case 'application/json':

View File

@ -16,14 +16,14 @@ import { isSuccess } from './isSuccess';
*/
export async function requestUsingXHR<T = any>(url: string, request: Readonly<RequestInit>): Promise<Result<T>> {
return new Promise(resolve => {
const xhr: XMLHttpRequest = new XMLHttpRequest();
const xhr = new XMLHttpRequest();
// Open the request, remember to do this before adding any headers,
// because the request needs to be initialized!
xhr.open(request.method!, url, true);
// Add the headers (required when dealing with JSON)
const headers: Headers = request.headers as Headers;
const headers = request.headers as Headers;
headers.forEach((value: string, key: string): void => {
xhr.setRequestHeader(key, value);
});
@ -43,7 +43,7 @@ export async function requestUsingXHR<T = any>(url: string, request: Readonly<Re
// Try to parse the content for any response status code.
// We check the "Content-Type" header to see if we need to parse the
// content as json or as plain text.
const contentType: string | null = xhr.getResponseHeader('Content-Type');
const contentType = xhr.getResponseHeader('Content-Type');
if (contentType) {
switch (contentType.toLowerCase()) {
case 'application/json':

View File

@ -10,7 +10,7 @@ export { OpenAPI } from './core/OpenAPI';
{{#if models}}
{{#each models}}
export { {{{base}}} } from './models/{{{base}}}';
export { {{{name}}} } from './models/{{{name}}}';
{{/each}}
{{/if}}
{{#if services}}

View File

@ -11,19 +11,19 @@ import { {{{this}}} } from '../models/{{{this}}}';
{{#if validation}}
import * as yup from 'yup';
{{/if}}
{{#if description}}
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
{{#if isInterface}}
export interface {{{name}}}{{{template}}}{{#if extend}} extends {{{extend}}}{{/if}} {
export interface {{{name}}}{{{template}}}{{#if extends}} extends{{#each extends}} {{{this}}}{{/each}}{{/if}} {
{{#each properties}}
{{#if description}}
/**
* {{{description}}}
*/
* {{{description}}}
*/
{{/if}}
{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}};
{{/each}}
@ -62,6 +62,7 @@ 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}}> {
@ -83,6 +84,7 @@ export enum {{{name}}} {
{{#if validation}}
export namespace {{{name}}} {
export const schema = {{{validation}}};
export function validate(value: any): Promise<{{{name}}}> {

View File

@ -44,7 +44,7 @@ export class {{{name}}} {
{{/each}}
{{/if}}
const result: Result = await request({
const result = await request({
method: '{{{method}}}',
path: `{{{path}}}`,{{#if parametersHeader}}
headers: {

View File

@ -2,8 +2,19 @@ import { getSortedImports } from './getSortedImports';
import { Model } from '../client/interfaces/Model';
export function cleanupModels(models: Model[]): Model[] {
models.forEach((models: Model): void => {
models.imports = getSortedImports(models.imports);
models.forEach(model => {
model.imports = getSortedImports(model.imports).filter(name => {
return model.name !== name;
});
model.properties = model.properties
.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);
});
});
return models;
}

View File

@ -1,28 +1,27 @@
import { Service } from '../client/interfaces/Service';
import { getSortedImports } from './getSortedImports';
import { Operation } from '../client/interfaces/Operation';
export function cleanupServices(services: Service[]): Service[] {
services.forEach((service: Service): void => {
const names: Map<string, number> = new Map<string, number>();
services.forEach(service => {
const names = new Map<string, number>();
service.imports = getSortedImports(service.imports);
service.imports = getSortedImports(service.imports).filter(name => {
return service.name !== name;
});
service.operations = service.operations
.map(
(operation: Operation): Operation => {
const name: string = operation.name;
const index: number = names.get(name) || 0;
if (index > 0) {
operation.name = `${name}${index}`;
}
names.set(name, index + 1);
return operation;
.map(operation => {
const name = operation.name;
const index = names.get(name) || 0;
if (index > 0) {
operation.name = `${name}${index}`;
}
)
.sort((a: Operation, b: Operation): number => {
const nameA: string = a.name.toLowerCase();
const nameB: string = b.name.toLowerCase();
names.set(name, index + 1);
return operation;
})
.sort((a, b) => {
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
return nameA.localeCompare(nameB);
});
});

View File

@ -24,8 +24,8 @@ function read(filePath: string): string {
* @param filePath
*/
export function getOpenApiSpec(filePath: string): any {
const content: string = read(filePath);
const extname: string = path.extname(filePath).toLowerCase();
const content = read(filePath);
const extname = path.extname(filePath).toLowerCase();
switch (extname) {
case '.yml':
case '.yaml':

View File

@ -12,8 +12,8 @@ export enum OpenApiVersion {
export function getOpenApiVersion(openApi: any): OpenApiVersion {
const info: any = openApi.swagger || openApi.openapi;
if (info && typeof info === 'string') {
const c: string = info.charAt(0);
const v: number = Number.parseInt(c);
const c = info.charAt(0);
const v = Number.parseInt(c);
if (v === OpenApiVersion.V2 || v === OpenApiVersion.V3) {
return v as OpenApiVersion;
}

View File

@ -6,12 +6,12 @@ export function getSortedImports(imports: string[]): string[] {
return imports
.filter(name => name)
.filter(name => name.trim())
.filter((name: string, index: number, arr: string[]) => {
.filter((name, index, arr) => {
return arr.indexOf(name) === index;
})
.sort((a, b) => {
const nameA: string = a.toLowerCase();
const nameB: string = b.toLowerCase();
const nameA = a.toLowerCase();
const nameB = b.toLowerCase();
return nameA.localeCompare(nameB, 'en');
});
}

View File

@ -14,9 +14,9 @@ describe('getSortedModels', () => {
template: null,
validation: null,
description: null,
extends: null,
extends: [],
imports: [],
values: [],
symbols: [],
properties: [],
enums: [],
},
@ -30,9 +30,9 @@ describe('getSortedModels', () => {
template: null,
validation: null,
description: null,
extends: null,
extends: [],
imports: [],
values: [],
symbols: [],
properties: [],
enums: [],
},
@ -46,9 +46,9 @@ describe('getSortedModels', () => {
template: null,
validation: null,
description: null,
extends: null,
extends: [],
imports: [],
values: [],
symbols: [],
properties: [],
enums: [],
},

View File

@ -2,8 +2,8 @@ import { Model } from '../client/interfaces/Model';
export function getSortedModels(models: Model[]): Model[] {
return models.sort((a, b) => {
const nameA: string = a.name.toLowerCase();
const nameB: string = b.name.toLowerCase();
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
return nameA.localeCompare(nameB, 'en');
});
}

View File

@ -2,8 +2,8 @@ import { Service } from '../client/interfaces/Service';
export function getSortedServices(services: Service[]): Service[] {
return services.sort((a, b) => {
const nameA: string = a.name.toLowerCase();
const nameB: string = b.name.toLowerCase();
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
return nameA.localeCompare(nameB, 'en');
});
}

View File

@ -7,9 +7,11 @@ import * as handlebars from 'handlebars';
*/
export function readHandlebarsTemplate(filePath: string): handlebars.TemplateDelegate {
if (fs.existsSync(filePath)) {
const template: string = fs.readFileSync(filePath, 'utf8').toString();
const template = fs.readFileSync(filePath, 'utf8').toString();
try {
return handlebars.compile(template);
return handlebars.compile(template, {
strict: true,
});
} catch (e) {
throw new Error(`Could not compile Handlebar template: "${filePath}"`);
}

View File

@ -15,9 +15,9 @@ export interface Templates {
* @param language The language we need to generate (Typescript or Javascript).
*/
export function readHandlebarsTemplates(language: Language): Templates {
const pathTemplateIndex: string = path.resolve(__dirname, `../../src/templates/${language}/index.hbs`);
const pathTemplateModel: string = path.resolve(__dirname, `../../src/templates/${language}/model.hbs`);
const pathTemplateService: string = path.resolve(__dirname, `../../src/templates/${language}/service.hbs`);
const pathTemplateIndex = path.resolve(__dirname, `../../src/templates/${language}/index.hbs`);
const pathTemplateModel = path.resolve(__dirname, `../../src/templates/${language}/model.hbs`);
const pathTemplateService = path.resolve(__dirname, `../../src/templates/${language}/service.hbs`);
try {
return {

View File

@ -23,9 +23,9 @@ import { cleanupModels } from './cleanupModels';
* @param outputPath
*/
export function writeClient(client: Client, language: Language, templates: Templates, outputPath: string): void {
const outputPathCore: string = path.resolve(outputPath, 'core');
const outputPathModels: string = path.resolve(outputPath, 'models');
const outputPathServices: string = path.resolve(outputPath, 'services');
const outputPathCore = path.resolve(outputPath, 'core');
const outputPathModels = path.resolve(outputPath, 'models');
const outputPathServices = path.resolve(outputPath, 'services');
// Clean output directory
try {
@ -45,9 +45,9 @@ export function writeClient(client: Client, language: Language, templates: Templ
}
// Copy all core files
const coreFiles: string = path.resolve(__dirname, `../../src/templates/${language}/core/`);
const coreFilesExt: string = getFileName('*', language);
const coreFilesList: string[] = glob.sync(coreFilesExt, { cwd: coreFiles });
const coreFiles = path.resolve(__dirname, `../../src/templates/${language}/core/`);
const coreFilesExt = getFileName('*', language);
const coreFilesList = glob.sync(coreFilesExt, { cwd: coreFiles });
coreFilesList.forEach(file =>
fs.copyFileSync(
path.resolve(coreFiles, file), // From input path
@ -57,7 +57,6 @@ export function writeClient(client: Client, language: Language, templates: Templ
// Write the client files
try {
// TODO: Cleanup models
writeClientIndex(client, language, templates.index, outputPath);
writeClientModels(getSortedModels(cleanupModels(client.models)), language, templates.model, outputPathModels);
writeClientServices(getSortedServices(cleanupServices(client.services)), language, templates.service, outputPathServices);

View File

@ -17,7 +17,7 @@ import { getFileName } from './getFileName';
* @param outputPath:
*/
export function writeClientIndex(client: Client, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
const fileName: string = getFileName('index', language);
const fileName = getFileName('index', language);
try {
fs.writeFileSync(
path.resolve(outputPath, fileName),

View File

@ -20,9 +20,9 @@ describe('writeClientModels', () => {
template: null,
validation: null,
description: null,
extends: null,
extends: [],
imports: [],
values: [],
symbols: [],
properties: [],
enums: [],
},

View File

@ -14,7 +14,7 @@ import { getFileName } from './getFileName';
*/
export function writeClientModels(models: Model[], language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
models.forEach(model => {
const fileName: string = getFileName(model.name, language);
const fileName = getFileName(model.name, language);
try {
fs.writeFileSync(path.resolve(outputPath, fileName), template(model));
} catch (e) {

View File

@ -14,7 +14,7 @@ import { getFileName } from './getFileName';
*/
export function writeClientServices(services: Service[], language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
services.forEach(service => {
const fileName: string = getFileName(service.name, language);
const fileName = getFileName(service.name, language);
try {
fs.writeFileSync(path.resolve(outputPath, fileName), template(service));
} catch (e) {

View File

@ -24,6 +24,10 @@
"description": "This is a simple string",
"type": "string"
},
"SimpleFile": {
"description": "This is a simple file",
"type": "File"
},
"SimpleEnumWithStrings": {
"description": "This is a simple enum with strings",
"enum": [
@ -76,6 +80,7 @@
"description": "This is a simple array with a properties",
"type": "array",
"items": {
"type": "object",
"properties": {
"foo": {
"type": "string"
@ -86,6 +91,10 @@
}
}
},
"SimpleReference": {
"description": "This is a simple model reference",
"$ref": "#/definitions/ModelWithString"
},
"ModelWithInteger": {
"description": "This is a model with one number property",
"type": "object",
@ -107,8 +116,8 @@
}
},
"ModelWithString": {
"type": "object",
"description": "This is a model with one string property",
"type": "object",
"properties": {
"prop": {
"description": "This is a simple string property",
@ -117,8 +126,8 @@
}
},
"ModelWithEnum": {
"type": "object",
"description": "This is a model with one enum",
"type": "object",
"properties": {
"prop": {
"description": "This is a simple enum with strings",
@ -131,8 +140,8 @@
}
},
"ModelWithEnumFromDescription": {
"type": "object",
"description": "This is a model with one enum",
"type": "object",
"properties": {
"prop": {
"type": "integer",
@ -141,8 +150,8 @@
}
},
"ModelWithReference": {
"type": "object",
"description": "This is a model with one property containing a reference",
"type": "object",
"properties": {
"prop": {
"$ref": "#/definitions/ModelWithString"
@ -150,8 +159,8 @@
}
},
"ModelWithCircularReference": {
"type": "object",
"description": "This is a model with one property containing a circular reference",
"type": "object",
"properties": {
"prop": {
"$ref": "#/definitions/ModelWithCircularReference"
@ -159,8 +168,8 @@
}
},
"ModelWithNestedProperties": {
"type": "object",
"description": "This is a model with one nested property",
"type": "object",
"properties": {
"first": {
"type": "object",
@ -176,6 +185,74 @@
}
}
}
},
"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": {
"type": "object",
"allOf": [
{
"$ref": "#/definitions/ModelWithString"
},
{
"type": "object",
"properties": {
"propExtendsA": {
"type": "string"
},
"propExtendsB": {
"$ref": "#/definitions/ModelWithString"
}
}
}
]
},
"ModelThatExtendsExtends": {
"type": "object",
"allOf": [
{
"$ref": "#/definitions/ModelThatExtends"
},
{
"type": "object",
"properties": {
"propExtendsC": {
"type": "string"
},
"propExtendsD": {
"$ref": "#/definitions/ModelWithString"
}
}
}
]
}
}
}