Skip to content

Commit

Permalink
refactor: add missed tests for custom operations (#634)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Oct 4, 2022
1 parent 4580db5 commit add289e
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 7 deletions.
Binary file added asyncapi-parser-2.0.0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion src/custom-operations/anonymous-naming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function assignNameToAnonymousMessages(document: AsyncAPIDocumentInterface) {
let anonymousMessageCounter = 0;
document.messages().forEach(message => {
if (message.name() === undefined && message.extensions().get(xParserMessageName)?.value() === undefined) {
setExtension(xParserMessageName, `<anonymous-message-${++anonymousMessageCounter}>`, message);
setExtension(xParserMessageName, message.id() || `<anonymous-message-${++anonymousMessageCounter}>`, message);
}
});
}
Expand Down
11 changes: 9 additions & 2 deletions src/custom-operations/apply-traits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,24 @@ export function applyTraitsV3(asyncapi: v2.AsyncAPIObject) { // TODO: Change typ
}

function applyAllTraits(asyncapi: Record<string, any>, paths: string[]) {
const visited: Set<unknown> = new Set();
paths.forEach(path => {
JSONPath({
path,
json: asyncapi,
resultType: 'value',
callback(value) { applyTraits(value); },
callback(value) {
if (visited.has(value)) {
return;
}
visited.add(value);
applyTraits(value);
},
});
});
}

function applyTraits(value: Record<string, unknown>) {
function applyTraits(value: Record<string, unknown> & { traits?: any[] }) {
if (Array.isArray(value.traits)) {
for (const trait of value.traits) {
for (const key in trait) {
Expand Down
6 changes: 3 additions & 3 deletions src/custom-operations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ export async function customOperations(parser: Parser, document: AsyncAPIDocumen
}

async function operationsV2(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, options: ParseOptions): Promise<void> {
checkCircularRefs(document);
anonymousNaming(document);

if (options.applyTraits) {
applyTraitsV2(detailed.parsed);
}
if (options.parseSchemas) {
await parseSchemasV2(parser, detailed);
}

checkCircularRefs(document);
anonymousNaming(document);
}

4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export function hasRef(value: unknown): value is { $ref: string } {
return isObject(value) && '$ref' in value && typeof value.$ref === 'string';
}

export function toJSONPathArray(jsonPath: string): Array<string | number> {
return jsonPath.split('/').map(untilde);
}

export function tilde(str: string) {
return str.replace(/[~/]{1}/g, (sub) => {
switch (sub) {
Expand Down
31 changes: 31 additions & 0 deletions test/custom-operations/anonymous-naming.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,37 @@ describe('custom operations - anonymous naming', function() {
expect(document?.components().messages()[0].extensions().get(xParserMessageName)?.value()).toEqual('message');
});

it('should try use messageId for x-parser-message-name', async function() {
const { document } = await parser.parse({
asyncapi: '2.4.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {
channel: {
publish: {
operationId: 'operation',
message: {
$ref: '#/components/messages/message'
}
}
}
},
components: {
messages: {
message: {
messageId: 'someId',
payload: {}
}
}
}
});

expect(document?.messages()).toHaveLength(1);
expect(document?.messages()[0].extensions().get(xParserMessageName)?.value()).toEqual('someId');
});

it('should not override x-parser-message-name if it exists', async function() {
const { document } = await parser.parse({
asyncapi: '2.0.0',
Expand Down
186 changes: 186 additions & 0 deletions test/custom-operations/apply-traits.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { AsyncAPIDocumentV2 } from '../../src/models';
import { Parser } from '../../src/parser';

import type { v2 } from '../../src/spec-types';

describe('custom operations - apply traits', function() {
const parser = new Parser();

it('should apply traits to operations', async function() {
const documentRaw = {
asyncapi: '2.0.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {
channel: {
publish: {
operationId: 'publishId',
traits: [
{
operationId: 'anotherPubId',
description: 'some description'
},
{
description: 'another description'
}
]
},
subscribe: {
operationId: 'subscribeId',
traits: [
{
operationId: 'anotherSubId',
description: 'some description'
},
{
description: 'another description'
}
]
}
}
}
};
const { document, diagnostics } = await parser.parse(documentRaw);

expect(document).toBeInstanceOf(AsyncAPIDocumentV2);
expect(diagnostics.length > 0).toEqual(true);

const publish = document?.json()?.channels?.channel?.publish;
delete publish?.traits;
expect(publish).toEqual({ operationId: 'anotherPubId', description: 'another description' });

const subscribe = document?.json()?.channels?.channel?.subscribe;
delete subscribe?.traits;
expect(subscribe).toEqual({ operationId: 'anotherSubId', description: 'another description' });
});

it('should apply traits to messages', async function() {
const documentRaw = {
asyncapi: '2.4.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {
channel: {
publish: {
operationId: 'operationId',
message: {
messageId: 'messageId',
traits: [
{
messageId: 'anotherMessageId1',
description: 'some description'
},
{
description: 'another description'
}
]
}
},
subscribe: {
message: {
oneOf: [
{
messageId: 'messageId',
traits: [
{
messageId: 'anotherMessageId2',
description: 'some description'
},
{
description: 'another description'
}
]
},
{
messageId: 'messageId',
traits: [
{
messageId: 'anotherId',
description: 'some description'
},
{
description: 'another description'
},
{
messageId: 'anotherMessageId3',
description: 'simple description'
}
]
}
],
}
}
}
}
};
const { document, diagnostics } = await parser.parse(documentRaw);

expect(document).toBeInstanceOf(AsyncAPIDocumentV2);
expect(diagnostics.length > 0).toEqual(true);

const message = document?.json()?.channels?.channel?.publish?.message;
delete (message as v2.MessageObject)?.traits;
expect(message).toEqual({ messageId: 'anotherMessageId1', description: 'another description', 'x-parser-message-name': 'anotherMessageId1' });

const messageOneOf1 = (document?.json()?.channels?.channel?.subscribe?.message as { oneOf: Array<v2.MessageObject> }).oneOf[0];
delete messageOneOf1?.traits;
expect(messageOneOf1).toEqual({ messageId: 'anotherMessageId2', description: 'another description', 'x-parser-message-name': 'anotherMessageId2' });

const messageOneOf2 = (document?.json()?.channels?.channel?.subscribe?.message as { oneOf: Array<v2.MessageObject> }).oneOf[1];
delete messageOneOf2?.traits;
expect(messageOneOf2).toEqual({ messageId: 'anotherMessageId3', description: 'simple description', 'x-parser-message-name': 'anotherMessageId3' });
});

it('should preserve this same references', async function() {
const documentRaw = {
asyncapi: '2.4.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {
channel: {
publish: {
operationId: 'publishId',
message: {
$ref: '#/components/messages/message',
}
},
}
},
components: {
messages: {
message: {
messageId: 'messageId',
traits: [
{
messageId: 'anotherId',
description: 'some description'
},
{
description: 'another description'
},
{
messageId: 'anotherMessageId3',
description: 'simple description'
}
]
}
}
}
};
const { document, diagnostics } = await parser.parse(documentRaw);

expect(document).toBeInstanceOf(AsyncAPIDocumentV2);
expect(diagnostics.length > 0).toEqual(true);

const message = document?.json()?.channels?.channel?.publish?.message;
delete (message as v2.MessageObject)?.traits;
expect(message).toEqual({ messageId: 'anotherMessageId3', description: 'simple description', 'x-parser-message-name': 'anotherMessageId3' });
expect(message === document?.json()?.components?.messages?.message).toEqual(true);
});
});
38 changes: 38 additions & 0 deletions test/custom-operations/parse-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,44 @@ describe('custom operations - parse schemas', function() {
expect((document?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual({ type: 'object', 'x-parser-schema-id': '<anonymous-schema-1>' });
});

it('should preserve this same references', async function() {
const documentRaw = {
asyncapi: '2.0.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {
channel: {
publish: {
operationId: 'operationId',
message: {
$ref: '#/components/messages/message'
}
}
}
},
components: {
messages: {
message: {
payload: {
type: 'object',
}
}
}
}
};
const { document, diagnostics } = await parser.parse(documentRaw);

expect(document).toBeInstanceOf(AsyncAPIDocumentV2);
expect(diagnostics.length > 0).toEqual(true);

expect((document?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual({ type: 'object', 'x-parser-schema-id': '<anonymous-schema-1>' });
expect((document?.json().components?.messages?.message as v2.MessageObject)?.payload).toEqual({ type: 'object', 'x-parser-schema-id': '<anonymous-schema-1>' });
// check if logic preserves references
expect((document?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload === (document?.json().components?.messages?.message as v2.MessageObject)?.payload).toEqual(true);
});

it('should parse invalid schema format', async function() {
const documentRaw = {
asyncapi: '2.0.0',
Expand Down
2 changes: 1 addition & 1 deletion test/from.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('fromURL() & fromFile()', function() {

describe('fromURL()', function() {
it('should operate on existing HTTP source', async function() {
const { document, diagnostics } = await fromURL(parser, 'https://raw.githubusercontent.com/asyncapi/spec/master/examples/simple.yml').parse();
const { document, diagnostics } = await fromURL(parser, 'https://raw.githubusercontent.com/asyncapi/spec/v2.0.0/examples/2.0.0/gitter-streaming.yml').parse();
expect(document).not.toBeUndefined();
expect(diagnostics.length > 0).toEqual(true);
expect(hasWarningDiagnostic(diagnostics)).toEqual(true);
Expand Down

0 comments on commit add289e

Please sign in to comment.