Skip to content

Commit

Permalink
refactor: the parsing process (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
KhudaDad414 authored Mar 18, 2022
1 parent 5369a57 commit 85087e4
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 105 deletions.
53 changes: 35 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"dependencies": {
"@asyncapi/parser": "^1.11.1",
"js-yaml": "^4.1.0",
"jsonpath-plus": "^6.0.1",
"lodash": "^4.17.21",
"merge-deep": "^3.0.3"
}
Expand Down
122 changes: 44 additions & 78 deletions src/ComponentProvider.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,60 @@
import type { AsyncAPIDocument, ChannelParameter, Message, MessageTrait, Schema } from '@asyncapi/parser';

import type {
AsyncAPIDocument,
ChannelParameter,
Message,
Schema,
} from '@asyncapi/parser';

import { JSONPath } from 'jsonpath-plus';
import _ from 'lodash';
/**
* This class will provide all sorts of data for optimizers.
*
* @private
*/
export class ComponentProvider {
messagePaths = ['$.channels.*.*.message[?(@property !== "oneOf")]^', '$.channels.*.*.message.oneOf.*', '$.components.messages.*'];
schemaPaths = [
'$.channels.*.*.message.traits[*]..[?(@.type)]',
'$.channels.*.*.message.headers',
'$.channels.*.*.message.headers..[?(@.type)]',
'$.channels.*.*.message.payload',
'$.channels.*.*.message.payload..[?(@.type)]',
'$.channels.*.parameters.*.schema[?(@.type)]',
'$.channels.*.parameters.*.schema..[?(@.type)]',
'$.components.schemas..[?(@.type)]',
];

parameterPaths = ['$.channels.*.parameters.*', '$.components.parameters.*'];
messages = new Map<string, Message>();
schemas = new Map<string, Schema>();
parameters = new Map<string, ChannelParameter>();

constructor(private document: AsyncAPIDocument) {
this.scanChannels();
this.scanComponents();
}

private scanChannels(): void {
const channels = this.document.channels();
for (const channelName in channels) {
const path = `channels.${channelName}`;
const channel = this.document.channel(channelName);

if (channel.hasPublish()) {
const message = channel.publish().message();
const currentPath = `${path}.publish.message`;
this.scanMessage(currentPath, message);
this.messages.set(currentPath, message);
}

if (channel.hasSubscribe()) {
const message = channel.subscribe().message();
const currentPath = `${path}.subscribe.message`;
this.scanMessage(currentPath, message);
this.messages.set(currentPath, message);
}

if (channel.hasParameters()) {
const currentPath = `${path}.parameters`;
for (const [name, channelParameter] of Object.entries(channel.parameters())) {
this.parameters.set(`${currentPath}.${name}`, channelParameter);
}
}
}
}

private scanSchema(path: string, schema: Schema): void {
if (!schema) {return;}
this.schemas.set(path, schema);
const schemaProperties = schema.properties();
for (const [propertyName, propertySchema] of Object.entries(schemaProperties)) {
this.schemas.set(`${path}.properties.${propertyName}`, propertySchema);
}
}

private scanMessageTraits(path: string, traits: MessageTrait[]): void {
for (const [index, trait] of Object.entries(traits)) {
if (trait.headers()) {
this.scanSchema(`${path}[${index}].headers`, trait.headers());
}
}
this.messages = this.parseComponents(this.messagePaths);
this.schemas = this.parseComponents(this.schemaPaths);
this.parameters = this.parseComponents(this.parameterPaths);
}

private scanMessage(path: string, message: Message): void {
this.scanSchema(`${path}.payload`, message.payload());
this.scanSchema(`${path}.headers`, message.headers());
this.scanMessageTraits(`${path}.traits`, message.traits());
private toLodashPath(path: string) {
return path.replace(/'\]\['/g, '.')
.slice(3, -2)
.replace(/'\]/g, '')
.replace(/\['/g, '.');
}

private scanComponents(): void {
const components = this.document.components();
if (!components) {
return;
}

if (components.hasMessages()) {
for (const [messageName, message] of Object.entries(components.messages())) {
this.messages.set(`components.messages.${messageName}`, message);
}
}

if (components.hasSchemas()) {
for (const [schemaName, schema] of Object.entries(components.schemas())) {
this.schemas.set(`components.schemas.${schemaName}`, schema);
}
}

if (components.hasParameters()) {
for (const [parameterName, parameter] of Object.entries(components.parameters())) {
this.parameters.set(`components.parameters.${parameterName}`, parameter);
}
}
private parseComponents(paths: string[]): any {
return _.chain(paths)
.map((path) => {
return JSONPath({
resultType: 'all',
json: this.document.json(),
path
});
})
.flatten()
.reduce((red, component) => {
return red.set(this.toLodashPath(component.path), component.value);
}, new Map())
.value();
}
}
2 changes: 1 addition & 1 deletion src/Optimizers/MoveToComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class MoveToComponents implements OptimizerInterface {
//check if the component already has a copy in components section of the specification. If it already has then we don't need to apply this optimization.
//It will be taken care of by ReuseComponents
if (this.doesHaveACopy(value1, components)) { continue; }
const componentName = value1.json().name || `${componentType}-${counter++}`;
const componentName = value1.name || `${componentType}-${counter++}`;
const target = `components.${componentType}s.${componentName}`;
elements.push({
path: key1,
Expand Down
4 changes: 2 additions & 2 deletions src/Utils/Helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ const compareComponents = (x: any, y: any): boolean => {
//Compares two components but also considers equality check. the referential equality check can be disabled by referentialEqualityCheck argument.
const isEqual = (component1: any, component2: any, referentialEqualityCheck: boolean): boolean => {
if (referentialEqualityCheck) {
return component1.json() === component2.json() || compareComponents(component1.json(), component2.json());
return component1 === component2 || compareComponents(component1, component2);
}
return component1.json() !== component2.json() && compareComponents(component1.json(), component2.json());
return component1 !== component2 && compareComponents(component1, component2);
};

const isInComponents = (path: string): boolean => {
Expand Down
12 changes: 6 additions & 6 deletions test/Utils/Helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@ import _ from 'lodash';
import { compareComponents, isEqual, isInComponents, isInChannels, toJS } from '../../src/Utils';

describe('Helpers', () => {
const testObject1 = { json: jest.fn().mockReturnValueOnce({ streetlightId: { schema: { type: 'string', 'x-extension': 'different_value' } } }) };
const testObject1_copy = { json: jest.fn().mockReturnValueOnce({ streetlightId: { schema: { type: 'string', 'x-extension': 'value' } } }) };
const testObject2 = { json: jest.fn().mockReturnValueOnce({ streetlightId: { schema: { type: 'number' } } }) };
const testObject1 = { streetlightId: { schema: { type: 'string', 'x-extension': 'different_value' } } };
const testObject1_copy = { streetlightId: { schema: { type: 'string', 'x-extension': 'value' } } };
const testObject2 = { streetlightId: { schema: { type: 'number' } } };
const testObject2_reference = testObject2;

describe('compareComponents', () => {
test('should return true.', () => {
expect(compareComponents(testObject1.json(), testObject1_copy.json())).toEqual(true);
expect(compareComponents(testObject1, testObject1_copy)).toEqual(true);
});

test('should return false.', () => {
expect(compareComponents(testObject1.json(), testObject2.json())).toEqual(false);
expect(compareComponents(testObject1, testObject2)).toEqual(false);
});
});
describe('isEqual', () => {
test('should return true.', () => {
expect(isEqual(testObject2, testObject2_reference, true)).toEqual(true);
expect(isEqual(testObject1, testObject1_copy, true)).toEqual(true);
expect(isEqual(testObject1, testObject1_copy, false)).toEqual(true);
});

test('should return false.', () => {
expect(isEqual(testObject1, testObject1_copy, false)).toEqual(false);
expect(isEqual(testObject2, testObject2_reference, false)).toEqual(false);
});
});
Expand Down

0 comments on commit 85087e4

Please sign in to comment.