-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fdc3):Initial AppDirectory implementation
- Loading branch information
1 parent
846f612
commit 8a6a291
Showing
12 changed files
with
617 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.7.34024.191 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppDirectory", "src\AppDirectory\AppDirectory.csproj", "{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3}" | ||
EndProject | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9E52D00C-AABB-4579-8724-6CC7049F021E}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppDirectory.Tests", "test\AppDirectory.Tests\AppDirectory.Tests.csproj", "{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1}" | ||
EndProject | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{62C29BA4-0D3B-4764-A898-38F29FF5C36C}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MorganStanley.ComposeUI.Testing", "..\..\..\shared\dotnet\MorganStanley.ComposeUI.Testing\MorganStanley.ComposeUI.Testing.csproj", "{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(NestedProjects) = preSolution | ||
{4B8CA047-2023-4E1A-BD73-85F7EB1D56B3} = {9E52D00C-AABB-4579-8724-6CC7049F021E} | ||
{EAA7B5A8-FD08-4E2F-809B-6CBD489714C1} = {9E52D00C-AABB-4579-8724-6CC7049F021E} | ||
{08AEA9CD-1DA8-4A73-BB10-90B0EF0EE8DC} = {62C29BA4-0D3B-4764-A898-38F29FF5C36C} | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {D2C43446-C01E-438A-8A88-554F1351D134} | ||
EndGlobalSection | ||
EndGlobal |
174 changes: 174 additions & 0 deletions
174
src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppDirectory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// 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.Concurrent; | ||
using System.IO.Abstractions; | ||
using System.Reactive.Disposables; | ||
using System.Security.Cryptography.X509Certificates; | ||
using Microsoft.Extensions.Caching.Memory; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Abstractions; | ||
using Microsoft.Extensions.Options; | ||
using Microsoft.Extensions.Primitives; | ||
using MorganStanley.Fdc3.AppDirectory; | ||
using MorganStanley.Fdc3.NewtonsoftJson.Serialization; | ||
using Newtonsoft.Json; | ||
|
||
namespace MorganStanley.ComposeUI.Fdc3.AppDirectory; | ||
|
||
public class AppDirectory : IAppDirectory | ||
{ | ||
public AppDirectory( | ||
IOptions<AppDirectoryOptions> options, | ||
IMemoryCache? cache = null, | ||
IFileSystem? fileSystem = null, | ||
ILogger<AppDirectory>? logger = null) | ||
{ | ||
_options = options.Value; | ||
_cache = cache ?? new MemoryCache(new MemoryCacheOptions()); | ||
_fileSystem = fileSystem ?? new FileSystem(); | ||
_logger = logger ?? NullLogger<AppDirectory>.Instance; | ||
} | ||
|
||
public async Task<IEnumerable<Fdc3App>> GetApps() | ||
{ | ||
return (await GetAppsCore()).Values; | ||
} | ||
|
||
public async Task<Fdc3App?> GetApp(string appId) | ||
{ | ||
if (!(await GetAppsCore()).TryGetValue(appId, out var app)) | ||
throw new AppNotFoundException(appId); | ||
|
||
return app; | ||
|
||
} | ||
|
||
private readonly IFileSystem _fileSystem; | ||
private readonly ILogger<AppDirectory> _logger; | ||
private readonly AppDirectoryOptions _options; | ||
private readonly IMemoryCache _cache; | ||
|
||
private static readonly string GetAppsCacheKey = typeof(AppDirectory).FullName + "." + nameof(GetApps); | ||
|
||
private Task<Dictionary<string, Fdc3App>> GetAppsCore() | ||
{ | ||
return _cache.GetOrCreateAsync( | ||
GetAppsCacheKey, | ||
async entry => | ||
{ | ||
var result = await LoadApps(); | ||
entry.ExpirationTokens.Add(result.ChangeToken); | ||
|
||
// Assuming that appIds are case-insensitive (not specified by the standard) | ||
return result.Apps.ToDictionary( | ||
app => app.AppId, | ||
StringComparer.OrdinalIgnoreCase); | ||
}); | ||
} | ||
|
||
private Task<(IEnumerable<Fdc3App> Apps, IChangeToken ChangeToken)> LoadApps() | ||
{ | ||
if (_fileSystem.File.Exists(_options.Source?.LocalPath)) | ||
{ | ||
return LoadAppsFromFile(_options.Source.LocalPath); | ||
} | ||
// TODO: add provider for online static files and FDC3 AppD API | ||
|
||
_logger.LogWarning("The configured source is empty or not supported"); | ||
|
||
return Task.FromResult<(IEnumerable<Fdc3App>, IChangeToken)>( | ||
( | ||
Enumerable.Empty<Fdc3App>(), | ||
NullChangeToken.Singleton)); | ||
} | ||
|
||
private Task<(IEnumerable<Fdc3App>, IChangeToken)> LoadAppsFromFile(string fileName) | ||
{ | ||
using var stream = _fileSystem.File.OpenRead(fileName); | ||
|
||
return Task.FromResult<(IEnumerable<Fdc3App>, IChangeToken)>( | ||
( | ||
LoadAppsFromStream(stream), | ||
new FileSystemChangeToken(fileName, _fileSystem))); | ||
} | ||
|
||
private static IEnumerable<Fdc3App> LoadAppsFromStream(Stream stream) | ||
{ | ||
var serializer = JsonSerializer.Create(new Fdc3JsonSerializerSettings()); | ||
using var textReader = new StreamReader(stream, leaveOpen: true); | ||
using var jsonReader = new JsonTextReader(textReader); | ||
|
||
return serializer.Deserialize<IEnumerable<Fdc3App>>(jsonReader) ?? Enumerable.Empty<Fdc3App>(); | ||
} | ||
|
||
private sealed class FileSystemChangeToken : IChangeToken | ||
{ | ||
public FileSystemChangeToken(string path, IFileSystem fileSystem) | ||
{ | ||
var fileSystemWatcher = fileSystem.FileSystemWatcher.New( | ||
fileSystem.Path.GetDirectoryName(path)!, | ||
fileSystem.Path.GetFileName(path)); | ||
|
||
fileSystemWatcher.Changed += (sender, args) => | ||
{ | ||
if (!args.ChangeType.HasFlag(WatcherChangeTypes.Changed)) | ||
return; | ||
|
||
fileSystemWatcher.Dispose(); | ||
HasChanged = true; | ||
|
||
foreach (var item in _callbacks) | ||
{ | ||
item.Value(); | ||
} | ||
|
||
_callbacks.Clear(); // Change tokens are only fired once | ||
}; | ||
|
||
fileSystemWatcher.EnableRaisingEvents = true; | ||
} | ||
|
||
public IDisposable RegisterChangeCallback(Action<object> callback, object state) | ||
{ | ||
if (HasChanged) | ||
{ | ||
callback(state); | ||
|
||
return Disposable.Empty; | ||
} | ||
|
||
var action = () => callback(state); | ||
|
||
_callbacks.TryAdd(action, action); | ||
|
||
return Disposable.Create(() => _callbacks.Remove(action, out _)); | ||
} | ||
|
||
public bool HasChanged { get; private set; } | ||
public bool ActiveChangeCallbacks => true; | ||
private readonly ConcurrentDictionary<object, Action> _callbacks = new(); | ||
} | ||
|
||
private sealed class NullChangeToken : IChangeToken | ||
{ | ||
public IDisposable RegisterChangeCallback(Action<object> callback, object state) | ||
{ | ||
return Disposable.Empty; | ||
} | ||
|
||
public bool HasChanged => false; | ||
public bool ActiveChangeCallbacks => true; | ||
|
||
public static readonly NullChangeToken Singleton = new(); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppDirectory.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AssemblyName>MorganStanley.ComposeUI.Fdc3.AppDirectory</AssemblyName> | ||
<RootNamespace>MorganStanley.ComposeUI.Fdc3.AppDirectory</RootNamespace> | ||
<LangVersion>latest</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" /> | ||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" /> | ||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" /> | ||
<PackageReference Include="MorganStanley.Fdc3.AppDirectory" Version="2.0.0-alpha.6" /> | ||
<PackageReference Include="MorganStanley.Fdc3.NewtonsoftJson" Version="2.0.0-alpha.6" /> | ||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||
<PackageReference Include="Roslyn.System.IO.Abstractions.Analyzers" Version="12.2.19"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="System.Reactive" Version="6.0.0" /> | ||
<PackageReference Include="TestableIO.System.IO.Abstractions" Version="19.2.69" /> | ||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="19.2.69" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="DependencyInjection\" /> | ||
</ItemGroup> | ||
</Project> |
48 changes: 48 additions & 0 deletions
48
src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppDirectoryOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// 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 Microsoft.Extensions.Options; | ||
using MorganStanley.Fdc3.AppDirectory; | ||
|
||
namespace MorganStanley.ComposeUI.Fdc3.AppDirectory; | ||
|
||
/// <summary> | ||
/// Configuration options for <see cref="AppDirectory"/> | ||
/// </summary> | ||
public sealed class AppDirectoryOptions : IOptions<AppDirectoryOptions> | ||
{ | ||
/// <summary> | ||
/// Gets or sets the source URL of a static JSON file. | ||
/// </summary> | ||
/// <remarks> | ||
/// Supported schemes are <c>file</c>, <c>http</c> and <c>https</c>. | ||
/// The static file must contain a single array of <see cref="Fdc3App"/> objects, | ||
/// using the schema defined by the FDC3 AppDirectory API specification (https://fdc3.finos.org/schemas/2.0/app-directory.html#tag/Application/paths/~1v2~1apps~1%7BappId%7D/get) | ||
/// UTF8 encoding is assumed unless a byte order mark or encoding header is present. | ||
/// </remarks> | ||
public Uri? Source { get; set; } | ||
|
||
// TODO: Implement fetching from AppD REST API | ||
///// <summary> | ||
///// Gets or sets the name of the <see cref="HttpClient"/> that is used to fetch | ||
///// the application definitions from a REST API defined by the FDC3 AppDirectory API specification (https://fdc3.finos.org/schemas/2.0/app-directory.html) | ||
///// </summary> | ||
///// <remarks> | ||
///// This property is only used when <see cref="Source"/> is set to <c>null</c>. | ||
///// When this property is set, an <see cref="IHttpClientFactory"/> instance must be provided as a constructor parameter | ||
///// for <see cref="AppDirectory"/>, with a matching HTTP client configured. | ||
///// </remarks> | ||
//public string? HttpClientName { get; set; } | ||
|
||
/// <inheritdoc/> | ||
public AppDirectoryOptions Value => this; | ||
} |
20 changes: 20 additions & 0 deletions
20
src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppNotFoundException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
|
||
namespace MorganStanley.ComposeUI.Fdc3.AppDirectory; | ||
|
||
public class AppNotFoundException : Exception | ||
{ | ||
public AppNotFoundException(string appId): base($"Could not find app with id '{appId}'") | ||
{ | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
...Directory/src/AppDirectory/DependencyInjection/ServiceCollectionAppDirectoryExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// 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. | ||
|
||
// ReSharper disable CheckNamespace | ||
// ReSharper disable UnusedMember.Global | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using MorganStanley.ComposeUI.Fdc3.AppDirectory; | ||
using MorganStanley.Fdc3.AppDirectory; | ||
|
||
namespace Microsoft.Extensions.DependencyInjection; | ||
|
||
public static class ServiceCollectionAppDirectoryExtensions | ||
{ | ||
public static IServiceCollection AddFdc3AppDirectory(this IServiceCollection serviceCollection) | ||
{ | ||
serviceCollection.TryAddSingleton<IAppDirectory, AppDirectory>(); | ||
|
||
return serviceCollection; | ||
} | ||
|
||
public static IServiceCollection AddFdc3AppDirectory( | ||
this IServiceCollection serviceCollection, | ||
Action<AppDirectoryOptions> configureOptions) | ||
{ | ||
serviceCollection.AddFdc3AppDirectory(); | ||
serviceCollection.AddOptions().Configure(configureOptions); | ||
|
||
return serviceCollection; | ||
} | ||
} |
Oops, something went wrong.