From 9ca098c697582b32cbfd78c711183936cd38ae75 Mon Sep 17 00:00:00 2001
From: lilla28 <36889371+lilla28@users.noreply.github.com>
Date: Wed, 11 Sep 2024 10:21:51 +0300
Subject: [PATCH] fix(fdc3) - Fixed example applications, refactored channel
handling, added channel exception, separated user channel set reader
* fix(fdc3) - joinUserChannel exception, triggering the fdc3Ready Event when we joined to the channel if the channel Id is defined
* fix(app-channel) - Remove redundant check if the app channel has been created when calling the method, added exception
* fix(fdc3) - Remove unnecessary property
* fix(userchannelset) - Seperated user channel set reader, fixed example projects to act upon the fdc3Ready event
---
.../fdc3-chart-and-grid/js-chart/chart.js | 7 +-
.../main-view/services/mock-data.service.ts | 15 +-
.../fdc3-pricing-and-chat/js-chat/chat.js | 2 +-
.../js-pricing/pricing.js | 2 +-
.../js-order-book/package.json | 1 +
.../market-watch/market-watch.component.ts | 300 +++++++++---------
.../js-order-book/src/index.html | 2 -
.../js-order-book/src/styles.scss | 1 +
.../js-trader-app/package.json | 1 +
.../js-trader-app/src/index.html | 2 -
.../js-trader-app/src/styles.scss | 1 +
package-lock.json | 2 +
.../Contracts/RaiseIntentRequest.cs | 5 -
.../ServiceCollectionExtensions.cs | 5 +-
.../Fdc3DesktopAgent.cs | 118 ++-----
.../Fdc3StartupAction.cs | 50 ++-
.../Fdc3StartupProperties.cs | 2 +-
.../IUserChannelSetReader.cs | 27 ++
.../Internal/UserChannelSetReader.cs | 102 ++++++
.../EndToEndTests.cs | 9 +-
.../Fdc3DesktopAgentTests.cs | 35 +-
...3DesktopAgentMessageRouterService.Tests.cs | 42 +--
.../src/ComposeUIChannel.spec.ts | 30 +-
.../src/ComposeUIDesktopAgent.spec.ts | 4 +-
.../src/ComposeUIDesktopAgent.ts | 33 +-
.../src/ComposeUIIntentHandling.spec.ts | 24 +-
...mposeUIMessageRouterMetadataClient.spec.ts | 18 +-
src/fdc3/js/composeui-fdc3/src/index.ts | 34 +-
.../MessageRouterIntentsClient.ts | 2 +-
.../messages/Fdc3RaiseIntentRequest.ts | 1 -
.../Shell/Properties/launchSettings.json | 2 +-
src/shell/dotnet/Shell/appsettings.json | 3 -
32 files changed, 466 insertions(+), 416 deletions(-)
create mode 100644 src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/IUserChannelSetReader.cs
create mode 100644 src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Infrastructure/Internal/UserChannelSetReader.cs
diff --git a/examples/fdc3-chart-and-grid/js-chart/chart.js b/examples/fdc3-chart-and-grid/js-chart/chart.js
index 4a086af95..fa9c8cd66 100644
--- a/examples/fdc3-chart-and-grid/js-chart/chart.js
+++ b/examples/fdc3-chart-and-grid/js-chart/chart.js
@@ -12,14 +12,11 @@ let currentChannel;
NoDataToDisplay(Highcharts);
-window.addEventListener('load', function() {
+window.addEventListener('fdc3Ready', async() => {
chart = Highcharts.chart('container', {
chart: {
type: 'column',
- events: {
- load: requestData
- }
},
title: {
text: 'Monthly Sales Data'
@@ -43,6 +40,8 @@ window.addEventListener('load', function() {
data: []
}]
});
+
+ await requestData();
});
window.addEventListener('close', async function(){
diff --git a/examples/fdc3-chart-and-grid/js-datagrid/src/app/main-view/services/mock-data.service.ts b/examples/fdc3-chart-and-grid/js-datagrid/src/app/main-view/services/mock-data.service.ts
index 8c31996ae..4ae360f9c 100644
--- a/examples/fdc3-chart-and-grid/js-datagrid/src/app/main-view/services/mock-data.service.ts
+++ b/examples/fdc3-chart-and-grid/js-datagrid/src/app/main-view/services/mock-data.service.ts
@@ -20,12 +20,15 @@ export class MockDataService{
constructor(){
this.market = new Market();
- this.connecting = new Promise(async(resolve, reject) => {
- try{
- resolve(await this.checkFdc3Connection());
- } catch(err) {
- reject(err);
- };
+
+ window.addEventListener('fdc3Ready', () => {
+ this.connecting = new Promise(async(resolve, reject) => {
+ try{
+ resolve(await this.checkFdc3Connection());
+ } catch(err) {
+ reject(err);
+ };
+ });
});
interval(1000).subscribe(() => {
diff --git a/examples/fdc3-pricing-and-chat/js-chat/chat.js b/examples/fdc3-pricing-and-chat/js-chat/chat.js
index a6331b350..d539ecf91 100644
--- a/examples/fdc3-pricing-and-chat/js-chat/chat.js
+++ b/examples/fdc3-pricing-and-chat/js-chat/chat.js
@@ -91,7 +91,7 @@ window.app = function () {
}
}();
-window.addEventListener('load', async function () {
+window.addEventListener('fdc3Ready', async function () {
intentListener = await window.fdc3.addIntentListener("StartChat", window.app.handleChatIntent);
await window.fdc3.joinUserChannel("fdc3.channel.1");
diff --git a/examples/fdc3-pricing-and-chat/js-pricing/pricing.js b/examples/fdc3-pricing-and-chat/js-pricing/pricing.js
index ea2f40d38..69dd257df 100644
--- a/examples/fdc3-pricing-and-chat/js-pricing/pricing.js
+++ b/examples/fdc3-pricing-and-chat/js-pricing/pricing.js
@@ -14,7 +14,7 @@
import "bootstrap/dist/css/bootstrap.css";
-window.addEventListener('load', async function () {
+window.addEventListener('fdc3Ready', async function () {
const pricingForm = document.querySelector("#pricing");
await this.window.fdc3.joinUserChannel("fdc3.channel.1");
pricingForm.addEventListener('submit', app.submitPrice);
diff --git a/examples/fdc3-trade-simulator/js-order-book/package.json b/examples/fdc3-trade-simulator/js-order-book/package.json
index 6504125da..ac82e1ec8 100644
--- a/examples/fdc3-trade-simulator/js-order-book/package.json
+++ b/examples/fdc3-trade-simulator/js-order-book/package.json
@@ -20,6 +20,7 @@
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@finos/fdc3": "^2.0.0",
+ "material-icons": "^1.13.12",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
diff --git a/examples/fdc3-trade-simulator/js-order-book/src/app/components/market-watch/market-watch.component.ts b/examples/fdc3-trade-simulator/js-order-book/src/app/components/market-watch/market-watch.component.ts
index 58a592973..f1e2a33f7 100644
--- a/examples/fdc3-trade-simulator/js-order-book/src/app/components/market-watch/market-watch.component.ts
+++ b/examples/fdc3-trade-simulator/js-order-book/src/app/components/market-watch/market-watch.component.ts
@@ -212,185 +212,187 @@ export class MarketWatchComponent implements OnInit, OnDestroy{
}
async ngOnInit() {
- this.subject.next(
- {
- Symbol: undefined,
- DataSource: [...ELEMENT_DATA]
- });
-
- try {
- this.channel = await window.fdc3.getOrCreateChannel('tradeIdeasChannel');
- const listener = await this.channel.addContextListener("fdc3.trade", async(context, metadata) => {
- const data = context['data'];
- let symbols = ELEMENT_DATA.filter(x => x.Symbol == data.symbol && x.Children?.length && x.Children?.length > 0);
- const topic: string = "fdc3." + data.symbol + "." + data.trader;
-
- if (!symbols || symbols.length == 0) {
- await this.channel!.broadcast(
- {
- type: topic,
- result: {
- success: false,
- action: "BUY",
- error: "No symbol found."
- }
- });
-
- return;
- }
-
- if (data.action === 'BUY') {
- const sumQuantity = symbols.reduce((sum, current) => {
- if (current.Children && current.Children.length > 0) {
- let s: number = current.Children.reduce((t, currentSymbol) => {
- if(currentSymbol.AskSize) {
- return t + currentSymbol.AskSize;
- }
- return t + 0;
- }, 0);
-
- return sum + s;
- }
- return sum + 0;
- }, 0);
-
+ window.addEventListener('fdc3Ready', async() => {
+ this.subject.next(
+ {
+ Symbol: undefined,
+ DataSource: [...ELEMENT_DATA]
+ });
- if (sumQuantity < data.quantity) {
+ try {
+ this.channel = await window.fdc3.getOrCreateChannel('tradeIdeasChannel');
+ const listener = await this.channel.addContextListener("fdc3.trade", async(context, metadata) => {
+ const data = context['data'];
+ let symbols = ELEMENT_DATA.filter(x => x.Symbol == data.symbol && x.Children?.length && x.Children?.length > 0);
+ const topic: string = "fdc3." + data.symbol + "." + data.trader;
+
+ if (!symbols || symbols.length == 0) {
await this.channel!.broadcast(
{
type: topic,
result: {
success: false,
action: "BUY",
- error: "Too much ticks were requested; not enough symbols are available on the target."
+ error: "No symbol found."
}
});
return;
}
- let price: number = 0;
- let size = data.quantity;
- for (let element of ELEMENT_DATA) {
- if (size == 0) {
- break;
- }
-
- if (element.Symbol != data.symbol || !element.Children || element.Children.length <= 0) {
- continue;
- }
-
- if (element.Children.at(0) && element.Children.at(0)?.AskSize && element.Children.at(0)!.AskSize! >= data.quantity) {
- element.Children.at(0)!.AskSize! = element.Children.at(0)!.AskSize! - data.quantity;
- element.Children.at(0)!.LastTrade = data.timestamp;
- price = element.Children.at(0)!.AskPrice != undefined ? element.Children.at(0)!.AskPrice! * data.quantity : 0;
- size = 0;
-
- await this.channel!.broadcast({
- type: topic,
- result: {
- success: true,
- action: "BUY",
- tradePrice: price
- }
- });
-
- break;
+ if (data.action === 'BUY') {
+ const sumQuantity = symbols.reduce((sum, current) => {
+ if (current.Children && current.Children.length > 0) {
+ let s: number = current.Children.reduce((t, currentSymbol) => {
+ if(currentSymbol.AskSize) {
+ return t + currentSymbol.AskSize;
+ }
+ return t + 0;
+ }, 0);
+
+ return sum + s;
+ }
+ return sum + 0;
+ }, 0);
+
+
+ if (sumQuantity < data.quantity) {
+ await this.channel!.broadcast(
+ {
+ type: topic,
+ result: {
+ success: false,
+ action: "BUY",
+ error: "Too much ticks were requested; not enough symbols are available on the target."
+ }
+ });
+
+ return;
}
-
- for (let innerElement of element.Children) {
- if (innerElement.Symbol != data.symbol) {
+
+ let price: number = 0;
+ let size = data.quantity;
+ for (let element of ELEMENT_DATA) {
+ if (size == 0) {
+ break;
+ }
+
+ if (element.Symbol != data.symbol || !element.Children || element.Children.length <= 0) {
continue;
}
-
- if (innerElement.AskSize && innerElement.AskSize >= size) {
- price = price + size * (innerElement.AskPrice == undefined ? 0 : innerElement.AskPrice);
- innerElement.AskSize = innerElement.AskSize - size;
- innerElement.LastTrade = data.timestamp;
+
+ if (element.Children.at(0) && element.Children.at(0)?.AskSize && element.Children.at(0)!.AskSize! >= data.quantity) {
+ element.Children.at(0)!.AskSize! = element.Children.at(0)!.AskSize! - data.quantity;
+ element.Children.at(0)!.LastTrade = data.timestamp;
+ price = element.Children.at(0)!.AskPrice != undefined ? element.Children.at(0)!.AskPrice! * data.quantity : 0;
size = 0;
+
+ await this.channel!.broadcast({
+ type: topic,
+ result: {
+ success: true,
+ action: "BUY",
+ tradePrice: price
+ }
+ });
+
break;
- } else if (innerElement.AskSize && innerElement.AskSize < size && innerElement.AskSize != 0) {
- price = price + innerElement.AskSize * (innerElement.AskPrice == undefined ? 0 : innerElement.AskPrice);
- size = size - innerElement.AskSize;
- innerElement.AskSize = 0;
- innerElement.LastTrade = data.timestamp;
+ }
+
+ for (let innerElement of element.Children) {
+ if (innerElement.Symbol != data.symbol) {
+ continue;
+ }
+
+ if (innerElement.AskSize && innerElement.AskSize >= size) {
+ price = price + size * (innerElement.AskPrice == undefined ? 0 : innerElement.AskPrice);
+ innerElement.AskSize = innerElement.AskSize - size;
+ innerElement.LastTrade = data.timestamp;
+ size = 0;
+ break;
+ } else if (innerElement.AskSize && innerElement.AskSize < size && innerElement.AskSize != 0) {
+ price = price + innerElement.AskSize * (innerElement.AskPrice == undefined ? 0 : innerElement.AskPrice);
+ size = size - innerElement.AskSize;
+ innerElement.AskSize = 0;
+ innerElement.LastTrade = data.timestamp;
+ }
}
}
+
+ await this.channel!.broadcast({
+ type: topic,
+ result: {
+ success: true,
+ action: "BUY",
+ tradePrice: price
+ }
+ });
+
+ this.subject.next(
+ {
+ Symbol: data.symbol,
+ DataSource: ELEMENT_DATA
+ });
+
+ return;
}
- await this.channel!.broadcast({
- type: topic,
- result: {
- success: true,
- action: "BUY",
- tradePrice: price
+ //It's selling the symbols - probably on the highest seller value (as it's not defined in this poc)
+ let symbolElement: SymbolElement | undefined;
+
+ ELEMENT_DATA.forEach((symbol) => {
+ if (symbol.Symbol != data.symbol) {
+ return;
+ }
+ if (symbol.Children && symbol.Children.length > 0) {
+ symbolElement = symbol.Children.reduce((prev, current) => {
+ if (prev.BidPrice && current.BidPrice
+ && prev.BidPrice > current.BidPrice) {
+ return prev;
+ }
+
+ return current;
+ });
}
});
-
- this.subject.next(
+
+ if(symbolElement) {
+ symbolElement.BidSize = symbolElement.BidSize + data.quantity;
+ symbolElement.LastTrade = data.timestamp;
+ await this.channel!.broadcast(
+ {
+ type: topic,
+ result: {
+ success: true,
+ action: "SELL",
+ }
+ });
+ this.subject.next(
{
Symbol: data.symbol,
- DataSource: ELEMENT_DATA
+ DataSource: [...ELEMENT_DATA]
});
-
- return;
- }
-
- //It's selling the symbols - probably on the highest seller value (as it's not defined in this poc)
- let symbolElement: SymbolElement | undefined;
-
- ELEMENT_DATA.forEach((symbol) => {
- if (symbol.Symbol != data.symbol) {
- return;
- }
- if (symbol.Children && symbol.Children.length > 0) {
- symbolElement = symbol.Children.reduce((prev, current) => {
- if (prev.BidPrice && current.BidPrice
- && prev.BidPrice > current.BidPrice) {
- return prev;
+ } else {
+ await this.channel!.broadcast(
+ {
+ type: topic,
+ result: {
+ success: false,
+ action: "SELL",
+ error: "Trader is not able to place its symbol for selling."
}
-
- return current;
- });
+ });
}
+
+ return;
});
-
- if(symbolElement) {
- symbolElement.BidSize = symbolElement.BidSize + data.quantity;
- symbolElement.LastTrade = data.timestamp;
- await this.channel!.broadcast(
- {
- type: topic,
- result: {
- success: true,
- action: "SELL",
- }
- });
- this.subject.next(
- {
- Symbol: data.symbol,
- DataSource: [...ELEMENT_DATA]
- });
- } else {
- await this.channel!.broadcast(
- {
- type: topic,
- result: {
- success: false,
- action: "SELL",
- error: "Trader is not able to place its symbol for selling."
- }
- });
- }
-
- return;
- });
-
- this.listeners.push(listener);
-
- } catch (err) {
- console.error(err);
- }
+
+ this.listeners.push(listener);
+
+ } catch (err) {
+ console.error(err);
+ }
+ });
}
public displayedColumns: string[] = ['Symbol', 'Description', 'AskPrice', 'AskSize', 'BidPrice', 'BidSize', 'LastTrade'];
diff --git a/examples/fdc3-trade-simulator/js-order-book/src/index.html b/examples/fdc3-trade-simulator/js-order-book/src/index.html
index 64217d60e..b83f48940 100644
--- a/examples/fdc3-trade-simulator/js-order-book/src/index.html
+++ b/examples/fdc3-trade-simulator/js-order-book/src/index.html
@@ -7,8 +7,6 @@
-
-
diff --git a/examples/fdc3-trade-simulator/js-order-book/src/styles.scss b/examples/fdc3-trade-simulator/js-order-book/src/styles.scss
index cbdd175fc..ac947ea84 100644
--- a/examples/fdc3-trade-simulator/js-order-book/src/styles.scss
+++ b/examples/fdc3-trade-simulator/js-order-book/src/styles.scss
@@ -1,5 +1,6 @@
/* Morgan Stanley makes this available to you under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
@use "@angular/material" as mat;
+@import 'material-icons/iconfont/material-icons.css';
// Dark Theme
$dark-theme-primary: mat.define-palette(mat.$indigo-palette);
diff --git a/examples/fdc3-trade-simulator/js-trader-app/package.json b/examples/fdc3-trade-simulator/js-trader-app/package.json
index 3c719042c..ae4743dfc 100644
--- a/examples/fdc3-trade-simulator/js-trader-app/package.json
+++ b/examples/fdc3-trade-simulator/js-trader-app/package.json
@@ -21,6 +21,7 @@
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@finos/fdc3": "^2.0.3",
+ "material-icons": "^1.13.12",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
diff --git a/examples/fdc3-trade-simulator/js-trader-app/src/index.html b/examples/fdc3-trade-simulator/js-trader-app/src/index.html
index f1a08d773..64680c49c 100644
--- a/examples/fdc3-trade-simulator/js-trader-app/src/index.html
+++ b/examples/fdc3-trade-simulator/js-trader-app/src/index.html
@@ -7,8 +7,6 @@
-
-
diff --git a/examples/fdc3-trade-simulator/js-trader-app/src/styles.scss b/examples/fdc3-trade-simulator/js-trader-app/src/styles.scss
index 90d4ee007..481768b49 100644
--- a/examples/fdc3-trade-simulator/js-trader-app/src/styles.scss
+++ b/examples/fdc3-trade-simulator/js-trader-app/src/styles.scss
@@ -1 +1,2 @@
/* You can add global styles to this file, and also import other style files */
+@import 'material-icons/iconfont/material-icons.css';
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index beff8b87a..6cc47f13d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -138,6 +138,7 @@
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@finos/fdc3": "^2.0.0",
+ "material-icons": "^1.13.12",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
@@ -176,6 +177,7 @@
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@finos/fdc3": "^2.0.3",
+ "material-icons": "^1.13.12",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Contracts/RaiseIntentRequest.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Contracts/RaiseIntentRequest.cs
index d02040488..c2c14502a 100644
--- a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Contracts/RaiseIntentRequest.cs
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Contracts/RaiseIntentRequest.cs
@@ -38,11 +38,6 @@ internal sealed class RaiseIntentRequest
///
public string Intent { get; set; }
- ///
- /// Indicates that the client selected an instance or an app to start and resolve the raised intent.
- ///
- public bool Selected { get; set; }
-
///
/// Context for identifying more the specific app that should handle the raised intent.
///
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/DependencyInjection/ServiceCollectionExtensions.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/DependencyInjection/ServiceCollectionExtensions.cs
index 9be52d896..2a6fa60dd 100644
--- a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/DependencyInjection/ServiceCollectionExtensions.cs
@@ -13,10 +13,7 @@
*/
using MorganStanley.ComposeUI.Fdc3.DesktopAgent;
-using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal;
-using MorganStanley.ComposeUI.Messaging;
-using MorganStanley.ComposeUI.Messaging.Abstractions;
using MorganStanley.ComposeUI.ModuleLoader;
using MorganStanley.ComposeUI.Shell.Fdc3;
@@ -35,6 +32,8 @@ public static IServiceCollection AddFdc3DesktopAgent(
builderAction(builder);
}
+
+ serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
serviceCollection.AddTransient();
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3DesktopAgent.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3DesktopAgent.cs
index 98ad2832e..0b75fcda3 100644
--- a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3DesktopAgent.cs
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3DesktopAgent.cs
@@ -25,27 +25,19 @@
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Exceptions;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal;
+using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol;
+using MorganStanley.ComposeUI.Messaging;
using MorganStanley.ComposeUI.ModuleLoader;
+using AppChannel = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Channels.AppChannel;
using AppIdentifier = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIdentifier;
using AppIntent = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppIntent;
using AppMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.AppMetadata;
+using Constants = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal.Constants;
using ContextMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.ContextMetadata;
using Icon = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.Icon;
+using ImplementationMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.ImplementationMetadata;
using IntentMetadata = Finos.Fdc3.AppDirectory.IntentMetadata;
using Screenshot = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.Screenshot;
-using AppChannel = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Channels.AppChannel;
-using MorganStanley.ComposeUI.Messaging;
-using System.Text.Json;
-using System.IO.Abstractions;
-using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Converters;
-using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol;
-using System.Text.Json.Serialization;
-using DisplayMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.DisplayMetadata;
-using ImplementationMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.ImplementationMetadata;
-using Constants = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal.Constants;
-using FileSystem = System.IO.Abstractions.FileSystem;
-using System.Reflection;
-using System;
namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent;
@@ -53,6 +45,7 @@ internal class Fdc3DesktopAgent : IFdc3DesktopAgentBridge
{
private readonly ILogger _logger;
private readonly IResolverUICommunicator _resolverUI;
+ private readonly IUserChannelSetReader _userChannelSetReader;
private readonly ConcurrentDictionary _userChannels = new();
private readonly ConcurrentDictionary _privateChannels = new();
private readonly ConcurrentDictionary _appChannels = new();
@@ -64,27 +57,20 @@ internal class Fdc3DesktopAgent : IFdc3DesktopAgentBridge
private readonly ConcurrentDictionary _raisedIntentResolutions = new();
private readonly ConcurrentDictionary> _pendingStartRequests = new();
private IAsyncDisposable? _subscription;
- private readonly IFileSystem _fileSystem;
- private readonly HttpClient _httpClient = new();
- private ConcurrentDictionary? _userChannelSet;
- private readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web)
- {
- Converters = { new DisplayMetadataJsonConverter(), new JsonStringEnumConverter() }
- };
public Fdc3DesktopAgent(
IAppDirectory appDirectory,
IModuleLoader moduleLoader,
IOptions options,
IResolverUICommunicator resolverUI,
- IFileSystem? fileSystem = null,
+ IUserChannelSetReader userChannelSetReader,
ILoggerFactory? loggerFactory = null)
{
_appDirectory = appDirectory;
_moduleLoader = moduleLoader;
_options = options.Value;
_resolverUI = resolverUI;
- _fileSystem = fileSystem ?? new FileSystem();
+ _userChannelSetReader = userChannelSetReader;
_loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
_logger = _loggerFactory.CreateLogger() ?? NullLogger.Instance;
}
@@ -92,7 +78,9 @@ public Fdc3DesktopAgent(
public async ValueTask AddUserChannel(Func addUserChannelFactory, string channelId)
{
ChannelItem? channelItem = null;
- if (_userChannelSet != null && !_userChannelSet.TryGetValue(channelId, out channelItem) || channelItem == null)
+ var userChannelSet = await _userChannelSetReader.GetUserChannelSet();
+
+ if (!userChannelSet.TryGetValue(channelId, out channelItem) || channelItem == null)
{
return null;
}
@@ -163,6 +151,13 @@ public async ValueTask AddAppChannel(Func> AddIntentListe
};
}
- public ValueTask GetUserChannels(GetUserChannelsRequest? request)
+ public async ValueTask GetUserChannels(GetUserChannelsRequest? request)
{
if (request == null)
{
- return ValueTask.FromResult(GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.PayloadNull));
+ return GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.PayloadNull);
}
if (!Guid.TryParse(request.InstanceId, out var instanceId))
{
- return ValueTask.FromResult(GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.MissingId));
+ return GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.MissingId);
}
if (!_runningModules.TryGetValue(instanceId, out _))
{
- return ValueTask.FromResult(GetUserChannelsResponse.Failure(ChannelError.AccessDenied));
+ return GetUserChannelsResponse.Failure(ChannelError.AccessDenied);
}
- var result = _userChannelSet?.Values;
+ var result = (await _userChannelSetReader.GetUserChannelSet()).Values;
if (result == null)
{
- return ValueTask.FromResult(GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.NoUserChannelSetFound));
+ return GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.NoUserChannelSetFound);
}
- return ValueTask.FromResult(GetUserChannelsResponse.Success(result));
+ return GetUserChannelsResponse.Success(result);
}
public async ValueTask JoinUserChannel(Func addUserChannelFactory, JoinUserChannelRequest request)
{
if (!Guid.TryParse(request.InstanceId, out var id) || !_runningModules.TryGetValue(id, out _))
{
- return JoinUserChannelResponse.Failed(ChannelError.AccessDenied);
+ return JoinUserChannelResponse.Failed(Fdc3DesktopAgentErrors.MissingId);
}
+ var userChannelSet = await _userChannelSetReader.GetUserChannelSet();
ChannelItem? channelItem = null;
- if (_userChannelSet != null && !_userChannelSet.TryGetValue(request.ChannelId, out channelItem))
+ if (!userChannelSet.TryGetValue(request.ChannelId, out channelItem))
{
return JoinUserChannelResponse.Failed(ChannelError.NoChannelFound);
}
@@ -550,7 +544,7 @@ public ValueTask GetUserChannels(GetUserChannelsRequest
if (channelItem != null)
{
- return JoinUserChannelResponse.Joined((DisplayMetadata)channelItem.DisplayMetadata);
+ return JoinUserChannelResponse.Joined(channelItem.DisplayMetadata);
}
return JoinUserChannelResponse.Joined();
@@ -662,11 +656,6 @@ public async ValueTask> RaiseIntent(Raise
//else for consistency it will return a single element array containing the intentMetadata which is allowed by the request.
Func, IEnumerable>?> selector = (fdc3App, appIntents) =>
{
- //If the user selects an application from the AppDirectory instead of the its running instance
- if (request.Selected && appIntents.TryGetValue(request.Intent, out var result) && result.Apps.Any())
- {
- return null;
- }
if (fdc3App.Interop?.Intents?.ListensFor == null
|| !fdc3App.Interop.Intents.ListensFor.TryGetValue(request.Intent!, out var intentMetadata))
@@ -1222,55 +1211,6 @@ private AppMetadata GetAppMetadata(Fdc3App app, string? instanceId, IntentMetada
};
}
- private async Task?> ReadUserChannelSet(CancellationToken cancellationToken = default)
- {
- var uri = _options.UserChannelConfigFile;
- IEnumerable? userChannelSet = null;
-
- if (uri == null && _options.UserChannelConfig == null)
- {
- var assembly = Assembly.GetExecutingAssembly();
- using var stream = assembly.GetManifestResourceStream(ResourceNames.DefaultUserChannelSet);
- if (stream == null)
- {
- return null;
- }
-
- using var streamReader = new StreamReader(stream);
-
- var result = streamReader.ReadToEnd();
- userChannelSet = JsonSerializer.Deserialize(result, _jsonSerializerOptions);
- }
- else if (_options.UserChannelConfig != null)
- {
- userChannelSet = _options.UserChannelConfig;
- }
- else if (uri != null)
- {
- if (uri.IsFile)
- {
- var path = uri.IsAbsoluteUri ? uri.AbsolutePath : Path.GetFullPath(uri.ToString());
-
- if (!_fileSystem.File.Exists(path))
- {
- _logger.LogError($"{Fdc3DesktopAgentErrors.NoUserChannelSetFound}, no user channel set was configured.");
- return null;
- }
-
- await using var stream = _fileSystem.File.OpenRead(path);
- userChannelSet = JsonSerializer.Deserialize(stream, _jsonSerializerOptions);
- }
- else if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
- {
- var response = await _httpClient.GetAsync(uri, cancellationToken);
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
- userChannelSet = JsonSerializer.Deserialize(stream, _jsonSerializerOptions);
- }
- }
-
- return new((userChannelSet ?? Array.Empty()).ToDictionary(x => x.Id, y => y));
- }
-
private Task RemoveModuleAsync(IModuleInstance instance)
{
if (!IsFdc3StartedModule(instance, out var fdc3InstanceId))
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupAction.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupAction.cs
index 20937a4eb..40c55d7ce 100644
--- a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupAction.cs
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupAction.cs
@@ -18,21 +18,25 @@
using ResourceReader = MorganStanley.ComposeUI.Utilities.ResourceReader;
using Microsoft.Extensions.Options;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
+using System.Text;
namespace MorganStanley.ComposeUI.Shell.Fdc3;
internal sealed class Fdc3StartupAction : IStartupAction
{
private readonly IAppDirectory _appDirectory;
+ private readonly IUserChannelSetReader _userChannelSetReader;
private readonly Fdc3DesktopAgentOptions _options;
private readonly ILogger _logger;
public Fdc3StartupAction(
IAppDirectory appDirectory,
+ IUserChannelSetReader userChannelSetReader,
IOptions options,
ILogger? logger = null)
{
_appDirectory = appDirectory;
+ _userChannelSetReader = userChannelSetReader;
_options = options.Value;
_logger = logger ?? NullLogger.Instance;
}
@@ -45,29 +49,43 @@ public async Task InvokeAsync(StartupContext startupContext, Func next)
try
{
var appId = (await _appDirectory.GetApp(startupContext.StartRequest.ModuleId)).AppId;
+ var userChannelSet = await _userChannelSetReader.GetUserChannelSet();
var fdc3InstanceId = startupContext.StartRequest.Parameters.FirstOrDefault(parameter => parameter.Key == Fdc3StartupParameters.Fdc3InstanceId).Value ?? Guid.NewGuid().ToString();
- var channelId = _options.ChannelId ?? "default";
+
+ //TODO: decide if we want to join to a channel automatically on startup of an fdc3 module
+ var channelId = _options.ChannelId ?? userChannelSet.FirstOrDefault().Key;
+
var fdc3StartupProperties = new Fdc3StartupProperties() { InstanceId = fdc3InstanceId, ChannelId = channelId };
fdc3InstanceId = startupContext.GetOrAddProperty(_ => fdc3StartupProperties).InstanceId;
var webProperties = startupContext.GetOrAddProperty();
- webProperties
- .ScriptProviders.Add(_ =>
- new ValueTask(
- $$"""
- window.composeui.fdc3 = {
- ...window.composeui.fdc3,
- config: {
- appId: "{{appId}}",
- instanceId: "{{fdc3InstanceId}}"
- },
- channelId: "{{channelId}}"
- };
- """));
+ var stringBuilder = new StringBuilder();
+ stringBuilder.Append($$"""
+ window.composeui.fdc3 = {
+ ...window.composeui.fdc3,
+ config: {
+ appId: "{{appId}}",
+ instanceId: "{{fdc3InstanceId}}"
+ }
+ """);
- webProperties
- .ScriptProviders.Add(_ => new ValueTask(ResourceReader.ReadResource(ResourceNames.Fdc3Bundle)));
+ if (channelId != null)
+ {
+ stringBuilder.Append($$"""
+ ,
+ channelId: "{{channelId}}"
+ """);
+ }
+
+ stringBuilder.Append($$"""
+ };
+ """);
+
+ stringBuilder.AppendLine();
+ stringBuilder.Append(ResourceReader.ReadResource(ResourceNames.Fdc3Bundle));
+
+ webProperties.ScriptProviders.Add(_ => new ValueTask(stringBuilder.ToString()));
}
catch (AppNotFoundException exception)
{
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupProperties.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupProperties.cs
index acb7a3c73..6c0a67158 100644
--- a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupProperties.cs
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Fdc3StartupProperties.cs
@@ -24,5 +24,5 @@ internal class Fdc3StartupProperties
///
/// Id of the channel the opened app should join
///
- public string ChannelId { get; init; }
+ public string? ChannelId { get; init; }
}
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/IUserChannelSetReader.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/IUserChannelSetReader.cs
new file mode 100644
index 000000000..9a3bf8e8c
--- /dev/null
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/IUserChannelSetReader.cs
@@ -0,0 +1,27 @@
+/*
+ * Morgan Stanley makes this available to you under the Apache License,
+ * Version 2.0 (the "License"). You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. Unless required by applicable law or agreed
+ * to in writing, software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol;
+
+namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent;
+
+public interface IUserChannelSetReader
+{
+ ///
+ /// Reads the defined user channel set.
+ ///
+ ///
+ ///
+ public Task> GetUserChannelSet(CancellationToken cancellationToken = default);
+}
diff --git a/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Infrastructure/Internal/UserChannelSetReader.cs b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Infrastructure/Internal/UserChannelSetReader.cs
new file mode 100644
index 000000000..47eddc457
--- /dev/null
+++ b/src/fdc3/dotnet/DesktopAgent/src/MorganStanley.ComposeUI.DesktopAgent/Infrastructure/Internal/UserChannelSetReader.cs
@@ -0,0 +1,102 @@
+/*
+ * Morgan Stanley makes this available to you under the Apache License,
+ * Version 2.0 (the "License"). You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. Unless required by applicable law or agreed
+ * to in writing, software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+
+using System.Collections.Immutable;
+using System.IO.Abstractions;
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Converters;
+using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
+using MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol;
+
+namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Infrastructure.Internal;
+
+internal class UserChannelSetReader : IUserChannelSetReader, IDisposable
+{
+ private readonly Fdc3DesktopAgentOptions _options;
+ private readonly IFileSystem _fileSystem;
+ private readonly HttpClient _httpClient;
+ private readonly ILogger _logger;
+ private IReadOnlyDictionary? _userChannelSet;
+ private readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web)
+ {
+ Converters = { new DisplayMetadataJsonConverter(), new JsonStringEnumConverter() }
+ };
+
+ public UserChannelSetReader(
+ IOptions options,
+ IFileSystem? fileSystem = null,
+ ILogger? logger = null)
+ {
+ _options = options.Value;
+ _fileSystem = fileSystem ?? new FileSystem();
+ _httpClient = new HttpClient();
+ _logger = logger ?? NullLogger.Instance;
+ }
+
+ public void Dispose()
+ {
+ _httpClient.Dispose();
+ }
+
+ public async Task> GetUserChannelSet(CancellationToken cancellationToken = default)
+ {
+ if (_userChannelSet != null)
+ {
+ return _userChannelSet;
+ }
+
+ var uri = _options.UserChannelConfigFile;
+
+ if (uri == null && _options.UserChannelConfig == null)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ using var stream = assembly.GetManifestResourceStream(ResourceNames.DefaultUserChannelSet);
+ if (stream != null)
+ {
+ var userChannels = JsonSerializer.Deserialize(stream, _jsonSerializerOptions);
+ _userChannelSet = userChannels?.ToDictionary(x => x.Id, y => y);
+ }
+ }
+ else if (_options.UserChannelConfig != null)
+ {
+ _userChannelSet = _options.UserChannelConfig.ToDictionary(x => x.Id, y => y);
+ }
+ else if (uri != null)
+ {
+ if (uri.IsFile)
+ {
+ var path = uri.IsAbsoluteUri ? uri.AbsolutePath : Path.GetFullPath(uri.ToString());
+
+ if (_fileSystem.File.Exists(path))
+ {
+ await using var stream = _fileSystem.File.OpenRead(path);
+ _userChannelSet = (JsonSerializer.Deserialize(stream, _jsonSerializerOptions))?.ToDictionary(x => x.Id, y => y);
+ }
+ }
+ else if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
+ {
+ var response = await _httpClient.GetAsync(uri, cancellationToken);
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ _userChannelSet = (JsonSerializer.Deserialize(stream, _jsonSerializerOptions))?.ToDictionary(x => x.Id, y => y);
+ }
+ }
+
+ return _userChannelSet ?? ImmutableDictionary.Empty;
+ }
+}
diff --git a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/EndToEndTests.cs b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/EndToEndTests.cs
index ac9a94b47..952b98849 100644
--- a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/EndToEndTests.cs
+++ b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/EndToEndTests.cs
@@ -460,7 +460,6 @@ public async Task RaiseIntentReturnsNoAppsFound()
MessageId = 2,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "noIntentShouldHandle",
- Selected = false,
Context = new Context(ContextTypes.Nothing)
};
@@ -509,7 +508,6 @@ public async Task RaiseIntentReturnsAppIntentWithOneExistingAppAndPublishesConte
MessageId = 2,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -575,7 +573,6 @@ public async Task StoreIntentResultReturnsSuccessfully()
MessageId = 2,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -708,7 +705,6 @@ public async Task GetIntentResultReturnsSuccessfully()
MessageId = 2,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -819,7 +815,6 @@ public async Task AddIntentListenerSubscribesWithExistingAppPerRaisedIntent()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
},
@@ -1045,7 +1040,7 @@ public async Task GetUserChannelsSucceeds()
[Fact]
- public async Task JoinUserChannelReturnsAccessDenied()
+ public async Task JoinUserChannelReturnsMissingId()
{
var request = new JoinUserChannelRequest
{
@@ -1060,7 +1055,7 @@ public async Task JoinUserChannelReturnsAccessDenied()
var result = response!.ReadJson(_options);
result.Should().NotBeNull();
- result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(ChannelError.AccessDenied));
+ result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(Fdc3DesktopAgentErrors.MissingId));
}
[Fact]
diff --git a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Fdc3DesktopAgentTests.cs b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Fdc3DesktopAgentTests.cs
index 5844e1cc3..bb742c34c 100644
--- a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Fdc3DesktopAgentTests.cs
+++ b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Fdc3DesktopAgentTests.cs
@@ -38,6 +38,7 @@
using Icon = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.Icon;
using ImplementationMetadata = MorganStanley.ComposeUI.Fdc3.DesktopAgent.Protocol.ImplementationMetadata;
using System.Collections.Concurrent;
+using System.Collections.Immutable;
namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests;
@@ -57,12 +58,14 @@ public class Fdc3DesktopAgentTests : IAsyncLifetime
public Fdc3DesktopAgentTests()
{
+ var options = new Fdc3DesktopAgentOptions();
+
_fdc3 = new Fdc3DesktopAgent(
_appDirectory,
_mockModuleLoader.Object,
- new Fdc3DesktopAgentOptions(),
+ options,
_mockResolverUICommunicator.Object,
- null,
+ new UserChannelSetReader(options),
NullLoggerFactory.Instance);
}
@@ -258,7 +261,6 @@ public async Task GetIntentResult_returns()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -320,7 +322,6 @@ public async Task GetIntentResult_fails()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -395,7 +396,6 @@ public async Task StoreIntentResult_returns()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -448,7 +448,6 @@ public async Task AddIntentListener_subscribes()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -490,7 +489,6 @@ public async Task AddIntentListener_unsubscribes()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -540,7 +538,6 @@ public async Task RaiseIntent_returns_NoAppsFound()
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "noAppShouldReturn",
- Selected = false,
Context = new Context("context2")
};
@@ -557,7 +554,6 @@ public async Task RaiseIntent_calls_ResolverUI()
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context(ContextTypes.Nothing)
};
@@ -595,7 +591,6 @@ public async Task RaiseIntent_returns_one_running_app()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -692,15 +687,19 @@ public async Task GetUserChannels_returns_access_denied_error()
}
[Fact]
- public async Task GetUserChannels_returns_no_user_channel_set_configured_error()
+ public async Task GetUserChannels_returns_empty_userChannel_set()
{
+ var options = new Fdc3DesktopAgentOptions
+ {
+ UserChannelConfigFile = new Uri("C://hello/world/test.json"),
+ };
+
var fdc3 = new Fdc3DesktopAgent(
_appDirectory,
_mockModuleLoader.Object,
- new Fdc3DesktopAgentOptions
- {
- UserChannelConfigFile = new Uri("C://hello/world/test.json"),
- }, _mockResolverUICommunicator.Object);
+ options,
+ _mockResolverUICommunicator.Object,
+ new UserChannelSetReader(options));
await fdc3.StartAsync(CancellationToken.None);
@@ -716,7 +715,7 @@ public async Task GetUserChannels_returns_no_user_channel_set_configured_error()
var result = await fdc3.GetUserChannels(request);
result.Should().NotBeNull();
- result.Should().BeEquivalentTo(GetUserChannelsResponse.Failure(Fdc3DesktopAgentErrors.NoUserChannelSetFound));
+ result.Should().BeEquivalentTo(GetUserChannelsResponse.Success(Enumerable.Empty()));
}
[Fact]
@@ -749,12 +748,12 @@ public async Task GetUserChannels_succeeds()
}
[Fact]
- public async Task JoinUserChannel_returns_access_denied_error_as_instance_id_not_found()
+ public async Task JoinUserChannel_returns_missing_id_error_as_instance_id_not_found()
{
var result = await _fdc3.JoinUserChannel((channelId) => new UserChannel(channelId, new Mock().Object, null), new() { InstanceId = Guid.NewGuid().ToString(), ChannelId = "test"});
result.Should().NotBeNull();
- result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(ChannelError.AccessDenied));
+ result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(Fdc3DesktopAgentErrors.MissingId));
}
[Fact]
diff --git a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs
index 68a22f6c6..752bed751 100644
--- a/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs
+++ b/src/fdc3/dotnet/DesktopAgent/test/MorganStanley.ComposeUI.DesktopAgent.Tests/Infrastructure/Internal/Fdc3DesktopAgentMessageRouterService.Tests.cs
@@ -56,18 +56,20 @@ public class Fdc3DesktopAgentMessageRouterServiceTests : IAsyncLifetime
public Fdc3DesktopAgentMessageRouterServiceTests()
{
+ var options = new Fdc3DesktopAgentOptions()
+ {
+ IntentResultTimeout = TimeSpan.FromMilliseconds(100),
+ ListenerRegistrationTimeout = TimeSpan.FromMilliseconds(100)
+ };
+
_fdc3 = new Fdc3DesktopAgentMessageRouterService(
_mockMessageRouter.Object,
new Fdc3DesktopAgent(
_appDirectory,
_mockModuleLoader.Object,
- new Fdc3DesktopAgentOptions()
- {
- IntentResultTimeout = TimeSpan.FromMilliseconds(100),
- ListenerRegistrationTimeout = TimeSpan.FromMilliseconds(100)
- },
+ options,
_mockResolverUICommunicator.Object,
- null,
+ new UserChannelSetReader(options),
NullLoggerFactory.Instance),
new Fdc3DesktopAgentOptions(),
NullLoggerFactory.Instance);
@@ -125,7 +127,6 @@ public async Task HandleRaiseIntent_returns_IntentDeliveryFailed_error_as_no_int
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4" }
};
@@ -154,7 +155,6 @@ public async Task HandleRaiseIntent_returns_IntentDeliveryFailed_error_as_no_int
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom")
};
@@ -194,7 +194,6 @@ public async Task
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -253,7 +252,6 @@ public async Task
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -276,13 +274,18 @@ public async Task
[Fact]
public async Task HandleRaiseIntent_calls_ResolverUI_by_Context_filter()
{
+ await _fdc3.StartAsync(CancellationToken.None);
+
+ //TODO: should add some identifier to the query => "fdc3:" + instance.Manifest.Id
+ var origin = await _mockModuleLoader.Object.StartModule(new StartRequest("appId4"));
+ var originFdc3InstanceId = Fdc3InstanceIdRetriever.Get(origin);
+
var instanceId = Guid.NewGuid().ToString();
var raiseIntentRequest = new RaiseIntentRequest
{
MessageId = 1,
Fdc3InstanceId = instanceId,
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2")
};
@@ -299,7 +302,6 @@ public async Task HandleRaiseIntent_calls_ResolverUI_by_Context_filter_if_fdc3_n
MessageId = 1,
Fdc3InstanceId = instanceId,
Intent = "intentMetadata4",
- Selected = false,
Context = new Context(ContextTypes.Nothing)
};
@@ -315,7 +317,6 @@ public async Task HandleRaiseIntent_fails_as_no_apps_found_by_AppIdentifier()
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "testIntent",
- Selected = false,
Context = new Context("contextType"),
TargetAppIdentifier = new AppIdentifier { AppId = "noAppShouldReturn" }
};
@@ -333,7 +334,6 @@ public async Task HandleRaiseIntent_fails_as_no_apps_found_by_Context()
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("noAppShouldReturn")
};
@@ -350,7 +350,6 @@ public async Task HandleRaiseIntent_fails_as_no_apps_found_by_Intent()
MessageId = 1,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "noAppShouldReturn",
- Selected = false,
Context = new Context("context2")
};
@@ -412,7 +411,6 @@ public async Task HandleStoreIntentResult_succeeds_with_channel()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -462,7 +460,6 @@ public async Task HandleStoreIntentResult_succeeds_with_context()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = true,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -513,7 +510,6 @@ public async Task HandleStoreIntentResult_succeeds_with_voidResult()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -608,7 +604,6 @@ public async Task HandleGetIntentResult_fails_due_no_intent_found()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -671,7 +666,6 @@ public async Task HandleGetIntentResult_succeeds_with_context()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -734,7 +728,6 @@ public async Task HandleGetIntentResult_succeeds_with_channel()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -798,7 +791,6 @@ public async Task HandleGetIntentResult_succeeds_with_voidResult()
MessageId = int.MaxValue,
Fdc3InstanceId = Guid.NewGuid().ToString(),
Intent = "intentMetadata4",
- Selected = false,
Context = new Context("context2"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -887,7 +879,6 @@ public async Task HandleAddIntentListener_subscribes_to_existing_raised_intent()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -936,7 +927,6 @@ public async Task HandleAddIntentListener_subscribes()
MessageId = 1,
Fdc3InstanceId = originFdc3InstanceId,
Intent = "intentMetadataCustom",
- Selected = false,
Context = new Context("contextCustom"),
TargetAppIdentifier = new AppIdentifier { AppId = "appId4", InstanceId = targetFdc3InstanceId }
};
@@ -1091,7 +1081,7 @@ public async Task HandleGetUserChannels_succeeds()
}
[Fact]
- public async Task HandleJoinUserChannel_returns_access_denied_error_as_instance_id_not_found()
+ public async Task HandleJoinUserChannel_returns_missing_Id_error_as_instance_id_not_found()
{
var request = new JoinUserChannelRequest
{
@@ -1101,7 +1091,7 @@ public async Task HandleJoinUserChannel_returns_access_denied_error_as_instance_
var result = await _fdc3.HandleJoinUserChannel(request, new());
result.Should().NotBeNull();
- result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(ChannelError.AccessDenied));
+ result.Should().BeEquivalentTo(JoinUserChannelResponse.Failed(Fdc3DesktopAgentErrors.MissingId));
}
[Fact]
diff --git a/src/fdc3/js/composeui-fdc3/src/ComposeUIChannel.spec.ts b/src/fdc3/js/composeui-fdc3/src/ComposeUIChannel.spec.ts
index a848c5495..52a9ae2e8 100644
--- a/src/fdc3/js/composeui-fdc3/src/ComposeUIChannel.spec.ts
+++ b/src/fdc3/js/composeui-fdc3/src/ComposeUIChannel.spec.ts
@@ -120,27 +120,6 @@ describe("AppChanel tests", () => {
}
};
});
-
- it("getOrCreateChannel returns successfully after it found the AppChannel in the cache", async () => {
- const messageRouterClientMock = {
- clientId: "dummy",
- subscribe: jest.fn(() => {
- return Promise.resolve({unsubscribe: () => {}});}),
-
- publish: jest.fn(() => { return Promise.resolve() }),
- connect: jest.fn(() => { return Promise.resolve() }),
- registerEndpoint: jest.fn(() => { return Promise.resolve() }),
- unregisterEndpoint: jest.fn(() => { return Promise.resolve() }),
- registerService: jest.fn(() => { return Promise.resolve() }),
- unregisterService: jest.fn(() => { return Promise.resolve() }),
- invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ found: true })}`) })
- };
-
- const desktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
- const channel1 = await desktopAgent.getOrCreateChannel("hello.world");
- const channel2 = await desktopAgent.getOrCreateChannel("hello.world");
- expect(channel2).toBe(channel1);
- });
it("getOrCreateChannel creates a channel", async () => {
let messageRouterClientMock: MessageRouter = {
@@ -153,10 +132,9 @@ describe("AppChanel tests", () => {
registerService: jest.fn(() => { return Promise.resolve() }),
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(undefined)})
- .mockImplementationOnce(() => Promise.resolve(JSON.stringify({ found: false })))
.mockImplementationOnce(() => Promise.resolve(JSON.stringify({ success: true })))
};
- const desktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
const channel = await desktopAgent.getOrCreateChannel("hello.world");
expect(channel).toBeInstanceOf(ComposeUIChannel);
});
@@ -172,11 +150,10 @@ describe("AppChanel tests", () => {
registerService: jest.fn(() => { return Promise.resolve() }),
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(undefined)})
- .mockImplementationOnce(() => Promise.resolve(JSON.stringify({ found: false })))
.mockImplementationOnce(() => Promise.resolve(JSON.stringify({ success: false, error: "dummy" })))
};
- const desktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getOrCreateChannel("hello.world"))
.rejects
.toThrow("dummy");
@@ -193,11 +170,10 @@ describe("AppChanel tests", () => {
registerService: jest.fn(() => { return Promise.resolve() }),
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(undefined)})
- .mockImplementationOnce(() => Promise.resolve(JSON.stringify({ found: false })))
.mockImplementationOnce(() => Promise.resolve(JSON.stringify({ success: false })))
};
- const desktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getOrCreateChannel("hello.world"))
.rejects
.toThrow(ChannelError.CreationFailed);
diff --git a/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.spec.ts b/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.spec.ts
index 71555685f..f8e47047a 100644
--- a/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.spec.ts
+++ b/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.spec.ts
@@ -80,14 +80,14 @@ describe('Tests for ComposeUIDesktopAgent implementation API', () => {
getUserChannels: jest.fn(() => Promise.reject("Not implemented")),
};
- desktopAgent = new ComposeUIDesktopAgent(dummyChannelId, messageRouterClient, channelFactory);
+ desktopAgent = new ComposeUIDesktopAgent(messageRouterClient, channelFactory);
await desktopAgent.joinUserChannel(dummyChannelId);
await new Promise(f => setTimeout(f, 100));
});
it('ComposeUIDesktopAgent could not be created as no instanceId found on window object', async () => {
window.composeui.fdc3.config = undefined;
- expect(() => new ComposeUIDesktopAgent(dummyChannelId, messageRouterClient))
+ expect(() => new ComposeUIDesktopAgent(messageRouterClient))
.toThrowError(ComposeUIErrors.InstanceIdNotFound);
});
diff --git a/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.ts b/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.ts
index 10b9d99c9..5dd9de99d 100644
--- a/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.ts
+++ b/src/fdc3/js/composeui-fdc3/src/ComposeUIDesktopAgent.ts
@@ -37,18 +37,6 @@ import { IntentsClient } from './infrastructure/IntentsClient';
import { MetadataClient } from './infrastructure/MetadataClient';
import { MessageRouterMetadataClient } from './infrastructure/MessageRouterMetadataClient';
-declare global {
- interface Window {
- composeui: {
- fdc3: {
- config: AppIdentifier | undefined;
- channelId : string;
- }
- }
- fdc3: DesktopAgent;
- }
-}
-
export class ComposeUIDesktopAgent implements DesktopAgent {
private appChannels: Channel[] = [];
private userChannels: Channel[] = [];
@@ -61,7 +49,7 @@ export class ComposeUIDesktopAgent implements DesktopAgent {
private metadataClient: MetadataClient;
//TODO: we should enable passing multiple channelId to the ctor.
- constructor(channelId: string, messageRouterClient: MessageRouter, channelFactory?: ChannelFactory) {
+ constructor(messageRouterClient: MessageRouter, channelFactory?: ChannelFactory) {
if (!window.composeui.fdc3.config || !window.composeui.fdc3.config.instanceId) {
throw new Error(ComposeUIErrors.InstanceIdNotFound);
}
@@ -70,12 +58,6 @@ export class ComposeUIDesktopAgent implements DesktopAgent {
this.channelFactory = channelFactory ?? new MessageRouterChannelFactory(messageRouterClient, window.composeui.fdc3.config.instanceId);
this.intentsClient = new MessageRouterIntentsClient(messageRouterClient, this.channelFactory);
this.metadataClient = new MessageRouterMetadataClient(messageRouterClient, window.composeui.fdc3.config);
-
- setTimeout(
- async () => {
- window.fdc3 = this;
- window.dispatchEvent(new Event("fdc3Ready"));
- }, 0);
}
//TODO
@@ -145,9 +127,11 @@ export class ComposeUIDesktopAgent implements DesktopAgent {
return await this.channelFactory.getUserChannels();
}
+ //TODO: add pending context listeners which were registered via the fdc3.addContextListener
public async joinUserChannel(channelId: string): Promise {
if (this.currentChannel) {
- return;
+ //DesktopAgnet clients can listen on only one channel
+ await this.leaveCurrentChannel();
}
let channel = this.userChannels.find(innerChannel => innerChannel.id == channelId);
@@ -169,13 +153,7 @@ export class ComposeUIDesktopAgent implements DesktopAgent {
return appChannel;
}
- try {
- appChannel = await this.channelFactory.getChannel(channelId, "app");
- } catch (err) {
- if (!appChannel) {
- appChannel = await this.channelFactory.createAppChannel(channelId);
- }
- }
+ appChannel = await this.channelFactory.createAppChannel(channelId);
this.addChannel(appChannel!);
return appChannel!;
@@ -190,6 +168,7 @@ export class ComposeUIDesktopAgent implements DesktopAgent {
}
//TODO: add messageRouter message that we are leaving the current channel to notify the backend.
+ //TODO: leave the current channel's listeners added via fdc3.addContextListener.
public async leaveCurrentChannel(): Promise {
this.currentChannel = undefined;
this.currentChannelListeners.forEach(listener => {
diff --git a/src/fdc3/js/composeui-fdc3/src/ComposeUIIntentHandling.spec.ts b/src/fdc3/js/composeui-fdc3/src/ComposeUIIntentHandling.spec.ts
index aa6697f6b..103ee9759 100644
--- a/src/fdc3/js/composeui-fdc3/src/ComposeUIIntentHandling.spec.ts
+++ b/src/fdc3/js/composeui-fdc3/src/ComposeUIIntentHandling.spec.ts
@@ -51,7 +51,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.findIntent("testIntent"))
.rejects
.toThrow(ComposeUIErrors.NoAnswerWasProvided);
@@ -76,7 +76,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(fdc3IntentResponse)}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.findIntent("testIntent"))
.rejects
.toThrow("Error happens...");
@@ -99,7 +99,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ appIntent: { itent: "dummyIntent", apps: [{ appId: "appId1" }, { appId: "appdId2" }] } })}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var resultAppIntent = await testDesktopAgent.findIntent("dummyIntent");
expect(messageRouterClientMock.invoke).toHaveBeenCalledWith(ComposeUITopic.findIntent(), JSON.stringify({ fdc3InstanceId: window?.composeui?.fdc3.config?.instanceId, intent: "dummyIntent" }));
expect(resultAppIntent).toMatchObject({ itent: "dummyIntent", apps: [{ appId: "appId1" }, { appId: "appdId2" }] });
@@ -121,7 +121,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.findIntentsByContext({ type: "fdc3.Instrument" }))
.rejects
.toThrow(ComposeUIErrors.NoAnswerWasProvided);
@@ -146,7 +146,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(fdc3IntentResponse)}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.findIntentsByContext({ type: "testType" }))
.rejects
.toThrow(fdc3IntentResponse.error);
@@ -195,7 +195,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(response)}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var resultAppIntent = await testDesktopAgent.findIntentsByContext({ type: "fdc3.Instrument" });
expect(messageRouterClientMock.invoke).toHaveBeenCalledWith(ComposeUITopic.findIntentsByContext(), JSON.stringify(request));
@@ -218,7 +218,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.raiseIntent("testIntent", { type: "fdc3.Instrument" }))
.rejects
.toThrow(ComposeUIErrors.NoAnswerWasProvided);
@@ -245,7 +245,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(fdc3IntentResponse)}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(testDesktopAgent.raiseIntent("testIntent", { type: "testType" }))
.rejects
.toThrow(fdc3IntentResponse.error);
@@ -281,7 +281,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
getIntentListener: jest.fn((intent: string, handler: IntentHandler) => { return >Promise.reject("not implemented") })
};
- const testDesktopAgent = new ComposeUIDesktopAgent("dummyPath", messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var result = await testDesktopAgent.raiseIntent("test", { type: "test" });
expect(messageRouterClientMock.invoke).toHaveBeenCalledTimes(1);
expect(result.intent).toMatch("test");
@@ -304,7 +304,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ stored: true })}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
const intentHandler: IntentHandler = (context, metadata) => {
return;
};
@@ -329,7 +329,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve("") })
};
- const testDesktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
const intentHandler: IntentHandler = (context, metadata) => {
return;
};
@@ -354,7 +354,7 @@ describe("Tests for ComposeUIDesktopAgent's intent handling", () => {
unregisterService: jest.fn(() => { return Promise.resolve() }),
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ stored: false, error: undefined })}`) })
};
- const testDesktopAgent = new ComposeUIDesktopAgent('dummyPath', messageRouterClientMock);
+ const testDesktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
const intentHandler: IntentHandler = (context, metadata) => {
return;
};
diff --git a/src/fdc3/js/composeui-fdc3/src/ComposeUIMessageRouterMetadataClient.spec.ts b/src/fdc3/js/composeui-fdc3/src/ComposeUIMessageRouterMetadataClient.spec.ts
index 1579f06f4..63386c644 100644
--- a/src/fdc3/js/composeui-fdc3/src/ComposeUIMessageRouterMetadataClient.spec.ts
+++ b/src/fdc3/js/composeui-fdc3/src/ComposeUIMessageRouterMetadataClient.spec.ts
@@ -43,7 +43,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getInfo())
.rejects
@@ -63,7 +63,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ error: "dummyError" })}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getInfo())
.rejects
@@ -101,7 +101,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(response)}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var result = await desktopAgent.getInfo();
expect(result).toMatchObject(implementationMetadata);
@@ -121,7 +121,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.findInstances({appId: "test"}))
.rejects
@@ -141,7 +141,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ error: "dummyError" })}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.findInstances({appId: "test"}))
.rejects
@@ -166,7 +166,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(response)}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var result = await desktopAgent.findInstances({appId: "test"});
expect(result).toMatchObject([{appId: "test", instanceId: "id"}]);
@@ -185,7 +185,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(undefined) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getAppMetadata({appId: "test"}))
.rejects
@@ -205,7 +205,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify({ error: "dummyError" })}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
await expect(desktopAgent.getAppMetadata({appId: "test"}))
.rejects
@@ -230,7 +230,7 @@ describe('MessageRouterMetadataClient tests', () => {
invoke: jest.fn(() => { return Promise.resolve(`${JSON.stringify(response)}`) })
};
- const desktopAgent = new ComposeUIDesktopAgent("testChannelId", messageRouterClientMock);
+ const desktopAgent = new ComposeUIDesktopAgent(messageRouterClientMock);
var result = await desktopAgent.getAppMetadata({appId: "test"});
expect(result).toMatchObject({appId: "test"});
diff --git a/src/fdc3/js/composeui-fdc3/src/index.ts b/src/fdc3/js/composeui-fdc3/src/index.ts
index ad4aa3435..389b6acac 100644
--- a/src/fdc3/js/composeui-fdc3/src/index.ts
+++ b/src/fdc3/js/composeui-fdc3/src/index.ts
@@ -12,10 +12,38 @@
*/
+import { AppIdentifier, DesktopAgent } from "@finos/fdc3";
import { ComposeUIDesktopAgent } from "./ComposeUIDesktopAgent";
import { createMessageRouter } from "@morgan-stanley/composeui-messaging-client";
-let fdc3 = new ComposeUIDesktopAgent("fdc3.channel.1", createMessageRouter());
-fdc3.joinUserChannel("fdc3.channel.1");
-export default fdc3;
+declare global {
+ interface Window {
+ composeui: {
+ fdc3: {
+ config: AppIdentifier | undefined;
+ channelId : string | undefined;
+ }
+ }
+ fdc3: DesktopAgent;
+ }
+}
+
+async function initialize(): Promise {
+ //TODO: decide if we want to join to a channel by default.
+ let channelId: string | undefined = window.composeui.fdc3.channelId;
+ const fdc3 = new ComposeUIDesktopAgent(createMessageRouter());
+
+ if (channelId) {
+ await fdc3.joinUserChannel(channelId)
+ .then(() => {
+ window.fdc3 = fdc3;
+ window.dispatchEvent(new Event("fdc3Ready"));
+ });
+ } else {
+ window.fdc3 = fdc3;
+ window.dispatchEvent(new Event("fdc3Ready"));
+ }
+}
+
+initialize();
diff --git a/src/fdc3/js/composeui-fdc3/src/infrastructure/MessageRouterIntentsClient.ts b/src/fdc3/js/composeui-fdc3/src/infrastructure/MessageRouterIntentsClient.ts
index 80421c473..e6fb7b148 100644
--- a/src/fdc3/js/composeui-fdc3/src/infrastructure/MessageRouterIntentsClient.ts
+++ b/src/fdc3/js/composeui-fdc3/src/infrastructure/MessageRouterIntentsClient.ts
@@ -81,7 +81,7 @@ export class MessageRouterIntentsClient implements IntentsClient {
}
const messageId = Math.floor(Math.random() * 10000);
- const message = new Fdc3RaiseIntentRequest(messageId, window.composeui.fdc3.config!.instanceId!, intent, false, context, app);
+ const message = new Fdc3RaiseIntentRequest(messageId, window.composeui.fdc3.config!.instanceId!, intent, context, app);
const responseFromService = await this.messageRouterClient.invoke(ComposeUITopic.raiseIntent(), JSON.stringify(message));
if (!responseFromService) {
throw new Error(ComposeUIErrors.NoAnswerWasProvided);
diff --git a/src/fdc3/js/composeui-fdc3/src/infrastructure/messages/Fdc3RaiseIntentRequest.ts b/src/fdc3/js/composeui-fdc3/src/infrastructure/messages/Fdc3RaiseIntentRequest.ts
index d8c7947fd..81c1f7168 100644
--- a/src/fdc3/js/composeui-fdc3/src/infrastructure/messages/Fdc3RaiseIntentRequest.ts
+++ b/src/fdc3/js/composeui-fdc3/src/infrastructure/messages/Fdc3RaiseIntentRequest.ts
@@ -19,7 +19,6 @@ export class Fdc3RaiseIntentRequest {
public readonly messageId: number,
public readonly fdc3InstanceId: string,
public readonly intent: string,
- public readonly selected: boolean,
public readonly context: Context,
public readonly targetAppIdentifier?: AppIdentifier) {
}
diff --git a/src/shell/dotnet/Shell/Properties/launchSettings.json b/src/shell/dotnet/Shell/Properties/launchSettings.json
index b4f4c5dee..c774ab1d3 100644
--- a/src/shell/dotnet/Shell/Properties/launchSettings.json
+++ b/src/shell/dotnet/Shell/Properties/launchSettings.json
@@ -1,4 +1,4 @@
-{
+{
"profiles": {
"Chart and grid": {
"commandName": "Project",
diff --git a/src/shell/dotnet/Shell/appsettings.json b/src/shell/dotnet/Shell/appsettings.json
index d8b13a9dd..5d9e6699f 100644
--- a/src/shell/dotnet/Shell/appsettings.json
+++ b/src/shell/dotnet/Shell/appsettings.json
@@ -8,9 +8,6 @@
"FDC3": {
"EnableFdc3": true,
- "DesktopAgent": {
- "ChannelId": "fdc3.channel.1"
- },
"AppDirectory": {
"Source": "https://directory.fdc3.finos.org/v2/apps/"
}