RemoteContentProvider get items WIP

This commit is contained in:
2023-08-28 09:29:52 +02:00
parent 79971fe0f4
commit bb44ca0308
49 changed files with 765 additions and 118 deletions

View File

@@ -28,6 +28,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly IContainerSizeScanProvider _containerSizeScanProvider;
private readonly IProgramsService _programsService;
private readonly IContentProviderRegistry _contentProviderRegistry;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ToolUserCommandHandlerService> _logger;
private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
@@ -45,6 +46,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
IContainerSizeScanProvider containerSizeScanProvider,
IProgramsService programsService,
IContentProviderRegistry contentProviderRegistry,
IServiceProvider serviceProvider,
ILogger<ToolUserCommandHandlerService> logger) : base(appState)
{
_systemClipboardService = systemClipboardService;
@@ -57,6 +59,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_containerSizeScanProvider = containerSizeScanProvider;
_programsService = programsService;
_contentProviderRegistry = contentProviderRegistry;
_serviceProvider = serviceProvider;
_logger = logger;
SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
@@ -92,7 +95,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
Func<Task<IRemoteConnection>>? connection = null;
if (path.StartsWith("http"))
{
connection = async () => await SignalRConnection.GetOrCreateForAsync(path);
connection = async () => await SignalRConnection.GetOrCreateForAsync(path, providerName.Value);
}
if (connection is null)
@@ -109,15 +112,17 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
var remoteContentProvider = new RemoteContentProvider(
_timelessContentProvider,
_serviceProvider,
connection,
"local",
providerName.Value);
_contentProviderRegistry.AddContentProvider(remoteContentProvider);
await _userCommandHandlerService.HandleCommandAsync(
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, remoteContentProvider)));
await remoteContentProvider.InitializeChildren();
}
private Task Edit()

View File

@@ -13,7 +13,7 @@ namespace FileTime.App.Core;
public static class Startup
{
public static IServiceCollection AddCoreAppServices(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
public static IServiceCollection AddAppCoreDependencies(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
{
serviceCollection.TryAddTransient<ITabViewModel, TabViewModel>();
serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>();

View File

@@ -2,14 +2,7 @@ using FileTime.App.Core;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.App.Core.Services.Persistence;
using FileTime.Core.Command;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Command.CreateElement;
using FileTime.Core.Command.Delete;
using FileTime.Core.CommandHandlers;
using FileTime.Core.ContentAccess;
using FileTime.Core.Services;
using FileTime.Core.Timeline;
using FileTime.Core;
using FileTime.Providers.Local;
using FileTime.Providers.LocalAdmin;
using FileTime.Providers.Remote;
@@ -25,36 +18,16 @@ public static class DependencyInjection
{
serviceCollection ??= new ServiceCollection();
serviceCollection.TryAddSingleton<ICommandScheduler, CommandScheduler>();
serviceCollection.TryAddSingleton<ITimelessContentProvider, TimelessContentProvider>();
serviceCollection.TryAddSingleton<ICommandRunner, CommandRunner>();
serviceCollection.TryAddSingleton<IContentAccessorFactory, ContentAccessorFactory>();
serviceCollection.TryAddSingleton<IContentProviderRegistry, ContentProviderRegistry>();
serviceCollection.TryAddSingleton<IRootContentProvider, RootContentProvider>();
//TODO: check local/remote context
serviceCollection.TryAddSingleton<ILocalCommandExecutor, LocalCommandExecutor>();
serviceCollection.TryAddSingleton<ICommandSchedulerNotifier, LocalCommandSchedulerNotifier>();
serviceCollection.TryAddSingleton<IApplicationSettings, ApplicationSettings>();
serviceCollection.TryAddSingleton<ITabPersistenceService, TabPersistenceService>();
serviceCollection.TryAddTransient<ITab, Tab>();
serviceCollection.TryAddSingleton<ITabEvents, TabEvents>();
serviceCollection.AddSingleton<IExitHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>());
serviceCollection.AddSingleton<IStartupHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>());
return serviceCollection
.AddCoreAppServices(configuration)
.AddCoreDependencies()
.AddAppCoreDependencies(configuration)
.AddLocalProviderServices()
.AddLocalAdminProviderServices(configuration)
.AddRemoteProviderServices()
.RegisterCommands()
.AddDefaultCommandHandlers();
.AddRemoteProviderServices();
}
private static IServiceCollection RegisterCommands(this IServiceCollection serviceCollection)
=> serviceCollection
.AddCommands()
.AddTransient<CreateContainerCommand>()
.AddTransient<CreateElementCommand>()
.AddTransient<DeleteCommand>();
}

View File

@@ -15,6 +15,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.CommandHandlers\FileTime.Core.CommandHandlers.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Extensions.DependencyInjection\FileTime.Core.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin\FileTime.Providers.LocalAdmin.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />

View File

@@ -6,7 +6,7 @@ using FileTime.App.Search;
using FileTime.ConsoleUI.App;
using FileTime.ConsoleUI.App.Services;
using FileTime.Providers.Local;
using FileTime.Server.Common;
using FileTime.Server;
using FileTime.Tools.Compression;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

View File

@@ -17,6 +17,7 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\Library\TerminalUI.DependencyInjection\TerminalUI.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" />
</ItemGroup>

View File

@@ -25,4 +25,5 @@
<ProjectReference Include="..\..\Library\InitableService\InitableService.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,8 +2,8 @@ namespace FileTime.Core.Timeline;
public class PointInTime
{
public static readonly PointInTime Eternal = new PointInTime();
public static readonly PointInTime Present = new PointInTime();
public static readonly PointInTime Eternal = new();
public static readonly PointInTime Present = new();
private readonly List<Difference> _differences;
@@ -46,5 +46,5 @@ public class PointInTime
return merged;
}
public static PointInTime CreateEmpty() => new PointInTime();
public static PointInTime CreateEmpty() => new();
}

View File

@@ -1,13 +0,0 @@
using FileTime.Core.Command.Copy;
using FileTime.Core.Command.Move;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.Command;
public static class Startup
{
public static IServiceCollection AddCommands(this IServiceCollection serviceCollection) =>
serviceCollection
.AddSingleton<CopyCommandFactory>()
.AddSingleton<MoveCommandFactory>();
}

View File

@@ -1,13 +0,0 @@
using FileTime.Core.Command;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.CommandHandlers;
public static class Startup
{
public static IServiceCollection AddDefaultCommandHandlers(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<ICommandHandler, StreamCopyCommandHandler>();
}
}

View File

@@ -28,6 +28,7 @@ public class ContentProviderRegistry : IContentProviderRegistry
private void InitializeContentProviderListIfNeeded()
{
if (_initialized) return;
lock (_lock)
{
if (!_initialized)

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FileTime.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.Core.CommandHandlers\FileTime.Core.CommandHandlers.csproj" />
<ProjectReference Include="..\FileTime.Core.Command\FileTime.Core.Command.csproj" />
<ProjectReference Include="..\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
<ProjectReference Include="..\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\FileTime.Core.Services\FileTime.Core.Services.csproj" />
<ProjectReference Include="..\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,88 @@
using FileTime.Core.Command;
using FileTime.Core.Command.Copy;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Command.CreateElement;
using FileTime.Core.Command.Delete;
using FileTime.Core.Command.Move;
using FileTime.Core.CommandHandlers;
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Serialization.Container;
using FileTime.Core.Services;
using FileTime.Core.Timeline;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Core;
public static class Startup
{
public static IServiceCollection AddCoreDependencies(this IServiceCollection serviceCollection)
=> serviceCollection
.AddCoreServices()
.AddTimelineServices()
.AddDefaultCommandHandlers()
.AddCommands()
.AddCommandFactories()
.AddCommandServices()
.AddContentAccessServices()
.AddSerialization();
private static IServiceCollection AddContentAccessServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<IContentAccessorFactory, ContentAccessorFactory>();
serviceCollection.TryAddSingleton<IContentProviderRegistry, ContentProviderRegistry>();
serviceCollection.TryAddSingleton<IRootContentProvider, RootContentProvider>();
return serviceCollection;
}
private static IServiceCollection AddCommandServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ICommandRunner, CommandRunner>();
return serviceCollection;
}
private static IServiceCollection AddCoreServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddTransient<ITab, Tab>();
serviceCollection.TryAddSingleton<ITabEvents, TabEvents>();
return serviceCollection;
}
private static IServiceCollection AddTimelineServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ICommandScheduler, CommandScheduler>();
serviceCollection.TryAddSingleton<ITimelessContentProvider, TimelessContentProvider>();
//TODO: check local/remote context
serviceCollection.TryAddSingleton<ILocalCommandExecutor, LocalCommandExecutor>();
serviceCollection.TryAddSingleton<ICommandSchedulerNotifier, LocalCommandSchedulerNotifier>();
return serviceCollection;
}
private static IServiceCollection AddCommands(this IServiceCollection serviceCollection)
=> serviceCollection
.AddTransient<CreateContainerCommand>()
.AddTransient<CreateElementCommand>()
.AddTransient<DeleteCommand>();
private static IServiceCollection AddCommandFactories(this IServiceCollection serviceCollection) =>
serviceCollection
.AddSingleton<CopyCommandFactory>()
.AddSingleton<MoveCommandFactory>();
private static IServiceCollection AddSerialization(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ISerializer<IContainer>, ContainerSerializer>();
return serviceCollection;
}
private static IServiceCollection AddDefaultCommandHandlers(this IServiceCollection serviceCollection)
=> serviceCollection
.AddSingleton<ICommandHandler, StreamCopyCommandHandler>();
}

View File

@@ -0,0 +1,14 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization;
public class AbsolutePathSerializer
{
public static SerializedAbsolutePath Serialize(AbsolutePath absolutePath)
=> new()
{
PointInTime = absolutePath.PointInTime,
Path = absolutePath.Path.Path,
Type = absolutePath.Type
};
}

View File

@@ -0,0 +1,5 @@
using FileTime.Core.ContentAccess;
namespace FileTime.Core.Serialization.Container;
public record ContainerDeserializationContext(IContentProvider ContentProvider);

View File

@@ -0,0 +1,10 @@
using System.Collections.ObjectModel;
using FileTime.Core.Models;
namespace FileTime.Core.Serialization.Container;
public record ContainerDeserializationResult(
Models.Container Container,
ObservableCollection<Exception> Exceptions,
ExtensionCollection Extensions,
ObservableCollection<AbsolutePath> Items);

View File

@@ -0,0 +1,67 @@
using System.Collections.ObjectModel;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Container;
public class ContainerDeserializer
{
private readonly ITimelessContentProvider _timelessContentProvider;
public ContainerDeserializer(ITimelessContentProvider timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
}
public ContainerDeserializationResult Deserialize(
SerializedContainer source,
ContainerDeserializationContext context)
{
ObservableCollection<Exception> exceptions = new();
ExtensionCollection extensions = new();
ObservableCollection<AbsolutePath> items = new();
var mappedItems = source.Items
.Select(x => new AbsolutePath(
_timelessContentProvider,
x.PointInTime,
new FullName(x.Path),
x.Type
)
);
foreach (var item in
mappedItems)
{
items.Add(item);
}
var container = new Models.Container(
source.Name,
source.DisplayName,
new FullName(source.FullName),
new NativePath(source.NativePath),
new AbsolutePath(_timelessContentProvider, PointInTime.Present, new FullName(source.Parent), AbsolutePathType.Container),
source.IsHidden,
source.IsExists,
source.CreatedAt,
source.ModifiedAt,
source.CanDelete,
source.CanRename,
source.Attributes,
context.ContentProvider,
source.AllowRecursiveDeletion,
PointInTime.Present,
exceptions,
new ReadOnlyExtensionCollection(extensions),
items
);
return new ContainerDeserializationResult(
container,
exceptions,
extensions,
items);
}
}

View File

@@ -0,0 +1,32 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization.Container;
public class ContainerSerializer : ISerializer<IContainer>
{
Task<ISerialized> ISerializer<IContainer>.SerializeAsync(int id, IContainer item) => Task.FromResult(Serialize(id, item));
private ISerialized Serialize(int id, IContainer container)
{
var items = container.Items.Select(AbsolutePathSerializer.Serialize).ToArray();
var serialized = new SerializedContainer
{
Id = id,
Name = container.Name,
DisplayName = container.DisplayName,
FullName = container.FullName!.Path,
NativePath = container.NativePath!.Path,
Parent = container.Parent!.Path.Path,
IsHidden = container.IsHidden,
IsExists = container.IsExists,
CreatedAt = container.CreatedAt,
ModifiedAt = container.ModifiedAt,
CanDelete = container.CanDelete,
CanRename = container.CanRename,
Attributes = container.Attributes,
AllowRecursiveDeletion = container.AllowRecursiveDeletion,
Items = items
};
return serialized;
}
}

View File

@@ -0,0 +1,29 @@
using System.Runtime.Serialization;
using FileTime.Core.Enums;
using MessagePack;
namespace FileTime.Core.Serialization.Container;
[DataContract]
[MessagePackObject]
public class SerializedContainer : ISerialized
{
[Key(0)] [DataMember(Order = 0)] public required int Id { get; set; }
[Key(1)] [DataMember(Order = 1)] public required string Name { get; set; }
[Key(2)] [DataMember(Order = 2)] public required string DisplayName { get; set; }
[Key(3)] [DataMember(Order = 3)] public required string FullName { get; set; }
[Key(4)] [DataMember(Order = 4)] public required string NativePath { get; set; }
[Key(5)] [DataMember(Order = 5)] public required string Parent { get; set; }
[Key(6)] [DataMember(Order = 6)] public required bool IsHidden { get; set; }
[Key(7)] [DataMember(Order = 7)] public required bool IsExists { get; set; }
[Key(8)] [DataMember(Order = 8)] public required DateTime? CreatedAt { get; set; }
[Key(9)] [DataMember(Order = 9)] public required DateTime? ModifiedAt { get; set; }
[Key(10)] [DataMember(Order = 10)] public required SupportsDelete CanDelete { get; set; }
[Key(11)] [DataMember(Order = 11)] public required bool CanRename { get; set; }
[Key(12)] [DataMember(Order = 12)] public required string? Attributes { get; set; }
[Key(13)] [DataMember(Order = 13)] public required bool AllowRecursiveDeletion { get; set; }
[Key(14)] [DataMember(Order = 14)] public required SerializedAbsolutePath[] Items { get; set; }
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.Core.Models\FileTime.Core.Models.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.124" />
<PackageReference Include="MessagePackAnalyzer" Version="2.5.124">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Serialization.Container;
namespace FileTime.Core.Serialization;
[MessagePack.Union(0, typeof(SerializedContainer))]
public interface ISerialized
{
int Id { get; }
}

View File

@@ -0,0 +1,14 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization;
public interface ISerializer
{
Task<ISerialized> SerializeAsync(int id, object item);
}
public interface ISerializer<T> where T : IItem
{
async Task<ISerialized> SerializeAsync(int id, object item) => await SerializeAsync(id, (T) item);
Task<ISerialized> SerializeAsync(int id, T item);
}

View File

@@ -0,0 +1,15 @@
using System.Runtime.Serialization;
using FileTime.Core.Enums;
using FileTime.Core.Timeline;
using MessagePack;
namespace FileTime.Core.Serialization;
[MessagePackObject]
[DataContract]
public class SerializedAbsolutePath
{
[Key(0)] [DataMember(Order = 0)] public required PointInTime PointInTime { get; set; }
[Key(1)] [DataMember(Order = 1)] public required string Path { get; set; }
[Key(2)] [DataMember(Order = 2)] public required AbsolutePathType Type { get; set; }
}

View File

@@ -0,0 +1,11 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Timeline;
public class PointInTimeSerializer
{
public static SerializedPointInTime Serialize(PointInTime pointInTime)
{
return new();
}
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Timeline;
public class SerializedDifference
{
public required SerializedAbsolutePath AbsolutePath { get; set; }
public required DifferenceActionType Action { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.Serialization.Timeline;
public class SerializedPointInTime
{
}

View File

@@ -188,7 +188,17 @@ public class Tab : ITab
private static IItem MapItem(AbsolutePath item)
{
var t = Task.Run(async () => await item.ResolveAsync(true));
var t = Task.Run(async () =>
{
try
{
return await item.ResolveAsync(true);
}
catch
{
return null;
}
});
t.Wait();
return t.Result;
}

View File

@@ -139,6 +139,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Providers.Smb", "P
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Providers.Smb.Abstractions", "Providers\FileTime.Providers.Smb.Abstractions\FileTime.Providers.Smb.Abstractions.csproj", "{595F736D-4711-4AD3-8D4F-13DD85FB35E7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Serialization", "Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj", "{0AAC0BDF-35F2-434C-882E-047415D55682}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Extensions.DependencyInjection", "Core\FileTime.Core.Extensions.DependencyInjection\FileTime.Core.Extensions.DependencyInjection.csproj", "{0A29616B-5413-4DA9-96EC-B45D6AF632C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Tracker", "Server\FileTime.Server.Tracker\FileTime.Server.Tracker.csproj", "{BD382403-86D8-4E99-B279-370FC22F059C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Extensions.DependencyInjection", "Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj", "{25AA9F04-EEEE-49C4-870B-CDFF71717687}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -381,6 +389,22 @@ Global
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.Build.0 = Release|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Release|Any CPU.Build.0 = Release|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Release|Any CPU.Build.0 = Release|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Release|Any CPU.Build.0 = Release|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -446,6 +470,10 @@ Global
{CA48A181-256E-4546-B164-6768B240BFFC} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{CC8AA3FC-7A5B-4558-AB35-DFF59F2AEF47} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{595F736D-4711-4AD3-8D4F-13DD85FB35E7} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{0AAC0BDF-35F2-434C-882E-047415D55682} = {3324D046-1E05-46B5-B1BA-82910D56B332}
{0A29616B-5413-4DA9-96EC-B45D6AF632C9} = {3324D046-1E05-46B5-B1BA-82910D56B332}
{BD382403-86D8-4E99-B279-370FC22F059C} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
{25AA9F04-EEEE-49C4-870B-CDFF71717687} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}

View File

@@ -9,6 +9,7 @@ using FileTime.GuiApp.App;
using FileTime.GuiApp.App.Font;
using FileTime.GuiApp.App.ViewModels;
using FileTime.GuiApp.App.Views;
using FileTime.Server;
using FileTime.Server.Common;
using FileTime.Tools.Compression;
using Microsoft.Extensions.DependencyInjection;
@@ -31,7 +32,7 @@ public class Application : Avalonia.Application
.AddCompression()
.ConfigureFont(configuration)
.RegisterLogging()
.RegisterServices()
.RegisterGuiServices()
.AddSettings()
.AddViewModels()
.BuildServiceProvider();

View File

@@ -46,6 +46,7 @@
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Search\FileTime.App.Search.csproj" />
<ProjectReference Include="..\..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.Font\FileTime.GuiApp.Font.csproj" />

View File

@@ -55,7 +55,7 @@ public static class Startup
return serviceCollection;
}
internal static IServiceCollection RegisterServices(this IServiceCollection serviceCollection)
internal static IServiceCollection RegisterGuiServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<IRxSchedulerService, AvaloniaRxSchedulerService>();
serviceCollection.TryAddSingleton<IKeyInputHandlerService, KeyInputHandlerService>();

View File

@@ -14,6 +14,7 @@ namespace FileTime.Providers.LocalAdmin;
public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler
{
private const string AdminContentProviderName = "localAdminRemote";
private class ConnectionInfo
{
public string? SignalRBaseUrl { get; init; }
@@ -144,9 +145,10 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
_remoteContentProvider = new RemoteContentProvider(
_timelessContentProvider,
async () => await SignalRConnection.GetOrCreateForAsync(_connectionInfo.SignalRBaseUrl),
_serviceProvider,
async () => await SignalRConnection.GetOrCreateForAsync(_connectionInfo.SignalRBaseUrl, AdminContentProviderName),
"local",
"localAdminRemote"
AdminContentProviderName
);
return Task.FromResult((IRemoteContentProvider)_remoteContentProvider);

View File

@@ -7,4 +7,5 @@ public interface IRemoteContentProvider : IContentProvider
{
Task<IRemoteConnection> GetRemoteConnectionAsync();
string RemoteProviderName { get; }
Task InitializeChildren();
}

View File

@@ -1,47 +1,101 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization.Container;
using FileTime.Core.Timeline;
using FileTime.Server.Common;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Providers.Remote;
public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider
{
public string RemoteProviderName { get; }
private readonly IServiceProvider _serviceProvider;
private readonly Func<Task<IRemoteConnection>> _remoteConnectionProvider;
private readonly SemaphoreSlim _initializeSemaphore = new(1, 1);
private bool _initialized;
public string RemoteProviderName { get; }
public RemoteContentProvider(
ITimelessContentProvider timelessContentProvider,
IServiceProvider serviceProvider,
Func<Task<IRemoteConnection>> remoteConnectionProvider,
string remoteName,
string name)
: base(name, timelessContentProvider)
{
RemoteProviderName = remoteName;
_serviceProvider = serviceProvider;
_remoteConnectionProvider = remoteConnectionProvider;
}
public async Task<IRemoteConnection> GetRemoteConnectionAsync()
=> await _remoteConnectionProvider();
public async Task InitializeChildren()
{
await _initializeSemaphore.WaitAsync();
try
{
if (_initialized) return;
//TODO: loading indicator
var connection = await GetRemoteConnectionAsync();
var children = await connection.GetChildren(RemoteProviderName, RemoteProviderName);
_initialized = true;
}
finally
{
_initializeSemaphore.Release();
}
}
//TODO implement
public override Task<IItem> GetItemByNativePathAsync(
public override async Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default) =>
throw new NotImplementedException();
ItemInitializationSettings itemInitializationSettings = default)
{
if (nativePath.Path == string.Empty)
{
return this;
}
var connection = await GetRemoteConnectionAsync();
var serialized = await connection.GetItemByNativePathAsync(
RemoteProviderName,
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
if (serialized is SerializedContainer serializedContainer)
{
var containerDeserializer = _serviceProvider.GetRequiredService<ContainerDeserializer>();
var container = containerDeserializer.Deserialize(
serializedContainer,
new ContainerDeserializationContext(this)
);
return container.Container;
}
throw new NotSupportedException();
}
//TODO: make it async
public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName)
{
var remoteFullname = new FullName(ConvertLocalFullNameToRemote(fullName));
var connection = await GetRemoteConnectionAsync();
var remoteNativePath = await connection.GetNativePathAsync(remoteFullname);
return new NativePath(remoteNativePath!.Path);
var remoteNativePath = await connection.GetNativePathAsync(RemoteProviderName, remoteFullname);
return new NativePath(remoteNativePath.Path);
}
public override FullName GetFullName(NativePath nativePath) => throw new NotImplementedException();

View File

@@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,4 +1,7 @@
using FileTime.Core.Models;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
namespace FileTime.Server.Common;
@@ -13,5 +16,17 @@ public interface IRemoteConnection
Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default);
Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default);
Task CloseWriterAsync(string transactionId);
Task<NativePath?> GetNativePathAsync(FullName fullName);
Task<NativePath> GetNativePathAsync(string contentProviderId, FullName fullName);
Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings);
Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName);
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Models;
namespace FileTime.Server.Common.ItemTracker;
public interface IItemTrackerRegistry
{
int Register(IItem item);
event Action<int>? ItemRemoved;
}

View File

@@ -2,5 +2,5 @@
public interface ISignalRClient
{
Task RemoveTrackedItem(int itemId);
}

View File

@@ -1,7 +1,13 @@
namespace FileTime.Server.Common.Connections.SignalR;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
namespace FileTime.Server.Common.Connections.SignalR;
public interface ISignalRHub
{
Task SetClientIdentifier(string providerName);
Task Exit();
Task CreateContainerAsync(string contentProviderId, string fullName);
Task CreateElementAsync(string contentProviderId, string fullName);
@@ -15,5 +21,16 @@ public interface ISignalRHub
//TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120
Task WriteBytesAsync(string transactionId, string data, int index);
Task CloseWriterAsync(string transactionId);
Task<string?> GetNativePathAsync(string fullNamePath);
Task<string> GetNativePathAsync(string contentProviderId, string fullNamePath);
Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings);
Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName);
}

View File

@@ -1,4 +1,7 @@
using FileTime.Core.Models;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
using InitableService;
using Microsoft.AspNetCore.SignalR.Client;
using Serilog;
@@ -6,7 +9,7 @@ using TypedSignalR.Client;
namespace FileTime.Server.Common.Connections.SignalR;
public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
public class SignalRConnection : IRemoteConnection, IAsyncInitable<string, string>
{
private static readonly Dictionary<string, SignalRConnection> Connections = new();
private static readonly object ConnectionsLock = new();
@@ -15,7 +18,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
private HubConnection _connection = null!;
private ISignalRHub _client = null!;
public async Task InitAsync(string baseUrl)
public async Task InitAsync(string baseUrl, string providerName)
{
_baseUrl = baseUrl;
@@ -26,9 +29,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
_connection = connectionBuilder.Build();
await _connection.StartAsync();
_client = _connection.CreateHubProxy<ISignalRHub>();
await _client.SetClientIdentifier(providerName);
}
public static async Task<SignalRConnection> GetOrCreateForAsync(string baseUrl)
public static async Task<SignalRConnection> GetOrCreateForAsync(string baseUrl, string providerName)
{
SignalRConnection? connection;
lock (ConnectionsLock)
@@ -47,7 +51,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
Connections.Add(baseUrl, connection);
}
await connection.InitAsync(baseUrl);
await connection.InitAsync(baseUrl, providerName);
return connection;
}
@@ -78,9 +82,32 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
public async Task CloseWriterAsync(string transactionId)
=> await _client.CloseWriterAsync(transactionId);
public async Task<NativePath?> GetNativePathAsync(FullName fullName)
public async Task<NativePath> GetNativePathAsync(string contentProviderId, FullName fullName)
{
var path = await _client.GetNativePathAsync(fullName.Path);
return path is null ? null : new NativePath(path);
var path = await _client.GetNativePathAsync(contentProviderId, fullName.Path);
return new NativePath(path);
}
public async Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings)
{
var item = await _client.GetItemByNativePathAsync(
contentProviderId,
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
return item;
}
public async Task<SerializedAbsolutePath[]> GetChildren(string contentProviderId, string fullName)
=> await _client.GetChildren(contentProviderId, fullName);
}

View File

@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\FileTime.Server.Common.Abstractions\FileTime.Server.Common.Abstractions.csproj" />
</ItemGroup>

View File

@@ -1,17 +0,0 @@
using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Server.Common;
public static class Startup
{
public static IServiceCollection AddServerCoreServices(this IServiceCollection services)
{
services.AddTransient<SignalRConnection>();
services.TryAddSingleton<IApplicationStopper, ApplicationStopper>();
services.AddSingleton<IContentAccessManager, ContentAccessManager>();
return services;
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FileTime.Server</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Server.Common\FileTime.Server.Common.csproj" />
<ProjectReference Include="..\FileTime.Server.Tracker\FileTime.Server.Tracker.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using FileTime.Server.Common;
using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess;
using FileTime.Server.Common.ItemTracker;
using FileTime.Server.Tracker.ItemTracker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Server;
public static class Startup
{
public static IServiceCollection AddServerCoreServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<SignalRConnection>();
serviceCollection.TryAddSingleton<IApplicationStopper, ApplicationStopper>();
serviceCollection.TryAddSingleton<IContentAccessManager, ContentAccessManager>();
return serviceCollection.AddRemoteTrackerServices();
}
private static IServiceCollection AddRemoteTrackerServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<IItemTrackerRegistry, ItemTrackerRegistry>();
return serviceCollection;
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Server.Common.Abstractions\FileTime.Server.Common.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,45 @@
using FileTime.Core.Models;
using FileTime.Server.Common.ItemTracker;
namespace FileTime.Server.Tracker.ItemTracker;
public class ItemTrackerRegistry : IItemTrackerRegistry
{
private readonly object _lock = new();
private readonly Dictionary<int, WeakReference<IItem>> _items = new();
private int _globalId = 1;
public event Action<int>? ItemRemoved;
public int Register(IItem item)
{
lock (_lock)
{
while (_items.ContainsKey(_globalId)) _globalId++;
_items[_globalId] = new WeakReference<IItem>(item);
return _globalId;
}
}
private void Clean()
{
lock (_lock)
{
var keys = _items.Keys.ToArray();
var keysToRemove = new List<int>();
foreach (var key in keys)
{
if (!_items[key].TryGetTarget(out _))
{
keysToRemove.Add(key);
}
}
foreach (var key in keysToRemove)
{
_items.Remove(key);
ItemRemoved?.Invoke(key);
}
}
}
}

View File

@@ -1,10 +1,14 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
using FileTime.Server.Common;
using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess;
using FileTime.Server.Common.ItemTracker;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Server.Web;
@@ -14,20 +18,46 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly IApplicationStopper _applicationStopper;
private readonly IContentAccessManager _contentAccessManager;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IItemTrackerRegistry _itemTrackerRegistry;
private readonly IServiceProvider _serviceProvider;
private readonly Dictionary<int, string> _trackedItemIds = new();
//TODO clean this sometimes
private readonly Dictionary<string, string> _clientIdToConnectionId = new();
public ConnectionHub(
IContentProviderRegistry contentProviderRegistry,
IContentAccessorFactory contentAccessorFactory,
IApplicationStopper applicationStopper,
IContentAccessManager contentAccessManager,
ITimelessContentProvider timelessContentProvider)
IItemTrackerRegistry itemTrackerRegistry,
IServiceProvider serviceProvider)
{
_contentProviderRegistry = contentProviderRegistry;
_contentAccessorFactory = contentAccessorFactory;
_applicationStopper = applicationStopper;
_contentAccessManager = contentAccessManager;
_timelessContentProvider = timelessContentProvider;
_itemTrackerRegistry = itemTrackerRegistry;
_serviceProvider = serviceProvider;
_itemTrackerRegistry.ItemRemoved += ItemTrackerRegistryOnItemRemoved;
}
private void ItemTrackerRegistryOnItemRemoved(int id)
{
if (_trackedItemIds.ContainsKey(id)) return;
var clientId = _trackedItemIds[id];
var connectionId = _clientIdToConnectionId[clientId];
Clients.Client(connectionId).RemoveTrackedItem(id);
}
public Task SetClientIdentifier(string providerName)
{
_clientIdToConnectionId[providerName] = Context.ConnectionId;
return Task.CompletedTask;
}
public Task Exit()
@@ -39,35 +69,35 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
public async Task CreateContainerAsync(string contentProviderId, string fullName)
{
//TODO handle no content provider with id
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var contentProvider = GetContentProvider(contentProviderId);
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
await itemCreator.CreateContainerAsync(contentProvider, new FullName(fullName));
}
public async Task CreateElementAsync(string contentProviderId, string fullName)
{
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var contentProvider = GetContentProvider(contentProviderId);
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName));
}
public async Task DeleteItemAsync(string contentProviderId, string fullName)
{
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var contentProvider = GetContentProvider(contentProviderId);
var itemDeleter = _contentAccessorFactory.GetItemDeleter(contentProvider);
await itemDeleter.DeleteAsync(contentProvider, new FullName(fullName));
}
public async Task MoveItemAsync(string contentProviderId, string fullName, string newPath)
{
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var contentProvider = GetContentProvider(contentProviderId);
var itemDeleter = _contentAccessorFactory.GetItemMover(contentProvider);
await itemDeleter.RenameAsync(contentProvider, new FullName(fullName), new FullName(newPath));
}
public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath)
{
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByNativePathAsync(new NativePath(nativePath), PointInTime.Present);
if (item is not IElement element)
throw new FileNotFoundException("Item is not an element", nativePath);
@@ -90,6 +120,55 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
return Task.CompletedTask;
}
public async Task<string?> GetNativePathAsync(string fullNamePath)
=> (await _timelessContentProvider.GetNativePathByFullNameAsync(new FullName(fullNamePath)))?.Path;
public async Task<string> GetNativePathAsync(string contentProviderId, string fullNamePath)
{
var contentProvider = GetContentProvider(contentProviderId);
return (await contentProvider.GetNativePathAsync(new FullName(fullNamePath))).Path;
}
public async Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings)
{
var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByNativePathAsync(
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
var id = _itemTrackerRegistry.Register(item);
var serializerType = typeof(ISerializer<>).MakeGenericType(item.GetType());
var serializer = (ISerializer) _serviceProvider.GetRequiredService(serializerType);
var serializedObject = await serializer.SerializeAsync(id, item);
return serializedObject;
}
public async Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName)
{
var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByFullNameAsync(
new FullName(fullName),
PointInTime.Present);
if (item is IContainer container)
return container.Items.Select(AbsolutePathSerializer.Serialize).ToArray();
throw new NotSupportedException();
}
private IContentProvider GetContentProvider(string contentProviderId)
=> _contentProviderRegistry
.ContentProviders
.First(p => p.Name == contentProviderId);
}

View File

@@ -11,6 +11,7 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
<ProjectReference Include="..\FileTime.Server.App\FileTime.Server.App.csproj" />
<ProjectReference Include="..\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\FileTime.Server.Web\FileTime.Server.Web.csproj" />
</ItemGroup>

View File

@@ -2,6 +2,7 @@
using Autofac.Extensions.DependencyInjection;
using FileTime.App.DependencyInjection;
using FileTime.Providers.Local;
using FileTime.Server;
using FileTime.Server.App;
using FileTime.Server.Common;
using Microsoft.Extensions.Configuration;