mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Abstracted model validation and properties
This commit is contained in:
parent
d6131dc780
commit
ea703d27dc
15
src/client/interfaces/Model.d.ts
vendored
15
src/client/interfaces/Model.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
||||
11
src/client/interfaces/ModelProperty.d.ts
vendored
11
src/client/interfaces/ModelProperty.d.ts
vendored
@ -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;
|
||||
}
|
||||
4
src/client/interfaces/Operation.d.ts
vendored
4
src/client/interfaces/Operation.d.ts
vendored
@ -4,8 +4,8 @@ import { OperationParameters } from './OperationParameters';
|
||||
export interface Operation extends OperationParameters {
|
||||
service: string;
|
||||
name: string;
|
||||
summary: string | null;
|
||||
description: string | null;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
deprecated: boolean;
|
||||
method: string;
|
||||
path: string;
|
||||
|
||||
@ -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[];
|
||||
|
||||
@ -7,5 +7,5 @@ export interface OperationParameters {
|
||||
parametersQuery: OperationParameter[];
|
||||
parametersForm: OperationParameter[];
|
||||
parametersHeader: OperationParameter[];
|
||||
parametersBody: OperationParameter | null;
|
||||
parametersBody?: OperationParameter;
|
||||
}
|
||||
|
||||
2
src/client/interfaces/OperationResponse.d.ts
vendored
2
src/client/interfaces/OperationResponse.d.ts
vendored
@ -3,6 +3,6 @@ export interface OperationResponse {
|
||||
text: string;
|
||||
type: string;
|
||||
base: string;
|
||||
template: string | null;
|
||||
template?: string;
|
||||
imports: string[];
|
||||
}
|
||||
|
||||
2
src/client/interfaces/Type.d.ts
vendored
2
src/client/interfaces/Type.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
export interface Type {
|
||||
type: string;
|
||||
base: string;
|
||||
template: string | null;
|
||||
template?: string;
|
||||
imports: string[];
|
||||
}
|
||||
|
||||
6
src/client/interfaces/Validation.d.ts
vendored
Normal file
6
src/client/interfaces/Validation.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export interface Validation {
|
||||
type: 'ref' | 'type' | 'enum' | 'array' | 'dictionary' | 'property' | 'model';
|
||||
childType?: string | null;
|
||||
childBase?: string | null;
|
||||
childValidation?: Validation;
|
||||
}
|
||||
@ -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: [],
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
62
src/openApi/v2/parser/getModelProperties.ts
Normal file
62
src/openApi/v2/parser/getModelProperties.ts
Normal file
@ -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;
|
||||
}
|
||||
@ -15,7 +15,6 @@ describe('getModelTemplate', () => {
|
||||
const template = getModelTemplate({
|
||||
type: 'string',
|
||||
base: 'string',
|
||||
template: null,
|
||||
imports: [],
|
||||
});
|
||||
expect(template).toEqual('');
|
||||
|
||||
@ -7,5 +7,5 @@ import { Type } from '../../../client/interfaces/Type';
|
||||
* @returns The model template type (<T> or empty).
|
||||
*/
|
||||
export function getModelTemplate(modelClass: Type): string {
|
||||
return modelClass.template !== null ? '<T>' : '';
|
||||
return modelClass.template ? '<T>' : '';
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -33,7 +33,6 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
|
||||
parametersQuery: [],
|
||||
parametersForm: [],
|
||||
parametersHeader: [],
|
||||
parametersBody: null,
|
||||
imports: [],
|
||||
errors: [],
|
||||
result: PrimaryType.VOID,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -18,7 +18,6 @@ export function getOperationParameters(openApi: OpenApi, parameters: (OpenApiPar
|
||||
parametersQuery: [],
|
||||
parametersForm: [],
|
||||
parametersHeader: [],
|
||||
parametersBody: null,
|
||||
};
|
||||
|
||||
// Iterate over the parameters
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon
|
||||
text: response.description || '',
|
||||
type: 'any',
|
||||
base: 'any',
|
||||
template: null,
|
||||
imports: [],
|
||||
};
|
||||
|
||||
|
||||
@ -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<string>');
|
||||
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<Model>');
|
||||
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<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([]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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: [],
|
||||
};
|
||||
|
||||
|
||||
@ -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 => {
|
||||
|
||||
16
src/openApi/v2/parser/getValidationForDictionary.ts
Normal file
16
src/openApi/v2/parser/getValidationForDictionary.ts
Normal file
@ -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<Dictionary<${model.type}>>(value =>`,
|
||||
`yup.object<Dictionary<${model.type}>>().shape(`,
|
||||
`Object.entries(value).reduce((obj, item) => ({`,
|
||||
`...obj,`,
|
||||
`[item[0]]: ${model.validation ? model.validation : 'yup.mixed()'},`,
|
||||
`}), {})`,
|
||||
`)`,
|
||||
`)`
|
||||
].join(EOL);
|
||||
}
|
||||
16
src/openApi/v2/parser/getValidationForDictionaryRef.ts
Normal file
16
src/openApi/v2/parser/getValidationForDictionaryRef.ts
Normal file
@ -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<Dictionary<${type.type}>>(value =>`,
|
||||
`yup.object<Dictionary<${type.type}>>().shape(`,
|
||||
`Object.entries(value).reduce((obj, item) => ({`,
|
||||
`...obj,`,
|
||||
`[item[0]]: ${type.base}.schema,`,
|
||||
`}), {})`,
|
||||
`)`,
|
||||
`)`
|
||||
].join(EOL);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
import { Service } from '../client/interfaces/Service';
|
||||
import { getSortedImports } from './getSortedImports';
|
||||
|
||||
export function exportService(service: Service): any {
|
||||
const names = new Map<string, number>();
|
||||
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;
|
||||
|
||||
@ -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<string, Model>();
|
||||
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<string, Model>())).toEqual([]);
|
||||
expect(getSortedModels(models)).toEqual(['Doe', 'Jane', 'John']);
|
||||
expect(getModelNames(new Map<string, Model>())).toEqual([]);
|
||||
expect(getModelNames(models)).toEqual(['Doe', 'Jane', 'John']);
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import { Model } from '../client/interfaces/Model';
|
||||
|
||||
export function getSortedModels(models: Map<string, Model>): string[] {
|
||||
export function getModelNames(models: Map<string, Model>): string[] {
|
||||
return Array.from(models.values())
|
||||
.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
@ -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<string, Service>();
|
||||
services.set('John', {
|
||||
@ -20,7 +20,7 @@ describe('getSortedServices', () => {
|
||||
imports: [],
|
||||
});
|
||||
|
||||
expect(getSortedServices(new Map<string, Service>())).toEqual([]);
|
||||
expect(getSortedServices(services)).toEqual(['Doe', 'Jane', 'John']);
|
||||
expect(getServiceNames(new Map<string, Service>())).toEqual([]);
|
||||
expect(getServiceNames(services)).toEqual(['Doe', 'Jane', 'John']);
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import { Service } from '../client/interfaces/Service';
|
||||
|
||||
export function getSortedServices(services: Map<string, Service>): string[] {
|
||||
export function getServiceNames(services: Map<string, Service>): string[] {
|
||||
return Array.from(services.values())
|
||||
.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
@ -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');
|
||||
});
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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: [],
|
||||
});
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user