diff --git a/src/client/interfaces/Enum.ts b/src/client/interfaces/Enum.d.ts similarity index 100% rename from src/client/interfaces/Enum.ts rename to src/client/interfaces/Enum.d.ts diff --git a/src/client/interfaces/Model.d.ts b/src/client/interfaces/Model.d.ts index 043052a4..2313fb42 100644 --- a/src/client/interfaces/Model.d.ts +++ b/src/client/interfaces/Model.d.ts @@ -1,15 +1,18 @@ -import { ModelProperty } from './ModelProperty'; import { Enum } from './Enum'; +import { Validation } from './Validation'; export interface Model { name: string; type: string; base: string; - template: string | null; - description: string | null; - validation: string | null; - extends: string[]; + template?: string; + description?: string; + readOnly?: boolean; + required?: boolean; + nullable?: boolean; imports: string[]; + extends: string[]; enum: Enum[]; - properties: ModelProperty[]; + properties: Model[]; + validation?: Validation; } diff --git a/src/client/interfaces/ModelProperty.d.ts b/src/client/interfaces/ModelProperty.d.ts deleted file mode 100644 index 48555e6a..00000000 --- a/src/client/interfaces/ModelProperty.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface ModelProperty { - name: string; - type: string; - base: string; - template: string | null; - readOnly: boolean; - required: boolean; - nullable: boolean; - description: string | null; - validation: string | null; -} diff --git a/src/client/interfaces/Operation.d.ts b/src/client/interfaces/Operation.d.ts index 59c91f10..ee1a5abc 100644 --- a/src/client/interfaces/Operation.d.ts +++ b/src/client/interfaces/Operation.d.ts @@ -4,8 +4,8 @@ import { OperationParameters } from './OperationParameters'; export interface Operation extends OperationParameters { service: string; name: string; - summary: string | null; - description: string | null; + summary?: string; + description?: string; deprecated: boolean; method: string; path: string; diff --git a/src/client/interfaces/OperationParameter.d.ts b/src/client/interfaces/OperationParameter.d.ts index a5ca58c7..5cdfbd73 100644 --- a/src/client/interfaces/OperationParameter.d.ts +++ b/src/client/interfaces/OperationParameter.d.ts @@ -4,9 +4,9 @@ export interface OperationParameter { name: string; type: string; base: string; - template: string | null; - description: string | null; - default: any | undefined; + template?: string; + description?: string; + default?: any; required: boolean; nullable: boolean; imports: string[]; diff --git a/src/client/interfaces/OperationParameters.d.ts b/src/client/interfaces/OperationParameters.d.ts index 775f5197..abff15ce 100644 --- a/src/client/interfaces/OperationParameters.d.ts +++ b/src/client/interfaces/OperationParameters.d.ts @@ -7,5 +7,5 @@ export interface OperationParameters { parametersQuery: OperationParameter[]; parametersForm: OperationParameter[]; parametersHeader: OperationParameter[]; - parametersBody: OperationParameter | null; + parametersBody?: OperationParameter; } diff --git a/src/client/interfaces/OperationResponse.d.ts b/src/client/interfaces/OperationResponse.d.ts index 5c8d1f92..e6d582ba 100644 --- a/src/client/interfaces/OperationResponse.d.ts +++ b/src/client/interfaces/OperationResponse.d.ts @@ -3,6 +3,6 @@ export interface OperationResponse { text: string; type: string; base: string; - template: string | null; + template?: string; imports: string[]; } diff --git a/src/client/interfaces/Type.d.ts b/src/client/interfaces/Type.d.ts index 5445eea5..18c1c23c 100644 --- a/src/client/interfaces/Type.d.ts +++ b/src/client/interfaces/Type.d.ts @@ -1,6 +1,6 @@ export interface Type { type: string; base: string; - template: string | null; + template?: string; imports: string[]; } diff --git a/src/client/interfaces/Validation.d.ts b/src/client/interfaces/Validation.d.ts new file mode 100644 index 00000000..cf2cece6 --- /dev/null +++ b/src/client/interfaces/Validation.d.ts @@ -0,0 +1,6 @@ +export interface Validation { + type: 'ref' | 'type' | 'enum' | 'array' | 'dictionary' | 'property' | 'model'; + childType?: string | null; + childBase?: string | null; + childValidation?: Validation; +} diff --git a/src/openApi/v2/parser/getArrayType.ts b/src/openApi/v2/parser/getArrayType.ts index c7d2b813..5d71928a 100644 --- a/src/openApi/v2/parser/getArrayType.ts +++ b/src/openApi/v2/parser/getArrayType.ts @@ -5,8 +5,8 @@ import { OpenApiItems } from '../interfaces/OpenApiItems'; export interface ArrayType { type: string; base: string; - template: string | null; - default: any | undefined; + template?: string; + default?: any; imports: string[]; } @@ -14,7 +14,6 @@ export function getArrayType(items: OpenApiItems): ArrayType { const result: ArrayType = { type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, - template: null, default: items.default, imports: [], }; diff --git a/src/openApi/v2/parser/getComment.ts b/src/openApi/v2/parser/getComment.ts index 291b6457..1db502e9 100644 --- a/src/openApi/v2/parser/getComment.ts +++ b/src/openApi/v2/parser/getComment.ts @@ -1,6 +1,6 @@ import { EOL } from 'os'; -export function getComment(comment: string | undefined): string | null { +export function getComment(comment: string | undefined): string | undefined { if (comment) { return comment .split(/(\r\n|\n|\r)+/g) @@ -9,5 +9,5 @@ export function getComment(comment: string | undefined): string | null { .join(EOL) .replace(/(\r\n|\n|\r)+/g, '$1 * '); } - return null; + return undefined; } diff --git a/src/openApi/v2/parser/getModel.ts b/src/openApi/v2/parser/getModel.ts index c52aa137..93543b76 100644 --- a/src/openApi/v2/parser/getModel.ts +++ b/src/openApi/v2/parser/getModel.ts @@ -1,36 +1,24 @@ -import {OpenApi} from '../interfaces/OpenApi'; -import {OpenApiSchema} from '../interfaces/OpenApiSchema'; -import {getComment} from './getComment'; -import {getType} from './getType'; -import {Model} from '../../../client/interfaces/Model'; -import {PrimaryType} from './constants'; -import {getEnumType} from './getEnumType'; -import {getEnum} from './getEnum'; -import {getEnumFromDescription} from './getEnumFromDescription'; -import {getTypeFromProperties} from './getTypeFromProperties'; -import {getValidationForRef} from './getValidationForRef'; -import {getValidationForEnum} from './getValidationForEnum'; -import {getValidationForArrayRef} from './getValidationForArrayRef'; -import {getValidationForType} from './getValidationForType'; -import {getValidationForArray} from './getValidationForArray'; -import {getValidationForProperties} from './getValidationForProperties'; +import { OpenApi } from '../interfaces/OpenApi'; +import { OpenApiSchema } from '../interfaces/OpenApiSchema'; +import { getComment } from './getComment'; +import { getType } from './getType'; +import { Model } from '../../../client/interfaces/Model'; +import { PrimaryType } from './constants'; +import { getEnumType } from './getEnumType'; +import { getEnum } from './getEnum'; +import { getEnumFromDescription } from './getEnumFromDescription'; +import { getTypeFromProperties } from './getTypeFromProperties'; +import { getModelProperties } from './getModelProperties'; export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: string = ''): Model { - // TODO: Properties now contain ALL properties, so we need to filter out enums - // before we render the file, plus we need to calculate the final TYPE of a model - // by checking all the properties! - // After this we also need to calculate the validation - // this should all be done in a cleanup / prepare phase, not in this parsing phase - const result: Model = { name, type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, - template: null, description: getComment(definition.description), - validation: null, - extends: [], + readOnly: definition.readOnly, imports: [], + extends: [], enum: [], properties: [], }; @@ -41,7 +29,9 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.base = definitionRef.base; result.template = definitionRef.template; result.imports.push(...definitionRef.imports); - result.validation = getValidationForRef(definitionRef); + result.validation = { + type: 'ref', + }; return result; } @@ -52,7 +42,9 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.type = getEnumType(enumerators); result.base = PrimaryType.STRING; result.enum.push(...enumerators); - result.validation = getValidationForEnum(name, enumerators); + result.validation = { + type: 'enum', + }; return result; } return result; @@ -65,7 +57,9 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.type = getEnumType(enumerators); result.base = PrimaryType.NUMBER; result.enum.push(...enumerators); - result.validation = getValidationForEnum(name, enumerators); + result.validation = { + type: 'enum', + }; return result; } return result; @@ -80,117 +74,27 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.base = arrayItems.base; result.template = arrayItems.template; result.imports.push(...arrayItems.imports); - result.validation = getValidationForArrayRef(arrayItems); + result.validation = { + type: 'array', + childType: arrayItems.type, + childBase: arrayItems.base, + }; } else { const arrayItems = getModel(openApi, definition.items); result.type = `${arrayItems.type}[]`; result.base = arrayItems.base; result.template = arrayItems.template; result.imports.push(...arrayItems.imports); - result.validation = getValidationForArray(name, arrayItems); + result.validation = { + type: 'array', + childType: arrayItems.type, + childBase: arrayItems.base, + childValidation: arrayItems.validation, + }; } return result; } - // Check if this model extends other models - if (definition.allOf) { - definition.allOf.forEach(parent => { - if (parent.$ref) { - const parentRef = getType(parent.$ref); - result.extends.push(parentRef.type); - result.imports.push(parentRef.base); - } - if (parent.type === 'object' && parent.properties) { - for (const propertyName in parent.properties) { - if (parent.properties.hasOwnProperty(propertyName)) { - const property = parent.properties[propertyName]; - const propertyRequired = !!(parent.required && parent.required.includes(propertyName)); - const propertyReadOnly = !!property.readOnly; - if (property.$ref) { - const prop = getType(property.$ref); - result.base = PrimaryType.OBJECT; - result.imports.push(...prop.imports); - result.properties.push({ - name: propertyName, - type: prop.type, - base: prop.base, - template: prop.template, - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - description: property.description || null, - validation: getValidationForRef(prop), - }); - } else { - const prop = getModel(openApi, property); - result.base = PrimaryType.OBJECT; - result.imports.push(...prop.imports); - result.properties.push({ - name: propertyName, - type: prop.type, - base: prop.base, - template: prop.template, - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - description: property.description || null, - validation: prop.validation, - }); - } - } - } - } - }); - result.type = getTypeFromProperties(result.properties); - result.base = PrimaryType.OBJECT; - result.validation = getValidationForProperties(name, result.properties, result.extends); - } - - if (definition.type === 'object' && definition.properties) { - for (const propertyName in definition.properties) { - if (definition.properties.hasOwnProperty(propertyName)) { - const property = definition.properties[propertyName]; - const propertyRequired = !!(definition.required && definition.required.includes(propertyName)); - const propertyReadOnly = !!property.readOnly; - if (property.$ref) { - const prop = getType(property.$ref); - result.base = PrimaryType.OBJECT; - result.imports.push(...prop.imports); - result.properties.push({ - name: propertyName, - type: prop.type, - base: prop.base, - template: prop.template, - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - description: property.description || null, - validation: getValidationForRef(prop), - }); - } else { - const prop = getModel(openApi, property); - result.base = PrimaryType.OBJECT; - result.imports.push(...prop.imports); - result.properties.push({ - name: propertyName, - type: prop.type, - base: prop.base, - template: prop.template, - readOnly: propertyReadOnly, - required: propertyRequired, - nullable: false, - description: property.description || null, - validation: prop.validation, - }); - } - } - } - result.type = getTypeFromProperties(result.properties); - result.base = PrimaryType.OBJECT; - result.validation = getValidationForProperties(name, result.properties, result.extends); - return result; - } - // If a property has additionalProperties, then it likely to be a dictionary type. // In that case parse the related property and assume it lives inside a string // based dictionary: { [key:string]: MyType } @@ -201,28 +105,74 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, name: stri result.base = 'Dictionary'; result.template = additionalProperties.type; result.imports.push(...additionalProperties.imports); - result.imports.push('Dictionary'); - console.log(name, 'Dictionary', result.type); + result.validation = { + type: 'dictionary', + childType: additionalProperties.type, + childBase: additionalProperties.base, + }; } else { const additionalProperties = getModel(openApi, definition.additionalProperties); result.type = `Dictionary<${additionalProperties.type}>`; result.base = 'Dictionary'; result.template = additionalProperties.type; result.imports.push(...additionalProperties.imports); - result.imports.push('Dictionary'); - console.log(name, 'Dictionary', result.type); + result.validation = { + type: 'dictionary', + childType: additionalProperties.type, + childBase: additionalProperties.base, + childValidation: additionalProperties.validation, + }; } return result; } + // Check if this model extends other models + if (definition.allOf) { + definition.allOf.forEach(parent => { + if (parent.$ref) { + const parentRef = getType(parent.$ref); + result.extends.push(parentRef.type); + result.imports.push(parentRef.base); + } + if (parent.type === 'object' && parent.properties) { + const properties = getModelProperties(openApi, parent); + properties.forEach(property => { + result.properties.push(property); + result.imports.push(...property.imports); + }); + } + }); + result.type = getTypeFromProperties(result.properties); + result.base = PrimaryType.OBJECT; + result.validation = { + type: 'model', + }; + } + + if (definition.type === 'object' && definition.properties) { + const properties = getModelProperties(openApi, definition); + properties.forEach(property => { + result.properties.push(property); + result.imports.push(...property.imports); + }); + result.type = getTypeFromProperties(result.properties); + result.base = PrimaryType.OBJECT; + result.validation = { + type: 'model', + }; + return result; + } + // If the schema has a type than it can be a basic or generic type. - if (definition.type !== 'object' && definition.type) { + if (definition.type) { const definitionType = getType(definition.type); result.type = definitionType.type; result.base = definitionType.base; result.template = definitionType.template; result.imports.push(...definitionType.imports); - result.validation = getValidationForType(definitionType); + result.validation = { + type: 'type', + }; return result; } diff --git a/src/openApi/v2/parser/getModelProperties.ts b/src/openApi/v2/parser/getModelProperties.ts new file mode 100644 index 00000000..8aa92561 --- /dev/null +++ b/src/openApi/v2/parser/getModelProperties.ts @@ -0,0 +1,62 @@ +import { OpenApi } from '../interfaces/OpenApi'; +import { OpenApiSchema } from '../interfaces/OpenApiSchema'; +import { getComment } from './getComment'; +import { getType } from './getType'; +import { Model } from '../../../client/interfaces/Model'; +import { OpenApiReference } from '../interfaces/OpenApiReference'; +import { getModel } from './getModel'; + +export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema & OpenApiReference): Model[] { + const result: 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({ + name: propertyName, + type: prop.type, + base: prop.base, + template: prop.template, + description: getComment(definition.description), + readOnly: propertyReadOnly, + required: propertyRequired, + imports: prop.imports, + extends: [], + enum: [], + properties: [], + validation: { + type: 'property', + childType: prop.type, + childBase: prop.base, + }, + }); + } else { + const prop = getModel(openApi, property); + result.push({ + name: propertyName, + type: prop.type, + base: prop.base, + template: prop.template, + description: property.description, + readOnly: propertyReadOnly, + required: propertyRequired, + imports: prop.imports, + extends: prop.extends, + enum: prop.enum, + properties: prop.properties, + validation: { + type: 'property', + childType: prop.type, + childBase: prop.base, + childValidation: prop.validation, + }, + }); + } + } + } + + return result; +} diff --git a/src/openApi/v2/parser/getModelTemplate.spec.ts b/src/openApi/v2/parser/getModelTemplate.spec.ts index 67e46f54..ed57d348 100644 --- a/src/openApi/v2/parser/getModelTemplate.spec.ts +++ b/src/openApi/v2/parser/getModelTemplate.spec.ts @@ -15,7 +15,6 @@ describe('getModelTemplate', () => { const template = getModelTemplate({ type: 'string', base: 'string', - template: null, imports: [], }); expect(template).toEqual(''); diff --git a/src/openApi/v2/parser/getModelTemplate.ts b/src/openApi/v2/parser/getModelTemplate.ts index 48cb218c..f472e784 100644 --- a/src/openApi/v2/parser/getModelTemplate.ts +++ b/src/openApi/v2/parser/getModelTemplate.ts @@ -7,5 +7,5 @@ import { Type } from '../../../client/interfaces/Type'; * @returns The model template type ( or empty). */ export function getModelTemplate(modelClass: Type): string { - return modelClass.template !== null ? '' : ''; + return modelClass.template ? '' : ''; } diff --git a/src/openApi/v2/parser/getModelValidation.ts b/src/openApi/v2/parser/getModelValidation.ts deleted file mode 100644 index 65b006dd..00000000 --- a/src/openApi/v2/parser/getModelValidation.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { EOL } from 'os'; -import { ModelProperty } from '../../../client/interfaces/ModelProperty'; - -export function getModelValidation(name: string, properties: ModelProperty[]): string { - return [ - `yup.object().shape({`, - // ...properties.map(property => { - // return ` ${property.name}: ${property.validation},`; - // }), - `}).noUnknown()`, - ].join(EOL); -} diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index 21752df5..885b9267 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -33,7 +33,6 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op: parametersQuery: [], parametersForm: [], parametersHeader: [], - parametersBody: null, imports: [], errors: [], result: PrimaryType.VOID, diff --git a/src/openApi/v2/parser/getOperationParameter.ts b/src/openApi/v2/parser/getOperationParameter.ts index c8b6d14e..712db409 100644 --- a/src/openApi/v2/parser/getOperationParameter.ts +++ b/src/openApi/v2/parser/getOperationParameter.ts @@ -12,7 +12,6 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame name: getOperationParameterName(parameter.name), type: 'any', base: 'any', - template: null, description: getComment(parameter.description), default: parameter.default, required: parameter.required || false, diff --git a/src/openApi/v2/parser/getOperationParameters.ts b/src/openApi/v2/parser/getOperationParameters.ts index f9a322ab..4e63cb79 100644 --- a/src/openApi/v2/parser/getOperationParameters.ts +++ b/src/openApi/v2/parser/getOperationParameters.ts @@ -18,7 +18,6 @@ export function getOperationParameters(openApi: OpenApi, parameters: (OpenApiPar parametersQuery: [], parametersForm: [], parametersHeader: [], - parametersBody: null, }; // Iterate over the parameters diff --git a/src/openApi/v2/parser/getOperationResponse.ts b/src/openApi/v2/parser/getOperationResponse.ts index f06122e4..800facc3 100644 --- a/src/openApi/v2/parser/getOperationResponse.ts +++ b/src/openApi/v2/parser/getOperationResponse.ts @@ -2,25 +2,17 @@ import { PrimaryType } from './constants'; import { OperationResponse } from '../../../client/interfaces/OperationResponse'; export function getOperationResponse(responses: OperationResponse[]): OperationResponse { - const response: 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: [], }; - - // 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) { - response.code = result.code; - response.text = result.text; - response.type = result.type; - response.base = result.base; - response.template = result.template; - response.imports.push(...result.imports); - } - - return response; } diff --git a/src/openApi/v2/parser/getOperationResponses.ts b/src/openApi/v2/parser/getOperationResponses.ts index 4e2b3919..1de667c8 100644 --- a/src/openApi/v2/parser/getOperationResponses.ts +++ b/src/openApi/v2/parser/getOperationResponses.ts @@ -25,7 +25,6 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon text: response.description || '', type: 'any', base: 'any', - template: null, imports: [], }; diff --git a/src/openApi/v2/parser/getType.spec.ts b/src/openApi/v2/parser/getType.spec.ts index 81776728..df497783 100644 --- a/src/openApi/v2/parser/getType.spec.ts +++ b/src/openApi/v2/parser/getType.spec.ts @@ -2,31 +2,31 @@ import { getType } from './getType'; describe('getType', () => { it('should convert int', () => { - const type = getType('int', null); + const type = getType('int'); expect(type.type).toEqual('number'); expect(type.base).toEqual('number'); - expect(type.template).toEqual(null); + expect(type.template).toEqual(undefined); expect(type.imports).toEqual([]); }); it('should convert string', () => { - const type = getType('String', null); + const type = getType('String'); expect(type.type).toEqual('string'); expect(type.base).toEqual('string'); - expect(type.template).toEqual(null); + expect(type.template).toEqual(undefined); expect(type.imports).toEqual([]); }); it('should convert string array', () => { - const type = getType('Array[String]', null); + const type = getType('Array[String]'); expect(type.type).toEqual('string[]'); expect(type.base).toEqual('string'); - expect(type.template).toEqual(null); + expect(type.template).toEqual(undefined); expect(type.imports).toEqual([]); }); it('should convert template with primary', () => { - const type = getType('#/definitions/Link[String]', null); + const type = getType('#/definitions/Link[String]'); expect(type.type).toEqual('Link'); expect(type.base).toEqual('Link'); expect(type.template).toEqual('string'); @@ -34,7 +34,7 @@ describe('getType', () => { }); it('should convert template with model', () => { - const type = getType('#/definitions/Link[Model]', null); + const type = getType('#/definitions/Link[Model]'); expect(type.type).toEqual('Link'); expect(type.base).toEqual('Link'); expect(type.template).toEqual('Model'); @@ -42,7 +42,7 @@ describe('getType', () => { }); it('should have double imports', () => { - const type = getType('#/definitions/Link[Link]', null); + const type = getType('#/definitions/Link[Link]'); expect(type.type).toEqual('Link'); expect(type.base).toEqual('Link'); expect(type.template).toEqual('Link'); @@ -53,7 +53,7 @@ describe('getType', () => { const type = getType('#/definitions/Link', 'Link'); expect(type.type).toEqual('T'); expect(type.base).toEqual('T'); - expect(type.template).toEqual(null); + expect(type.template).toEqual(undefined); expect(type.imports).toEqual([]); }); }); diff --git a/src/openApi/v2/parser/getType.ts b/src/openApi/v2/parser/getType.ts index fd77a13b..7712afad 100644 --- a/src/openApi/v2/parser/getType.ts +++ b/src/openApi/v2/parser/getType.ts @@ -8,11 +8,10 @@ import { PrimaryType } from './constants'; * @param value String value like "integer" or "Link[Model]". * @param template Optional template class from parent (needed to process generics) */ -export function getType(value: string | undefined, template: string | null = null): Type { +export function getType(value: string | undefined, template?: string): Type { const result: Type = { type: PrimaryType.OBJECT, base: PrimaryType.OBJECT, - template: null, imports: [], }; diff --git a/src/openApi/v2/parser/getTypeFromProperties.ts b/src/openApi/v2/parser/getTypeFromProperties.ts index ee45e223..770b551d 100644 --- a/src/openApi/v2/parser/getTypeFromProperties.ts +++ b/src/openApi/v2/parser/getTypeFromProperties.ts @@ -1,7 +1,7 @@ import { EOL } from 'os'; -import { ModelProperty } from '../../../client/interfaces/ModelProperty'; +import { Model } from '../../../client/interfaces/Model'; -export function getTypeFromProperties(properties: ModelProperty[]): string { +export function getTypeFromProperties(properties: Model[]): string { return [ `{`, ...properties.map(property => { diff --git a/src/openApi/v2/parser/getValidationForDictionary.ts b/src/openApi/v2/parser/getValidationForDictionary.ts new file mode 100644 index 00000000..17f077c2 --- /dev/null +++ b/src/openApi/v2/parser/getValidationForDictionary.ts @@ -0,0 +1,16 @@ +import { Model } from '../../../client/interfaces/Model'; +import { EOL } from 'os'; + +export function getValidationForDictionary(name: string, model: Model): string { + /* prettier-ignore */ + return [ + `yup.lazy>(value =>`, + `yup.object>().shape(`, + `Object.entries(value).reduce((obj, item) => ({`, + `...obj,`, + `[item[0]]: ${model.validation ? model.validation : 'yup.mixed()'},`, + `}), {})`, + `)`, + `)` + ].join(EOL); +} diff --git a/src/openApi/v2/parser/getValidationForDictionaryRef.ts b/src/openApi/v2/parser/getValidationForDictionaryRef.ts new file mode 100644 index 00000000..85705e27 --- /dev/null +++ b/src/openApi/v2/parser/getValidationForDictionaryRef.ts @@ -0,0 +1,16 @@ +import { Type } from '../../../client/interfaces/Type'; +import { EOL } from 'os'; + +export function getValidationForDictionaryRef(type: Type): string { + /* prettier-ignore */ + return [ + `yup.lazy>(value =>`, + `yup.object>().shape(`, + `Object.entries(value).reduce((obj, item) => ({`, + `...obj,`, + `[item[0]]: ${type.base}.schema,`, + `}), {})`, + `)`, + `)` + ].join(EOL); +} diff --git a/src/openApi/v2/parser/getValidationForProperties.ts b/src/openApi/v2/parser/getValidationForProperties.ts index fbd99630..88ce8cca 100644 --- a/src/openApi/v2/parser/getValidationForProperties.ts +++ b/src/openApi/v2/parser/getValidationForProperties.ts @@ -1,11 +1,11 @@ import { EOL } from 'os'; -import { ModelProperty } from '../../../client/interfaces/ModelProperty'; +import { Model } from '../../../client/interfaces/Model'; -export function getValidationForProperties(name: string, properties: ModelProperty[], extendClasses: string[]): string { +export function getValidationForProperties(name: string, model: Model): string { return [ - ...extendClasses.map(extendClass => `${extendClass}.schema.concat(`), + ...model.extends.map(extend => `${extend}.schema.concat(`), `yup.object${name ? `<${name}>` : ''}().shape({`, - ...properties.map(property => { + ...model.properties.map(property => { let validation = ''; validation = `${validation}${property.name}: yup.lazy(() => ${property.validation}.default(undefined))`; validation = `${validation}${property.required ? '.required()' : ''}`; @@ -13,6 +13,6 @@ export function getValidationForProperties(name: string, properties: ModelProper return `${validation},`; }), `}).noUnknown()`, - ...extendClasses.map(() => `)`), + ...model.extends.map(() => `)`), ].join(EOL); } diff --git a/src/utils/exportModel.ts b/src/utils/exportModel.ts index 42c16530..85ec303d 100644 --- a/src/utils/exportModel.ts +++ b/src/utils/exportModel.ts @@ -1,12 +1,20 @@ -import { getSortedImports } from './getSortedImports'; import { Model } from '../client/interfaces/Model'; export function exportModel(model: Model): any { return { ...model, - imports: getSortedImports(model.imports).filter(name => { - return model.name !== name; - }), + imports: model.imports + .filter(name => { + return model.name !== name; + }) + .filter((name, index, arr) => { + return arr.indexOf(name) === index; + }) + .sort((a, b) => { + const nameA = a.toLowerCase(); + const nameB = b.toLowerCase(); + return nameA.localeCompare(nameB); + }), properties: model.properties .filter((property, index, arr) => { return arr.findIndex(item => item.name === property.name) === index; diff --git a/src/utils/exportService.ts b/src/utils/exportService.ts index 1889203f..5882ebb3 100644 --- a/src/utils/exportService.ts +++ b/src/utils/exportService.ts @@ -1,13 +1,21 @@ import { Service } from '../client/interfaces/Service'; -import { getSortedImports } from './getSortedImports'; export function exportService(service: Service): any { const names = new Map(); return { ...service, - imports: getSortedImports(service.imports).filter(name => { - return service.name !== name; - }), + imports: service.imports + .filter(name => { + return service.name !== name; + }) + .filter((name, index, arr) => { + return arr.indexOf(name) === index; + }) + .sort((a, b) => { + const nameA = a.toLowerCase(); + const nameB = b.toLowerCase(); + return nameA.localeCompare(nameB); + }), operations: service.operations .map(operation => { const name = operation.name; diff --git a/src/utils/getSortedModels.spec.ts b/src/utils/getModelNames.spec.ts similarity index 61% rename from src/utils/getSortedModels.spec.ts rename to src/utils/getModelNames.spec.ts index 87498a75..432c3cc7 100644 --- a/src/utils/getSortedModels.spec.ts +++ b/src/utils/getModelNames.spec.ts @@ -1,18 +1,18 @@ -import { getSortedModels } from './getSortedModels'; +import { getModelNames } from './getModelNames'; import { Model } from '../client/interfaces/Model'; -describe('getSortedModels', () => { +describe('getModelNames', () => { it('should return sorted list', () => { const models = new Map(); models.set('John', { name: 'John', type: 'John', base: 'John', - template: null, - description: null, - validation: null, - extends: [], + readOnly: false, + required: false, + nullable: false, imports: [], + extends: [], enum: [], properties: [], }); @@ -20,11 +20,11 @@ describe('getSortedModels', () => { name: 'Jane', type: 'Jane', base: 'Jane', - template: null, - description: null, - validation: null, - extends: [], + readOnly: false, + required: false, + nullable: false, imports: [], + extends: [], enum: [], properties: [], }); @@ -32,16 +32,16 @@ describe('getSortedModels', () => { name: 'Doe', type: 'Doe', base: 'Doe', - template: null, - description: null, - validation: null, - extends: [], + readOnly: false, + required: false, + nullable: false, imports: [], + extends: [], enum: [], properties: [], }); - expect(getSortedModels(new Map())).toEqual([]); - expect(getSortedModels(models)).toEqual(['Doe', 'Jane', 'John']); + expect(getModelNames(new Map())).toEqual([]); + expect(getModelNames(models)).toEqual(['Doe', 'Jane', 'John']); }); }); diff --git a/src/utils/getSortedModels.ts b/src/utils/getModelNames.ts similarity index 81% rename from src/utils/getSortedModels.ts rename to src/utils/getModelNames.ts index f9f6b25d..07818533 100644 --- a/src/utils/getSortedModels.ts +++ b/src/utils/getModelNames.ts @@ -1,6 +1,6 @@ import { Model } from '../client/interfaces/Model'; -export function getSortedModels(models: Map): string[] { +export function getModelNames(models: Map): string[] { return Array.from(models.values()) .sort((a, b) => { const nameA = a.name.toLowerCase(); diff --git a/src/utils/getSortedServices.spec.ts b/src/utils/getServiceNames.spec.ts similarity index 68% rename from src/utils/getSortedServices.spec.ts rename to src/utils/getServiceNames.spec.ts index cdb6d19a..be24dbc8 100644 --- a/src/utils/getSortedServices.spec.ts +++ b/src/utils/getServiceNames.spec.ts @@ -1,7 +1,7 @@ -import { getSortedServices } from './getSortedServices'; +import { getServiceNames } from './getServiceNames'; import { Service } from '../client/interfaces/Service'; -describe('getSortedServices', () => { +describe('getServiceNames', () => { it('should return sorted list', () => { const services = new Map(); services.set('John', { @@ -20,7 +20,7 @@ describe('getSortedServices', () => { imports: [], }); - expect(getSortedServices(new Map())).toEqual([]); - expect(getSortedServices(services)).toEqual(['Doe', 'Jane', 'John']); + expect(getServiceNames(new Map())).toEqual([]); + expect(getServiceNames(services)).toEqual(['Doe', 'Jane', 'John']); }); }); diff --git a/src/utils/getSortedServices.ts b/src/utils/getServiceNames.ts similarity index 80% rename from src/utils/getSortedServices.ts rename to src/utils/getServiceNames.ts index 35cc7c88..ac908c1c 100644 --- a/src/utils/getSortedServices.ts +++ b/src/utils/getServiceNames.ts @@ -1,6 +1,6 @@ import { Service } from '../client/interfaces/Service'; -export function getSortedServices(services: Map): string[] { +export function getServiceNames(services: Map): string[] { return Array.from(services.values()) .sort((a, b) => { const nameA = a.name.toLowerCase(); diff --git a/src/utils/getSortedImports.spec.ts b/src/utils/getSortedImports.spec.ts deleted file mode 100644 index e459db32..00000000 --- a/src/utils/getSortedImports.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { getSortedImports } from './getSortedImports'; - -describe('getSortedImports', () => { - it('should return sorted list', () => { - const arr = ['a', 'b', 'c']; - - expect(getSortedImports([])).toEqual([]); - expect(getSortedImports([...arr])).toEqual(arr); - expect(getSortedImports([...arr].reverse())).toEqual(arr); - expect(getSortedImports([' ', ...arr])).toEqual(arr); - expect(getSortedImports([...arr, ' '])).toEqual(arr); - expect(getSortedImports([...arr, ...arr])).toEqual(arr); - }); -}); diff --git a/src/utils/getSortedImports.ts b/src/utils/getSortedImports.ts deleted file mode 100644 index 78d462dd..00000000 --- a/src/utils/getSortedImports.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Sort a list of strings and filter out any duplicates. - * @param imports List of strings. - */ -export function getSortedImports(imports: string[]): string[] { - return imports - .filter(name => name) - .filter(name => name.trim()) - .filter((name, index, arr) => { - return arr.indexOf(name) === index; - }) - .sort((a, b) => { - const nameA = a.toLowerCase(); - const nameB = b.toLowerCase(); - return nameA.localeCompare(nameB, 'en'); - }); -} diff --git a/src/utils/writeClientIndex.ts b/src/utils/writeClientIndex.ts index fbb7a6a7..c2b2913c 100644 --- a/src/utils/writeClientIndex.ts +++ b/src/utils/writeClientIndex.ts @@ -2,8 +2,8 @@ import { Client } from '../client/interfaces/Client'; import * as handlebars from 'handlebars'; import * as fs from 'fs'; import * as path from 'path'; -import { getSortedModels } from './getSortedModels'; -import { getSortedServices } from './getSortedServices'; +import { getModelNames } from './getModelNames'; +import { getServiceNames } from './getServiceNames'; import { Language } from '../index'; import { getFileName } from './getFileName'; @@ -24,8 +24,8 @@ export function writeClientIndex(client: Client, language: Language, template: h template({ server: client.server, version: client.version, - models: getSortedModels(client.models), - services: getSortedServices(client.services), + models: getModelNames(client.models), + services: getServiceNames(client.services), }) ); } catch (e) { diff --git a/src/utils/writeClientModels.spec.ts b/src/utils/writeClientModels.spec.ts index 87c94c24..d15de3f7 100644 --- a/src/utils/writeClientModels.spec.ts +++ b/src/utils/writeClientModels.spec.ts @@ -14,11 +14,11 @@ describe('writeClientModels', () => { name: 'Item', type: 'Item', base: 'Item', - template: null, - description: null, - validation: null, - extends: [], + readOnly: false, + required: false, + nullable: false, imports: [], + extends: [], enum: [], properties: [], }); diff --git a/test/mock/v2/spec.json b/test/mock/v2/spec.json index 6799af7f..6149738d 100644 --- a/test/mock/v2/spec.json +++ b/test/mock/v2/spec.json @@ -102,6 +102,13 @@ "type": "string" } }, + "DictionaryWithReference": { + "description": "This is a string reference", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ModelWithString" + } + }, "DictionaryWithArray": { "description": "This is a complex dictionary", "type": "object",