mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Cleanup done, need to run through the test cases!
This commit is contained in:
parent
121ecd527f
commit
e938bd9b5c
5
src/client/interfaces/Enum.d.ts
vendored
5
src/client/interfaces/Enum.d.ts
vendored
@ -1,7 +1,8 @@
|
||||
import { EnumValue } from './EnumValue';
|
||||
import { EnumSymbol } from './EnumSymbol';
|
||||
|
||||
export interface Enum {
|
||||
name: string;
|
||||
type: string;
|
||||
values: EnumValue[];
|
||||
symbols: EnumSymbol[];
|
||||
validation: string | null;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export interface EnumValue {
|
||||
export interface EnumSymbol {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
6
src/client/interfaces/Model.d.ts
vendored
6
src/client/interfaces/Model.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
import { ModelProperty } from './ModelProperty';
|
||||
import { Enum } from './Enum';
|
||||
import { EnumValue } from './EnumValue';
|
||||
import { EnumSymbol } from './EnumSymbol';
|
||||
|
||||
export interface Model {
|
||||
isInterface: boolean;
|
||||
@ -12,9 +12,9 @@ export interface Model {
|
||||
template: string | null;
|
||||
validation: string | null;
|
||||
description: string | null;
|
||||
extends: string | null;
|
||||
extends: string[];
|
||||
imports: string[];
|
||||
enums: Enum[];
|
||||
values: EnumValue[];
|
||||
symbols: EnumSymbol[];
|
||||
properties: ModelProperty[];
|
||||
}
|
||||
|
||||
2
src/client/interfaces/ModelProperty.d.ts
vendored
2
src/client/interfaces/ModelProperty.d.ts
vendored
@ -4,6 +4,6 @@ export interface ModelProperty {
|
||||
required: boolean;
|
||||
nullable: boolean;
|
||||
readOnly: boolean;
|
||||
validation: string | null;
|
||||
description: string | null;
|
||||
validation: string | null;
|
||||
}
|
||||
|
||||
6
src/client/interfaces/Operation.d.ts
vendored
6
src/client/interfaces/Operation.d.ts
vendored
@ -4,9 +4,9 @@ import { OperationParameters } from './OperationParameters';
|
||||
export interface Operation extends OperationParameters {
|
||||
service: string;
|
||||
name: string;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
deprecated?: boolean;
|
||||
summary: string | null;
|
||||
description: string | null;
|
||||
deprecated: boolean;
|
||||
method: string;
|
||||
path: string;
|
||||
errors: OperationError[];
|
||||
|
||||
14
src/index.ts
14
src/index.ts
@ -29,8 +29,8 @@ export enum HttpClient {
|
||||
* @param httpClient: The selected httpClient (fetch or XHR)
|
||||
*/
|
||||
export function generate(input: string, output: string, language: Language = Language.TYPESCRIPT, httpClient: HttpClient = HttpClient.FETCH): void {
|
||||
const inputPath: string = path.resolve(process.cwd(), input);
|
||||
const outputPath: string = path.resolve(process.cwd(), output);
|
||||
const inputPath = path.resolve(process.cwd(), input);
|
||||
const outputPath = path.resolve(process.cwd(), output);
|
||||
|
||||
console.log(chalk.bold.green('Generate:'));
|
||||
console.log(chalk.grey(' Input:'), input);
|
||||
@ -42,22 +42,22 @@ export function generate(input: string, output: string, language: Language = Lan
|
||||
try {
|
||||
// Load the specification, read the OpenAPI version and load the
|
||||
// handlebar templates for the given language
|
||||
const openApi: any = getOpenApiSpec(inputPath);
|
||||
const openApiVersion: OpenApiVersion = getOpenApiVersion(openApi);
|
||||
const templates: Templates = readHandlebarsTemplates(language);
|
||||
const openApi = getOpenApiSpec(inputPath);
|
||||
const openApiVersion = getOpenApiVersion(openApi);
|
||||
const templates = readHandlebarsTemplates(language);
|
||||
|
||||
switch (language) {
|
||||
case Language.JAVASCRIPT:
|
||||
case Language.TYPESCRIPT:
|
||||
// Generate and write version 2 client
|
||||
if (openApiVersion === OpenApiVersion.V2) {
|
||||
const clientV2: Client = parseV2(openApi);
|
||||
const clientV2 = parseV2(openApi);
|
||||
writeClient(clientV2, language, templates, outputPath);
|
||||
}
|
||||
|
||||
// Generate and write version 3 client
|
||||
if (openApiVersion === OpenApiVersion.V3) {
|
||||
const clientV3: Client = parseV3(openApi);
|
||||
const clientV3 = parseV3(openApi);
|
||||
writeClient(clientV3, language, templates, outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
1
src/openApi/v2/interfaces/OpenApiSchema.d.ts
vendored
1
src/openApi/v2/interfaces/OpenApiSchema.d.ts
vendored
@ -7,6 +7,7 @@ import { OpenApiXml } from './OpenApiXml';
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
|
||||
*/
|
||||
export interface OpenApiSchema {
|
||||
$ref?: string;
|
||||
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
|
||||
title?: string;
|
||||
description?: string;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { getType } from './getType';
|
||||
import { PrimaryType } from './constants';
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
import { OpenApiItems } from '../interfaces/OpenApiItems';
|
||||
|
||||
export interface ArrayType {
|
||||
@ -22,7 +21,7 @@ export function getArrayType(items: OpenApiItems): ArrayType {
|
||||
|
||||
// If the parameter has a type than it can be a basic or generic type.
|
||||
if (items.type) {
|
||||
const itemsType: Type = getType(items.type);
|
||||
const itemsType = getType(items.type);
|
||||
result.type = itemsType.type;
|
||||
result.base = itemsType.base;
|
||||
result.template = itemsType.template;
|
||||
@ -31,7 +30,7 @@ export function getArrayType(items: OpenApiItems): ArrayType {
|
||||
// If the parameter is an Array type, we check for the child type,
|
||||
// so we can create a typed array, otherwise this will be a "any[]".
|
||||
if (items.type === 'array' && items.items) {
|
||||
const arrayType: ArrayType = getArrayType(items.items);
|
||||
const arrayType = getArrayType(items.items);
|
||||
result.type = `${arrayType.type}[]`;
|
||||
result.base = arrayType.base;
|
||||
result.template = arrayType.template;
|
||||
|
||||
@ -1,25 +1,12 @@
|
||||
import { ModelEnumValue } from '../../../client/interfaces/ModelEnumValue';
|
||||
import { Enum } from '../../../client/interfaces/Enum';
|
||||
|
||||
export function getModelEnum(values?: (string | number)[]): ModelEnumValue[] {
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
.filter((value: string | number, index: number, arr: (string | number)[]) => {
|
||||
return arr.indexOf(value) === index;
|
||||
})
|
||||
.map(
|
||||
(value: string | number): ModelEnumValue => {
|
||||
if (typeof value === 'number') {
|
||||
return {
|
||||
name: `NUM_${value}`,
|
||||
value: String(value),
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
|
||||
value: `'${value}'`,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
return [];
|
||||
export function getModelEnum(): Enum {
|
||||
const prop: Enum = {
|
||||
name: '',
|
||||
type: '',
|
||||
values: [],
|
||||
validation: null,
|
||||
};
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
23
src/openApi/v2/parser/getEnumSymbols.ts
Normal file
23
src/openApi/v2/parser/getEnumSymbols.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
|
||||
|
||||
export function getEnumSymbols(values?: (string | number)[]): EnumSymbol[] {
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
.filter((value, index, arr) => {
|
||||
return arr.indexOf(value) === index;
|
||||
})
|
||||
.map(value => {
|
||||
if (typeof value === 'number') {
|
||||
return {
|
||||
name: `NUM_${value}`,
|
||||
value: String(value),
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
|
||||
value: `'${value}'`,
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@ -1,16 +1,16 @@
|
||||
import { ModelEnumValue } from '../../../client/interfaces/ModelEnumValue';
|
||||
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
|
||||
|
||||
export function getModelEnumFromDescription(description: string): ModelEnumValue[] {
|
||||
export function getEnumSymbolsFromDescription(description: string): EnumSymbol[] {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+,?)+$/g.test(description)) {
|
||||
const matches: RegExpMatchArray | null = description.match(/(\w+=[0-9]+,?)/g);
|
||||
const matches = description.match(/(\w+=[0-9]+,?)/g);
|
||||
if (matches) {
|
||||
// Grab the values from the description
|
||||
const symbols: ModelEnumValue[] = [];
|
||||
matches.forEach((match: string): void => {
|
||||
const name: string = match.split('=')[0];
|
||||
const value: number = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
const symbols: EnumSymbol[] = [];
|
||||
matches.forEach(match => {
|
||||
const name = match.split('=')[0];
|
||||
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
if (name && Number.isInteger(value)) {
|
||||
symbols.push({
|
||||
name: name.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
|
||||
@ -20,7 +20,7 @@ export function getModelEnumFromDescription(description: string): ModelEnumValue
|
||||
});
|
||||
|
||||
// Filter out any duplicate names
|
||||
return symbols.filter((symbol: ModelEnumValue, index: number, arr: ModelEnumValue[]): boolean => {
|
||||
return symbols.filter((symbol, index, arr) => {
|
||||
return arr.map(item => item.name).indexOf(symbol.name) === index;
|
||||
});
|
||||
}
|
||||
@ -1,15 +1,18 @@
|
||||
export function getEnumType(symbols: ModelSymbol[], addParentheses = false): string {
|
||||
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
|
||||
import { getEnumValues } from './getEnumValues';
|
||||
|
||||
export function getEnumType(symbols: EnumSymbol[], addParentheses: boolean = false): string {
|
||||
// Fetch values from the symbols, just to be sure we filter out
|
||||
// any double values and finally we sort them to make them easier
|
||||
// to read when we use them in our generated code.
|
||||
const entries: string[] = getEnumValues(symbols);
|
||||
const values = getEnumValues(symbols);
|
||||
|
||||
// Add grouping parentheses if needed. This can be handy if enum values
|
||||
// are used in Arrays, so that you will get the following definition:
|
||||
// const myArray: ('EnumValue1' | 'EnumValue2' | 'EnumValue3')[];
|
||||
if (entries.length > 1 && addParentheses) {
|
||||
return `(${entries.join(' | ')})`;
|
||||
if (values.length > 1 && addParentheses) {
|
||||
return `(${values.join(' | ')})`;
|
||||
}
|
||||
|
||||
return entries.join(' | ');
|
||||
return values.join(' | ');
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
export function getEnumValues(symbols: ModelSymbol[]): string[] {
|
||||
import { EnumSymbol } from '../../../client/interfaces/EnumSymbol';
|
||||
|
||||
export function getEnumValues(symbols: EnumSymbol[]): string[] {
|
||||
// Fetch values from the symbols, just to be sure we filter out
|
||||
// any double values and finally we sort them to make them easier
|
||||
// to read when we use them in our generated code.
|
||||
return symbols
|
||||
.map(symbol => symbol.value)
|
||||
.filter((value: string, index: number, arr: string[]): boolean => {
|
||||
return arr.indexOf(value) === index;
|
||||
.filter((symbol, index, arr) => {
|
||||
return arr.indexOf(symbol) === index;
|
||||
})
|
||||
.sort();
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { PrimaryType, TYPE_MAPPINGS } from './constants';
|
||||
* Get mapped type for given type to any basic Typescript/Javascript type.
|
||||
*/
|
||||
export function getMappedType(type: string): PrimaryType | string {
|
||||
const mapped: string | undefined = TYPE_MAPPINGS.get(type.toLowerCase());
|
||||
const mapped = TYPE_MAPPINGS.get(type.toLowerCase());
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
@ -2,158 +2,204 @@ import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { getComment } from './getComment';
|
||||
import { getType } from './getType';
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
import { getEnumType } from './getEnumType';
|
||||
import { PrimaryType } from './constants';
|
||||
import { OpenApiReference } from '../interfaces/OpenApiReference';
|
||||
import { getRef } from './getRef';
|
||||
import { getEnumValues } from './getEnumValues';
|
||||
import { Model } from '../../../client/interfaces/Model';
|
||||
import { getValidationForRef } from './getValidationForRef';
|
||||
import { getValidationForType } from './getValidationForType';
|
||||
import { getValidationForArrayRef } from './getValidationForArrayRef';
|
||||
import { getModelType } from './getModelType';
|
||||
import { getModelValidation } from './getModelValidation';
|
||||
import { getValidation } from './getValidation';
|
||||
import { PrimaryType } from './constants';
|
||||
import { getEnumType } from './getEnumType';
|
||||
import { getEnumSymbols } from './getEnumSymbols';
|
||||
import { getEnumValues } from './getEnumValues';
|
||||
import { getEnumSymbolsFromDescription } from './getEnumSymbolsFromDescription';
|
||||
|
||||
export function getModel(openApi: OpenApi, schema: OpenApiSchema, name: string): Model {
|
||||
export function getModel(openApi: OpenApi, definition: OpenApiSchema, definitionName: string = 'unknown'): Model {
|
||||
const result: Model = {
|
||||
name,
|
||||
isInterface: false,
|
||||
isType: false,
|
||||
isEnum: false,
|
||||
name: definitionName,
|
||||
type: 'any',
|
||||
base: 'any',
|
||||
template: null,
|
||||
validation: null,
|
||||
description: getComment(schema.description),
|
||||
extends: null,
|
||||
description: getComment(definition.description),
|
||||
extends: [],
|
||||
imports: [],
|
||||
enums: [],
|
||||
symbols: [],
|
||||
properties: [],
|
||||
};
|
||||
|
||||
if (definition.$ref) {
|
||||
const definitionRef = getType(definition.$ref);
|
||||
result.isType = true;
|
||||
result.type = definitionRef.type;
|
||||
result.base = definitionRef.base;
|
||||
result.template = definitionRef.template;
|
||||
result.validation = getValidationForRef(definitionRef);
|
||||
result.imports.push(...definitionRef.imports);
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the param is a enum then return the values as an inline type.
|
||||
if (schema.enum) {
|
||||
const enumSymbols: ModelSymbol[] = getEnumSymbols(schema.enum);
|
||||
if (definition.enum) {
|
||||
const enumSymbols = getEnumSymbols(definition.enum);
|
||||
if (enumSymbols.length) {
|
||||
result.isEnum = true;
|
||||
result.symbols = enumSymbols;
|
||||
result.type = getEnumType(enumSymbols);
|
||||
result.base = PrimaryType.STRING;
|
||||
result.validation = `yup.mixed<${name}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
|
||||
result.validation = `yup.mixed<${definitionName}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the param is a enum then return the values as an inline type.
|
||||
if (schema.type === 'int' && schema.description) {
|
||||
const enumSymbols: ModelSymbol[] = getEnumSymbolsFromDescription(schema.description);
|
||||
if (definition.type === 'int' && definition.description) {
|
||||
const enumSymbols = getEnumSymbolsFromDescription(definition.description);
|
||||
if (enumSymbols.length) {
|
||||
result.isEnum = true;
|
||||
result.symbols = enumSymbols;
|
||||
result.type = getEnumType(enumSymbols);
|
||||
result.base = PrimaryType.NUMBER;
|
||||
result.validation = `yup.mixed<${name}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
|
||||
result.validation = `yup.mixed<${definitionName}>().oneOf([${getEnumValues(enumSymbols).join(', ')}])`;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the schema is an Array type, we check for the child type,
|
||||
// so we can create a typed array, otherwise this will be a "any[]".
|
||||
if (schema.type === 'array' && schema.items) {
|
||||
if (schema.items.$ref) {
|
||||
const arrayType: Type = getType(schema.items.$ref);
|
||||
result.imports.push(...arrayType.imports);
|
||||
if (definition.type === 'array' && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItemsRef = getType(definition.items.$ref);
|
||||
result.imports.push(...arrayItemsRef.imports);
|
||||
result.isType = true;
|
||||
result.type = `${arrayType.type}[]`;
|
||||
result.base = arrayType.base;
|
||||
result.template = arrayType.template;
|
||||
result.validation = `yup.array<${result.name}>().of(${result.base}.schema)`; // TODO: Simple strings!
|
||||
result.imports.push(...arrayType.imports);
|
||||
result.type = `${arrayItemsRef.type}[]`;
|
||||
result.base = arrayItemsRef.base;
|
||||
result.template = arrayItemsRef.template;
|
||||
result.validation = getValidationForArrayRef(arrayItemsRef);
|
||||
result.imports.push(...arrayItemsRef.imports);
|
||||
} else {
|
||||
const array: Schema = getSchema(openApi, schema.items, 'unkown');
|
||||
const arrayType: string = getSchemaType(array);
|
||||
const arrayItemsModel = getModel(openApi, definition.items);
|
||||
result.isType = true;
|
||||
result.type = `${arrayType}[]`;
|
||||
result.base = arrayType;
|
||||
result.template = null;
|
||||
result.validation = `yup.array<${result.name}>().of(${result.base}.schema)`; // TODO: Simple strings!
|
||||
result.imports.push(...array.imports);
|
||||
result.type = `${arrayItemsModel.type}[]`;
|
||||
result.base = arrayItemsModel.base;
|
||||
result.template = arrayItemsModel.template;
|
||||
// result.validation = getValidationForArray(array.validation || 'any');
|
||||
result.imports.push(...arrayItemsModel.imports);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if this model extends other models
|
||||
if (schema.allOf) {
|
||||
schema.allOf.forEach((parent: OpenApiSchema & OpenApiReference): void => {
|
||||
const parentSchema: SchemaReference = getSchemaReference(openApi, parent);
|
||||
result.extends.push(parentSchema.type);
|
||||
result.imports.push(parentSchema.base);
|
||||
|
||||
// Merge properties of other models
|
||||
if (parent.properties) {
|
||||
for (const propertyName in schema.properties) {
|
||||
if (schema.properties.hasOwnProperty(propertyName)) {
|
||||
const propertyRef: OpenApiSchema & OpenApiReference = schema.properties[propertyName];
|
||||
const propertyRequired: boolean = (schema.required && schema.required.includes(propertyName)) || false;
|
||||
const property: SchemaProperty = getSchemaProperty(openApi, propertyRef, propertyName, propertyRequired);
|
||||
result.imports.push(...property.imports);
|
||||
result.properties.set(propertyName, property);
|
||||
if (definition.allOf) {
|
||||
definition.allOf.forEach(parent => {
|
||||
if (parent.$ref) {
|
||||
const parentRef = getType(parent.$ref);
|
||||
result.extends.push(parentRef.type);
|
||||
result.imports.push(parentRef.base);
|
||||
}
|
||||
if (parent.type === 'object' && parent.properties) {
|
||||
for (const propertyName in parent.properties) {
|
||||
if (parent.properties.hasOwnProperty(propertyName)) {
|
||||
const property = parent.properties[propertyName];
|
||||
const propertyRequired = !!(parent.required && parent.required.includes(propertyName));
|
||||
const propertyReadOnly = !!property.readOnly;
|
||||
if (property.$ref) {
|
||||
const propertyRef = getType(property.$ref);
|
||||
result.imports.push(...propertyRef.imports);
|
||||
result.properties.push({
|
||||
name: propertyName,
|
||||
type: propertyRef.type,
|
||||
required: propertyRequired,
|
||||
nullable: false,
|
||||
readOnly: propertyReadOnly,
|
||||
description: property.description || null,
|
||||
validation: getValidationForRef(propertyRef, propertyRequired),
|
||||
});
|
||||
} else {
|
||||
const propertyModel = getModel(openApi, property);
|
||||
result.imports.push(...propertyModel.imports);
|
||||
result.properties.push({
|
||||
name: propertyName,
|
||||
type: propertyModel.type,
|
||||
required: propertyRequired,
|
||||
nullable: false,
|
||||
readOnly: propertyReadOnly,
|
||||
description: property.description || null,
|
||||
validation: propertyModel.validation ? getValidation(propertyModel.validation, propertyRequired) : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
if (schema.type === 'object' && schema.properties) {
|
||||
// Validation needs to also check extended schema!
|
||||
// Check ModelThatExtends.ts
|
||||
result.isInterface = true;
|
||||
result.type = 'interface';
|
||||
result.base = 'interface';
|
||||
result.type = getModelType(result.properties);
|
||||
result.validation = getModelValidation(definitionName, result.properties);
|
||||
result.base = PrimaryType.OBJECT;
|
||||
result.template = null;
|
||||
}
|
||||
|
||||
for (const propertyName in schema.properties) {
|
||||
if (schema.properties.hasOwnProperty(propertyName)) {
|
||||
const propertyRequired: boolean = (schema.required && schema.required.includes(propertyName)) || false;
|
||||
const propertyRef: OpenApiSchema & OpenApiReference = schema.properties[propertyName];
|
||||
|
||||
if (propertyRef.$ref) {
|
||||
const propertyType: Type = getType(propertyRef.$ref);
|
||||
result.imports.push(...propertyType.imports);
|
||||
if (definition.type === 'object' && definition.properties) {
|
||||
for (const propertyName in definition.properties) {
|
||||
if (definition.properties.hasOwnProperty(propertyName)) {
|
||||
const property = definition.properties[propertyName];
|
||||
const propertyRequired = !!(definition.required && definition.required.includes(propertyName));
|
||||
const propertyReadOnly = !!property.readOnly;
|
||||
if (property.$ref) {
|
||||
const propertyRef = getType(property.$ref);
|
||||
result.imports.push(...propertyRef.imports);
|
||||
result.properties.push({
|
||||
name: propertyName,
|
||||
type: propertyType.type,
|
||||
type: propertyRef.type,
|
||||
required: propertyRequired,
|
||||
nullable: false,
|
||||
readOnly: false,
|
||||
readOnly: propertyReadOnly,
|
||||
description: property.description || null,
|
||||
validation: getValidationForRef(propertyRef, propertyRequired),
|
||||
});
|
||||
} else {
|
||||
const property: OpenApiSchema = getRef<OpenApiSchema>(openApi, propertyRef);
|
||||
const propertySchema: Schema = getSchema(openApi, property, propertyName);
|
||||
const propertyType: string = getSchemaType(propertySchema);
|
||||
result.imports.push(...propertySchema.imports);
|
||||
const propertyModel = getModel(openApi, property);
|
||||
result.imports.push(...propertyModel.imports);
|
||||
result.properties.push({
|
||||
name: propertyName,
|
||||
type: propertyType,
|
||||
type: propertyModel.type,
|
||||
required: propertyRequired,
|
||||
nullable: false,
|
||||
readOnly: property.readOnly || false,
|
||||
description: property.description,
|
||||
readOnly: propertyReadOnly,
|
||||
description: property.description || null,
|
||||
validation: propertyModel.validation ? getValidation(propertyModel.validation, propertyRequired) : null,
|
||||
});
|
||||
|
||||
// TODO: This also needs a validation logic, maybe we can store that
|
||||
// per schema and have them 'concatenate' on demand??
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.isInterface = true;
|
||||
result.type = getModelType(result.properties);
|
||||
result.validation = getModelValidation(definitionName, result.properties);
|
||||
result.base = PrimaryType.OBJECT;
|
||||
result.template = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the schema has a type than it can be a basic or generic type.
|
||||
if (schema.type) {
|
||||
const schemaType: Type = getType(schema.type);
|
||||
if (definition.type !== 'object' && definition.type) {
|
||||
const definitionType = getType(definition.type);
|
||||
result.isType = true;
|
||||
result.type = schemaType.type;
|
||||
result.base = schemaType.base;
|
||||
result.template = schemaType.template;
|
||||
result.imports.push(...schemaType.imports);
|
||||
result.type = definitionType.type;
|
||||
result.base = definitionType.base;
|
||||
result.template = definitionType.template;
|
||||
result.validation = getValidationForType(definitionType);
|
||||
result.imports.push(...definitionType.imports);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { getModelTemplate } from './getModelTemplate';
|
||||
|
||||
describe('getModelTemplate', () => {
|
||||
it('should return generic for template type', () => {
|
||||
const template: string = getModelTemplate({
|
||||
const template = getModelTemplate({
|
||||
type: 'Link<Model>',
|
||||
base: 'Link',
|
||||
template: 'Model',
|
||||
@ -12,7 +12,7 @@ describe('getModelTemplate', () => {
|
||||
});
|
||||
|
||||
it('should return empty for primary type', () => {
|
||||
const template: string = getModelTemplate({
|
||||
const template = getModelTemplate({
|
||||
type: 'string',
|
||||
base: 'string',
|
||||
template: null,
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
import { Model } from '../../../client/interfaces/Model';
|
||||
import { EOL } from 'os';
|
||||
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
|
||||
|
||||
// string
|
||||
// array[test]
|
||||
// array[{
|
||||
// foo: string
|
||||
// bar: string
|
||||
// }]
|
||||
export function getModelType(model: Model): string {
|
||||
// if (schema.properties) {
|
||||
// return schema.type
|
||||
// }
|
||||
if (model.type) {
|
||||
return model.type;
|
||||
}
|
||||
return 'any';
|
||||
export function getModelType(properties: ModelProperty[]): string {
|
||||
return [
|
||||
`{`,
|
||||
...properties.map(property => {
|
||||
return ` ${property.readOnly ? 'readonly ' : ''}${property.name}${property.required ? '' : '?'}: ${property.type},`;
|
||||
}),
|
||||
`}`,
|
||||
].join(EOL);
|
||||
}
|
||||
|
||||
12
src/openApi/v2/parser/getModelValidation.ts
Normal file
12
src/openApi/v2/parser/getModelValidation.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { EOL } from 'os';
|
||||
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
|
||||
|
||||
export function getModelValidation(name: string, properties: ModelProperty[]): string {
|
||||
return [
|
||||
`yup.object().shape({`,
|
||||
...properties.map(property => {
|
||||
return ` ${property.name}: ${property.validation},`;
|
||||
}),
|
||||
`}).noUnknown()`,
|
||||
].join(EOL);
|
||||
}
|
||||
@ -1,14 +1,13 @@
|
||||
import { Model } from '../../../client/interfaces/Model';
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { getModel } from './getModel';
|
||||
|
||||
export function getModels(openApi: OpenApi): Model[] {
|
||||
const models: Model[] = [];
|
||||
for (const definitionName in openApi.definitions) {
|
||||
if (openApi.definitions.hasOwnProperty(definitionName)) {
|
||||
const definition: OpenApiSchema = openApi.definitions[definitionName];
|
||||
const definitionModel: Model = getModel(openApi, definition, definitionName);
|
||||
const definition = openApi.definitions[definitionName];
|
||||
const definitionModel = getModel(openApi, definition, definitionName);
|
||||
models.push(definitionModel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,13 +9,14 @@ import { getComment } from './getComment';
|
||||
import { getOperationResponses } from './getOperationResponses';
|
||||
import { getOperationResponse } from './getOperationResponse';
|
||||
import { getOperationErrors } from './getOperationErrors';
|
||||
import { Operation } from '../../../client/interfaces/Operation';
|
||||
|
||||
export function getOperation(openApi: OpenApi, url: string, method: string, op: OpenApiOperation): Operation {
|
||||
const serviceName: string = (op.tags && op.tags[0]) || 'Service';
|
||||
const serviceClassName: string = getServiceClassName(serviceName);
|
||||
const operationNameFallback: string = `${method}${serviceClassName}`;
|
||||
const operationName: string = getOperationName(op.operationId || operationNameFallback);
|
||||
const operationPath: string = getOperationPath(url);
|
||||
const serviceName = (op.tags && op.tags[0]) || 'Service';
|
||||
const serviceClassName = getServiceClassName(serviceName);
|
||||
const operationNameFallback = `${method}${serviceClassName}`;
|
||||
const operationName = getOperationName(op.operationId || operationNameFallback);
|
||||
const operationPath = getOperationPath(url);
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const result: Operation = {
|
||||
@ -23,7 +24,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
|
||||
name: operationName,
|
||||
summary: getComment(op.summary),
|
||||
description: getComment(op.description),
|
||||
deprecated: op.deprecated,
|
||||
deprecated: op.deprecated || false,
|
||||
method: method,
|
||||
path: operationPath,
|
||||
parameters: [],
|
||||
@ -39,7 +40,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
|
||||
|
||||
// Parse the operation parameters (path, query, body, etc).
|
||||
if (op.parameters) {
|
||||
const parameters: OperationParameters = getOperationParameters(openApi, op.parameters);
|
||||
const parameters = getOperationParameters(openApi, op.parameters);
|
||||
result.imports.push(...parameters.imports);
|
||||
result.parameters.push(...parameters.parameters);
|
||||
result.parametersPath.push(...parameters.parametersPath);
|
||||
@ -51,9 +52,9 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
|
||||
|
||||
// Parse the operation responses.
|
||||
if (op.responses) {
|
||||
const responses: OperationResponse[] = getOperationResponses(openApi, op.responses);
|
||||
const response: OperationResponse = getOperationResponse(responses);
|
||||
const errors: OperationError[] = getOperationErrors(responses);
|
||||
const responses = getOperationResponses(openApi, op.responses);
|
||||
const response = getOperationResponse(responses);
|
||||
const errors = getOperationErrors(responses);
|
||||
result.imports.push(...response.imports);
|
||||
result.errors = errors;
|
||||
result.result = response.type;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
import { OperationError } from '../../../client/interfaces/OperationError';
|
||||
|
||||
export function getOperationErrors(responses: OperationResponse[]): OperationError[] {
|
||||
return responses
|
||||
.filter((response: OperationResponse): boolean => {
|
||||
.filter(response => {
|
||||
return response.code >= 300 && response.text !== undefined && response.text !== '';
|
||||
})
|
||||
.map(
|
||||
(response: OperationResponse): OperationError => ({
|
||||
code: response.code,
|
||||
text: response.text,
|
||||
})
|
||||
);
|
||||
.map(response => ({
|
||||
code: response.code,
|
||||
text: response.text,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -6,6 +6,6 @@ import camelCase from 'camelcase';
|
||||
* the most popular Javascript and Typescript writing style.
|
||||
*/
|
||||
export function getOperationName(value: string): string {
|
||||
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
return camelCase(clean);
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import { getType } from './getType';
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getParameterName } from './getParameterName';
|
||||
import { getComment } from './getComment';
|
||||
import { getOperationParameterName } from './getOperationParameterName';
|
||||
import { OperationParameter } from '../../../client/interfaces/OperationParameter';
|
||||
|
||||
export function getParameter(openApi: OpenApi, parameter: OpenApiParameter): Parameter {
|
||||
const result: Parameter = {
|
||||
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
|
||||
const result: OperationParameter = {
|
||||
in: parameter.in,
|
||||
prop: parameter.name,
|
||||
name: getParameterName(parameter.name),
|
||||
name: getOperationParameterName(parameter.name),
|
||||
type: 'any',
|
||||
base: 'any',
|
||||
template: null,
|
||||
@ -22,7 +22,7 @@ export function getParameter(openApi: OpenApi, parameter: OpenApiParameter): Par
|
||||
|
||||
// If the parameter has a type than it can be a basic or generic type.
|
||||
if (parameter.type) {
|
||||
const parameterType: Type = getType(parameter.type);
|
||||
const parameterType = getType(parameter.type);
|
||||
result.type = parameterType.type;
|
||||
result.base = parameterType.base;
|
||||
result.template = parameterType.template;
|
||||
|
||||
@ -4,7 +4,7 @@ import camelCase from 'camelcase';
|
||||
* Replaces any invalid characters from a parameter name.
|
||||
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
|
||||
*/
|
||||
export function getParameterName(value: string): string {
|
||||
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
export function getOperationParameterName(value: string): string {
|
||||
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
return camelCase(clean);
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import { OpenApiReference } from '../interfaces/OpenApiReference';
|
||||
import { getParameter } from './getParameter';
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getRef } from './getRef';
|
||||
import { OperationParameters } from '../../../client/interfaces/OperationParameters';
|
||||
import { OperationParameter } from '../../../client/interfaces/OperationParameter';
|
||||
import { getOperationParameter } from './getOperationParameter';
|
||||
|
||||
function sortByRequired(a: Parameter, b: Parameter): number {
|
||||
function sortByRequired(a: OperationParameter, b: OperationParameter): number {
|
||||
return a.required && !b.required ? -1 : !a.required && b.required ? 1 : 0;
|
||||
}
|
||||
|
||||
@ -21,8 +23,8 @@ export function getOperationParameters(openApi: OpenApi, parameters: (OpenApiPar
|
||||
|
||||
// Iterate over the parameters
|
||||
parameters.forEach(parameter => {
|
||||
const paramRef: OpenApiParameter = getRef<OpenApiParameter>(openApi, parameter);
|
||||
const param: Parameter = getParameter(openApi, paramRef);
|
||||
const paramRef = getRef<OpenApiParameter>(openApi, parameter);
|
||||
const param = getOperationParameter(openApi, paramRef);
|
||||
|
||||
// We ignore the "api-version" param, since we do not want to add this
|
||||
// as the first / default parameter for each of the service calls.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { PrimaryType } from './constants';
|
||||
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
export function getOperationResponse(responses: OperationResponse[]): OperationResponse {
|
||||
const response: OperationResponse = {
|
||||
@ -11,7 +12,7 @@ export function getOperationResponse(responses: OperationResponse[]): OperationR
|
||||
};
|
||||
|
||||
// Fetch the first valid (2XX range) response code and return that type.
|
||||
const result: OperationResponse | undefined = responses.find(response => response.code && response.code >= 200 && response.code < 300);
|
||||
const result = responses.find(response => response.code && response.code >= 200 && response.code < 300);
|
||||
if (result) {
|
||||
response.code = result.code;
|
||||
response.text = result.text;
|
||||
|
||||
@ -6,7 +6,7 @@ export function getOperationResponseCode(value: string | 'default'): number | nu
|
||||
|
||||
// Check if we can parse the code and return of successful.
|
||||
if (/[0-9]+/g.test(value)) {
|
||||
const code: number = parseInt(value);
|
||||
const code = parseInt(value);
|
||||
if (Number.isInteger(code)) {
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { OpenApiResponses } from '../interfaces/OpenApiResponses';
|
||||
import { getOperationResponseCode } from './getOperationResponseCode';
|
||||
import { OpenApiResponse } from '../interfaces/OpenApiResponse';
|
||||
import { OpenApiReference } from '../interfaces/OpenApiReference';
|
||||
import { getRef } from './getRef';
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
export function getOperationResponses(openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] {
|
||||
const results: OperationResponse[] = [];
|
||||
@ -12,9 +12,9 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon
|
||||
// status code and response message (if any).
|
||||
for (const code in responses) {
|
||||
if (responses.hasOwnProperty(code)) {
|
||||
const responseOrReference: OpenApiResponse & OpenApiReference = responses[code];
|
||||
const response: OpenApiResponse = getRef<OpenApiResponse>(openApi, responseOrReference);
|
||||
const responseCode: number | null = getOperationResponseCode(code);
|
||||
const responseOrReference = responses[code];
|
||||
const response = getRef<OpenApiResponse>(openApi, responseOrReference);
|
||||
const responseCode = getOperationResponseCode(code);
|
||||
|
||||
// If there is a response code then we check what data we get back,
|
||||
// if there is no typed data, we just return <any> so the user is still
|
||||
|
||||
@ -5,7 +5,7 @@ export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
|
||||
if (item.$ref) {
|
||||
// Fetch the paths to the definitions, this converts:
|
||||
// "#/definitions/Form" to ["definitions", "Form"]
|
||||
const paths: string[] = item.$ref
|
||||
const paths = item.$ref
|
||||
.replace(/^#/g, '')
|
||||
.split('/')
|
||||
.filter(item => item);
|
||||
|
||||
@ -5,8 +5,8 @@ import { OpenApi } from '../interfaces/OpenApi';
|
||||
* @param openApi
|
||||
*/
|
||||
export function getServer(openApi: OpenApi): string {
|
||||
const scheme: string = (openApi.schemes && openApi.schemes[0]) || 'http';
|
||||
const host: string | undefined = openApi.host;
|
||||
const basePath: string = openApi.basePath || '';
|
||||
const scheme = (openApi.schemes && openApi.schemes[0]) || 'http';
|
||||
const host = openApi.host;
|
||||
const basePath = openApi.basePath || '';
|
||||
return host ? `${scheme}://${host}${basePath}` : basePath;
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@ import camelCase from 'camelcase';
|
||||
* the input string to PascalCase and appends the "Service" prefix if needed.
|
||||
*/
|
||||
export function getServiceClassName(value: string): string {
|
||||
const clean: string = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
const name: string = camelCase(clean, { pascalCase: true });
|
||||
const clean = value.replace(/[^\w\s\-]+/g, '_').trim();
|
||||
const name = camelCase(clean, { pascalCase: true });
|
||||
if (name && !name.endsWith('Service')) {
|
||||
return `${name}Service`;
|
||||
}
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
* This basically removes any "v" prefix from the version string.
|
||||
* @param version
|
||||
*/
|
||||
export function getServiceVersion(version: string = '1.0'): string {
|
||||
export function getServiceVersion(version = '1.0'): string {
|
||||
return version.replace(/^v/gi, '');
|
||||
}
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { Service } from '../../../client/interfaces/Service';
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { OpenApiPath } from '../interfaces/OpenApiPath';
|
||||
import { Method } from './constants';
|
||||
|
||||
/**
|
||||
* Get the OpenAPI services
|
||||
*/
|
||||
export function getServices(openApi: OpenApi): Service[] {
|
||||
const services: Map<string, Service> = new Map<string, Service>();
|
||||
const services = new Map<string, Service>();
|
||||
for (const url in openApi.paths) {
|
||||
if (openApi.paths.hasOwnProperty(url)) {
|
||||
const path: OpenApiPath = openApi.paths[url];
|
||||
const path = openApi.paths[url];
|
||||
for (const method in path) {
|
||||
if (path.hasOwnProperty(method)) {
|
||||
switch (method) {
|
||||
|
||||
@ -17,15 +17,15 @@ export function getType(value: string | undefined, template: string | null = nul
|
||||
};
|
||||
|
||||
// Remove definitions prefix and cleanup string.
|
||||
const valueClean: string = stripNamespace(value || '');
|
||||
const valueClean = stripNamespace(value || '');
|
||||
|
||||
// Check of we have an Array type or generic type, for instance: "Link[Model]".
|
||||
if (/\[.*\]$/g.test(valueClean)) {
|
||||
const matches: RegExpMatchArray | null = valueClean.match(/(.*?)\[(.*)\]$/);
|
||||
const matches = valueClean.match(/(.*?)\[(.*)\]$/);
|
||||
if (matches && matches.length) {
|
||||
// Both of the types can be complex types so parse each of them.
|
||||
const match1: Type = getType(matches[1]);
|
||||
const match2: Type = getType(matches[2]);
|
||||
const match1 = getType(matches[1]);
|
||||
const match2 = getType(matches[2]);
|
||||
|
||||
// If the first match is a generic array then construct a correct array type,
|
||||
// for example the type "Array[Model]" becomes "Model[]".
|
||||
@ -50,7 +50,7 @@ export function getType(value: string | undefined, template: string | null = nul
|
||||
result.imports.push(...match2.imports);
|
||||
}
|
||||
} else if (hasMappedType(valueClean)) {
|
||||
const mapped: string = getMappedType(valueClean);
|
||||
const mapped = getMappedType(valueClean);
|
||||
result.type = mapped;
|
||||
result.base = mapped;
|
||||
} else if (valueClean) {
|
||||
|
||||
11
src/openApi/v2/parser/getValidation.ts
Normal file
11
src/openApi/v2/parser/getValidation.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export function getValidation(validation: string, required: boolean = false, nullable: boolean = false): string {
|
||||
if (required) {
|
||||
validation = `${validation}.required()`;
|
||||
}
|
||||
|
||||
if (nullable) {
|
||||
validation = `${validation}.nullable()`;
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
15
src/openApi/v2/parser/getValidationForArrayRef.ts
Normal file
15
src/openApi/v2/parser/getValidationForArrayRef.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
|
||||
export function getValidationForArrayRef(ref: Type, required: boolean = false, nullable: boolean = false): string {
|
||||
let validation = `yup.array<${ref.type}>().of(${ref.base}.schema)`;
|
||||
|
||||
if (required) {
|
||||
validation = `${validation}.required()`;
|
||||
}
|
||||
|
||||
if (nullable) {
|
||||
validation = `${validation}.nullable()`;
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
28
src/openApi/v2/parser/getValidationForArrayType.ts
Normal file
28
src/openApi/v2/parser/getValidationForArrayType.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { PrimaryType } from './constants';
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
|
||||
export function getValidationForArrayType(type: Type, required: boolean = false, nullable: boolean = false): string {
|
||||
let validation = `yup.array<any>().of(yup.mixed())`;
|
||||
|
||||
switch (type.type) {
|
||||
case PrimaryType.BOOLEAN:
|
||||
validation = `yup.array<boolean>().of(yup.boolean())`;
|
||||
break;
|
||||
case PrimaryType.NUMBER:
|
||||
validation = `yup.array<number>().of(yup.number())`;
|
||||
break;
|
||||
case PrimaryType.STRING:
|
||||
validation = `yup.array<string>().of(yup.string())`;
|
||||
break;
|
||||
}
|
||||
|
||||
if (required) {
|
||||
validation = `${validation}.required()`;
|
||||
}
|
||||
|
||||
if (nullable) {
|
||||
validation = `${validation}.nullable()`;
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
15
src/openApi/v2/parser/getValidationForRef.ts
Normal file
15
src/openApi/v2/parser/getValidationForRef.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
|
||||
export function getValidationForRef(ref: Type, required = false, nullable = false): string {
|
||||
let validation = `${ref.base}.schema`;
|
||||
|
||||
if (required) {
|
||||
validation = `${validation}.required()`;
|
||||
}
|
||||
|
||||
if (nullable) {
|
||||
validation = `${validation}.nullable()`;
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
28
src/openApi/v2/parser/getValidationForType.ts
Normal file
28
src/openApi/v2/parser/getValidationForType.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { PrimaryType } from './constants';
|
||||
import { Type } from '../../../client/interfaces/Type';
|
||||
|
||||
export function getValidationForType(type: Type, required: boolean = false, nullable: boolean = false): string {
|
||||
let validation = `yup.mixed<${type.type}>()`;
|
||||
|
||||
switch (type.type) {
|
||||
case PrimaryType.BOOLEAN:
|
||||
validation = `yup.boolean()`;
|
||||
break;
|
||||
case PrimaryType.NUMBER:
|
||||
validation = `yup.number()`;
|
||||
break;
|
||||
case PrimaryType.STRING:
|
||||
validation = `yup.string()`;
|
||||
break;
|
||||
}
|
||||
|
||||
if (required) {
|
||||
validation = `${validation}.required()`;
|
||||
}
|
||||
|
||||
if (nullable) {
|
||||
validation = `${validation}.nullable()`;
|
||||
}
|
||||
|
||||
return validation;
|
||||
}
|
||||
@ -6,6 +6,7 @@ describe('isPrimaryType', () => {
|
||||
expect(isPrimaryType('boolean')).toBeTruthy();
|
||||
expect(isPrimaryType('string')).toBeTruthy();
|
||||
expect(isPrimaryType('any')).toBeTruthy();
|
||||
expect(isPrimaryType('any[]')).toBeTruthy();
|
||||
expect(isPrimaryType('void')).toBeTruthy();
|
||||
expect(isPrimaryType('null')).toBeTruthy();
|
||||
expect(isPrimaryType('Array')).toBeFalsy();
|
||||
|
||||
@ -7,6 +7,7 @@ import { PrimaryType } from './constants';
|
||||
export function isPrimaryType(type: string): type is PrimaryType {
|
||||
switch (type.toLowerCase()) {
|
||||
case PrimaryType.FILE:
|
||||
case PrimaryType.ARRAY:
|
||||
case PrimaryType.OBJECT:
|
||||
case PrimaryType.BOOLEAN:
|
||||
case PrimaryType.NUMBER:
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
import { OpenApi } from '../interfaces/OpenApi';
|
||||
import { OpenApiServer } from '../interfaces/OpenApiServer';
|
||||
import { Dictionary } from '../../../utils/types';
|
||||
import { OpenApiServerVariable } from '../interfaces/OpenApiServerVariable';
|
||||
|
||||
export function getServer(openApi: OpenApi): string {
|
||||
const server: OpenApiServer | undefined = openApi.servers && openApi.servers[0];
|
||||
const variables: Dictionary<OpenApiServerVariable> = (server && server.variables) || {};
|
||||
let url: string = (server && server.url) || '';
|
||||
const server = openApi.servers && openApi.servers[0];
|
||||
const variables = (server && server.variables) || {};
|
||||
let url = (server && server.url) || '';
|
||||
for (const variable in variables) {
|
||||
if (variables.hasOwnProperty(variable)) {
|
||||
url = url.replace(`{${variable}}`, variables[variable].default);
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
* @param params Key value based object.
|
||||
*/
|
||||
export function getFormData(params: { [key: string]: any }): FormData {
|
||||
const formData: FormData = new FormData();
|
||||
const formData = new FormData();
|
||||
for (const key in params) {
|
||||
if (typeof params[key] !== 'undefined') {
|
||||
const value: any = params[key];
|
||||
|
||||
@ -17,10 +17,10 @@ import { Result } from './Result';
|
||||
*/
|
||||
export async function request<T = any>(options: Readonly<RequestOptions>): Promise<Result<T>> {
|
||||
// Create the request URL
|
||||
let url: string = `${OpenAPI.BASE}${options.path}`;
|
||||
let url = `${OpenAPI.BASE}${options.path}`;
|
||||
|
||||
// Create request headers
|
||||
const headers: Headers = new Headers({
|
||||
const headers = new Headers({
|
||||
...options.headers,
|
||||
Accept: 'application/json',
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@ import { Result } from './Result';
|
||||
*/
|
||||
export async function requestUsingFetch<T = any>(url: string, request: Readonly<RequestInit>): Promise<Result<T>> {
|
||||
// Fetch response using fetch API.
|
||||
const response: Response = await fetch(url, request);
|
||||
const response = await fetch(url, request);
|
||||
|
||||
// Create result object.
|
||||
const result: Result = {
|
||||
@ -28,7 +28,7 @@ export async function requestUsingFetch<T = any>(url: string, request: Readonly<
|
||||
// Try to parse the content for any response status code.
|
||||
// We check the "Content-Type" header to see if we need to parse the
|
||||
// content as json or as plain text.
|
||||
const contentType: string | null = response.headers.get('Content-Type');
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
switch (contentType.toLowerCase()) {
|
||||
case 'application/json':
|
||||
|
||||
@ -16,14 +16,14 @@ import { isSuccess } from './isSuccess';
|
||||
*/
|
||||
export async function requestUsingXHR<T = any>(url: string, request: Readonly<RequestInit>): Promise<Result<T>> {
|
||||
return new Promise(resolve => {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// Open the request, remember to do this before adding any headers,
|
||||
// because the request needs to be initialized!
|
||||
xhr.open(request.method!, url, true);
|
||||
|
||||
// Add the headers (required when dealing with JSON)
|
||||
const headers: Headers = request.headers as Headers;
|
||||
const headers = request.headers as Headers;
|
||||
headers.forEach((value: string, key: string): void => {
|
||||
xhr.setRequestHeader(key, value);
|
||||
});
|
||||
@ -43,7 +43,7 @@ export async function requestUsingXHR<T = any>(url: string, request: Readonly<Re
|
||||
// Try to parse the content for any response status code.
|
||||
// We check the "Content-Type" header to see if we need to parse the
|
||||
// content as json or as plain text.
|
||||
const contentType: string | null = xhr.getResponseHeader('Content-Type');
|
||||
const contentType = xhr.getResponseHeader('Content-Type');
|
||||
if (contentType) {
|
||||
switch (contentType.toLowerCase()) {
|
||||
case 'application/json':
|
||||
|
||||
@ -10,7 +10,7 @@ export { OpenAPI } from './core/OpenAPI';
|
||||
{{#if models}}
|
||||
|
||||
{{#each models}}
|
||||
export { {{{base}}} } from './models/{{{base}}}';
|
||||
export { {{{name}}} } from './models/{{{name}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#if services}}
|
||||
|
||||
@ -11,19 +11,19 @@ import { {{{this}}} } from '../models/{{{this}}}';
|
||||
{{#if validation}}
|
||||
import * as yup from 'yup';
|
||||
{{/if}}
|
||||
{{#if description}}
|
||||
|
||||
{{#if description}}
|
||||
/**
|
||||
* {{{description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if isInterface}}
|
||||
export interface {{{name}}}{{{template}}}{{#if extend}} extends {{{extend}}}{{/if}} {
|
||||
export interface {{{name}}}{{{template}}}{{#if extends}} extends{{#each extends}} {{{this}}}{{/each}}{{/if}} {
|
||||
{{#each properties}}
|
||||
{{#if description}}
|
||||
/**
|
||||
* {{{description}}}
|
||||
*/
|
||||
* {{{description}}}
|
||||
*/
|
||||
{{/if}}
|
||||
{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}};
|
||||
{{/each}}
|
||||
@ -62,6 +62,7 @@ export type {{{name}}} = {{{type}}}{{#if nullable}} | null{{/if}};
|
||||
{{#if validation}}
|
||||
|
||||
export namespace {{{name}}} {
|
||||
|
||||
export const schema = {{{validation}}};
|
||||
|
||||
export function validate(value: any): Promise<{{{name}}}{{#if nullable}} | null{{/if}}> {
|
||||
@ -83,6 +84,7 @@ export enum {{{name}}} {
|
||||
{{#if validation}}
|
||||
|
||||
export namespace {{{name}}} {
|
||||
|
||||
export const schema = {{{validation}}};
|
||||
|
||||
export function validate(value: any): Promise<{{{name}}}> {
|
||||
|
||||
@ -44,7 +44,7 @@ export class {{{name}}} {
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
const result: Result = await request({
|
||||
const result = await request({
|
||||
method: '{{{method}}}',
|
||||
path: `{{{path}}}`,{{#if parametersHeader}}
|
||||
headers: {
|
||||
|
||||
@ -2,8 +2,19 @@ import { getSortedImports } from './getSortedImports';
|
||||
import { Model } from '../client/interfaces/Model';
|
||||
|
||||
export function cleanupModels(models: Model[]): Model[] {
|
||||
models.forEach((models: Model): void => {
|
||||
models.imports = getSortedImports(models.imports);
|
||||
models.forEach(model => {
|
||||
model.imports = getSortedImports(model.imports).filter(name => {
|
||||
return model.name !== name;
|
||||
});
|
||||
model.properties = model.properties
|
||||
.filter((property, index, arr) => {
|
||||
return arr.findIndex(item => item.name === property.name) === index;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
});
|
||||
return models;
|
||||
}
|
||||
|
||||
@ -1,28 +1,27 @@
|
||||
import { Service } from '../client/interfaces/Service';
|
||||
import { getSortedImports } from './getSortedImports';
|
||||
import { Operation } from '../client/interfaces/Operation';
|
||||
|
||||
export function cleanupServices(services: Service[]): Service[] {
|
||||
services.forEach((service: Service): void => {
|
||||
const names: Map<string, number> = new Map<string, number>();
|
||||
services.forEach(service => {
|
||||
const names = new Map<string, number>();
|
||||
|
||||
service.imports = getSortedImports(service.imports);
|
||||
service.imports = getSortedImports(service.imports).filter(name => {
|
||||
return service.name !== name;
|
||||
});
|
||||
|
||||
service.operations = service.operations
|
||||
.map(
|
||||
(operation: Operation): Operation => {
|
||||
const name: string = operation.name;
|
||||
const index: number = names.get(name) || 0;
|
||||
if (index > 0) {
|
||||
operation.name = `${name}${index}`;
|
||||
}
|
||||
names.set(name, index + 1);
|
||||
return operation;
|
||||
.map(operation => {
|
||||
const name = operation.name;
|
||||
const index = names.get(name) || 0;
|
||||
if (index > 0) {
|
||||
operation.name = `${name}${index}`;
|
||||
}
|
||||
)
|
||||
.sort((a: Operation, b: Operation): number => {
|
||||
const nameA: string = a.name.toLowerCase();
|
||||
const nameB: string = b.name.toLowerCase();
|
||||
names.set(name, index + 1);
|
||||
return operation;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
});
|
||||
|
||||
@ -24,8 +24,8 @@ function read(filePath: string): string {
|
||||
* @param filePath
|
||||
*/
|
||||
export function getOpenApiSpec(filePath: string): any {
|
||||
const content: string = read(filePath);
|
||||
const extname: string = path.extname(filePath).toLowerCase();
|
||||
const content = read(filePath);
|
||||
const extname = path.extname(filePath).toLowerCase();
|
||||
switch (extname) {
|
||||
case '.yml':
|
||||
case '.yaml':
|
||||
|
||||
@ -12,8 +12,8 @@ export enum OpenApiVersion {
|
||||
export function getOpenApiVersion(openApi: any): OpenApiVersion {
|
||||
const info: any = openApi.swagger || openApi.openapi;
|
||||
if (info && typeof info === 'string') {
|
||||
const c: string = info.charAt(0);
|
||||
const v: number = Number.parseInt(c);
|
||||
const c = info.charAt(0);
|
||||
const v = Number.parseInt(c);
|
||||
if (v === OpenApiVersion.V2 || v === OpenApiVersion.V3) {
|
||||
return v as OpenApiVersion;
|
||||
}
|
||||
|
||||
@ -6,12 +6,12 @@ export function getSortedImports(imports: string[]): string[] {
|
||||
return imports
|
||||
.filter(name => name)
|
||||
.filter(name => name.trim())
|
||||
.filter((name: string, index: number, arr: string[]) => {
|
||||
.filter((name, index, arr) => {
|
||||
return arr.indexOf(name) === index;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const nameA: string = a.toLowerCase();
|
||||
const nameB: string = b.toLowerCase();
|
||||
const nameA = a.toLowerCase();
|
||||
const nameB = b.toLowerCase();
|
||||
return nameA.localeCompare(nameB, 'en');
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,9 +14,9 @@ describe('getSortedModels', () => {
|
||||
template: null,
|
||||
validation: null,
|
||||
description: null,
|
||||
extends: null,
|
||||
extends: [],
|
||||
imports: [],
|
||||
values: [],
|
||||
symbols: [],
|
||||
properties: [],
|
||||
enums: [],
|
||||
},
|
||||
@ -30,9 +30,9 @@ describe('getSortedModels', () => {
|
||||
template: null,
|
||||
validation: null,
|
||||
description: null,
|
||||
extends: null,
|
||||
extends: [],
|
||||
imports: [],
|
||||
values: [],
|
||||
symbols: [],
|
||||
properties: [],
|
||||
enums: [],
|
||||
},
|
||||
@ -46,9 +46,9 @@ describe('getSortedModels', () => {
|
||||
template: null,
|
||||
validation: null,
|
||||
description: null,
|
||||
extends: null,
|
||||
extends: [],
|
||||
imports: [],
|
||||
values: [],
|
||||
symbols: [],
|
||||
properties: [],
|
||||
enums: [],
|
||||
},
|
||||
|
||||
@ -2,8 +2,8 @@ import { Model } from '../client/interfaces/Model';
|
||||
|
||||
export function getSortedModels(models: Model[]): Model[] {
|
||||
return models.sort((a, b) => {
|
||||
const nameA: string = a.name.toLowerCase();
|
||||
const nameB: string = b.name.toLowerCase();
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
return nameA.localeCompare(nameB, 'en');
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@ import { Service } from '../client/interfaces/Service';
|
||||
|
||||
export function getSortedServices(services: Service[]): Service[] {
|
||||
return services.sort((a, b) => {
|
||||
const nameA: string = a.name.toLowerCase();
|
||||
const nameB: string = b.name.toLowerCase();
|
||||
const nameA = a.name.toLowerCase();
|
||||
const nameB = b.name.toLowerCase();
|
||||
return nameA.localeCompare(nameB, 'en');
|
||||
});
|
||||
}
|
||||
|
||||
@ -7,9 +7,11 @@ import * as handlebars from 'handlebars';
|
||||
*/
|
||||
export function readHandlebarsTemplate(filePath: string): handlebars.TemplateDelegate {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const template: string = fs.readFileSync(filePath, 'utf8').toString();
|
||||
const template = fs.readFileSync(filePath, 'utf8').toString();
|
||||
try {
|
||||
return handlebars.compile(template);
|
||||
return handlebars.compile(template, {
|
||||
strict: true,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`Could not compile Handlebar template: "${filePath}"`);
|
||||
}
|
||||
|
||||
@ -15,9 +15,9 @@ export interface Templates {
|
||||
* @param language The language we need to generate (Typescript or Javascript).
|
||||
*/
|
||||
export function readHandlebarsTemplates(language: Language): Templates {
|
||||
const pathTemplateIndex: string = path.resolve(__dirname, `../../src/templates/${language}/index.hbs`);
|
||||
const pathTemplateModel: string = path.resolve(__dirname, `../../src/templates/${language}/model.hbs`);
|
||||
const pathTemplateService: string = path.resolve(__dirname, `../../src/templates/${language}/service.hbs`);
|
||||
const pathTemplateIndex = path.resolve(__dirname, `../../src/templates/${language}/index.hbs`);
|
||||
const pathTemplateModel = path.resolve(__dirname, `../../src/templates/${language}/model.hbs`);
|
||||
const pathTemplateService = path.resolve(__dirname, `../../src/templates/${language}/service.hbs`);
|
||||
|
||||
try {
|
||||
return {
|
||||
|
||||
@ -23,9 +23,9 @@ import { cleanupModels } from './cleanupModels';
|
||||
* @param outputPath
|
||||
*/
|
||||
export function writeClient(client: Client, language: Language, templates: Templates, outputPath: string): void {
|
||||
const outputPathCore: string = path.resolve(outputPath, 'core');
|
||||
const outputPathModels: string = path.resolve(outputPath, 'models');
|
||||
const outputPathServices: string = path.resolve(outputPath, 'services');
|
||||
const outputPathCore = path.resolve(outputPath, 'core');
|
||||
const outputPathModels = path.resolve(outputPath, 'models');
|
||||
const outputPathServices = path.resolve(outputPath, 'services');
|
||||
|
||||
// Clean output directory
|
||||
try {
|
||||
@ -45,9 +45,9 @@ export function writeClient(client: Client, language: Language, templates: Templ
|
||||
}
|
||||
|
||||
// Copy all core files
|
||||
const coreFiles: string = path.resolve(__dirname, `../../src/templates/${language}/core/`);
|
||||
const coreFilesExt: string = getFileName('*', language);
|
||||
const coreFilesList: string[] = glob.sync(coreFilesExt, { cwd: coreFiles });
|
||||
const coreFiles = path.resolve(__dirname, `../../src/templates/${language}/core/`);
|
||||
const coreFilesExt = getFileName('*', language);
|
||||
const coreFilesList = glob.sync(coreFilesExt, { cwd: coreFiles });
|
||||
coreFilesList.forEach(file =>
|
||||
fs.copyFileSync(
|
||||
path.resolve(coreFiles, file), // From input path
|
||||
@ -57,7 +57,6 @@ export function writeClient(client: Client, language: Language, templates: Templ
|
||||
|
||||
// Write the client files
|
||||
try {
|
||||
// TODO: Cleanup models
|
||||
writeClientIndex(client, language, templates.index, outputPath);
|
||||
writeClientModels(getSortedModels(cleanupModels(client.models)), language, templates.model, outputPathModels);
|
||||
writeClientServices(getSortedServices(cleanupServices(client.services)), language, templates.service, outputPathServices);
|
||||
|
||||
@ -17,7 +17,7 @@ import { getFileName } from './getFileName';
|
||||
* @param outputPath:
|
||||
*/
|
||||
export function writeClientIndex(client: Client, language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
|
||||
const fileName: string = getFileName('index', language);
|
||||
const fileName = getFileName('index', language);
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
path.resolve(outputPath, fileName),
|
||||
|
||||
@ -20,9 +20,9 @@ describe('writeClientModels', () => {
|
||||
template: null,
|
||||
validation: null,
|
||||
description: null,
|
||||
extends: null,
|
||||
extends: [],
|
||||
imports: [],
|
||||
values: [],
|
||||
symbols: [],
|
||||
properties: [],
|
||||
enums: [],
|
||||
},
|
||||
|
||||
@ -14,7 +14,7 @@ import { getFileName } from './getFileName';
|
||||
*/
|
||||
export function writeClientModels(models: Model[], language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
|
||||
models.forEach(model => {
|
||||
const fileName: string = getFileName(model.name, language);
|
||||
const fileName = getFileName(model.name, language);
|
||||
try {
|
||||
fs.writeFileSync(path.resolve(outputPath, fileName), template(model));
|
||||
} catch (e) {
|
||||
|
||||
@ -14,7 +14,7 @@ import { getFileName } from './getFileName';
|
||||
*/
|
||||
export function writeClientServices(services: Service[], language: Language, template: handlebars.TemplateDelegate, outputPath: string): void {
|
||||
services.forEach(service => {
|
||||
const fileName: string = getFileName(service.name, language);
|
||||
const fileName = getFileName(service.name, language);
|
||||
try {
|
||||
fs.writeFileSync(path.resolve(outputPath, fileName), template(service));
|
||||
} catch (e) {
|
||||
|
||||
@ -24,6 +24,10 @@
|
||||
"description": "This is a simple string",
|
||||
"type": "string"
|
||||
},
|
||||
"SimpleFile": {
|
||||
"description": "This is a simple file",
|
||||
"type": "File"
|
||||
},
|
||||
"SimpleEnumWithStrings": {
|
||||
"description": "This is a simple enum with strings",
|
||||
"enum": [
|
||||
@ -76,6 +80,7 @@
|
||||
"description": "This is a simple array with a properties",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"foo": {
|
||||
"type": "string"
|
||||
@ -86,6 +91,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SimpleReference": {
|
||||
"description": "This is a simple model reference",
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
"ModelWithInteger": {
|
||||
"description": "This is a model with one number property",
|
||||
"type": "object",
|
||||
@ -107,8 +116,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithString": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one string property",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"description": "This is a simple string property",
|
||||
@ -117,8 +126,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithEnum": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one enum",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"description": "This is a simple enum with strings",
|
||||
@ -131,8 +140,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithEnumFromDescription": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one enum",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"type": "integer",
|
||||
@ -141,8 +150,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithReference": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one property containing a reference",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
@ -150,8 +159,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithCircularReference": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one property containing a circular reference",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"$ref": "#/definitions/ModelWithCircularReference"
|
||||
@ -159,8 +168,8 @@
|
||||
}
|
||||
},
|
||||
"ModelWithNestedProperties": {
|
||||
"type": "object",
|
||||
"description": "This is a model with one nested property",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"first": {
|
||||
"type": "object",
|
||||
@ -176,6 +185,74 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModelWithDuplicateProperties": {
|
||||
"description": "This is a model with duplicated properties",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prop": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
"prop": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
"prop": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModelWithDuplicateImports": {
|
||||
"description": "This is a model with duplicated imports",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propA": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
"propB": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
"propC": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModelThatExtends": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propExtendsA": {
|
||||
"type": "string"
|
||||
},
|
||||
"propExtendsB": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ModelThatExtendsExtends": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ModelThatExtends"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propExtendsC": {
|
||||
"type": "string"
|
||||
},
|
||||
"propExtendsD": {
|
||||
"$ref": "#/definitions/ModelWithString"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user