From fdabe253a4ff9d4ba303b182dc318a919d4e38d9 Mon Sep 17 00:00:00 2001 From: Thomas O'Connor Date: Tue, 27 Nov 2018 17:16:25 -0500 Subject: [PATCH] RUN-4677 - Channel lifecycle (#203) * disconnect and destroy * undo gruntfile * lifecycle calls * add error messages for timing issue on destroy and disconnect --- .../src/api/interappbus/channel/channel.ts | 5 +++- .../src/api/interappbus/channel/client.ts | 16 +++++++++++++ .../src/api/interappbus/channel/index.ts | 24 +++++++++++++++++-- .../src/api/interappbus/channel/provider.ts | 10 +++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/js-adapter/src/api/interappbus/channel/channel.ts b/js-adapter/src/api/interappbus/channel/channel.ts index 1baf8a5ee..2ccd28fef 100644 --- a/js-adapter/src/api/interappbus/channel/channel.ts +++ b/js-adapter/src/api/interappbus/channel/channel.ts @@ -27,6 +27,7 @@ export interface ChannelMessagePayload extends Identity { } export class ChannelBase { + protected removeChannel: (mapKey: string) => void; protected subscriptions: any; public defaultAction: (action?: string, payload?: any, senderIdentity?: ProviderIdentity) => any; private preAction: (...args: any[]) => any; @@ -35,6 +36,7 @@ export class ChannelBase { private defaultSet: boolean; protected send: (to: Identity, action: string, payload: any) => Promise>; protected providerIdentity: ProviderIdentity; + protected sendRaw: Transport['sendAction']; constructor (providerIdentity: ProviderIdentity, send: Transport['sendAction']) { this.defaultSet = false; @@ -43,6 +45,7 @@ export class ChannelBase { this.defaultAction = () => { throw new Error('No action registered'); }; + this.sendRaw = send; this.send = async (to: Identity, action: string, payload: any) => { const raw = await send('send-channel-message', { ...to, providerIdentity: this.providerIdentity, action, payload }) .catch(reason => { @@ -77,7 +80,7 @@ export class ChannelBase { this.preAction = idOrResult(func); } - public onError(func: (e: any, action: string, id: Identity) => any) { + public onError(func: (action: string, error: any, id: Identity) => any) { if (this.errorMiddleware) { throw new Error('Already registered error middleware'); } diff --git a/js-adapter/src/api/interappbus/channel/client.ts b/js-adapter/src/api/interappbus/channel/client.ts index a3dd45d44..5fa77a971 100644 --- a/js-adapter/src/api/interappbus/channel/client.ts +++ b/js-adapter/src/api/interappbus/channel/client.ts @@ -1,12 +1,28 @@ import { ChannelBase, ProviderIdentity } from './channel'; import Transport from '../../../transport/transport'; +type DisconnectionListener = (providerIdentity: ProviderIdentity) => any; + export class ChannelClient extends ChannelBase { + private disconnectListener: DisconnectionListener; + constructor(providerIdentity: ProviderIdentity, send: Transport['sendAction']) { super(providerIdentity, send); + this.disconnectListener = () => undefined; } public async dispatch(action: string, payload?: any): Promise { return this.send(this.providerIdentity, action, payload); } + + public onDisconnection(listener: DisconnectionListener): void { + this.disconnectListener = listener; + } + + public async disconnect(): Promise { + const { channelName } = this.providerIdentity; + await this.sendRaw('disconnect-from-channel', { channelName }); + const { channelId } = this.providerIdentity; + this.removeChannel(channelId); + } } diff --git a/js-adapter/src/api/interappbus/channel/index.ts b/js-adapter/src/api/interappbus/channel/index.ts index eeaf6b8c4..8e48e0efe 100644 --- a/js-adapter/src/api/interappbus/channel/index.ts +++ b/js-adapter/src/api/interappbus/channel/index.ts @@ -72,6 +72,15 @@ export class Channel extends EmitterBase { const channel = new ChannelClient(providerIdentity, this.wire.sendAction.bind(this.wire)); const key = providerIdentity.channelId; this.channelMap.set(key, channel); + //@ts-ignore use of protected property + channel.removeChannel = this.removeChannelFromMap.bind(this); + this.on('disconnected', (eventPayload: ProviderIdentity) => { + if (eventPayload.channelName === channelName) { + this.removeChannelFromMap(key); + //@ts-ignore use of private property + channel.disconnectListener(eventPayload); + } + }); return channel; } catch (e) { const shouldWait: boolean = Object.assign({ wait: true }, opts).wait; @@ -95,6 +104,8 @@ export class Channel extends EmitterBase { const channel = new ChannelProvider(providerIdentity, this.wire.sendAction.bind(this.wire)); const key = providerIdentity.channelId; this.channelMap.set(key, channel); + //@ts-ignore use of protected property + channel.removeChannel = this.removeChannelFromMap.bind(this); this.on('client-disconnected', (eventPayload: ProviderIdentity) => { if (eventPayload.channelName === channelName) { channel.connections = channel.connections.filter(identity => { @@ -106,6 +117,11 @@ export class Channel extends EmitterBase { }); return channel; } + + protected removeChannelFromMap(mapKey: string) { + this.channelMap.delete(mapKey); + } + public onmessage = (msg: ChannelMessage) => { if (msg.action === 'process-channel-message') { this.processChannelMessage(msg); @@ -121,7 +137,9 @@ export class Channel extends EmitterBase { const key = providerIdentity.channelId; const bus = this.channelMap.get(key); if (!bus) { - return; + ackToSender.payload.success = false; + ackToSender.payload.reason = `Client connection with identity ${JSON.stringify(this.wire.me)} no longer connected.`; + return this.wire.sendRaw(ackToSender); } try { const res = await bus.processAction(action, payload, senderIdentity); @@ -139,7 +157,9 @@ export class Channel extends EmitterBase { const key = providerIdentity.channelId; const bus = this.channelMap.get(key); if (!bus) { - return; + ackToSender.payload.success = false; + ackToSender.payload.reason = `Channel "${providerIdentity.channelName}" has been destroyed.`; + return this.wire.sendRaw(ackToSender); } try { if (!(bus instanceof ChannelProvider)) { diff --git a/js-adapter/src/api/interappbus/channel/provider.ts b/js-adapter/src/api/interappbus/channel/provider.ts index 63b20df88..638746621 100644 --- a/js-adapter/src/api/interappbus/channel/provider.ts +++ b/js-adapter/src/api/interappbus/channel/provider.ts @@ -3,6 +3,7 @@ import Transport from '../../../transport/transport'; import { Identity } from '../../../main'; export type ConnectionListener = (identity: Identity, connectionMessage?: any) => any; +export type DisconnectionListener = (identity: Identity) => any; export class ChannelProvider extends ChannelBase { private connectListener: ConnectionListener; @@ -33,8 +34,15 @@ export class ChannelProvider extends ChannelBase { this.connectListener = listener; } - public onDisconnection(listener: ConnectionListener): void { + public onDisconnection(listener: DisconnectionListener): void { this.disconnectListener = listener; } + public async destroy(): Promise { + const { channelName } = this.providerIdentity; + await this.sendRaw('destroy-channel', { channelName }); + const { channelId } = this.providerIdentity; + this.removeChannel(channelId); + } + } \ No newline at end of file