Skip to content

Commit

Permalink
AppDirectory+Shell integration
Browse files Browse the repository at this point in the history
  • Loading branch information
BalassaMarton committed Sep 22, 2023
1 parent fadc9b5 commit 4bec5a8
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 111 deletions.
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
75 changes: 46 additions & 29 deletions src/shell/dotnet/Shell/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using MorganStanley.ComposeUI.Fdc3.AppDirectory;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;
using MorganStanley.ComposeUI.Messaging.Server.WebSocket;
using MorganStanley.ComposeUI.Shell.Fdc3;
using MorganStanley.ComposeUI.Shell.Utilities;

namespace MorganStanley.ComposeUI.Shell;
Expand All @@ -40,7 +43,7 @@ public partial class App : Application
_host
?? throw new InvalidOperationException(
"Attempted to access the Host object before async startup has completed");

/// <summary>
/// Creates a new window of the specified type. Constructor arguments that are not registered in DI can be provided.
/// </summary>
Expand Down Expand Up @@ -96,43 +99,57 @@ private async Task StartAsync(StartupEventArgs e)

private void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
// TODO: Extensibility: plugins should be able to configure the service collection.
services.AddMessageRouterServer(
mr => mr
.UseWebSockets()
.UseAccessTokenValidator(
(clientId, token) =>
{
// TODO: Assign a separate token for each client and only allow a single connection with each token
if (_messageRouterAccessToken != token)
throw new InvalidOperationException("The provided access token is invalid");
}));

services.AddMessageRouter(
mr => mr
.UseServer()
.UseAccessToken(_messageRouterAccessToken));

//This can be replaced by the `InjectFdc3BackendServiceIfEnabledFromConfig` extension method
///* services.InjectFdc3BackendServiceIfEnabledFromConfig(context.Configuration.GetSection(Fdc3Options.Fdc3OptionsName));
var fdc3Options = context.Configuration.GetSection(Fdc3Options.Fdc3OptionsName).Get<Fdc3Options>();

//TODO: This should be feature toggle, once we have feature toggles - instead of having `EnableFdc3` inside Fdc3Options.
if (fdc3Options != null && fdc3Options.EnableFdc3)
services.AddHttpClient();
services.Configure<LoggerFactoryOptions>(context.Configuration.GetSection("Logging"));
ConfigureMessageRouter();

Check warning on line 104 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L102-L104

Added lines #L102 - L104 were not covered by tests

ConfigureFdc3();

Check warning on line 106 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L106

Added line #L106 was not covered by tests


void ConfigureMessageRouter()
{
services.AddFdc3DesktopAgent(builder => builder.Configure<Fdc3Options>(context.Configuration));
// TODO: Extensibility: plugins should be able to configure the service collection.
services.AddMessageRouterServer(
mr => mr
.UseWebSockets()
.UseAccessTokenValidator(
(clientId, token) =>
{
// TODO: Assign a separate token for each client and only allow a single connection with each token
if (_messageRouterAccessToken != token)
throw new InvalidOperationException("The provided access token is invalid");
}));

Check warning on line 121 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L112-L121

Added lines #L112 - L121 were not covered by tests

services.AddMessageRouter(
mr => mr
.UseServer()
.UseAccessToken(_messageRouterAccessToken));

Check warning on line 126 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L123-L126

Added lines #L123 - L126 were not covered by tests
}
//*/

services.Configure<LoggerFactoryOptions>(context.Configuration.GetSection("Logging"));
void ConfigureFdc3()
{
var fdc3ConfigurationSection = context.Configuration.GetSection("FDC3");
var fdc3Options = fdc3ConfigurationSection.Get<Fdc3Options>();

Check warning on line 132 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L131-L132

Added lines #L131 - L132 were not covered by tests

// TODO: Use feature flag instead
if (fdc3Options is {EnableFdc3: true})

Check warning on line 135 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L135

Added line #L135 was not covered by tests
{
services.AddFdc3DesktopAgent();
services.AddFdc3AppDirectory();

Check warning on line 138 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L137-L138

Added lines #L137 - L138 were not covered by tests

services.Configure<Fdc3Options>(fdc3ConfigurationSection);
services.Configure<Fdc3DesktopAgentOptions>(fdc3ConfigurationSection.GetSection(nameof(fdc3Options.DesktopAgent)));
services.Configure<AppDirectoryOptions>(fdc3ConfigurationSection.GetSection(nameof(fdc3Options.AppDirectory)));

Check warning on line 142 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L140-L142

Added lines #L140 - L142 were not covered by tests
}
}

Check warning on line 144 in src/shell/dotnet/Shell/App.xaml.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/App.xaml.cs#L144

Added line #L144 was not covered by tests
}

// TODO: Extensibility: Plugins should be notified here.
// Add any feature-specific async init code that depends on a running Host to this method
private async Task OnHostInitializedAsync()
{
InjectMessageRouterConfig();

var fdc3Options = Host.Services.GetRequiredService<IOptions<Fdc3Options>>();

if (fdc3Options.Value.EnableFdc3) InjectFdc3();
Expand Down Expand Up @@ -195,4 +212,4 @@ private void InjectFdc3()
var iife = ResourceReader.ReadResource(ResourceNames.Fdc3Bundle);
WebWindow.AddPreloadScript(iife);
}
}
}
29 changes: 29 additions & 0 deletions src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Extensions.Options;
using MorganStanley.ComposeUI.Fdc3.AppDirectory;
using MorganStanley.ComposeUI.Fdc3.DesktopAgent.DependencyInjection;

namespace MorganStanley.ComposeUI.Shell.Fdc3;

/// <summary>
/// Configuration root for FDC3 features. This object is configured under the <c>FDC3</c> section.
/// </summary>
public class Fdc3Options : IOptions<Fdc3Options>
{
/// <summary>
/// When set to <value>true</value>, it will enable Fdc3 backend service.
/// </summary>
public bool EnableFdc3 { get; set; }

Check warning on line 15 in src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs#L15

Added line #L15 was not covered by tests

/// <summary>
/// Options for the FDC3 Desktop Agent
/// </summary>
public Fdc3DesktopAgentOptions DesktopAgent { get; set; } = new();

Check warning on line 20 in src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs#L20

Added line #L20 was not covered by tests

/// <summary>
/// Options for the FDC3 App Directory
/// </summary>
public AppDirectoryOptions AppDirectory { get; set; } = new();

Check warning on line 25 in src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs#L25

Added line #L25 was not covered by tests

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

Check warning on line 28 in src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs

View check run for this annotation

Codecov / codecov/patch

src/shell/dotnet/Shell/Fdc3/Fdc3Options.cs#L28

Added line #L28 was not covered by tests
}
Loading

0 comments on commit 4bec5a8

Please sign in to comment.