Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Module Loader into the shell #382

Merged
merged 1 commit into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project>
<ItemGroup>
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.0" />
<PackageVersion Include="CommunityToolkit.HighPerformance" Version="8.2.1" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.0" />
<PackageVersion Include="CommunityToolkit.HighPerformance" Version="8.2.1" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="FluentAssertions.Json" Version="6.1.0" />
<PackageVersion Include="Google.Protobuf" Version="3.23.4" />
<PackageVersion Include="Grpc.AspNetCore.Server" Version="2.53.0" />
Expand All @@ -13,7 +14,7 @@
<PackageVersion Include="Grpc.Tools" Version="2.54.0" />
<PackageVersion Include="Grpc" Version="2.46.6" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.4.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
Expand All @@ -25,7 +26,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2045.28" />
<PackageVersion Include="Moq.Contrib.HttpClient" Version="1.4.0" />
<PackageVersion Include="Moq" Version="4.20.69" />
Expand Down
14 changes: 14 additions & 0 deletions src/messaging/dotnet/src/Client/Client/MessageRouterClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// and limitations under the License.

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
Expand Down Expand Up @@ -436,6 +437,8 @@ private async Task<TResponse> SendRequestAsync<TResponse>(
CancellationToken cancellationToken)
where TResponse : AbstractResponse
{
CheckNotOnMainThread();

var tcs = new TaskCompletionSource<AbstractResponse>(TaskCreationOptions.RunContinuationsAsynchronously);

if (!_pendingRequests.TryAdd(request.RequestId, tcs))
Expand Down Expand Up @@ -727,6 +730,17 @@ private ValueTask TryUnsubscribe(Topic topic)
: default;
}

[DebuggerStepThrough]
private static void CheckNotOnMainThread()
{
#if DEBUG
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
throw new InvalidOperationException("The current thread is the main thread. Awaiting the resulting Task can cause a deadlock.");
}
#endif
}

private enum ConnectionState
{
NotConnected,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

namespace MorganStanley.ComposeUI.Messaging.Server;

// TODO: Also implement IMessageRouter to speed up in-process messaging
internal class MessageRouterServer : IMessageRouterServer
{
public MessageRouterServer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface IModuleRunner
{
string ModuleType { get; }

Task Start(IModuleInstance moduleInstance, StartupContext startupContext, Func<Task> pipeline);
Task Start(StartupContext startupContext, Func<Task> pipeline);

Task Stop(IModuleInstance moduleInstance);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.

namespace MorganStanley.ComposeUI.ModuleLoader;

public static class ModuleLoaderConstants
{
public static readonly Uri DefaultUri = new("about:blank");
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
private readonly object _lock = new();
private readonly List<object> _properties = new();

public StartupContext(StartRequest startRequest)
public StartupContext(StartRequest startRequest, IModuleInstance moduleInstance)
{
StartRequest = startRequest;
ModuleInstance = moduleInstance;
}

public StartRequest StartRequest { get; }

public IModuleInstance ModuleInstance { get; }

public void AddProperty<T>(T value)
{
ArgumentNullException.ThrowIfNull(value, nameof(value));
Expand All @@ -42,3 +45,34 @@
}
}
}

public static class StartupContextExtensions
{
public static IEnumerable<T> GetProperties<T>(this StartupContext startupContext)
{
return startupContext.GetProperties().OfType<T>();

Check warning on line 53 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L53

Added line #L53 was not covered by tests
}

public static T GetOrAddProperty<T>(this StartupContext startupContext, Func<StartupContext, T> newValueFactory)
{
var property = startupContext.GetProperties<T>().FirstOrDefault();

Check warning on line 58 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L58

Added line #L58 was not covered by tests

if (property == null)

Check warning on line 60 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L60

Added line #L60 was not covered by tests
{
property = newValueFactory(startupContext);
startupContext.AddProperty(property);

Check warning on line 63 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L62-L63

Added lines #L62 - L63 were not covered by tests
}

return property;

Check warning on line 66 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L66

Added line #L66 was not covered by tests
}

public static T GetOrAddProperty<T>(this StartupContext startupContext, Func<T> newValueFactory)
{
return GetOrAddProperty<T>(startupContext, _ => newValueFactory());

Check warning on line 71 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L71

Added line #L71 was not covered by tests
}

public static T GetOrAddProperty<T>(this StartupContext startupContext) where T : class, new()
{
return GetOrAddProperty<T>(startupContext, _ => new T());

Check warning on line 76 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader.Abstractions/StartupContext.cs#L76

Added line #L76 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public sealed class WebManifestDetails
/// <summary>
/// The URL to open when this module is started.
/// </summary>
public Uri Url { get; init; }
public Uri Url { get; init; } = ModuleLoaderConstants.DefaultUri;

/// <summary>
/// The URL of the window icon, if any.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace MorganStanley.ComposeUI.ModuleLoader;
/// </summary>
public sealed class WebStartupProperties
{
public Uri Url { get; set; }
public Uri Url { get; set; } = ModuleLoaderConstants.DefaultUri;
public Uri? IconUrl { get; set; }
}
public List<WebModuleScriptProvider> ScriptProviders { get; } = new();
}

public delegate ValueTask<string> WebModuleScriptProvider(IModuleInstance moduleInstance);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using MorganStanley.ComposeUI.ModuleLoader;
using MorganStanley.ComposeUI.ModuleLoader.Runners;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
throw new Exception($"Unknown Module id: {request.ModuleId}");
}

if (!_moduleRunners.TryGetValue(request.ModuleId, out var moduleRunner))
if (!_moduleRunners.TryGetValue(manifest.ModuleType, out var moduleRunner))
BalassaMarton marked this conversation as resolved.
Show resolved Hide resolved
{
throw new Exception($"No module runner available for {manifest.ModuleType} module type");
}
Expand All @@ -57,15 +57,15 @@
_modules.TryAdd(instanceId, moduleInstance);

_lifetimeEvents.OnNext(new LifetimeEvent.Starting(moduleInstance));
var startupContext = new StartupContext(request);
var startupContext = new StartupContext(request, moduleInstance);

var pipeline = _startupActions
.Reverse()
.Aggregate(
() => Task.CompletedTask,
(next, action) => () => action.InvokeAsync(startupContext, next));

await moduleRunner.Start(moduleInstance, startupContext, pipeline);
await moduleRunner.Start(startupContext, pipeline);
moduleInstance.AddProperties(startupContext.GetProperties());
_lifetimeEvents.OnNext(new LifetimeEvent.Started(moduleInstance));

Expand All @@ -84,11 +84,12 @@

public async ValueTask DisposeAsync()
{
_lifetimeEvents.Dispose();
foreach (var module in _modules.Values)
{
await StopModuleInternal(module);
}
_modules.Clear();
_lifetimeEvents.Dispose();

Check warning on line 92 in src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader/ModuleLoader.cs

View check run for this annotation

Codecov / codecov/patch

src/module-loader/dotnet/src/MorganStanley.ComposeUI.ModuleLoader/ModuleLoader.cs#L91-L92

Added lines #L91 - L92 were not covered by tests
}

private async Task StopModuleInternal(IModuleInstance moduleInstance)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal class NativeModuleRunner : IModuleRunner
{
public string ModuleType => ComposeUI.ModuleLoader.ModuleType.Native;

public Task Start(IModuleInstance moduleInstance, StartupContext startupContext, Func<Task> pipeline)
public Task Start(StartupContext startupContext, Func<Task> pipeline)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ internal class WebModuleRunner : IModuleRunner
{
public string ModuleType => ComposeUI.ModuleLoader.ModuleType.Web;

public async Task Start(IModuleInstance moduleInstance, StartupContext startupContext, Func<Task> pipeline)
public async Task Start(StartupContext startupContext, Func<Task> pipeline)
{
if (moduleInstance.Manifest.TryGetDetails(out WebManifestDetails details))
if (startupContext.ModuleInstance.Manifest.TryGetDetails(out WebManifestDetails details))
{
startupContext.AddProperty(new WebStartupProperties { IconUrl = details.IconUrl, Url = details.Url });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
// or implied. See the License for the specific language governing permissions
// and limitations under the License.

using Moq;

namespace MorganStanley.ComposeUI.ModuleLoader.Abstractions.Tests;

public class StartupContextTests
Expand All @@ -23,7 +25,7 @@ public void WhenAdd_AddedValuesCanBeRetrieved()
new MyContextInfo { Name = "Test2" }
};

StartupContext context = new StartupContext(new StartRequest("test"));
StartupContext context = new StartupContext(new StartRequest("test"), Mock.Of<IModuleInstance>());
context.AddProperty(expected[0]);
context.AddProperty(expected[1]);

Expand All @@ -35,7 +37,7 @@ public void WhenAdd_AddedValuesCanBeRetrieved()
[Fact]
public void GivenNullArgument_WhenAdd_ThrowsArgumentNullException()
{
StartupContext context = new StartupContext(new StartRequest("test"));
StartupContext context = new StartupContext(new StartRequest("test"), Mock.Of<IModuleInstance>());
Assert.Throws<ArgumentNullException>(() => context.AddProperty<object>(null!));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ public async Task WhenStart_WebStartupPropertiesAreAddedToStartupContext()
var moduleManifestMock = new MockModuleManifest(details);
moduleInstanceMock.Setup(m => m.Manifest).Returns(moduleManifestMock);
var startRequest = new StartRequest("test");
var startupContext = new StartupContext(startRequest);
var startupContext = new StartupContext(startRequest, moduleInstanceMock.Object);
static Task MockPipeline() => Task.CompletedTask;

var runner = new WebModuleRunner();
await runner.Start(moduleInstanceMock.Object, startupContext, MockPipeline);
await runner.Start(startupContext, MockPipeline);

var result = startupContext.GetProperties();
Assert.NotNull(result);
Expand Down
14 changes: 14 additions & 0 deletions src/shell/dotnet/Shell.sln
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MorganStanley.ComposeUI.Mes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppDirectory", "..\..\fdc3\dotnet\AppDirectory\src\AppDirectory\AppDirectory.csproj", "{70EAC402-B711-4528-B4DE-34081EB8676E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MorganStanley.ComposeUI.ModuleLoader.Abstractions", "..\..\module-loader\dotnet\src\MorganStanley.ComposeUI.ModuleLoader.Abstractions\MorganStanley.ComposeUI.ModuleLoader.Abstractions.csproj", "{F94A14A9-38B2-4573-A74B-979044428152}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MorganStanley.ComposeUI.ModuleLoader", "..\..\module-loader\dotnet\src\MorganStanley.ComposeUI.ModuleLoader\MorganStanley.ComposeUI.ModuleLoader.csproj", "{1D387A56-DC64-4EB0-870D-3872BE4716B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -61,6 +65,14 @@ Global
{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
{F94A14A9-38B2-4573-A74B-979044428152}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F94A14A9-38B2-4573-A74B-979044428152}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F94A14A9-38B2-4573-A74B-979044428152}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F94A14A9-38B2-4573-A74B-979044428152}.Release|Any CPU.Build.0 = Release|Any CPU
{1D387A56-DC64-4EB0-870D-3872BE4716B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D387A56-DC64-4EB0-870D-3872BE4716B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D387A56-DC64-4EB0-870D-3872BE4716B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D387A56-DC64-4EB0-870D-3872BE4716B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -73,6 +85,8 @@ Global
{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}
{F94A14A9-38B2-4573-A74B-979044428152} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
{1D387A56-DC64-4EB0-870D-3872BE4716B4} = {E7A2C581-4BF4-47A5-8A11-59B2DEBADCA7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4C901E6C-4B9A-48C2-AB16-461040DC57B4}
Expand Down
12 changes: 12 additions & 0 deletions src/shell/dotnet/Shell.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">Morgan Stanley makes this available to you under the Apache License,&#xD;
Version 2.0 (the "License"). You may obtain a copy of the License at&#xD;
&#xD;
http://www.apache.org/licenses/LICENSE-2.0.&#xD;
&#xD;
See the NOTICE file distributed with this work for additional information&#xD;
regarding copyright ownership. Unless required by applicable law or agreed&#xD;
to in writing, software distributed under the License is distributed on an&#xD;
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express&#xD;
or implied. See the License for the specific language governing permissions&#xD;
and limitations under the License.</s:String></wpf:ResourceDictionary>
20 changes: 20 additions & 0 deletions src/shell/dotnet/Shell/Abstractions/IInitializeAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.Threading.Tasks;

namespace MorganStanley.ComposeUI.Shell.Abstractions;

public interface IInitializeAsync
{
Task InitializeAsync();
}
Loading
Loading