From fbd354ebfb506daa01313ee541aace690c486945 Mon Sep 17 00:00:00 2001 From: Ferdi Koomen Date: Fri, 22 Nov 2019 22:57:34 +0100 Subject: [PATCH] - Added service generation test cases - Cleanup of models - Cleanup of comma generatiom - Formatted results --- src/client/interfaces/Model.d.ts | 17 +- src/client/interfaces/Operation.d.ts | 3 +- src/client/interfaces/OperationError.d.ts | 2 +- src/client/interfaces/OperationParameter.d.ts | 17 +- src/client/interfaces/OperationResponse.d.ts | 9 +- src/client/interfaces/Schema.ts | 11 + src/client/interfaces/Validation.d.ts | 6 - src/openApi/v2/index.ts | 3 +- src/openApi/v2/parser/getModel.ts | 139 +++++++------ src/openApi/v2/parser/getModelProperties.ts | 54 ++--- src/openApi/v2/parser/getModels.ts | 2 +- src/openApi/v2/parser/getOperation.ts | 36 ++-- src/openApi/v2/parser/getOperationErrors.ts | 10 +- .../v2/parser/getOperationParameter.ts | 111 +++++----- .../v2/parser/getOperationParameters.ts | 46 ++--- src/openApi/v2/parser/getOperationResponse.ts | 19 -- .../v2/parser/getOperationResponses.ts | 37 ++-- src/openApi/v2/parser/getOperationResults.ts | 38 ++++ src/openApi/v3/index.ts | 3 +- .../v3/parser/getServiceVersion.spec.ts | 9 + src/openApi/v3/parser/getServiceVersion.ts | 8 + src/templates/typescript/core/OpenAPI.hbs | 11 + src/templates/typescript/core/OpenAPI.ts | 10 - .../typescript/core/RequestOptions.ts | 3 +- src/templates/typescript/core/request.ts | 26 +-- .../typescript/core/requestUsingFetch.ts | 2 +- .../typescript/core/requestUsingXHR.ts | 2 +- src/templates/typescript/model.hbs | 2 +- .../typescript/{ => partials}/exportArray.hbs | 6 +- .../{ => partials}/exportDictionary.hbs | 6 +- .../typescript/{ => partials}/exportEnum.hbs | 2 +- .../{ => partials}/exportGeneric.hbs | 6 +- .../{ => partials}/exportInterface.hbs | 4 +- .../{ => partials}/exportReference.hbs | 6 +- src/templates/typescript/partials/result.hbs | 5 + .../typescript/{ => partials}/type.hbs | 12 +- .../typeArray.hbs} | 2 +- .../typeDictionary.hbs} | 2 +- .../typeEnum.hbs} | 0 .../typeGeneric.hbs} | 0 .../typescript/partials/typeInterface.hbs | 14 ++ .../typeReference.hbs} | 0 .../typescript/{ => partials}/validation.hbs | 12 +- .../validationArray.hbs} | 2 +- .../validationDictionary.hbs} | 8 +- .../typescript/partials/validationEnum.hbs | 9 + .../validationGeneric.hbs} | 0 .../partials/validationInterface.hbs | 21 ++ .../validationReference.hbs} | 0 src/templates/typescript/service.hbs | 49 +++-- src/templates/typescript/typeForInterface.hbs | 14 -- .../typescript/validationForEnum.hbs | 9 - .../typescript/validationForInterface.hbs | 21 -- src/utils/format.ts | 2 +- src/utils/getModelNames.spec.ts | 21 +- src/utils/readHandlebarsTemplates.ts | 30 +-- src/utils/writeClient.spec.ts | 5 +- src/utils/writeClient.ts | 12 +- src/utils/writeClientIndex.spec.ts | 3 +- src/utils/writeClientIndex.ts | 1 + src/utils/writeClientModels.spec.ts | 12 +- src/utils/writeClientModels.ts | 12 +- src/utils/writeClientServices.spec.ts | 5 +- src/utils/writeClientServices.ts | 12 +- src/utils/writeClientSettings.ts | 24 +++ test/mock/v2/spec.json | 195 +++++++++++++++++- 66 files changed, 723 insertions(+), 457 deletions(-) create mode 100644 src/client/interfaces/Schema.ts delete mode 100644 src/client/interfaces/Validation.d.ts delete mode 100644 src/openApi/v2/parser/getOperationResponse.ts create mode 100644 src/openApi/v2/parser/getOperationResults.ts create mode 100644 src/openApi/v3/parser/getServiceVersion.spec.ts create mode 100644 src/openApi/v3/parser/getServiceVersion.ts create mode 100644 src/templates/typescript/core/OpenAPI.hbs delete mode 100644 src/templates/typescript/core/OpenAPI.ts rename src/templates/typescript/{ => partials}/exportArray.hbs (62%) rename src/templates/typescript/{ => partials}/exportDictionary.hbs (62%) rename src/templates/typescript/{ => partials}/exportEnum.hbs (88%) rename src/templates/typescript/{ => partials}/exportGeneric.hbs (62%) rename src/templates/typescript/{ => partials}/exportInterface.hbs (80%) rename src/templates/typescript/{ => partials}/exportReference.hbs (62%) create mode 100644 src/templates/typescript/partials/result.hbs rename src/templates/typescript/{ => partials}/type.hbs (59%) rename src/templates/typescript/{typeForArray.hbs => partials/typeArray.hbs} (62%) rename src/templates/typescript/{typeForDictionary.hbs => partials/typeDictionary.hbs} (60%) rename src/templates/typescript/{typeForEnum.hbs => partials/typeEnum.hbs} (100%) rename src/templates/typescript/{typeForGeneric.hbs => partials/typeGeneric.hbs} (100%) create mode 100644 src/templates/typescript/partials/typeInterface.hbs rename src/templates/typescript/{typeForReference.hbs => partials/typeReference.hbs} (100%) rename src/templates/typescript/{ => partials}/validation.hbs (53%) rename src/templates/typescript/{validationForArray.hbs => partials/validationArray.hbs} (52%) rename src/templates/typescript/{validationForDictionary.hbs => partials/validationDictionary.hbs} (59%) create mode 100644 src/templates/typescript/partials/validationEnum.hbs rename src/templates/typescript/{validationForGeneric.hbs => partials/validationGeneric.hbs} (100%) create mode 100644 src/templates/typescript/partials/validationInterface.hbs rename src/templates/typescript/{validationForReference.hbs => partials/validationReference.hbs} (100%) delete mode 100644 src/templates/typescript/typeForInterface.hbs delete mode 100644 src/templates/typescript/validationForEnum.hbs delete mode 100644 src/templates/typescript/validationForInterface.hbs create mode 100644 src/utils/writeClientSettings.ts diff --git a/src/client/interfaces/Model.d.ts b/src/client/interfaces/Model.d.ts index 3cdc9dd0..4ac56817 100644 --- a/src/client/interfaces/Model.d.ts +++ b/src/client/interfaces/Model.d.ts @@ -1,17 +1,12 @@ import { Enum } from './Enum'; +import { Schema } from './Schema'; -export interface Model { +export interface Model extends Schema { name: string; - export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface'; - type: string; - base: string; - template: string | null; - link: Model | null; - description: string | null; - readOnly: boolean; - required: boolean; - nullable: boolean; - imports: string[]; + isProperty: boolean; + isReadOnly: boolean; + isRequired: boolean; + isNullable: boolean; extends: string[]; enum: Enum[]; enums: Model[]; diff --git a/src/client/interfaces/Operation.d.ts b/src/client/interfaces/Operation.d.ts index 59c91f10..b27f1abb 100644 --- a/src/client/interfaces/Operation.d.ts +++ b/src/client/interfaces/Operation.d.ts @@ -1,5 +1,6 @@ import { OperationError } from './OperationError'; import { OperationParameters } from './OperationParameters'; +import { OperationResponse } from './OperationResponse'; export interface Operation extends OperationParameters { service: string; @@ -10,5 +11,5 @@ export interface Operation extends OperationParameters { method: string; path: string; errors: OperationError[]; - result: string; + results: OperationResponse[]; } diff --git a/src/client/interfaces/OperationError.d.ts b/src/client/interfaces/OperationError.d.ts index 63076f6f..bfb74199 100644 --- a/src/client/interfaces/OperationError.d.ts +++ b/src/client/interfaces/OperationError.d.ts @@ -1,4 +1,4 @@ export interface OperationError { code: number; - text: string; + description: string; } diff --git a/src/client/interfaces/OperationParameter.d.ts b/src/client/interfaces/OperationParameter.d.ts index f0969677..8173a2cd 100644 --- a/src/client/interfaces/OperationParameter.d.ts +++ b/src/client/interfaces/OperationParameter.d.ts @@ -1,19 +1,12 @@ -import { Model } from './Model'; import { Enum } from './Enum'; +import { Schema } from './Schema'; -export interface OperationParameter { +export interface OperationParameter extends Schema { prop: string; in: 'path' | 'query' | 'header' | 'formData' | 'body'; name: string; - export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface'; - type: string; - base: string; - template: string | null; - description: string | null; - required: boolean; - nullable: boolean; - imports: string[]; - enum: Enum[]; - model: Model | null; default: any; + isRequired: boolean; + isNullable: boolean; + enum: Enum[]; } diff --git a/src/client/interfaces/OperationResponse.d.ts b/src/client/interfaces/OperationResponse.d.ts index 5c8d1f92..d1f52153 100644 --- a/src/client/interfaces/OperationResponse.d.ts +++ b/src/client/interfaces/OperationResponse.d.ts @@ -1,8 +1,5 @@ -export interface OperationResponse { +import { Schema } from './Schema'; + +export interface OperationResponse extends Schema { code: number; - text: string; - type: string; - base: string; - template: string | null; - imports: string[]; } diff --git a/src/client/interfaces/Schema.ts b/src/client/interfaces/Schema.ts new file mode 100644 index 00000000..62dcb143 --- /dev/null +++ b/src/client/interfaces/Schema.ts @@ -0,0 +1,11 @@ +import { Model } from './Model'; + +export interface Schema { + export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface'; + type: string; + base: string; + template: string | null; + link: Model | null; + description: string | null; + imports: string[]; +} diff --git a/src/client/interfaces/Validation.d.ts b/src/client/interfaces/Validation.d.ts deleted file mode 100644 index dd9fbad9..00000000 --- a/src/client/interfaces/Validation.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Validation { - type: 'ref' | 'type' | 'enum' | 'array' | 'dictionary' | 'properties'; - childType: string | null; - childBase: string | null; - childValidation: Validation | null; -} diff --git a/src/openApi/v2/index.ts b/src/openApi/v2/index.ts index 16b9bc26..dbfb034b 100644 --- a/src/openApi/v2/index.ts +++ b/src/openApi/v2/index.ts @@ -3,6 +3,7 @@ import { Client } from '../../client/interfaces/Client'; import { getServer } from './parser/getServer'; import { getServices } from './parser/getServices'; import { getModels } from './parser/getModels'; +import { getServiceVersion } from './parser/getServiceVersion'; /** * Parse the OpenAPI specification to a Client model that contains @@ -11,7 +12,7 @@ import { getModels } from './parser/getModels'; */ export function parse(openApi: OpenApi): Client { return { - version: openApi.info.version, + version: getServiceVersion(openApi.info.version), server: getServer(openApi), models: getModels(openApi), services: getServices(openApi), diff --git a/src/openApi/v2/parser/getModel.ts b/src/openApi/v2/parser/getModel.ts index bcc8ac95..f69ea813 100644 --- a/src/openApi/v2/parser/getModel.ts +++ b/src/openApi/v2/parser/getModel.ts @@ -9,18 +9,19 @@ import { getEnum } from './getEnum'; import { getEnumFromDescription } from './getEnumFromDescription'; import { getModelProperties } from './getModelProperties'; -export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: string = ''): Model { - const result: Model = { - name, +export function getModel(openApi: OpenApi, definition: OpenApiSchema, isProperty: boolean = false, name: string = ''): Model { + const model: Model = { + name: name, export: 'interface', type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, template: null, link: null, description: getComment(definition.description), - readOnly: definition.readOnly || false, - required: false, - nullable: false, + isProperty: isProperty, + isReadOnly: definition.readOnly || false, + isRequired: false, + isNullable: false, imports: [], extends: [], enum: [], @@ -30,97 +31,99 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri if (definition.$ref) { const definitionRef = getType(definition.$ref); - result.export = 'reference'; - result.type = definitionRef.type; - result.base = definitionRef.base; - result.template = definitionRef.template; - result.imports.push(...definitionRef.imports); - return result; + model.export = 'reference'; + model.type = definitionRef.type; + model.base = definitionRef.base; + model.template = definitionRef.template; + model.imports.push(...definitionRef.imports); + return model; } if (definition.enum) { const enumerators = getEnum(definition.enum); if (enumerators.length) { - result.export = 'enum'; - result.type = getEnumType(enumerators); - result.base = PrimaryType.STRING; - result.enum.push(...enumerators); - return result; + model.export = 'enum'; + model.type = getEnumType(enumerators); + model.base = PrimaryType.STRING; + model.enum.push(...enumerators); + return model; } } if ((definition.type === 'int' || definition.type === 'integer') && definition.description) { const enumerators = getEnumFromDescription(definition.description); if (enumerators.length) { - result.export = 'enum'; - result.type = getEnumType(enumerators); - result.base = PrimaryType.NUMBER; - result.enum.push(...enumerators); - return result; + model.export = 'enum'; + model.type = getEnumType(enumerators); + model.base = PrimaryType.NUMBER; + model.enum.push(...enumerators); + return model; } } if (definition.type === 'array' && definition.items) { if (definition.items.$ref) { const arrayItems = getType(definition.items.$ref); - result.export = 'array'; - result.type = arrayItems.type; - result.base = arrayItems.base; - result.template = arrayItems.template; - result.imports.push(...arrayItems.imports); + model.export = 'array'; + model.type = arrayItems.type; + model.base = arrayItems.base; + model.template = arrayItems.template; + model.imports.push(...arrayItems.imports); + return model; } else { - const arrayItems = getModel(openApi, definition.items); - result.export = 'array'; - result.type = arrayItems.type; - result.base = arrayItems.base; - result.template = arrayItems.template; - result.link = arrayItems; - result.imports.push(...arrayItems.imports); + const arrayItems = getModel(openApi, definition.items, true); + model.export = 'array'; + model.type = arrayItems.type; + model.base = arrayItems.base; + model.template = arrayItems.template; + model.link = arrayItems; + model.imports.push(...arrayItems.imports); + return model; } - return result; } if (definition.type === 'object' && definition.additionalProperties && typeof definition.additionalProperties === 'object') { if (definition.additionalProperties.$ref) { const additionalProperties = getType(definition.additionalProperties.$ref); - result.export = 'dictionary'; - result.type = additionalProperties.type; - result.base = additionalProperties.base; - result.template = additionalProperties.template; - result.imports.push(...additionalProperties.imports); - result.imports.push('Dictionary'); + model.export = 'dictionary'; + model.type = additionalProperties.type; + model.base = additionalProperties.base; + model.template = additionalProperties.template; + model.imports.push(...additionalProperties.imports); + model.imports.push('Dictionary'); + return model; } else { const additionalProperties = getModel(openApi, definition.additionalProperties); - result.export = 'dictionary'; - result.type = additionalProperties.type; - result.base = additionalProperties.base; - result.template = additionalProperties.template; - result.link = additionalProperties; - result.imports.push(...additionalProperties.imports); - result.imports.push('Dictionary'); + model.export = 'dictionary'; + model.type = additionalProperties.type; + model.base = additionalProperties.base; + model.template = additionalProperties.template; + model.link = additionalProperties; + model.imports.push(...additionalProperties.imports); + model.imports.push('Dictionary'); + return model; } - return result; } if (definition.type === 'object') { - result.export = 'interface'; - result.type = PrimaryType.OBJECT; - result.base = PrimaryType.OBJECT; + model.export = 'interface'; + model.type = PrimaryType.OBJECT; + model.base = PrimaryType.OBJECT; if (definition.allOf) { definition.allOf.forEach(parent => { if (parent.$ref) { const parentRef = getType(parent.$ref); - result.extends.push(parentRef.type); - result.imports.push(parentRef.base); + model.extends.push(parentRef.type); + model.imports.push(parentRef.base); } if (parent.type === 'object' && parent.properties) { const properties = getModelProperties(openApi, parent); properties.forEach(property => { - result.properties.push(property); - result.imports.push(...property.imports); + model.properties.push(property); + model.imports.push(...property.imports); if (property.export === 'enum') { - result.enums.push(property); + model.enums.push(property); } }); } @@ -130,27 +133,27 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri if (definition.properties) { const properties = getModelProperties(openApi, definition); properties.forEach(property => { - result.properties.push(property); - result.imports.push(...property.imports); + model.properties.push(property); + model.imports.push(...property.imports); if (property.export === 'enum') { - result.enums.push(property); + model.enums.push(property); } }); } - return result; + return model; } // If the schema has a type than it can be a basic or generic type. if (definition.type) { const definitionType = getType(definition.type); - result.export = 'generic'; - result.type = definitionType.type; - result.base = definitionType.base; - result.template = definitionType.template; - result.imports.push(...definitionType.imports); - return result; + model.export = 'generic'; + model.type = definitionType.type; + model.base = definitionType.base; + model.template = definitionType.template; + model.imports.push(...definitionType.imports); + return model; } - return result; + return model; } diff --git a/src/openApi/v2/parser/getModelProperties.ts b/src/openApi/v2/parser/getModelProperties.ts index 8abbcf14..07edbd65 100644 --- a/src/openApi/v2/parser/getModelProperties.ts +++ b/src/openApi/v2/parser/getModelProperties.ts @@ -6,53 +6,55 @@ import { Model } from '../../../client/interfaces/Model'; import { getModel } from './getModel'; export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema): Model[] { - const result: Model[] = []; + const models: Model[] = []; 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 prop = getType(property.$ref); - result.push({ + const model = getType(property.$ref); + models.push({ name: propertyName, export: 'reference', - type: prop.type, - base: prop.base, - template: prop.template, + type: model.type, + base: model.base, + template: model.template, link: null, description: getComment(property.description), - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - imports: prop.imports, + isProperty: true, + isReadOnly: propertyReadOnly, + isRequired: propertyRequired, + isNullable: false, + imports: model.imports, extends: [], enum: [], enums: [], properties: [], }); } else { - const prop = getModel(openApi, property); - result.push({ + const model = getModel(openApi, property); + models.push({ name: propertyName, - export: prop.export, - type: prop.type, - base: prop.base, - template: prop.template, - link: prop.link, + export: model.export, + type: model.type, + base: model.base, + template: model.template, + link: model.link, description: getComment(property.description), - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - imports: prop.imports, - extends: prop.extends, - enum: prop.enum, - enums: prop.enums, - properties: prop.properties, + isProperty: true, + isReadOnly: propertyReadOnly, + isRequired: propertyRequired, + isNullable: false, + imports: model.imports, + extends: model.extends, + enum: model.enum, + enums: model.enums, + properties: model.properties, }); } } } - return result; + return models; } diff --git a/src/openApi/v2/parser/getModels.ts b/src/openApi/v2/parser/getModels.ts index 0ba3e7a0..7aeadb01 100644 --- a/src/openApi/v2/parser/getModels.ts +++ b/src/openApi/v2/parser/getModels.ts @@ -9,7 +9,7 @@ export function getModels(openApi: OpenApi): Map { if (openApi.definitions.hasOwnProperty(definitionName)) { const definition = openApi.definitions[definitionName]; const definitionType = getType(definitionName); - const model = getModel(openApi, definition, definitionType.base); + const model = getModel(openApi, definition, false, definitionType.base); models.set(definitionType.base, model); } } diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index f433681c..23007b82 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -6,10 +6,9 @@ import { getOperationPath } from './getOperationPath'; import { OpenApi } from '../interfaces/OpenApi'; import { getComment } from './getComment'; import { Operation } from '../../../client/interfaces/Operation'; -import { PrimaryType } from './constants'; import { getOperationParameters } from './getOperationParameters'; import { getOperationResponses } from './getOperationResponses'; -import { getOperationResponse } from './getOperationResponse'; +import { getOperationResults } from './getOperationResults'; import { getOperationErrors } from './getOperationErrors'; export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation): Operation { @@ -20,7 +19,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op: const operationPath = getOperationPath(url); // Create a new operation object for this method. - const result: Operation = { + const operation: Operation = { service: serviceClassName, name: operationName, summary: getComment(op.summary), @@ -36,30 +35,31 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op: parametersBody: null, imports: [], errors: [], - result: PrimaryType.VOID, + results: [], }; // Parse the operation parameters (path, query, body, etc). if (op.parameters) { const parameters = getOperationParameters(openApi, op.parameters); - result.imports.push(...parameters.imports); - result.parameters.push(...parameters.parameters); - result.parametersPath.push(...parameters.parametersPath); - result.parametersQuery.push(...parameters.parametersQuery); - result.parametersForm.push(...parameters.parametersForm); - result.parametersHeader.push(...parameters.parametersHeader); - result.parametersBody = parameters.parametersBody; + operation.imports.push(...parameters.imports); + operation.parameters.push(...parameters.parameters); + operation.parametersPath.push(...parameters.parametersPath); + operation.parametersQuery.push(...parameters.parametersQuery); + operation.parametersForm.push(...parameters.parametersForm); + operation.parametersHeader.push(...parameters.parametersHeader); + operation.parametersBody = parameters.parametersBody; } // Parse the operation responses. if (op.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; + const operationResponses = getOperationResponses(openApi, op.responses); + const operationResults = getOperationResults(operationResponses); + operation.errors = getOperationErrors(operationResponses); + operationResults.forEach(operationResult => { + operation.results.push(operationResult); + operation.imports.push(...operationResult.imports); + }); } - return result; + return operation; } diff --git a/src/openApi/v2/parser/getOperationErrors.ts b/src/openApi/v2/parser/getOperationErrors.ts index b4152696..2615e074 100644 --- a/src/openApi/v2/parser/getOperationErrors.ts +++ b/src/openApi/v2/parser/getOperationErrors.ts @@ -1,13 +1,13 @@ import { OperationResponse } from '../../../client/interfaces/OperationResponse'; import { OperationError } from '../../../client/interfaces/OperationError'; -export function getOperationErrors(responses: OperationResponse[]): OperationError[] { - return responses - .filter(response => { - return response.code >= 300 && response.text !== undefined && response.text !== ''; +export function getOperationErrors(operationResponses: OperationResponse[]): OperationError[] { + return operationResponses + .filter(operationResponse => { + return operationResponse.code >= 300 && operationResponse.description; }) .map(response => ({ code: response.code, - text: response.text, + description: response.description!, })); } diff --git a/src/openApi/v2/parser/getOperationParameter.ts b/src/openApi/v2/parser/getOperationParameter.ts index bfb000b1..35bc4488 100644 --- a/src/openApi/v2/parser/getOperationParameter.ts +++ b/src/openApi/v2/parser/getOperationParameter.ts @@ -11,95 +11,106 @@ import { getEnumFromDescription } from './getEnumFromDescription'; import { getModel } from './getModel'; export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter { - const result: OperationParameter = { + const operationParameter: OperationParameter = { in: parameter.in, prop: parameter.name, - name: getOperationParameterName(parameter.name), export: 'interface', + name: getOperationParameterName(parameter.name), type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, template: null, + link: null, description: getComment(parameter.description), - required: parameter.required || false, - nullable: false, + default: parameter.default, + isRequired: parameter.required || false, + isNullable: false, imports: [], enum: [], - model: null, - default: null, }; if (parameter.$ref) { const definitionRef = getType(parameter.$ref); - result.export = 'reference'; - result.type = definitionRef.type; - result.base = definitionRef.base; - result.template = definitionRef.template; - result.imports.push(...definitionRef.imports); - return result; + operationParameter.export = 'reference'; + operationParameter.type = definitionRef.type; + operationParameter.base = definitionRef.base; + operationParameter.template = definitionRef.template; + operationParameter.imports.push(...definitionRef.imports); + return operationParameter; } if (parameter.enum) { const enumerators = getEnum(parameter.enum); if (enumerators.length) { - result.export = 'enum'; - result.type = getEnumType(enumerators); - result.base = PrimaryType.STRING; - result.enum.push(...enumerators); - return result; + operationParameter.export = 'enum'; + operationParameter.type = getEnumType(enumerators); + operationParameter.base = PrimaryType.STRING; + operationParameter.enum.push(...enumerators); + return operationParameter; } } if ((parameter.type === 'int' || parameter.type === 'integer') && parameter.description) { const enumerators = getEnumFromDescription(parameter.description); if (enumerators.length) { - result.export = 'enum'; - result.type = getEnumType(enumerators); - result.base = PrimaryType.NUMBER; - result.enum.push(...enumerators); - return result; + operationParameter.export = 'enum'; + operationParameter.type = getEnumType(enumerators); + operationParameter.base = PrimaryType.NUMBER; + operationParameter.enum.push(...enumerators); + return operationParameter; } } - // if (parameter.type === 'array' && parameter.items) { - // const arrayItems = getModel(openApi, parameter.items); - // result.export = 'array'; - // result.type = arrayItems.type; - // result.base = arrayItems.base; - // result.template = arrayItems.template; - // result.link = arrayItems; - // result.imports.push(...arrayItems.imports); - // return result; - // } + if (parameter.type === 'array' && parameter.items) { + const items = getType(parameter.items.type); + operationParameter.export = 'array'; + operationParameter.type = items.type; + operationParameter.base = items.base; + operationParameter.template = items.template; + operationParameter.imports.push(...items.imports); + return operationParameter; + } + + if (parameter.type === 'object' && parameter.items) { + const items = getType(parameter.items.type); + operationParameter.export = 'dictionary'; + operationParameter.type = items.type; + operationParameter.base = items.base; + operationParameter.template = items.template; + operationParameter.imports.push(...items.imports); + return operationParameter; + } if (parameter.schema) { if (parameter.schema.$ref) { const model = getType(parameter.schema.$ref); - result.export = 'reference'; - result.type = model.type; - result.base = model.base; - result.template = model.template; - result.imports.push(...model.imports); + operationParameter.export = 'reference'; + operationParameter.type = model.type; + operationParameter.base = model.base; + operationParameter.template = model.template; + operationParameter.imports.push(...model.imports); + return operationParameter; } else { const model = getModel(openApi, parameter.schema); - result.export = 'interface'; - result.type = model.type; - result.base = model.base; - result.template = model.template; - result.imports.push(...model.imports); - result.model = model; + operationParameter.export = 'interface'; + operationParameter.type = model.type; + operationParameter.base = model.base; + operationParameter.template = model.template; + operationParameter.imports.push(...model.imports); + operationParameter.link = model; + return operationParameter; } } // If the parameter has a type than it can be a basic or generic type. if (parameter.type) { const definitionType = getType(parameter.type); - result.export = 'generic'; - result.type = definitionType.type; - result.base = definitionType.base; - result.template = definitionType.template; - result.imports.push(...definitionType.imports); - return result; + operationParameter.export = 'generic'; + operationParameter.type = definitionType.type; + operationParameter.base = definitionType.base; + operationParameter.template = definitionType.template; + operationParameter.imports.push(...definitionType.imports); + return operationParameter; } - return result; + return operationParameter; } diff --git a/src/openApi/v2/parser/getOperationParameters.ts b/src/openApi/v2/parser/getOperationParameters.ts index 92eb292f..eff8b29f 100644 --- a/src/openApi/v2/parser/getOperationParameters.ts +++ b/src/openApi/v2/parser/getOperationParameters.ts @@ -6,11 +6,11 @@ import { OperationParameter } from '../../../client/interfaces/OperationParamete import { getOperationParameter } from './getOperationParameter'; function sortByRequired(a: OperationParameter, b: OperationParameter): number { - return a.required && !b.required ? -1 : !a.required && b.required ? 1 : 0; + return a.isRequired && !b.isRequired ? -1 : !a.isRequired && b.isRequired ? 1 : 0; } export function getOperationParameters(openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters { - const result: OperationParameters = { + const operationParameters: OperationParameters = { imports: [], parameters: [], parametersPath: [], @@ -30,42 +30,42 @@ export function getOperationParameters(openApi: OpenApi, parameters: OpenApiPara if (param.prop !== 'api-version') { switch (parameter.in) { case 'path': - result.parametersPath.push(param); - result.parameters.push(param); - result.imports.push(...param.imports); + operationParameters.parametersPath.push(param); + operationParameters.parameters.push(param); + operationParameters.imports.push(...param.imports); break; case 'query': - result.parametersQuery.push(param); - result.parameters.push(param); - result.imports.push(...param.imports); + operationParameters.parametersQuery.push(param); + operationParameters.parameters.push(param); + operationParameters.imports.push(...param.imports); break; case 'header': - result.parametersHeader.push(param); - result.parameters.push(param); - result.imports.push(...param.imports); + operationParameters.parametersHeader.push(param); + operationParameters.parameters.push(param); + operationParameters.imports.push(...param.imports); break; case 'formData': - result.parametersForm.push(param); - result.parameters.push(param); - result.imports.push(...param.imports); + operationParameters.parametersForm.push(param); + operationParameters.parameters.push(param); + operationParameters.imports.push(...param.imports); break; case 'body': - result.parametersBody = param; - result.parameters.push(param); - result.imports.push(...param.imports); + operationParameters.parametersBody = param; + operationParameters.parameters.push(param); + operationParameters.imports.push(...param.imports); break; } } }); - result.parameters = result.parameters.sort(sortByRequired); - result.parametersPath = result.parametersPath.sort(sortByRequired); - result.parametersQuery = result.parametersQuery.sort(sortByRequired); - result.parametersForm = result.parametersForm.sort(sortByRequired); - result.parametersHeader = result.parametersHeader.sort(sortByRequired); - return result; + operationParameters.parameters = operationParameters.parameters.sort(sortByRequired); + operationParameters.parametersPath = operationParameters.parametersPath.sort(sortByRequired); + operationParameters.parametersQuery = operationParameters.parametersQuery.sort(sortByRequired); + operationParameters.parametersForm = operationParameters.parametersForm.sort(sortByRequired); + operationParameters.parametersHeader = operationParameters.parametersHeader.sort(sortByRequired); + return operationParameters; } diff --git a/src/openApi/v2/parser/getOperationResponse.ts b/src/openApi/v2/parser/getOperationResponse.ts deleted file mode 100644 index 9e018438..00000000 --- a/src/openApi/v2/parser/getOperationResponse.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { PrimaryType } from './constants'; -import { OperationResponse } from '../../../client/interfaces/OperationResponse'; - -export function getOperationResponse(responses: OperationResponse[]): OperationResponse { - // Fetch the first valid (2XX range) response code and return that type. - const result = responses.find(response => response.code && response.code >= 200 && response.code < 300); - if (result) { - return result; - } - - return { - code: 200, - text: '', - type: PrimaryType.OBJECT, - base: PrimaryType.OBJECT, - template: null, - imports: [], - }; -} diff --git a/src/openApi/v2/parser/getOperationResponses.ts b/src/openApi/v2/parser/getOperationResponses.ts index 21b3b8fc..9dd90fe0 100644 --- a/src/openApi/v2/parser/getOperationResponses.ts +++ b/src/openApi/v2/parser/getOperationResponses.ts @@ -6,9 +6,11 @@ import { OpenApi } from '../interfaces/OpenApi'; import { OperationResponse } from '../../../client/interfaces/OperationResponse'; import { getType } from './getType'; import { getModel } from './getModel'; +import { getComment } from './getComment'; +import { PrimaryType } from './constants'; export function getOperationResponses(openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] { - const results: OperationResponse[] = []; + const operationResponses: OperationResponse[] = []; // Iterate over each response code and get the // status code and response message (if any). @@ -22,12 +24,14 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon // if there is no typed data, we just return so the user is still // free to do their own casting if needed. if (responseCode) { - const result: OperationResponse = { + const operationResponse: OperationResponse = { code: responseCode, - text: response.description || '', - type: 'any', - base: 'any', + description: getComment(response.description)!, + export: 'generic', + type: PrimaryType.OBJECT, + base: PrimaryType.OBJECT, template: null, + link: null, imports: [], }; @@ -38,26 +42,29 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon if (response.schema) { if (response.schema.$ref) { const model = getType(response.schema.$ref); - result.type = model.type; - result.base = model.base; - result.template = model.template; - result.imports.push(...model.imports); + operationResponse.export = 'reference'; + operationResponse.type = model.type; + operationResponse.base = model.base; + operationResponse.template = model.template; + operationResponse.imports.push(...model.imports); } else { const model = getModel(openApi, response.schema); - result.type = model.type; - result.base = model.base; - result.template = model.template; - result.imports.push(...model.imports); + operationResponse.export = model.export; + operationResponse.type = model.type; + operationResponse.base = model.base; + operationResponse.template = model.template; + operationResponse.imports.push(...model.imports); + operationResponse.link = model; } } - results.push(result); + operationResponses.push(operationResponse); } } } // Sort the responses to 2XX success codes come before 4XX and 5XX error codes. - return results.sort((a, b): number => { + return operationResponses.sort((a, b): number => { return a.code < b.code ? -1 : a.code > b.code ? 1 : 0; }); } diff --git a/src/openApi/v2/parser/getOperationResults.ts b/src/openApi/v2/parser/getOperationResults.ts new file mode 100644 index 00000000..47476c23 --- /dev/null +++ b/src/openApi/v2/parser/getOperationResults.ts @@ -0,0 +1,38 @@ +import { PrimaryType } from './constants'; +import { OperationResponse } from '../../../client/interfaces/OperationResponse'; +import { Schema } from '../../../client/interfaces/Schema'; + +function areEqual(a: Schema, b: Schema): boolean { + return a.type === b.type && a.base === b.base && a.template === b.template; +} + +export function getOperationResults(operationResponses: OperationResponse[]): OperationResponse[] { + const operationResults: OperationResponse[] = []; + + operationResponses.forEach(operationResponse => { + if (operationResponse.code && operationResponse.code >= 200 && operationResponse.code < 300) { + operationResults.push(operationResponse); + } + }); + + if (!operationResults.length) { + operationResults.push({ + code: 200, + description: '', + export: 'interface', + type: PrimaryType.OBJECT, + base: PrimaryType.OBJECT, + template: null, + imports: [], + link: null, + }); + } + + return operationResults.filter((operationResult, index, arr) => { + return ( + arr.findIndex(item => { + return areEqual(item, operationResult); + }) === index + ); + }); +} diff --git a/src/openApi/v3/index.ts b/src/openApi/v3/index.ts index 1d063210..39d5033b 100644 --- a/src/openApi/v3/index.ts +++ b/src/openApi/v3/index.ts @@ -3,6 +3,7 @@ import { Client } from '../../client/interfaces/Client'; import { getServer } from './parser/getServer'; import { getModels } from './parser/getModels'; import { getServices } from './parser/getServices'; +import { getServiceVersion } from './parser/getServiceVersion'; /** * Parse the OpenAPI specification to a Client model that contains @@ -11,7 +12,7 @@ import { getServices } from './parser/getServices'; */ export function parse(openApi: OpenApi): Client { return { - version: openApi.info.version, + version: getServiceVersion(openApi.info.version), server: getServer(openApi), models: getModels(openApi), services: getServices(openApi), diff --git a/src/openApi/v3/parser/getServiceVersion.spec.ts b/src/openApi/v3/parser/getServiceVersion.spec.ts new file mode 100644 index 00000000..3e72898b --- /dev/null +++ b/src/openApi/v3/parser/getServiceVersion.spec.ts @@ -0,0 +1,9 @@ +import { getServiceVersion } from './getServiceVersion'; + +describe('getServiceVersion', () => { + it('should produce correct result', () => { + expect(getServiceVersion('1.0')).toEqual('1.0'); + expect(getServiceVersion('v1.0')).toEqual('1.0'); + expect(getServiceVersion('V1.0')).toEqual('1.0'); + }); +}); diff --git a/src/openApi/v3/parser/getServiceVersion.ts b/src/openApi/v3/parser/getServiceVersion.ts new file mode 100644 index 00000000..9986f1af --- /dev/null +++ b/src/openApi/v3/parser/getServiceVersion.ts @@ -0,0 +1,8 @@ +/** + * Convert the service version to 'normal' version. + * This basically removes any "v" prefix from the version string. + * @param version + */ +export function getServiceVersion(version = '1.0'): string { + return version.replace(/^v/gi, ''); +} diff --git a/src/templates/typescript/core/OpenAPI.hbs b/src/templates/typescript/core/OpenAPI.hbs new file mode 100644 index 00000000..e47cff6d --- /dev/null +++ b/src/templates/typescript/core/OpenAPI.hbs @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/* prettier-ignore */ + +export namespace OpenAPI { + export let BASE = '{{{server}}}'; + export let VERSION = '{{{version}}}'; + export let CLIENT = '{{{httpClient}}}'; + export let TOKEN = ''; +} diff --git a/src/templates/typescript/core/OpenAPI.ts b/src/templates/typescript/core/OpenAPI.ts deleted file mode 100644 index e66257c3..00000000 --- a/src/templates/typescript/core/OpenAPI.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -/* prettier-ignore */ - -export namespace OpenAPI { - export let BASE: string = ''; - export let TOKEN: string = ''; - export let VERSION: string = '{VERSION}'; -} diff --git a/src/templates/typescript/core/RequestOptions.ts b/src/templates/typescript/core/RequestOptions.ts index 08c22fb5..39cceadc 100644 --- a/src/templates/typescript/core/RequestOptions.ts +++ b/src/templates/typescript/core/RequestOptions.ts @@ -4,8 +4,7 @@ /* prettier-ignore */ export interface RequestOptions { - type: 'fetch' | 'xhr'; - method: string; + method: 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch'; path: string; headers?: { [key: string]: any }; query?: { [key: string]: any }; diff --git a/src/templates/typescript/core/request.ts b/src/templates/typescript/core/request.ts index 1c1c2657..ff633d92 100644 --- a/src/templates/typescript/core/request.ts +++ b/src/templates/typescript/core/request.ts @@ -3,13 +3,13 @@ /* eslint-disable */ /* prettier-ignore */ -import { getFormData } from './getFormData'; -import { getQueryString } from './getQueryString'; -import { OpenAPI } from './OpenAPI'; -import { RequestOptions } from './RequestOptions'; -import { requestUsingFetch } from './requestUsingFetch'; -import { requestUsingXHR } from './requestUsingXHR'; -import { Result } from './Result'; +import {getFormData} from './getFormData'; +import {getQueryString} from './getQueryString'; +import {OpenAPI} from './OpenAPI'; +import {RequestOptions} from './RequestOptions'; +import {requestUsingFetch} from './requestUsingFetch'; +import {requestUsingXHR} from './requestUsingXHR'; +import {Result} from './Result'; /** * Create the request. @@ -24,14 +24,14 @@ export async function request(options: Readonly): Promi // Create request headers const headers = new Headers({ ...options.headers, - Accept: 'application/json', + Accept: 'application/json' }); // Create request settings const request: RequestInit = { headers, method: options.method, - credentials: 'same-origin', + credentials: 'same-origin' }; // If we have a bearer token then we set the authentication header. @@ -63,11 +63,11 @@ export async function request(options: Readonly): Promi } try { - switch (options.type) { - case 'fetch': - return await requestUsingFetch(url, request); + switch (OpenAPI.CLIENT) { case 'xhr': return await requestUsingXHR(url, request); + default: + return await requestUsingFetch(url, request); } } catch (error) { return { @@ -75,7 +75,7 @@ export async function request(options: Readonly): Promi ok: false, status: 0, statusText: '', - body: error, + body: error }; } } diff --git a/src/templates/typescript/core/requestUsingFetch.ts b/src/templates/typescript/core/requestUsingFetch.ts index 27d217c9..d47fde54 100644 --- a/src/templates/typescript/core/requestUsingFetch.ts +++ b/src/templates/typescript/core/requestUsingFetch.ts @@ -23,7 +23,7 @@ export async function requestUsingFetch(url: string, request: Readonly< ok: response.ok, status: response.status, statusText: response.statusText, - body: null, + body: null }; // Try to parse the content for any response status code. diff --git a/src/templates/typescript/core/requestUsingXHR.ts b/src/templates/typescript/core/requestUsingXHR.ts index 79a74d1d..e7897daf 100644 --- a/src/templates/typescript/core/requestUsingXHR.ts +++ b/src/templates/typescript/core/requestUsingXHR.ts @@ -38,7 +38,7 @@ export async function requestUsingXHR(url: string, request: Readonlytype}}{{#if nullable}} | null{{/if}}; +export type {{{name}}} = {{>type}}{{#if isNullable}} | null{{/if}}; export namespace {{{name}}} { export const schema = {{>validation}}; - export async function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + export async function validate(value: any): Promise<{{{name}}}{{#if isNullable}} | null{{/if}}> { return schema.validate(value, { strict: true }); } - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + export function validateSync(value: any): {{{name}}}{{#if isNullable}} | null{{/if}} { return schema.validateSync(value, { strict: true }); } } diff --git a/src/templates/typescript/exportDictionary.hbs b/src/templates/typescript/partials/exportDictionary.hbs similarity index 62% rename from src/templates/typescript/exportDictionary.hbs rename to src/templates/typescript/partials/exportDictionary.hbs index aa164c47..1e7a09bd 100644 --- a/src/templates/typescript/exportDictionary.hbs +++ b/src/templates/typescript/partials/exportDictionary.hbs @@ -3,17 +3,17 @@ * {{{description}}} */ {{/if}} -export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; +export type {{{name}}} = {{>type}}{{#if isNullable}} | null{{/if}}; export namespace {{{name}}} { export const schema = {{>validation}}; - export async function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + export async function validate(value: any): Promise<{{{name}}}{{#if isNullable}} | null{{/if}}> { return schema.validate(value, { strict: true }); } - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + export function validateSync(value: any): {{{name}}}{{#if isNullable}} | null{{/if}} { return schema.validateSync(value, { strict: true }); } } diff --git a/src/templates/typescript/exportEnum.hbs b/src/templates/typescript/partials/exportEnum.hbs similarity index 88% rename from src/templates/typescript/exportEnum.hbs rename to src/templates/typescript/partials/exportEnum.hbs index 9d449ef8..fa79d73d 100644 --- a/src/templates/typescript/exportEnum.hbs +++ b/src/templates/typescript/partials/exportEnum.hbs @@ -5,7 +5,7 @@ {{/if}} export enum {{{name}}} { {{#each enum}} - {{{name}}} = {{{value}}}, + {{{name}}} = {{{value}}}{{#unless @last}},{{/unless}} {{/each}} } diff --git a/src/templates/typescript/exportGeneric.hbs b/src/templates/typescript/partials/exportGeneric.hbs similarity index 62% rename from src/templates/typescript/exportGeneric.hbs rename to src/templates/typescript/partials/exportGeneric.hbs index aa164c47..1e7a09bd 100644 --- a/src/templates/typescript/exportGeneric.hbs +++ b/src/templates/typescript/partials/exportGeneric.hbs @@ -3,17 +3,17 @@ * {{{description}}} */ {{/if}} -export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; +export type {{{name}}} = {{>type}}{{#if isNullable}} | null{{/if}}; export namespace {{{name}}} { export const schema = {{>validation}}; - export async function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + export async function validate(value: any): Promise<{{{name}}}{{#if isNullable}} | null{{/if}}> { return schema.validate(value, { strict: true }); } - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + export function validateSync(value: any): {{{name}}}{{#if isNullable}} | null{{/if}} { return schema.validateSync(value, { strict: true }); } } diff --git a/src/templates/typescript/exportInterface.hbs b/src/templates/typescript/partials/exportInterface.hbs similarity index 80% rename from src/templates/typescript/exportInterface.hbs rename to src/templates/typescript/partials/exportInterface.hbs index 937014db..5e993958 100644 --- a/src/templates/typescript/exportInterface.hbs +++ b/src/templates/typescript/partials/exportInterface.hbs @@ -10,7 +10,7 @@ export interface {{{name}}}{{#if extends}} extends {{#each extends}}{{{this}}}{{ * {{{description}}} */ {{/if}} - {{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type parent=../name}}{{#if nullable}} | null{{/if}}; + {{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless isRequired}}?{{/unless}}: {{>type parent=../name}}{{#if isNullable}} | null{{/if}}; {{/each}} } @@ -24,7 +24,7 @@ export namespace {{{name}}} { {{/if}} export enum {{{name}}} { {{#each enum}} - {{{name}}} = {{{value}}}, + {{{name}}} = {{{value}}}{{#unless @last}},{{/unless}} {{/each}} } diff --git a/src/templates/typescript/exportReference.hbs b/src/templates/typescript/partials/exportReference.hbs similarity index 62% rename from src/templates/typescript/exportReference.hbs rename to src/templates/typescript/partials/exportReference.hbs index aa164c47..1e7a09bd 100644 --- a/src/templates/typescript/exportReference.hbs +++ b/src/templates/typescript/partials/exportReference.hbs @@ -3,17 +3,17 @@ * {{{description}}} */ {{/if}} -export type {{{name}}} = {{>type}}{{#if nullable}} | null{{/if}}; +export type {{{name}}} = {{>type}}{{#if isNullable}} | null{{/if}}; export namespace {{{name}}} { export const schema = {{>validation}}; - export async function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> { + export async function validate(value: any): Promise<{{{name}}}{{#if isNullable}} | null{{/if}}> { return schema.validate(value, { strict: true }); } - export function validateSync(value: any): {{{name}}}{{#if nullable}} | null{{/if}} { + export function validateSync(value: any): {{{name}}}{{#if isNullable}} | null{{/if}} { return schema.validateSync(value, { strict: true }); } } diff --git a/src/templates/typescript/partials/result.hbs b/src/templates/typescript/partials/result.hbs new file mode 100644 index 00000000..c8f4770f --- /dev/null +++ b/src/templates/typescript/partials/result.hbs @@ -0,0 +1,5 @@ +{{~#if results~}} +{{#each results}}{{>type}}{{#unless @last}} | {{/unless}}{{/each}} +{{~else~}} +void +{{~/if~}} diff --git a/src/templates/typescript/type.hbs b/src/templates/typescript/partials/type.hbs similarity index 59% rename from src/templates/typescript/type.hbs rename to src/templates/typescript/partials/type.hbs index 25b48568..89d9c849 100644 --- a/src/templates/typescript/type.hbs +++ b/src/templates/typescript/partials/type.hbs @@ -1,13 +1,13 @@ {{#eq export 'reference'}} -{{>typeForReference}} +{{>typeReference}} {{else eq export 'generic'}} -{{>typeForGeneric}} +{{>typeGeneric}} {{else eq export 'enum'}} -{{>typeForEnum}} +{{>typeEnum}} {{else eq export 'array'}} -{{>typeForArray}} +{{>typeArray}} {{else eq export 'dictionary'}} -{{>typeForDictionary}} +{{>typeDictionary}} {{else eq export 'interface'}} -{{>typeForInterface}} +{{>typeInterface}} {{/eq}} diff --git a/src/templates/typescript/typeForArray.hbs b/src/templates/typescript/partials/typeArray.hbs similarity index 62% rename from src/templates/typescript/typeForArray.hbs rename to src/templates/typescript/partials/typeArray.hbs index 784faf7a..0fd14c91 100644 --- a/src/templates/typescript/typeForArray.hbs +++ b/src/templates/typescript/partials/typeArray.hbs @@ -1,5 +1,5 @@ {{~#if link~}} -Array<{{>type link child=true}}> +Array<{{>type link}}> {{~else~}} Array<{{{type}}}> {{~/if~}} diff --git a/src/templates/typescript/typeForDictionary.hbs b/src/templates/typescript/partials/typeDictionary.hbs similarity index 60% rename from src/templates/typescript/typeForDictionary.hbs rename to src/templates/typescript/partials/typeDictionary.hbs index a213c65d..413903ee 100644 --- a/src/templates/typescript/typeForDictionary.hbs +++ b/src/templates/typescript/partials/typeDictionary.hbs @@ -1,5 +1,5 @@ {{~#if link~}} -Dictionary<{{>type link child=true}}> +Dictionary<{{>type link}}> {{~else~}} Dictionary<{{{type}}}> {{~/if~}} diff --git a/src/templates/typescript/typeForEnum.hbs b/src/templates/typescript/partials/typeEnum.hbs similarity index 100% rename from src/templates/typescript/typeForEnum.hbs rename to src/templates/typescript/partials/typeEnum.hbs diff --git a/src/templates/typescript/typeForGeneric.hbs b/src/templates/typescript/partials/typeGeneric.hbs similarity index 100% rename from src/templates/typescript/typeForGeneric.hbs rename to src/templates/typescript/partials/typeGeneric.hbs diff --git a/src/templates/typescript/partials/typeInterface.hbs b/src/templates/typescript/partials/typeInterface.hbs new file mode 100644 index 00000000..f8fbc3a0 --- /dev/null +++ b/src/templates/typescript/partials/typeInterface.hbs @@ -0,0 +1,14 @@ +{{~#if properties~}} +{ +{{#each properties}} +{{#if description}} +/** + * {{{description}}} + */ +{{/if}} +{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless isRequired}}?{{/unless}}: {{>type}}{{#if isNullable}} | null{{/if}}{{#unless @last}},{{/unless}} +{{/each}} +} +{{~else~}} +any +{{~/if~}} diff --git a/src/templates/typescript/typeForReference.hbs b/src/templates/typescript/partials/typeReference.hbs similarity index 100% rename from src/templates/typescript/typeForReference.hbs rename to src/templates/typescript/partials/typeReference.hbs diff --git a/src/templates/typescript/validation.hbs b/src/templates/typescript/partials/validation.hbs similarity index 53% rename from src/templates/typescript/validation.hbs rename to src/templates/typescript/partials/validation.hbs index 76eb8b50..4dc63815 100644 --- a/src/templates/typescript/validation.hbs +++ b/src/templates/typescript/partials/validation.hbs @@ -1,13 +1,13 @@ {{#eq export 'reference'}} -{{>validationForReference}} +{{>validationReference}} {{else eq export 'generic'}} -{{>validationForGeneric}} +{{>validationGeneric}} {{else eq export 'enum'}} -{{>validationForEnum}} +{{>validationEnum}} {{else eq export 'array'}} -{{>validationForArray}} +{{>validationArray}} {{else eq export 'dictionary'}} -{{>validationForDictionary}} +{{>validationDictionary}} {{else eq export 'interface'}} -{{>validationForInterface}} +{{>validationInterface}} {{/eq}} diff --git a/src/templates/typescript/validationForArray.hbs b/src/templates/typescript/partials/validationArray.hbs similarity index 52% rename from src/templates/typescript/validationForArray.hbs rename to src/templates/typescript/partials/validationArray.hbs index a88f311d..a51849a9 100644 --- a/src/templates/typescript/validationForArray.hbs +++ b/src/templates/typescript/partials/validationArray.hbs @@ -1,5 +1,5 @@ {{~#if link~}} -yup.array<{{>type link child=true}}>().of({{>validation link child=true}}) +yup.array<{{>type link}}>().of({{>validation link}}) {{~else~}} yup.array<{{{type}}}>().of({{{base}}}.schema) {{~/if~}} diff --git a/src/templates/typescript/validationForDictionary.hbs b/src/templates/typescript/partials/validationDictionary.hbs similarity index 59% rename from src/templates/typescript/validationForDictionary.hbs rename to src/templates/typescript/partials/validationDictionary.hbs index 9e4139ca..d0a2a4bf 100644 --- a/src/templates/typescript/validationForDictionary.hbs +++ b/src/templates/typescript/partials/validationDictionary.hbs @@ -1,9 +1,9 @@ {{~#if link~}} -yup.lazytype link child=true}}>>(value => { - return yup.objecttype link child=true}}>>().shape( +yup.lazytype link}}>>(value => { + return yup.objecttype link}}>>().shape( Object.entries(value).reduce((obj, item) => ({ ...obj, - [item[0]]: {{>validation link child=true}}, + [item[0]]: {{>validation link}} }), {}) ); }) @@ -12,7 +12,7 @@ yup.lazy>(value => { return yup.object>().shape( Object.entries(value).reduce((obj, item) => ({ ...obj, - [item[0]]: {{{base}}}.schema, + [item[0]]: {{{base}}}.schema }), {}) ); }) diff --git a/src/templates/typescript/partials/validationEnum.hbs b/src/templates/typescript/partials/validationEnum.hbs new file mode 100644 index 00000000..b3f27b2b --- /dev/null +++ b/src/templates/typescript/partials/validationEnum.hbs @@ -0,0 +1,9 @@ +yup.mixed<{{{name}}}>().oneOf([ + {{#each enum}} + {{#eq ../name name}} + {{{value}}}{{#unless @last}},{{/unless}} + {{else}} + {{{../name}}}.{{{name}}}{{#unless @last}},{{/unless}} + {{/eq}} + {{/each}} +]) diff --git a/src/templates/typescript/validationForGeneric.hbs b/src/templates/typescript/partials/validationGeneric.hbs similarity index 100% rename from src/templates/typescript/validationForGeneric.hbs rename to src/templates/typescript/partials/validationGeneric.hbs diff --git a/src/templates/typescript/partials/validationInterface.hbs b/src/templates/typescript/partials/validationInterface.hbs new file mode 100644 index 00000000..a9c8a7d5 --- /dev/null +++ b/src/templates/typescript/partials/validationInterface.hbs @@ -0,0 +1,21 @@ +( +{{#if extends}} +{{#each extends}} +{{{this}}}.schema.concat( +{{/each}} +{{/if}} +{{#if properties}} +yup.object{{#unless isProperty}}{{#if name}}<{{{name}}}>{{/if}}{{/unless}}().shape({ + {{#each properties}} + {{{name}}}: yup.lazy(() => {{>validation}}.default(undefined){{#if isNullable}}.isNullable(){{/if}}){{#if isRequired}}.isRequired(){{/if}}{{#unless @last}},{{/unless}} + {{/each}} +}).noUnknown() +{{else}} +yup.object() +{{/if}} +{{#if extends}} +{{#each extends}} +) +{{/each}} +{{/if}} +) diff --git a/src/templates/typescript/validationForReference.hbs b/src/templates/typescript/partials/validationReference.hbs similarity index 100% rename from src/templates/typescript/validationForReference.hbs rename to src/templates/typescript/partials/validationReference.hbs diff --git a/src/templates/typescript/service.hbs b/src/templates/typescript/service.hbs index b41d59c1..b8db335b 100644 --- a/src/templates/typescript/service.hbs +++ b/src/templates/typescript/service.hbs @@ -2,8 +2,8 @@ /* tslint:disable */ /* eslint-disable */ /* prettier-ignore */ -{{#if imports}} +{{#if imports}} {{#each imports}} import { {{{this}}} } from '../models/{{{this}}}'; {{/each}} @@ -11,7 +11,6 @@ import { {{{this}}} } from '../models/{{{this}}}'; import { ApiError, catchGenericError } from '../core/ApiError'; import { request } from '../core/request'; import { OpenAPI } from '../core/OpenAPI'; -import { Result } from '../core/Result'; export class {{{name}}} { @@ -31,37 +30,43 @@ export class {{{name}}} { * @param {{{name}}} {{{description}}} {{/each}} {{/if}} - * @return {{{result}}} + {{#each results}} + * @result {{{type}}} {{{description}}} + {{/each}} + * @throws ApiError */ - public static async {{{name}}}({{#each parameters}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}}{{#unless @last}}, {{/unless}}{{/each}}): Promise<{{{result}}}> { + public static async {{{name}}}({{#if parameters}} + {{#each parameters}} + {{{name}}}{{#unless isRequired}}?{{/unless}}: {{>type}}{{#if isNullable}} | null{{/if}}{{#if default}} = {{{default}}}{{/if}}{{#unless @last}},{{/unless}} + {{/each}} + {{/if}}): Promise<{{>result}}> { - const result: Result<{{{result}}}> = await request({ - type: '{{{../httpClient}}}', + const result = await request({ method: '{{{method}}}', - path: `{{{path}}}`, - {{#if parametersHeader}} + path: `{{{path}}}` + {{~#if parametersHeader~}}, headers: { {{#each parametersHeader}} - '{{{prop}}}': {{{name}}}, + '{{{prop}}}': {{{name}}}{{#unless @last}},{{/unless}} {{/each}} - }, - {{/if}} - {{#if parametersQuery}} + } + {{~/if~}} + {{~#if parametersQuery~}}, query: { {{#each parametersQuery}} - '{{{prop}}}': {{{name}}}, + '{{{prop}}}': {{{name}}}{{#unless @last}},{{/unless}} {{/each}} - }, - {{/if}} - {{#if parametersForm}} + } + {{~/if~}} + {{~#if parametersForm~}}, formData: { {{#each parametersForm}} - '{{{prop}}}': {{{name}}}, + '{{{prop}}}': {{{name}}}{{#unless @last}},{{/unless}} {{/each}} - }, - {{/if}} - {{#if parametersBody}} - body: {{{parametersBody.name}}}, + } + {{~/if~}} + {{~#if parametersBody~}}, + body: {{{parametersBody.name}}} {{/if}} }); {{#if errors}} @@ -69,7 +74,7 @@ export class {{{name}}} { if (!result.ok) { switch (result.status) { {{#each errors}} - case {{{code}}}: throw new ApiError(result, `{{{text}}}`); + case {{{code}}}: throw new ApiError(result, `{{{description}}}`); {{/each}} } } diff --git a/src/templates/typescript/typeForInterface.hbs b/src/templates/typescript/typeForInterface.hbs deleted file mode 100644 index 7f937ca8..00000000 --- a/src/templates/typescript/typeForInterface.hbs +++ /dev/null @@ -1,14 +0,0 @@ -{{~#if properties~}} -{ -{{#each properties}} -{{#if description}} -/** - * {{{description}}} - */ -{{/if}} -{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{>type child=true}}{{#if nullable}} | null{{/if}}, -{{/each}} -} -{{~else~}} -any -{{~/if~}} diff --git a/src/templates/typescript/validationForEnum.hbs b/src/templates/typescript/validationForEnum.hbs deleted file mode 100644 index 7dd5c8d4..00000000 --- a/src/templates/typescript/validationForEnum.hbs +++ /dev/null @@ -1,9 +0,0 @@ -yup.mixed<{{{name}}}>().oneOf([ - {{#each enum}} - {{#unless child}} - {{{../name}}}.{{{name}}}, - {{else}} - {{{value}}}, - {{/unless}} - {{/each}} -]) diff --git a/src/templates/typescript/validationForInterface.hbs b/src/templates/typescript/validationForInterface.hbs deleted file mode 100644 index dbd3d2ee..00000000 --- a/src/templates/typescript/validationForInterface.hbs +++ /dev/null @@ -1,21 +0,0 @@ -( -{{#if extends}} -{{#each extends}} -{{{this}}}.schema.concat( -{{/each}} -{{/if}} -{{#if properties}} -yup.object{{#unless child}}<{{{name}}}>{{/unless}}().shape({ - {{#each properties}} - {{{name}}}: yup.lazy(() => {{>validation child=true}}.default(undefined){{#if nullable}}.nullable(){{/if}}){{#if required}}.required(){{/if}}, - {{/each}} -}).noUnknown() -{{else}} -yup.object() -{{/if}} -{{#if extends}} -{{#each extends}} -) -{{/each}} -{{/if}} -) diff --git a/src/utils/format.ts b/src/utils/format.ts index 4513923d..05157547 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -9,7 +9,7 @@ export function format(s: string): string { if (line.endsWith('(') || line.endsWith('{') || line.endsWith('[')) { indent++; } - if (line.startsWith(')') || line.startsWith('}') || line.startsWith(']')) { + if ((line.startsWith(')') || line.startsWith('}') || line.startsWith(']')) && i) { indent--; i--; } diff --git a/src/utils/getModelNames.spec.ts b/src/utils/getModelNames.spec.ts index 473a2933..5acc47fb 100644 --- a/src/utils/getModelNames.spec.ts +++ b/src/utils/getModelNames.spec.ts @@ -12,9 +12,10 @@ describe('getModelNames', () => { template: null, link: null, description: null, - readOnly: false, - required: false, - nullable: false, + isProperty: false, + isReadOnly: false, + isRequired: false, + isNullable: false, imports: [], extends: [], enum: [], @@ -29,9 +30,10 @@ describe('getModelNames', () => { template: null, link: null, description: null, - readOnly: false, - required: false, - nullable: false, + isProperty: false, + isReadOnly: false, + isRequired: false, + isNullable: false, imports: [], extends: [], enum: [], @@ -46,9 +48,10 @@ describe('getModelNames', () => { template: null, link: null, description: null, - readOnly: false, - required: false, - nullable: false, + isProperty: false, + isReadOnly: false, + isRequired: false, + isNullable: false, imports: [], extends: [], enum: [], diff --git a/src/utils/readHandlebarsTemplates.ts b/src/utils/readHandlebarsTemplates.ts index 1c83c365..3be404ea 100644 --- a/src/utils/readHandlebarsTemplates.ts +++ b/src/utils/readHandlebarsTemplates.ts @@ -3,11 +3,13 @@ import { readHandlebarsTemplate } from './readHandlebarsTemplate'; import { Language } from '../index'; import * as path from 'path'; import { registerHandlebarHelpers } from './registerHandlebarHelpers'; +import * as glob from 'glob'; export interface Templates { index: Handlebars.TemplateDelegate; model: Handlebars.TemplateDelegate; service: Handlebars.TemplateDelegate; + settings: Handlebars.TemplateDelegate; } /** @@ -23,33 +25,17 @@ export function readHandlebarsTemplates(language: Language): Templates { index: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/index.hbs`)), model: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/model.hbs`)), service: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/service.hbs`)), + settings: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/core/OpenAPI.hbs`)), }; - Handlebars.registerPartial({ - exportGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportGeneric.hbs`)), - exportReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportReference.hbs`)), - exportInterface: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportInterface.hbs`)), - exportEnum: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportEnum.hbs`)), - exportDictionary: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportDictionary.hbs`)), - exportArray: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/exportArray.hbs`)), - validation: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validation.hbs`)), - validationForGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForGeneric.hbs`)), - validationForReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForReference.hbs`)), - validationForEnum: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForEnum.hbs`)), - validationForInterface: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForInterface.hbs`)), - validationForDictionary: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForDictionary.hbs`)), - validationForArray: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/validationForArray.hbs`)), - type: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/type.hbs`)), - typeForArray: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForArray.hbs`)), - typeForDictionary: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForDictionary.hbs`)), - typeForEnum: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForEnum.hbs`)), - typeForInterface: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForInterface.hbs`)), - typeForReference: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForReference.hbs`)), - typeForGeneric: readHandlebarsTemplate(path.resolve(__dirname, `../../src/templates/${language}/typeForGeneric.hbs`)), + const partials = path.resolve(__dirname, `../../src/templates/${language}/partials`); + const partialsFiles = glob.sync('*.hbs', { cwd: partials }); + partialsFiles.forEach(partial => { + Handlebars.registerPartial(path.basename(partial, '.hbs'), readHandlebarsTemplate(path.resolve(partials, partial))); }); return templates; } catch (e) { - throw e; + throw new Error(`Could not read Handlebar templates`); } } diff --git a/src/utils/writeClient.spec.ts b/src/utils/writeClient.spec.ts index 9f0a4cc5..d8d045ae 100644 --- a/src/utils/writeClient.spec.ts +++ b/src/utils/writeClient.spec.ts @@ -4,7 +4,7 @@ import * as rimraf from 'rimraf'; import * as fs from 'fs'; import { Client } from '../client/interfaces/Client'; import { Templates } from './readHandlebarsTemplates'; -import { Language } from '../index'; +import { HttpClient, Language } from '../index'; import * as glob from 'glob'; import { Model } from '../client/interfaces/Model'; import { Service } from '../client/interfaces/Service'; @@ -32,11 +32,12 @@ describe('writeClient', () => { index: () => 'dummy', model: () => 'dummy', service: () => 'dummy', + settings: () => 'dummy', }; globSync.mockReturnValue([]); - writeClient(client, Language.TYPESCRIPT, templates, '/'); + writeClient(client, Language.TYPESCRIPT, HttpClient.FETCH, templates, '/'); expect(rimrafSync).toBeCalled(); expect(mkdirpSync).toBeCalled(); diff --git a/src/utils/writeClient.ts b/src/utils/writeClient.ts index 8e9e449e..29769cb3 100644 --- a/src/utils/writeClient.ts +++ b/src/utils/writeClient.ts @@ -9,6 +9,7 @@ import { writeClientIndex } from './writeClientIndex'; import { HttpClient, Language } from '../index'; import * as fs from 'fs'; import * as glob from 'glob'; +import { writeClientSettings } from './writeClientSettings'; /** * Write our OpenAPI client, using the given templates at the given output path. @@ -43,18 +44,19 @@ export function writeClient(client: Client, language: Language, httpClient: Http // Copy all support files const supportFiles = path.resolve(__dirname, `../../src/templates/${language}/`); const supportFilesList = glob.sync('**/*.{ts,js}', { cwd: supportFiles }); - supportFilesList.forEach(file => + supportFilesList.forEach(file => { fs.copyFileSync( path.resolve(supportFiles, file), // From input path path.resolve(outputPath, file) // To output path - ) - ); + ); + }); // Write the client files try { writeClientIndex(client, language, templates, outputPath); - writeClientModels(client.models, language, httpClient, templates, outputPathModels); - writeClientServices(client.services, language, httpClient, templates, outputPathServices); + writeClientModels(client.models, language, templates, outputPathModels); + writeClientServices(client.services, language, templates, outputPathServices); + writeClientSettings(client, language, httpClient, templates, outputPathCore); } catch (e) { throw e; } diff --git a/src/utils/writeClientIndex.spec.ts b/src/utils/writeClientIndex.spec.ts index 2ce68bb7..104b2b6b 100644 --- a/src/utils/writeClientIndex.spec.ts +++ b/src/utils/writeClientIndex.spec.ts @@ -14,7 +14,7 @@ describe('writeClientIndex', () => { it('should write to filesystem', () => { const client: Client = { server: 'http://localhost:8080', - version: 'v1', + version: '1.0', models: new Map(), services: new Map(), }; @@ -22,6 +22,7 @@ describe('writeClientIndex', () => { index: () => 'dummy', model: () => 'dummy', service: () => 'dummy', + settings: () => 'dummy', }; writeClientIndex(client, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/index.ts', 'dummy'); diff --git a/src/utils/writeClientIndex.ts b/src/utils/writeClientIndex.ts index 902c52a4..555c5014 100644 --- a/src/utils/writeClientIndex.ts +++ b/src/utils/writeClientIndex.ts @@ -29,6 +29,7 @@ export function writeClientIndex(client: Client, language: Language, templates: }) ); } catch (e) { + console.log(e); throw new Error(`Could not write index: "${fileName}"`); } } diff --git a/src/utils/writeClientModels.spec.ts b/src/utils/writeClientModels.spec.ts index 6bb2af26..b55604d6 100644 --- a/src/utils/writeClientModels.spec.ts +++ b/src/utils/writeClientModels.spec.ts @@ -1,7 +1,7 @@ import { writeClientModels } from './writeClientModels'; import * as fs from 'fs'; import { Model } from '../client/interfaces/Model'; -import { HttpClient, Language } from '../index'; +import { Language } from '../index'; import { Templates } from './readHandlebarsTemplates'; jest.mock('fs'); @@ -19,9 +19,10 @@ describe('writeClientModels', () => { template: null, link: null, description: null, - readOnly: false, - required: false, - nullable: false, + isProperty: false, + isReadOnly: false, + isRequired: false, + isNullable: false, imports: [], extends: [], enum: [], @@ -32,8 +33,9 @@ describe('writeClientModels', () => { index: () => 'dummy', model: () => 'dummy', service: () => 'dummy', + settings: () => 'dummy', }; - writeClientModels(models, Language.TYPESCRIPT, HttpClient.FETCH, templates, '/'); + writeClientModels(models, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy'); }); }); diff --git a/src/utils/writeClientModels.ts b/src/utils/writeClientModels.ts index 2e70d7a9..6b0982ba 100644 --- a/src/utils/writeClientModels.ts +++ b/src/utils/writeClientModels.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import { Model } from '../client/interfaces/Model'; import * as path from 'path'; -import { HttpClient, Language } from '../index'; +import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportModel } from './exportModel'; import { Templates } from './readHandlebarsTemplates'; @@ -11,22 +11,18 @@ import { format } from './format'; * 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 httpClient The selected httpClient (fetch or XHR). * @param templates The loaded handlebar templates. * @param outputPath */ -export function writeClientModels(models: Map, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void { +export function writeClientModels(models: Map, language: Language, templates: Templates, outputPath: string): void { models.forEach(model => { const fileName = getFileName(model.name, language); try { const templateData = exportModel(model); - const templateResult = templates.model({ - language, - httpClient, - ...templateData, - }); + const templateResult = templates.model(templateData); fs.writeFileSync(path.resolve(outputPath, fileName), format(templateResult)); } catch (e) { + console.log(e); throw new Error(`Could not write model: "${fileName}"`); } }); diff --git a/src/utils/writeClientServices.spec.ts b/src/utils/writeClientServices.spec.ts index cf16df65..de7adcb7 100644 --- a/src/utils/writeClientServices.spec.ts +++ b/src/utils/writeClientServices.spec.ts @@ -1,7 +1,7 @@ import { writeClientServices } from './writeClientServices'; import * as fs from 'fs'; import { Service } from '../client/interfaces/Service'; -import { HttpClient, Language } from '../index'; +import { Language } from '../index'; import { Templates } from './readHandlebarsTemplates'; jest.mock('fs'); @@ -20,8 +20,9 @@ describe('writeClientServices', () => { index: () => 'dummy', model: () => 'dummy', service: () => 'dummy', + settings: () => 'dummy', }; - writeClientServices(services, Language.TYPESCRIPT, HttpClient.FETCH, templates, '/'); + writeClientServices(services, Language.TYPESCRIPT, templates, '/'); expect(fsWriteFileSync).toBeCalledWith('/Item.ts', 'dummy'); }); }); diff --git a/src/utils/writeClientServices.ts b/src/utils/writeClientServices.ts index d8f865f7..24495fe7 100644 --- a/src/utils/writeClientServices.ts +++ b/src/utils/writeClientServices.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { Service } from '../client/interfaces/Service'; -import { HttpClient, Language } from '../index'; +import { Language } from '../index'; import { getFileName } from './getFileName'; import { exportService } from './exportService'; import { Templates } from './readHandlebarsTemplates'; @@ -11,22 +11,18 @@ import { format } from './format'; * 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 httpClient The selected httpClient (fetch or XHR). * @param templates The loaded handlebar templates. * @param outputPath */ -export function writeClientServices(services: Map, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void { +export function writeClientServices(services: Map, language: Language, templates: Templates, outputPath: string): void { services.forEach(service => { const fileName = getFileName(service.name, language); try { const templateData = exportService(service); - const templateResult = templates.service({ - language, - httpClient, - ...templateData, - }); + const templateResult = templates.service(templateData); fs.writeFileSync(path.resolve(outputPath, fileName), format(templateResult)); } catch (e) { + console.log(e); throw new Error(`Could not write service: "${fileName}"`); } }); diff --git a/src/utils/writeClientSettings.ts b/src/utils/writeClientSettings.ts new file mode 100644 index 00000000..f4542fa5 --- /dev/null +++ b/src/utils/writeClientSettings.ts @@ -0,0 +1,24 @@ +import { Client } from '../client/interfaces/Client'; +import * as fs from 'fs'; +import * as path from 'path'; +import { HttpClient, Language } from '../index'; +import { getFileName } from './getFileName'; +import { Templates } from './readHandlebarsTemplates'; + +export function writeClientSettings(client: Client, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void { + const fileName = getFileName('OpenAPI', language); + try { + fs.writeFileSync( + path.resolve(outputPath, fileName), + templates.settings({ + language, + httpClient, + server: client.server, + version: client.version, + }) + ); + } catch (e) { + console.log(e); + throw new Error(`Could not write settings: "${fileName}"`); + } +} diff --git a/test/mock/v2/spec.json b/test/mock/v2/spec.json index 9908d14c..02c56978 100644 --- a/test/mock/v2/spec.json +++ b/test/mock/v2/spec.json @@ -10,12 +10,203 @@ "http" ], "paths": { - "/api/v{api-version}/dummy": { + "/api/v{api-version}/simple": { "get": { "tags": [ - "Service" + "Simple" ], "operationId": "GetCallWithoutParametersAndResponse" + }, + "put": { + "tags": [ + "Simple" + ], + "operationId": "PutCallWithoutParametersAndResponse" + }, + "post": { + "tags": [ + "Simple" + ], + "operationId": "PostCallWithoutParametersAndResponse" + }, + "delete": { + "tags": [ + "Simple" + ], + "operationId": "DeleteCallWithoutParametersAndResponse" + }, + "options": { + "tags": [ + "Simple" + ], + "operationId": "OptionsCallWithoutParametersAndResponse" + }, + "head": { + "tags": [ + "Simple" + ], + "operationId": "HeadCallWithoutParametersAndResponse" + }, + "patch": { + "tags": [ + "Simple" + ], + "operationId": "PatchCallWithoutParametersAndResponse" + } + }, + "/api/v{api-version}/parameters": { + "get": { + "tags": [ + "Parameters" + ], + "operationId": "CallWithParameters", + "parameters": [ + { + "description": "This is the parameter that goes into the request header", + "name": "parameterHeader", + "in": "header", + "required": true, + "type": "string" + }, + { + "description": "This is the parameter that goes into the request query params", + "name": "parameterQuery", + "in": "query", + "required": true, + "type": "string" + }, + { + "description": "This is the parameter that goes into the request form data", + "name": "parameterForm", + "in": "formData", + "required": true, + "type": "string" + }, + { + "description": "This is the parameter that is send as request body", + "name": "parameterBody", + "in": "body", + "required": true, + "type": "string" + }, + { + "name": "api-version", + "in": "path", + "required": true, + "type": "string" + } + ] + } + }, + "/api/v{api-version}/response": { + "get": { + "tags": [ + "Response" + ], + "operationId": "CallWithResponse", + "responses": { + "default": { + "description": "Message for default response", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + } + } + }, + "post": { + "tags": [ + "Response" + ], + "operationId": "CallWithDuplicateResponses", + "responses": { + "default": { + "description": "Message for default response", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "201": { + "description": "Message for 201 response", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "202": { + "description": "Message for 202 response", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "500": { + "description": "Message for 500 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "501": { + "description": "Message for 501 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "502": { + "description": "Message for 502 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + } + } + }, + "put": { + "tags": [ + "Response" + ], + "operationId": "CallWithResponses", + "responses": { + "default": { + "description": "Message for default response", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "201": { + "description": "Message for 201 response", + "schema": { + "$ref": "#/definitions/ModelThatExtends" + } + }, + "202": { + "description": "Message for 202 response", + "schema": { + "$ref": "#/definitions/ModelThatExtendsExtends" + } + }, + "500": { + "description": "Message for 500 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "501": { + "description": "Message for 501 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + }, + "502": { + "description": "Message for 502 error", + "schema": { + "$ref": "#/definitions/ModelWithString" + } + } + } + } + }, + "/api/v{api-version}/complex": { + "get": { + "tags": [ + "Complex" + ] } } },