From d04b871af71627be7e582d9aaa1c1019a2ae7b5b Mon Sep 17 00:00:00 2001 From: Sergio Moya <1083296+smoya@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:25:31 +0200 Subject: [PATCH] fix: include all channels in message.channels() (#842) --- src/models/v3/message.ts | 19 +++- test/models/v3/message.spec.ts | 190 +++++++++++++++++---------------- 2 files changed, 111 insertions(+), 98 deletions(-) diff --git a/src/models/v3/message.ts b/src/models/v3/message.ts index ce05a9e3d..123079927 100644 --- a/src/models/v3/message.ts +++ b/src/models/v3/message.ts @@ -1,3 +1,4 @@ +import { Channel } from './channel'; import { Channels } from './channels'; import { Operations } from './operations'; import { Operation } from './operation'; @@ -58,16 +59,26 @@ export class Message extends MessageTrait implements MessageIn channels(): ChannelsInterface { const channels: ChannelInterface[] = []; - const channelData: any[] = []; + const channelsData: any[] = []; this.operations().forEach(operation => { operation.channels().forEach(channel => { - const channelsData = channel.json(); - if (!channelData.includes(channelsData)) { - channelData.push(channelsData); + const channelData = channel.json(); + // Comparing with the data (JSON) because same channel could exist but it will include the ID based on where it is declared. For example, asyncapi.channels contain ID field. + if (!channelsData.includes(channelData)) { + channelsData.push(channelData); channels.push(channel); } }); }); + + Object.entries((this._meta.asyncapi?.parsed as v3.AsyncAPIObject)?.channels || {}).forEach(([channelId, channelData]) => { + const channelModel = this.createModel(Channel, channelData as v3.ChannelObject, { id: channelId, pointer: `/channels/${channelId}` }); + if (!channelsData.includes(channelData) && channelModel.messages().some(m => m.json() === this._json)) { + channelsData.push(channelData); + channels.push(channelModel); + } + }); + return new Channels(channels); } diff --git a/test/models/v3/message.spec.ts b/test/models/v3/message.spec.ts index 91432f36d..3bcca68d0 100644 --- a/test/models/v3/message.spec.ts +++ b/test/models/v3/message.spec.ts @@ -1,7 +1,13 @@ +import { Channel } from '../../../src/models/v3/channel'; +import { Channels } from '../../../src/models/v3/channels'; import { Message } from '../../../src/models/v3/message'; import { MessageTraits } from '../../../src/models/v3/message-traits'; import { MessageTrait } from '../../../src/models/v3/message-trait'; import { Schema } from '../../../src/models/v3/schema'; +import { Servers } from '../../../src/models/v3/servers'; +import { Server } from '../../../src/models/v3/server'; +import { Operations } from '../../../src/models/v3/operations'; +import { Operation } from '../../../src/models/v3/operation'; import { assertCoreModel } from './utils'; @@ -71,100 +77,96 @@ describe('Message model', function() { }); }); - // describe('.servers()', function() { - // it('should return collection of servers - available on all servers', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { servers: { production: {}, development: {} }, channels: { 'user/signup': { publish: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.servers()).toBeInstanceOf(Servers); - // expect(d.servers().all()).toHaveLength(2); - // expect(d.servers().all()[0]).toBeInstanceOf(Server); - // expect(d.servers().all()[0].id()).toEqual('production'); - // expect(d.servers().all()[1]).toBeInstanceOf(Server); - // expect(d.servers().all()[1].id()).toEqual('development'); - // }); - - // it('should return collection of servers - available on selected servers', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { servers: { production: {}, development: {} }, channels: { 'user/signup': { publish: { message: doc }, servers: ['production'] } } } } as any, pointer: '', id: 'message' }); - // expect(d.servers()).toBeInstanceOf(Servers); - // expect(d.servers().all()).toHaveLength(1); - // expect(d.servers().all()[0]).toBeInstanceOf(Server); - // expect(d.servers().all()[0].id()).toEqual('production'); - // }); - - // it('should return collection of servers - do not duplicate servers', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { servers: { production: {}, development: {} }, channels: { 'user/signup': { publish: { message: doc }, subscribe: { message: doc }, servers: ['production'] } } } } as any, pointer: '', id: 'message' }); - // expect(d.servers()).toBeInstanceOf(Servers); - // expect(d.servers().all()).toHaveLength(1); - // expect(d.servers().all()[0]).toBeInstanceOf(Server); - // expect(d.servers().all()[0].id()).toEqual('production'); - // }); - // }); - - // describe('.channels()', function() { - // it('should return collection of channels - single channel', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.channels()).toBeInstanceOf(Channels); - // expect(d.channels().all()).toHaveLength(1); - // expect(d.channels().all()[0]).toBeInstanceOf(Channel); - // expect(d.channels().all()[0].address()).toEqual('user/signup'); - // }); - - // it('should return collection of channels - multiple channels', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc } }, 'user/logout': { subscribe: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.channels()).toBeInstanceOf(Channels); - // expect(d.channels().all()).toHaveLength(2); - // expect(d.channels().all()[0]).toBeInstanceOf(Channel); - // expect(d.channels().all()[0].address()).toEqual('user/signup'); - // expect(d.channels().all()[1]).toBeInstanceOf(Channel); - // expect(d.channels().all()[1].address()).toEqual('user/logout'); - // }); - - // it('should return collection of channels - do not duplicate channels', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc }, subscribe: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.channels()).toBeInstanceOf(Channels); - // expect(d.channels().all()).toHaveLength(1); - // expect(d.channels().all()[0]).toBeInstanceOf(Channel); - // expect(d.channels().all()[0].address()).toEqual('user/signup'); - // }); - // }); - - // describe('.operations()', function() { - // it('should return collection of operations - single operation', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.operations()).toBeInstanceOf(Operations); - // expect(d.operations().all()).toHaveLength(1); - // expect(d.operations().all()[0]).toBeInstanceOf(Operation); - // expect(d.operations().all()[0].action()).toEqual('publish'); - // }); - - // it('should return collection of operations - multiple operations', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc }, subscribe: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.operations()).toBeInstanceOf(Operations); - // expect(d.operations().all()).toHaveLength(2); - // expect(d.operations().all()[0]).toBeInstanceOf(Operation); - // expect(d.operations().all()[0].action()).toEqual('subscribe'); - // expect(d.operations().all()[1]).toBeInstanceOf(Operation); - // expect(d.operations().all()[1].action()).toEqual('publish'); - // }); - - // it('should return collection of operations - multiple operations on different channels', function() { - // const doc = {}; - // const d = new Message(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: doc } }, 'user/logout': { subscribe: { message: doc } } } } } as any, pointer: '', id: 'message' }); - // expect(d.operations()).toBeInstanceOf(Operations); - // expect(d.operations().all()).toHaveLength(2); - // expect(d.operations().all()[0]).toBeInstanceOf(Operation); - // expect(d.operations().all()[0].action()).toEqual('publish'); - // expect(d.operations().all()[1]).toBeInstanceOf(Operation); - // expect(d.operations().all()[1].action()).toEqual('subscribe'); - // }); - // }); + describe('.servers()', function() { + it('should return collection of servers - available on all servers', function() { + const doc = {}; + const serverProduction = {host: 'mqtt://myhost.io', protocol: 'mqtt'}; + const serverDevelopment = {host: 'mqtt://dev.myhost.io', protocol: 'mqtt'}; + const channel = { }; // no channels assigned, means all channels are related + const d = new Message(doc, { asyncapi: { parsed: { servers: { production: serverProduction, development: serverDevelopment, demo: {} }, channels: { userSignedUp: channel }, operations: { userSignUp: { action: 'send', messages: [doc], channel } } } } as any, pointer: '', id: 'message' }); + + expect(d.servers()).toBeInstanceOf(Servers); + expect(d.servers().all()).toHaveLength(3); + expect(d.servers().all()[0]).toBeInstanceOf(Server); + expect(d.servers().all()[0].id()).toEqual('production'); + expect(d.servers().all()[1]).toBeInstanceOf(Server); + expect(d.servers().all()[1].id()).toEqual('development'); + expect(d.servers().all()[2]).toBeInstanceOf(Server); + expect(d.servers().all()[2].id()).toEqual('demo'); + }); + + it('should return collection of servers - available on selected servers', function() { + const doc = {}; + const serverProduction = {host: 'mqtt://myhost.io', protocol: 'mqtt'}; + const serverDevelopment = {host: 'mqtt://dev.myhost.io', protocol: 'mqtt'}; + const channel = { servers: [serverProduction, serverDevelopment]}; // selecting 2 of the 3 servers + const d = new Message(doc, { asyncapi: { parsed: { servers: { production: serverProduction, development: serverDevelopment, demo: {} }, channels: { userSignedUp: channel }, operations: { userSignUp: { action: 'send', messages: [doc], channel } } } } as any, pointer: '', id: 'message' }); + + expect(d.servers()).toBeInstanceOf(Servers); + expect(d.servers().all()).toHaveLength(2); + expect(d.servers().all()[0]).toBeInstanceOf(Server); + expect(d.servers().all()[0].id()).toEqual('production'); + expect(d.servers().all()[1]).toBeInstanceOf(Server); + expect(d.servers().all()[1].id()).toEqual('development'); + }); + }); + + describe('.channels()', function() { + it('should return collection of channels - single channel', function() { + const doc = {}; + const channel = {address: 'user/signup', messages: { messageOne: doc }}; + const d = new Message(doc, { asyncapi: { parsed: {channels: { userSignUp: channel } } } as any, pointer: '', id: 'message' }); + expect(d.channels()).toBeInstanceOf(Channels); + expect(d.channels().all()).toHaveLength(1); + expect(d.channels().all()[0]).toBeInstanceOf(Channel); + expect(d.channels().all()[0].address()).toEqual('user/signup'); + }); + + it('should return collection of channels - multiple channels', function() { + const doc = {}; + const channelOne = {address: 'user/signup', messages: { messageOne: doc }}; + const channelTwo = {address: 'user/logout', messages: { messageOne: doc }}; + const d = new Message(doc, { asyncapi: { parsed: {channels: { userSignUp: channelOne, userLogOut: channelTwo } } } as any, pointer: '', id: 'message' }); + expect(d.channels()).toBeInstanceOf(Channels); + expect(d.channels().all()).toHaveLength(2); + expect(d.channels().all()[0]).toBeInstanceOf(Channel); + expect(d.channels().all()[0].address()).toEqual('user/signup'); + expect(d.channels().all()[1]).toBeInstanceOf(Channel); + expect(d.channels().all()[1].address()).toEqual('user/logout'); + }); + + it('should return collection of channels - do not duplicate channels', function() { + const doc = {}; + const channel = {address: 'user/signup', messages: { messageOne: doc }}; + const d = new Message(doc, { asyncapi: { parsed: {channels: { userSignUp: channel }, operations: { userSignUp: { action: 'send', messages: [doc], channel } } } } as any, pointer: '', id: 'message' }); + expect(d.channels()).toBeInstanceOf(Channels); + expect(d.channels().all()).toHaveLength(1); + expect(d.channels().all()[0]).toBeInstanceOf(Channel); + expect(d.channels().all()[0].address()).toEqual('user/signup'); + }); + }); + + describe('.operations()', function() { + it('should return collection of operations - single operation', function() { + const doc = {}; + const d = new Message(doc, { asyncapi: { parsed: { operations: { userSignUp: { action: 'send', messages: [doc] } } } } as any, pointer: '', id: 'message' }); + expect(d.operations()).toBeInstanceOf(Operations); + expect(d.operations().all()).toHaveLength(1); + expect(d.operations().all()[0]).toBeInstanceOf(Operation); + expect(d.operations().all()[0].id()).toEqual('userSignUp'); + }); + + it('should return collection of operations - multiple operations', function() { + const doc = {}; + const d = new Message(doc, { asyncapi: { parsed: { operations: { userSignUp: { action: 'send', messages: [doc] }, userLogOut: { action: 'send', messages: [doc] } } } } as any, pointer: '', id: 'message' }); + expect(d.operations()).toBeInstanceOf(Operations); + expect(d.operations().all()).toHaveLength(2); + expect(d.operations().all()[0]).toBeInstanceOf(Operation); + expect(d.operations().all()[0].id()).toEqual('userSignUp'); + expect(d.operations().all()[1]).toBeInstanceOf(Operation); + expect(d.operations().all()[1].id()).toEqual('userLogOut'); + }); + }); describe('.traits()', function() { it('should return collection of traits', function() {