Skip to content

Commit

Permalink
feat: integrate new ParserJS version (#925)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Oct 7, 2022
1 parent 4376516 commit f24bd1d
Show file tree
Hide file tree
Showing 10 changed files with 2,063 additions and 660 deletions.
7 changes: 4 additions & 3 deletions examples/asyncapi-from-parser/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { parse } from '@asyncapi/parser';
import { Parser } from '@asyncapi/parser';
import { TypeScriptGenerator } from '../../src';

const generator = new TypeScriptGenerator();
Expand Down Expand Up @@ -30,8 +30,9 @@ const AsyncAPIDocument = {
};

export async function generate() : Promise<void> {
const parsedDoc = await parse(JSON.stringify(AsyncAPIDocument));
const models = await generator.generate(parsedDoc as any);
const parser = new Parser();
const { document } = await parser.parse(JSON.stringify(AsyncAPIDocument));
const models = await generator.generate(document as any);
for (const model of models) {
console.log(model.result);
}
Expand Down
6 changes: 6 additions & 0 deletions examples/integrate-with-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,11 @@
"test:watch": "react-scripts test --watchAll=false",
"eject": "react-scripts eject",
"test:debug": "react-scripts --inspect-brk test --runInBand --no-cache"
},
"jest": {
"moduleNameMapper": {
"^nimma/legacy$": "<rootDir>/../../node_modules/nimma/dist/legacy/cjs/index.js",
"^nimma/(.*)": "<rootDir>/../../node_modules/nimma/dist/cjs/$1"
}
}
}
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ module.exports = {
collectCoverageFrom: [
'src/**'
],
moduleNameMapper: {
'^nimma/legacy$': '<rootDir>/node_modules/nimma/dist/legacy/cjs/index.js',
'^nimma/(.*)': '<rootDir>/node_modules/nimma/dist/cjs/$1',
},
modulePathIgnorePatterns: [
'<rootDir>/examples/TEMPLATE',
'<rootDir>/test/generators/template'
Expand Down
2,368 changes: 1,805 additions & 563 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.9",
"@apidevtools/swagger-parser": "^10.0.3",
"@asyncapi/parser": "^1.17.0",
"@asyncapi/parser": "^2.0.0-next-major.5",
"alterschema": "^1.1.1",
"change-case": "^4.1.2",
"openapi-types": "9.3.0",
Expand Down
4 changes: 2 additions & 2 deletions src/models/ProcessorOptions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ParserOptions } from '@asyncapi/parser';
import { ParseOptions } from '@asyncapi/parser';
import { TypeScriptInputProcessorOptions } from '../processors/index';

export interface ProcessorOptions {
asyncapi?: ParserOptions,
asyncapi?: ParseOptions,
typescript?: TypeScriptInputProcessorOptions,
}
121 changes: 75 additions & 46 deletions src/processors/AsyncAPIInputProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,62 @@
import {parse, AsyncAPIDocument, Schema as AsyncAPISchema, ParserOptions} from '@asyncapi/parser';
import { Parser } from '@asyncapi/parser';
import { AbstractInputProcessor } from './AbstractInputProcessor';
import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor';
import { InputMetaModel, ProcessorOptions } from '../models';
import { Logger } from '../utils';
import { AsyncapiV2Schema } from '../models/AsyncapiV2Schema';
import { convertToMetaModel } from '../helpers';

import type { AsyncAPIDocumentInterface, SchemaInterface as AsyncAPISchema } from '@asyncapi/parser';

/**
* Class for processing AsyncAPI inputs
*/
export class AsyncAPIInputProcessor extends AbstractInputProcessor {
private parser = new Parser();

static supportedVersions = ['2.0.0', '2.1.0', '2.2.0', '2.3.0', '2.4.0', '2.5.0'];

/**
* Process the input as an AsyncAPI document
*
* @param input
*/
async process(input: Record<string, any>, options?: ProcessorOptions): Promise<InputMetaModel> {
// eslint-disable-next-line sonarjs/cognitive-complexity
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;
let doc: AsyncAPIDocumentInterface;
const inputModel = new InputMetaModel();
if (!AsyncAPIInputProcessor.isFromParser(input)) {
doc = await parse(input as any, options?.asyncapi || {} as ParserOptions);
const { document, diagnostics } = await this.parser.parse(input as any, options?.asyncapi || {});
if (document) {
doc = document;
} else {
const err = new Error('Input is not an correct AsyncAPI document so it cannot be processed.');
(err as any).diagnostics = diagnostics;
throw err;
}
} else {
doc = input as AsyncAPIDocument;
doc = input as AsyncAPIDocumentInterface;
}

inputModel.originalInput = doc;
// Go over all the message payloads and convert them to models
for (const [, message] of doc.allMessages()) {
const schema = AsyncAPIInputProcessor.convertToInternalSchema(message.payload());
const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema);
if (newCommonModel.$id !== undefined) {
if (inputModel.models[newCommonModel.$id] !== undefined) {
Logger.warn(`Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, newCommonModel);
for (const message of doc.allMessages()) {
const payload = message.payload();
if (payload) {
const schema = AsyncAPIInputProcessor.convertToInternalSchema(payload);
const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema);
if (newCommonModel.$id !== undefined) {
if (inputModel.models[newCommonModel.$id] !== undefined) {
Logger.warn(`Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, newCommonModel);
}
const metaModel = convertToMetaModel(newCommonModel);
inputModel.models[metaModel.name] = metaModel;
} else {
Logger.warn('Model did not have $id which is required, ignoring.', newCommonModel);
}
const metaModel = convertToMetaModel(newCommonModel);
inputModel.models[metaModel.name] = metaModel;
} else {
Logger.warn('Model did not have $id which is required, ignoring.', newCommonModel);
}
}
return inputModel;
Expand All @@ -54,14 +70,15 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
*
* @param schema to reflect name for
*/
/* eslint-disable @typescript-eslint/no-non-null-assertion */
// eslint-disable-next-line sonarjs/cognitive-complexity
static convertToInternalSchema(
schema: AsyncAPISchema | boolean,
alreadyIteratedSchemas: Map<string, AsyncapiV2Schema> = new Map()
): AsyncapiV2Schema | boolean {
if (typeof schema === 'boolean') {return schema;}

let schemaUid = schema.uid();
let schemaUid = schema.id();
//Because the constraint functionality of generators cannot handle -, <, >, we remove them from the id if it's an anonymous schema.
if (schemaUid.includes('<anonymous-schema')) {
schemaUid = schemaUid.replace('<', '').replace(/-/g, '_').replace('>', '');
Expand All @@ -75,63 +92,66 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
convertedSchema[this.MODELGEN_INFFERED_NAME] = schemaUid;
alreadyIteratedSchemas.set(schemaUid, convertedSchema);

if (schema.allOf() !== null) {
convertedSchema.allOf = schema.allOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
if (schema.allOf()) {
convertedSchema.allOf = schema.allOf()!.map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
}
if (schema.oneOf() !== null) {
convertedSchema.oneOf = schema.oneOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
if (schema.oneOf()) {
convertedSchema.oneOf = schema.oneOf()!.map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
}
if (schema.anyOf() !== null) {
convertedSchema.anyOf = schema.anyOf().map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
if (schema.anyOf()) {
convertedSchema.anyOf = schema.anyOf()!.map((item) => this.convertToInternalSchema(item, alreadyIteratedSchemas));
}
if (schema.not() !== null) {
convertedSchema.not = this.convertToInternalSchema(schema.not(), alreadyIteratedSchemas);
if (schema.not()) {
convertedSchema.not = this.convertToInternalSchema(schema.not()!, alreadyIteratedSchemas);
}
if (
typeof schema.additionalItems() === 'object' &&
schema.additionalItems() !== null
) {
convertedSchema.additionalItems = this.convertToInternalSchema(schema.additionalItems(), alreadyIteratedSchemas);
}
if (schema.contains() !== null) {
convertedSchema.contains = this.convertToInternalSchema(schema.contains(), alreadyIteratedSchemas);
if (schema.contains()) {
convertedSchema.contains = this.convertToInternalSchema(schema.contains()!, alreadyIteratedSchemas);
}
if (schema.propertyNames() !== null) {
convertedSchema.propertyNames = this.convertToInternalSchema(schema.propertyNames(), alreadyIteratedSchemas);
if (schema.propertyNames()) {
convertedSchema.propertyNames = this.convertToInternalSchema(schema.propertyNames()!, alreadyIteratedSchemas);
}
if (schema.if() !== null) {
convertedSchema.if = this.convertToInternalSchema(schema.if(), alreadyIteratedSchemas);
if (schema.if()) {
convertedSchema.if = this.convertToInternalSchema(schema.if()!, alreadyIteratedSchemas);
}
if (schema.then() !== null) {
convertedSchema.then = this.convertToInternalSchema(schema.then(), alreadyIteratedSchemas);
if (schema.then()) {
convertedSchema.then = this.convertToInternalSchema(schema.then()!, alreadyIteratedSchemas);
}
if (schema.else() !== null) {
convertedSchema.else = this.convertToInternalSchema(schema.else(), alreadyIteratedSchemas);
if (schema.else()) {
convertedSchema.else = this.convertToInternalSchema(schema.else()!, alreadyIteratedSchemas);
}
if (
typeof schema.additionalProperties() === 'object' &&
schema.additionalProperties() !== null
) {
convertedSchema.additionalProperties = this.convertToInternalSchema(schema.additionalProperties(), alreadyIteratedSchemas);
}
if (schema.items() !== null) {
if (schema.items()) {
if (Array.isArray(schema.items())) {
convertedSchema.items = (schema.items() as AsyncAPISchema[]).map((item) => this.convertToInternalSchema(item), alreadyIteratedSchemas);
} else {
convertedSchema.items = this.convertToInternalSchema(schema.items() as AsyncAPISchema, alreadyIteratedSchemas);
}
}

if (schema.properties() !== null && Object.keys(schema.properties()).length) {
const schemaProperties = schema.properties();
if (schemaProperties && Object.keys(schemaProperties).length) {
const properties : {[key: string]: AsyncapiV2Schema | boolean} = {};
for (const [propertyName, propertySchema] of Object.entries(schema.properties())) {
for (const [propertyName, propertySchema] of Object.entries(schemaProperties)) {
properties[String(propertyName)] = this.convertToInternalSchema(propertySchema, alreadyIteratedSchemas);
}
convertedSchema.properties = properties;
}
if (schema.dependencies() !== null && Object.keys(schema.dependencies()).length) {

const schemaDependencies = schema.dependencies();
if (schemaDependencies && Object.keys(schemaDependencies).length) {
const dependencies: { [key: string]: AsyncapiV2Schema | boolean | string[] } = {};
for (const [dependencyName, dependency] of Object.entries(schema.dependencies())) {
for (const [dependencyName, dependency] of Object.entries(schemaDependencies)) {
if (typeof dependency === 'object' && !Array.isArray(dependency)) {
dependencies[String(dependencyName)] = this.convertToInternalSchema(dependency, alreadyIteratedSchemas);
} else {
Expand All @@ -140,29 +160,36 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
}
convertedSchema.dependencies = dependencies;
}
if (schema.patternProperties() !== null && Object.keys(schema.patternProperties()).length) {

const schemaPatternProperties = schema.patternProperties();
if (schemaPatternProperties && Object.keys(schemaPatternProperties).length) {
const patternProperties: { [key: string]: AsyncapiV2Schema | boolean } = {};
for (const [patternPropertyName, patternProperty] of Object.entries(schema.patternProperties())) {
for (const [patternPropertyName, patternProperty] of Object.entries(schemaPatternProperties)) {
patternProperties[String(patternPropertyName)] = this.convertToInternalSchema(patternProperty, alreadyIteratedSchemas);
}
convertedSchema.patternProperties = patternProperties;
}
if (schema.definitions() !== null && Object.keys(schema.definitions()).length) {

const schemaDefinitions = schema.definitions();
if (schemaDefinitions && Object.keys(schemaDefinitions).length) {
const definitions: { [key: string]: AsyncapiV2Schema | boolean } = {};
for (const [definitionName, definition] of Object.entries(schema.definitions())) {
for (const [definitionName, definition] of Object.entries(schemaDefinitions)) {
definitions[String(definitionName)] = this.convertToInternalSchema(definition, alreadyIteratedSchemas);
}
convertedSchema.definitions = definitions;
}

return convertedSchema;
}
/* eslint-enable @typescript-eslint/no-non-null-assertion */

/**
* Figures out if an object is of type AsyncAPI document
*
* @param input
*/
shouldProcess(input: Record<string, any>) : boolean {
shouldProcess(input?: Record<string, any>) : boolean {
if (!input) {return false;}
const version = this.tryGetVersionOfDocument(input);
if (!version) {return false;}
return AsyncAPIInputProcessor.supportedVersions.includes(version);
Expand All @@ -173,7 +200,8 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
tryGetVersionOfDocument(input: Record<string, any>) : string | undefined {
tryGetVersionOfDocument(input?: Record<string, any>) : string | undefined {
if (!input) {return;}
if (AsyncAPIInputProcessor.isFromParser(input)) {
return input.version();
}
Expand All @@ -185,7 +213,8 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor {
*
* @param input
*/
static isFromParser(input: Record<string, any>): boolean {
static isFromParser(input?: Record<string, any>): boolean {
if (!input) {return false;}
if (input['_json'] !== undefined && input['_json'].asyncapi !== undefined &&
typeof input.version === 'function') {
return true;
Expand Down
Loading

0 comments on commit f24bd1d

Please sign in to comment.