Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: refactored input processors #767

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/processors/AbstractInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>, options?: ProcessorOptions): Promise<CommonInputModel>;
abstract process(input: Record<string, any>, options?: ProcessorOptions): Promise<InputMetaModel>;
abstract shouldProcess(input: Record<string, any>): boolean;
}
22 changes: 14 additions & 8 deletions src/processors/AsyncAPIInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,25 +17,30 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
async process(input: Record<string, any>, options?: ProcessorOptions): Promise<CommonInputModel> {
async process(input: Record<string, any>, options?: ProcessorOptions): Promise<InputMetaModel> {
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;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/processors/InputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -45,7 +45,7 @@ export class InputProcessor {
* @param input to process
* @param options passed to the processors
*/
process(input: Record<string, any>, options?: ProcessorOptions): Promise<CommonInputModel> {
process(input: Record<string, any>, options?: ProcessorOptions): Promise<InputMetaModel> {
for (const [type, processor] of this.processors) {
if (type === 'default') {continue;}
if (processor.shouldProcess(input)) {
Expand Down
44 changes: 27 additions & 17 deletions src/processors/JsonSchemaInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,7 +16,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
process(input: Record<string, any>): Promise<CommonInputModel> {
process(input: Record<string, any>): Promise<InputMetaModel> {
if (this.shouldProcess(input)) {
switch (input.$schema) {
case 'http://json-schema.org/draft-04/schema':
Expand Down Expand Up @@ -58,50 +59,59 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor {
*
* @param input to process as draft 7
*/
private async processDraft7(input: Record<string, any>) : Promise<CommonInputModel> {
private async processDraft7(input: Record<string, any>) : Promise<InputMetaModel> {
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<string, any>;
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;
}

/**
* Process a draft-4 schema
*
* @param input to process as draft 4
*/
private async processDraft4(input: Record<string, any>) : Promise<CommonInputModel> {
private async processDraft4(input: Record<string, any>) : Promise<InputMetaModel> {
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<string, any>;
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;
}

/**
* Process a draft-6 schema
*
* @param input to process as draft-6
*/
private async processDraft6(input: Record<string, any>) : Promise<CommonInputModel> {
private async processDraft6(input: Record<string, any>) : Promise<InputMetaModel> {
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<string, any>;
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<string, any>) {
Expand Down
21 changes: 12 additions & 9 deletions src/processors/OpenAPIInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,11 +17,11 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
async process(input: Record<string, any>): Promise<CommonInputModel> {
async process(input: Record<string, any>): Promise<InputMetaModel> {
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;

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

Expand All @@ -68,15 +69,15 @@ 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(/\//, '_');
this.iterateMediaType((response as OpenAPIV3.ResponseObject).content || {}, `${path}_${formattedResponseName}`, inputModel);
}
}

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

/**
Expand Down
29 changes: 17 additions & 12 deletions src/processors/SwaggerInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,11 +17,11 @@ export class SwaggerInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
async process(input: Record<string, any>): Promise<CommonInputModel> {
async process(input: Record<string, any>): Promise<InputMetaModel> {
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.
Expand All @@ -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.
Expand Down
19 changes: 12 additions & 7 deletions src/processors/TypeScriptInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -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*/

Expand Down Expand Up @@ -67,25 +68,29 @@ export class TypeScriptInputProcessor extends AbstractInputProcessor {
return true;
}

process(input: Record<string, any>, options?: ProcessorOptions): Promise<CommonInputModel> {
const common = new CommonInputModel();
process(input: Record<string, any>, options?: ProcessorOptions): Promise<InputMetaModel> {
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<string, any>);
common.models = {...common.models, ...commonModels };
const newCommonModels = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema as Record<string, any>);
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);
}
}