[#10] MetricsAndOptions #10

Closed
PavelGrossSpb wants to merge 5 commits from PavelGrossSpb:MetricsAndOptions into master
84 changed files with 2314 additions and 1016 deletions

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ vendor/
# IDE # IDE
.idea .idea
.vscode .vscode
.vs
# coverage # coverage
coverage.txt coverage.txt

View file

@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# #
VisualStudioVersion = 17.5.002.0 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}" 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU {5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
GlobalSection(SolutionProperties) = preSolution {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU
HideSolutionNode = FALSE {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 EndGlobalSection
EndGlobal EndGlobal

143
README.md
View file

@ -25,133 +25,50 @@ using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap; using FrostFS.SDK.ModelsV2.Netmap;
using Microsoft.Extensions.Options;
var fsClient = Client.GetInstance(<your_key>, <your_host>);
// List containers var options = Options.Create(new ClientSettings
var containersIds = await fsClient.ListContainersAsync(); {
Key = <your_key>,
Host = <your_host>
});
var fsClient = Client.GetInstance(options);
// Mertics
fsClient.GrpcInvoked += (sender, info) =>
{
Console.WriteLine($"Invoked: {info.MethodName}. Elapsed time: {info.ElapsedTimeMicroSec}");
};
// Create container
var placementPolicy = new PlacementPolicy(true, new Replica(1)); var placementPolicy = new PlacementPolicy(true, new Replica(1));
var containerId = await fsClient.CreateContainerAsync(
new Container(
BasicAcl.PublicRW,
placementPolicy
)
);
// Get container var containerId = await fsClient.CreateContainerAsync(new Container(BasicAcl.PublicRW, placementPolicy));
var container = await fsClient.GetContainerAsync(cId);
// Delete container
await fsClient.DeleteContainerAsync(containerId);
```
### Object
```csharp
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
var fsClient = Client.GetInstance(<your_key>, <your_host>);
// Search regular objects
var objectsIds = await fsClient.SearchObjectAsync(
cId,
ObjectFilter.RootFilter()
);
// Put object // Put object
var f = File.OpenRead("cat.jpg"); var fileStream = File.OpenRead("cat.jpg");
var cat = new ObjectHeader( var header = new ObjectHeader(
containerId: cId, containerId: containerId,
type: ObjectType.Regular, type: ObjectType.Regular,
new ObjectAttribute("Filename", "cat.jpg") new ObjectAttribute("Filename", "cat.jpg")
); );
var oId = await fsClient.PutObjectAsync(cat, f);
// Get object header var param = new PutObjectParameters
var objHeader = await fsClient.GetObjectHeadAsync(cId, oId);
// Get object
var obj = await fsClient.GetObjectAsync(cId, oId);
```
### Custom client cut
```csharp
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.SDK.ClientV2.Extensions;
using FrostFS.SDK.ClientV2.Interfaces;
var fsClient = Client.GetInstance(<your_key>, <your_host>);
ContainerId containerId = <containerId>;
string fileName = <fileName>;
await PutObjectClientCut(fsClient, containerId, fileName);
static async Task<ObjectId?> PutObjectClientCut(IFrostFSClient fsClient, ContainerId containerId, string fileName)
{ {
List<ObjectId> sentObjectIds = []; Header = header,
FrostFS.SDK.ModelsV2.Object? currentObject; Payload = fileStream
};
var partSize = 1024 * 1024; var oId = await fsClient.PutObjectAsync(param);
var buffer = new byte[partSize];
var largeObject = new LargeObject(containerId); // Search regular objects
await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [ObjectFilter.RootFilter()]))
{
// Get object header
var info = await fsClient.GetObjectHeadAsync(containerId, objId);
var split = new Split(); // Get object
var obj = await fsClient.GetObjectAsync(containerId, oId);
var fileInfo = new FileInfo(fileName);
var fullLength = (ulong)fileInfo.Length;
var fileNameAttribute = new ObjectAttribute("fileName", fileInfo.Name);
using var stream = File.OpenRead(fileName);
while (true)
{
var bytesCount = await stream.ReadAsync(buffer.AsMemory(0, partSize));
split.Previous = sentObjectIds.LastOrDefault();
largeObject.AppendBlock(buffer, bytesCount);
currentObject = new FrostFS.SDK.ModelsV2.Object(containerId, bytesCount < partSize ? buffer.Take(bytesCount).ToArray() : buffer)
.AddAttribute(fileNameAttribute)
.SetSplit(split);
if (largeObject.PayloadLength == fullLength)
break;
var objectId = await fsClient.PutSingleObjectAsync(currentObject);
sentObjectIds.Add(objectId);
}
if (sentObjectIds.Any())
{
largeObject.CalculateHash()
.AddAttribute(fileNameAttribute);
currentObject.SetParent(largeObject);
var objectId = await fsClient.PutSingleObjectAsync(currentObject);
sentObjectIds.Add(objectId);
var linkObject = new LinkObject(containerId, split.SplitId, largeObject)
.AddChildren(sentObjectIds)
.AddAttribute(fileNameAttribute);
_ = await fsClient.PutSingleObjectAsync(linkObject);
return currentObject.GetParentId();
}
return await fsClient.PutSingleObjectAsync(currentObject);
} }
``` ```

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography; using System.Data.Common;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FrostFS.Container; using FrostFS.Container;
using FrostFS.Netmap; using FrostFS.Netmap;
@ -9,77 +8,203 @@ using FrostFS.Object;
using FrostFS.SDK.ClientV2.Interfaces; using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.Session; using FrostFS.Session;
using Grpc.Core; using Grpc.Core;
using Grpc.Net.Client; using Grpc.Net.Client;
using static FrostFS.Netmap.NetworkConfig.Types; using Microsoft.Extensions.Options;
using Version = FrostFS.SDK.ModelsV2.Version; using Version = FrostFS.SDK.ModelsV2.Version;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
public partial class Client: IFrostFSClient public class Client : IFrostFSClient, IDisposable
{ {
private GrpcChannel? _channel; private bool isDisposed;
private readonly ECDsa _key;
public readonly OwnerId OwnerId;
public readonly Version Version = new(2, 13);
private readonly Dictionary<string, ulong> NetworkSettings = []; internal ClientEnvironment ClientCtx { get; set; }
private ContainerService.ContainerServiceClient? _containerServiceClient; public static IFrostFSClient GetInstance(IOptions<ClientSettings> options)
private NetmapService.NetmapServiceClient? _netmapServiceClient;
private ObjectService.ObjectServiceClient? _objectServiceClient;
private SessionService.SessionServiceClient? _sessionServiceClient;
public static IFrostFSClient GetInstance(string key, string host)
{ {
return new Client(key, host); return new Client(options);
} }
private Client(string key, string host) /// <summary>
/// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
/// </summary>
/// <param name="key">User's key</param>
/// <param name="containerService">ContainerService.ContainerServiceClient implementation</param>
/// <param name="netmapService">Netmap.NetmapService.NetmapServiceClient implementation</param>
/// <param name="sessionService">Session.SessionService.SessionServiceClient implementation</param>
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
/// <returns></returns>
public static IFrostFSClient GetTestInstance(
IOptions<ClientSettings> settings,
NetmapService.NetmapServiceClient netmapService,
SessionService.SessionServiceClient sessionService,
ContainerService.ContainerServiceClient containerService,
ObjectService.ObjectServiceClient objectService)
{ {
// TODO: Развязать клиент и реализацию GRPC return new Client(settings, containerService, netmapService, sessionService, objectService);
_key = key.LoadWif(); }
OwnerId = OwnerId.FromKey(_key);
InitGrpcChannel(host); private Client(
InitContainerClient(); IOptions<ClientSettings> settings,
InitNetmapClient(); ContainerService.ContainerServiceClient containerService,
InitObjectClient(); NetmapService.NetmapServiceClient netmapService,
InitSessionClient(); 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),
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<ClientSettings> options)
{
var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
clientSettings.Validate();
var ecdsaKey = clientSettings.Key.LoadWif();
var channel = InitGrpcChannel(clientSettings.Host);
ClientCtx = new ClientEnvironment(
key: ecdsaKey,
owner: OwnerId.FromKey(ecdsaKey),
channel: InitGrpcChannel(clientSettings.Host),
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(); CheckFrostFsVersionSupport();
InitNetworkInfoAsync();
} }
private async void InitNetworkInfoAsync() event EventHandler<GrpcCallInfo> IFrostFSClient.GrpcInvoked
{ {
var info = await GetNetworkInfoAsync(); add
foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
{ {
SetNetworksParam(param); ClientCtx.GrpcInvokedEvent += value;
}
remove
{
ClientCtx.GrpcInvokedEvent -= value;
} }
} }
private void SetNetworksParam(Parameter param) public void Dispose()
{ {
var key = Encoding.UTF8.GetString(param.Key.ToByteArray()); Dispose(true);
GC.SuppressFinalize(this);
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() protected virtual void Dispose(bool disposing)
{ {
var localNodeInfo = await GetLocalNodeInfoAsync(); if (disposing && !isDisposed)
if (!localNodeInfo.Version.IsSupported(Version)) {
ClientCtx.Dispose();
isDisposed = true;
}
}
public Task<ModelsV2.Container> GetContainerAsync(ContainerId containerId, Context? ctx = null)
{
ValidateEnvironment(ref ctx);
return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!);
}
public IAsyncEnumerable<ContainerId> ListContainersAsync(Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.ContainerService!.ListContainersAsync(ctx!);
}
public Task<ContainerId> 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<NetmapSnapshot> GetNetmapSnapshotAsync(Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!);
}
public Task<ModelsV2.Netmap.NodeInfo> GetNodeInfoAsync(Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
}
public Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!);
}
public Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!);
}
public Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
{
ValidateEnvironment(ref ctx);
return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!);
}
public Task<ObjectId> 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<ObjectId> SearchObjectsAsync(
ContainerId containerId,
IEnumerable<ObjectFilter> 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."; var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
Console.WriteLine(msg); Console.WriteLine(msg);
@ -87,7 +212,18 @@ public partial class Client: IFrostFSClient
} }
} }
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)
{ {
Uri uri; Uri uri;
try try
@ -116,30 +252,10 @@ public partial class Client: IFrostFSClient
throw new ArgumentException(msg); throw new ArgumentException(msg);
} }
_channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
{ {
Credentials = grpcCredentials, Credentials = grpcCredentials,
HttpHandler = new System.Net.Http.HttpClientHandler() 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);
} }
} }

View file

@ -6,9 +6,14 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="Moq.AutoMock" Version="3.5.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,34 +1,39 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Netmap;
namespace FrostFS.SDK.ClientV2.Interfaces; namespace FrostFS.SDK.ClientV2.Interfaces;
public interface IFrostFSClient public interface IFrostFSClient
{ {
Task<ModelsV2.Container> GetContainerAsync(ContainerId containerId); Task<ModelsV2.Container> GetContainerAsync(ContainerId containerId, Context? context = default);
IAsyncEnumerable<ContainerId> ListContainersAsync(); IAsyncEnumerable<ContainerId> ListContainersAsync(Context? context = default);
Task<ContainerId> CreateContainerAsync(ModelsV2.Container container); Task<ContainerId> CreateContainerAsync(ModelsV2.Container container, Context? context = default);
Task DeleteContainerAsync(ContainerId containerId); Task DeleteContainerAsync(ContainerId containerId, Context? context = default);
Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId); Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId); Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default);
Task<ObjectId> PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default); Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default);
Task<ObjectId> PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default); Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, CancellationToken cancellationToken = default); IAsyncEnumerable<ObjectId> SearchObjectsAsync(ContainerId cid, IEnumerable<ObjectFilter> filters, Context? context = default);
Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId); Task<NetmapSnapshot> GetNetmapSnapshotAsync(Context? context = default);
IAsyncEnumerable<ObjectId> SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters); Task<NodeInfo> GetNodeInfoAsync(Context? context = default);
ObjectId CalculateObjectId(ObjectHeader header);
event EventHandler<GrpcCallInfo> GrpcInvoked;
} }

View file

@ -22,16 +22,12 @@ public static class ContainerMapper
public static ModelsV2.Container ToModel(this Container.Container container) public static ModelsV2.Container ToModel(this Container.Container container)
{ {
var basicAclName = Enum.GetName(typeof(BasicAcl), container.BasicAcl); if (!Enum.IsDefined(typeof(BasicAcl),(int)container.BasicAcl))
if (basicAclName is null)
{
throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'."); throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'.");
}
return new ModelsV2.Container( BasicAcl acl = (BasicAcl)container.BasicAcl;
(BasicAcl)Enum.Parse(typeof(BasicAcl), basicAclName),
container.PlacementPolicy.ToModel() return new ModelsV2.Container(acl, container.PlacementPolicy.ToModel())
)
{ {
Nonce = container.Nonce.ToUuid(), Nonce = container.Nonce.ToUuid(),
Version = container.Version.ToModel() Version = container.Version.ToModel()

View file

@ -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()
};
}
}

View file

@ -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());
}
}

View file

@ -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()
);
}
}

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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
};
}
}

View file

@ -4,52 +4,10 @@ using FrostFS.Object;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using Google.Protobuf; using Google.Protobuf;
using MatchType = FrostFS.Object.MatchType;
using ObjectType = FrostFS.Object.ObjectType; using ObjectType = FrostFS.Object.ObjectType;
namespace FrostFS.SDK.ClientV2.Mappers.GRPC; 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 class ObjectHeaderMapper
{ {
public static Header ToGrpcMessage(this ObjectHeader header) public static Header ToGrpcMessage(this ObjectHeader header)
@ -130,37 +88,3 @@ public static class ObjectHeaderMapper
return model; 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)
};
}
}

View file

@ -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;
}
}

View file

@ -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)
};
}
}

View file

@ -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<ModelsV2.Container> 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<ContainerId> 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<ContainerId> 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);
}
}

View file

@ -0,0 +1,117 @@
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<ModelsV2.Container> 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 Context.InvokeAsyncUnaryWithMetrics(() =>
containerServiceClient.GetAsync(request, null, context.Deadline, context.CancellationToken),
nameof(containerServiceClient.GetAsync));
Verifier.CheckResponse(response);
return response.Body.Container.ToModel();
}
internal async IAsyncEnumerable<ContainerId> 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 Context.InvokeAsyncUnaryWithMetrics(() =>
containerServiceClient.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(containerServiceClient.ListAsync));
Verifier.CheckResponse(response);
foreach (var cid in response.Body.ContainerIds)
{
yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray()));
}
}
internal async Task<ContainerId> 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 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 Context.InvokeAsyncUnaryWithMetrics(() =>
containerServiceClient.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(containerServiceClient.DeleteAsync));
Verifier.CheckResponse(response);
}
}

View file

@ -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<NodeInfo> 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<NetworkInfoResponse> GetNetworkInfoAsync()
{
var request = new NetworkInfoRequest
{
Body = new NetworkInfoRequest.Types.Body { }
};
request.AddMetaHeader();
request.Sign(_key);
return await _netmapServiceClient.NetworkInfoAsync(request);
}
}

View file

@ -0,0 +1,134 @@
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<NetworkSettings> 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<NodeInfo> GetLocalNodeInfoAsync(Context ctx)
{
var request = new LocalNodeInfoRequest
{
Body = new LocalNodeInfoRequest.Types.Body { }
};
request.AddMetaHeader();
request.Sign(Context.Key);
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<NetworkInfoResponse> GetNetworkInfoAsync(Context ctx)
{
var request = new NetworkInfoRequest
{
Body = new NetworkInfoRequest.Types.Body { }
};
request.AddMetaHeader();
request.Sign(Context.Key);
var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(netmapServiceClient.NetworkInfoAsync));
Verifier.CheckResponse(response);
return response;
}
internal async Task<NetmapSnapshot> GetNetmapSnapshotAsync(Context ctx)
{
var request = new NetmapSnapshotRequest
{
Body = new NetmapSnapshotRequest.Types.Body { }
};
request.AddMetaHeader();
request.Sign(Context.Key);
var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(netmapServiceClient.NetmapSnapshotAsync));
Verifier.CheckResponse(response);
return response.ToModel();
}
private bool GetBoolValue(byte[] bytes)
{
return bytes.Any(b => b != 0);
}
private ulong GetLongValue(byte[] bytes)
{
ulong val = 0;
for (var i = bytes.Length - 1; i >= 0; i--)
val = (val << 8) + bytes[i];
return val;
}
private 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;
}
}
}

View file

@ -7,20 +7,20 @@ using FrostFS.Object;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
internal class ObjectReader : IDisposable internal class ObjectReader(AsyncServerStreamingCall<GetResponse> call) : IDisposable
{ {
public AsyncServerStreamingCall<GetResponse> Call { get; set; } public AsyncServerStreamingCall<GetResponse> Call { get; private set; } = call;
public async Task<Object.Object> ReadHeader() public async Task<Object.Object> ReadHeader()
{ {
if (!await Call.ResponseStream.MoveNext()) if (!await Call.ResponseStream.MoveNext())
throw new InvalidOperationException("unexpect end of stream"); throw new InvalidOperationException("unexpected end of stream");
var response = Call.ResponseStream.Current; var response = Call.ResponseStream.Current;
Verifier.CheckResponse(response); Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init) 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 return new Object.Object
{ {
@ -38,7 +38,7 @@ internal class ObjectReader : IDisposable
Verifier.CheckResponse(response); Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk) 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(); return response.Body.Chunk.ToByteArray();
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,16 +10,22 @@ using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using FrostFS.Session; using FrostFS.Session;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ClientV2.Extensions; using FrostFS.SDK.ClientV2.Extensions;
using System.Threading;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
public partial class Client internal class ObjectServiceProvider : ContextAccessor
{ {
public async Task<ObjectHeader> 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<ObjectHeader> GetObjectHeadAsync(ContainerId cid, ObjectId oid, Context ctx)
{ {
var request = new HeadRequest var request = new HeadRequest
{ {
@ -34,18 +39,22 @@ public partial class Client
} }
}; };
request.AddMetaHeader(); request.AddMetaHeader();
request.Sign(_key); request.Sign(Context.Key);
var response = await _objectServiceClient!.HeadAsync(request);
var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
objectServiceClient!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.HeadAsync));
Verifier.CheckResponse(response); Verifier.CheckResponse(response);
return response.Body.Header.Header.ToModel(); return response.Body.Header.Header.ToModel();
} }
public async Task<ModelsV2.Object> GetObjectAsync(ContainerId cid, ObjectId oid) internal async Task<ModelsV2.Object> GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
{ {
var sessionToken = await CreateSessionAsync(uint.MaxValue); var sessionToken = await GetOrCreateSession(ctx);
var request = new GetRequest var request = new GetRequest
{ {
Body = new GetRequest.Types.Body Body = new GetRequest.Types.Body
@ -64,41 +73,137 @@ public partial class Client
cid.ToGrpcMessage(), cid.ToGrpcMessage(),
oid.ToGrpcMessage(), oid.ToGrpcMessage(),
ObjectSessionContext.Types.Verb.Get, ObjectSessionContext.Types.Verb.Get,
_key Context.Key
); );
request.Sign(_key); request.Sign(Context.Key);
var obj = await GetObject(request);
var obj = await GetObject(request, ctx);
return obj.ToModel(); return obj.ToModel();
} }
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default) internal Task<ObjectId> PutObjectAsync(PutObjectParameters parameters, Context ctx)
{ {
return await PutObject(header, payload, cancellationToken); if (parameters.Header == null)
} throw new ArgumentException("Value cannot be null", nameof(parameters.Header));
public async Task<ObjectId> PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default) if (parameters.Payload == null)
{ throw new ArgumentException("Value cannot be null", nameof(parameters.Payload));
using var stream = new MemoryStream(payload);
return await PutObject(header, stream, cancellationToken);
}
private Task<ObjectId> PutObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken) if (parameters.ClientCut)
{ return PutClientCutObject(parameters, ctx);
if (header.ClientCut)
return PutClientCutObject(header, payload, cancellationToken);
else else
return PutStreamObject(header, payload, cancellationToken); return PutStreamObject(parameters, ctx);
} }
private async Task<ObjectId> PutClientCutObject(ObjectHeader header, Stream payloadStream, CancellationToken cancellationToken) internal async Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object @object, Context ctx)
{ {
ObjectId? objectId = null; 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 Context.InvokeAsyncUnaryWithMetrics(() =>
objectServiceClient!.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.PutSingleAsync));
Verifier.CheckResponse(response);
return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
}
internal ObjectId CalculateObjectId(ObjectHeader header)
{
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 Context.InvokeAsyncUnaryWithMetrics(() =>
objectServiceClient!.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.DeleteAsync));
Verifier.CheckResponse(response);
}
internal async IAsyncEnumerable<ObjectId> SearchObjectsAsync(
ContainerId cid,
IEnumerable<ObjectFilter> 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<ObjectId> PutClientCutObject(PutObjectParameters parameters, Context ctx)
{
var payloadStream = parameters.Payload!;
var header = parameters.Header!;
ObjectId? objectId;
List<ObjectId> sentObjectIds = []; List<ObjectId> sentObjectIds = [];
ModelsV2.Object? currentObject; ModelsV2.Object? currentObject;
var partSize = (int)NetworkSettings["MaxObjectSize"]; var networkSettings = await Context.InvokeAsyncWithMetrics(() =>
Context.NetmapService!.GetNetworkSettingsAsync(ctx),
nameof(Context.NetmapService.GetNetworkSettingsAsync));
var partSize = (int)networkSettings.MaxObjectSize;
var buffer = new byte[partSize]; var buffer = new byte[partSize];
var largeObject = new LargeObject(header.ContainerId); var largeObject = new LargeObject(header.ContainerId);
@ -109,8 +214,6 @@ public partial class Client
while (true) while (true)
{ {
cancellationToken.ThrowIfCancellationRequested();
var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize); var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize);
split.Previous = sentObjectIds.LastOrDefault(); split.Previous = sentObjectIds.LastOrDefault();
@ -124,7 +227,7 @@ public partial class Client
if (largeObject.PayloadLength == fullLength) if (largeObject.PayloadLength == fullLength)
break; break;
objectId = await PutSingleObjectAsync(currentObject, cancellationToken); objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId!); sentObjectIds.Add(objectId!);
} }
@ -135,26 +238,30 @@ public partial class Client
currentObject.SetParent(largeObject); currentObject.SetParent(largeObject);
objectId = await PutSingleObjectAsync(currentObject, cancellationToken); objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId); sentObjectIds.Add(objectId);
var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject) var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject)
.AddChildren(sentObjectIds); .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<ObjectId> PutStreamObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken) private async Task<ObjectId> 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(); var hdr = header.ToGrpcMessage();
hdr.OwnerId = OwnerId.ToGrpcMessage(); hdr.OwnerId = Context.Owner.ToGrpcMessage();
hdr.Version = Version.ToGrpcMessage(); hdr.Version = Context.Version.ToGrpcMessage();
var oid = new ObjectID var oid = new ObjectID
{ {
@ -178,19 +285,17 @@ public partial class Client
hdr.ContainerId, hdr.ContainerId,
oid, oid,
ObjectSessionContext.Types.Verb.Put, 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]; var buffer = new byte[Constants.ObjectChunkSize];
while (true) while (true)
{ {
cancellationToken.ThrowIfCancellationRequested(); var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize, ctx.CancellationToken);
var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
if (bufferLength == 0) if (bufferLength == 0)
break; break;
@ -201,7 +306,7 @@ public partial class Client
}; };
request.VerifyHeader = null; request.VerifyHeader = null;
request.Sign(_key); request.Sign(Context.Key);
await stream.Write(request); await stream.Write(request);
} }
@ -211,40 +316,98 @@ public partial class Client
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()); return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
} }
public async Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object @object, CancellationToken cancellationToken = default) // TODO: add implementation with stream writer!
private async Task<Object.Object> GetObject(GetRequest request, Context ctx)
{ {
var sessionToken = await CreateSessionAsync(uint.MaxValue); using var stream = GetObjectInit(request, ctx);
var obj = CreateObject(@object); var obj = await stream.ReadHeader();
var payload = new byte[obj.Header.PayloadLength];
var offset = 0L;
var chunk = await stream.ReadChunk();
var request = new PutSingleRequest while (chunk is not null && (ulong)offset < obj.Header.PayloadLength)
{ {
Body = new () { Object = obj } var length = Math.Min((long)obj.Header.PayloadLength - offset, chunk.Length);
};
request.AddMetaHeader(); Array.Copy(chunk, 0, payload, offset, length);
request.AddObjectSessionToken( offset += chunk.Length;
sessionToken, chunk = await stream.ReadChunk();
obj.Header.ContainerId, }
obj.ObjectId,
ObjectSessionContext.Types.Verb.Put,
_key
);
request.Sign(_key); obj.Payload = ByteString.CopyFrom(payload);
var response = await _objectServiceClient!.PutSingleAsync(request, null, null, cancellationToken); return obj;
Verifier.CheckResponse(response);
return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
} }
public Object.Object CreateObject(ModelsV2.Object @object) private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx)
{
if (initRequest is null)
throw new ArgumentNullException(nameof(initRequest));
var call = Context.InvokeWithMetrics(() =>
objectServiceClient!.Get(initRequest, null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.Get));
return new ObjectReader(call);
}
private async Task<ObjectStreamer> PutObjectInit(PutRequest initRequest, Context ctx)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
var call = Context.InvokeWithMetrics(() =>
objectServiceClient!.Put(null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.Put));
await call.RequestStream.WriteAsync(initRequest);
return new ObjectStreamer(call);
}
private async IAsyncEnumerable<ObjectID> SearchObjects(SearchRequest request, Context ctx)
{
using var stream = GetSearchReader(request, ctx);
while (true)
{
var ids = await Context.InvokeAsyncWithMetrics(() =>
stream.Read(ctx.CancellationToken),
"SearchObject.ReadStream");
if (ids == null)
break;
foreach (var oid in ids)
{
yield return oid;
}
}
}
private SearchReader GetSearchReader(SearchRequest initRequest, Context ctx)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
var call = Context.InvokeWithMetrics(() =>
objectServiceClient!.Search(initRequest, null, ctx.Deadline, ctx.CancellationToken),
nameof(objectServiceClient.Search));
return new SearchReader(call);
}
private Object.Object CreateObject(ModelsV2.Object @object)
{ {
var grpcHeader = @object.Header.ToGrpcMessage(); var grpcHeader = @object.Header.ToGrpcMessage();
grpcHeader.OwnerId = OwnerId.ToGrpcMessage(); grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
grpcHeader.Version = Version.ToGrpcMessage(); grpcHeader.Version = Context.Version.ToGrpcMessage();
if (@object.Payload != null) if (@object.Payload != null)
{ {
@ -271,8 +434,8 @@ public partial class Client
grpcHeader.Split.ParentHeader = grpcParentHeader; grpcHeader.Split.ParentHeader = grpcParentHeader;
grpcHeader.Split.ParentSignature = new Refs.Signature grpcHeader.Split.ParentSignature = new Refs.Signature
{ {
Key = ByteString.CopyFrom(_key.PublicKey()), Key = ByteString.CopyFrom(Context.Key.PublicKey()),
Sign = ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())), Sign = ByteString.CopyFrom(Context.Key.SignData(grpcHeader.Split.Parent.ToByteArray())),
}; };
split.Parent = grpcHeader.Split.Parent.ToModel(); split.Parent = grpcHeader.Split.Parent.ToModel();
@ -290,159 +453,29 @@ public partial class Client
obj.Signature = new Refs.Signature obj.Signature = new Refs.Signature
{ {
Key = ByteString.CopyFrom(_key.PublicKey()), Key = ByteString.CopyFrom(Context.Key.PublicKey()),
Sign = ByteString.CopyFrom(_key.SignData(obj.ObjectId.ToByteArray())), Sign = ByteString.CopyFrom(Context.Key.SignData(obj.ObjectId.ToByteArray())),
}; };
return obj; return obj;
} }
public Header CreateHeader(ObjectHeader header, byte[]? payload) private Header CreateHeader(ObjectHeader header, byte[]? payload)
{ {
var grpcHeader = header.ToGrpcMessage(); var grpcHeader = header.ToGrpcMessage();
grpcHeader.OwnerId = OwnerId.ToGrpcMessage(); grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
grpcHeader.Version = Version.ToGrpcMessage(); grpcHeader.Version = Context.Version.ToGrpcMessage();
if (header.PayloadCheckSum != null) if (header.PayloadCheckSum != null)
{ grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum);
grpcHeader.PayloadHash = new Checksum else if (payload != null)
{ grpcHeader.PayloadHash = Sha256Checksum(payload);
Type = ChecksumType.Sha256,
Sum = ByteString.CopyFrom(header.PayloadCheckSum)
};
}
else
{
if (payload != null)
grpcHeader.PayloadHash = Sha256Checksum(payload);
}
return grpcHeader; return grpcHeader;
} }
public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid) private Checksum Sha256Checksum(byte[] data)
{
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<ObjectId> 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<Object.Object> GetObject(GetRequest request)
{
using var stream = GetObjectInit(request);
var obj = await stream.ReadHeader();
var payload = new byte[obj.Header.PayloadLength];
var offset = 0L;
var chunk = await stream.ReadChunk();
while (chunk is not null && (ulong)offset < obj.Header.PayloadLength)
{
var length = Math.Min((long)obj.Header.PayloadLength - offset, chunk.Length);
Array.Copy(chunk, 0, payload, offset, length);
offset += chunk.Length;
chunk = await stream.ReadChunk();
}
obj.Payload = ByteString.CopyFrom(payload);
return obj;
}
private ObjectReader GetObjectInit(GetRequest initRequest)
{
if (initRequest is null)
throw new ArgumentNullException(nameof(initRequest));
return new ObjectReader
{
Call = _objectServiceClient!.Get(initRequest)
};
}
private async Task<ObjectStreamer> PutObjectInit(PutRequest initRequest)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
var call = _objectServiceClient!.Put();
await call.RequestStream.WriteAsync(initRequest);
return new ObjectStreamer(call);
}
private async IAsyncEnumerable<ObjectID> SearchObjects(SearchRequest request)
{
using var stream = GetSearchReader(request);
var ids = await stream.Read();
while (ids is not null)
{
foreach (var oid in ids)
{
yield return oid;
}
ids = await stream.Read();
}
}
private SearchReader GetSearchReader(SearchRequest initRequest)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
return new SearchReader(_objectServiceClient!.Search(initRequest));
}
public Checksum Sha256Checksum(byte[] data)
{ {
return new Checksum return new Checksum
{ {
@ -450,8 +483,16 @@ public partial class Client
Sum = ByteString.CopyFrom(data.Sha256()) Sum = ByteString.CopyFrom(data.Sha256())
}; };
} }
private async Task<Session.SessionToken> GetOrCreateSession(Context ctx)
{
if (string.IsNullOrEmpty(ctx.SessionToken))
{
return await Context.InvokeAsyncWithMetrics(() =>
Context.SessionService!.CreateSessionAsync(uint.MaxValue, ctx),
nameof(Context.SessionService.CreateSessionAsync));
}
return Convert.FromBase64String(ctx.SessionToken).DeserializeSessionToken();
}
} }

View file

@ -7,6 +7,7 @@ using Grpc.Core;
using FrostFS.Object; using FrostFS.Object;
using FrostFS.Refs; using FrostFS.Refs;
using System.Threading;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
@ -14,14 +15,13 @@ internal class SearchReader(AsyncServerStreamingCall<SearchResponse> call) : IDi
{ {
public AsyncServerStreamingCall<SearchResponse> Call { get; private set; } = call; public AsyncServerStreamingCall<SearchResponse> Call { get; private set; } = call;
public async Task<List<ObjectID>?> Read() public async Task<List<ObjectID>?> Read(CancellationToken cancellationToken)
{ {
if (!await Call.ResponseStream.MoveNext()) if (!await Call.ResponseStream.MoveNext(cancellationToken))
{
return null; return null;
}
var response = Call.ResponseStream.Current; var response = Call.ResponseStream.Current;
Verifier.CheckResponse(response); Verifier.CheckResponse(response);
return response.Body?.IdList.ToList(); return response.Body?.IdList.ToList();

View file

@ -5,28 +5,38 @@ using FrostFS.Session;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
public partial class Client internal class SessionServiceProvider : ContextAccessor
{ {
private async Task<SessionToken> CreateSessionAsync(ulong expiration) private readonly SessionService.SessionServiceClient? _sessionServiceClient;
internal SessionServiceProvider(SessionService.SessionServiceClient? sessionServiceClient, ClientEnvironment context)
: base (context)
{
_sessionServiceClient = sessionServiceClient;
}
internal async Task<SessionToken> CreateSessionAsync(ulong expiration, Context ctx)
{ {
var request = new CreateRequest var request = new CreateRequest
{ {
Body = new CreateRequest.Types.Body Body = new CreateRequest.Types.Body
{ {
OwnerId = OwnerId.ToGrpcMessage(), OwnerId = Context.Owner.ToGrpcMessage(),
Expiration = expiration, Expiration = expiration
} }
}; };
request.AddMetaHeader(); request.AddMetaHeader();
request.Sign(_key); request.Sign(Context.Key);
return await CreateSession(request); var token = await CreateSession(request, ctx);
return token;
} }
private async Task<SessionToken> CreateSession(CreateRequest request) internal async Task<SessionToken> 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 return new SessionToken
{ {

View file

@ -0,0 +1,116 @@
using System.Security.Cryptography;
using Grpc.Net.Client;
using FrostFS.SDK.ModelsV2;
using System;
using System.Threading.Tasks;
using Grpc.Core;
using System.Diagnostics;
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 event EventHandler<GrpcCallInfo>? GrpcInvokedEvent;
internal bool IsEventRequested => GrpcInvokedEvent != null;
internal async Task<T> InvokeAsyncWithMetrics<T>(Func<Task<T>> func, string methodName)
{
if (!IsEventRequested)
return await func();
var watch = Stopwatch.StartNew();
bool exitWithException = false;
try
{
return await func();
}
catch
{
exitWithException = true;
throw;
}
finally
{
watch.Stop();
GrpcInvokedEvent?.Invoke(this, new GrpcCallInfo(methodName, watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency, exitWithException));
}
}
internal T InvokeWithMetrics<T>(Func<T> func, string methodName)
{
if (!IsEventRequested)
return func();
var watch = Stopwatch.StartNew();
bool exitWithException = false;
try
{
return func();
}
catch
{
exitWithException = true;
throw;
}
finally
{
watch.Stop();
GrpcInvokedEvent?.Invoke(this, new GrpcCallInfo(methodName, watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency, exitWithException));
}
}
internal async Task<T> InvokeAsyncUnaryWithMetrics<T>(Func<AsyncUnaryCall<T>> func, string methodName)
{
if (!IsEventRequested)
return await func();
var watch = Stopwatch.StartNew();
bool exitWithException = false;
try
{
return await func();
}
catch
{
exitWithException = true;
throw;
}
finally
{
watch.Stop();
GrpcInvokedEvent?.Invoke(this, new GrpcCallInfo(methodName, watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency, exitWithException));
}
}
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();
}
}
}

View file

@ -0,0 +1,8 @@
namespace FrostFS.SDK.ClientV2;
internal class ContextAccessor(ClientEnvironment context)
{
protected ClientEnvironment Context { get; set; } = context;
}

View file

@ -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<string, object> UnnamedSettings { get; } = [];
}

View file

@ -1,10 +1,11 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Extensions; namespace FrostFS.SDK.ClientV2.Extensions;
public static class Extensions public static class ObjectExtensions
{ {
public static ModelsV2.Object SetPayloadLength(this ModelsV2.Object obj, ulong length) 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); linkObject.Header.Split!.Children.AddRange(objectIds);
return linkObject; 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;
}
} }

View file

@ -41,10 +41,10 @@ namespace System
} }
/// <summary>Create an Index pointing at first element.</summary> /// <summary>Create an Index pointing at first element.</summary>
public static Index Start => new Index(0); public static Index Start => new(0);
/// <summary>Create an Index pointing at beyond last element.</summary> /// <summary>Create an Index pointing at beyond last element.</summary>
public static Index End => new Index(~0); public static Index End => new(~0);
/// <summary>Create an Index from the start at the position indicated by the value.</summary> /// <summary>Create an Index from the start at the position indicated by the value.</summary>
/// <param name="value">The index value from the start.</param> /// <param name="value">The index value from the start.</param>
@ -116,7 +116,7 @@ namespace System
/// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary> /// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary>
/// <param name="value">An object to compare with this object</param> /// <param name="value">An object to compare with this object</param>
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;
/// <summary>Indicates whether the current Index object is equal to another Index object.</summary> /// <summary>Indicates whether the current Index object is equal to another Index object.</summary>
/// <param name="other">An object to compare with this object</param> /// <param name="other">An object to compare with this object</param>
@ -147,22 +147,16 @@ namespace System
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
/// </code> /// </code>
/// </remarks> /// </remarks>
internal readonly struct Range : IEquatable<Range> /// <remarks>Construct a Range object using the start and end indexes.</remarks>
/// <param name="start">Represent the inclusive start index of the range.</param>
/// <param name="end">Represent the exclusive end index of the range.</param>
internal readonly struct Range(Index start, Index end) : IEquatable<Range>
{ {
/// <summary>Represent the inclusive start index of the Range.</summary> /// <summary>Represent the inclusive start index of the Range.</summary>
public Index Start { get; } public Index Start { get; } = start;
/// <summary>Represent the exclusive end index of the Range.</summary> /// <summary>Represent the exclusive end index of the Range.</summary>
public Index End { get; } public Index End { get; } = end;
/// <summary>Construct a Range object using the start and end indexes.</summary>
/// <param name="start">Represent the inclusive start index of the range.</param>
/// <param name="end">Represent the exclusive end index of the range.</param>
public Range(Index start, Index end)
{
Start = start;
End = end;
}
/// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary> /// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary>
/// <param name="value">An object to compare with this object</param> /// <param name="value">An object to compare with this object</param>
@ -188,13 +182,13 @@ namespace System
} }
/// <summary>Create a Range object starting from start index to the end of the collection.</summary> /// <summary>Create a Range object starting from start index to the end of the collection.</summary>
public static Range StartAt(Index start) => new Range(start, Index.End); public static Range StartAt(Index start) => new(start, Index.End);
/// <summary>Create a Range object starting from first element in the collection to the end Index.</summary> /// <summary>Create a Range object starting from first element in the collection to the end Index.</summary>
public static Range EndAt(Index end) => new Range(Index.Start, end); public static Range EndAt(Index end) => new(Index.Start, end);
/// <summary>Create a Range object starting from first element to the end.</summary> /// <summary>Create a Range object starting from first element to the end.</summary>
public static Range All => new Range(Index.Start, Index.End); public static Range All => new(Index.Start, Index.End);
/// <summary>Calculate the start offset and length of range object using a collection length.</summary> /// <summary>Calculate the start offset and length of range object using a collection length.</summary>
/// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param> /// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param>
@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices
if (length == 0) if (length == 0)
{ {
return Array.Empty<T>(); return [];
} }
var dest = new T[length]; var dest = new T[length];

View file

@ -10,20 +10,24 @@ public static class RequestConstructor
{ {
public static void AddMetaHeader(this IRequest request, RequestMetaHeader? metaHeader = null) public static void AddMetaHeader(this IRequest request, RequestMetaHeader? metaHeader = null)
{ {
if (request.MetaHeader is not null) return; if (request.MetaHeader is not null)
return;
metaHeader ??= MetaHeader.Default().ToGrpcMessage(); metaHeader ??= MetaHeader.Default().ToGrpcMessage();
request.MetaHeader = metaHeader; request.MetaHeader = metaHeader;
} }
public static void AddObjectSessionToken( public static void AddObjectSessionToken(
this IRequest request, this IRequest request,
SessionToken sessionToken, Session.SessionToken sessionToken,
ContainerID cid, ContainerID cid,
ObjectID oid, ObjectID oid,
ObjectSessionContext.Types.Verb verb, ObjectSessionContext.Types.Verb verb,
ECDsa key) ECDsa key)
{ {
if (request.MetaHeader.SessionToken is not null) return; if (request.MetaHeader.SessionToken is not null)
return;
request.MetaHeader.SessionToken = sessionToken; request.MetaHeader.SessionToken = sessionToken;
var ctx = new ObjectSessionContext var ctx = new ObjectSessionContext
{ {
@ -34,6 +38,7 @@ public static class RequestConstructor
}, },
Verb = verb Verb = verb
}; };
request.MetaHeader.SessionToken.Body.Object = ctx; request.MetaHeader.SessionToken.Body.Object = ctx;
request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body); request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body);
} }

View file

@ -84,7 +84,7 @@ public static class RequestSigner
return sig; return sig;
} }
public static void Sign(this IVerificableMessage message, ECDsa key) public static void Sign(this IVerifiableMessage message, ECDsa key)
{ {
var meta = message.GetMetaHeader(); var meta = message.GetMetaHeader();
IVerificationHeader verify = message switch IVerificationHeader verify = message switch
@ -95,12 +95,15 @@ public static class RequestSigner
}; };
var verifyOrigin = message.GetVerificationHeader(); var verifyOrigin = message.GetVerificationHeader();
if (verifyOrigin is null) if (verifyOrigin is null)
verify.BodySignature = key.SignMessagePart(message.GetBody()); verify.BodySignature = key.SignMessagePart(message.GetBody());
else
verify.SetOrigin(verifyOrigin);
verify.MetaSignature = key.SignMessagePart(meta); verify.MetaSignature = key.SignMessagePart(meta);
verify.OriginSignature = key.SignMessagePart(verifyOrigin); verify.OriginSignature = key.SignMessagePart(verifyOrigin);
verify.SetOrigin(verifyOrigin);
message.SetVerificationHeader(verify); message.SetVerificationHeader(verify);
} }
} }

View file

@ -68,7 +68,7 @@ public static class Verifier
return false; return false;
using var key = sig.Key.ToByteArray().LoadPublicKey(); using var key = sig.Key.ToByteArray().LoadPublicKey();
var data2Verify = data is null ? Array.Empty<byte>() : data.ToByteArray(); var data2Verify = data is null ? [] : data.ToByteArray();
return key.VerifyData(data2Verify, sig.Sign.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); 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()); return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader());
} }
@ -100,7 +100,17 @@ public static class Verifier
throw new FormatException($"invalid response, type={resp.GetType()}"); throw new FormatException($"invalid response, type={resp.GetType()}");
var status = resp.MetaHeader.Status.ToModel(); var status = resp.MetaHeader.Status.ToModel();
if (!status.IsSuccess()) if (!status.IsSuccess)
throw new ApplicationException(status.ToString()); throw new ApplicationException(status.ToString());
} }
/// <summary>
/// This method is intended for unit tests for request verification.
/// </summary>
/// <param name="request">Created by SDK request to gRpc proxy</param>
public static void CheckRequest(IRequest request)
{
if (!request.Verify())
throw new FormatException($"invalid response, type={request.GetType()}");
}
} }

View file

@ -17,4 +17,4 @@ using System.Runtime.InteropServices;
[assembly: Guid("08a8487e-39ce-41fb-9c24-13f73ff2bde0")] [assembly: Guid("08a8487e-39ce-41fb-9c24-13f73ff2bde0")]
[assembly: InternalsVisibleToAttribute("FrostFS.SDK.Cryptography.Test")] [assembly: InternalsVisibleTo("FrostFS.SDK.Cryptography.Test")]

View file

@ -34,9 +34,11 @@ public static class Base58
byte[] checksum = data.ToArray().Sha256().Sha256(); byte[] checksum = data.ToArray().Sha256().Sha256();
Span<byte> buffer = stackalloc byte[data.Length + 4]; Span<byte> buffer = stackalloc byte[data.Length + 4];
data.CopyTo(buffer); data.CopyTo(buffer);
checksum[..4].AsSpan().CopyTo(buffer[data.Length..]); checksum[..4].AsSpan().CopyTo(buffer[data.Length..]);
var ret = Encode(buffer); var ret = Encode(buffer);
buffer.Clear(); buffer.Clear();
return ret; return ret;
} }

View file

@ -6,7 +6,7 @@ using System.Security.Cryptography;
namespace FrostFS.SDK.Cryptography; namespace FrostFS.SDK.Cryptography;
public static class Helper public static class Extentions
{ {
internal static byte[] RIPEMD160(this byte[] value) internal static byte[] RIPEMD160(this byte[] value)
{ {

View file

@ -6,6 +6,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" /> <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.27.0" /> <PackageReference Include="Google.Protobuf" Version="3.27.0" />

View file

@ -56,7 +56,7 @@ public static class KeyExtension
var script = new byte[] { 0x0c, CompressedPublicKeyLength }; //PUSHDATA1 33 var script = new byte[] { 0x0c, CompressedPublicKeyLength }; //PUSHDATA1 33
script = ArrayHelper.Concat(script, publicKey); 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 script = ArrayHelper.Concat(script, BitConverter.GetBytes(CheckSigDescriptor)); //Neo_Crypto_CheckSig
return script; return script;
@ -80,7 +80,7 @@ public static class KeyExtension
private static byte[] GetPrivateKeyFromWIF(string wif) private static byte[] GetPrivateKeyFromWIF(string wif)
{ {
if (wif == null) if (wif == null)
throw new ArgumentNullException(); throw new ArgumentNullException(nameof(wif));
var data = wif.Base58CheckDecode(); var data = wif.Base58CheckDecode();
@ -117,7 +117,7 @@ public static class KeyExtension
var pos = 33 - param.Q.X.Length; var pos = 33 - param.Q.X.Length;
param.Q.X.CopyTo(pubkey, pos); 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; pubkey[0] = 0x2;
else else
pubkey[0] = 0x3; pubkey[0] = 0x3;

View file

@ -41,10 +41,10 @@ namespace System
} }
/// <summary>Create an Index pointing at first element.</summary> /// <summary>Create an Index pointing at first element.</summary>
public static Index Start => new Index(0); public static Index Start => new(0);
/// <summary>Create an Index pointing at beyond last element.</summary> /// <summary>Create an Index pointing at beyond last element.</summary>
public static Index End => new Index(~0); public static Index End => new(~0);
/// <summary>Create an Index from the start at the position indicated by the value.</summary> /// <summary>Create an Index from the start at the position indicated by the value.</summary>
/// <param name="value">The index value from the start.</param> /// <param name="value">The index value from the start.</param>
@ -116,7 +116,7 @@ namespace System
/// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary> /// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary>
/// <param name="value">An object to compare with this object</param> /// <param name="value">An object to compare with this object</param>
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;
/// <summary>Indicates whether the current Index object is equal to another Index object.</summary> /// <summary>Indicates whether the current Index object is equal to another Index object.</summary>
/// <param name="other">An object to compare with this object</param> /// <param name="other">An object to compare with this object</param>
@ -147,22 +147,16 @@ namespace System
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
/// </code> /// </code>
/// </remarks> /// </remarks>
internal readonly struct Range : IEquatable<Range> /// <remarks>Construct a Range object using the start and end indexes.</remarks>
/// <param name="start">Represent the inclusive start index of the range.</param>
/// <param name="end">Represent the exclusive end index of the range.</param>
internal readonly struct Range (Index start, Index end) : IEquatable<Range>
{ {
/// <summary>Represent the inclusive start index of the Range.</summary> /// <summary>Represent the inclusive start index of the Range.</summary>
public Index Start { get; } public Index Start { get; } = start;
/// <summary>Represent the exclusive end index of the Range.</summary> /// <summary>Represent the exclusive end index of the Range.</summary>
public Index End { get; } public Index End { get; } = end;
/// <summary>Construct a Range object using the start and end indexes.</summary>
/// <param name="start">Represent the inclusive start index of the range.</param>
/// <param name="end">Represent the exclusive end index of the range.</param>
public Range(Index start, Index end)
{
Start = start;
End = end;
}
/// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary> /// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary>
/// <param name="value">An object to compare with this object</param> /// <param name="value">An object to compare with this object</param>
@ -188,13 +182,13 @@ namespace System
} }
/// <summary>Create a Range object starting from start index to the end of the collection.</summary> /// <summary>Create a Range object starting from start index to the end of the collection.</summary>
public static Range StartAt(Index start) => new Range(start, Index.End); public static Range StartAt(Index start) => new(start, Index.End);
/// <summary>Create a Range object starting from first element in the collection to the end Index.</summary> /// <summary>Create a Range object starting from first element in the collection to the end Index.</summary>
public static Range EndAt(Index end) => new Range(Index.Start, end); public static Range EndAt(Index end) => new(Index.Start, end);
/// <summary>Create a Range object starting from first element to the end.</summary> /// <summary>Create a Range object starting from first element to the end.</summary>
public static Range All => new Range(Index.Start, Index.End); public static Range All => new(Index.Start, Index.End);
/// <summary>Calculate the start offset and length of range object using a collection length.</summary> /// <summary>Calculate the start offset and length of range object using a collection length.</summary>
/// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param> /// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param>
@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices
if (length == 0) if (length == 0)
{ {
return Array.Empty<T>(); return [];
} }
var dest = new T[length]; var dest = new T[length];

View file

@ -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());
}
}

View file

@ -5,17 +5,10 @@ using FrostFS.SDK.ModelsV2.Netmap;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class Container public class Container(BasicAcl basicAcl, PlacementPolicy placementPolicy)
{ {
public Guid Nonce { get; set; } public Guid Nonce { get; set; } = Guid.NewGuid();
public BasicAcl BasicAcl { get; set; } public BasicAcl BasicAcl { get; set; } = basicAcl;
public PlacementPolicy PlacementPolicy { get; set; } public PlacementPolicy PlacementPolicy { get; set; } = placementPolicy;
public Version Version { get; set; } public Version? Version { get; set; }
public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy)
{
Nonce = Guid.NewGuid();
BasicAcl = basicAcl;
PlacementPolicy = placementPolicy;
}
} }

View file

@ -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;
}

View file

@ -4,6 +4,9 @@ namespace FrostFS.SDK.ModelsV2.Enums;
public enum BasicAcl public enum BasicAcl
{ {
[Description("Not defined ACL")]
NotDefined = 0x00000000,
[Description("Basic ACL for private container")] [Description("Basic ACL for private container")]
Private = 0x1C8C8CCC, Private = 0x1C8C8CCC,

View file

@ -6,6 +6,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj" /> <ProjectReference Include="..\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -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;
}

View file

@ -1,17 +1,10 @@
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class MetaHeader public class MetaHeader(Version version, int epoch, int ttl)
{ {
public Version Version { get; set; } public Version Version { get; set; } = version;
public int Epoch { get; set; } public int Epoch { get; set; } = epoch;
public int Ttl { get; set; } public int Ttl { get; set; } = ttl;
public MetaHeader(Version version, int epoch, int ttl)
{
Version = version;
Epoch = epoch;
Ttl = ttl;
}
public static MetaHeader Default() public static MetaHeader Default()
{ {

View file

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace FrostFS.SDK.ModelsV2.Netmap;
public class NetmapSnapshot(ulong epoch, IReadOnlyList<NodeInfo> nodeInfoCollection)
{
public ulong Epoch { get; private set; } = epoch;
public IReadOnlyList<NodeInfo> NodeInfoCollection { get; private set; } = nodeInfoCollection;
}

View file

@ -1,9 +1,28 @@
using System;
using System.Collections.Generic;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2.Netmap; namespace FrostFS.SDK.ModelsV2.Netmap;
public class NodeInfo public class NodeInfo
{ {
public NodeState State { get; set; } public NodeInfo(
public Version? Version { get; set; } Version version,
NodeState state,
IReadOnlyCollection<string> addresses,
IReadOnlyDictionary<string, string> attributes,
ReadOnlyMemory<byte> 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<string> Addresses { get; private set; }
public IReadOnlyDictionary<string, string> Attributes { get; private set; }
public ReadOnlyMemory<byte> PublicKey { get; private set; }
} }

View file

@ -1,13 +1,28 @@
using System;
using System.Linq;
namespace FrostFS.SDK.ModelsV2.Netmap; namespace FrostFS.SDK.ModelsV2.Netmap;
public class PlacementPolicy public class PlacementPolicy(bool unique, params Replica[] replicas) : IComparable<PlacementPolicy>
{ {
public Replica[] Replicas { get; private set; } public Replica[] Replicas { get; private set; } = replicas;
public bool Unique { get; private set; } public bool Unique { get; private set; } = unique;
public PlacementPolicy(bool unique, params Replica[] replicas) public int CompareTo(PlacementPolicy other)
{ {
Replicas = replicas; var notEqual = other == null
Unique = unique; || 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;
} }
} }

View file

@ -1,3 +1,4 @@
using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using FrostFS.SDK.ModelsV2.Enums; using FrostFS.SDK.ModelsV2.Enums;
@ -5,8 +6,11 @@ namespace FrostFS.SDK.ModelsV2;
public class Object 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) public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
@ -16,41 +20,45 @@ public class Object
} }
public ObjectHeader Header { get; set; } public ObjectHeader Header { get; set; }
public ObjectId ObjectId { get; set; }
public ObjectId? ObjectId
{
get; internal set;
}
public byte[] Payload { get; set; } public byte[] Payload { get; set; }
public Signature Signature { get; set; }
public Signature? Signature { get; set; }
public void SetParent(LargeObject largeObject) 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() Header.Split.ParentHeader = largeObject.Header;
{
return Header.Split?.Parent;
} }
} }
public class LargeObject(ContainerId container) : Object(container, []) 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) public void AppendBlock(byte[] bytes, int count)
{ {
Header.PayloadLength += (ulong)count; Header!.PayloadLength += (ulong)count;
this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0); this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0);
} }
public LargeObject CalculateHash() public LargeObject CalculateHash()
{ {
this.payloadHash.TransformFinalBlock([], 0, 0); this.payloadHash.TransformFinalBlock([], 0, 0);
Header.PayloadCheckSum = this.payloadHash.Hash; Header!.PayloadCheckSum = this.payloadHash.Hash;
return this; return this;
} }
public ulong PayloadLength 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, []) public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
{ {
Header.Split = new Split(splitId) Header!.Split = new Split(splitId)
{ {
ParentHeader = largeObject.Header ParentHeader = largeObject.Header
}; };

View file

@ -22,8 +22,6 @@ public class ObjectHeader
public Split? Split { get; set; } public Split? Split { get; set; }
public bool ClientCut { get; set; }
public ObjectHeader( public ObjectHeader(
ContainerId containerId, ContainerId containerId,
ObjectType type = ObjectType.Regular, ObjectType type = ObjectType.Regular,

View file

@ -4,14 +4,9 @@ using FrostFS.SDK.Cryptography;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class OwnerId public class OwnerId(string id)
{ {
public string Value { get; } public string Value { get; } = id;
public OwnerId(string id)
{
Value = id;
}
public static OwnerId FromKey(ECDsa key) public static OwnerId FromKey(ECDsa key)
{ {

View file

@ -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; }
}

View file

@ -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;
}

View file

@ -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; }
}

View file

@ -0,0 +1,7 @@
namespace FrostFS.SDK.ModelsV2;
public enum SignatureScheme {
EcdsaSha512,
EcdsaRfc6979Sha256,
EcdsaRfc6979Sha256WalletConnect
}

View file

@ -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 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();
}
}

View file

@ -1,20 +1,14 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class Split public class Split(SplitId splitId)
{ {
public Split() : this(new SplitId()) public Split() : this(new SplitId())
{ {
} }
public Split(SplitId splitId) public SplitId SplitId { get; private set; } = splitId;
{
SplitId = splitId;
}
public SplitId SplitId { get; private set; }
public ObjectId? Parent { get; set; } public ObjectId? Parent { get; set; }
@ -26,63 +20,3 @@ public class Split
public List<ObjectId> Children { get; } = []; public List<ObjectId> 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();
}
}

View file

@ -2,21 +2,12 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class Status public class Status(StatusCode code, string? message = null)
{ {
public StatusCode Code { get; set; } public StatusCode Code { get; set; } = code;
public string Message { get; set; } public string Message { get; set; } = message ?? string.Empty;
public Status(StatusCode code, string? message = null) public bool IsSuccess => Code == StatusCode.Success;
{
Code = code;
Message = message ?? string.Empty;
}
public bool IsSuccess()
{
return Code == StatusCode.Success;
}
public override string ToString() public override string ToString()
{ {

View file

@ -6,6 +6,10 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.1" /> <PackageReference Include="Google.Protobuf" Version="3.26.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.62.0" />

View file

@ -1,6 +1,6 @@
namespace FrostFS.Session; namespace FrostFS.Session;
public interface IRequest : IVerificableMessage public interface IRequest : IVerifiableMessage
{ {
RequestMetaHeader MetaHeader { get; set; } RequestMetaHeader MetaHeader { get; set; }
RequestVerificationHeader VerifyHeader { get; set; } RequestVerificationHeader VerifyHeader { get; set; }

View file

@ -1,6 +1,6 @@
namespace FrostFS.Session; namespace FrostFS.Session;
public interface IResponse : IVerificableMessage public interface IResponse : IVerifiableMessage
{ {
ResponseMetaHeader MetaHeader { get; set; } ResponseMetaHeader MetaHeader { get; set; }
ResponseVerificationHeader VerifyHeader { get; set; } ResponseVerificationHeader VerifyHeader { get; set; }

View file

@ -2,7 +2,7 @@ using Google.Protobuf;
namespace FrostFS.Session; namespace FrostFS.Session;
public interface IVerificableMessage : IMessage public interface IVerifiableMessage : IMessage
{ {
IMetaHeader GetMetaHeader(); IMetaHeader GetMetaHeader();
void SetMetaHeader(IMetaHeader metaHeader); void SetMetaHeader(IMetaHeader metaHeader);

View file

@ -6,22 +6,22 @@ namespace FrostFS.Container;
public partial class AnnounceUsedSpaceRequest : IRequest public partial class AnnounceUsedSpaceRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -34,22 +34,22 @@ public partial class AnnounceUsedSpaceRequest : IRequest
public partial class AnnounceUsedSpaceResponse : IResponse public partial class AnnounceUsedSpaceResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -62,22 +62,22 @@ public partial class AnnounceUsedSpaceResponse : IResponse
public partial class GetRequest : IRequest public partial class GetRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -90,22 +90,22 @@ public partial class GetRequest : IRequest
public partial class GetResponse : IResponse public partial class GetResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -118,22 +118,22 @@ public partial class GetResponse : IResponse
public partial class PutRequest : IRequest public partial class PutRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -146,22 +146,22 @@ public partial class PutRequest : IRequest
public partial class PutResponse : IResponse public partial class PutResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -174,22 +174,22 @@ public partial class PutResponse : IResponse
public partial class DeleteRequest : IRequest public partial class DeleteRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -202,22 +202,22 @@ public partial class DeleteRequest : IRequest
public partial class DeleteResponse : IResponse public partial class DeleteResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -230,22 +230,22 @@ public partial class DeleteResponse : IResponse
public partial class ListRequest : IRequest public partial class ListRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -258,22 +258,22 @@ public partial class ListRequest : IRequest
public partial class ListResponse : IResponse public partial class ListResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -286,22 +286,22 @@ public partial class ListResponse : IResponse
public partial class SetExtendedACLRequest : IRequest public partial class SetExtendedACLRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -314,22 +314,22 @@ public partial class SetExtendedACLRequest : IRequest
public partial class SetExtendedACLResponse : IResponse public partial class SetExtendedACLResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -342,22 +342,22 @@ public partial class SetExtendedACLResponse : IResponse
public partial class GetExtendedACLRequest : IRequest public partial class GetExtendedACLRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -370,22 +370,22 @@ public partial class GetExtendedACLRequest : IRequest
public partial class GetExtendedACLResponse : IResponse public partial class GetExtendedACLResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }

View file

@ -5,22 +5,22 @@ namespace FrostFS.Netmap;
public partial class LocalNodeInfoRequest : IRequest public partial class LocalNodeInfoRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -33,22 +33,22 @@ public partial class LocalNodeInfoRequest : IRequest
public partial class LocalNodeInfoResponse : IResponse public partial class LocalNodeInfoResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -61,22 +61,22 @@ public partial class LocalNodeInfoResponse : IResponse
public partial class NetworkInfoRequest : IRequest public partial class NetworkInfoRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -89,22 +89,79 @@ public partial class NetworkInfoRequest : IRequest
public partial class NetworkInfoResponse : IResponse public partial class NetworkInfoResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
public IMessage GetBody()
{
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; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }

View file

@ -6,22 +6,22 @@ namespace FrostFS.Object
{ {
public partial class GetRequest : IRequest public partial class GetRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -34,22 +34,22 @@ namespace FrostFS.Object
public partial class GetResponse : IResponse public partial class GetResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -62,22 +62,22 @@ namespace FrostFS.Object
public partial class PutRequest : IRequest public partial class PutRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -90,22 +90,22 @@ namespace FrostFS.Object
public partial class PutResponse : IResponse public partial class PutResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -118,22 +118,22 @@ namespace FrostFS.Object
public partial class PutSingleRequest : IRequest public partial class PutSingleRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -146,22 +146,22 @@ namespace FrostFS.Object
public partial class PutSingleResponse : IResponse public partial class PutSingleResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -174,22 +174,22 @@ namespace FrostFS.Object
public partial class DeleteRequest : IRequest public partial class DeleteRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -203,25 +203,25 @@ namespace FrostFS.Object
public partial class DeleteResponse : IResponse public partial class DeleteResponse : IResponse
{ {
[DebuggerStepThrough] [DebuggerStepThrough]
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
[DebuggerStepThrough] [DebuggerStepThrough]
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
[DebuggerStepThrough] [DebuggerStepThrough]
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
[DebuggerStepThrough] [DebuggerStepThrough]
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -235,22 +235,22 @@ namespace FrostFS.Object
public partial class HeadRequest : IRequest public partial class HeadRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -263,22 +263,22 @@ namespace FrostFS.Object
public partial class HeadResponse : IResponse public partial class HeadResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -290,22 +290,22 @@ namespace FrostFS.Object
} }
public partial class SearchRequest : IRequest public partial class SearchRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -318,22 +318,22 @@ namespace FrostFS.Object
public partial class SearchResponse : IResponse public partial class SearchResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -346,22 +346,22 @@ namespace FrostFS.Object
public partial class GetRangeRequest : IRequest public partial class GetRangeRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -374,22 +374,22 @@ namespace FrostFS.Object
public partial class GetRangeResponse : IResponse public partial class GetRangeResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -402,22 +402,22 @@ namespace FrostFS.Object
public partial class GetRangeHashRequest : IRequest public partial class GetRangeHashRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }
@ -430,22 +430,22 @@ namespace FrostFS.Object
public partial class GetRangeHashResponse : IResponse public partial class GetRangeHashResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }

View file

@ -4,22 +4,22 @@ namespace FrostFS.Session;
public partial class CreateResponse : IResponse public partial class CreateResponse : IResponse
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (ResponseMetaHeader)metaHeader; MetaHeader = (ResponseMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (ResponseVerificationHeader)verificationHeader; VerifyHeader = (ResponseVerificationHeader)verificationHeader;
} }
@ -32,22 +32,22 @@ public partial class CreateResponse : IResponse
public partial class CreateRequest : IRequest public partial class CreateRequest : IRequest
{ {
IMetaHeader IVerificableMessage.GetMetaHeader() IMetaHeader IVerifiableMessage.GetMetaHeader()
{ {
return MetaHeader; return MetaHeader;
} }
IVerificationHeader IVerificableMessage.GetVerificationHeader() IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{ {
return VerifyHeader; return VerifyHeader;
} }
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader) void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{ {
MetaHeader = (RequestMetaHeader)metaHeader; MetaHeader = (RequestMetaHeader)metaHeader;
} }
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader) void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{ {
VerifyHeader = (RequestVerificationHeader)verificationHeader; VerifyHeader = (RequestVerificationHeader)verificationHeader;
} }

View file

@ -0,0 +1,85 @@
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,
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,
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()
// {
// }
}

View file

@ -0,0 +1,208 @@
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
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 fsClient = Client.GetInstance(GetOptions(key, url));
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 fsClient = Client.GetInstance(GetOptions(this.key, this.url));
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 fsClient = Client.GetInstance(GetOptions(this.key, this.url));
fsClient.GrpcInvoked += (sender, info) =>
{
Assert.NotNull(info);
};
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 cid in fsClient.ListContainersAsync())
{
Assert.Fail("Containers exist");
}
}
[Fact]
public async void ClientCutScenarioTest()
{
var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
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<ClientSettings> 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<ModelsV2.Container> 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;
}
}
}
}

View file

@ -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<ContainerService.ContainerServiceClient> GetMock();
}

View file

@ -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<ContainerService.ContainerServiceClient> GetMock()
{
var mock = new Mock<ContainerService.ContainerServiceClient>();
var v = mock.Setup(x => x.DeleteAsync(
It.IsAny<DeleteRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()));
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<Object.DeleteResponse>(
Task.FromResult(deleteResponse),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => Console.WriteLine("disposed"));
});
return mock;
}
}
// objectServiceClientMock.Setup(x => x.Head(It.IsAny<Object.HeadRequest>(), It.IsAny<Metadata>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()))
// .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]) }
// }
// }
// };
// });

View file

@ -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<ContainerService.ContainerServiceClient> GetMock()
{
var mock = new Mock<ContainerService.ContainerServiceClient>();
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<GetResponse>(
Task.FromResult(getResponse),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
mock.Setup(x => x.GetAsync(
It.IsAny<GetRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.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<Metadata>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()))
// .Returns((Metadata m, DateTime dt, CancellationToken ct) =>
// {
// return new AsyncClientStreamingCall<FrostFS.Object.PutRequest, FrostFS.Object.PutResponse>(null, null, null, null, null, null);
// //IClientStreamWriter<TRequest> requestStream, Task<TResponse> responseAsync, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> 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<Object.HeadRequest>(), It.IsAny<Metadata>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()))
// .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<Object.DeleteRequest>(), It.IsAny<Metadata>(), It.IsAny<DateTime?>(), It.IsAny<CancellationToken>()))
// .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<byte>()))
// // ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())),
// }
// }
// };
// });

View file

@ -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<ContainerService.ContainerServiceClient> GetMock()
{
var mock = new Mock<ContainerService.ContainerServiceClient>();
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<PutResponse>(
Task.FromResult(response),
Task.FromResult(metadata),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => metadata,
() => { });
mock.Setup(x => x.PutAsync(
It.IsAny<PutRequest>(),
It.IsAny<Metadata>(),
It.IsAny<DateTime?>(),
It.IsAny<CancellationToken>()))
.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;
}
}

View file

@ -0,0 +1,14 @@
using Moq;
using FrostFS.Netmap;
namespace FrostFS.SDK.Tests;
public class NetmapMock(string key) : ServiceBase(key)
{
public Mock<NetmapService.NetmapServiceClient> GetMock()
{
var mock = new Mock<NetmapService.NetmapServiceClient>();
return mock;
}
}

View file

@ -0,0 +1,14 @@
using Moq;
using FrostFS.Object;
namespace FrostFS.SDK.Tests;
public class ObjectMock(string key) : ServiceBase(key)
{
public Mock<ObjectService.ObjectServiceClient> GetMock()
{
var mock = new Mock<ObjectService.ObjectServiceClient>();
return mock;
}
}

View file

@ -0,0 +1,14 @@
using Moq;
using FrostFS.Session;
namespace FrostFS.SDK.Tests;
public class SessionMock(string key) : ServiceBase(key)
{
public Mock<SessionService.SessionServiceClient> GetMock()
{
var mock = new Mock<SessionService.SessionServiceClient>();
return mock;
}
}