Skip to content

Commit

Permalink
Merge pull request #360 from BalassaMarton/fdc3-appdirectory-shell-in…
Browse files Browse the repository at this point in the history
…tegration

AppDirectory+Shell integration
  • Loading branch information
BalassaMarton authored Sep 28, 2023
2 parents fadc9b5 + 7fd5d5f commit 190378a
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 115 deletions.
13 changes: 13 additions & 0 deletions examples/fdc3-appdirectory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# FCD3 App Directory example

This example contains a JSON file that can be used as the data source for ComposeUI's FDC3 App Directory implementation.
The App Directory contains the `js-chart` and `js-grid` examples.

To run the example:

1. Run the `serve-chart-and-grid.ps1` script to run the local server for the included web apps.
1. Launch the Shell project using the configuration named `Chart and grid`, or use these command line arguments:
```
--url http://localhost:4200 --FDC3:AppDirectory:Source $(ComposeUIRepositoryRoot)/examples/fdc3-appdirectory/apps.json
```
(you'll have to manually substitute `$(ComposeUIRepositoryRoot)` with the actual root path of the repo).
22 changes: 22 additions & 0 deletions examples/fdc3-appdirectory/apps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"applications": [
{
"appId": "js-chart",
"name": "js-chart",
"title": "JS Chart",
"type": "web",
"details": {
"url": "http://localhost:8080/"
}
},
{
"appId": "js-grid",
"name": "js-grid",
"title": "JS Grid",
"type": "web",
"details": {
"url": "http://localhost:4200/"
}
}
]
}
4 changes: 3 additions & 1 deletion examples/serve-chart-and-grid.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
cd..
Push-Location .
Set-Location $PSScriptRoot\..
npm i
npx lerna run build --stream --scope "{@morgan-stanley/composeui-example-chart,@morgan-stanley/composeui-example-grid}"
npx lerna run start --stream --scope "{@morgan-stanley/composeui-example-chart,@morgan-stanley/composeui-example-grid}"
Pop-Location
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public sealed class AppDirectoryOptions : IOptions<AppDirectoryOptions>
/// </summary>
public int CacheExpirationInSeconds { get; set; } = (int)DefaultCacheExpiration.TotalSeconds;

/// <inheritdoc />
public AppDirectoryOptions Value => this;

/// <summary>
/// Gets the default cache expiration time
/// </summary>
public static readonly TimeSpan DefaultCacheExpiration = TimeSpan.FromHours(1);

/// <inheritdoc />
public AppDirectoryOptions Value => this;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
* and limitations under the License.
*/

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;

Expand All @@ -28,27 +26,13 @@ public Fdc3DesktopAgentBuilder(IServiceCollection serviceCollection)
}

/// <summary>
/// Method, for configuring `Fdc3Options` from full `IConfiguration` by searching for section `Fdc3Options.Fdc3OptionsName`.
/// </summary>
/// <typeparam name="TOptions">Must be Fdc3Options reference type.</typeparam>
/// <param name="configuration">Full IConfigration passed from the Application.</param>
/// <returns></returns>
public Fdc3DesktopAgentBuilder Configure<TOptions>(IConfiguration configuration)
where TOptions : class, IOptions<Fdc3Options>
{
ServiceCollection.Configure<TOptions>(configuration.GetSection(Fdc3Options.Fdc3OptionsName));

return this;
}

/// <summary>
/// Extension method, for configuring the `Fdc3Options` by `Action`.
/// Extension method, for configuring the <see cref="Fdc3DesktopAgentOptions"/> by a delegate.
/// </summary>
/// <param name="configureOptions"></param>
/// <returns></returns>
public Fdc3DesktopAgentBuilder Configure(Action<Fdc3Options> configureOptions)
public Fdc3DesktopAgentBuilder Configure(Action<Fdc3DesktopAgentOptions> configureOptions)
{
ServiceCollection.AddOptions<Fdc3Options>()
ServiceCollection.AddOptions<Fdc3DesktopAgentOptions>()
.Configure(configureOptions);

return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,12 @@

namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;

public sealed class Fdc3Options : IOptions<Fdc3Options>
public sealed class Fdc3DesktopAgentOptions : IOptions<Fdc3DesktopAgentOptions>
{
public static readonly string Fdc3OptionsName = "Fdc3Options";

/// <summary>
/// When set to <value>true</value>, it will enable Fdc3 backend service.
/// </summary>
public bool EnableFdc3 { get; set; }

/// <summary>
/// When set to any value, it will start the DesktopAgent with passing the value to the `WithUserChannel` builder action.
/// When set to any value, the Desktop Agent will create the specified user channel on startup.
/// </summary>
public string? ChannelId { get; set; }

Fdc3Options IOptions<Fdc3Options>.Value => this;
Fdc3DesktopAgentOptions IOptions<Fdc3DesktopAgentOptions>.Value => this;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* and limitations under the License.
*/

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
Expand All @@ -35,29 +34,4 @@ public static IServiceCollection AddFdc3DesktopAgent(
serviceCollection.AddSingleton<IHostedService, Fdc3DesktopAgent>();
return serviceCollection;
}

/// <summary>
/// Checks the configuration, if that contains Fdc3Options part, where the user could set the EnableFdc3 tag to true.
/// If that tag value is true, it will add the DesktopAgent service to the ServiceCollection, with the given Fdc3Options, that was set in the configuration.
/// </summary>
/// <param name="serviceCollection"></param>
/// <param name="configuration">This should be the IConfigurationSection, which contains the configuration for Fdc3.</param>
/// <param name="builderAction"></param>
/// <returns></returns>
public static IServiceCollection InjectFdc3BackendServiceIfEnabledFromConfig(
this IServiceCollection serviceCollection,
IConfiguration configuration,
Action<Fdc3DesktopAgentBuilder>? builderAction = null)
{
var fdc3Options = configuration.Get<Fdc3Options>();

//TODO: This should be feature toggle, once we have feature toggles in the future - instead of having `EnableFdc3` inside Fdc3Options.
if (fdc3Options.EnableFdc3)
{
serviceCollection.Configure<Fdc3Options>(configuration);
serviceCollection.AddFdc3DesktopAgent(builderAction);
}

return serviceCollection;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public class Fdc3DesktopAgent : IHostedService
private readonly List<UserChannel> _userChannels = new();

private readonly ILoggerFactory _loggerFactory;
private readonly Fdc3Options _options;
private readonly Fdc3DesktopAgentOptions _options;
private readonly IMessageRouter _messageRouter;

public Fdc3DesktopAgent(
IOptions<Fdc3Options> options,
IOptions<Fdc3DesktopAgentOptions> options,
IMessageRouter messageRouter,
ILoggerFactory? loggerFactory = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,29 @@ public async Task InitializeAsync()
builder.ConfigureServices(
services =>
{
services.AddMessageRouterServer(s => s.UseWebSockets(opt =>
{
opt.RootPath = _webSocketUri.AbsolutePath;
opt.Port = _webSocketUri.Port;
}));
services.AddMessageRouterServer(
s => s.UseWebSockets(
opt =>
{
opt.RootPath = _webSocketUri.AbsolutePath;
opt.Port = _webSocketUri.Port;
}));
services.AddMessageRouter(mr => mr.UseServer());
services.AddFdc3DesktopAgent(fdc3 => fdc3.Configure(builder =>
{
builder.EnableFdc3 = true; //no impact here
builder.ChannelId = TestChannel; //DesktopAgent will call the `AddUserChannel` method, as this property is set for the `_options` field;
}));
services.AddFdc3DesktopAgent(
fdc3 => fdc3.Configure(
builder =>
{
builder.ChannelId =
TestChannel; //DesktopAgent will call the `AddUserChannel` method, as this property is set for the `_options` field;
}));
});
_host = builder.Build();
await _host.StartAsync();

// Create a client acting in place of an application
_clientServices = new ServiceCollection()
.AddMessageRouter(mr => mr.UseWebSocket(
.AddMessageRouter(
mr => mr.UseWebSocket(
new MessageRouterWebSocketOptions
{
Uri = _webSocketUri
Expand Down Expand Up @@ -145,11 +150,22 @@ public async void FindUserChannelReturnsNoChannelFoundForNonExistingChannel()
private int _counter = 0;

private MessageBuffer EmptyContextType => MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest());
private MessageBuffer ContextType => MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest { ContextType = new Contact().Type });
private MessageBuffer OtherContextType => MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest { ContextType = new Email(null).Type });
private MessageBuffer GetContext() => MessageBuffer.Factory.CreateJson(new Contact(new ContactID() { Email = $"test{_counter}@test.org", FdsId = $"test{_counter++}" }, "Testy Tester"));

private MessageBuffer FindRequest => MessageBuffer.Factory.CreateJson(new FindChannelRequest { ChannelId = TestChannel, ChannelType = ChannelType.User });
private MessageBuffer FindNonExistingRequest => MessageBuffer.Factory.CreateJson(new FindChannelRequest { ChannelId = "nonexisting", ChannelType = ChannelType.User });
private MessageBuffer ContextType =>
MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest {ContextType = new Contact().Type});

private MessageBuffer OtherContextType =>
MessageBuffer.Factory.CreateJson(new GetCurrentContextRequest {ContextType = new Email(null).Type});

private MessageBuffer GetContext() => MessageBuffer.Factory.CreateJson(
new Contact(
new ContactID() {Email = $"test{_counter}@test.org", FdsId = $"test{_counter++}"},
"Testy Tester"));

private MessageBuffer FindRequest => MessageBuffer.Factory.CreateJson(
new FindChannelRequest {ChannelId = TestChannel, ChannelType = ChannelType.User});

private MessageBuffer FindNonExistingRequest => MessageBuffer.Factory.CreateJson(
new FindChannelRequest {ChannelId = "nonexisting", ChannelType = ChannelType.User});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace MorganStanley.ComposeUI.Fdc3.DesktopAgent.Tests;
public class Fdc3DesktopAgentTests
{
private Fdc3DesktopAgent _fdc3 = new Fdc3DesktopAgent(
new Fdc3Options(),
new Fdc3DesktopAgentOptions(),
new Mock<IMessageRouter>().Object,
NullLoggerFactory.Instance);
private const string TestChannel = "testChannel";
Expand Down
7 changes: 7 additions & 0 deletions src/shell/dotnet/Shell.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DesktopAgent", "..\..\fdc3\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MorganStanley.ComposeUI.Messaging.Client", "..\..\messaging\dotnet\src\Client\MorganStanley.ComposeUI.Messaging.Client.csproj", "{3153E575-513F-4B1A-86D0-CBE5A7A8C606}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppDirectory", "..\..\fdc3\dotnet\AppDirectory\src\AppDirectory\AppDirectory.csproj", "{70EAC402-B711-4528-B4DE-34081EB8676E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -55,6 +57,10 @@ Global
{3153E575-513F-4B1A-86D0-CBE5A7A8C606}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3153E575-513F-4B1A-86D0-CBE5A7A8C606}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3153E575-513F-4B1A-86D0-CBE5A7A8C606}.Release|Any CPU.Build.0 = Release|Any CPU
{70EAC402-B711-4528-B4DE-34081EB8676E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70EAC402-B711-4528-B4DE-34081EB8676E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70EAC402-B711-4528-B4DE-34081EB8676E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70EAC402-B711-4528-B4DE-34081EB8676E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -66,6 +72,7 @@ Global
{DD91C297-06D4-4578-9C75-1BA5D8595AA0} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
{7A94BC15-8FE5-4C84-8572-3C72248AFF8C} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
{3153E575-513F-4B1A-86D0-CBE5A7A8C606} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
{70EAC402-B711-4528-B4DE-34081EB8676E} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4C901E6C-4B9A-48C2-AB16-461040DC57B4}
Expand Down
Loading

0 comments on commit 190378a

Please sign in to comment.