@morgan-stanley/composeui-fdc3 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +6 -0
- package/jest.config.ts +23 -0
- package/package.json +49 -0
- package/rollup.config.js +16 -0
- package/src/ComposeUIChannel.spec.ts +242 -0
- package/src/ComposeUIContextListener.spec.ts +109 -0
- package/src/ComposeUIDesktopAgent.spec.ts +197 -0
- package/src/ComposeUIDesktopAgent.ts +252 -0
- package/src/ComposeUIIntentHandling.spec.ts +369 -0
- package/src/ComposeUIMessageRouterChannelFactory.spec.ts +200 -0
- package/src/ComposeUIMessageRouterMetadataClient.spec.ts +240 -0
- package/src/ComposeUIMessageRouterOpenClient.spec.ts +239 -0
- package/src/index.ts +66 -0
- package/src/infrastructure/ChannelFactory.ts +25 -0
- package/src/infrastructure/ChannelItem.ts +20 -0
- package/src/infrastructure/ChannelType.ts +14 -0
- package/src/infrastructure/ComposeUIChannel.ts +87 -0
- package/src/infrastructure/ComposeUIContextListener.ts +154 -0
- package/src/infrastructure/ComposeUIErrors.ts +21 -0
- package/src/infrastructure/ComposeUIIntentListener.ts +105 -0
- package/src/infrastructure/ComposeUIIntentResolution.ts +58 -0
- package/src/infrastructure/ComposeUIPrivateChannel.ts +197 -0
- package/src/infrastructure/ComposeUITopic.ts +155 -0
- package/src/infrastructure/IntentsClient.ts +22 -0
- package/src/infrastructure/MessageRouterChannelFactory.ts +182 -0
- package/src/infrastructure/MessageRouterIntentsClient.ts +125 -0
- package/src/infrastructure/MessageRouterMetadataClient.ts +75 -0
- package/src/infrastructure/MessageRouterOpenClient.ts +106 -0
- package/src/infrastructure/MetadataClient.ts +19 -0
- package/src/infrastructure/OpenAppIdentifier.ts +18 -0
- package/src/infrastructure/OpenClient.ts +18 -0
- package/src/infrastructure/PrivateChannelContextListenerEventListener.ts +45 -0
- package/src/infrastructure/PrivateChannelDisconnectEventListener.ts +45 -0
- package/src/infrastructure/messages/Fdc3AddContextListenerRequest.ts +22 -0
- package/src/infrastructure/messages/Fdc3AddContextListenerResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3CreateAppChannelRequest.ts +17 -0
- package/src/infrastructure/messages/Fdc3CreateAppChannelResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3CreatePrivateChannelRequest.ts +16 -0
- package/src/infrastructure/messages/Fdc3CreatePrivateChannelResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3FindChannelRequest.ts +24 -0
- package/src/infrastructure/messages/Fdc3FindChannelResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3FindInstancesRequest.ts +17 -0
- package/src/infrastructure/messages/Fdc3FindInstancesResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3FindIntentRequest.ts +24 -0
- package/src/infrastructure/messages/Fdc3FindIntentResponse.ts +19 -0
- package/src/infrastructure/messages/Fdc3FindIntentsByContextRequest.ts +23 -0
- package/src/infrastructure/messages/Fdc3FindIntentsByContextResponse.ts +19 -0
- package/src/infrastructure/messages/Fdc3GetAppMetadataRequest.ts +17 -0
- package/src/infrastructure/messages/Fdc3GetAppMetadataResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3GetCurrentContextRequest.ts +18 -0
- package/src/infrastructure/messages/Fdc3GetInfoRequest.ts +17 -0
- package/src/infrastructure/messages/Fdc3GetInfoResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3GetIntentResultRequest.ts +23 -0
- package/src/infrastructure/messages/Fdc3GetIntentResultResponse.ts +23 -0
- package/src/infrastructure/messages/Fdc3GetOpenedAppContextRequest.ts +15 -0
- package/src/infrastructure/messages/Fdc3GetOpenedAppContextResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3GetUserChannelsRequest.ts +15 -0
- package/src/infrastructure/messages/Fdc3GetUserChannelsResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3IntentListenerRequest.ts +22 -0
- package/src/infrastructure/messages/Fdc3IntentListenerResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3JoinUserChannelRequest.ts +15 -0
- package/src/infrastructure/messages/Fdc3JoinUserChannelResponse.ts +20 -0
- package/src/infrastructure/messages/Fdc3OpenRequest.ts +21 -0
- package/src/infrastructure/messages/Fdc3OpenResponse.ts +18 -0
- package/src/infrastructure/messages/Fdc3PrivateChannelInternalEvent.ts +26 -0
- package/src/infrastructure/messages/Fdc3RaiseIntentForContextRequest.ts +20 -0
- package/src/infrastructure/messages/Fdc3RaiseIntentRequest.ts +24 -0
- package/src/infrastructure/messages/Fdc3RaiseIntentResolutionRequest.ts +20 -0
- package/src/infrastructure/messages/Fdc3RaiseIntentResponse.ts +21 -0
- package/src/infrastructure/messages/Fdc3RemoveContextListenerRequest.ts +19 -0
- package/src/infrastructure/messages/Fdc3RemoveContextListenerResponse.ts +17 -0
- package/src/infrastructure/messages/Fdc3StoreIntentResultRequest.ts +29 -0
- package/src/infrastructure/messages/Fdc3StoreIntentResultResponse.ts +17 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Morgan Stanley makes this available to you under the Apache License,
|
|
3
|
+
* Version 2.0 (the "License"). You may obtain a copy of the License at
|
|
4
|
+
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
* See the NOTICE file distributed with this work for additional information
|
|
6
|
+
* regarding copyright ownership. Unless required by applicable law or agreed
|
|
7
|
+
* to in writing, software distributed under the License is distributed on an
|
|
8
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
9
|
+
* or implied. See the License for the specific language governing permissions
|
|
10
|
+
* and limitations under the License.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { jest } from '@jest/globals';
|
|
15
|
+
import { ComposeUIChannel } from './infrastructure/ComposeUIChannel';
|
|
16
|
+
import { MessageRouter } from '@morgan-stanley/composeui-messaging-client';
|
|
17
|
+
import { ComposeUIContextListener } from './infrastructure/ComposeUIContextListener';
|
|
18
|
+
import { ComposeUIDesktopAgent } from './ComposeUIDesktopAgent';
|
|
19
|
+
import { ComposeUITopic } from './infrastructure/ComposeUITopic';
|
|
20
|
+
import { Channel, ChannelError, ContextHandler } from '@finos/fdc3';
|
|
21
|
+
import { ComposeUIErrors } from './infrastructure/ComposeUIErrors';
|
|
22
|
+
import { ChannelFactory } from './infrastructure/ChannelFactory';
|
|
23
|
+
import { ComposeUIPrivateChannel } from './infrastructure/ComposeUIPrivateChannel';
|
|
24
|
+
import { ChannelType } from './infrastructure/ChannelType';
|
|
25
|
+
import { Fdc3GetOpenedAppContextResponse } from './infrastructure/messages/Fdc3GetOpenedAppContextResponse';
|
|
26
|
+
|
|
27
|
+
const dummyContext = { type: "dummyContextType" };
|
|
28
|
+
const dummyChannelId = "dummy";
|
|
29
|
+
let messageRouterClient: MessageRouter;
|
|
30
|
+
let desktopAgent: ComposeUIDesktopAgent;
|
|
31
|
+
|
|
32
|
+
const testInstrument = {
|
|
33
|
+
type: 'fdc3.instrument',
|
|
34
|
+
id: {
|
|
35
|
+
ticker: 'AAPL'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const contextMessageHandlerMock = jest.fn((something) => {
|
|
39
|
+
return "dummy";
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('Tests for ComposeUIDesktopAgent implementation API', () => {
|
|
43
|
+
//Be aware that currently the tests are for User channels mostly!
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
window.composeui = {
|
|
46
|
+
fdc3: {
|
|
47
|
+
config: {
|
|
48
|
+
appId: "testAppId",
|
|
49
|
+
instanceId: "testInstanceId"
|
|
50
|
+
},
|
|
51
|
+
channelId : "test",
|
|
52
|
+
openAppIdentifier: {
|
|
53
|
+
openedAppContextId: "test"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
messageRouterClient = {
|
|
59
|
+
clientId: "dummy",
|
|
60
|
+
subscribe: jest.fn(() => {
|
|
61
|
+
return Promise.resolve({ unsubscribe: () => { } });
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
publish: jest.fn(() => { return Promise.resolve() }),
|
|
65
|
+
connect: jest.fn(() => { return Promise.resolve() }),
|
|
66
|
+
registerEndpoint: jest.fn(() => { return Promise.resolve() }),
|
|
67
|
+
unregisterEndpoint: jest.fn(() => { return Promise.resolve() }),
|
|
68
|
+
registerService: jest.fn(() => { return Promise.resolve() }),
|
|
69
|
+
unregisterService: jest.fn(() => { return Promise.resolve() }),
|
|
70
|
+
invoke: jest.fn(() => {
|
|
71
|
+
return Promise.resolve(JSON.stringify({ context: "", payload: `${JSON.stringify(dummyContext)}` }))
|
|
72
|
+
})
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let channelFactory: ChannelFactory = {
|
|
76
|
+
createPrivateChannel: jest.fn(() => { return Promise.resolve(new ComposeUIPrivateChannel("privateId", messageRouterClient, true)) }),
|
|
77
|
+
getChannel: jest.fn(async (channelId: string, channelType: ChannelType) => {
|
|
78
|
+
if (channelId == dummyChannelId) { return new ComposeUIChannel(channelId, channelType, messageRouterClient); }
|
|
79
|
+
else { throw new Error(ChannelError.NoChannelFound); }
|
|
80
|
+
}),
|
|
81
|
+
getIntentListener: jest.fn(() => Promise.reject("Not implemented")),
|
|
82
|
+
createAppChannel: jest.fn(() => Promise.reject("Not implemented")),
|
|
83
|
+
joinUserChannel: jest.fn(() => Promise.resolve(new ComposeUIChannel(dummyChannelId, "user", messageRouterClient))),
|
|
84
|
+
getUserChannels: jest.fn(() => Promise.reject("Not implemented")),
|
|
85
|
+
getContextListener: jest.fn((openHandled: boolean, channel: Channel, handler: ContextHandler, contextType?: string) => {return Promise.resolve(new ComposeUIContextListener(true, messageRouterClient, handler, contextType))})
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
desktopAgent = new ComposeUIDesktopAgent(messageRouterClient, channelFactory);
|
|
89
|
+
await desktopAgent.joinUserChannel(dummyChannelId);
|
|
90
|
+
await new Promise(f => setTimeout(f, 100));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('ComposeUIDesktopAgent could not be created as no instanceId found on window object', async () => {
|
|
94
|
+
window.composeui.fdc3.config = undefined;
|
|
95
|
+
expect(() => new ComposeUIDesktopAgent(messageRouterClient))
|
|
96
|
+
.toThrowError(ComposeUIErrors.InstanceIdNotFound);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('broadcast will trigger publish method of the messageRouter', async () => {
|
|
100
|
+
await desktopAgent.broadcast(testInstrument);
|
|
101
|
+
expect(messageRouterClient.publish).toBeCalledTimes(1);
|
|
102
|
+
expect(messageRouterClient.publish).toHaveBeenCalledWith(ComposeUITopic.broadcast(dummyChannelId, "user"), JSON.stringify(testInstrument));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('broadcast will fail as the current channel is not defined', async () => {
|
|
106
|
+
await desktopAgent.leaveCurrentChannel();
|
|
107
|
+
await expect(desktopAgent.broadcast(testInstrument))
|
|
108
|
+
.rejects
|
|
109
|
+
.toThrow("The current channel has not been set.");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('default channel can be retrieved', async () => {
|
|
113
|
+
var result = await desktopAgent.getCurrentChannel();
|
|
114
|
+
expect(result).toMatchObject<Partial<Channel>>({ id: dummyChannelId, type: "user" });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('leaveCurrentChannel will set the current user channel to undefined', async () => {
|
|
118
|
+
await desktopAgent.leaveCurrentChannel();
|
|
119
|
+
var result = await desktopAgent.getCurrentChannel();
|
|
120
|
+
expect(result).toBeFalsy();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('getCurrentChannel will get the current user channel', async () => {
|
|
124
|
+
await desktopAgent.leaveCurrentChannel();
|
|
125
|
+
await desktopAgent.joinUserChannel(dummyChannelId);
|
|
126
|
+
var result = await desktopAgent.getCurrentChannel();
|
|
127
|
+
expect(result).toMatchObject<Partial<Channel>>({ id: dummyChannelId, type: "user" });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('listener could not handle context message as its not subscribed', async () => {
|
|
131
|
+
await desktopAgent.leaveCurrentChannel();
|
|
132
|
+
const listener = <ComposeUIContextListener>await desktopAgent.addContextListener("fdc3.instrument", contextMessageHandlerMock)
|
|
133
|
+
var result = await desktopAgent.getCurrentChannel();
|
|
134
|
+
expect(result).toBeFalsy();
|
|
135
|
+
expect(listener.handleContextMessage(dummyContext))
|
|
136
|
+
.rejects
|
|
137
|
+
.toThrow("The current listener is not subscribed.");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('joinChannel will set the current user channel', async () => {
|
|
141
|
+
await desktopAgent.joinChannel(dummyChannelId);
|
|
142
|
+
var result = await desktopAgent.getCurrentChannel();
|
|
143
|
+
expect(result).toMatchObject<Partial<Channel>>({ id: dummyChannelId, type: "user" });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('createPrivateChannel returns the channel', async () => {
|
|
147
|
+
let channel = await desktopAgent.createPrivateChannel();
|
|
148
|
+
expect(channel).toBeInstanceOf(ComposeUIPrivateChannel);
|
|
149
|
+
expect(channel.type).toBe("private");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('addContextListener handles openAppContext', async() => {
|
|
153
|
+
const response: Fdc3GetOpenedAppContextResponse = {
|
|
154
|
+
context: {type: "fdc3.instrument"}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
messageRouterClient = {
|
|
158
|
+
clientId: "dummy",
|
|
159
|
+
subscribe: jest.fn(() => {
|
|
160
|
+
return Promise.resolve({ unsubscribe: () => { } });
|
|
161
|
+
}),
|
|
162
|
+
|
|
163
|
+
publish: jest.fn(() => { return Promise.resolve() }),
|
|
164
|
+
connect: jest.fn(() => { return Promise.resolve() }),
|
|
165
|
+
registerEndpoint: jest.fn(() => { return Promise.resolve() }),
|
|
166
|
+
unregisterEndpoint: jest.fn(() => { return Promise.resolve() }),
|
|
167
|
+
registerService: jest.fn(() => { return Promise.resolve() }),
|
|
168
|
+
unregisterService: jest.fn(() => { return Promise.resolve() }),
|
|
169
|
+
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(undefined)}`) })
|
|
170
|
+
.mockImplementationOnce(() => Promise.resolve(`${JSON.stringify(response)}`))
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
let channelFactory: ChannelFactory = {
|
|
174
|
+
createPrivateChannel: jest.fn(() => { return Promise.resolve(new ComposeUIPrivateChannel("privateId", messageRouterClient, true)) }),
|
|
175
|
+
getChannel: jest.fn(async (channelId: string, channelType: ChannelType) => {
|
|
176
|
+
if (channelId == dummyChannelId) { return new ComposeUIChannel(channelId, channelType, messageRouterClient); }
|
|
177
|
+
else { throw new Error(ChannelError.NoChannelFound); }
|
|
178
|
+
}),
|
|
179
|
+
getIntentListener: jest.fn(() => Promise.reject("Not implemented")),
|
|
180
|
+
createAppChannel: jest.fn(() => Promise.reject("Not implemented")),
|
|
181
|
+
joinUserChannel: jest.fn(() => Promise.resolve(new ComposeUIChannel(dummyChannelId, "user", messageRouterClient))),
|
|
182
|
+
getUserChannels: jest.fn(() => Promise.reject("Not implemented")),
|
|
183
|
+
getContextListener: jest.fn((openHandled: boolean, channel: Channel, handler: ContextHandler, contextType?: string) => {return Promise.resolve(new ComposeUIContextListener(true, messageRouterClient, handler, contextType))})
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
desktopAgent = new ComposeUIDesktopAgent(messageRouterClient, channelFactory);
|
|
187
|
+
|
|
188
|
+
//Fill the openedAppContext field
|
|
189
|
+
await desktopAgent.getOpenedAppContext();
|
|
190
|
+
|
|
191
|
+
const currentChannel = await desktopAgent.getCurrentChannel();
|
|
192
|
+
expect(currentChannel).toBe(null);
|
|
193
|
+
|
|
194
|
+
const listener = await desktopAgent.addContextListener("fdc3.instrument", contextMessageHandlerMock);
|
|
195
|
+
expect(contextMessageHandlerMock).toHaveBeenCalledTimes(1);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Morgan Stanley makes this available to you under the Apache License,
|
|
3
|
+
* Version 2.0 (the "License"). You may obtain a copy of the License at
|
|
4
|
+
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
* See the NOTICE file distributed with this work for additional information
|
|
6
|
+
* regarding copyright ownership. Unless required by applicable law or agreed
|
|
7
|
+
* to in writing, software distributed under the License is distributed on an
|
|
8
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
9
|
+
* or implied. See the License for the specific language governing permissions
|
|
10
|
+
* and limitations under the License.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
AppIdentifier,
|
|
16
|
+
AppIntent,
|
|
17
|
+
AppMetadata,
|
|
18
|
+
Channel,
|
|
19
|
+
ChannelError,
|
|
20
|
+
Context,
|
|
21
|
+
ContextHandler,
|
|
22
|
+
DesktopAgent,
|
|
23
|
+
ImplementationMetadata,
|
|
24
|
+
IntentHandler,
|
|
25
|
+
IntentResolution,
|
|
26
|
+
Listener,
|
|
27
|
+
PrivateChannel
|
|
28
|
+
} from '@finos/fdc3';
|
|
29
|
+
|
|
30
|
+
import { MessageRouter } from '@morgan-stanley/composeui-messaging-client';
|
|
31
|
+
import { ComposeUIContextListener } from './infrastructure/ComposeUIContextListener';
|
|
32
|
+
import { ComposeUIErrors } from './infrastructure/ComposeUIErrors';
|
|
33
|
+
import { ChannelFactory } from './infrastructure/ChannelFactory';
|
|
34
|
+
import { MessageRouterChannelFactory } from './infrastructure/MessageRouterChannelFactory';
|
|
35
|
+
import { MessageRouterIntentsClient } from './infrastructure/MessageRouterIntentsClient';
|
|
36
|
+
import { IntentsClient } from './infrastructure/IntentsClient';
|
|
37
|
+
import { MetadataClient } from './infrastructure/MetadataClient';
|
|
38
|
+
import { MessageRouterMetadataClient } from './infrastructure/MessageRouterMetadataClient';
|
|
39
|
+
import { OpenClient } from "./infrastructure/OpenClient";
|
|
40
|
+
import { MessageRouterOpenClient } from "./infrastructure/MessageRouterOpenClient";
|
|
41
|
+
|
|
42
|
+
export class ComposeUIDesktopAgent implements DesktopAgent {
|
|
43
|
+
private appChannels: Channel[] = [];
|
|
44
|
+
private userChannels: Channel[] = [];
|
|
45
|
+
private privateChannels: Channel[] = [];
|
|
46
|
+
private currentChannel?: Channel;
|
|
47
|
+
private topLevelContextListeners: ComposeUIContextListener[] = [];
|
|
48
|
+
private intentListeners: Listener[] = [];
|
|
49
|
+
private channelFactory: ChannelFactory;
|
|
50
|
+
private intentsClient: IntentsClient;
|
|
51
|
+
private metadataClient: MetadataClient;
|
|
52
|
+
private openClient: OpenClient;
|
|
53
|
+
private openedAppContext?: Context;
|
|
54
|
+
private openedAppContextHandled: boolean = false;
|
|
55
|
+
|
|
56
|
+
//TODO: we should enable passing multiple channelId to the ctor.
|
|
57
|
+
constructor(messageRouterClient: MessageRouter, channelFactory?: ChannelFactory) {
|
|
58
|
+
if (!window.composeui.fdc3.config || !window.composeui.fdc3.config.instanceId) {
|
|
59
|
+
throw new Error(ComposeUIErrors.InstanceIdNotFound);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// TODO: inject this directly instead of the messageRouter
|
|
63
|
+
this.channelFactory = channelFactory ?? new MessageRouterChannelFactory(messageRouterClient, window.composeui.fdc3.config.instanceId);
|
|
64
|
+
this.intentsClient = new MessageRouterIntentsClient(messageRouterClient, this.channelFactory);
|
|
65
|
+
this.metadataClient = new MessageRouterMetadataClient(messageRouterClient, window.composeui.fdc3.config);
|
|
66
|
+
this.openClient = new MessageRouterOpenClient(window.composeui.fdc3.config.instanceId!, messageRouterClient, window.composeui.fdc3.openAppIdentifier);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async open(app?: string | AppIdentifier, context?: Context): Promise<AppIdentifier> {
|
|
70
|
+
return await this.openClient.open(app, context);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async findIntent(intent: string, context?: Context, resultType?: string): Promise<AppIntent> {
|
|
74
|
+
return await this.intentsClient.findIntent(intent, context, resultType);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public async findIntentsByContext(context: Context, resultType?: string): Promise<Array<AppIntent>> {
|
|
78
|
+
return await this.intentsClient.findIntentsByContext(context, resultType);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public async findInstances(app: AppIdentifier): Promise<Array<AppIdentifier>> {
|
|
82
|
+
return await this.metadataClient.findInstances(app);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async broadcast(context: Context): Promise<void> {
|
|
86
|
+
if (!this.currentChannel) {
|
|
87
|
+
throw new Error(ComposeUIErrors.CurrentChannelNotSet);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return this.currentChannel.broadcast(context);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public async raiseIntent(intent: string, context: Context, app?: string | AppIdentifier): Promise<IntentResolution> {
|
|
94
|
+
return await this.intentsClient.raiseIntent(intent, context, app);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public async raiseIntentForContext(context: Context, app?: string | AppIdentifier): Promise<IntentResolution> {
|
|
98
|
+
return await this.intentsClient.raiseIntentForContext(context, app);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public async addIntentListener(intent: string, handler: IntentHandler): Promise<Listener> {
|
|
102
|
+
var listener = await this.channelFactory.getIntentListener(intent, handler);
|
|
103
|
+
this.intentListeners.push(listener);
|
|
104
|
+
return listener;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public async addContextListener(contextType?: string | null | ContextHandler, handler?: ContextHandler): Promise<Listener> {
|
|
108
|
+
if (contextType && typeof contextType != 'string') {
|
|
109
|
+
handler = contextType;
|
|
110
|
+
contextType = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (this.openedAppContext
|
|
114
|
+
&& handler
|
|
115
|
+
&& (contextType == this.openedAppContext?.type || this.openedAppContext.type == null || !this.openedAppContext.type)) {
|
|
116
|
+
|
|
117
|
+
if (this.openedAppContextHandled === false) {
|
|
118
|
+
handler(this.openedAppContext);
|
|
119
|
+
this.openedAppContextHandled = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!this.openedAppContext && this.openedAppContextHandled !== true) {
|
|
124
|
+
//There is no context to handle -aka app was not opened via the fdc3.open
|
|
125
|
+
this.openedAppContextHandled = true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const listener = <ComposeUIContextListener>await this.channelFactory.getContextListener(this.openedAppContextHandled, this.currentChannel, handler, contextType);
|
|
129
|
+
this.topLevelContextListeners.push(listener);
|
|
130
|
+
|
|
131
|
+
if (!this.currentChannel) {
|
|
132
|
+
return listener;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return await new Promise<Listener>((resolve) => {
|
|
136
|
+
resolve(listener);
|
|
137
|
+
}).finally(() => {
|
|
138
|
+
queueMicrotask(async () => await this.callHandlerOnChannelsCurrentContext(listener));
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public async getUserChannels(): Promise<Array<Channel>> {
|
|
143
|
+
return await this.channelFactory.getUserChannels();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public async joinUserChannel(channelId: string): Promise<void> {
|
|
147
|
+
if (this.currentChannel) {
|
|
148
|
+
//DesktopAgnet clients can listen on only one channel
|
|
149
|
+
await this.leaveCurrentChannel();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let channel = this.userChannels.find(innerChannel => innerChannel.id == channelId);
|
|
153
|
+
if (!channel) {
|
|
154
|
+
channel = await this.channelFactory.joinUserChannel(channelId);
|
|
155
|
+
|
|
156
|
+
if (!channel) {
|
|
157
|
+
throw new Error(ChannelError.NoChannelFound);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.addChannel(channel);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.currentChannel = channel;
|
|
164
|
+
|
|
165
|
+
for (const listener of this.topLevelContextListeners) {
|
|
166
|
+
await listener.subscribe(this.currentChannel.id, this.currentChannel.type)
|
|
167
|
+
.finally(() => {
|
|
168
|
+
queueMicrotask(async () => await this.callHandlerOnChannelsCurrentContext(listener));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public async getOrCreateChannel(channelId: string): Promise<Channel> {
|
|
174
|
+
let appChannel = this.appChannels.find(channel => channel.id == channelId);
|
|
175
|
+
if (appChannel) {
|
|
176
|
+
return appChannel;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
appChannel = await this.channelFactory.createAppChannel(channelId);
|
|
180
|
+
|
|
181
|
+
this.addChannel(appChannel!);
|
|
182
|
+
return appChannel!;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async createPrivateChannel(): Promise<PrivateChannel> {
|
|
186
|
+
return await this.channelFactory.createPrivateChannel();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public async getCurrentChannel(): Promise<Channel | null> {
|
|
190
|
+
return this.currentChannel ?? null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public async leaveCurrentChannel(): Promise<void> {
|
|
194
|
+
//The context listeners, that have been added through the `fdc3.addContextListener()` should unsubscribe
|
|
195
|
+
for (const listener of this.topLevelContextListeners) {
|
|
196
|
+
await listener.unsubscribe();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this.currentChannel = undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public async getInfo(): Promise<ImplementationMetadata> {
|
|
203
|
+
return await this.metadataClient.getInfo();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public async getAppMetadata(app: AppIdentifier): Promise<AppMetadata> {
|
|
207
|
+
return await this.metadataClient.getAppMetadata(app);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Deprecated, alias to getUserChannels
|
|
211
|
+
// https://fdc3.finos.org/docs/2.0/api/ref/DesktopAgent#getsystemchannels-deprecated
|
|
212
|
+
public getSystemChannels(): Promise<Channel[]> {
|
|
213
|
+
return this.getUserChannels();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Deprecated, alias to joinUserChannel
|
|
217
|
+
// https://fdc3.finos.org/docs/2.0/api/ref/DesktopAgent#joinchannel-deprecated
|
|
218
|
+
public joinChannel(channelId: string): Promise<void> {
|
|
219
|
+
return this.joinUserChannel(channelId);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public async getOpenedAppContext(): Promise<void> {
|
|
223
|
+
try {
|
|
224
|
+
this.openedAppContext = await this.openClient.getOpenedAppContext();
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error("The opened app via fdc3.open() could not retrieve the context: ", err);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private addChannel(channel: Channel): void {
|
|
231
|
+
if (channel == null) return;
|
|
232
|
+
switch (channel.type) {
|
|
233
|
+
case "app":
|
|
234
|
+
this.appChannels.push(channel);
|
|
235
|
+
break;
|
|
236
|
+
case "user":
|
|
237
|
+
this.userChannels.push(channel);
|
|
238
|
+
break;
|
|
239
|
+
case "private":
|
|
240
|
+
this.privateChannels.push(channel);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async callHandlerOnChannelsCurrentContext(listener: ComposeUIContextListener) : Promise<void> {
|
|
246
|
+
const lastContext = await this.currentChannel!.getCurrentContext(listener.contextType);
|
|
247
|
+
|
|
248
|
+
if (lastContext) {
|
|
249
|
+
await listener.handleContextMessage(lastContext);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|