Skip to content
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 @@ public sealed class StartupContext
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 IEnumerable<object> GetProperties()
}
}
}

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

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

if (property == null)
{
property = newValueFactory(startupContext);
startupContext.AddProperty(property);
}

return property;
}

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

public static T GetOrAddProperty<T>(this StartupContext startupContext) where T : class, new()
{
return GetOrAddProperty<T>(startupContext, _ => new T());
}
}
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 @@ public async Task<IModuleInstance> StartModule(StartRequest request)
throw new Exception($"Unknown Module id: {request.ModuleId}");
}

if (!_moduleRunners.TryGetValue(request.ModuleId, out var moduleRunner))
if (!_moduleRunners.TryGetValue(manifest.ModuleType, out var moduleRunner))
{
throw new Exception($"No module runner available for {manifest.ModuleType} module type");
}
Expand All @@ -57,15 +57,15 @@ public async Task<IModuleInstance> StartModule(StartRequest request)
_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 Task StopModule(StopRequest request)

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

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