- Added service generation test cases

- Cleanup of models
- Cleanup of comma generatiom
- Formatted results
This commit is contained in:
Ferdi Koomen 2019-11-22 22:57:34 +01:00
parent 20209f7aa2
commit fbd354ebfb
66 changed files with 723 additions and 457 deletions

View File

@ -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[];

View File

@ -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[];
}

View File

@ -1,4 +1,4 @@
export interface OperationError {
code: number;
text: string;
description: string;
}

View File

@ -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[];
}

View File

@ -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[];
}

View File

@ -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[];
}

View File

@ -1,6 +0,0 @@
export interface Validation {
type: 'ref' | 'type' | 'enum' | 'array' | 'dictionary' | 'properties';
childType: string | null;
childBase: string | null;
childValidation: Validation | null;
}

View File

@ -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),

View File

@ -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;
}

View File

@ -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;
}

View File

@ -9,7 +9,7 @@ export function getModels(openApi: OpenApi): Map<string, Model> {
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);
}
}

View File

@ -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;
}

View File

@ -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!,
}));
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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: [],
};
}

View File

@ -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 <any> 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;
});
}

View File

@ -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
);
});
}

View File

@ -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),

View File

@ -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');
});
});

View File

@ -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, '');
}

View File

@ -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 = '';
}

View File

@ -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}';
}

View File

@ -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 };

View File

@ -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<T = any>(options: Readonly<RequestOptions>): 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<T = any>(options: Readonly<RequestOptions>): Promi
}
try {
switch (options.type) {
case 'fetch':
return await requestUsingFetch<T>(url, request);
switch (OpenAPI.CLIENT) {
case 'xhr':
return await requestUsingXHR<T>(url, request);
default:
return await requestUsingFetch<T>(url, request);
}
} catch (error) {
return {
@ -75,7 +75,7 @@ export async function request<T = any>(options: Readonly<RequestOptions>): Promi
ok: false,
status: 0,
statusText: '',
body: error,
body: error
};
}
}

View File

@ -23,7 +23,7 @@ export async function requestUsingFetch<T = any>(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.

View File

@ -38,7 +38,7 @@ export async function requestUsingXHR<T = any>(url: string, request: Readonly<Re
ok: isSuccess(xhr.status),
status: xhr.status,
statusText: xhr.statusText,
body: null,
body: null
};
// Try to parse the content for any response status code.

View File

@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
{{#if imports}}
{{#if imports}}
{{#each imports}}
import { {{{this}}} } from '../models/{{{this}}}';
{{/each}}

View File

@ -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 });
}
}

View File

@ -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 });
}
}

View File

@ -5,7 +5,7 @@
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{{name}}} = {{{value}}},
{{{name}}} = {{{value}}}{{#unless @last}},{{/unless}}
{{/each}}
}

View File

@ -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 });
}
}

View File

@ -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}}
}

View File

@ -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 });
}
}

View File

@ -0,0 +1,5 @@
{{~#if results~}}
{{#each results}}{{>type}}{{#unless @last}} | {{/unless}}{{/each}}
{{~else~}}
void
{{~/if~}}

View File

@ -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}}

View File

@ -1,5 +1,5 @@
{{~#if link~}}
Array<{{>type link child=true}}>
Array<{{>type link}}>
{{~else~}}
Array<{{{type}}}>
{{~/if~}}

View File

@ -1,5 +1,5 @@
{{~#if link~}}
Dictionary<{{>type link child=true}}>
Dictionary<{{>type link}}>
{{~else~}}
Dictionary<{{{type}}}>
{{~/if~}}

View File

@ -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~}}

View File

@ -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}}

View File

@ -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~}}

View File

@ -1,9 +1,9 @@
{{~#if link~}}
yup.lazy<Dictionary<{{>type link child=true}}>>(value => {
return yup.object<Dictionary<{{>type link child=true}}>>().shape(
yup.lazy<Dictionary<{{>type link}}>>(value => {
return yup.object<Dictionary<{{>type 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<Dictionary<{{{type}}}>>(value => {
return yup.object<Dictionary<{{{type}}}>>().shape(
Object.entries(value).reduce((obj, item) => ({
...obj,
[item[0]]: {{{base}}}.schema,
[item[0]]: {{{base}}}.schema
}), {})
);
})

View File

@ -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}}
])

View File

@ -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<any>()
{{/if}}
{{#if extends}}
{{#each extends}}
)
{{/each}}
{{/if}}
)

View File

@ -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}}
}
}

View File

@ -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~}}

View File

@ -1,9 +0,0 @@
yup.mixed<{{{name}}}>().oneOf([
{{#each enum}}
{{#unless child}}
{{{../name}}}.{{{name}}},
{{else}}
{{{value}}},
{{/unless}}
{{/each}}
])

View File

@ -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<any>()
{{/if}}
{{#if extends}}
{{#each extends}}
)
{{/each}}
{{/if}}
)

View File

@ -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--;
}

View File

@ -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: [],

View File

@ -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`);
}
}

View File

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

View File

@ -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;
}

View File

@ -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<string, Model>(),
services: new Map<string, Service>(),
};
@ -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');

View File

@ -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}"`);
}
}

View File

@ -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');
});
});

View File

@ -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<string, Model>, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void {
export function writeClientModels(models: Map<string, Model>, language: Language, templates: Templates, outputPath: string): void {
models.forEach(model => {
const fileName = getFileName(model.name, language);
try {
const templateData = exportModel(model);
const templateResult = 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}"`);
}
});

View File

@ -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');
});
});

View File

@ -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<string, Service>, language: Language, httpClient: HttpClient, templates: Templates, outputPath: string): void {
export function writeClientServices(services: Map<string, Service>, language: Language, templates: Templates, outputPath: string): void {
services.forEach(service => {
const fileName = getFileName(service.name, language);
try {
const templateData = exportService(service);
const templateResult = 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}"`);
}
});

View File

@ -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}"`);
}
}

View File

@ -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"
]
}
}
},