-
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 62f36da
Showing
11 changed files
with
537 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 |
156 changes: 156 additions & 0 deletions
156
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,156 @@ | ||
// 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 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 Task<IEnumerable<Fdc3App>> GetApps() | ||
{ | ||
return _cache.GetOrCreateAsync( | ||
GetAppsCacheKey, | ||
async entry => | ||
{ | ||
var serializer = JsonSerializer.Create(new Fdc3JsonSerializerSettings()); | ||
var result = await GetAppsCore(); | ||
using var textReader = new StreamReader(result.Stream, leaveOpen: false); | ||
await using var jsonReader = new JsonTextReader(textReader); | ||
entry.ExpirationTokens.Add(result.ChangeToken); | ||
|
||
return serializer.Deserialize<List<Fdc3App>>(jsonReader) | ||
?? Enumerable.Empty<Fdc3App>(); | ||
}); | ||
} | ||
|
||
public async Task<Fdc3App?> GetApp(string appId) | ||
{ | ||
return (await GetApps()).FirstOrDefault( | ||
app => string.Equals(app.AppId, appId, StringComparison.OrdinalIgnoreCase)) // Assuming that appIds are case-insensitive (not specified by the standard) | ||
?? throw new AppNotFoundException(appId); | ||
} | ||
|
||
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<(Stream Stream, IChangeToken ChangeToken)> GetAppsCore() | ||
{ | ||
if (_fileSystem.File.Exists(_options.Source?.LocalPath)) | ||
{ | ||
return GetAppsFromFile(_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<(Stream, IChangeToken)>( | ||
( | ||
new MemoryStream("[]"u8.ToArray()), | ||
Check failure on line 84 in src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppDirectory.cs GitHub Actions / build (6.0.x, 18.x)
Check failure on line 84 in src/fdc3/dotnet/AppDirectory/src/AppDirectory/AppDirectory.cs GitHub Actions / build (6.0.x, 18.x)
|
||
NullChangeToken.Singleton)); | ||
} | ||
|
||
private Task<(Stream, IChangeToken)> GetAppsFromFile(string fileName) | ||
{ | ||
Stream stream = _fileSystem.File.OpenRead(fileName); | ||
IChangeToken changeToken = new FileSystemChangeToken(fileName, _fileSystem); | ||
|
||
return Task.FromResult((stream, changeToken)); | ||
} | ||
|
||
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.