diff --git a/src/processors/AbstractInputProcessor.ts b/src/processors/AbstractInputProcessor.ts index 27031a2fa6..6bf0da6cb2 100644 --- a/src/processors/AbstractInputProcessor.ts +++ b/src/processors/AbstractInputProcessor.ts @@ -1,6 +1,7 @@ -import { ProcessorOptions, CommonInputModel } from '../models'; +import { ProcessorOptions, InputMetaModel } from '../models'; + export abstract class AbstractInputProcessor { public static MODELGEN_INFFERED_NAME = 'x-modelgen-inferred-name'; - abstract process(input: Record, options?: ProcessorOptions): Promise; + abstract process(input: Record, options?: ProcessorOptions): Promise; abstract shouldProcess(input: Record): boolean; } diff --git a/src/processors/AsyncAPIInputProcessor.ts b/src/processors/AsyncAPIInputProcessor.ts index b556918922..bf44cf0a70 100644 --- a/src/processors/AsyncAPIInputProcessor.ts +++ b/src/processors/AsyncAPIInputProcessor.ts @@ -1,9 +1,10 @@ import {parse, AsyncAPIDocument, Schema as AsyncAPISchema, ParserOptions} from '@asyncapi/parser'; import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, ProcessorOptions } from '../models'; +import { CommonModel, InputMetaModel, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import { AsyncapiV2Schema } from '../models/AsyncapiV2Schema'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing AsyncAPI inputs @@ -16,25 +17,30 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { * * @param input */ - async process(input: Record, options?: ProcessorOptions): Promise { + async process(input: Record, options?: ProcessorOptions): Promise { if (!this.shouldProcess(input)) {throw new Error('Input is not an AsyncAPI document so it cannot be processed.');} Logger.debug('Processing input as an AsyncAPI document'); let doc: AsyncAPIDocument; - const common = new CommonInputModel(); + const inputModel = new InputMetaModel(); if (!AsyncAPIInputProcessor.isFromParser(input)) { doc = await parse(input as any, options?.asyncapi || {} as ParserOptions); } else { doc = input as AsyncAPIDocument; } - common.originalInput = doc; - + inputModel.originalInput = doc; + + //Intermediate model before meta model + let commonModels: {[key: string]: CommonModel} = {}; for (const [, message] of doc.allMessages()) { const schema = AsyncAPIInputProcessor.convertToInternalSchema(message.payload()); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema); - common.models = {...common.models, ...commonModels}; + const newCommonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema); + commonModels = {...commonModels, ...newCommonModels}; + } + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); } - return common; + return inputModel; } /** diff --git a/src/processors/InputProcessor.ts b/src/processors/InputProcessor.ts index a4dd57cfec..e41e98b350 100644 --- a/src/processors/InputProcessor.ts +++ b/src/processors/InputProcessor.ts @@ -1,7 +1,7 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { AsyncAPIInputProcessor } from './AsyncAPIInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { ProcessorOptions, CommonInputModel } from '../models'; +import { ProcessorOptions, InputMetaModel } from '../models'; import { SwaggerInputProcessor } from './SwaggerInputProcessor'; import { OpenAPIInputProcessor } from './OpenAPIInputProcessor'; import { TypeScriptInputProcessor } from './TypeScriptInputProcessor'; @@ -45,7 +45,7 @@ export class InputProcessor { * @param input to process * @param options passed to the processors */ - process(input: Record, options?: ProcessorOptions): Promise { + process(input: Record, options?: ProcessorOptions): Promise { for (const [type, processor] of this.processors) { if (type === 'default') {continue;} if (processor.shouldProcess(input)) { diff --git a/src/processors/JsonSchemaInputProcessor.ts b/src/processors/JsonSchemaInputProcessor.ts index 66ee53229c..b452fe616f 100644 --- a/src/processors/JsonSchemaInputProcessor.ts +++ b/src/processors/JsonSchemaInputProcessor.ts @@ -1,10 +1,11 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import $RefParser from '@apidevtools/json-schema-ref-parser'; import path from 'path'; -import { CommonModel, CommonInputModel, Draft4Schema, Draft7Schema, Draft6Schema, SwaggerV2Schema, OpenapiV3Schema, AsyncapiV2Schema } from '../models'; +import { CommonModel, InputMetaModel, Draft4Schema, Draft7Schema, Draft6Schema, SwaggerV2Schema, OpenapiV3Schema, AsyncapiV2Schema } from '../models'; import { Logger } from '../utils'; import { postInterpretModel } from '../interpreter/PostInterpreter'; import { Interpreter } from '../interpreter/Interpreter'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing JSON Schema @@ -15,7 +16,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { * * @param input */ - process(input: Record): Promise { + process(input: Record): Promise { if (this.shouldProcess(input)) { switch (input.$schema) { case 'http://json-schema.org/draft-04/schema': @@ -58,16 +59,19 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { * * @param input to process as draft 7 */ - private async processDraft7(input: Record) : Promise { + private async processDraft7(input: Record) : Promise { Logger.debug('Processing input as a JSON Schema Draft 7 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; await this.dereferenceInputs(input); const parsedSchema = Draft7Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); + } Logger.debug('Completed processing input as JSON Schema draft 7 document'); - return commonInputModel; + return inputModel; } /** @@ -75,16 +79,19 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { * * @param input to process as draft 4 */ - private async processDraft4(input: Record) : Promise { + private async processDraft4(input: Record) : Promise { Logger.debug('Processing input as JSON Schema Draft 4 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; await this.dereferenceInputs(input); const parsedSchema = Draft4Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); + } Logger.debug('Completed processing input as JSON Schema draft 4 document'); - return commonInputModel; + return inputModel; } /** @@ -92,16 +99,19 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { * * @param input to process as draft-6 */ - private async processDraft6(input: Record) : Promise { + private async processDraft6(input: Record) : Promise { Logger.debug('Processing input as a JSON Schema Draft 6 document'); - const commonInputModel = new CommonInputModel(); - commonInputModel.originalInput = input; + const inputModel = new InputMetaModel(); + inputModel.originalInput = input; input = JsonSchemaInputProcessor.reflectSchemaNames(input, {}, 'root', true) as Record; await this.dereferenceInputs(input); const parsedSchema = Draft6Schema.toSchema(input); - commonInputModel.models = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(parsedSchema); + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); + } Logger.debug('Completed processing input as JSON Schema draft 6 document'); - return commonInputModel; + return inputModel; } private async dereferenceInputs(input: Record) { diff --git a/src/processors/OpenAPIInputProcessor.ts b/src/processors/OpenAPIInputProcessor.ts index 59e4274a40..31b6129c35 100644 --- a/src/processors/OpenAPIInputProcessor.ts +++ b/src/processors/OpenAPIInputProcessor.ts @@ -1,9 +1,10 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, OpenapiV3Schema } from '../models'; +import { InputMetaModel, OpenapiV3Schema } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV3 } from 'openapi-types'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing OpenAPI V3.0 inputs @@ -16,11 +17,11 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { * * @param input */ - async process(input: Record): Promise { + async process(input: Record): Promise { if (!this.shouldProcess(input)) {throw new Error('Input is not a OpenAPI document so it cannot be processed.');} Logger.debug('Processing input as an OpenAPI document'); - const inputModel = new CommonInputModel(); + const inputModel = new InputMetaModel(); inputModel.originalInput = input; const api = (await SwaggerParser.dereference(input as any) as unknown) as OpenAPIV3.Document; @@ -30,7 +31,7 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { return inputModel; } - private processPath(pathObject: OpenAPIV3.PathItemObject | undefined, path: string, inputModel: CommonInputModel) { + private processPath(pathObject: OpenAPIV3.PathItemObject | undefined, path: string, inputModel: InputMetaModel) { if (pathObject) { //Remove all special chars from path let formattedPathName = path.replace(/[^\w\s*]+/g, ''); @@ -49,7 +50,7 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { } } - private processOperation(operation: OpenAPIV3.OperationObject | undefined, path: string, inputModel: CommonInputModel) { + private processOperation(operation: OpenAPIV3.OperationObject | undefined, path: string, inputModel: InputMetaModel) { if (operation) { this.iterateResponses(operation.responses, path, inputModel); @@ -68,7 +69,7 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { } } - private iterateResponses(responses: OpenAPIV3.ResponsesObject, path: string, inputModel: CommonInputModel) { + private iterateResponses(responses: OpenAPIV3.ResponsesObject, path: string, inputModel: InputMetaModel) { for (const [responseName, response] of Object.entries(responses)) { //Replace any '/' with '_' const formattedResponseName = responseName.replace(/\//, '_'); @@ -76,7 +77,7 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { } } - private iterateMediaType(mediaTypes: {[media: string]: OpenAPIV3.MediaTypeObject}, path: string, inputModel: CommonInputModel) { + private iterateMediaType(mediaTypes: {[media: string]: OpenAPIV3.MediaTypeObject}, path: string, inputModel: InputMetaModel) { for (const [mediaContent, mediaTypeObject] of Object.entries(mediaTypes)) { const mediaType = mediaTypeObject; if (mediaType.schema === undefined) { continue; } @@ -87,10 +88,12 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { } } - private includeSchema(schema: OpenAPIV3.SchemaObject, name: string, inputModel: CommonInputModel) { + private includeSchema(schema: OpenAPIV3.SchemaObject, name: string, inputModel: InputMetaModel) { const internalSchema = OpenAPIInputProcessor.convertToInternalSchema(schema, name); const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(internalSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); + } } /** diff --git a/src/processors/SwaggerInputProcessor.ts b/src/processors/SwaggerInputProcessor.ts index 91b7faa664..e593b78e30 100644 --- a/src/processors/SwaggerInputProcessor.ts +++ b/src/processors/SwaggerInputProcessor.ts @@ -1,9 +1,10 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { CommonInputModel, SwaggerV2Schema } from '../models'; +import { InputMetaModel, SwaggerV2Schema } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV2 } from 'openapi-types'; +import { convertToMetaModel } from '../helpers'; /** * Class for processing Swagger inputs @@ -16,11 +17,11 @@ export class SwaggerInputProcessor extends AbstractInputProcessor { * * @param input */ - async process(input: Record): Promise { + async process(input: Record): Promise { if (!this.shouldProcess(input)) {throw new Error('Input is not a Swagger document so it cannot be processed.');} Logger.debug('Processing input as a Swagger document'); - const common = new CommonInputModel(); + const common = new InputMetaModel(); common.originalInput = input; //Since we require that all references have been dereferenced, we cannot "simply" support already parsed inputs. @@ -42,37 +43,41 @@ export class SwaggerInputProcessor extends AbstractInputProcessor { return common; } - private processOperation(operation: OpenAPIV2.OperationObject | undefined, path: string, inputModel: CommonInputModel) { + private processOperation(operation: OpenAPIV2.OperationObject | undefined, path: string, inputModel: InputMetaModel) { if (operation) { this.includeResponses(operation.responses, path, inputModel); this.includeParameters(operation.parameters, path, inputModel); } } - private includeResponses(responses: OpenAPIV2.ResponsesObject, path: string, inputModel: CommonInputModel) { + private includeResponses(responses: OpenAPIV2.ResponsesObject, path: string, inputModel: InputMetaModel) { for (const [responseName, response] of Object.entries(responses)) { if (response !== undefined) { const getOperationResponseSchema = (response as OpenAPIV2.ResponseObject).schema; if (getOperationResponseSchema !== undefined) { - const swaggerSchema = SwaggerInputProcessor.convertToInternalSchema(getOperationResponseSchema, `${path}_${responseName}`); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(swaggerSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + this.includeSchema(getOperationResponseSchema, `${path}_${responseName}`, inputModel); } } } } - private includeParameters(parameters: OpenAPIV2.Parameters | undefined, path: string, inputModel: CommonInputModel) { + private includeParameters(parameters: OpenAPIV2.Parameters | undefined, path: string, inputModel: InputMetaModel) { for (const parameterObject of parameters || []) { const parameter = parameterObject as OpenAPIV2.Parameter; if (parameter.in === 'body') { const bodyParameterSchema = parameter.schema; - const swaggerSchema = SwaggerInputProcessor.convertToInternalSchema(bodyParameterSchema, `${path}_body`); - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(swaggerSchema); - inputModel.models = {...inputModel.models, ...commonModels}; + this.includeSchema(bodyParameterSchema, `${path}_body`, inputModel); } } } + + private includeSchema(schema: OpenAPIV2.SchemaObject, name: string, inputModel: InputMetaModel) { + const internalSchema = SwaggerInputProcessor.convertToInternalSchema(schema, name); + const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(internalSchema); + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); + } + } /** * Converts a Swagger 2.0 Schema to the internal schema format. diff --git a/src/processors/TypeScriptInputProcessor.ts b/src/processors/TypeScriptInputProcessor.ts index c85d2e1c91..eafeb2d329 100644 --- a/src/processors/TypeScriptInputProcessor.ts +++ b/src/processors/TypeScriptInputProcessor.ts @@ -1,9 +1,10 @@ -import { CommonInputModel, ProcessorOptions } from '../models'; +import { CommonModel, InputMetaModel, ProcessorOptions } from '../models'; import { resolve } from 'path'; import ts from 'typescript'; import * as TJS from 'typescript-json-schema'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; import { AbstractInputProcessor } from './AbstractInputProcessor'; +import { convertToMetaModel } from '../helpers'; /** Class for processing Typescript code inputs to Common module*/ @@ -67,25 +68,29 @@ export class TypeScriptInputProcessor extends AbstractInputProcessor { return true; } - process(input: Record, options?: ProcessorOptions): Promise { - const common = new CommonInputModel(); + process(input: Record, options?: ProcessorOptions): Promise { + const inputModel = new InputMetaModel(); if (!this.shouldProcess(input)) { return Promise.reject(new Error('Input is not of the valid file format')); } const { fileContents, baseFile } = input; - common.originalInput = fileContents; + inputModel.originalInput = fileContents; // obtain generated schema const generatedSchemas = this.generateJSONSchema(baseFile, '*', options?.typescript); if (generatedSchemas) { + let commonModels: {[key: string]: CommonModel} = {}; for (const schema of generatedSchemas) { - const commonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema as Record); - common.models = {...common.models, ...commonModels }; + const newCommonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema as Record); + commonModels = {...commonModels, ...newCommonModels }; + } + for (const [key, commonModel] of Object.entries(commonModels)) { + inputModel.models[String(key)] = convertToMetaModel(commonModel); } } - return Promise.resolve(common); + return Promise.resolve(inputModel); } }