diff --git a/.gitignore b/.gitignore index cf6e05a..ae0b81f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ vendor/ # IDE .idea .vscode +.vs # coverage coverage.txt diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln index fcfe732..c192375 100644 --- a/FrostFS.SDK.sln +++ b/FrostFS.SDK.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}" @@ -10,6 +10,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ModelsV2", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,8 +34,9 @@ Global {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs index 96fb227..2b87c46 100644 --- a/src/FrostFS.SDK.ClientV2/Client.cs +++ b/src/FrostFS.SDK.ClientV2/Client.cs @@ -1,93 +1,223 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using FrostFS.Container; +using FrostFS.Container; using FrostFS.Netmap; using FrostFS.Object; using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.Cryptography; using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.Session; using Grpc.Core; using Grpc.Net.Client; -using static FrostFS.Netmap.NetworkConfig.Types; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; using Version = FrostFS.SDK.ModelsV2.Version; namespace FrostFS.SDK.ClientV2; -public partial class Client: IFrostFSClient +public class Client : IFrostFSClient { - private GrpcChannel? _channel; - private readonly ECDsa _key; - public readonly OwnerId OwnerId; - public readonly Version Version = new(2, 13); + private bool isDisposed; - private readonly Dictionary NetworkSettings = []; + internal ClientEnvironment ClientCtx { get; set; } - private ContainerService.ContainerServiceClient? _containerServiceClient; - private NetmapService.NetmapServiceClient? _netmapServiceClient; - private ObjectService.ObjectServiceClient? _objectServiceClient; - private SessionService.SessionServiceClient? _sessionServiceClient; - - public static IFrostFSClient GetInstance(string key, string host) + public static IFrostFSClient GetInstance(IOptions clientOptions, GrpcChannelOptions channelOptions) { - return new Client(key, host); + return new Client(clientOptions, channelOptions); } - private Client(string key, string host) + /// + /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client. + /// + /// Global setting for client + /// Setting for gRPC cjannel + /// ContainerService.ContainerServiceClient implementation + /// Netmap.NetmapService.NetmapServiceClient implementation + /// Session.SessionService.SessionServiceClient implementation + /// Object.ObjectService.ObjectServiceClient implementation + /// + public static IFrostFSClient GetTestInstance( + IOptions clientOptions, + GrpcChannelOptions? channelOptions, + NetmapService.NetmapServiceClient netmapService, + SessionService.SessionServiceClient sessionService, + ContainerService.ContainerServiceClient containerService, + ObjectService.ObjectServiceClient objectService) { - // TODO: Развязать клиент и реализацию GRPC - _key = key.LoadWif(); - OwnerId = OwnerId.FromKey(_key); - InitGrpcChannel(host); - InitContainerClient(); - InitNetmapClient(); - InitObjectClient(); - InitSessionClient(); + return new Client(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService); + } + + private Client( + IOptions settings, + GrpcChannelOptions? channelOptions, + ContainerService.ContainerServiceClient containerService, + NetmapService.NetmapServiceClient netmapService, + SessionService.SessionServiceClient sessionService, + ObjectService.ObjectServiceClient objectService) + { + var ecdsaKey = settings.Value.Key.LoadWif(); + OwnerId.FromKey(ecdsaKey); + + ClientCtx = new ClientEnvironment( + key: ecdsaKey, + owner: OwnerId.FromKey(ecdsaKey), + channel: InitGrpcChannel(settings.Value.Host, channelOptions), + version: new Version(2, 13)); + + ClientCtx.ContainerService = new ContainerServiceProvider(containerService, ClientCtx); + ClientCtx.NetmapService = new NetmapServiceProvider(netmapService, ClientCtx); + ClientCtx.SessionService = new SessionServiceProvider(sessionService, ClientCtx); + ClientCtx.ObjectService = new ObjectServiceProvider(objectService, ClientCtx); + } + + private Client(IOptions options, GrpcChannelOptions channelOptions) + { + var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized"); + + clientSettings.Validate(); + + var ecdsaKey = clientSettings.Key.LoadWif(); + + var channel = InitGrpcChannel(clientSettings.Host, channelOptions); + + ClientCtx = new ClientEnvironment( + key: ecdsaKey, + owner: OwnerId.FromKey(ecdsaKey), + channel: channel, + version: new Version(2, 13)); + + ClientCtx.ContainerService = new ContainerServiceProvider(new ContainerService.ContainerServiceClient(channel), ClientCtx); + ClientCtx.NetmapService = new NetmapServiceProvider(new NetmapService.NetmapServiceClient(channel), ClientCtx); + ClientCtx.SessionService = new SessionServiceProvider(new SessionService.SessionServiceClient(channel), ClientCtx); + ClientCtx.ObjectService = new ObjectServiceProvider(new ObjectService.ObjectServiceClient(channel), ClientCtx); + CheckFrostFsVersionSupport(); - - InitNetworkInfoAsync(); } - private async void InitNetworkInfoAsync() + public void Dispose() { - var info = await GetNetworkInfoAsync(); + Dispose(true); + GC.SuppressFinalize(this); + } - foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters) + protected virtual void Dispose(bool disposing) + { + if (disposing && !isDisposed) { - SetNetworksParam(param); + ClientCtx.Dispose(); + isDisposed = true; } } - private void SetNetworksParam(Parameter param) - { - var key = Encoding.UTF8.GetString(param.Key.ToByteArray()); + public GrpcChannel Channel => ClientCtx.Channel; - var encodedValue = param.Value.ToByteArray(); - - ulong val = 0; - for (var i = encodedValue.Length - 1; i >= 0; i--) - { - val = (val << 8) + encodedValue[i]; - } - - NetworkSettings.Add(key, val); - } - - private async void CheckFrostFsVersionSupport() + public Task GetContainerAsync(ContainerId containerId, Context? ctx = null) { - var localNodeInfo = await GetLocalNodeInfoAsync(); - if (!localNodeInfo.Version.IsSupported(Version)) + ValidateEnvironment(ref ctx); + return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!); + } + + public IAsyncEnumerable ListContainersAsync(Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ContainerService!.ListContainersAsync(ctx!); + } + + public Task CreateContainerAsync(ModelsV2.Container container, Context? ctx = null) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ContainerService!.CreateContainerAsync(container, ctx!); + } + + public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ContainerService!.DeleteContainerAsync(containerId, ctx!); + } + + public Task GetNetmapSnapshotAsync(Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!); + } + + public Task GetNodeInfoAsync(Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!); + } + + public Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!); + } + + public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!); + } + + public Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!); + } + + public Task PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.PutSingleObjectAsync(obj, ctx!); + } + + public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.DeleteObjectAsync(containerId, objectId, ctx!); + } + + public IAsyncEnumerable SearchObjectsAsync( + ContainerId containerId, + IEnumerable filters, + Context? ctx = default) + { + ValidateEnvironment(ref ctx); + return ClientCtx.ObjectService!.SearchObjectsAsync(containerId, filters, ctx!); + } + + public ObjectId CalculateObjectId(ObjectHeader header) + { + return ClientCtx.ObjectService!.CalculateObjectId(header); + } + + private async void CheckFrostFsVersionSupport(Context? ctx = default) + { + ValidateEnvironment(ref ctx); + var localNodeInfo = await ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!); + + if (!localNodeInfo.Version.IsSupported(ClientCtx.Version)) { var msg = $"FrostFS {localNodeInfo.Version} is not supported."; Console.WriteLine(msg); throw new ApplicationException(msg); } } - - private void InitGrpcChannel(string host) + + private void ValidateEnvironment(ref Context? ctx) + { + if (isDisposed) + throw new Exception("Client is disposed."); + + if (ClientCtx == null || !ClientCtx.Initialized) + throw new Exception("Client is not initialized."); + + ctx ??= new Context(); + } + + private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions) { Uri uri; try @@ -97,49 +227,25 @@ public partial class Client: IFrostFSClient catch (UriFormatException e) { var msg = $"Host '{host}' has invalid format. Error: {e.Message}"; - Console.WriteLine(msg); throw new ArgumentException(msg); } - ChannelCredentials grpcCredentials; - switch (uri.Scheme) + ChannelCredentials grpcCredentials = uri.Scheme switch { - case "https": - grpcCredentials = ChannelCredentials.SecureSsl; - break; - case "http": - grpcCredentials = ChannelCredentials.Insecure; - break; - default: - var msg = $"Host '{host}' has invalid URI scheme: '{uri.Scheme}'."; - Console.WriteLine(msg); - throw new ArgumentException(msg); + "https" => ChannelCredentials.SecureSsl, + "http" => ChannelCredentials.Insecure, + _ => throw new ArgumentException($"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.") + }; + + if (channelOptions != null) + { + return GrpcChannel.ForAddress(uri, channelOptions); } - - _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions - { - Credentials = grpcCredentials, - HttpHandler = new System.Net.Http.HttpClientHandler() - }); - } - - private void InitContainerClient() - { - _containerServiceClient = new ContainerService.ContainerServiceClient(_channel); - } - - private void InitNetmapClient() - { - _netmapServiceClient = new NetmapService.NetmapServiceClient(_channel); - } - - private void InitObjectClient() - { - _objectServiceClient = new ObjectService.ObjectServiceClient(_channel); - } - - private void InitSessionClient() - { - _sessionServiceClient = new SessionService.SessionServiceClient(_channel); + + return GrpcChannel.ForAddress(uri, new GrpcChannelOptions + { + Credentials = grpcCredentials, + HttpHandler = new HttpClientHandler() + }); } } diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj index d446e0e..818bce2 100644 --- a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj +++ b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj @@ -6,9 +6,14 @@ enable + + true + + - + + diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs index 9ae6833..4234a1d 100644 --- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs @@ -1,34 +1,41 @@ +using System; using System.Collections.Generic; -using System.IO; -using System.Threading; using System.Threading.Tasks; using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Netmap; +using Grpc.Net.Client; namespace FrostFS.SDK.ClientV2.Interfaces; -public interface IFrostFSClient +public interface IFrostFSClient : IDisposable { - Task GetContainerAsync(ContainerId containerId); + Task GetContainerAsync(ContainerId containerId, Context? context = default); - IAsyncEnumerable ListContainersAsync(); + IAsyncEnumerable ListContainersAsync(Context? context = default); - Task CreateContainerAsync(ModelsV2.Container container); + Task CreateContainerAsync(ModelsV2.Container container, Context? context = default); - Task DeleteContainerAsync(ContainerId containerId); + Task DeleteContainerAsync(ContainerId containerId, Context? context = default); - Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId); + Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default); + Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); - Task GetObjectAsync(ContainerId containerId, ObjectId objectId); + Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default); + + Task PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default); - Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default); + Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default); - Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default); + IAsyncEnumerable SearchObjectsAsync(ContainerId cid, IEnumerable filters, Context? context = default); - Task PutSingleObjectAsync(ModelsV2.Object obj, CancellationToken cancellationToken = default); + Task GetNetmapSnapshotAsync(Context? context = default); - Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId); + Task GetNodeInfoAsync(Context? context = default); + + ObjectId CalculateObjectId(ObjectHeader header); + + GrpcChannel Channel { get; } +} - IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters); -} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs similarity index 75% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Container.cs index 93eac3f..3d1207f 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs @@ -22,19 +22,15 @@ public static class ContainerMapper public static ModelsV2.Container ToModel(this Container.Container container) { - var basicAclName = Enum.GetName(typeof(BasicAcl), container.BasicAcl); - if (basicAclName is null) - { + if (!Enum.IsDefined(typeof(BasicAcl),(int)container.BasicAcl)) throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'."); - } + + BasicAcl acl = (BasicAcl)container.BasicAcl; - return new ModelsV2.Container( - (BasicAcl)Enum.Parse(typeof(BasicAcl), basicAclName), - container.PlacementPolicy.ToModel() - ) + return new ModelsV2.Container(acl, container.PlacementPolicy.ToModel()) { Nonce = container.Nonce.ToUuid(), Version = container.Version.ToModel() }; } -} \ No newline at end of file +} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs rename to src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs deleted file mode 100644 index f8f2d56..0000000 --- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -using FrostFS.Netmap; -using FrostFS.SDK.ModelsV2.Enums; -using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; - -namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; - -public static class NodeInfoMapper -{ - public static NodeInfo ToModel(this LocalNodeInfoResponse.Types.Body nodeInfo) - { - var nodeStateName = Enum.GetName(typeof(NodeState), nodeInfo.NodeInfo.State); - if (nodeStateName is null) - { - throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.NodeInfo.State}'."); - } - return new NodeInfo - { - State = (NodeState)Enum.Parse(typeof(NodeState), nodeStateName), - Version = nodeInfo.Version.ToModel() - }; - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs b/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs rename to src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs new file mode 100644 index 0000000..359041c --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs @@ -0,0 +1,15 @@ +using System.Linq; +using FrostFS.Netmap; +using FrostFS.SDK.ModelsV2.Netmap; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +public static class NetmapMapper +{ + public static NetmapSnapshot ToModel(this NetmapSnapshotResponse netmap) + { + return new NetmapSnapshot( + netmap.Body.Netmap.Epoch, + netmap.Body.Netmap.Nodes.Select(n => n.ToModel(netmap.MetaHeader.Version)).ToArray()); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs new file mode 100644 index 0000000..da87dfd --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using FrostFS.Netmap; +using FrostFS.SDK.ModelsV2.Enums; +using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; + +public static class NodeInfoMapper +{ + public static NodeInfo ToModel(this LocalNodeInfoResponse.Types.Body node) + { + return node.NodeInfo.ToModel(node.Version); + } + + public static NodeInfo ToModel(this FrostFS.Netmap.NodeInfo nodeInfo, Refs.Version version) + { + NodeState state = nodeInfo.State switch + { + FrostFS.Netmap.NodeInfo.Types.State.Unspecified => NodeState.Unspecified, + FrostFS.Netmap.NodeInfo.Types.State.Online => NodeState.Online, + FrostFS.Netmap.NodeInfo.Types.State.Offline => NodeState.Offline, + FrostFS.Netmap.NodeInfo.Types.State.Maintenance => NodeState.Maintenance, + _ => throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.State}'.") + }; + + return new NodeInfo( + version: version.ToModel(), + state: state, + addresses: [.. nodeInfo.Addresses], + attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value), + publicKey: nodeInfo.PublicKey.ToByteArray() + ); + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs new file mode 100644 index 0000000..be61b13 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs @@ -0,0 +1,14 @@ +using FrostFS.SDK.ModelsV2; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ObjectMapper +{ + public static ModelsV2.Object ToModel(this Object.Object obj) + { + return new ModelsV2.Object( + ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()), + obj.Header.ToModel(), + obj.Payload.ToByteArray()); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs new file mode 100644 index 0000000..75c4bb8 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs @@ -0,0 +1,21 @@ +using FrostFS.Object; +using FrostFS.SDK.ModelsV2; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ObjectAttributeMapper +{ + public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute) + { + return new Header.Types.Attribute + { + Key = attribute.Key, + Value = attribute.Value + }; + } + + public static ObjectAttribute ToModel(this Header.Types.Attribute attribute) + { + return new ObjectAttribute(attribute.Key, attribute.Value); + } +} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs new file mode 100644 index 0000000..ee819c6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs @@ -0,0 +1,30 @@ +using System; +using FrostFS.Object; +using FrostFS.SDK.ModelsV2; +using MatchType = FrostFS.Object.MatchType; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class ObjectFilterMapper +{ + public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter) + { + var objMatchTypeName = filter.MatchType switch + { + ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified, + ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual, + ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual, + ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent, + ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix, + + _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.") + }; + + return new SearchRequest.Types.Body.Types.Filter + { + MatchType = objMatchTypeName, + Key = filter.Key, + Value = filter.Value + }; + } +} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs similarity index 53% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs index 15a7b1c..6ded9d2 100644 --- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs @@ -4,52 +4,10 @@ using FrostFS.Object; using FrostFS.SDK.Cryptography; using FrostFS.SDK.ModelsV2; using Google.Protobuf; -using MatchType = FrostFS.Object.MatchType; using ObjectType = FrostFS.Object.ObjectType; namespace FrostFS.SDK.ClientV2.Mappers.GRPC; -public static class ObjectAttributeMapper -{ - public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute) - { - return new Header.Types.Attribute - { - Key = attribute.Key, - Value = attribute.Value - }; - } - - public static ObjectAttribute ToModel(this Header.Types.Attribute attribute) - { - return new ObjectAttribute(attribute.Key, attribute.Value); - } -} - -public static class ObjectFilterMapper -{ - public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter) - { - var objMatchTypeName = filter.MatchType switch - { - ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified, - ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual, - ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual, - ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent, - ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix, - - _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.") - }; - - return new SearchRequest.Types.Body.Types.Filter - { - MatchType = objMatchTypeName, - Key = filter.Key, - Value = filter.Value - }; - } -} - public static class ObjectHeaderMapper { public static Header ToGrpcMessage(this ObjectHeader header) @@ -116,7 +74,7 @@ public static class ObjectHeaderMapper if (header.Split != null) { - model.Split = new Split(SplitId.CrateFromBinary(header.Split.SplitId.ToByteArray())) + model.Split = new Split(SplitId.CreateFromBinary(header.Split.SplitId.ToByteArray())) { Parent = header.Split.Parent?.ToModel(), ParentHeader = header.Split.ParentHeader?.ToModel(), @@ -130,37 +88,3 @@ public static class ObjectHeaderMapper return model; } } - -public static class ObjectMapper -{ - public static ModelsV2.Object ToModel(this Object.Object obj) - { - return new ModelsV2.Object() - { - Header = obj.Header.ToModel(), - ObjectId = ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()), - Payload = obj.Payload.ToByteArray() - }; - } -} - -public static class SignatureMapper -{ - public static Refs.Signature ToGrpcMessage(this Signature signature) - { - var scheme = signature.Scheme switch - { - SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256, - SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect, - SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512, - _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", paramName: nameof(signature.Scheme)) - }; - - return new Refs.Signature - { - Key = ByteString.CopyFrom(signature.Key), - Scheme = scheme, - Sign = ByteString.CopyFrom(signature.Sign) - }; - } -} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs rename to src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs new file mode 100644 index 0000000..402f7e1 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs @@ -0,0 +1,25 @@ + +using System; +using Google.Protobuf; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class SessionMapper +{ + internal static string SerializeSessionToken(this Session.SessionToken token) + { + byte[] bytes = new byte[token.CalculateSize()]; + CodedOutputStream stream = new(bytes); + token.WriteTo(stream); + + return Convert.ToBase64String(bytes); + } + + internal static Session.SessionToken DeserializeSessionToken(this byte[] bytes) + { + Session.SessionToken token = new(); + token.MergeFrom(bytes); + + return token; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs new file mode 100644 index 0000000..2da358a --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs @@ -0,0 +1,26 @@ +using System; +using FrostFS.SDK.ModelsV2; +using Google.Protobuf; + +namespace FrostFS.SDK.ClientV2.Mappers.GRPC; + +public static class SignatureMapper +{ + public static Refs.Signature ToGrpcMessage(this Signature signature) + { + var scheme = signature.Scheme switch + { + SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256, + SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect, + SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512, + _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", paramName: nameof(signature.Scheme)) + }; + + return new Refs.Signature + { + Key = ByteString.CopyFrom(signature.Key), + Scheme = scheme, + Sign = ByteString.CopyFrom(signature.Sign) + }; + } +} diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs b/src/FrostFS.SDK.ClientV2/Mappers/Status.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Status.cs diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs b/src/FrostFS.SDK.ClientV2/Mappers/Version.cs similarity index 100% rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs rename to src/FrostFS.SDK.ClientV2/Mappers/Version.cs diff --git a/src/FrostFS.SDK.ClientV2/Services/Container.cs b/src/FrostFS.SDK.ClientV2/Services/Container.cs deleted file mode 100644 index 7566492..0000000 --- a/src/FrostFS.SDK.ClientV2/Services/Container.cs +++ /dev/null @@ -1,85 +0,0 @@ -using FrostFS.Container; -using FrostFS.SDK.ClientV2.Mappers.GRPC; -using FrostFS.SDK.Cryptography; -using FrostFS.SDK.ModelsV2; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace FrostFS.SDK.ClientV2; - -public partial class Client -{ - public async Task GetContainerAsync(ContainerId cid) - { - var request = new GetRequest - { - Body = new GetRequest.Types.Body - { - ContainerId = cid.ToGrpcMessage() - }, - }; - - request.AddMetaHeader(); - request.Sign(_key); - var response = await _containerServiceClient.GetAsync(request); - Verifier.CheckResponse(response); - return response.Body.Container.ToModel(); - } - - public async IAsyncEnumerable ListContainersAsync() - { - var request = new ListRequest - { - Body = new ListRequest.Types.Body - { - OwnerId = OwnerId.ToGrpcMessage() - } - }; - - request.AddMetaHeader(); - request.Sign(_key); - var response = await _containerServiceClient.ListAsync(request); - Verifier.CheckResponse(response); - foreach (var cid in response.Body.ContainerIds) - { - yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray())); - } - } - - public async Task CreateContainerAsync(ModelsV2.Container container) - { - var cntnr = container.ToGrpcMessage(); - cntnr.OwnerId = OwnerId.ToGrpcMessage(); - cntnr.Version = Version.ToGrpcMessage(); - - var request = new PutRequest - { - Body = new PutRequest.Types.Body - { - Container = cntnr, - Signature = _key.SignRFC6979(cntnr), - } - }; - request.AddMetaHeader(); - request.Sign(_key); - var response = await _containerServiceClient.PutAsync(request); - Verifier.CheckResponse(response); - return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray())); - } - - public async Task DeleteContainerAsync(ContainerId cid) - { - var request = new DeleteRequest - { - Body = new DeleteRequest.Types.Body - { - ContainerId = cid.ToGrpcMessage(), - Signature = _key.SignRFC6979(cid.ToGrpcMessage().Value) - } - }; - request.AddMetaHeader(); - request.Sign(_key); - var response = await _containerServiceClient.DeleteAsync(request); - Verifier.CheckResponse(response); - } -} \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs new file mode 100644 index 0000000..b2d72ee --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs @@ -0,0 +1,113 @@ +using System.Threading.Tasks; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using FrostFS.SDK.ModelsV2; +using FrostFS.Container; + +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using System.Collections.Generic; + +namespace FrostFS.SDK.ClientV2; + +internal class ContainerServiceProvider : ContextAccessor +{ + private readonly ContainerService.ContainerServiceClient containerServiceClient; + + internal ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context) + : base(context) + { + containerServiceClient = service; + } + + internal async Task GetContainerAsync(ContainerId cid, Context context) + { + var request = new GetRequest + { + Body = new GetRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage() + }, + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await containerServiceClient.GetAsync(request, null, context.Deadline, context.CancellationToken); + + Verifier.CheckResponse(response); + + return response.Body.Container.ToModel(); + } + + internal async IAsyncEnumerable ListContainersAsync(Context ctx) + { + var request = new ListRequest + { + Body = new ListRequest.Types.Body + { + OwnerId = Context.Owner.ToGrpcMessage() + } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await containerServiceClient.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + + foreach (var cid in response.Body.ContainerIds) + { + yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray())); + } + } + + internal async Task CreateContainerAsync(ModelsV2.Container container, Context ctx) + { + var grpcContainer = container.ToGrpcMessage(); + grpcContainer.OwnerId = Context.Owner.ToGrpcMessage(); + grpcContainer.Version = Context.Version.ToGrpcMessage(); + + var request = new PutRequest + { + Body = new PutRequest.Types.Body + { + Container = grpcContainer, + Signature = Context.Key.SignRFC6979(grpcContainer), + } + }; + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + //var response = await Context.InvokeAsyncUnaryWithMetrics(() => + // containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken), + // nameof(containerServiceClient.PutAsync)); + + Verifier.CheckResponse(response); + + return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray())); + } + + internal async Task DeleteContainerAsync(ContainerId cid, Context ctx) + { + var request = new DeleteRequest + { + Body = new DeleteRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage(), + Signature = Context.Key.SignRFC6979(cid.ToGrpcMessage().Value) + } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await containerServiceClient.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + } +} + + diff --git a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs b/src/FrostFS.SDK.ClientV2/Services/Netmap.cs deleted file mode 100644 index ce439cc..0000000 --- a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Threading.Tasks; - -using FrostFS.Netmap; -using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; - -using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; - -namespace FrostFS.SDK.ClientV2; - -public partial class Client -{ - public async Task GetLocalNodeInfoAsync() - { - var request = new LocalNodeInfoRequest - { - Body = new LocalNodeInfoRequest.Types.Body { } - }; - - request.AddMetaHeader(); - request.Sign(_key); - var response = await _netmapServiceClient.LocalNodeInfoAsync(request); - - return response.Body.ToModel(); - } - - public async Task GetNetworkInfoAsync() - { - var request = new NetworkInfoRequest - { - Body = new NetworkInfoRequest.Types.Body { } - }; - - request.AddMetaHeader(); - request.Sign(_key); - - return await _netmapServiceClient.NetworkInfoAsync(request); - } -} diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs new file mode 100644 index 0000000..1060a20 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs @@ -0,0 +1,132 @@ +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FrostFS.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo; + +using FrostFS.SDK.ModelsV2.Netmap; +using static FrostFS.Netmap.NetworkConfig.Types; + +namespace FrostFS.SDK.ClientV2; + +internal class NetmapServiceProvider : ContextAccessor +{ + private readonly NetmapService.NetmapServiceClient netmapServiceClient; + + internal NetmapServiceProvider(NetmapService.NetmapServiceClient netmapServiceClient, ClientEnvironment context) + : base(context) + { + this.netmapServiceClient = netmapServiceClient; + } + + internal async Task GetNetworkSettingsAsync(Context ctx) + { + if (Context.NetworkSettings != null) + return Context.NetworkSettings; + + var info = await GetNetworkInfoAsync(ctx); + + var settings = new NetworkSettings(); + + foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters) + { + SetNetworksParam(param, settings); + } + + Context.NetworkSettings = settings; + + return settings; + } + + internal async Task GetLocalNodeInfoAsync(Context ctx) + { + var request = new LocalNodeInfoRequest + { + Body = new LocalNodeInfoRequest.Types.Body { } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + //var response = await Context.InvokeAsyncUnaryWithMetrics(() => + // netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken), + // nameof(netmapServiceClient.LocalNodeInfoAsync)); + + Verifier.CheckResponse(response); + + return response.Body.ToModel(); + } + + internal async Task GetNetworkInfoAsync(Context ctx) + { + var request = new NetworkInfoRequest + { + Body = new NetworkInfoRequest.Types.Body { } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + + return response; + } + + internal async Task GetNetmapSnapshotAsync(Context ctx) + { + var request = new NetmapSnapshotRequest + { + Body = new NetmapSnapshotRequest.Types.Body { } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + + return response.ToModel(); + } + + private static bool GetBoolValue(byte[] bytes) + { + return bytes.Any(b => b != 0); + } + + private static ulong GetLongValue(byte[] bytes) + { + ulong val = 0; + for (var i = bytes.Length - 1; i >= 0; i--) + val = (val << 8) + bytes[i]; + + return val; + } + + private static void SetNetworksParam(Parameter param, NetworkSettings settings) + { + var key = Encoding.UTF8.GetString(param.Key.ToByteArray()); + + var valueBytes = param.Value.ToByteArray(); + switch (key) + { + case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break; + case "EpochDuration": settings.EpochDuration = GetLongValue(valueBytes); break; + case "IRCandidateFee": settings.IRCandidateFee = GetLongValue(valueBytes); break; + case "MaxECDataCount": settings.MaxECDataCount = GetLongValue(valueBytes); break; + case "MaxECParityCount": settings.MaxECParityCount = GetLongValue(valueBytes); break; + case "MaxObjectSize": settings.MaxObjectSize = GetLongValue(valueBytes); break; + case "WithdrawalFee": settings.WithdrawalFee = GetLongValue(valueBytes); break; + case "HomomorphicHashingDisabled": settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); break; + case "MaintenanceModeAllowed": settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); break; + default: settings.UnnamedSettings.Add(key, valueBytes); break; + } + } +} + + diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs index 415c001..73e982a 100644 --- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs @@ -7,20 +7,20 @@ using FrostFS.Object; namespace FrostFS.SDK.ClientV2; -internal class ObjectReader : IDisposable +internal class ObjectReader(AsyncServerStreamingCall call) : IDisposable { - public AsyncServerStreamingCall Call { get; set; } + public AsyncServerStreamingCall Call { get; private set; } = call; public async Task ReadHeader() { if (!await Call.ResponseStream.MoveNext()) - throw new InvalidOperationException("unexpect end of stream"); + throw new InvalidOperationException("unexpected end of stream"); var response = Call.ResponseStream.Current; Verifier.CheckResponse(response); if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init) - throw new InvalidOperationException("unexpect message type"); + throw new InvalidOperationException("unexpected message type"); return new Object.Object { @@ -38,7 +38,7 @@ internal class ObjectReader : IDisposable Verifier.CheckResponse(response); if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk) - throw new InvalidOperationException("unexpect message type"); + throw new InvalidOperationException("unexpected message type"); return response.Body.Chunk.ToByteArray(); } diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs similarity index 58% rename from src/FrostFS.SDK.ClientV2/Services/Object.cs rename to src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs index 004df1d..8619cdd 100644 --- a/src/FrostFS.SDK.ClientV2/Services/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs @@ -1,26 +1,33 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Threading.Tasks; + using Google.Protobuf; +using System.Threading.Tasks; + using FrostFS.Object; using FrostFS.Refs; using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.Cryptography; using FrostFS.Session; - using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ClientV2.Extensions; -using System.Threading; namespace FrostFS.SDK.ClientV2; -public partial class Client +internal class ObjectServiceProvider : ContextAccessor { - public async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid) + private readonly ObjectService.ObjectServiceClient objectServiceClient; + + internal ObjectServiceProvider(ObjectService.ObjectServiceClient objectServiceClient, ClientEnvironment context) + : base (context) + { + this.objectServiceClient = objectServiceClient; + } + + internal async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid, Context ctx) { var request = new HeadRequest { @@ -34,18 +41,20 @@ public partial class Client } }; - request.AddMetaHeader(); - request.Sign(_key); - var response = await _objectServiceClient!.HeadAsync(request); + request.Sign(Context.Key); + + var response = await objectServiceClient!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken); + Verifier.CheckResponse(response); return response.Body.Header.Header.ToModel(); } - public async Task GetObjectAsync(ContainerId cid, ObjectId oid) + internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx) { - var sessionToken = await CreateSessionAsync(uint.MaxValue); + var sessionToken = await GetOrCreateSession(ctx); + var request = new GetRequest { Body = new GetRequest.Types.Body @@ -64,41 +73,131 @@ public partial class Client cid.ToGrpcMessage(), oid.ToGrpcMessage(), ObjectSessionContext.Types.Verb.Get, - _key + Context.Key ); - request.Sign(_key); - var obj = await GetObject(request); + request.Sign(Context.Key); + + var obj = await GetObject(request, ctx); return obj.ToModel(); } - public async Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default) + internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx) { - return await PutObject(header, payload, cancellationToken); - } - - public async Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default) - { - using var stream = new MemoryStream(payload); - return await PutObject(header, stream, cancellationToken); - } - - private Task PutObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken) - { - if (header.ClientCut) - return PutClientCutObject(header, payload, cancellationToken); + if (parameters.Header == null) + throw new ArgumentException("Value cannot be null", nameof(parameters.Header)); + + if (parameters.Payload == null) + throw new ArgumentException("Value cannot be null", nameof(parameters.Payload)); + + if (parameters.ClientCut) + return PutClientCutObject(parameters, ctx); else - return PutStreamObject(header, payload, cancellationToken); + return PutStreamObject(parameters, ctx); + } + + internal async Task PutSingleObjectAsync(ModelsV2.Object @object, Context ctx) + { + var sessionToken = await GetOrCreateSession(ctx); + + var obj = CreateObject(@object); + + var request = new PutSingleRequest + { + Body = new PutSingleRequest.Types.Body() + { + Object = obj + } + }; + + request.AddMetaHeader(); + request.AddObjectSessionToken( + sessionToken, + obj.Header.ContainerId, + obj.ObjectId, + ObjectSessionContext.Types.Verb.Put, + Context.Key + ); + + request.Sign(Context.Key); + + var response = await objectServiceClient!.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + + return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()); } - private async Task PutClientCutObject(ObjectHeader header, Stream payloadStream, CancellationToken cancellationToken) + internal ObjectId CalculateObjectId(ObjectHeader header) { - ObjectId? objectId = null; + var grpcHeader = CreateHeader(header, []); + + return new ObjectID { Value = grpcHeader.Sha256() }.ToModel(); + } + + internal async Task DeleteObjectAsync(ContainerId cid, ObjectId oid, Context ctx) + { + var request = new DeleteRequest + { + Body = new DeleteRequest.Types.Body + { + Address = new Address + { + ContainerId = cid.ToGrpcMessage(), + ObjectId = oid.ToGrpcMessage() + } + } + }; + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var response = await objectServiceClient!.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken); + + Verifier.CheckResponse(response); + } + + internal async IAsyncEnumerable SearchObjectsAsync( + ContainerId cid, + IEnumerable filters, + Context ctx) + { + var request = new SearchRequest + { + Body = new SearchRequest.Types.Body + { + ContainerId = cid.ToGrpcMessage(), + Filters = { }, + Version = 1 // TODO: clarify this param + } + }; + + request.Body.Filters.AddRange(filters.Select(f => f.ToGrpcMessage())); + + request.AddMetaHeader(); + request.Sign(Context.Key); + + var objectsIds = SearchObjects(request, ctx); + + await foreach (var oid in objectsIds) + { + yield return ObjectId.FromHash(oid.Value.ToByteArray()); + } + } + + private async Task PutClientCutObject(PutObjectParameters parameters, Context ctx) + { + var payloadStream = parameters.Payload!; + var header = parameters.Header!; + + ObjectId? objectId; List sentObjectIds = []; ModelsV2.Object? currentObject; - var partSize = (int)NetworkSettings["MaxObjectSize"]; + var networkSettings = await Context.NetmapService!.GetNetworkSettingsAsync(ctx); + + var partSize = (int)networkSettings.MaxObjectSize; var buffer = new byte[partSize]; var largeObject = new LargeObject(header.ContainerId); @@ -109,8 +208,6 @@ public partial class Client while (true) { - cancellationToken.ThrowIfCancellationRequested(); - var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize); split.Previous = sentObjectIds.LastOrDefault(); @@ -124,37 +221,41 @@ public partial class Client if (largeObject.PayloadLength == fullLength) break; - objectId = await PutSingleObjectAsync(currentObject, cancellationToken); + objectId = await PutSingleObjectAsync(currentObject, ctx); sentObjectIds.Add(objectId!); } - if (sentObjectIds.Any()) + if (sentObjectIds.Count != 0) { largeObject.CalculateHash(); currentObject.SetParent(largeObject); - objectId = await PutSingleObjectAsync(currentObject, cancellationToken); + objectId = await PutSingleObjectAsync(currentObject, ctx); sentObjectIds.Add(objectId); var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject) .AddChildren(sentObjectIds); - _ = await PutSingleObjectAsync(linkObject, cancellationToken); + _ = await PutSingleObjectAsync(linkObject, ctx); - return currentObject.GetParentId(); + return CalculateObjectId(largeObject.Header); } - return await PutSingleObjectAsync(currentObject, cancellationToken); + return await PutSingleObjectAsync(currentObject, ctx); } - private async Task PutStreamObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken) + private async Task PutStreamObject(PutObjectParameters parameters, Context ctx) { - var sessionToken = await CreateSessionAsync(uint.MaxValue); + var payload = parameters.Payload!; + var header = parameters.Header!; + + var sessionToken = await GetOrCreateSession(ctx); + var hdr = header.ToGrpcMessage(); - hdr.OwnerId = OwnerId.ToGrpcMessage(); - hdr.Version = Version.ToGrpcMessage(); + hdr.OwnerId = Context.Owner.ToGrpcMessage(); + hdr.Version = Context.Version.ToGrpcMessage(); var oid = new ObjectID { @@ -178,19 +279,17 @@ public partial class Client hdr.ContainerId, oid, ObjectSessionContext.Types.Verb.Put, - _key + Context.Key ); - request.Sign(_key); + request.Sign(Context.Key); - using var stream = await PutObjectInit(request); + using var stream = await PutObjectInit(request, ctx); var buffer = new byte[Constants.ObjectChunkSize]; while (true) { - cancellationToken.ThrowIfCancellationRequested(); - - var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize); + var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize, ctx.CancellationToken); if (bufferLength == 0) break; @@ -201,7 +300,7 @@ public partial class Client }; request.VerifyHeader = null; - request.Sign(_key); + request.Sign(Context.Key); await stream.Write(request); } @@ -211,168 +310,11 @@ public partial class Client return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()); } - public async Task PutSingleObjectAsync(ModelsV2.Object @object, CancellationToken cancellationToken = default) + // TODO: add implementation with stream writer! + private async Task GetObject(GetRequest request, Context ctx) { - var sessionToken = await CreateSessionAsync(uint.MaxValue); - - var obj = CreateObject(@object); - - var request = new PutSingleRequest - { - Body = new () { Object = obj } - }; - - request.AddMetaHeader(); - request.AddObjectSessionToken( - sessionToken, - obj.Header.ContainerId, - obj.ObjectId, - ObjectSessionContext.Types.Verb.Put, - _key - ); - - request.Sign(_key); - - var response = await _objectServiceClient!.PutSingleAsync(request, null, null, cancellationToken); - Verifier.CheckResponse(response); - - return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()); - } - - public Object.Object CreateObject(ModelsV2.Object @object) - { - var grpcHeader = @object.Header.ToGrpcMessage(); - - grpcHeader.OwnerId = OwnerId.ToGrpcMessage(); - grpcHeader.Version = Version.ToGrpcMessage(); - - if (@object.Payload != null) - { - grpcHeader.PayloadLength = (ulong)@object.Payload.Length; - grpcHeader.PayloadHash = Sha256Checksum(@object.Payload); - } - - var split = @object.Header.Split; - if (split != null) - { - grpcHeader.Split = new Header.Types.Split - { - SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null - }; - - if (split.Children != null && split.Children.Any()) - grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage())); - - if (split.ParentHeader is not null) - { - var grpcParentHeader = CreateHeader(split.ParentHeader, []); - - grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() }; - grpcHeader.Split.ParentHeader = grpcParentHeader; - grpcHeader.Split.ParentSignature = new Refs.Signature - { - Key = ByteString.CopyFrom(_key.PublicKey()), - Sign = ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())), - }; - - split.Parent = grpcHeader.Split.Parent.ToModel(); - } - - grpcHeader.Split.Previous = split.Previous?.ToGrpcMessage(); - } - - var obj = new Object.Object - { - Header = grpcHeader, - ObjectId = new ObjectID { Value = grpcHeader.Sha256() }, - Payload = ByteString.CopyFrom(@object.Payload) - }; - - obj.Signature = new Refs.Signature - { - Key = ByteString.CopyFrom(_key.PublicKey()), - Sign = ByteString.CopyFrom(_key.SignData(obj.ObjectId.ToByteArray())), - }; + using var stream = GetObjectInit(request, ctx); - return obj; - } - - public Header CreateHeader(ObjectHeader header, byte[]? payload) - { - var grpcHeader = header.ToGrpcMessage(); - - grpcHeader.OwnerId = OwnerId.ToGrpcMessage(); - grpcHeader.Version = Version.ToGrpcMessage(); - - if (header.PayloadCheckSum != null) - { - grpcHeader.PayloadHash = new Checksum - { - Type = ChecksumType.Sha256, - Sum = ByteString.CopyFrom(header.PayloadCheckSum) - }; - } - else - { - if (payload != null) - grpcHeader.PayloadHash = Sha256Checksum(payload); - } - - return grpcHeader; - } - - public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid) - { - var request = new DeleteRequest - { - Body = new DeleteRequest.Types.Body - { - Address = new Address - { - ContainerId = cid.ToGrpcMessage(), - ObjectId = oid.ToGrpcMessage() - } - } - }; - - request.AddMetaHeader(); - request.Sign(_key); - var response = await _objectServiceClient!.DeleteAsync(request); - Verifier.CheckResponse(response); - } - - public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters) - { - var request = new SearchRequest - { - Body = new SearchRequest.Types.Body - { - ContainerId = cid.ToGrpcMessage(), - Filters = { }, - Version = 1 - } - }; - - foreach (var filter in filters) - { - request.Body.Filters.Add(filter.ToGrpcMessage()); - } - - - request.AddMetaHeader(); - request.Sign(_key); - var objectsIds = SearchObjects(request); - - - await foreach (var oid in objectsIds) - { - yield return ObjectId.FromHash(oid.Value.ToByteArray()); - } - } - - private async Task GetObject(GetRequest request) - { - using var stream = GetObjectInit(request); var obj = await stream.ReadHeader(); var payload = new byte[obj.Header.PayloadLength]; var offset = 0L; @@ -392,57 +334,134 @@ public partial class Client return obj; } - private ObjectReader GetObjectInit(GetRequest initRequest) + private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx) { if (initRequest is null) throw new ArgumentNullException(nameof(initRequest)); - - return new ObjectReader - { - Call = _objectServiceClient!.Get(initRequest) - }; + var call = objectServiceClient!.Get(initRequest, null, ctx.Deadline, ctx.CancellationToken); + + return new ObjectReader(call); } - private async Task PutObjectInit(PutRequest initRequest) + private async Task PutObjectInit(PutRequest initRequest, Context ctx) { if (initRequest is null) { throw new ArgumentNullException(nameof(initRequest)); } - var call = _objectServiceClient!.Put(); + var call = objectServiceClient!.Put(null, ctx.Deadline, ctx.CancellationToken); + await call.RequestStream.WriteAsync(initRequest); return new ObjectStreamer(call); } - private async IAsyncEnumerable SearchObjects(SearchRequest request) + private async IAsyncEnumerable SearchObjects(SearchRequest request, Context ctx) { - using var stream = GetSearchReader(request); - var ids = await stream.Read(); - while (ids is not null) + using var stream = GetSearchReader(request, ctx); + + while (true) { + var ids = await stream.Read(ctx.CancellationToken); + + if (ids == null) + break; + foreach (var oid in ids) { yield return oid; } - - ids = await stream.Read(); } } - private SearchReader GetSearchReader(SearchRequest initRequest) + private SearchReader GetSearchReader(SearchRequest initRequest, Context ctx) { if (initRequest is null) { throw new ArgumentNullException(nameof(initRequest)); } - return new SearchReader(_objectServiceClient!.Search(initRequest)); + var call = objectServiceClient!.Search(initRequest, null, ctx.Deadline, ctx.CancellationToken); + + return new SearchReader(call); + } + + private Object.Object CreateObject(ModelsV2.Object @object) + { + var grpcHeader = @object.Header.ToGrpcMessage(); + + grpcHeader.OwnerId = Context.Owner.ToGrpcMessage(); + grpcHeader.Version = Context.Version.ToGrpcMessage(); + + if (@object.Payload != null) + { + grpcHeader.PayloadLength = (ulong)@object.Payload.Length; + grpcHeader.PayloadHash = Sha256Checksum(@object.Payload); + } + + var split = @object.Header.Split; + if (split != null) + { + grpcHeader.Split = new Header.Types.Split + { + SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null + }; + + if (split.Children != null && split.Children.Count != 0) + grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage())); + + if (split.ParentHeader is not null) + { + var grpcParentHeader = CreateHeader(split.ParentHeader, []); + + grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() }; + grpcHeader.Split.ParentHeader = grpcParentHeader; + grpcHeader.Split.ParentSignature = new Refs.Signature + { + Key = ByteString.CopyFrom(Context.Key.PublicKey()), + Sign = ByteString.CopyFrom(Context.Key.SignData(grpcHeader.Split.Parent.ToByteArray())), + }; + + split.Parent = grpcHeader.Split.Parent.ToModel(); + } + + grpcHeader.Split.Previous = split.Previous?.ToGrpcMessage(); + } + + var obj = new Object.Object + { + Header = grpcHeader, + ObjectId = new ObjectID { Value = grpcHeader.Sha256() }, + Payload = ByteString.CopyFrom(@object.Payload) + }; + + obj.Signature = new Refs.Signature + { + Key = ByteString.CopyFrom(Context.Key.PublicKey()), + Sign = ByteString.CopyFrom(Context.Key.SignData(obj.ObjectId.ToByteArray())), + }; + + return obj; } - public Checksum Sha256Checksum(byte[] data) + private Header CreateHeader(ObjectHeader header, byte[]? payload) + { + var grpcHeader = header.ToGrpcMessage(); + + grpcHeader.OwnerId = Context.Owner.ToGrpcMessage(); + grpcHeader.Version = Context.Version.ToGrpcMessage(); + + if (header.PayloadCheckSum != null) + grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum); + else if (payload != null) + grpcHeader.PayloadHash = Sha256Checksum(payload); + + return grpcHeader; + } + + private static Checksum Sha256Checksum(byte[] data) { return new Checksum { @@ -450,8 +469,14 @@ public partial class Client Sum = ByteString.CopyFrom(data.Sha256()) }; } + + private async Task GetOrCreateSession(Context ctx) + { + if (string.IsNullOrEmpty(ctx.SessionToken)) + { + return await Context.SessionService!.CreateSessionAsync(uint.MaxValue, ctx); + } + + return Convert.FromBase64String(ctx.SessionToken).DeserializeSessionToken(); + } } - - - - diff --git a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs index c3d425a..6bc72df 100644 --- a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs +++ b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs @@ -7,6 +7,7 @@ using Grpc.Core; using FrostFS.Object; using FrostFS.Refs; +using System.Threading; namespace FrostFS.SDK.ClientV2; @@ -14,14 +15,13 @@ internal class SearchReader(AsyncServerStreamingCall call) : IDi { public AsyncServerStreamingCall Call { get; private set; } = call; - public async Task?> Read() + public async Task?> Read(CancellationToken cancellationToken) { - if (!await Call.ResponseStream.MoveNext()) - { + if (!await Call.ResponseStream.MoveNext(cancellationToken)) return null; - } - + var response = Call.ResponseStream.Current; + Verifier.CheckResponse(response); return response.Body?.IdList.ToList(); diff --git a/src/FrostFS.SDK.ClientV2/Services/Session.cs b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs similarity index 50% rename from src/FrostFS.SDK.ClientV2/Services/Session.cs rename to src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs index 3dc52a1..c8c3c47 100644 --- a/src/FrostFS.SDK.ClientV2/Services/Session.cs +++ b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs @@ -5,28 +5,38 @@ using FrostFS.Session; namespace FrostFS.SDK.ClientV2; -public partial class Client -{ - private async Task CreateSessionAsync(ulong expiration) +internal class SessionServiceProvider : ContextAccessor +{ + private readonly SessionService.SessionServiceClient? _sessionServiceClient; + + internal SessionServiceProvider(SessionService.SessionServiceClient? sessionServiceClient, ClientEnvironment context) + : base (context) + { + _sessionServiceClient = sessionServiceClient; + } + + internal async Task CreateSessionAsync(ulong expiration, Context ctx) { var request = new CreateRequest { Body = new CreateRequest.Types.Body { - OwnerId = OwnerId.ToGrpcMessage(), - Expiration = expiration, + OwnerId = Context.Owner.ToGrpcMessage(), + Expiration = expiration } }; request.AddMetaHeader(); - request.Sign(_key); + request.Sign(Context.Key); - return await CreateSession(request); + var token = await CreateSession(request, ctx); + + return token; } - private async Task CreateSession(CreateRequest request) + internal async Task CreateSession(CreateRequest request, Context ctx) { - var resp = await _sessionServiceClient.CreateAsync(request); + var resp = await _sessionServiceClient!.CreateAsync(request, null, ctx.Deadline, ctx.CancellationToken); return new SessionToken { diff --git a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs new file mode 100644 index 0000000..037078b --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs @@ -0,0 +1,40 @@ +using FrostFS.SDK.ModelsV2; +using Grpc.Net.Client; +using System; +using System.Security.Cryptography; + +namespace FrostFS.SDK.ClientV2; + +public class ClientEnvironment(ECDsa key, OwnerId owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable +{ + internal OwnerId Owner { get; } = owner; + internal GrpcChannel Channel { get; private set; } = channel; + internal ECDsa Key { get; } = key; + internal ModelsV2.Version Version { get; } = version; + internal NetworkSettings? NetworkSettings { get; set; } + + internal ContainerServiceProvider? ContainerService { get; set; } + internal NetmapServiceProvider? NetmapService { get; set; } + internal SessionServiceProvider? SessionService { get; set; } + internal ObjectServiceProvider? ObjectService { get; set; } + + internal bool Initialized => + ContainerService != null + && NetmapService != null + && SessionService != null + && ObjectService != null; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Channel.Dispose(); + } + } +} diff --git a/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs b/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs new file mode 100644 index 0000000..75edcc6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs @@ -0,0 +1,8 @@ +namespace FrostFS.SDK.ClientV2; + +internal class ContextAccessor(ClientEnvironment context) +{ + protected ClientEnvironment Context { get; set; } = context; +} + + diff --git a/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs b/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs new file mode 100644 index 0000000..03067c6 --- /dev/null +++ b/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace FrostFS.SDK.ClientV2; + +public class NetworkSettings + { + public ulong ContainerFee { get; internal set; } + public ulong ContainerAliasFee { get; internal set; } + public ulong InnerRingCandidateFee { get; internal set; } + public ulong WithdrawFee { get; internal set; } + public ulong EpochDuration { get; internal set; } + public ulong IRCandidateFee { get; internal set; } + public ulong MaxObjectSize { get; internal set; } + public ulong MaxECDataCount { get; internal set; } + public ulong MaxECParityCount { get; internal set; } + public ulong WithdrawalFee { get; internal set; } + public bool HomomorphicHashingDisabled { get; internal set; } + public bool MaintenanceModeAllowed { get; internal set; } + + public Dictionary UnnamedSettings { get; } = []; + } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs similarity index 76% rename from src/FrostFS.SDK.ClientV2/Extensions/Object.cs rename to src/FrostFS.SDK.ClientV2/Tools/Object.cs index 3765e71..e7e59f1 100644 --- a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Object.cs @@ -1,10 +1,11 @@ +using System; using System.Collections.Generic; using FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ClientV2.Extensions; -public static class Extensions +public static class ObjectExtensions { public static ModelsV2.Object SetPayloadLength(this ModelsV2.Object obj, ulong length) { @@ -41,4 +42,15 @@ public static class Extensions linkObject.Header.Split!.Children.AddRange(objectIds); return linkObject; } + + public static ModelsV2.Object CalculateObjectId(this ModelsV2.Object obj) + { + if (obj.Payload == null) + throw new MissingFieldException("Payload cannot be null"); + + if (obj.Header == null) + throw new MissingFieldException("Header cannot be null"); + + return obj; + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ClientV2/Range.cs b/src/FrostFS.SDK.ClientV2/Tools/Range.cs similarity index 91% rename from src/FrostFS.SDK.ClientV2/Range.cs rename to src/FrostFS.SDK.ClientV2/Tools/Range.cs index 8158314..9c989c1 100644 --- a/src/FrostFS.SDK.ClientV2/Range.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Range.cs @@ -41,10 +41,10 @@ namespace System } /// Create an Index pointing at first element. - public static Index Start => new Index(0); + public static Index Start => new(0); /// Create an Index pointing at beyond last element. - public static Index End => new Index(~0); + public static Index End => new(~0); /// Create an Index from the start at the position indicated by the value. /// The index value from the start. @@ -116,7 +116,7 @@ namespace System /// Indicates whether the current Index object is equal to another object of the same type. /// An object to compare with this object - public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + public override bool Equals(object? value) => value is Index index && _value == index._value; /// Indicates whether the current Index object is equal to another Index object. /// An object to compare with this object @@ -147,22 +147,16 @@ namespace System /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } /// /// - internal readonly struct Range : IEquatable + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + internal readonly struct Range(Index start, Index end) : IEquatable { /// Represent the inclusive start index of the Range. - public Index Start { get; } + public Index Start { get; } = start; /// Represent the exclusive end index of the Range. - public Index End { get; } - - /// Construct a Range object using the start and end indexes. - /// Represent the inclusive start index of the range. - /// Represent the exclusive end index of the range. - public Range(Index start, Index end) - { - Start = start; - End = end; - } + public Index End { get; } = end; /// Indicates whether the current Range object is equal to another object of the same type. /// An object to compare with this object @@ -188,13 +182,13 @@ namespace System } /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new Range(start, Index.End); + public static Range StartAt(Index start) => new(start, Index.End); /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new Range(Index.Start, end); + public static Range EndAt(Index end) => new(Index.Start, end); /// Create a Range object starting from first element to the end. - public static Range All => new Range(Index.Start, Index.End); + public static Range All => new(Index.Start, Index.End); /// Calculate the start offset and length of range object using a collection length. /// The length of the collection that the range will be used with. length has to be a positive value. @@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices if (length == 0) { - return Array.Empty(); + return []; } var dest = new T[length]; diff --git a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs similarity index 90% rename from src/FrostFS.SDK.ClientV2/RequestConstructor.cs rename to src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs index fb632ee..6b1d4cf 100644 --- a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs @@ -17,13 +17,15 @@ public static class RequestConstructor public static void AddObjectSessionToken( this IRequest request, - SessionToken sessionToken, + Session.SessionToken sessionToken, ContainerID cid, ObjectID oid, ObjectSessionContext.Types.Verb verb, ECDsa key) { - if (request.MetaHeader.SessionToken is not null) return; + if (request.MetaHeader.SessionToken is not null) + return; + request.MetaHeader.SessionToken = sessionToken; var ctx = new ObjectSessionContext { @@ -34,6 +36,7 @@ public static class RequestConstructor }, Verb = verb }; + request.MetaHeader.SessionToken.Body.Object = ctx; request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body); } diff --git a/src/FrostFS.SDK.ClientV2/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs similarity index 95% rename from src/FrostFS.SDK.ClientV2/RequestSigner.cs rename to src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs index 23f8eb6..6016ddf 100644 --- a/src/FrostFS.SDK.ClientV2/RequestSigner.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs @@ -84,7 +84,7 @@ public static class RequestSigner return sig; } - public static void Sign(this IVerificableMessage message, ECDsa key) + public static void Sign(this IVerifiableMessage message, ECDsa key) { var meta = message.GetMetaHeader(); IVerificationHeader verify = message switch @@ -95,12 +95,15 @@ public static class RequestSigner }; var verifyOrigin = message.GetVerificationHeader(); + if (verifyOrigin is null) verify.BodySignature = key.SignMessagePart(message.GetBody()); - + else + verify.SetOrigin(verifyOrigin); + verify.MetaSignature = key.SignMessagePart(meta); verify.OriginSignature = key.SignMessagePart(verifyOrigin); - verify.SetOrigin(verifyOrigin); + message.SetVerificationHeader(verify); } } diff --git a/src/FrostFS.SDK.ClientV2/Verifier.cs b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs similarity index 86% rename from src/FrostFS.SDK.ClientV2/Verifier.cs rename to src/FrostFS.SDK.ClientV2/Tools/Verifier.cs index ad6b3e6..5811ff1 100644 --- a/src/FrostFS.SDK.ClientV2/Verifier.cs +++ b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs @@ -68,7 +68,7 @@ public static class Verifier return false; using var key = sig.Key.ToByteArray().LoadPublicKey(); - var data2Verify = data is null ? Array.Empty() : data.ToByteArray(); + var data2Verify = data is null ? [] : data.ToByteArray(); return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); } @@ -89,7 +89,7 @@ public static class Verifier return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin); } - public static bool Verify(this IVerificableMessage message) + public static bool Verify(this IVerifiableMessage message) { return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader()); } @@ -100,7 +100,17 @@ public static class Verifier throw new FormatException($"invalid response, type={resp.GetType()}"); var status = resp.MetaHeader.Status.ToModel(); - if (!status.IsSuccess()) + if (!status.IsSuccess) throw new ApplicationException(status.ToString()); } + + /// + /// This method is intended for unit tests for request verification. + /// + /// Created by SDK request to gRpc proxy + public static void CheckRequest(IRequest request) + { + if (!request.Verify()) + throw new FormatException($"invalid response, type={request.GetType()}"); + } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs index a593c3a..a75808b 100644 --- a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs +++ b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs @@ -17,4 +17,4 @@ using System.Runtime.InteropServices; [assembly: Guid("08a8487e-39ce-41fb-9c24-13f73ff2bde0")] -[assembly: InternalsVisibleToAttribute("FrostFS.SDK.Cryptography.Test")] \ No newline at end of file +[assembly: InternalsVisibleTo("FrostFS.SDK.Cryptography.Test")] \ No newline at end of file diff --git a/src/FrostFS.SDK.Cryptography/Base58.cs b/src/FrostFS.SDK.Cryptography/Base58.cs index d9d91cf..f33794a 100644 --- a/src/FrostFS.SDK.Cryptography/Base58.cs +++ b/src/FrostFS.SDK.Cryptography/Base58.cs @@ -34,9 +34,11 @@ public static class Base58 byte[] checksum = data.ToArray().Sha256().Sha256(); Span buffer = stackalloc byte[data.Length + 4]; data.CopyTo(buffer); + checksum[..4].AsSpan().CopyTo(buffer[data.Length..]); var ret = Encode(buffer); buffer.Clear(); + return ret; } diff --git a/src/FrostFS.SDK.Cryptography/Helper.cs b/src/FrostFS.SDK.Cryptography/Extentions.cs similarity index 97% rename from src/FrostFS.SDK.Cryptography/Helper.cs rename to src/FrostFS.SDK.Cryptography/Extentions.cs index 198dcb5..c27f5ef 100644 --- a/src/FrostFS.SDK.Cryptography/Helper.cs +++ b/src/FrostFS.SDK.Cryptography/Extentions.cs @@ -6,7 +6,7 @@ using System.Security.Cryptography; namespace FrostFS.SDK.Cryptography; -public static class Helper +public static class Extentions { internal static byte[] RIPEMD160(this byte[] value) { diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index aa2afc9..e276848 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -6,6 +6,10 @@ enable + + true + + diff --git a/src/FrostFS.SDK.Cryptography/Key.cs b/src/FrostFS.SDK.Cryptography/Key.cs index 56f71cb..de96791 100644 --- a/src/FrostFS.SDK.Cryptography/Key.cs +++ b/src/FrostFS.SDK.Cryptography/Key.cs @@ -56,7 +56,7 @@ public static class KeyExtension var script = new byte[] { 0x0c, CompressedPublicKeyLength }; //PUSHDATA1 33 script = ArrayHelper.Concat(script, publicKey); - script = ArrayHelper.Concat(script, new byte[] { 0x41 }); //SYSCALL + script = ArrayHelper.Concat(script, [0x41]); //SYSCALL script = ArrayHelper.Concat(script, BitConverter.GetBytes(CheckSigDescriptor)); //Neo_Crypto_CheckSig return script; @@ -80,7 +80,7 @@ public static class KeyExtension private static byte[] GetPrivateKeyFromWIF(string wif) { if (wif == null) - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(wif)); var data = wif.Base58CheckDecode(); @@ -117,7 +117,7 @@ public static class KeyExtension var pos = 33 - param.Q.X.Length; param.Q.X.CopyTo(pubkey, pos); - if (new BigInteger(param.Q.Y.Reverse().Concat(new byte[] { 0x00 }).ToArray()).IsEven) + if (new BigInteger(param.Q.Y.Reverse().Concat(new byte[] { 0x0 }).ToArray()).IsEven) pubkey[0] = 0x2; else pubkey[0] = 0x3; diff --git a/src/FrostFS.SDK.Cryptography/Range.cs b/src/FrostFS.SDK.Cryptography/Range.cs index 8158314..7313a64 100644 --- a/src/FrostFS.SDK.Cryptography/Range.cs +++ b/src/FrostFS.SDK.Cryptography/Range.cs @@ -41,10 +41,10 @@ namespace System } /// Create an Index pointing at first element. - public static Index Start => new Index(0); + public static Index Start => new(0); /// Create an Index pointing at beyond last element. - public static Index End => new Index(~0); + public static Index End => new(~0); /// Create an Index from the start at the position indicated by the value. /// The index value from the start. @@ -116,7 +116,7 @@ namespace System /// Indicates whether the current Index object is equal to another object of the same type. /// An object to compare with this object - public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; + public override bool Equals(object? value) => value is Index index && _value == index._value; /// Indicates whether the current Index object is equal to another Index object. /// An object to compare with this object @@ -147,22 +147,16 @@ namespace System /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } /// /// - internal readonly struct Range : IEquatable + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + internal readonly struct Range (Index start, Index end) : IEquatable { /// Represent the inclusive start index of the Range. - public Index Start { get; } + public Index Start { get; } = start; /// Represent the exclusive end index of the Range. - public Index End { get; } - - /// Construct a Range object using the start and end indexes. - /// Represent the inclusive start index of the range. - /// Represent the exclusive end index of the range. - public Range(Index start, Index end) - { - Start = start; - End = end; - } + public Index End { get; } = end; /// Indicates whether the current Range object is equal to another object of the same type. /// An object to compare with this object @@ -188,13 +182,13 @@ namespace System } /// Create a Range object starting from start index to the end of the collection. - public static Range StartAt(Index start) => new Range(start, Index.End); + public static Range StartAt(Index start) => new(start, Index.End); /// Create a Range object starting from first element in the collection to the end Index. - public static Range EndAt(Index end) => new Range(Index.Start, end); + public static Range EndAt(Index end) => new(Index.Start, end); /// Create a Range object starting from first element to the end. - public static Range All => new Range(Index.Start, Index.End); + public static Range All => new(Index.Start, Index.End); /// Calculate the start offset and length of range object using a collection length. /// The length of the collection that the range will be used with. length has to be a positive value. @@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices if (length == 0) { - return Array.Empty(); + return []; } var dest = new T[length]; diff --git a/src/FrostFS.SDK.ModelsV2/ClientSettings.cs b/src/FrostFS.SDK.ModelsV2/ClientSettings.cs new file mode 100644 index 0000000..4268d53 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/ClientSettings.cs @@ -0,0 +1,28 @@ +using System; +using System.Text; + +namespace FrostFS.SDK.ModelsV2; + +public class ClientSettings +{ + private static readonly string errorTemplate = "{0} is required parameter"; + + public string Key { get; set; } = string.Empty; + + public string Host { get; set; } = string.Empty; + + public void Validate() + { + StringBuilder? error = null; + + if (string.IsNullOrWhiteSpace(Key)) + (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Key))); + + if (string.IsNullOrWhiteSpace(Host)) + (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Host))); + + if (error != null) + throw new ArgumentException(error.ToString()); + } + +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Container.cs b/src/FrostFS.SDK.ModelsV2/Container.cs index d32ff8f..4533325 100644 --- a/src/FrostFS.SDK.ModelsV2/Container.cs +++ b/src/FrostFS.SDK.ModelsV2/Container.cs @@ -5,17 +5,10 @@ using FrostFS.SDK.ModelsV2.Netmap; namespace FrostFS.SDK.ModelsV2; -public class Container +public class Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { - public Guid Nonce { get; set; } - public BasicAcl BasicAcl { get; set; } - public PlacementPolicy PlacementPolicy { get; set; } - public Version Version { get; set; } - - public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) - { - Nonce = Guid.NewGuid(); - BasicAcl = basicAcl; - PlacementPolicy = placementPolicy; - } + public Guid Nonce { get; set; } = Guid.NewGuid(); + public BasicAcl BasicAcl { get; set; } = basicAcl; + public PlacementPolicy PlacementPolicy { get; set; } = placementPolicy; + public Version? Version { get; set; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Context.cs b/src/FrostFS.SDK.ModelsV2/Context.cs new file mode 100644 index 0000000..302dafc --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Context.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading; + +namespace FrostFS.SDK.ClientV2; + +public class Context() +{ + public CancellationToken CancellationToken { get; set; } = default; + public TimeSpan Timeout { get; set; } = default; + public string SessionToken { get; set; } = string.Empty; + + public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null; +} diff --git a/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs index 5c222fc..9418039 100644 --- a/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs +++ b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs @@ -3,7 +3,10 @@ using System.ComponentModel; namespace FrostFS.SDK.ModelsV2.Enums; public enum BasicAcl -{ +{ + [Description("Not defined ACL")] + NotDefined = 0x00000000, + [Description("Basic ACL for private container")] Private = 0x1C8C8CCC, diff --git a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj index 96a477c..0f47676 100644 --- a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj +++ b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj @@ -6,6 +6,10 @@ enable + + true + + diff --git a/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs b/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs new file mode 100644 index 0000000..71ac276 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs @@ -0,0 +1,8 @@ +namespace FrostFS.SDK.ModelsV2; + +public class GrpcCallInfo(string methodName, long elapsedMicroSec, bool hasError) +{ + public string MethodName { get; set; } = methodName; + public long ElapsedTimeMicroSec { get; set; } = elapsedMicroSec; + public bool HasError { get; } = hasError; +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs index 68abbb3..d2b84a2 100644 --- a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs +++ b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs @@ -1,17 +1,10 @@ namespace FrostFS.SDK.ModelsV2; -public class MetaHeader +public class MetaHeader(Version version, int epoch, int ttl) { - public Version Version { get; set; } - public int Epoch { get; set; } - public int Ttl { get; set; } - - public MetaHeader(Version version, int epoch, int ttl) - { - Version = version; - Epoch = epoch; - Ttl = ttl; - } + public Version Version { get; set; } = version; + public int Epoch { get; set; } = epoch; + public int Ttl { get; set; } = ttl; public static MetaHeader Default() { diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs new file mode 100644 index 0000000..fb8dba1 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace FrostFS.SDK.ModelsV2.Netmap; + +public class NetmapSnapshot(ulong epoch, IReadOnlyList nodeInfoCollection) +{ + public ulong Epoch { get; private set; } = epoch; + + public IReadOnlyList NodeInfoCollection { get; private set; } = nodeInfoCollection; +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs index d67d987..9433c04 100644 --- a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs +++ b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs @@ -1,9 +1,28 @@ +using System; +using System.Collections.Generic; using FrostFS.SDK.ModelsV2.Enums; namespace FrostFS.SDK.ModelsV2.Netmap; public class NodeInfo { - public NodeState State { get; set; } - public Version? Version { get; set; } + public NodeInfo( + Version version, + NodeState state, + IReadOnlyCollection addresses, + IReadOnlyDictionary attributes, + ReadOnlyMemory publicKey) + { + Version = version; + State = state; + Addresses = addresses; + Attributes = attributes; + PublicKey = publicKey; + } + + public NodeState State { get; private set; } + public Version Version { get; private set; } + public IReadOnlyCollection Addresses { get; private set; } + public IReadOnlyDictionary Attributes { get; private set; } + public ReadOnlyMemory PublicKey { get; private set; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs index b638bb6..bb7b1a3 100644 --- a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs +++ b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs @@ -1,13 +1,28 @@ +using System; +using System.Linq; + namespace FrostFS.SDK.ModelsV2.Netmap; -public class PlacementPolicy +public class PlacementPolicy(bool unique, params Replica[] replicas) : IComparable { - public Replica[] Replicas { get; private set; } - public bool Unique { get; private set; } + public Replica[] Replicas { get; private set; } = replicas; + public bool Unique { get; private set; } = unique; - public PlacementPolicy(bool unique, params Replica[] replicas) + public int CompareTo(PlacementPolicy other) { - Replicas = replicas; - Unique = unique; + var notEqual = other == null + || Unique != other.Unique + || Replicas.Length != other.Replicas.Length; + + if (notEqual) + return 1; + + foreach (var replica in Replicas) + { + if (!other!.Replicas.Any(r => r.Count == replica.Count && r.Selector == replica.Selector)) + return 1; + } + + return 0; } } \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs new file mode 100644 index 0000000..3822dce --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading.Tasks; + +namespace FrostFS.SDK.ModelsV2; + +public interface IObjectReader : IDisposable +{ + Task ReadChunk(); +} diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs index b4236b1..51c4630 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs @@ -1,3 +1,4 @@ +using System; using System.Security.Cryptography; using FrostFS.SDK.ModelsV2.Enums; @@ -5,8 +6,11 @@ namespace FrostFS.SDK.ModelsV2; public class Object { - public Object() + public Object(ObjectId objectId, ObjectHeader header, byte[] payload) { + ObjectId = objectId; + Payload = payload; + Header = header; } public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular) @@ -14,43 +18,47 @@ public class Object Payload = payload; Header = new ObjectHeader(containerId: container, type: objectType, attributes: []); } - + public ObjectHeader Header { get; set; } - public ObjectId ObjectId { get; set; } + + public ObjectId? ObjectId + { + get; internal set; + } + public byte[] Payload { get; set; } - public Signature Signature { get; set; } + + public Signature? Signature { get; set; } public void SetParent(LargeObject largeObject) { - Header.Split.ParentHeader = largeObject.Header; - } + if (Header?.Split == null) + throw new Exception("The object is not initialized properly"); - public ObjectId? GetParentId() - { - return Header.Split?.Parent; + Header.Split.ParentHeader = largeObject.Header; } } public class LargeObject(ContainerId container) : Object(container, []) { - private SHA256 payloadHash = SHA256.Create(); + private readonly SHA256 payloadHash = SHA256.Create(); public void AppendBlock(byte[] bytes, int count) { - Header.PayloadLength += (ulong)count; + Header!.PayloadLength += (ulong)count; this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0); } public LargeObject CalculateHash() { this.payloadHash.TransformFinalBlock([], 0, 0); - Header.PayloadCheckSum = this.payloadHash.Hash; + Header!.PayloadCheckSum = this.payloadHash.Hash; return this; } public ulong PayloadLength { - get { return Header.PayloadLength; } + get { return Header!.PayloadLength; } } } @@ -58,7 +66,7 @@ public class LinkObject : Object { public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, []) { - Header.Split = new Split(splitId) + Header!.Split = new Split(splitId) { ParentHeader = largeObject.Header }; diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs index db9b867..83bee87 100644 --- a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs +++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs @@ -22,8 +22,6 @@ public class ObjectHeader public Split? Split { get; set; } - public bool ClientCut { get; set; } - public ObjectHeader( ContainerId containerId, ObjectType type = ObjectType.Regular, diff --git a/src/FrostFS.SDK.ModelsV2/ObjectId.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs similarity index 100% rename from src/FrostFS.SDK.ModelsV2/ObjectId.cs rename to src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs diff --git a/src/FrostFS.SDK.ModelsV2/OwnerId.cs b/src/FrostFS.SDK.ModelsV2/OwnerId.cs index 85cf239..8746572 100644 --- a/src/FrostFS.SDK.ModelsV2/OwnerId.cs +++ b/src/FrostFS.SDK.ModelsV2/OwnerId.cs @@ -4,14 +4,9 @@ using FrostFS.SDK.Cryptography; namespace FrostFS.SDK.ModelsV2; -public class OwnerId +public class OwnerId(string id) { - public string Value { get; } - - public OwnerId(string id) - { - Value = id; - } + public string Value { get; } = id; public static OwnerId FromKey(ECDsa key) { diff --git a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs new file mode 100644 index 0000000..23cbc38 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace FrostFS.SDK.ModelsV2; + +public class PutObjectParameters +{ + public ObjectHeader? Header { get; set; } + + public Stream? Payload { get; set; } + + public bool ClientCut { get; set; } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ModelsV2/SessionToken.cs b/src/FrostFS.SDK.ModelsV2/SessionToken.cs new file mode 100644 index 0000000..6fde99e --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/SessionToken.cs @@ -0,0 +1,8 @@ +namespace FrostFS.SDK.ModelsV2; + +public class SessionToken(byte[] sessionKey, byte[] id) +{ + public byte[] Id { get; } = id; + + public byte[] SessionKey { get; } = sessionKey; +} diff --git a/src/FrostFS.SDK.ModelsV2/Signature.cs b/src/FrostFS.SDK.ModelsV2/Signature.cs new file mode 100644 index 0000000..1d36e1c --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/Signature.cs @@ -0,0 +1,8 @@ +namespace FrostFS.SDK.ModelsV2; + +public class Signature +{ + public byte[]? Key { get; set; } + public byte[]? Sign { get; set; } + public SignatureScheme Scheme { get; set; } +} diff --git a/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs b/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs new file mode 100644 index 0000000..bd6c41d --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs @@ -0,0 +1,7 @@ +namespace FrostFS.SDK.ModelsV2; + +public enum SignatureScheme { + EcdsaSha512, + EcdsaRfc6979Sha256, + EcdsaRfc6979Sha256WalletConnect + } diff --git a/src/FrostFS.SDK.ModelsV2/SplitId.cs b/src/FrostFS.SDK.ModelsV2/SplitId.cs new file mode 100644 index 0000000..eddbd14 --- /dev/null +++ b/src/FrostFS.SDK.ModelsV2/SplitId.cs @@ -0,0 +1,50 @@ +using System; + +namespace FrostFS.SDK.ModelsV2; + +public class SplitId +{ + private Guid id; + + public SplitId() + { + this.id = Guid.NewGuid(); + } + public SplitId(Guid guid) + { + this.id = guid; + } + + private SplitId(byte[] binary) + { + this.id = new Guid(binary); + } + + private SplitId(string str) + { + this.id = new Guid(str); + } + + public static SplitId CreateFromBinary(byte[] binaryData) + { + return new SplitId(binaryData); + } + + public static SplitId CreateFromString(string stringData) + { + return new SplitId(stringData); + } + + public override string ToString() + { + return this.id.ToString(); + } + + public byte[]? ToBinary() + { + if (this.id == Guid.Empty) + return null; + + return this.id.ToByteArray(); + } +} diff --git a/src/FrostFS.SDK.ModelsV2/Splitter.cs b/src/FrostFS.SDK.ModelsV2/Splitter.cs index f48f903..d4622df 100644 --- a/src/FrostFS.SDK.ModelsV2/Splitter.cs +++ b/src/FrostFS.SDK.ModelsV2/Splitter.cs @@ -1,20 +1,14 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace FrostFS.SDK.ModelsV2; -public class Split +public class Split(SplitId splitId) { public Split() : this(new SplitId()) { } - public Split(SplitId splitId) - { - SplitId = splitId; - } - - public SplitId SplitId { get; private set; } + public SplitId SplitId { get; private set; } = splitId; public ObjectId? Parent { get; set; } @@ -26,63 +20,3 @@ public class Split public List Children { get; } = []; } - - public enum SignatureScheme { - EcdsaSha512, - EcdsaRfc6979Sha256, - EcdsaRfc6979Sha256WalletConnect - } - -public class Signature -{ - public byte[] Key { get; set; } - public byte[] Sign { get; set; } - public SignatureScheme Scheme { get; set; } -} - -public class SplitId -{ - private Guid id; - - public SplitId() - { - this.id = Guid.NewGuid(); - } - public SplitId(Guid guid) - { - this.id = guid; - } - - private SplitId(byte[] binary) - { - this.id = new Guid(binary); - } - - private SplitId(string str) - { - this.id = new Guid(str); - } - - public static SplitId CrateFromBinary(byte[] binaryData) - { - return new SplitId(binaryData); - } - - public static SplitId CrateFromString(string stringData) - { - return new SplitId(stringData); - } - - public override string ToString() - { - return this.id.ToString(); - } - - public byte[]? ToBinary() - { - if (this.id == Guid.Empty) - return null; - - return this.id.ToByteArray(); - } -} diff --git a/src/FrostFS.SDK.ModelsV2/Status.cs b/src/FrostFS.SDK.ModelsV2/Status.cs index 2d56441..d52d189 100644 --- a/src/FrostFS.SDK.ModelsV2/Status.cs +++ b/src/FrostFS.SDK.ModelsV2/Status.cs @@ -2,21 +2,12 @@ using FrostFS.SDK.ModelsV2.Enums; namespace FrostFS.SDK.ModelsV2; -public class Status +public class Status(StatusCode code, string? message = null) { - public StatusCode Code { get; set; } - public string Message { get; set; } + public StatusCode Code { get; set; } = code; + public string Message { get; set; } = message ?? string.Empty; - public Status(StatusCode code, string? message = null) - { - Code = code; - Message = message ?? string.Empty; - } - - public bool IsSuccess() - { - return Code == StatusCode.Success; - } + public bool IsSuccess => Code == StatusCode.Success; public override string ToString() { diff --git a/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj index 746a8b4..0a559ed 100644 --- a/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj +++ b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj @@ -6,6 +6,10 @@ enable + + true + + diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs index f75db43..7efad42 100644 --- a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs @@ -1,6 +1,6 @@ namespace FrostFS.Session; -public interface IRequest : IVerificableMessage +public interface IRequest : IVerifiableMessage { RequestMetaHeader MetaHeader { get; set; } RequestVerificationHeader VerifyHeader { get; set; } diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs index 609be8e..ac6382a 100644 --- a/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs @@ -1,6 +1,6 @@ namespace FrostFS.Session; -public interface IResponse : IVerificableMessage +public interface IResponse : IVerifiableMessage { ResponseMetaHeader MetaHeader { get; set; } ResponseVerificationHeader VerifyHeader { get; set; } diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs index 3903757..57fce64 100644 --- a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs +++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs @@ -2,7 +2,7 @@ using Google.Protobuf; namespace FrostFS.Session; -public interface IVerificableMessage : IMessage +public interface IVerifiableMessage : IMessage { IMetaHeader GetMetaHeader(); void SetMetaHeader(IMetaHeader metaHeader); diff --git a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs index a46ddc2..d8bd601 100644 --- a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs @@ -6,22 +6,22 @@ namespace FrostFS.Container; public partial class AnnounceUsedSpaceRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -34,22 +34,22 @@ public partial class AnnounceUsedSpaceRequest : IRequest public partial class AnnounceUsedSpaceResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -62,22 +62,22 @@ public partial class AnnounceUsedSpaceResponse : IResponse public partial class GetRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -90,22 +90,22 @@ public partial class GetRequest : IRequest public partial class GetResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -118,22 +118,22 @@ public partial class GetResponse : IResponse public partial class PutRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -146,22 +146,22 @@ public partial class PutRequest : IRequest public partial class PutResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -174,22 +174,22 @@ public partial class PutResponse : IResponse public partial class DeleteRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -202,22 +202,22 @@ public partial class DeleteRequest : IRequest public partial class DeleteResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -230,22 +230,22 @@ public partial class DeleteResponse : IResponse public partial class ListRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -258,22 +258,22 @@ public partial class ListRequest : IRequest public partial class ListResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -286,22 +286,22 @@ public partial class ListResponse : IResponse public partial class SetExtendedACLRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -314,22 +314,22 @@ public partial class SetExtendedACLRequest : IRequest public partial class SetExtendedACLResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -342,22 +342,22 @@ public partial class SetExtendedACLResponse : IResponse public partial class GetExtendedACLRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -370,22 +370,22 @@ public partial class GetExtendedACLRequest : IRequest public partial class GetExtendedACLResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } diff --git a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs index 207568d..ba2d11e 100644 --- a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs @@ -5,22 +5,22 @@ namespace FrostFS.Netmap; public partial class LocalNodeInfoRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -33,22 +33,22 @@ public partial class LocalNodeInfoRequest : IRequest public partial class LocalNodeInfoResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -61,22 +61,22 @@ public partial class LocalNodeInfoResponse : IResponse public partial class NetworkInfoRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -89,22 +89,22 @@ public partial class NetworkInfoRequest : IRequest public partial class NetworkInfoResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -114,3 +114,60 @@ public partial class NetworkInfoResponse : IResponse return Body; } } + + +public partial class NetmapSnapshotRequest : IRequest +{ + IMetaHeader IVerifiableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerifiableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (RequestMetaHeader)metaHeader; + } + + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (RequestVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} + +public partial class NetmapSnapshotResponse : IResponse +{ + IMetaHeader IVerifiableMessage.GetMetaHeader() + { + return MetaHeader; + } + + IVerificationHeader IVerifiableMessage.GetVerificationHeader() + { + return VerifyHeader; + } + + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) + { + MetaHeader = (ResponseMetaHeader)metaHeader; + } + + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + { + VerifyHeader = (ResponseVerificationHeader)verificationHeader; + } + + public IMessage GetBody() + { + return Body; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs index a7cd446..de933fb 100644 --- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs @@ -6,22 +6,22 @@ namespace FrostFS.Object { public partial class GetRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -34,22 +34,22 @@ namespace FrostFS.Object public partial class GetResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -62,22 +62,22 @@ namespace FrostFS.Object public partial class PutRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -90,22 +90,22 @@ namespace FrostFS.Object public partial class PutResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -118,22 +118,22 @@ namespace FrostFS.Object public partial class PutSingleRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -146,22 +146,22 @@ namespace FrostFS.Object public partial class PutSingleResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -174,22 +174,22 @@ namespace FrostFS.Object public partial class DeleteRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -203,25 +203,25 @@ namespace FrostFS.Object public partial class DeleteResponse : IResponse { [DebuggerStepThrough] - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } [DebuggerStepThrough] - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } [DebuggerStepThrough] - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } [DebuggerStepThrough] - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -235,22 +235,22 @@ namespace FrostFS.Object public partial class HeadRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -263,22 +263,22 @@ namespace FrostFS.Object public partial class HeadResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -290,22 +290,22 @@ namespace FrostFS.Object } public partial class SearchRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -318,22 +318,22 @@ namespace FrostFS.Object public partial class SearchResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -346,22 +346,22 @@ namespace FrostFS.Object public partial class GetRangeRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -374,22 +374,22 @@ namespace FrostFS.Object public partial class GetRangeResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -402,22 +402,22 @@ namespace FrostFS.Object public partial class GetRangeHashRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } @@ -430,22 +430,22 @@ namespace FrostFS.Object public partial class GetRangeHashResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } diff --git a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs index fd78adc..937af24 100644 --- a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs +++ b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs @@ -4,22 +4,22 @@ namespace FrostFS.Session; public partial class CreateResponse : IResponse { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (ResponseMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (ResponseVerificationHeader)verificationHeader; } @@ -32,22 +32,22 @@ public partial class CreateResponse : IResponse public partial class CreateRequest : IRequest { - IMetaHeader IVerificableMessage.GetMetaHeader() + IMetaHeader IVerifiableMessage.GetMetaHeader() { return MetaHeader; } - IVerificationHeader IVerificableMessage.GetVerificationHeader() + IVerificationHeader IVerifiableMessage.GetVerificationHeader() { return VerifyHeader; } - void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) + void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader) { MetaHeader = (RequestMetaHeader)metaHeader; } - void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) + void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) { VerifyHeader = (RequestVerificationHeader)verificationHeader; } diff --git a/src/FrostFS.SDK.Tests/ClientTest.cs b/src/FrostFS.SDK.Tests/ClientTest.cs new file mode 100644 index 0000000..e543b7f --- /dev/null +++ b/src/FrostFS.SDK.Tests/ClientTest.cs @@ -0,0 +1,87 @@ +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; +using Microsoft.Extensions.Options; + +namespace FrostFS.SDK.Tests; + +public class ClientTest +{ + private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + + [Fact] + public async void CreateContainerTest() + { + var factory = new PutContainerMock(this.key) + { + PlacementPolicy = new PlacementPolicy(true, new Replica(1)), + Version = new ModelsV2.Version(2, 13), + ContainerGuid = Guid.NewGuid() + }; + + var settings = Options.Create(new ClientSettings + { + Key = key, + Host = "http://localhost:8080" + }); + + var fsClient = Client.GetTestInstance( + settings, + null, + new NetmapMock(this.key).GetMock().Object, + new SessionMock(this.key).GetMock().Object, + factory.GetMock().Object, + new ObjectMock(this.key).GetMock().Object); + + var result = await fsClient.CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, factory.PlacementPolicy)); + + Assert.NotNull(result); + Assert.NotNull(result.Value); + Assert.True(Base58.Encode(factory.ContainerGuid.ToBytes()) == result.Value); + } + + [Fact] + public async void GetContainerTest() + { + var factory = new GetContainerMock(this.key) + { + PlacementPolicy = new PlacementPolicy(true, new Replica(1)), + Version = new ModelsV2.Version(2, 13), + Acl = BasicAcl.PublicRO, + ContainerGuid = Guid.NewGuid(), + }; + + var settings = Options.Create(new ClientSettings + { + Key = key, + Host = "http://localhost:8080" + }); + + var fsClient = Client.GetTestInstance( + settings, + null, + new NetmapMock(this.key).GetMock().Object, + new SessionMock(this.key).GetMock().Object, + factory.GetMock().Object, + new ObjectMock(this.key).GetMock().Object); + + var cid = new ContainerId(Base58.Encode(factory.ContainerGuid.ToBytes())); + + var context = new Context(); + + var result = await fsClient.GetContainerAsync(cid, context); + + Assert.NotNull(result); + Assert.Equal(factory.Acl, result.BasicAcl); + Assert.Equal(factory.ContainerGuid, result.Nonce); + Assert.Equal(0, factory.PlacementPolicy.CompareTo(result.PlacementPolicy)); + Assert.Equal(factory.Version.ToString(), result.Version!.ToString()); + } + + // [Fact] + // public async void DeleteObjectAsyncTest() + // { + // } +} diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/ClientTestLive.cs new file mode 100644 index 0000000..70dda66 --- /dev/null +++ b/src/FrostFS.SDK.Tests/ClientTestLive.cs @@ -0,0 +1,229 @@ +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Interfaces; +using FrostFS.SDK.ModelsV2; +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; +using Grpc.Core; +using Grpc.Net.Client; +using Microsoft.Extensions.Options; + +namespace FrostFS.SDK.Tests; + +public class ClientTestLive +{ + private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq"; + private readonly string url = "http://172.29.238.97:8080"; + + [Fact] + public async void NetworkMapTest() + { + var channelOptions = new GrpcChannelOptions + { + Credentials = ChannelCredentials.Insecure, + HttpHandler = new HttpClientHandler() + }; + + using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions); + + var result = await fsClient.GetNetmapSnapshotAsync(); + + Assert.True(result.Epoch > 0); + Assert.Single(result.NodeInfoCollection); + + var item = result.NodeInfoCollection[0]; + Assert.Equal(2, item.Version.Major); + Assert.Equal(13, item.Version.Minor); + Assert.Equal(NodeState.Online, item.State); + Assert.True(item.PublicKey.Length > 0); + Assert.Single(item.Addresses); + Assert.Equal(9, item.Attributes.Count); + } + + [Fact] + public async void NodeInfoTest() + { + var channelOptions = new GrpcChannelOptions + { + Credentials = ChannelCredentials.Insecure, + HttpHandler = new HttpClientHandler() + }; + + using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions); + + var result = await fsClient.GetNodeInfoAsync(); + + Assert.Equal(2, result.Version.Major); + Assert.Equal(13, result.Version.Minor); + Assert.Equal(NodeState.Online, result.State); + Assert.True(result.PublicKey.Length > 0); + Assert.Single(result.Addresses); + Assert.Equal(9, result.Attributes.Count); + } + + [Fact] + public async void SimpleScenarioTest() + { + var channelOptions = new GrpcChannelOptions + { + Credentials = ChannelCredentials.Insecure, + HttpHandler = new HttpClientHandler() + }; + + using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions); + + await Cleanup(fsClient); + + var containerId = await fsClient.CreateContainerAsync( + new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)))); + + var context = new Context { Timeout = TimeSpan.FromSeconds(10) }; + + var container = await GetContainer(fsClient, containerId, context); + + Assert.NotNull(container); + + var param = new PutObjectParameters + { + Header = new ObjectHeader( + containerId: containerId, + type: ObjectType.Regular, + new ObjectAttribute("fileName", "test")), + Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), + ClientCut = false + }; + + var objectId = await fsClient.PutObjectAsync(param); + + var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test"); + + bool hasObject = false; + await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter])) + { + hasObject = true; + + var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); + Assert.Equal(10u, objHeader.PayloadLength); + Assert.Single(objHeader.Attributes); + Assert.Equal("fileName", objHeader.Attributes.First().Key); + Assert.Equal("test", objHeader.Attributes.First().Value); + } + + Assert.True(hasObject); + + var @object = await fsClient.GetObjectAsync(containerId, objectId!); + + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload); + + await Cleanup(fsClient); + + await Task.Delay(2000); + + await foreach (var _ in fsClient.ListContainersAsync()) + { + Assert.Fail("Containers exist"); + } + } + + [Fact] + public async void ClientCutScenarioTest() + { + var channelOptions = new GrpcChannelOptions + { + Credentials = ChannelCredentials.Insecure, + HttpHandler = new HttpClientHandler() + }; + + using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions); + + await Cleanup(fsClient); + + var containerId = await fsClient.CreateContainerAsync( + new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)))); + + var context = new Context { Timeout = TimeSpan.FromSeconds(10) }; + var container = await GetContainer(fsClient, containerId, context); + + Assert.NotNull(container); + + var param = new PutObjectParameters + { + Header = new ObjectHeader( + containerId: containerId, + type: ObjectType.Regular, + new ObjectAttribute("fileName", "test")), + Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), + ClientCut = true + }; + + var objectId = await fsClient.PutObjectAsync(param); + + var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test"); + + bool hasObject = false; + await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter])) + { + hasObject = true; + + var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId); + Assert.Equal(10u, objHeader.PayloadLength); + Assert.Single(objHeader.Attributes); + Assert.Equal("fileName", objHeader.Attributes.First().Key); + Assert.Equal("test", objHeader.Attributes.First().Value); + } + + Assert.True(hasObject); + + var @object = await fsClient.GetObjectAsync(containerId, objectId!); + + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload); + + await Cleanup(fsClient); + + await Task.Delay(2000); + + await foreach (var _ in fsClient.ListContainersAsync()) + { + Assert.Fail("Containers exist"); + } + } + + private static IOptions GetOptions(string key, string url) + { + var settings = new ClientSettings + { + Key = key, + Host = url + }; + + return Options.Create(settings); + } + + static async Task Cleanup(IFrostFSClient fsClient) + { + await foreach (var cid in fsClient.ListContainersAsync()) + { + await fsClient.DeleteContainerAsync(cid); + } + } + + static async Task GetContainer(IFrostFSClient fsClient, ContainerId id, Context ctx) + { + while (true) + { + try + { + await Task.Delay(100); + return await fsClient.GetContainerAsync(id, ctx); + } + catch (ApplicationException) + { + if (DateTime.UtcNow >= ctx.Deadline) + throw new TimeoutException(); + } + catch (Grpc.Core.RpcException) + { + throw; + } + } + } +} diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs new file mode 100644 index 0000000..bbf1476 --- /dev/null +++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs @@ -0,0 +1,28 @@ +using System.Security.Cryptography; +using FrostFS.Container; +using Moq; + +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ModelsV2.Enums; +using FrostFS.SDK.ModelsV2.Netmap; + +namespace FrostFS.SDK.Tests; + +public abstract class ServiceBase(string key) +{ + public ECDsa Key { get; private set; } = key.LoadWif(); + public ModelsV2.Version Version { get; set; } = DefaultVersion; + public BasicAcl Acl { get; set; } = DefaultAcl; + public PlacementPolicy PlacementPolicy { get; set; } = DefaultPlacementPolicy; + + public static ModelsV2.Version DefaultVersion { get; } = new(2, 13); + public static BasicAcl DefaultAcl { get; } = BasicAcl.PublicRW; + public static PlacementPolicy DefaultPlacementPolicy { get; } = new PlacementPolicy(true, new Replica(1)); +} + +public abstract class ContainerServiceBase(string key) : ServiceBase (key) +{ + public Guid ContainerGuid { get; set; } = Guid.NewGuid(); + + public abstract Mock GetMock(); +} diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs new file mode 100644 index 0000000..a752d10 --- /dev/null +++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs @@ -0,0 +1,69 @@ +using FrostFS.Container; +using FrostFS.Session; +using Google.Protobuf; +using Grpc.Core; +using Moq; + +namespace FrostFS.SDK.Tests; + +public class DeleteContainerMock(string key) : ContainerServiceBase(key) +{ + public override Mock GetMock() + { + var mock = new Mock(); + + var v = mock.Setup(x => x.DeleteAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())); + + + v.Returns((Object.DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + var deleteResponse = new Object.DeleteResponse + { + Body = new Object.DeleteResponse.Types.Body + { + Tombstone = new Refs.Address + { + ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) }, + ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) } + } + }, + MetaHeader = new ResponseMetaHeader() + }; + + var metadata = new Metadata(); + + return new AsyncUnaryCall( + Task.FromResult(deleteResponse), + Task.FromResult(metadata), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => metadata, + () => { }); + }); + + return mock; + } +} + + + // objectServiceClientMock.Setup(x => x.Head(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + // .Returns((Object.DeleteRequest r, Metadata m, DateTime dt, CancellationToken ct) => + // { + // return new + // { + // Body = new Object.DeleteResponse.Types.Body + // { + // Tombstone = new Refs.Address + // { + // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) }, + // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) } + // } + // } + // }; + // }); + + + \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs new file mode 100644 index 0000000..61feb5e --- /dev/null +++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs @@ -0,0 +1,163 @@ +using FrostFS.Container; +using FrostFS.Session; +using Google.Protobuf; +using Grpc.Core; +using Moq; + +using FrostFS.SDK.Cryptography; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ModelsV2.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC; + +namespace FrostFS.SDK.Tests; + +public class GetContainerMock(string key) : ContainerServiceBase(key) +{ + public override Mock GetMock() + { + var mock = new Mock(); + + var grpcVersion = Version.ToGrpcMessage(); + + var getResponse = new GetResponse + { + Body = new GetResponse.Types.Body + { + Container = new Container.Container + { + Version = grpcVersion, + Nonce = ByteString.CopyFrom(ContainerGuid.ToBytes()), + BasicAcl = (uint)Acl, + PlacementPolicy = PlacementPolicy.ToGrpcMessage() + } + }, + MetaHeader = new ResponseMetaHeader + { + Version = grpcVersion, + Epoch = 100, + Ttl = 1 + } + }; + + getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse); + + var metadata = new Metadata(); + var getContainerResponse = new AsyncUnaryCall( + Task.FromResult(getResponse), + Task.FromResult(metadata), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => metadata, + () => { }); + + mock.Setup(x => x.GetAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + return getContainerResponse; + }); + + return mock; + } + + private ResponseVerificationHeader GetResponseVerificationHeader(GetResponse response) + { + var verifyHeader = new ResponseVerificationHeader + { + MetaSignature = new Refs.Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray())) + }, + BodySignature = new Refs.Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray())) + }, + OriginSignature = new Refs.Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData([])) + } + }; + + return verifyHeader; + } +} + +// objectServiceClientMock.Setup( +// x => x.Put(It.IsAny(), It.IsAny(), It.IsAny())) +// .Returns((Metadata m, DateTime dt, CancellationToken ct) => +// { +// return new AsyncClientStreamingCall(null, null, null, null, null, null); + +// //IClientStreamWriter requestStream, Task responseAsync, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction +// }); + +// return objectServiceClientMock; +// } + + +// } + + + // public virtual global::FrostFS.Object.HeadResponse Head(global::FrostFS.Object.HeadRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + // { + // return Head(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + // } + + // objectServiceClientMock.Setup(x => x.Head(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + // .Returns((Object.DeleteRequest r, Metadata m, DateTime dt, CancellationToken ct) => + // { + // return new + // { + // Body = new Object.DeleteResponse.Types.Body + // { + // Tombstone = new Refs.Address + // { + // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) }, + // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) } + // } + // } + // }; + // }); + + + // objectServiceClientMock.Setup(x => x.Delete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + // .Returns((Object.DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + // { + // return new Object.DeleteResponse + // { + // Body = new Object.DeleteResponse.Types.Body + // { + // Tombstone = new Refs.Address + // { + // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) }, + // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) } + // } + // }, + // MetaHeader = new ResponseMetaHeader() + // { + // }, + // VerifyHeader = new ResponseVerificationHeader() + // { + // MetaSignature = new Refs.Signature + // { + // Key = ByteString.CopyFrom(_key.PublicKey()), + // Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, + // Sign = ByteString.CopyFrom(_key.SignData(Array.Empty())) + + // // ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())), + // } + // } + + // }; + // }); \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs new file mode 100644 index 0000000..3243bdf --- /dev/null +++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs @@ -0,0 +1,88 @@ +using FrostFS.Container; +using FrostFS.Session; +using Google.Protobuf; +using Grpc.Core; +using Moq; +using FrostFS.SDK.ClientV2; +using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap; +using FrostFS.SDK.ClientV2.Mappers.GRPC; +using FrostFS.SDK.Cryptography; +using FrostFS.Refs; + +namespace FrostFS.SDK.Tests; + +public class PutContainerMock(string key) : ContainerServiceBase(key) +{ + public override Mock GetMock() + { + var mock = new Mock(); + + PutResponse response = new() + { + Body = new PutResponse.Types.Body + { + ContainerId = new ContainerID + { + Value = ByteString.CopyFrom(ContainerGuid.ToBytes()) + } + }, + MetaHeader = new ResponseMetaHeader + { + Version = Version is null ? DefaultVersion.ToGrpcMessage() : Version.ToGrpcMessage(), + Epoch = 100, + Ttl = 1 + } + }; + + response.VerifyHeader = PutResponseVerificationHeader(response); + + var metadata = new Metadata(); + var putContainerResponse = new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(metadata), + () => new Grpc.Core.Status(StatusCode.OK, string.Empty), + () => metadata, + () => { }); + + mock.Setup(x => x.PutAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) => + { + Verifier.CheckRequest(r); + + return putContainerResponse; + }); + + return mock; + } + + private ResponseVerificationHeader PutResponseVerificationHeader(PutResponse response) + { + var verifyHeader = new ResponseVerificationHeader + { + MetaSignature = new Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray())) + }, + BodySignature = new Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray())) + }, + OriginSignature = new Signature + { + Key = ByteString.CopyFrom(Key.PublicKey()), + Scheme = SignatureScheme.EcdsaRfc6979Sha256, + Sign = ByteString.CopyFrom(Key.SignData([])) + } + }; + + return verifyHeader; + } +} \ No newline at end of file diff --git a/src/FrostFS.SDK.Tests/NetmapMock.cs b/src/FrostFS.SDK.Tests/NetmapMock.cs new file mode 100644 index 0000000..0cc3889 --- /dev/null +++ b/src/FrostFS.SDK.Tests/NetmapMock.cs @@ -0,0 +1,14 @@ +using Moq; +using FrostFS.Netmap; + +namespace FrostFS.SDK.Tests; + +public class NetmapMock(string key) : ServiceBase(key) +{ + public Mock GetMock() + { + var mock = new Mock(); + + return mock; + } +} diff --git a/src/FrostFS.SDK.Tests/ObjectMock.cs b/src/FrostFS.SDK.Tests/ObjectMock.cs new file mode 100644 index 0000000..96296f8 --- /dev/null +++ b/src/FrostFS.SDK.Tests/ObjectMock.cs @@ -0,0 +1,14 @@ +using Moq; +using FrostFS.Object; + +namespace FrostFS.SDK.Tests; + +public class ObjectMock(string key) : ServiceBase(key) +{ + public Mock GetMock() + { + var mock = new Mock(); + + return mock; + } +} diff --git a/src/FrostFS.SDK.Tests/SessionMock.cs b/src/FrostFS.SDK.Tests/SessionMock.cs new file mode 100644 index 0000000..8309fde --- /dev/null +++ b/src/FrostFS.SDK.Tests/SessionMock.cs @@ -0,0 +1,14 @@ +using Moq; +using FrostFS.Session; + +namespace FrostFS.SDK.Tests; + +public class SessionMock(string key) : ServiceBase(key) +{ + public Mock GetMock() + { + var mock = new Mock(); + + return mock; + } +}