From 79764c9b6737f806b28671ab6eda3c4fe71d864a Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Mon, 10 Jun 2024 10:44:48 +0300
Subject: [PATCH 1/3] [#4] Infrastructure for Client Cut
Signed-off-by: Pavel Gross
---
FrostFS.SDK.sln | 7 +-
src/FrostFS.SDK.ClientV2/Client.cs | 10 +-
src/FrostFS.SDK.ClientV2/Extensions/Object.cs | 44 +++
.../FrostFS.SDK.ClientV2.csproj | 1 +
.../Interfaces/IFrostFSClient.cs | 13 +-
.../Mappers/GRPC/ContainerId.cs | 3 +-
.../Mappers/GRPC/Object.cs | 92 ++++-
.../Mappers/GRPC/ObjectId.cs | 5 +
src/FrostFS.SDK.ClientV2/RequestSigner.cs | 2 +-
.../Services/Container.cs | 8 +-
src/FrostFS.SDK.ClientV2/Services/Object.cs | 317 ++++++++++++------
src/FrostFS.SDK.Cryptography/Base58.cs | 2 -
src/FrostFS.SDK.Cryptography/Helper.cs | 1 -
src/FrostFS.SDK.ModelsV2/ContainerId.cs | 28 +-
src/FrostFS.SDK.ModelsV2/Object/Object.cs | 65 ++++
.../Object/ObjectAttribute.cs | 13 +
.../{Object.cs => Object/ObjectFilter.cs} | 39 ---
.../Object/ObjectHeader.cs | 37 ++
src/FrostFS.SDK.ModelsV2/Splitter.cs | 88 +++++
.../object/Extension.Message.cs | 56 ++++
.../session/Extension.XHeader.cs | 2 +-
21 files changed, 642 insertions(+), 191 deletions(-)
create mode 100644 src/FrostFS.SDK.ClientV2/Extensions/Object.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Object/Object.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
rename src/FrostFS.SDK.ModelsV2/{Object.cs => Object/ObjectFilter.cs} (56%)
create mode 100644 src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Splitter.cs
diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln
index d7d789e..67e5eb3 100644
--- a/FrostFS.SDK.sln
+++ b/FrostFS.SDK.sln
@@ -29,9 +29,8 @@ Global
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU
- {B738F3E1-654D-41A3-B068-58ED122BB688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B738F3E1-654D-41A3-B068-58ED122BB688}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B738F3E1-654D-41A3-B068-58ED122BB688}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B738F3E1-654D-41A3-B068-58ED122BB688}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 7cb47d6..7e9e29c 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -1,5 +1,6 @@
using System;
using System.Security.Cryptography;
+using System.Threading.Tasks;
using FrostFS.Container;
using FrostFS.Netmap;
using FrostFS.Object;
@@ -25,7 +26,12 @@ public partial class Client: IFrostFSClient
private ObjectService.ObjectServiceClient? _objectServiceClient;
private SessionService.SessionServiceClient? _sessionServiceClient;
- public Client(string key, string host)
+ public static IFrostFSClient GetInstance(string key, string host)
+ {
+ return new Client(key, host);
+ }
+
+ private Client(string key, string host)
{
// TODO: Развязать клиент и реализацию GRPC
_key = key.LoadWif();
@@ -37,7 +43,7 @@ public partial class Client: IFrostFSClient
InitSessionClient();
CheckFrostFsVersionSupport();
}
-
+
private async void CheckFrostFsVersionSupport()
{
var localNodeInfo = await GetLocalNodeInfoAsync();
diff --git a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs b/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
new file mode 100644
index 0000000..93d8d5f
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
@@ -0,0 +1,44 @@
+
+using System.Collections.Generic;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Extensions;
+
+public static class Extensions
+{
+ public static ModelsV2.Object SetPayloadLength(this ModelsV2.Object obj, ulong length)
+ {
+ obj.Header.PayloadLength = length;
+ return obj;
+ }
+
+ public static ModelsV2.Object AddAttribute(this ModelsV2.Object obj, string key, string value)
+ {
+ obj.AddAttribute(new ObjectAttribute(key, value));
+ return obj;
+ }
+
+ public static ModelsV2.Object AddAttribute(this ModelsV2.Object obj, ObjectAttribute attribute)
+ {
+ obj.Header.Attributes.Add(attribute);
+ return obj;
+ }
+
+ public static ModelsV2.Object AddAttributes(this ModelsV2.Object obj, IEnumerable attributes)
+ {
+ obj.Header.Attributes.AddRange(attributes);
+ return obj;
+ }
+
+ public static ModelsV2.Object SetSplit(this ModelsV2.Object obj, Split split)
+ {
+ obj.Header.Split = split;
+ return obj;
+ }
+
+ public static LinkObject AddChildren(this LinkObject linkObject, IEnumerable objectIds)
+ {
+ linkObject.Header.Split.Children.AddRange(objectIds);
+ return linkObject;
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
index 0208178..d446e0e 100644
--- a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
+++ b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
@@ -8,6 +8,7 @@
+
diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
index 8412c6c..5b82d1b 100644
--- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
+++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
@@ -9,13 +9,24 @@ namespace FrostFS.SDK.ClientV2.Interfaces;
public interface IFrostFSClient
{
Task GetContainerAsync(ContainerId containerId);
+
IAsyncEnumerable ListContainersAsync();
+
Task CreateContainerAsync(ModelsV2.Container container);
+
Task DeleteContainerAsync(ContainerId containerId);
+
Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId);
+
Task GetObjectAsync(ContainerId containerId, ObjectId objectId);
+
Task PutObjectAsync(ObjectHeader header, Stream payload);
+
Task PutObjectAsync(ObjectHeader header, byte[] payload);
+
+ Task PutSingleObjectAsync(ModelsV2.Object obj);
+
Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId);
- IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters);
+
+ IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters);
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs
index 1bfd614..920c49a 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs
@@ -1,4 +1,5 @@
using FrostFS.Refs;
+using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using Google.Protobuf;
@@ -10,7 +11,7 @@ public static class ContainerIdMapper
{
return new ContainerID
{
- Value = ByteString.CopyFrom(containerId.ToHash())
+ Value = ByteString.CopyFrom(Base58.Decode(containerId.Value))
};
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
index b6e68f6..ef21202 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
@@ -1,8 +1,9 @@
using System;
using System.Linq;
-
using FrostFS.Object;
+using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
+using Google.Protobuf;
using MatchType = FrostFS.Object.MatchType;
using ObjectType = FrostFS.Object.ObjectType;
@@ -29,11 +30,20 @@ public static class ObjectFilterMapper
{
public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
{
- var objMatchTypeName = Enum.GetName(typeof(MatchType), filter.MatchType)
- ?? throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.");
+ 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 = (MatchType)Enum.Parse(typeof(MatchType), objMatchTypeName),
+ MatchType = objMatchTypeName,
Key = filter.Key,
Value = filter.Value
};
@@ -44,13 +54,19 @@ public static class ObjectHeaderMapper
{
public static Header ToGrpcMessage(this ObjectHeader header)
{
- var objTypeName = Enum.GetName(typeof(ObjectType), header.ObjectType)
- ?? throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.");
- var head = new Header
+ var objTypeName = header.ObjectType switch
{
- Attributes = { },
+ ModelsV2.Enums.ObjectType.Regular => ObjectType.Regular,
+ ModelsV2.Enums.ObjectType.Lock => ObjectType.Lock,
+ ModelsV2.Enums.ObjectType.Tombstone => ObjectType.Tombstone,
+ _ => throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.")
+ };
+
+ var head = new Header
+ {
ContainerId = header.ContainerId.ToGrpcMessage(),
- ObjectType = (ObjectType)Enum.Parse(typeof(ObjectType), objTypeName)
+ ObjectType = objTypeName,
+ PayloadLength = header.PayloadLength
};
foreach (var attribute in header.Attributes)
@@ -58,20 +74,42 @@ public static class ObjectHeaderMapper
head.Attributes.Add(attribute.ToGrpcMessage());
}
+ var split = header.Split;
+ if (split != null)
+ {
+ head.Split = new Header.Types.Split
+ {
+ Parent = split.Parent?.ToGrpcMessage(),
+ ParentSignature = split.ParentSignature?.ToGrpcMessage(),
+ ParentHeader = split.ParentHeader?.ToGrpcMessage(),
+ Previous = split.Previous?.ToGrpcMessage(),
+ SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null
+ };
+
+ if (split.Children != null && split.Children.Any())
+ head.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
+ }
+
return head;
}
public static ObjectHeader ToModel(this Header header)
{
- var objTypeName = Enum.GetName(typeof(ModelsV2.Enums.ObjectType), header.ObjectType)
- ?? throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.");
+ var objTypeName = header.ObjectType switch
+ {
+ ObjectType.Regular => ModelsV2.Enums.ObjectType.Regular,
+ ObjectType.Lock => ModelsV2.Enums.ObjectType.Lock,
+ ObjectType.Tombstone => ModelsV2.Enums.ObjectType.Tombstone,
+ _ => throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.")
+ };
+
return new ObjectHeader(
- ContainerId.FromHash(header.ContainerId.Value.ToByteArray()),
- (ModelsV2.Enums.ObjectType)Enum.Parse(typeof(ModelsV2.Enums.ObjectType), objTypeName),
+ new ContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())),
+ objTypeName,
header.Attributes.Select(attribute => attribute.ToModel()).ToArray()
)
{
- Size = (long)header.PayloadLength,
+ PayloadLength = header.PayloadLength,
Version = header.Version.ToModel()
};
}
@@ -81,11 +119,33 @@ public static class ObjectMapper
{
public static ModelsV2.Object ToModel(this Object.Object obj)
{
- return new ModelsV2.Object
+ 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)
+ };
}
-}
\ No newline at end of file
+}
+
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs
index 4c28035..bfd568d 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs
@@ -13,4 +13,9 @@ public static class ObjectIdMapper
Value = ByteString.CopyFrom(objectId.ToHash())
};
}
+
+ public static ObjectId ToModel(this ObjectID objectId)
+ {
+ return ObjectId.FromHash(objectId.Value.ToByteArray());
+ }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/RequestSigner.cs
index 3747c92..23f8eb6 100644
--- a/src/FrostFS.SDK.ClientV2/RequestSigner.cs
+++ b/src/FrostFS.SDK.ClientV2/RequestSigner.cs
@@ -91,7 +91,7 @@ public static class RequestSigner
{
IRequest => new RequestVerificationHeader(),
IResponse => new ResponseVerificationHeader(),
- _ => throw new InvalidOperationException("Unsopported message type")
+ _ => throw new InvalidOperationException("Unsupported message type")
};
var verifyOrigin = message.GetVerificationHeader();
diff --git a/src/FrostFS.SDK.ClientV2/Services/Container.cs b/src/FrostFS.SDK.ClientV2/Services/Container.cs
index 886768c..7566492 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Container.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/Container.cs
@@ -1,5 +1,6 @@
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;
@@ -17,6 +18,7 @@ public partial class Client
ContainerId = cid.ToGrpcMessage()
},
};
+
request.AddMetaHeader();
request.Sign(_key);
var response = await _containerServiceClient.GetAsync(request);
@@ -33,13 +35,14 @@ public partial class Client
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 ContainerId.FromHash(cid.Value.ToByteArray());
+ yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray()));
}
}
@@ -48,6 +51,7 @@ public partial class Client
var cntnr = container.ToGrpcMessage();
cntnr.OwnerId = OwnerId.ToGrpcMessage();
cntnr.Version = Version.ToGrpcMessage();
+
var request = new PutRequest
{
Body = new PutRequest.Types.Body
@@ -60,7 +64,7 @@ public partial class Client
request.Sign(_key);
var response = await _containerServiceClient.PutAsync(request);
Verifier.CheckResponse(response);
- return ContainerId.FromHash(response.Body.ContainerId.Value.ToByteArray());
+ return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray()));
}
public async Task DeleteContainerAsync(ContainerId cid)
diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/Object.cs
index 180e351..bc98dc8 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/Object.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf;
@@ -9,9 +10,10 @@ using FrostFS.Object;
using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
-using FrostFS.SDK.ModelsV2;
using FrostFS.Session;
+using FrostFS.SDK.ModelsV2;
+
namespace FrostFS.SDK.ClientV2;
public partial class Client
@@ -20,7 +22,7 @@ public partial class Client
{
var request = new HeadRequest
{
- Body = new HeadRequest.Types.Body
+ Body = new HeadRequest.Types.Body
{
Address = new Address
{
@@ -29,12 +31,12 @@ public partial class Client
}
}
};
-
+
request.AddMetaHeader();
request.Sign(_key);
- var response = await _objectServiceClient.HeadAsync(request);
+ var response = await _objectServiceClient!.HeadAsync(request);
Verifier.CheckResponse(response);
-
+
return response.Body.Header.Header.ToModel();
}
@@ -45,7 +47,6 @@ public partial class Client
{
Body = new GetRequest.Types.Body
{
- Raw = false,
Address = new Address
{
ContainerId = cid.ToGrpcMessage(),
@@ -76,85 +77,8 @@ public partial class Client
public async Task PutObjectAsync(ObjectHeader header, byte[] payload)
{
- return await PutObject(header, new MemoryStream(payload));
- }
-
- public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid)
- {
- var request = new DeleteRequest
- {
- Body = new DeleteRequest.Types.Body
- {
- Address = new Address
- {
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
- }
- }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _objectServiceClient.DeleteAsync(request);
- Verifier.CheckResponse(response);
- }
-
- public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters)
- {
- var request = new SearchRequest
- {
- Body = new SearchRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage(),
- Filters = { },
- Version = 1
- }
- };
-
- foreach (var filter in filters)
- {
- request.Body.Filters.Add(filter.ToGrpcMessage());
- }
-
- request.AddMetaHeader();
- request.Sign(_key);
- var objectsIds = SearchObjects(request);
-
- await foreach (var oid in objectsIds)
- {
- yield return ObjectId.FromHash(oid.Value.ToByteArray());
- }
- }
-
- private async Task GetObject(GetRequest request)
- {
- using var stream = GetObjectInit(request);
- var obj = await stream.ReadHeader();
- var payload = new byte[obj.Header.PayloadLength];
- var offset = 0;
- var chunk = await stream.ReadChunk();
-
- while (chunk is not null)
- {
- chunk.CopyTo(payload, offset);
- 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)
- };
+ using var stream = new MemoryStream(payload);
+ return await PutObject(header, stream);
}
private async Task PutObject(ObjectHeader header, Stream payload)
@@ -163,12 +87,12 @@ public partial class Client
var hdr = header.ToGrpcMessage();
hdr.OwnerId = OwnerId.ToGrpcMessage();
hdr.Version = Version.ToGrpcMessage();
-
+
var oid = new ObjectID
{
Value = hdr.Sha256()
};
-
+
var request = new PutRequest
{
Body = new PutRequest.Types.Body
@@ -188,23 +112,27 @@ public partial class Client
ObjectSessionContext.Types.Verb.Put,
_key
);
-
+
request.Sign(_key);
using var stream = await PutObjectInit(request);
var buffer = new byte[Constants.ObjectChunkSize];
- var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
-
- while (bufferLength > 0)
+
+ while (true)
{
+ var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
+
+ if (bufferLength == 0)
+ break;
+
request.Body = new PutRequest.Types.Body
{
Chunk = ByteString.CopyFrom(buffer[..bufferLength]),
};
+
request.VerifyHeader = null;
request.Sign(_key);
await stream.Write(request);
- bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
}
var response = await stream.Close();
@@ -213,6 +141,192 @@ public partial class Client
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
}
+ public async Task PutSingleObjectAsync(ModelsV2.Object @object)
+ {
+ var sessionToken = await CreateSessionAsync(uint.MaxValue);
+
+ var obj = CreateObject(@object);
+
+ var request = new PutSingleRequest
+ {
+ Body = new () { Object = obj }
+ };
+
+ request.AddMetaHeader();
+ request.AddObjectSessionToken(
+ sessionToken,
+ obj.Header.ContainerId,
+ obj.ObjectId,
+ ObjectSessionContext.Types.Verb.Put,
+ _key
+ );
+
+ request.Sign(_key);
+
+ var response = await _objectServiceClient!.PutSingleAsync(request);
+ Verifier.CheckResponse(response);
+
+ return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
+ }
+
+ public Object.Object CreateObject(ModelsV2.Object @object)
+ {
+ var grpcHeader = @object.Header.ToGrpcMessage();
+
+ grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
+ grpcHeader.Version = Version.ToGrpcMessage();
+
+ if (@object.Payload != null)
+ {
+ grpcHeader.PayloadLength = (ulong)@object.Payload.Length;
+ grpcHeader.PayloadHash = Sha256Checksum(@object.Payload);
+ }
+
+ var split = @object.Header.Split;
+ if (split != null)
+ {
+ grpcHeader.Split = new Header.Types.Split
+ {
+ SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null
+ };
+
+ if (split.Children != null && split.Children.Any())
+ grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
+
+ if (split.ParentHeader is not null)
+ {
+ var grpcParentHeader = CreateHeader(split.ParentHeader, []);
+
+ grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() };
+ grpcHeader.Split.ParentHeader = grpcParentHeader;
+ grpcHeader.Split.ParentSignature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(_key.PublicKey()),
+ Sign = ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())),
+ };
+
+ split.Parent = grpcHeader.Split.Parent.ToModel();
+ }
+ }
+
+ var obj = new Object.Object
+ {
+ Header = grpcHeader,
+ ObjectId = new ObjectID { Value = grpcHeader.Sha256() },
+ Payload = ByteString.CopyFrom(@object.Payload)
+ };
+
+ obj.Signature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(_key.PublicKey()),
+ Sign = ByteString.CopyFrom(_key.SignData(obj.ObjectId.ToByteArray())),
+ };
+
+ return obj;
+ }
+
+ public Header CreateHeader(ObjectHeader header, byte[]? payload)
+ {
+ var grpcHeader = header.ToGrpcMessage();
+
+ grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
+ grpcHeader.Version = Version.ToGrpcMessage();
+
+ if (header.PayloadCheckSum != null)
+ {
+ grpcHeader.PayloadHash = new Checksum
+ {
+ Type = ChecksumType.Sha256,
+ Sum = ByteString.CopyFrom(header.PayloadCheckSum)
+ };
+ }
+ else
+ {
+ if (payload != null)
+ grpcHeader.PayloadHash = Sha256Checksum(payload);
+ }
+
+ return grpcHeader;
+ }
+
+ public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid)
+ {
+ var request = new DeleteRequest
+ {
+ Body = new DeleteRequest.Types.Body
+ {
+ Address = new Address
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ ObjectId = oid.ToGrpcMessage()
+ }
+ }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(_key);
+ var response = await _objectServiceClient!.DeleteAsync(request);
+ Verifier.CheckResponse(response);
+ }
+
+ public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters)
+ {
+ var request = new SearchRequest
+ {
+ Body = new SearchRequest.Types.Body
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ Filters = { },
+ Version = 1
+ }
+ };
+
+ foreach (var filter in filters)
+ {
+ request.Body.Filters.Add(filter.ToGrpcMessage());
+ }
+
+ request.AddMetaHeader();
+ request.Sign(_key);
+ var objectsIds = SearchObjects(request);
+
+ await foreach (var oid in objectsIds)
+ {
+ yield return ObjectId.FromHash(oid.Value.ToByteArray());
+ }
+ }
+
+ private async Task GetObject(GetRequest request)
+ {
+ using var stream = GetObjectInit(request);
+ var obj = await stream.ReadHeader();
+ var payload = new byte[obj.Header.PayloadLength];
+ var offset = 0;
+ var chunk = await stream.ReadChunk();
+
+ while (chunk is not null)
+ {
+ chunk.CopyTo(payload, offset);
+ 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 PutObjectInit(PutRequest initRequest)
{
if (initRequest is null)
@@ -220,9 +334,9 @@ public partial class Client
throw new ArgumentNullException(nameof(initRequest));
}
- var call = _objectServiceClient.Put();
+ var call = _objectServiceClient!.Put();
await call.RequestStream.WriteAsync(initRequest);
-
+
return new ObjectStreamer(call);
}
@@ -236,7 +350,7 @@ public partial class Client
{
yield return oid;
}
-
+
ids = await stream.Read();
}
}
@@ -248,6 +362,17 @@ public partial class Client
throw new ArgumentNullException(nameof(initRequest));
}
- return new SearchReader(_objectServiceClient.Search(initRequest));
+ return new SearchReader(_objectServiceClient!.Search(initRequest));
+ }
+
+ public Checksum Sha256Checksum(byte[] data)
+ {
+ return new Checksum
+ {
+ Type = ChecksumType.Sha256,
+ Sum = ByteString.CopyFrom(data.Sha256())
+ };
}
}
+
+
diff --git a/src/FrostFS.SDK.Cryptography/Base58.cs b/src/FrostFS.SDK.Cryptography/Base58.cs
index 36711c0..d9d91cf 100644
--- a/src/FrostFS.SDK.Cryptography/Base58.cs
+++ b/src/FrostFS.SDK.Cryptography/Base58.cs
@@ -3,8 +3,6 @@ using System.Linq;
using System.Numerics;
using System.Text;
-using Org.BouncyCastle.Security.Certificates;
-
namespace FrostFS.SDK.Cryptography;
public static class Base58
diff --git a/src/FrostFS.SDK.Cryptography/Helper.cs b/src/FrostFS.SDK.Cryptography/Helper.cs
index 93058d9..266a193 100644
--- a/src/FrostFS.SDK.Cryptography/Helper.cs
+++ b/src/FrostFS.SDK.Cryptography/Helper.cs
@@ -44,7 +44,6 @@ public static class Helper
{
return ByteString.CopyFrom(data.ToByteArray().Sha256());
}
-
public static ulong Murmur64(this byte[] value, uint seed)
{
diff --git a/src/FrostFS.SDK.ModelsV2/ContainerId.cs b/src/FrostFS.SDK.ModelsV2/ContainerId.cs
index 8bfbc54..349eb1c 100644
--- a/src/FrostFS.SDK.ModelsV2/ContainerId.cs
+++ b/src/FrostFS.SDK.ModelsV2/ContainerId.cs
@@ -1,30 +1,8 @@
-using System;
+namespace FrostFS.SDK.ModelsV2;
-using FrostFS.SDK.Cryptography;
-
-namespace FrostFS.SDK.ModelsV2;
-
-public class ContainerId
+public class ContainerId(string id)
{
- public string Value { get; set; }
-
- public ContainerId(string id)
- {
- Value = id;
- }
-
- public static ContainerId FromHash(byte[] hash)
- {
- if (hash.Length != Constants.Sha256HashLength)
- throw new FormatException("ContainerID must be a sha256 hash.");
-
- return new ContainerId(Base58.Encode(hash));
- }
-
- public byte[] ToHash()
- {
- return Base58.Decode(Value);
- }
+ public string Value { get; set; } = id;
public override string ToString()
{
diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
new file mode 100644
index 0000000..5810699
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
@@ -0,0 +1,65 @@
+using System.Security.Cryptography;
+using FrostFS.SDK.ModelsV2.Enums;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class Object
+{
+ public Object()
+ {
+ }
+
+ public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
+ {
+ Payload = payload;
+ Header = new ObjectHeader(containerId: container, type: objectType, attributes: []);
+ }
+
+ public ObjectHeader Header { get; set; }
+ public ObjectId ObjectId { get; set; }
+ public byte[] Payload { get; set; }
+ public Signature Signature { get; set; }
+
+ public void SetParent(LargeObject largeObject)
+ {
+ Header.Split!.ParentHeader = largeObject.Header;
+ }
+
+ public ObjectId? GetParentId()
+ {
+ return Header.Split?.Parent;
+ }
+}
+
+public class LargeObject(ContainerId container) : Object(container, [])
+{
+ private SHA256 payloadHash = SHA256.Create();
+
+ public void AppendBlock(byte[] bytes, int count)
+ {
+ Header.PayloadLength += (ulong)count;
+ this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0);
+ }
+
+ public void CalculateHash()
+ {
+ this.payloadHash.TransformFinalBlock([], 0, 0);
+ Header.PayloadCheckSum = this.payloadHash.Hash;
+ }
+
+ public ulong PayloadLength
+ {
+ get { return Header.PayloadLength; }
+ }
+}
+
+public class LinkObject : Object
+{
+ public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
+ {
+ Header.Split = new Split(splitId)
+ {
+ ParentHeader = largeObject.Header
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
new file mode 100644
index 0000000..b114f2b
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
@@ -0,0 +1,13 @@
+namespace FrostFS.SDK.ModelsV2;
+
+public class ObjectAttribute
+{
+ public string Key { get; set; }
+ public string Value { get; set; }
+
+ public ObjectAttribute(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ }
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
similarity index 56%
rename from src/FrostFS.SDK.ModelsV2/Object.cs
rename to src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
index 7db6289..5fdb54f 100644
--- a/src/FrostFS.SDK.ModelsV2/Object.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
@@ -2,18 +2,6 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2;
-public class ObjectAttribute
-{
- public string Key { get; set; }
- public string Value { get; set; }
-
- public ObjectAttribute(string key, string value)
- {
- Key = key;
- Value = value;
- }
-}
-
public class ObjectFilter
{
private const string HeaderPrefix = "$Object:";
@@ -48,30 +36,3 @@ public class ObjectFilter
return new ObjectFilter(matchType, HeaderPrefix + "version", version.ToString());
}
}
-
-public class ObjectHeader
-{
- public ObjectAttribute[] Attributes { get; set; }
- public ContainerId ContainerId { get; set; }
- public long Size { get; set; }
- public ObjectType ObjectType { get; set; }
- public Version Version { get; set; }
-
- public ObjectHeader(
- ContainerId containerId,
- ObjectType type = ObjectType.Regular,
- params ObjectAttribute[] attributes
- )
- {
- Attributes = attributes;
- ContainerId = containerId;
- ObjectType = type;
- }
-}
-
-public class Object
-{
- public ObjectHeader Header { get; set; }
- public ObjectId ObjectId { get; set; }
- public byte[] Payload { get; set; }
-}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
new file mode 100644
index 0000000..8f51160
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+using FrostFS.SDK.ModelsV2.Enums;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class ObjectHeader
+{
+ public OwnerId OwnerId { get; set; }
+
+ public List Attributes { get; set; }
+
+ public ContainerId ContainerId { get; set; }
+
+ public ulong PayloadLength { get; set; }
+
+ public byte[]? PayloadCheckSum { get; set; }
+
+ public ObjectType ObjectType { get; set; }
+
+ public Version Version { get; set; }
+
+ public Split? Split { get; set; }
+
+ public bool ClientCut { get; set; }
+
+ public ObjectHeader(
+ ContainerId containerId,
+ ObjectType type = ObjectType.Regular,
+ params ObjectAttribute[] attributes
+ )
+ {
+ Attributes = [.. attributes];
+ ContainerId = containerId;
+ ObjectType = type;
+ }
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Splitter.cs b/src/FrostFS.SDK.ModelsV2/Splitter.cs
new file mode 100644
index 0000000..f48f903
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Splitter.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class Split
+{
+ public Split() : this(new SplitId())
+ {
+ }
+
+ public Split(SplitId splitId)
+ {
+ SplitId = splitId;
+ }
+
+ public SplitId SplitId { get; private set; }
+
+ public ObjectId? Parent { get; set; }
+
+ public ObjectId? Previous { get; set; }
+
+ public Signature? ParentSignature { get; set; }
+
+ public ObjectHeader? ParentHeader { get; set; }
+
+ public List Children { get; } = [];
+}
+
+ public enum SignatureScheme {
+ EcdsaSha512,
+ EcdsaRfc6979Sha256,
+ EcdsaRfc6979Sha256WalletConnect
+ }
+
+public class Signature
+{
+ public byte[] Key { get; set; }
+ public byte[] Sign { get; set; }
+ public SignatureScheme Scheme { get; set; }
+}
+
+public class SplitId
+{
+ private Guid id;
+
+ public SplitId()
+ {
+ this.id = Guid.NewGuid();
+ }
+ public SplitId(Guid guid)
+ {
+ this.id = guid;
+ }
+
+ private SplitId(byte[] binary)
+ {
+ this.id = new Guid(binary);
+ }
+
+ private SplitId(string str)
+ {
+ this.id = new Guid(str);
+ }
+
+ public static SplitId CrateFromBinary(byte[] binaryData)
+ {
+ return new SplitId(binaryData);
+ }
+
+ public static SplitId CrateFromString(string stringData)
+ {
+ return new SplitId(stringData);
+ }
+
+ public override string ToString()
+ {
+ return this.id.ToString();
+ }
+
+ public byte[]? ToBinary()
+ {
+ if (this.id == Guid.Empty)
+ return null;
+
+ return this.id.ToByteArray();
+ }
+}
diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
index 243ab00..6334c71 100644
--- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
@@ -115,6 +115,62 @@ namespace FrostFS.Object
}
}
+ public partial class PutSingleRequest : IRequest
+ {
+ IMetaHeader IVerificableMessage.GetMetaHeader()
+ {
+ return MetaHeader;
+ }
+
+ IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ {
+ return VerifyHeader;
+ }
+
+ void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ {
+ MetaHeader = (RequestMetaHeader)metaHeader;
+ }
+
+ void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ {
+ VerifyHeader = (RequestVerificationHeader)verificationHeader;
+ }
+
+ public IMessage GetBody()
+ {
+ return Body;
+ }
+ }
+
+ public partial class PutSingleResponse : IResponse
+ {
+ IMetaHeader IVerificableMessage.GetMetaHeader()
+ {
+ return MetaHeader;
+ }
+
+ IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ {
+ return VerifyHeader;
+ }
+
+ void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ {
+ MetaHeader = (ResponseMetaHeader)metaHeader;
+ }
+
+ void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ {
+ VerifyHeader = (ResponseVerificationHeader)verificationHeader;
+ }
+
+ public IMessage GetBody()
+ {
+ return Body;
+ }
+ }
+
public partial class DeleteRequest : IRequest
{
IMetaHeader IVerificableMessage.GetMetaHeader()
diff --git a/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs
index f8f2695..c0c5b25 100644
--- a/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs
+++ b/src/FrostFS.SDK.ProtosV2/session/Extension.XHeader.cs
@@ -2,7 +2,7 @@
public partial class XHeader
{
- public const string ReservedXHeaderPrefix = "__NEOFS__";
+ public const string ReservedXHeaderPrefix = "__SYSTEM__";
public const string XHeaderNetmapEpoch = ReservedXHeaderPrefix + "NETMAP_EPOCH";
public const string XHeaderNetmapLookupDepth = ReservedXHeaderPrefix + "NETMAP_LOOKUP_DEPTH";
}
--
2.45.2
From edf10b970be7fc65257d987537386bc962ee397f Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Mon, 10 Jun 2024 11:11:09 +0300
Subject: [PATCH 2/3] [#4] sample for custom Client Cut
Signed-off-by: Pavel Gross
---
README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 75 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 832b42f..542a544 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
-var fsClient = new Client(, );
+var fsClient = Client.GetInstance(, );
// List containers
var containersIds = await fsClient.ListContainersAsync();
@@ -55,7 +55,7 @@ using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
-var fsClient = new Client(, );
+var fsClient = Client.GetInstance(, );
// Search regular objects
var objectsIds = await fsClient.SearchObjectAsync(
@@ -77,4 +77,77 @@ 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(, );
+
+ContainerId containerId = ;
+string fileName = ;
+
+await PutObjectClientCut(fsClient, containerId, fileName);
+
+static async Task PutObjectClientCut(IFrostFSClient fsClient, ContainerId containerId, string fileName)
+{
+ List sentObjectIds = [];
+ FrostFS.SDK.ModelsV2.Object? currentObject;
+
+ var partSize = 1024 * 1024;
+ var buffer = new byte[partSize];
+
+ var largeObject = new LargeObject(containerId);
+
+ var split = new Split();
+
+ 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, buffer)
+ .AddAttribute(fileNameAttribute)
+ .SetSplit(split);
+
+ if (largeObject.PayloadLength == fullLength)
+ break;
+
+ var objectId = await fsClient.PutSingleObjectAsync(currentObject);
+ sentObjectIds.Add(objectId);
+ }
+
+ if (sentObjectIds.Any())
+ {
+ largeObject.CalculateHash();
+
+ var linkObject = new LinkObject(containerId, split.SplitId, largeObject)
+ .AddChildren(sentObjectIds);
+
+ _ = await fsClient.PutSingleObjectAsync(linkObject);
+
+ currentObject.SetParent(largeObject);
+ _ = await fsClient.PutSingleObjectAsync(currentObject);
+
+ return currentObject.GetParentId();
+ }
+
+ return await fsClient.PutSingleObjectAsync(currentObject);
+}
+
```
\ No newline at end of file
--
2.45.2
From 85506a997eeb5ac278d29a42b0100680eb838197 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Sat, 22 Jun 2024 23:03:30 +0300
Subject: [PATCH 3/3] [#10] Netmap metrics and options
---
.gitignore | 1 +
FrostFS.SDK.sln | 11 +-
README.md | 147 ++----
src/FrostFS.SDK.ClientV2/Client.cs | 270 +++++++---
.../FrostFS.SDK.ClientV2.csproj | 7 +-
.../Interfaces/IFrostFSClient.cs | 31 +-
.../Mappers/{GRPC => }/Container.cs | 14 +-
.../Mappers/{GRPC => }/ContainerId.cs | 0
.../Mappers/GRPC/Netmap/NodeInfo.cs | 24 -
.../Mappers/{GRPC => }/MetaHeader.cs | 0
.../Mappers/Netmap/Netmap.cs | 15 +
.../Mappers/Netmap/NodeInfo.cs | 35 ++
.../{GRPC => }/Netmap/PlacementPolicy.cs | 0
.../Mappers/{GRPC => }/Netmap/Replica.cs | 0
.../Mappers/Object/Object.cs | 14 +
.../Mappers/Object/ObjectAttributeMapper.cs | 21 +
.../Mappers/Object/ObjectFilterMapper.cs | 30 ++
.../ObjectHeaderMapper.cs} | 76 ---
.../Mappers/{GRPC => Object}/ObjectId.cs | 0
.../Mappers/{GRPC => }/OwnerId.cs | 0
.../Mappers/Session/Session.cs | 25 +
.../Mappers/SignatureMapper.cs | 26 +
.../Mappers/{GRPC => }/Status.cs | 0
.../Mappers/{GRPC => }/Version.cs | 0
.../Services/Container.cs | 85 ----
.../Services/ContainerServiceProvider.cs | 117 +++++
src/FrostFS.SDK.ClientV2/Services/Netmap.cs | 38 --
.../Services/NetmapServiceProvider.cs | 134 +++++
.../Services/ObjectReader.cs | 10 +-
.../{Object.cs => ObjectServiceProvider.cs} | 477 ++++++++++--------
.../Services/SearchReader.cs | 10 +-
.../{Session.cs => SessionServiceProvider.cs} | 28 +-
.../Tools/ClientEnvironment.cs | 116 +++++
.../Tools/ContextAccessor.cs | 8 +
.../Tools/NetworkSettings.cs | 21 +
.../{Extensions => Tools}/Object.cs | 14 +-
src/FrostFS.SDK.ClientV2/{ => Tools}/Range.cs | 32 +-
.../{ => Tools}/RequestConstructor.cs | 11 +-
.../{ => Tools}/RequestSigner.cs | 9 +-
.../{ => Tools}/Verifier.cs | 16 +-
src/FrostFS.SDK.Cryptography/AssemblyInfo.cs | 2 +-
src/FrostFS.SDK.Cryptography/Base58.cs | 2 +
.../{Helper.cs => Extentions.cs} | 2 +-
.../FrostFS.SDK.Cryptography.csproj | 4 +
src/FrostFS.SDK.Cryptography/Key.cs | 6 +-
src/FrostFS.SDK.Cryptography/Range.cs | 32 +-
src/FrostFS.SDK.ModelsV2/ClientSettings.cs | 28 +
src/FrostFS.SDK.ModelsV2/Container.cs | 17 +-
src/FrostFS.SDK.ModelsV2/Context.cs | 13 +
src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs | 5 +-
.../FrostFS.SDK.ModelsV2.csproj | 4 +
src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs | 8 +
src/FrostFS.SDK.ModelsV2/MetaHeader.cs | 15 +-
src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs | 10 +
src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs | 23 +-
.../Netmap/PlacementPolicy.cs | 27 +-
src/FrostFS.SDK.ModelsV2/Object/Object.cs | 36 +-
.../Object/ObjectHeader.cs | 2 -
.../{ => Object}/ObjectId.cs | 0
src/FrostFS.SDK.ModelsV2/OwnerId.cs | 9 +-
.../PutObjectParameters.cs | 12 +
src/FrostFS.SDK.ModelsV2/SessionToken.cs | 8 +
src/FrostFS.SDK.ModelsV2/Signature.cs | 8 +
src/FrostFS.SDK.ModelsV2/SignatureScheme.cs | 7 +
src/FrostFS.SDK.ModelsV2/SplitId.cs | 50 ++
src/FrostFS.SDK.ModelsV2/Splitter.cs | 72 +--
src/FrostFS.SDK.ModelsV2/Status.cs | 17 +-
.../FrostFS.SDK.ProtosV2.csproj | 4 +
.../Interfaces/IRequest.cs | 2 +-
.../Interfaces/IResponse.cs | 2 +-
.../Interfaces/IVerifiableMessage.cs | 2 +-
.../container/Extension.Message.cs | 112 ++--
.../netmap/Extension.Message.cs | 89 +++-
.../object/Extension.Message.cs | 128 ++---
.../session/Extension.Message.cs | 16 +-
src/FrostFS.SDK.Tests/ClientTest.cs | 85 ++++
src/FrostFS.SDK.Tests/ClientTestLive.cs | 208 ++++++++
.../ContainerServiceBase.cs | 28 +
.../DeleteContainerMock.cs | 69 +++
.../ContainerServiceMocks/GetContainerMock.cs | 163 ++++++
.../ContainerServiceMocks/PutContainerMock.cs | 88 ++++
src/FrostFS.SDK.Tests/NetmapMock.cs | 14 +
src/FrostFS.SDK.Tests/ObjectMock.cs | 14 +
src/FrostFS.SDK.Tests/SessionMock.cs | 14 +
84 files changed, 2314 insertions(+), 1016 deletions(-)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/Container.cs (75%)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/ContainerId.cs (100%)
delete mode 100644 src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/MetaHeader.cs (100%)
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/Netmap/PlacementPolicy.cs (100%)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/Netmap/Replica.cs (100%)
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC/Object.cs => Object/ObjectHeaderMapper.cs} (55%)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => Object}/ObjectId.cs (100%)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/OwnerId.cs (100%)
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/Status.cs (100%)
rename src/FrostFS.SDK.ClientV2/Mappers/{GRPC => }/Version.cs (100%)
delete mode 100644 src/FrostFS.SDK.ClientV2/Services/Container.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
delete mode 100644 src/FrostFS.SDK.ClientV2/Services/Netmap.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
rename src/FrostFS.SDK.ClientV2/Services/{Object.cs => ObjectServiceProvider.cs} (56%)
rename src/FrostFS.SDK.ClientV2/Services/{Session.cs => SessionServiceProvider.cs} (50%)
create mode 100644 src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs
rename src/FrostFS.SDK.ClientV2/{Extensions => Tools}/Object.cs (76%)
rename src/FrostFS.SDK.ClientV2/{ => Tools}/Range.cs (91%)
rename src/FrostFS.SDK.ClientV2/{ => Tools}/RequestConstructor.cs (84%)
rename src/FrostFS.SDK.ClientV2/{ => Tools}/RequestSigner.cs (95%)
rename src/FrostFS.SDK.ClientV2/{ => Tools}/Verifier.cs (86%)
rename src/FrostFS.SDK.Cryptography/{Helper.cs => Extentions.cs} (97%)
create mode 100644 src/FrostFS.SDK.ModelsV2/ClientSettings.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Context.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs
rename src/FrostFS.SDK.ModelsV2/{ => Object}/ObjectId.cs (100%)
create mode 100644 src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/SessionToken.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/Signature.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/SignatureScheme.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/SplitId.cs
create mode 100644 src/FrostFS.SDK.Tests/ClientTest.cs
create mode 100644 src/FrostFS.SDK.Tests/ClientTestLive.cs
create mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs
create mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs
create mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs
create mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs
create mode 100644 src/FrostFS.SDK.Tests/NetmapMock.cs
create mode 100644 src/FrostFS.SDK.Tests/ObjectMock.cs
create mode 100644 src/FrostFS.SDK.Tests/SessionMock.cs
diff --git a/.gitignore b/.gitignore
index cf6e05a..ae0b81f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ vendor/
# IDE
.idea
.vscode
+.vs
# coverage
coverage.txt
diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln
index fcfe732..c192375 100644
--- a/FrostFS.SDK.sln
+++ b/FrostFS.SDK.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
#
VisualStudioVersion = 17.5.002.0
-MinimumVisualStudioVersion =
+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}"
@@ -10,6 +10,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ModelsV2", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -32,8 +34,9 @@ Global
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 89ab89a..43f4040 100644
--- a/README.md
+++ b/README.md
@@ -25,133 +25,50 @@ using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
+using Microsoft.Extensions.Options;
-var fsClient = Client.GetInstance(, );
-// List containers
-var containersIds = await fsClient.ListContainersAsync();
+var options = Options.Create(new ClientSettings
+ {
+ Key = ,
+ 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 containerId = await fsClient.CreateContainerAsync(
- new Container(
- BasicAcl.PublicRW,
- placementPolicy
- )
-);
-// Get container
-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(, );
-
-// Search regular objects
-var objectsIds = await fsClient.SearchObjectAsync(
- cId,
- ObjectFilter.RootFilter()
-);
+var containerId = await fsClient.CreateContainerAsync(new Container(BasicAcl.PublicRW, placementPolicy));
// Put object
-var f = File.OpenRead("cat.jpg");
-var cat = new ObjectHeader(
- containerId: cId,
+var fileStream = File.OpenRead("cat.jpg");
+var header = new ObjectHeader(
+ containerId: containerId,
type: ObjectType.Regular,
new ObjectAttribute("Filename", "cat.jpg")
);
-var oId = await fsClient.PutObjectAsync(cat, f);
-// Get object header
-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(, );
-
-ContainerId containerId = ;
-string fileName = ;
-
-await PutObjectClientCut(fsClient, containerId, fileName);
-
-static async Task PutObjectClientCut(IFrostFSClient fsClient, ContainerId containerId, string fileName)
+var param = new PutObjectParameters
{
- List sentObjectIds = [];
- FrostFS.SDK.ModelsV2.Object? currentObject;
+ Header = header,
+ Payload = fileStream
+};
- var partSize = 1024 * 1024;
- var buffer = new byte[partSize];
+var oId = await fsClient.PutObjectAsync(param);
- var largeObject = new LargeObject(containerId);
-
- var split = new Split();
-
- var fileInfo = new FileInfo(fileName);
- var fullLength = (ulong)fileInfo.Length;
- var fileNameAttribute = new ObjectAttribute("fileName", fileInfo.Name);
+// Search regular objects
+await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [ObjectFilter.RootFilter()]))
+{
+ // Get object header
+ var info = await fsClient.GetObjectHeadAsync(containerId, objId);
- 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);
-}
-
-```
\ No newline at end of file
+ // Get object
+ var obj = await fsClient.GetObjectAsync(containerId, oId);
+}
+```
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 96fb227..093951f 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Security.Cryptography;
-using System.Text;
+using System.Data.Common;
using System.Threading.Tasks;
using FrostFS.Container;
using FrostFS.Netmap;
@@ -9,85 +8,222 @@ using FrostFS.Object;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.Session;
using Grpc.Core;
using Grpc.Net.Client;
-using static FrostFS.Netmap.NetworkConfig.Types;
+using Microsoft.Extensions.Options;
using Version = FrostFS.SDK.ModelsV2.Version;
namespace FrostFS.SDK.ClientV2;
-public partial class Client: IFrostFSClient
+public class Client : IFrostFSClient, IDisposable
{
- private GrpcChannel? _channel;
- private readonly ECDsa _key;
- public readonly OwnerId OwnerId;
- public readonly Version Version = new(2, 13);
+ private bool isDisposed;
- private readonly Dictionary NetworkSettings = [];
+ internal ClientEnvironment ClientCtx { get; set; }
- private ContainerService.ContainerServiceClient? _containerServiceClient;
- private NetmapService.NetmapServiceClient? _netmapServiceClient;
- private ObjectService.ObjectServiceClient? _objectServiceClient;
- private SessionService.SessionServiceClient? _sessionServiceClient;
-
- public static IFrostFSClient GetInstance(string key, string host)
+ public static IFrostFSClient GetInstance(IOptions options)
{
- return new Client(key, host);
+ return new Client(options);
}
- private Client(string key, string host)
+ ///
+ /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
+ ///
+ /// User's key
+ /// ContainerService.ContainerServiceClient implementation
+ /// Netmap.NetmapService.NetmapServiceClient implementation
+ /// Session.SessionService.SessionServiceClient implementation
+ /// Object.ObjectService.ObjectServiceClient implementation
+ ///
+ public static IFrostFSClient GetTestInstance(
+ IOptions settings,
+ NetmapService.NetmapServiceClient netmapService,
+ SessionService.SessionServiceClient sessionService,
+ ContainerService.ContainerServiceClient containerService,
+ ObjectService.ObjectServiceClient objectService)
{
- // TODO: Развязать клиент и реализацию GRPC
- _key = key.LoadWif();
- OwnerId = OwnerId.FromKey(_key);
- InitGrpcChannel(host);
- InitContainerClient();
- InitNetmapClient();
- InitObjectClient();
- InitSessionClient();
+ return new Client(settings, containerService, netmapService, sessionService, objectService);
+ }
+
+ private Client(
+ IOptions settings,
+ ContainerService.ContainerServiceClient containerService,
+ NetmapService.NetmapServiceClient netmapService,
+ SessionService.SessionServiceClient sessionService,
+ ObjectService.ObjectServiceClient objectService)
+ {
+ var ecdsaKey = settings.Value.Key.LoadWif();
+ OwnerId.FromKey(ecdsaKey);
+
+ ClientCtx = new ClientEnvironment(
+ key: ecdsaKey,
+ owner: OwnerId.FromKey(ecdsaKey),
+ channel: InitGrpcChannel(settings.Value.Host),
+ version: new Version(2, 13));
+
+ ClientCtx.ContainerService = new ContainerServiceProvider(containerService, ClientCtx);
+ ClientCtx.NetmapService = new NetmapServiceProvider(netmapService, ClientCtx);
+ ClientCtx.SessionService = new SessionServiceProvider(sessionService, ClientCtx);
+ ClientCtx.ObjectService = new ObjectServiceProvider(objectService, ClientCtx);
+ }
+
+ private Client(IOptions options)
+ {
+ 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();
-
- InitNetworkInfoAsync();
}
- private async void InitNetworkInfoAsync()
+ event EventHandler IFrostFSClient.GrpcInvoked
{
- var info = await GetNetworkInfoAsync();
-
- foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
+ add
{
- SetNetworksParam(param);
+ ClientCtx.GrpcInvokedEvent += value;
+ }
+ remove
+ {
+ ClientCtx.GrpcInvokedEvent -= value;
}
}
- private void SetNetworksParam(Parameter param)
- {
- var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
-
- var encodedValue = param.Value.ToByteArray();
-
- ulong val = 0;
- for (var i = encodedValue.Length - 1; i >= 0; i--)
- {
- val = (val << 8) + encodedValue[i];
- }
-
- NetworkSettings.Add(key, val);
- }
-
- private async void CheckFrostFsVersionSupport()
+ public void Dispose()
{
- var localNodeInfo = await GetLocalNodeInfoAsync();
- if (!localNodeInfo.Version.IsSupported(Version))
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing && !isDisposed)
+ {
+ ClientCtx.Dispose();
+ isDisposed = true;
+ }
+ }
+
+ public Task GetContainerAsync(ContainerId containerId, Context? ctx = null)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!);
+ }
+
+ public IAsyncEnumerable ListContainersAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.ListContainersAsync(ctx!);
+ }
+
+ public Task CreateContainerAsync(ModelsV2.Container container, Context? ctx = null)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.CreateContainerAsync(container, ctx!);
+ }
+
+ public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.DeleteContainerAsync(containerId, ctx!);
+ }
+
+ public Task GetNetmapSnapshotAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!);
+ }
+
+ public Task GetNodeInfoAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
+ }
+
+ public Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!);
+ }
+
+ public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!);
+ }
+
+ public Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!);
+ }
+
+ public Task PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.PutSingleObjectAsync(obj, ctx!);
+ }
+
+ public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.DeleteObjectAsync(containerId, objectId, ctx!);
+ }
+
+ public IAsyncEnumerable SearchObjectsAsync(
+ ContainerId containerId,
+ IEnumerable filters,
+ Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.SearchObjectsAsync(containerId, filters, ctx!);
+ }
+
+ public ObjectId CalculateObjectId(ObjectHeader header)
+ {
+ return ClientCtx.ObjectService!.CalculateObjectId(header);
+ }
+
+ private async void CheckFrostFsVersionSupport(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ var localNodeInfo = await ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
+
+ if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
{
var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
Console.WriteLine(msg);
throw new ApplicationException(msg);
}
}
-
- private void InitGrpcChannel(string host)
+
+ private void ValidateEnvironment(ref Context? ctx)
+ {
+ if (isDisposed)
+ throw new Exception("Client is disposed.");
+
+ if (ClientCtx == null || !ClientCtx.Initialized)
+ throw new Exception("Client is not initialized.");
+
+ ctx ??= new Context();
+ }
+
+ private static GrpcChannel InitGrpcChannel(string host)
{
Uri uri;
try
@@ -116,30 +252,10 @@ public partial class Client: IFrostFSClient
throw new ArgumentException(msg);
}
- _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
- {
- Credentials = grpcCredentials,
- HttpHandler = new System.Net.Http.HttpClientHandler()
- });
- }
-
- private void InitContainerClient()
- {
- _containerServiceClient = new ContainerService.ContainerServiceClient(_channel);
- }
-
- private void InitNetmapClient()
- {
- _netmapServiceClient = new NetmapService.NetmapServiceClient(_channel);
- }
-
- private void InitObjectClient()
- {
- _objectServiceClient = new ObjectService.ObjectServiceClient(_channel);
- }
-
- private void InitSessionClient()
- {
- _sessionServiceClient = new SessionService.SessionServiceClient(_channel);
+ return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
+ {
+ Credentials = grpcCredentials,
+ HttpHandler = new System.Net.Http.HttpClientHandler()
+ });
}
}
diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
index d446e0e..818bce2 100644
--- a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
+++ b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
@@ -6,9 +6,14 @@
enable
+
+ true
+
+
-
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
index 9ae6833..f74a084 100644
--- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
+++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
@@ -1,34 +1,39 @@
+using System;
using System.Collections.Generic;
-using System.IO;
-using System.Threading;
using System.Threading.Tasks;
using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Netmap;
namespace FrostFS.SDK.ClientV2.Interfaces;
public interface IFrostFSClient
{
- Task GetContainerAsync(ContainerId containerId);
+ Task GetContainerAsync(ContainerId containerId, Context? context = default);
- IAsyncEnumerable ListContainersAsync();
+ IAsyncEnumerable ListContainersAsync(Context? context = default);
- Task CreateContainerAsync(ModelsV2.Container container);
+ Task CreateContainerAsync(ModelsV2.Container container, Context? context = default);
- Task DeleteContainerAsync(ContainerId containerId);
+ Task DeleteContainerAsync(ContainerId containerId, Context? context = default);
- Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId);
+ Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
+ Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
- Task GetObjectAsync(ContainerId containerId, ObjectId objectId);
+ Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default);
+
+ Task PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default);
- Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default);
+ Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
- Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default);
+ IAsyncEnumerable SearchObjectsAsync(ContainerId cid, IEnumerable filters, Context? context = default);
- Task PutSingleObjectAsync(ModelsV2.Object obj, CancellationToken cancellationToken = default);
+ Task GetNetmapSnapshotAsync(Context? context = default);
- Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId);
+ Task GetNodeInfoAsync(Context? context = default);
- IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters);
+ ObjectId CalculateObjectId(ObjectHeader header);
+
+ event EventHandler GrpcInvoked;
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs
similarity index 75%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Container.cs
index 93eac3f..3d1207f 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs
@@ -22,19 +22,15 @@ public static class ContainerMapper
public static ModelsV2.Container ToModel(this Container.Container container)
{
- var basicAclName = Enum.GetName(typeof(BasicAcl), container.BasicAcl);
- if (basicAclName is null)
- {
+ if (!Enum.IsDefined(typeof(BasicAcl),(int)container.BasicAcl))
throw new ArgumentException($"Unknown BasicACL rule. Value: '{container.BasicAcl}'.");
- }
+
+ BasicAcl acl = (BasicAcl)container.BasicAcl;
- return new ModelsV2.Container(
- (BasicAcl)Enum.Parse(typeof(BasicAcl), basicAclName),
- container.PlacementPolicy.ToModel()
- )
+ return new ModelsV2.Container(acl, container.PlacementPolicy.ToModel())
{
Nonce = container.Nonce.ToUuid(),
Version = container.Version.ToModel()
};
}
-}
\ No newline at end of file
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/ContainerId.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/ContainerId.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs
deleted file mode 100644
index f8f2d56..0000000
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/NodeInfo.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-using FrostFS.Netmap;
-using FrostFS.SDK.ModelsV2.Enums;
-using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo;
-
-namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
-
-public static class NodeInfoMapper
-{
- public static NodeInfo ToModel(this LocalNodeInfoResponse.Types.Body nodeInfo)
- {
- var nodeStateName = Enum.GetName(typeof(NodeState), nodeInfo.NodeInfo.State);
- if (nodeStateName is null)
- {
- throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.NodeInfo.State}'.");
- }
- return new NodeInfo
- {
- State = (NodeState)Enum.Parse(typeof(NodeState), nodeStateName),
- Version = nodeInfo.Version.ToModel()
- };
- }
-}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs b/src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/MetaHeader.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/MetaHeader.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs
new file mode 100644
index 0000000..359041c
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Netmap.cs
@@ -0,0 +1,15 @@
+using System.Linq;
+using FrostFS.Netmap;
+using FrostFS.SDK.ModelsV2.Netmap;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+
+public static class NetmapMapper
+{
+ public static NetmapSnapshot ToModel(this NetmapSnapshotResponse netmap)
+ {
+ return new NetmapSnapshot(
+ netmap.Body.Netmap.Epoch,
+ netmap.Body.Netmap.Nodes.Select(n => n.ToModel(netmap.MetaHeader.Version)).ToArray());
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs
new file mode 100644
index 0000000..da87dfd
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/NodeInfo.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Linq;
+using FrostFS.Netmap;
+using FrostFS.SDK.ModelsV2.Enums;
+using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+
+public static class NodeInfoMapper
+{
+ public static NodeInfo ToModel(this LocalNodeInfoResponse.Types.Body node)
+ {
+ return node.NodeInfo.ToModel(node.Version);
+ }
+
+ public static NodeInfo ToModel(this FrostFS.Netmap.NodeInfo nodeInfo, Refs.Version version)
+ {
+ NodeState state = nodeInfo.State switch
+ {
+ FrostFS.Netmap.NodeInfo.Types.State.Unspecified => NodeState.Unspecified,
+ FrostFS.Netmap.NodeInfo.Types.State.Online => NodeState.Online,
+ FrostFS.Netmap.NodeInfo.Types.State.Offline => NodeState.Offline,
+ FrostFS.Netmap.NodeInfo.Types.State.Maintenance => NodeState.Maintenance,
+ _ => throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.State}'.")
+ };
+
+ return new NodeInfo(
+ version: version.ToModel(),
+ state: state,
+ addresses: [.. nodeInfo.Addresses],
+ attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
+ publicKey: nodeInfo.PublicKey.ToByteArray()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/PlacementPolicy.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Netmap/PlacementPolicy.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs b/src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Netmap/Replica.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Netmap/Replica.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
new file mode 100644
index 0000000..be61b13
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
@@ -0,0 +1,14 @@
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class ObjectMapper
+{
+ public static ModelsV2.Object ToModel(this Object.Object obj)
+ {
+ return new ModelsV2.Object(
+ ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
+ obj.Header.ToModel(),
+ obj.Payload.ToByteArray());
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs
new file mode 100644
index 0000000..75c4bb8
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectAttributeMapper.cs
@@ -0,0 +1,21 @@
+using FrostFS.Object;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class ObjectAttributeMapper
+{
+ public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute)
+ {
+ return new Header.Types.Attribute
+ {
+ Key = attribute.Key,
+ Value = attribute.Value
+ };
+ }
+
+ public static ObjectAttribute ToModel(this Header.Types.Attribute attribute)
+ {
+ return new ObjectAttribute(attribute.Key, attribute.Value);
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
new file mode 100644
index 0000000..ee819c6
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
@@ -0,0 +1,30 @@
+using System;
+using FrostFS.Object;
+using FrostFS.SDK.ModelsV2;
+using MatchType = FrostFS.Object.MatchType;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class ObjectFilterMapper
+{
+ public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
+ {
+ var objMatchTypeName = filter.MatchType switch
+ {
+ ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified,
+ ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual,
+ ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual,
+ ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent,
+ ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix,
+
+ _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.")
+ };
+
+ return new SearchRequest.Types.Body.Types.Filter
+ {
+ MatchType = objMatchTypeName,
+ Key = filter.Key,
+ Value = filter.Value
+ };
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
similarity index 55%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
index 15a7b1c..7e82dd3 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
@@ -4,52 +4,10 @@ using FrostFS.Object;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using Google.Protobuf;
-using MatchType = FrostFS.Object.MatchType;
using ObjectType = FrostFS.Object.ObjectType;
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
-public static class ObjectAttributeMapper
-{
- public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute)
- {
- return new Header.Types.Attribute
- {
- Key = attribute.Key,
- Value = attribute.Value
- };
- }
-
- public static ObjectAttribute ToModel(this Header.Types.Attribute attribute)
- {
- return new ObjectAttribute(attribute.Key, attribute.Value);
- }
-}
-
-public static class ObjectFilterMapper
-{
- public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
- {
- var objMatchTypeName = filter.MatchType switch
- {
- ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified,
- ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual,
- ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual,
- ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent,
- ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix,
-
- _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.")
- };
-
- return new SearchRequest.Types.Body.Types.Filter
- {
- MatchType = objMatchTypeName,
- Key = filter.Key,
- Value = filter.Value
- };
- }
-}
-
public static class ObjectHeaderMapper
{
public static Header ToGrpcMessage(this ObjectHeader header)
@@ -130,37 +88,3 @@ public static class ObjectHeaderMapper
return model;
}
}
-
-public static class ObjectMapper
-{
- public static ModelsV2.Object ToModel(this Object.Object obj)
- {
- return new ModelsV2.Object()
- {
- Header = obj.Header.ToModel(),
- ObjectId = ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
- Payload = obj.Payload.ToByteArray()
- };
- }
-}
-
-public static class SignatureMapper
-{
- public static Refs.Signature ToGrpcMessage(this Signature signature)
- {
- var scheme = signature.Scheme switch
- {
- SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256,
- SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect,
- SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512,
- _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", paramName: nameof(signature.Scheme))
- };
-
- return new Refs.Signature
- {
- Key = ByteString.CopyFrom(signature.Key),
- Scheme = scheme,
- Sign = ByteString.CopyFrom(signature.Sign)
- };
- }
-}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
new file mode 100644
index 0000000..402f7e1
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
@@ -0,0 +1,25 @@
+
+using System;
+using Google.Protobuf;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class SessionMapper
+{
+ internal static string SerializeSessionToken(this Session.SessionToken token)
+ {
+ byte[] bytes = new byte[token.CalculateSize()];
+ CodedOutputStream stream = new(bytes);
+ token.WriteTo(stream);
+
+ return Convert.ToBase64String(bytes);
+ }
+
+ internal static Session.SessionToken DeserializeSessionToken(this byte[] bytes)
+ {
+ Session.SessionToken token = new();
+ token.MergeFrom(bytes);
+
+ return token;
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs
new file mode 100644
index 0000000..2da358a
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/SignatureMapper.cs
@@ -0,0 +1,26 @@
+using System;
+using FrostFS.SDK.ModelsV2;
+using Google.Protobuf;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class SignatureMapper
+{
+ public static Refs.Signature ToGrpcMessage(this Signature signature)
+ {
+ var scheme = signature.Scheme switch
+ {
+ SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256,
+ SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect,
+ SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512,
+ _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", paramName: nameof(signature.Scheme))
+ };
+
+ return new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(signature.Key),
+ Scheme = scheme,
+ Sign = ByteString.CopyFrom(signature.Sign)
+ };
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs b/src/FrostFS.SDK.ClientV2/Mappers/Status.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Status.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Status.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs b/src/FrostFS.SDK.ClientV2/Mappers/Version.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Version.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Version.cs
diff --git a/src/FrostFS.SDK.ClientV2/Services/Container.cs b/src/FrostFS.SDK.ClientV2/Services/Container.cs
deleted file mode 100644
index 7566492..0000000
--- a/src/FrostFS.SDK.ClientV2/Services/Container.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using FrostFS.Container;
-using FrostFS.SDK.ClientV2.Mappers.GRPC;
-using FrostFS.SDK.Cryptography;
-using FrostFS.SDK.ModelsV2;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace FrostFS.SDK.ClientV2;
-
-public partial class Client
-{
- public async Task GetContainerAsync(ContainerId cid)
- {
- var request = new GetRequest
- {
- Body = new GetRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage()
- },
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _containerServiceClient.GetAsync(request);
- Verifier.CheckResponse(response);
- return response.Body.Container.ToModel();
- }
-
- public async IAsyncEnumerable ListContainersAsync()
- {
- var request = new ListRequest
- {
- Body = new ListRequest.Types.Body
- {
- OwnerId = OwnerId.ToGrpcMessage()
- }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _containerServiceClient.ListAsync(request);
- Verifier.CheckResponse(response);
- foreach (var cid in response.Body.ContainerIds)
- {
- yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray()));
- }
- }
-
- public async Task CreateContainerAsync(ModelsV2.Container container)
- {
- var cntnr = container.ToGrpcMessage();
- cntnr.OwnerId = OwnerId.ToGrpcMessage();
- cntnr.Version = Version.ToGrpcMessage();
-
- var request = new PutRequest
- {
- Body = new PutRequest.Types.Body
- {
- Container = cntnr,
- Signature = _key.SignRFC6979(cntnr),
- }
- };
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _containerServiceClient.PutAsync(request);
- Verifier.CheckResponse(response);
- return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray()));
- }
-
- public async Task DeleteContainerAsync(ContainerId cid)
- {
- var request = new DeleteRequest
- {
- Body = new DeleteRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage(),
- Signature = _key.SignRFC6979(cid.ToGrpcMessage().Value)
- }
- };
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _containerServiceClient.DeleteAsync(request);
- Verifier.CheckResponse(response);
- }
-}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
new file mode 100644
index 0000000..61f60c1
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
@@ -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 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 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 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);
+ }
+}
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs b/src/FrostFS.SDK.ClientV2/Services/Netmap.cs
deleted file mode 100644
index ce439cc..0000000
--- a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.Threading.Tasks;
-
-using FrostFS.Netmap;
-using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
-
-using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo;
-
-namespace FrostFS.SDK.ClientV2;
-
-public partial class Client
-{
- public async Task GetLocalNodeInfoAsync()
- {
- var request = new LocalNodeInfoRequest
- {
- Body = new LocalNodeInfoRequest.Types.Body { }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _netmapServiceClient.LocalNodeInfoAsync(request);
-
- return response.Body.ToModel();
- }
-
- public async Task GetNetworkInfoAsync()
- {
- var request = new NetworkInfoRequest
- {
- Body = new NetworkInfoRequest.Types.Body { }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
-
- return await _netmapServiceClient.NetworkInfoAsync(request);
- }
-}
diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
new file mode 100644
index 0000000..da3282e
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
@@ -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 GetNetworkSettingsAsync(Context ctx)
+ {
+ if (Context.NetworkSettings != null)
+ return Context.NetworkSettings;
+
+ var info = await GetNetworkInfoAsync(ctx);
+
+ var settings = new NetworkSettings();
+
+ foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
+ {
+ SetNetworksParam(param, settings);
+ }
+
+ Context.NetworkSettings = settings;
+
+ return settings;
+ }
+
+ internal async Task GetLocalNodeInfoAsync(Context ctx)
+ {
+ var request = new LocalNodeInfoRequest
+ {
+ Body = new LocalNodeInfoRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ nameof(netmapServiceClient.LocalNodeInfoAsync));
+
+ Verifier.CheckResponse(response);
+
+ return response.Body.ToModel();
+ }
+
+ internal async Task GetNetworkInfoAsync(Context ctx)
+ {
+ var request = new NetworkInfoRequest
+ {
+ Body = new NetworkInfoRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ nameof(netmapServiceClient.NetworkInfoAsync));
+
+ Verifier.CheckResponse(response);
+
+ return response;
+ }
+
+ internal async Task GetNetmapSnapshotAsync(Context ctx)
+ {
+ var request = new NetmapSnapshotRequest
+ {
+ Body = new NetmapSnapshotRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await 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;
+ }
+ }
+}
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
index 415c001..73e982a 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
@@ -7,20 +7,20 @@ using FrostFS.Object;
namespace FrostFS.SDK.ClientV2;
-internal class ObjectReader : IDisposable
+internal class ObjectReader(AsyncServerStreamingCall call) : IDisposable
{
- public AsyncServerStreamingCall Call { get; set; }
+ public AsyncServerStreamingCall Call { get; private set; } = call;
public async Task ReadHeader()
{
if (!await Call.ResponseStream.MoveNext())
- throw new InvalidOperationException("unexpect end of stream");
+ throw new InvalidOperationException("unexpected end of stream");
var response = Call.ResponseStream.Current;
Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init)
- throw new InvalidOperationException("unexpect message type");
+ throw new InvalidOperationException("unexpected message type");
return new Object.Object
{
@@ -38,7 +38,7 @@ internal class ObjectReader : IDisposable
Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk)
- throw new InvalidOperationException("unexpect message type");
+ throw new InvalidOperationException("unexpected message type");
return response.Body.Chunk.ToByteArray();
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
similarity index 56%
rename from src/FrostFS.SDK.ClientV2/Services/Object.cs
rename to src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index 004df1d..dcb15f7 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -11,16 +10,22 @@ using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using FrostFS.Session;
-
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ClientV2.Extensions;
-using System.Threading;
namespace FrostFS.SDK.ClientV2;
-public partial class Client
+internal class ObjectServiceProvider : ContextAccessor
{
- public async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid)
+ private readonly ObjectService.ObjectServiceClient objectServiceClient;
+
+ internal ObjectServiceProvider(ObjectService.ObjectServiceClient objectServiceClient, ClientEnvironment context)
+ : base (context)
+ {
+ this.objectServiceClient = objectServiceClient;
+ }
+
+ internal async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid, Context ctx)
{
var request = new HeadRequest
{
@@ -34,18 +39,22 @@ public partial class Client
}
};
-
request.AddMetaHeader();
- request.Sign(_key);
- var response = await _objectServiceClient!.HeadAsync(request);
+ request.Sign(Context.Key);
+
+ var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ objectServiceClient!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ nameof(objectServiceClient.HeadAsync));
+
Verifier.CheckResponse(response);
return response.Body.Header.Header.ToModel();
}
- public async Task GetObjectAsync(ContainerId cid, ObjectId oid)
+ internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
+ var sessionToken = await GetOrCreateSession(ctx);
+
var request = new GetRequest
{
Body = new GetRequest.Types.Body
@@ -64,41 +73,137 @@ public partial class Client
cid.ToGrpcMessage(),
oid.ToGrpcMessage(),
ObjectSessionContext.Types.Verb.Get,
- _key
+ Context.Key
);
- request.Sign(_key);
- var obj = await GetObject(request);
+ request.Sign(Context.Key);
+
+ var obj = await GetObject(request, ctx);
return obj.ToModel();
}
- public async Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default)
+ internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx)
{
- return await PutObject(header, payload, cancellationToken);
- }
-
- public async Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default)
- {
- using var stream = new MemoryStream(payload);
- return await PutObject(header, stream, cancellationToken);
- }
-
- private Task PutObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
- {
- if (header.ClientCut)
- return PutClientCutObject(header, payload, cancellationToken);
+ if (parameters.Header == null)
+ throw new ArgumentException("Value cannot be null", nameof(parameters.Header));
+
+ if (parameters.Payload == null)
+ throw new ArgumentException("Value cannot be null", nameof(parameters.Payload));
+
+ if (parameters.ClientCut)
+ return PutClientCutObject(parameters, ctx);
else
- return PutStreamObject(header, payload, cancellationToken);
+ return PutStreamObject(parameters, ctx);
+ }
+
+ internal async Task PutSingleObjectAsync(ModelsV2.Object @object, Context ctx)
+ {
+ var sessionToken = await GetOrCreateSession(ctx);
+
+ var obj = CreateObject(@object);
+
+ var request = new PutSingleRequest
+ {
+ Body = new PutSingleRequest.Types.Body()
+ {
+ Object = obj
+ }
+ };
+
+ request.AddMetaHeader();
+ request.AddObjectSessionToken(
+ sessionToken,
+ obj.Header.ContainerId,
+ obj.ObjectId,
+ ObjectSessionContext.Types.Verb.Put,
+ Context.Key
+ );
+
+ request.Sign(Context.Key);
+
+ var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ objectServiceClient!.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ nameof(objectServiceClient.PutSingleAsync));
+
+ Verifier.CheckResponse(response);
+
+ return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
}
- private async Task PutClientCutObject(ObjectHeader header, Stream payloadStream, CancellationToken cancellationToken)
+ internal ObjectId CalculateObjectId(ObjectHeader header)
{
- ObjectId? objectId = null;
+ var grpcHeader = CreateHeader(header, []);
+
+ return new ObjectID { Value = grpcHeader.Sha256() }.ToModel();
+ }
+
+ internal async Task DeleteObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
+ {
+ var request = new DeleteRequest
+ {
+ Body = new DeleteRequest.Types.Body
+ {
+ Address = new Address
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ ObjectId = oid.ToGrpcMessage()
+ }
+ }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ objectServiceClient!.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ nameof(objectServiceClient.DeleteAsync));
+
+ Verifier.CheckResponse(response);
+ }
+
+ internal async IAsyncEnumerable SearchObjectsAsync(
+ ContainerId cid,
+ IEnumerable filters,
+ Context ctx)
+ {
+ var request = new SearchRequest
+ {
+ Body = new SearchRequest.Types.Body
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ Filters = { },
+ Version = 1 // TODO: clarify this param
+ }
+ };
+
+ request.Body.Filters.AddRange(filters.Select(f => f.ToGrpcMessage()));
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var objectsIds = SearchObjects(request, ctx);
+
+ await foreach (var oid in objectsIds)
+ {
+ yield return ObjectId.FromHash(oid.Value.ToByteArray());
+ }
+ }
+
+ private async Task PutClientCutObject(PutObjectParameters parameters, Context ctx)
+ {
+ var payloadStream = parameters.Payload!;
+ var header = parameters.Header!;
+
+ ObjectId? objectId;
List sentObjectIds = [];
ModelsV2.Object? currentObject;
- var partSize = (int)NetworkSettings["MaxObjectSize"];
+ var networkSettings = await Context.InvokeAsyncWithMetrics(() =>
+ Context.NetmapService!.GetNetworkSettingsAsync(ctx),
+ nameof(Context.NetmapService.GetNetworkSettingsAsync));
+
+ var partSize = (int)networkSettings.MaxObjectSize;
var buffer = new byte[partSize];
var largeObject = new LargeObject(header.ContainerId);
@@ -109,8 +214,6 @@ public partial class Client
while (true)
{
- cancellationToken.ThrowIfCancellationRequested();
-
var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize);
split.Previous = sentObjectIds.LastOrDefault();
@@ -124,7 +227,7 @@ public partial class Client
if (largeObject.PayloadLength == fullLength)
break;
- objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+ objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId!);
}
@@ -135,26 +238,30 @@ public partial class Client
currentObject.SetParent(largeObject);
- objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+ objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId);
var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject)
.AddChildren(sentObjectIds);
- _ = await PutSingleObjectAsync(linkObject, cancellationToken);
+ _ = await PutSingleObjectAsync(linkObject, ctx);
- return currentObject.GetParentId();
+ return CalculateObjectId(largeObject.Header);
}
- return await PutSingleObjectAsync(currentObject, cancellationToken);
+ return await PutSingleObjectAsync(currentObject, ctx);
}
- private async Task PutStreamObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
+ private async Task PutStreamObject(PutObjectParameters parameters, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
+ var payload = parameters.Payload!;
+ var header = parameters.Header!;
+
+ var sessionToken = await GetOrCreateSession(ctx);
+
var hdr = header.ToGrpcMessage();
- hdr.OwnerId = OwnerId.ToGrpcMessage();
- hdr.Version = Version.ToGrpcMessage();
+ hdr.OwnerId = Context.Owner.ToGrpcMessage();
+ hdr.Version = Context.Version.ToGrpcMessage();
var oid = new ObjectID
{
@@ -178,19 +285,17 @@ public partial class Client
hdr.ContainerId,
oid,
ObjectSessionContext.Types.Verb.Put,
- _key
+ Context.Key
);
- request.Sign(_key);
+ request.Sign(Context.Key);
- using var stream = await PutObjectInit(request);
+ using var stream = await PutObjectInit(request, ctx);
var buffer = new byte[Constants.ObjectChunkSize];
while (true)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
+ var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize, ctx.CancellationToken);
if (bufferLength == 0)
break;
@@ -201,7 +306,7 @@ public partial class Client
};
request.VerifyHeader = null;
- request.Sign(_key);
+ request.Sign(Context.Key);
await stream.Write(request);
}
@@ -211,40 +316,98 @@ public partial class Client
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
}
- public async Task PutSingleObjectAsync(ModelsV2.Object @object, CancellationToken cancellationToken = default)
+ // TODO: add implementation with stream writer!
+ private async Task GetObject(GetRequest request, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
-
- var obj = CreateObject(@object);
+ using var stream = GetObjectInit(request, ctx);
+
+ 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();
- request.AddObjectSessionToken(
- sessionToken,
- obj.Header.ContainerId,
- obj.ObjectId,
- ObjectSessionContext.Types.Verb.Put,
- _key
- );
+ Array.Copy(chunk, 0, payload, offset, length);
+ offset += chunk.Length;
+ chunk = await stream.ReadChunk();
+ }
- request.Sign(_key);
+ obj.Payload = ByteString.CopyFrom(payload);
- var response = await _objectServiceClient!.PutSingleAsync(request, null, null, cancellationToken);
- Verifier.CheckResponse(response);
-
- return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
+ return obj;
+ }
+
+ 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 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 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);
}
- public Object.Object CreateObject(ModelsV2.Object @object)
+ private Object.Object CreateObject(ModelsV2.Object @object)
{
var grpcHeader = @object.Header.ToGrpcMessage();
- grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
- grpcHeader.Version = Version.ToGrpcMessage();
+ grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
+ grpcHeader.Version = Context.Version.ToGrpcMessage();
if (@object.Payload != null)
{
@@ -271,8 +434,8 @@ public partial class Client
grpcHeader.Split.ParentHeader = grpcParentHeader;
grpcHeader.Split.ParentSignature = new Refs.Signature
{
- Key = ByteString.CopyFrom(_key.PublicKey()),
- Sign = ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())),
+ Key = ByteString.CopyFrom(Context.Key.PublicKey()),
+ Sign = ByteString.CopyFrom(Context.Key.SignData(grpcHeader.Split.Parent.ToByteArray())),
};
split.Parent = grpcHeader.Split.Parent.ToModel();
@@ -290,159 +453,29 @@ public partial class Client
obj.Signature = new Refs.Signature
{
- Key = ByteString.CopyFrom(_key.PublicKey()),
- Sign = ByteString.CopyFrom(_key.SignData(obj.ObjectId.ToByteArray())),
+ Key = ByteString.CopyFrom(Context.Key.PublicKey()),
+ Sign = ByteString.CopyFrom(Context.Key.SignData(obj.ObjectId.ToByteArray())),
};
return obj;
}
- public Header CreateHeader(ObjectHeader header, byte[]? payload)
+ private Header CreateHeader(ObjectHeader header, byte[]? payload)
{
var grpcHeader = header.ToGrpcMessage();
- grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
- grpcHeader.Version = Version.ToGrpcMessage();
+ grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
+ grpcHeader.Version = Context.Version.ToGrpcMessage();
if (header.PayloadCheckSum != null)
- {
- grpcHeader.PayloadHash = new Checksum
- {
- Type = ChecksumType.Sha256,
- Sum = ByteString.CopyFrom(header.PayloadCheckSum)
- };
- }
- else
- {
- if (payload != null)
- grpcHeader.PayloadHash = Sha256Checksum(payload);
- }
-
+ grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum);
+ else if (payload != null)
+ grpcHeader.PayloadHash = Sha256Checksum(payload);
+
return grpcHeader;
}
- public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid)
- {
- var request = new DeleteRequest
- {
- Body = new DeleteRequest.Types.Body
- {
- Address = new Address
- {
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
- }
- }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _objectServiceClient!.DeleteAsync(request);
- Verifier.CheckResponse(response);
- }
-
- public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters)
- {
- var request = new SearchRequest
- {
- Body = new SearchRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage(),
- Filters = { },
- Version = 1
- }
- };
-
- foreach (var filter in filters)
- {
- request.Body.Filters.Add(filter.ToGrpcMessage());
- }
-
-
- request.AddMetaHeader();
- request.Sign(_key);
- var objectsIds = SearchObjects(request);
-
-
- await foreach (var oid in objectsIds)
- {
- yield return ObjectId.FromHash(oid.Value.ToByteArray());
- }
- }
-
- private async Task GetObject(GetRequest request)
- {
- using var stream = GetObjectInit(request);
- var obj = await stream.ReadHeader();
- var payload = new byte[obj.Header.PayloadLength];
- var offset = 0L;
- 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 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 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)
+ private Checksum Sha256Checksum(byte[] data)
{
return new Checksum
{
@@ -450,8 +483,16 @@ public partial class Client
Sum = ByteString.CopyFrom(data.Sha256())
};
}
+
+ private async Task 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();
+ }
}
-
-
-
-
diff --git a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs
index c3d425a..6bc72df 100644
--- a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs
@@ -7,6 +7,7 @@ using Grpc.Core;
using FrostFS.Object;
using FrostFS.Refs;
+using System.Threading;
namespace FrostFS.SDK.ClientV2;
@@ -14,14 +15,13 @@ internal class SearchReader(AsyncServerStreamingCall call) : IDi
{
public AsyncServerStreamingCall Call { get; private set; } = call;
- public async Task?> Read()
+ public async Task?> Read(CancellationToken cancellationToken)
{
- if (!await Call.ResponseStream.MoveNext())
- {
+ if (!await Call.ResponseStream.MoveNext(cancellationToken))
return null;
- }
-
+
var response = Call.ResponseStream.Current;
+
Verifier.CheckResponse(response);
return response.Body?.IdList.ToList();
diff --git a/src/FrostFS.SDK.ClientV2/Services/Session.cs b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
similarity index 50%
rename from src/FrostFS.SDK.ClientV2/Services/Session.cs
rename to src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
index 3dc52a1..c8c3c47 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Session.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
@@ -5,28 +5,38 @@ using FrostFS.Session;
namespace FrostFS.SDK.ClientV2;
-public partial class Client
-{
- private async Task CreateSessionAsync(ulong expiration)
+internal class SessionServiceProvider : ContextAccessor
+{
+ private readonly SessionService.SessionServiceClient? _sessionServiceClient;
+
+ internal SessionServiceProvider(SessionService.SessionServiceClient? sessionServiceClient, ClientEnvironment context)
+ : base (context)
+ {
+ _sessionServiceClient = sessionServiceClient;
+ }
+
+ internal async Task CreateSessionAsync(ulong expiration, Context ctx)
{
var request = new CreateRequest
{
Body = new CreateRequest.Types.Body
{
- OwnerId = OwnerId.ToGrpcMessage(),
- Expiration = expiration,
+ OwnerId = Context.Owner.ToGrpcMessage(),
+ Expiration = expiration
}
};
request.AddMetaHeader();
- request.Sign(_key);
+ request.Sign(Context.Key);
- return await CreateSession(request);
+ var token = await CreateSession(request, ctx);
+
+ return token;
}
- private async Task CreateSession(CreateRequest request)
+ internal async Task CreateSession(CreateRequest request, Context ctx)
{
- var resp = await _sessionServiceClient.CreateAsync(request);
+ var resp = await _sessionServiceClient!.CreateAsync(request, null, ctx.Deadline, ctx.CancellationToken);
return new SessionToken
{
diff --git a/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs
new file mode 100644
index 0000000..54aeb51
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs
@@ -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? GrpcInvokedEvent;
+
+ internal bool IsEventRequested => GrpcInvokedEvent != null;
+
+ internal async Task InvokeAsyncWithMetrics(Func> 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(Func 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 InvokeAsyncUnaryWithMetrics(Func> 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();
+ }
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs b/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs
new file mode 100644
index 0000000..75edcc6
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs
@@ -0,0 +1,8 @@
+namespace FrostFS.SDK.ClientV2;
+
+internal class ContextAccessor(ClientEnvironment context)
+{
+ protected ClientEnvironment Context { get; set; } = context;
+}
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs b/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs
new file mode 100644
index 0000000..03067c6
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Tools/NetworkSettings.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace FrostFS.SDK.ClientV2;
+
+public class NetworkSettings
+ {
+ public ulong ContainerFee { get; internal set; }
+ public ulong ContainerAliasFee { get; internal set; }
+ public ulong InnerRingCandidateFee { get; internal set; }
+ public ulong WithdrawFee { get; internal set; }
+ public ulong EpochDuration { get; internal set; }
+ public ulong IRCandidateFee { get; internal set; }
+ public ulong MaxObjectSize { get; internal set; }
+ public ulong MaxECDataCount { get; internal set; }
+ public ulong MaxECParityCount { get; internal set; }
+ public ulong WithdrawalFee { get; internal set; }
+ public bool HomomorphicHashingDisabled { get; internal set; }
+ public bool MaintenanceModeAllowed { get; internal set; }
+
+ public Dictionary UnnamedSettings { get; } = [];
+ }
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
similarity index 76%
rename from src/FrostFS.SDK.ClientV2/Extensions/Object.cs
rename to src/FrostFS.SDK.ClientV2/Tools/Object.cs
index 3765e71..e7e59f1 100644
--- a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
@@ -1,10 +1,11 @@
+using System;
using System.Collections.Generic;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Extensions;
-public static class Extensions
+public static class ObjectExtensions
{
public static ModelsV2.Object SetPayloadLength(this ModelsV2.Object obj, ulong length)
{
@@ -41,4 +42,15 @@ public static class Extensions
linkObject.Header.Split!.Children.AddRange(objectIds);
return linkObject;
}
+
+ public static ModelsV2.Object CalculateObjectId(this ModelsV2.Object obj)
+ {
+ if (obj.Payload == null)
+ throw new MissingFieldException("Payload cannot be null");
+
+ if (obj.Header == null)
+ throw new MissingFieldException("Header cannot be null");
+
+ return obj;
+ }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Range.cs b/src/FrostFS.SDK.ClientV2/Tools/Range.cs
similarity index 91%
rename from src/FrostFS.SDK.ClientV2/Range.cs
rename to src/FrostFS.SDK.ClientV2/Tools/Range.cs
index 8158314..9c989c1 100644
--- a/src/FrostFS.SDK.ClientV2/Range.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Range.cs
@@ -41,10 +41,10 @@ namespace System
}
/// Create an Index pointing at first element.
- public static Index Start => new Index(0);
+ public static Index Start => new(0);
/// Create an Index pointing at beyond last element.
- public static Index End => new Index(~0);
+ public static Index End => new(~0);
/// Create an Index from the start at the position indicated by the value.
/// The index value from the start.
@@ -116,7 +116,7 @@ namespace System
/// Indicates whether the current Index object is equal to another object of the same type.
/// An object to compare with this object
- public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value;
+ public override bool Equals(object? value) => value is Index index && _value == index._value;
/// Indicates whether the current Index object is equal to another Index object.
/// An object to compare with this object
@@ -147,22 +147,16 @@ namespace System
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
///
///
- internal readonly struct Range : IEquatable
+ /// Construct a Range object using the start and end indexes.
+ /// Represent the inclusive start index of the range.
+ /// Represent the exclusive end index of the range.
+ internal readonly struct Range(Index start, Index end) : IEquatable
{
/// Represent the inclusive start index of the Range.
- public Index Start { get; }
+ public Index Start { get; } = start;
/// Represent the exclusive end index of the Range.
- public Index End { get; }
-
- /// Construct a Range object using the start and end indexes.
- /// Represent the inclusive start index of the range.
- /// Represent the exclusive end index of the range.
- public Range(Index start, Index end)
- {
- Start = start;
- End = end;
- }
+ public Index End { get; } = end;
/// Indicates whether the current Range object is equal to another object of the same type.
/// An object to compare with this object
@@ -188,13 +182,13 @@ namespace System
}
/// Create a Range object starting from start index to the end of the collection.
- public static Range StartAt(Index start) => new Range(start, Index.End);
+ public static Range StartAt(Index start) => new(start, Index.End);
/// Create a Range object starting from first element in the collection to the end Index.
- public static Range EndAt(Index end) => new Range(Index.Start, end);
+ public static Range EndAt(Index end) => new(Index.Start, end);
/// Create a Range object starting from first element to the end.
- public static Range All => new Range(Index.Start, Index.End);
+ public static Range All => new(Index.Start, Index.End);
/// Calculate the start offset and length of range object using a collection length.
/// The length of the collection that the range will be used with. length has to be a positive value.
@@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices
if (length == 0)
{
- return Array.Empty();
+ return [];
}
var dest = new T[length];
diff --git a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
similarity index 84%
rename from src/FrostFS.SDK.ClientV2/RequestConstructor.cs
rename to src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
index fb632ee..17865e2 100644
--- a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
@@ -10,20 +10,24 @@ public static class RequestConstructor
{
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();
request.MetaHeader = metaHeader;
}
public static void AddObjectSessionToken(
this IRequest request,
- SessionToken sessionToken,
+ Session.SessionToken sessionToken,
ContainerID cid,
ObjectID oid,
ObjectSessionContext.Types.Verb verb,
ECDsa key)
{
- if (request.MetaHeader.SessionToken is not null) return;
+ if (request.MetaHeader.SessionToken is not null)
+ return;
+
request.MetaHeader.SessionToken = sessionToken;
var ctx = new ObjectSessionContext
{
@@ -34,6 +38,7 @@ public static class RequestConstructor
},
Verb = verb
};
+
request.MetaHeader.SessionToken.Body.Object = ctx;
request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body);
}
diff --git a/src/FrostFS.SDK.ClientV2/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs
similarity index 95%
rename from src/FrostFS.SDK.ClientV2/RequestSigner.cs
rename to src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs
index 23f8eb6..6016ddf 100644
--- a/src/FrostFS.SDK.ClientV2/RequestSigner.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs
@@ -84,7 +84,7 @@ public static class RequestSigner
return sig;
}
- public static void Sign(this IVerificableMessage message, ECDsa key)
+ public static void Sign(this IVerifiableMessage message, ECDsa key)
{
var meta = message.GetMetaHeader();
IVerificationHeader verify = message switch
@@ -95,12 +95,15 @@ public static class RequestSigner
};
var verifyOrigin = message.GetVerificationHeader();
+
if (verifyOrigin is null)
verify.BodySignature = key.SignMessagePart(message.GetBody());
-
+ else
+ verify.SetOrigin(verifyOrigin);
+
verify.MetaSignature = key.SignMessagePart(meta);
verify.OriginSignature = key.SignMessagePart(verifyOrigin);
- verify.SetOrigin(verifyOrigin);
+
message.SetVerificationHeader(verify);
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Verifier.cs b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
similarity index 86%
rename from src/FrostFS.SDK.ClientV2/Verifier.cs
rename to src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
index ad6b3e6..5811ff1 100644
--- a/src/FrostFS.SDK.ClientV2/Verifier.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
@@ -68,7 +68,7 @@ public static class Verifier
return false;
using var key = sig.Key.ToByteArray().LoadPublicKey();
- var data2Verify = data is null ? Array.Empty() : data.ToByteArray();
+ var data2Verify = data is null ? [] : data.ToByteArray();
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
}
@@ -89,7 +89,7 @@ public static class Verifier
return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin);
}
- public static bool Verify(this IVerificableMessage message)
+ public static bool Verify(this IVerifiableMessage message)
{
return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader());
}
@@ -100,7 +100,17 @@ public static class Verifier
throw new FormatException($"invalid response, type={resp.GetType()}");
var status = resp.MetaHeader.Status.ToModel();
- if (!status.IsSuccess())
+ if (!status.IsSuccess)
throw new ApplicationException(status.ToString());
}
+
+ ///
+ /// This method is intended for unit tests for request verification.
+ ///
+ /// Created by SDK request to gRpc proxy
+ public static void CheckRequest(IRequest request)
+ {
+ if (!request.Verify())
+ throw new FormatException($"invalid response, type={request.GetType()}");
+ }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs
index a593c3a..a75808b 100644
--- a/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs
+++ b/src/FrostFS.SDK.Cryptography/AssemblyInfo.cs
@@ -17,4 +17,4 @@ using System.Runtime.InteropServices;
[assembly: Guid("08a8487e-39ce-41fb-9c24-13f73ff2bde0")]
-[assembly: InternalsVisibleToAttribute("FrostFS.SDK.Cryptography.Test")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("FrostFS.SDK.Cryptography.Test")]
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Cryptography/Base58.cs b/src/FrostFS.SDK.Cryptography/Base58.cs
index d9d91cf..f33794a 100644
--- a/src/FrostFS.SDK.Cryptography/Base58.cs
+++ b/src/FrostFS.SDK.Cryptography/Base58.cs
@@ -34,9 +34,11 @@ public static class Base58
byte[] checksum = data.ToArray().Sha256().Sha256();
Span buffer = stackalloc byte[data.Length + 4];
data.CopyTo(buffer);
+
checksum[..4].AsSpan().CopyTo(buffer[data.Length..]);
var ret = Encode(buffer);
buffer.Clear();
+
return ret;
}
diff --git a/src/FrostFS.SDK.Cryptography/Helper.cs b/src/FrostFS.SDK.Cryptography/Extentions.cs
similarity index 97%
rename from src/FrostFS.SDK.Cryptography/Helper.cs
rename to src/FrostFS.SDK.Cryptography/Extentions.cs
index 198dcb5..c27f5ef 100644
--- a/src/FrostFS.SDK.Cryptography/Helper.cs
+++ b/src/FrostFS.SDK.Cryptography/Extentions.cs
@@ -6,7 +6,7 @@ using System.Security.Cryptography;
namespace FrostFS.SDK.Cryptography;
-public static class Helper
+public static class Extentions
{
internal static byte[] RIPEMD160(this byte[] value)
{
diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
index aa2afc9..e276848 100644
--- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
+++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
@@ -6,6 +6,10 @@
enable
+
+ true
+
+
diff --git a/src/FrostFS.SDK.Cryptography/Key.cs b/src/FrostFS.SDK.Cryptography/Key.cs
index 56f71cb..de96791 100644
--- a/src/FrostFS.SDK.Cryptography/Key.cs
+++ b/src/FrostFS.SDK.Cryptography/Key.cs
@@ -56,7 +56,7 @@ public static class KeyExtension
var script = new byte[] { 0x0c, CompressedPublicKeyLength }; //PUSHDATA1 33
script = ArrayHelper.Concat(script, publicKey);
- script = ArrayHelper.Concat(script, new byte[] { 0x41 }); //SYSCALL
+ script = ArrayHelper.Concat(script, [0x41]); //SYSCALL
script = ArrayHelper.Concat(script, BitConverter.GetBytes(CheckSigDescriptor)); //Neo_Crypto_CheckSig
return script;
@@ -80,7 +80,7 @@ public static class KeyExtension
private static byte[] GetPrivateKeyFromWIF(string wif)
{
if (wif == null)
- throw new ArgumentNullException();
+ throw new ArgumentNullException(nameof(wif));
var data = wif.Base58CheckDecode();
@@ -117,7 +117,7 @@ public static class KeyExtension
var pos = 33 - param.Q.X.Length;
param.Q.X.CopyTo(pubkey, pos);
- if (new BigInteger(param.Q.Y.Reverse().Concat(new byte[] { 0x00 }).ToArray()).IsEven)
+ if (new BigInteger(param.Q.Y.Reverse().Concat(new byte[] { 0x0 }).ToArray()).IsEven)
pubkey[0] = 0x2;
else
pubkey[0] = 0x3;
diff --git a/src/FrostFS.SDK.Cryptography/Range.cs b/src/FrostFS.SDK.Cryptography/Range.cs
index 8158314..7313a64 100644
--- a/src/FrostFS.SDK.Cryptography/Range.cs
+++ b/src/FrostFS.SDK.Cryptography/Range.cs
@@ -41,10 +41,10 @@ namespace System
}
/// Create an Index pointing at first element.
- public static Index Start => new Index(0);
+ public static Index Start => new(0);
/// Create an Index pointing at beyond last element.
- public static Index End => new Index(~0);
+ public static Index End => new(~0);
/// Create an Index from the start at the position indicated by the value.
/// The index value from the start.
@@ -116,7 +116,7 @@ namespace System
/// Indicates whether the current Index object is equal to another object of the same type.
/// An object to compare with this object
- public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value;
+ public override bool Equals(object? value) => value is Index index && _value == index._value;
/// Indicates whether the current Index object is equal to another Index object.
/// An object to compare with this object
@@ -147,22 +147,16 @@ namespace System
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
///
///
- internal readonly struct Range : IEquatable
+ /// Construct a Range object using the start and end indexes.
+ /// Represent the inclusive start index of the range.
+ /// Represent the exclusive end index of the range.
+ internal readonly struct Range (Index start, Index end) : IEquatable
{
/// Represent the inclusive start index of the Range.
- public Index Start { get; }
+ public Index Start { get; } = start;
/// Represent the exclusive end index of the Range.
- public Index End { get; }
-
- /// Construct a Range object using the start and end indexes.
- /// Represent the inclusive start index of the range.
- /// Represent the exclusive end index of the range.
- public Range(Index start, Index end)
- {
- Start = start;
- End = end;
- }
+ public Index End { get; } = end;
/// Indicates whether the current Range object is equal to another object of the same type.
/// An object to compare with this object
@@ -188,13 +182,13 @@ namespace System
}
/// Create a Range object starting from start index to the end of the collection.
- public static Range StartAt(Index start) => new Range(start, Index.End);
+ public static Range StartAt(Index start) => new(start, Index.End);
/// Create a Range object starting from first element in the collection to the end Index.
- public static Range EndAt(Index end) => new Range(Index.Start, end);
+ public static Range EndAt(Index end) => new(Index.Start, end);
/// Create a Range object starting from first element to the end.
- public static Range All => new Range(Index.Start, Index.End);
+ public static Range All => new(Index.Start, Index.End);
/// Calculate the start offset and length of range object using a collection length.
/// The length of the collection that the range will be used with. length has to be a positive value.
@@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices
if (length == 0)
{
- return Array.Empty();
+ return [];
}
var dest = new T[length];
diff --git a/src/FrostFS.SDK.ModelsV2/ClientSettings.cs b/src/FrostFS.SDK.ModelsV2/ClientSettings.cs
new file mode 100644
index 0000000..4268d53
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/ClientSettings.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Text;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class ClientSettings
+{
+ private static readonly string errorTemplate = "{0} is required parameter";
+
+ public string Key { get; set; } = string.Empty;
+
+ public string Host { get; set; } = string.Empty;
+
+ public void Validate()
+ {
+ StringBuilder? error = null;
+
+ if (string.IsNullOrWhiteSpace(Key))
+ (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Key)));
+
+ if (string.IsNullOrWhiteSpace(Host))
+ (error ??= new StringBuilder()).AppendLine(string.Format(errorTemplate, nameof(Host)));
+
+ if (error != null)
+ throw new ArgumentException(error.ToString());
+ }
+
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Container.cs b/src/FrostFS.SDK.ModelsV2/Container.cs
index d32ff8f..4533325 100644
--- a/src/FrostFS.SDK.ModelsV2/Container.cs
+++ b/src/FrostFS.SDK.ModelsV2/Container.cs
@@ -5,17 +5,10 @@ using FrostFS.SDK.ModelsV2.Netmap;
namespace FrostFS.SDK.ModelsV2;
-public class Container
+public class Container(BasicAcl basicAcl, PlacementPolicy placementPolicy)
{
- public Guid Nonce { get; set; }
- public BasicAcl BasicAcl { get; set; }
- public PlacementPolicy PlacementPolicy { get; set; }
- public Version Version { get; set; }
-
- public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy)
- {
- Nonce = Guid.NewGuid();
- BasicAcl = basicAcl;
- PlacementPolicy = placementPolicy;
- }
+ public Guid Nonce { get; set; } = Guid.NewGuid();
+ public BasicAcl BasicAcl { get; set; } = basicAcl;
+ public PlacementPolicy PlacementPolicy { get; set; } = placementPolicy;
+ public Version? Version { get; set; }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Context.cs b/src/FrostFS.SDK.ModelsV2/Context.cs
new file mode 100644
index 0000000..302dafc
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Context.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Threading;
+
+namespace FrostFS.SDK.ClientV2;
+
+public class Context()
+{
+ public CancellationToken CancellationToken { get; set; } = default;
+ public TimeSpan Timeout { get; set; } = default;
+ public string SessionToken { get; set; } = string.Empty;
+
+ public DateTime? Deadline => Timeout.Ticks > 0 ? DateTime.UtcNow.Add(Timeout) : null;
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs
index 5c222fc..9418039 100644
--- a/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs
+++ b/src/FrostFS.SDK.ModelsV2/Enums/BasicAcl.cs
@@ -3,7 +3,10 @@ using System.ComponentModel;
namespace FrostFS.SDK.ModelsV2.Enums;
public enum BasicAcl
-{
+{
+ [Description("Not defined ACL")]
+ NotDefined = 0x00000000,
+
[Description("Basic ACL for private container")]
Private = 0x1C8C8CCC,
diff --git a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
index 96a477c..0f47676 100644
--- a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
+++ b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
@@ -6,6 +6,10 @@
enable
+
+ true
+
+
diff --git a/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs b/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs
new file mode 100644
index 0000000..71ac276
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/GrpcCallInfo.cs
@@ -0,0 +1,8 @@
+namespace FrostFS.SDK.ModelsV2;
+
+public class GrpcCallInfo(string methodName, long elapsedMicroSec, bool hasError)
+{
+ public string MethodName { get; set; } = methodName;
+ public long ElapsedTimeMicroSec { get; set; } = elapsedMicroSec;
+ public bool HasError { get; } = hasError;
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs
index 68abbb3..d2b84a2 100644
--- a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs
+++ b/src/FrostFS.SDK.ModelsV2/MetaHeader.cs
@@ -1,17 +1,10 @@
namespace FrostFS.SDK.ModelsV2;
-public class MetaHeader
+public class MetaHeader(Version version, int epoch, int ttl)
{
- public Version Version { get; set; }
- public int Epoch { get; set; }
- public int Ttl { get; set; }
-
- public MetaHeader(Version version, int epoch, int ttl)
- {
- Version = version;
- Epoch = epoch;
- Ttl = ttl;
- }
+ public Version Version { get; set; } = version;
+ public int Epoch { get; set; } = epoch;
+ public int Ttl { get; set; } = ttl;
public static MetaHeader Default()
{
diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs
new file mode 100644
index 0000000..fb8dba1
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Netmap/NetmapInfo.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace FrostFS.SDK.ModelsV2.Netmap;
+
+public class NetmapSnapshot(ulong epoch, IReadOnlyList nodeInfoCollection)
+{
+ public ulong Epoch { get; private set; } = epoch;
+
+ public IReadOnlyList NodeInfoCollection { get; private set; } = nodeInfoCollection;
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
index d67d987..9433c04 100644
--- a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
+++ b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
@@ -1,9 +1,28 @@
+using System;
+using System.Collections.Generic;
using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2.Netmap;
public class NodeInfo
{
- public NodeState State { get; set; }
- public Version? Version { get; set; }
+ public NodeInfo(
+ Version version,
+ NodeState state,
+ IReadOnlyCollection addresses,
+ IReadOnlyDictionary attributes,
+ ReadOnlyMemory publicKey)
+ {
+ Version = version;
+ State = state;
+ Addresses = addresses;
+ Attributes = attributes;
+ PublicKey = publicKey;
+ }
+
+ public NodeState State { get; private set; }
+ public Version Version { get; private set; }
+ public IReadOnlyCollection Addresses { get; private set; }
+ public IReadOnlyDictionary Attributes { get; private set; }
+ public ReadOnlyMemory PublicKey { get; private set; }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs
index b638bb6..bb7b1a3 100644
--- a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs
+++ b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs
@@ -1,13 +1,28 @@
+using System;
+using System.Linq;
+
namespace FrostFS.SDK.ModelsV2.Netmap;
-public class PlacementPolicy
+public class PlacementPolicy(bool unique, params Replica[] replicas) : IComparable
{
- public Replica[] Replicas { get; private set; }
- public bool Unique { get; private set; }
+ public Replica[] Replicas { get; private set; } = replicas;
+ public bool Unique { get; private set; } = unique;
- public PlacementPolicy(bool unique, params Replica[] replicas)
+ public int CompareTo(PlacementPolicy other)
{
- Replicas = replicas;
- Unique = unique;
+ var notEqual = other == null
+ || Unique != other.Unique
+ || Replicas.Length != other.Replicas.Length;
+
+ if (notEqual)
+ return 1;
+
+ foreach (var replica in Replicas)
+ {
+ if (!other!.Replicas.Any(r => r.Count == replica.Count && r.Selector == replica.Selector))
+ return 1;
+ }
+
+ return 0;
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
index b4236b1..51c4630 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
@@ -1,3 +1,4 @@
+using System;
using System.Security.Cryptography;
using FrostFS.SDK.ModelsV2.Enums;
@@ -5,8 +6,11 @@ namespace FrostFS.SDK.ModelsV2;
public class Object
{
- public Object()
+ public Object(ObjectId objectId, ObjectHeader header, byte[] payload)
{
+ ObjectId = objectId;
+ Payload = payload;
+ Header = header;
}
public Object(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
@@ -14,43 +18,47 @@ public class Object
Payload = payload;
Header = new ObjectHeader(containerId: container, type: objectType, attributes: []);
}
-
+
public ObjectHeader Header { get; set; }
- public ObjectId ObjectId { get; set; }
+
+ public ObjectId? ObjectId
+ {
+ get; internal set;
+ }
+
public byte[] Payload { get; set; }
- public Signature Signature { get; set; }
+
+ public Signature? Signature { get; set; }
public void SetParent(LargeObject largeObject)
{
- Header.Split.ParentHeader = largeObject.Header;
- }
+ if (Header?.Split == null)
+ throw new Exception("The object is not initialized properly");
- public ObjectId? GetParentId()
- {
- return Header.Split?.Parent;
+ Header.Split.ParentHeader = largeObject.Header;
}
}
public class LargeObject(ContainerId container) : Object(container, [])
{
- private SHA256 payloadHash = SHA256.Create();
+ private readonly SHA256 payloadHash = SHA256.Create();
public void AppendBlock(byte[] bytes, int count)
{
- Header.PayloadLength += (ulong)count;
+ Header!.PayloadLength += (ulong)count;
this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0);
}
public LargeObject CalculateHash()
{
this.payloadHash.TransformFinalBlock([], 0, 0);
- Header.PayloadCheckSum = this.payloadHash.Hash;
+ Header!.PayloadCheckSum = this.payloadHash.Hash;
return this;
}
public ulong PayloadLength
{
- get { return Header.PayloadLength; }
+ get { return Header!.PayloadLength; }
}
}
@@ -58,7 +66,7 @@ public class LinkObject : Object
{
public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
{
- Header.Split = new Split(splitId)
+ Header!.Split = new Split(splitId)
{
ParentHeader = largeObject.Header
};
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
index db9b867..83bee87 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
@@ -22,8 +22,6 @@ public class ObjectHeader
public Split? Split { get; set; }
- public bool ClientCut { get; set; }
-
public ObjectHeader(
ContainerId containerId,
ObjectType type = ObjectType.Regular,
diff --git a/src/FrostFS.SDK.ModelsV2/ObjectId.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/ObjectId.cs
rename to src/FrostFS.SDK.ModelsV2/Object/ObjectId.cs
diff --git a/src/FrostFS.SDK.ModelsV2/OwnerId.cs b/src/FrostFS.SDK.ModelsV2/OwnerId.cs
index 85cf239..8746572 100644
--- a/src/FrostFS.SDK.ModelsV2/OwnerId.cs
+++ b/src/FrostFS.SDK.ModelsV2/OwnerId.cs
@@ -4,14 +4,9 @@ using FrostFS.SDK.Cryptography;
namespace FrostFS.SDK.ModelsV2;
-public class OwnerId
+public class OwnerId(string id)
{
- public string Value { get; }
-
- public OwnerId(string id)
- {
- Value = id;
- }
+ public string Value { get; } = id;
public static OwnerId FromKey(ECDsa key)
{
diff --git a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
new file mode 100644
index 0000000..23cbc38
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
@@ -0,0 +1,12 @@
+using System.IO;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class PutObjectParameters
+{
+ public ObjectHeader? Header { get; set; }
+
+ public Stream? Payload { get; set; }
+
+ public bool ClientCut { get; set; }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/SessionToken.cs b/src/FrostFS.SDK.ModelsV2/SessionToken.cs
new file mode 100644
index 0000000..6fde99e
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/SessionToken.cs
@@ -0,0 +1,8 @@
+namespace FrostFS.SDK.ModelsV2;
+
+public class SessionToken(byte[] sessionKey, byte[] id)
+{
+ public byte[] Id { get; } = id;
+
+ public byte[] SessionKey { get; } = sessionKey;
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Signature.cs b/src/FrostFS.SDK.ModelsV2/Signature.cs
new file mode 100644
index 0000000..1d36e1c
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Signature.cs
@@ -0,0 +1,8 @@
+namespace FrostFS.SDK.ModelsV2;
+
+public class Signature
+{
+ public byte[]? Key { get; set; }
+ public byte[]? Sign { get; set; }
+ public SignatureScheme Scheme { get; set; }
+}
diff --git a/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs b/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs
new file mode 100644
index 0000000..bd6c41d
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs
@@ -0,0 +1,7 @@
+namespace FrostFS.SDK.ModelsV2;
+
+public enum SignatureScheme {
+ EcdsaSha512,
+ EcdsaRfc6979Sha256,
+ EcdsaRfc6979Sha256WalletConnect
+ }
diff --git a/src/FrostFS.SDK.ModelsV2/SplitId.cs b/src/FrostFS.SDK.ModelsV2/SplitId.cs
new file mode 100644
index 0000000..908c143
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/SplitId.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class SplitId
+{
+ private Guid id;
+
+ public SplitId()
+ {
+ this.id = Guid.NewGuid();
+ }
+ public SplitId(Guid guid)
+ {
+ this.id = guid;
+ }
+
+ private SplitId(byte[] binary)
+ {
+ this.id = new Guid(binary);
+ }
+
+ private SplitId(string str)
+ {
+ this.id = new Guid(str);
+ }
+
+ public static SplitId CrateFromBinary(byte[] binaryData)
+ {
+ return new SplitId(binaryData);
+ }
+
+ public static SplitId CrateFromString(string stringData)
+ {
+ return new SplitId(stringData);
+ }
+
+ public override string ToString()
+ {
+ return this.id.ToString();
+ }
+
+ public byte[]? ToBinary()
+ {
+ if (this.id == Guid.Empty)
+ return null;
+
+ return this.id.ToByteArray();
+ }
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Splitter.cs b/src/FrostFS.SDK.ModelsV2/Splitter.cs
index f48f903..d4622df 100644
--- a/src/FrostFS.SDK.ModelsV2/Splitter.cs
+++ b/src/FrostFS.SDK.ModelsV2/Splitter.cs
@@ -1,20 +1,14 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace FrostFS.SDK.ModelsV2;
-public class Split
+public class Split(SplitId splitId)
{
public Split() : this(new SplitId())
{
}
- public Split(SplitId splitId)
- {
- SplitId = splitId;
- }
-
- public SplitId SplitId { get; private set; }
+ public SplitId SplitId { get; private set; } = splitId;
public ObjectId? Parent { get; set; }
@@ -26,63 +20,3 @@ public class Split
public List Children { get; } = [];
}
-
- public enum SignatureScheme {
- EcdsaSha512,
- EcdsaRfc6979Sha256,
- EcdsaRfc6979Sha256WalletConnect
- }
-
-public class Signature
-{
- public byte[] Key { get; set; }
- public byte[] Sign { get; set; }
- public SignatureScheme Scheme { get; set; }
-}
-
-public class SplitId
-{
- private Guid id;
-
- public SplitId()
- {
- this.id = Guid.NewGuid();
- }
- public SplitId(Guid guid)
- {
- this.id = guid;
- }
-
- private SplitId(byte[] binary)
- {
- this.id = new Guid(binary);
- }
-
- private SplitId(string str)
- {
- this.id = new Guid(str);
- }
-
- public static SplitId CrateFromBinary(byte[] binaryData)
- {
- return new SplitId(binaryData);
- }
-
- public static SplitId CrateFromString(string stringData)
- {
- return new SplitId(stringData);
- }
-
- public override string ToString()
- {
- return this.id.ToString();
- }
-
- public byte[]? ToBinary()
- {
- if (this.id == Guid.Empty)
- return null;
-
- return this.id.ToByteArray();
- }
-}
diff --git a/src/FrostFS.SDK.ModelsV2/Status.cs b/src/FrostFS.SDK.ModelsV2/Status.cs
index 2d56441..d52d189 100644
--- a/src/FrostFS.SDK.ModelsV2/Status.cs
+++ b/src/FrostFS.SDK.ModelsV2/Status.cs
@@ -2,21 +2,12 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2;
-public class Status
+public class Status(StatusCode code, string? message = null)
{
- public StatusCode Code { get; set; }
- public string Message { get; set; }
+ public StatusCode Code { get; set; } = code;
+ public string Message { get; set; } = message ?? string.Empty;
- public Status(StatusCode code, string? message = null)
- {
- Code = code;
- Message = message ?? string.Empty;
- }
-
- public bool IsSuccess()
- {
- return Code == StatusCode.Success;
- }
+ public bool IsSuccess => Code == StatusCode.Success;
public override string ToString()
{
diff --git a/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj
index 746a8b4..0a559ed 100644
--- a/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj
+++ b/src/FrostFS.SDK.ProtosV2/FrostFS.SDK.ProtosV2.csproj
@@ -6,6 +6,10 @@
enable
+
+ true
+
+
diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs
index f75db43..7efad42 100644
--- a/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs
+++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IRequest.cs
@@ -1,6 +1,6 @@
namespace FrostFS.Session;
-public interface IRequest : IVerificableMessage
+public interface IRequest : IVerifiableMessage
{
RequestMetaHeader MetaHeader { get; set; }
RequestVerificationHeader VerifyHeader { get; set; }
diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs
index 609be8e..ac6382a 100644
--- a/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs
+++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IResponse.cs
@@ -1,6 +1,6 @@
namespace FrostFS.Session;
-public interface IResponse : IVerificableMessage
+public interface IResponse : IVerifiableMessage
{
ResponseMetaHeader MetaHeader { get; set; }
ResponseVerificationHeader VerifyHeader { get; set; }
diff --git a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs
index 3903757..57fce64 100644
--- a/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs
+++ b/src/FrostFS.SDK.ProtosV2/Interfaces/IVerifiableMessage.cs
@@ -2,7 +2,7 @@ using Google.Protobuf;
namespace FrostFS.Session;
-public interface IVerificableMessage : IMessage
+public interface IVerifiableMessage : IMessage
{
IMetaHeader GetMetaHeader();
void SetMetaHeader(IMetaHeader metaHeader);
diff --git a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs
index a46ddc2..d8bd601 100644
--- a/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/container/Extension.Message.cs
@@ -6,22 +6,22 @@ namespace FrostFS.Container;
public partial class AnnounceUsedSpaceRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -34,22 +34,22 @@ public partial class AnnounceUsedSpaceRequest : IRequest
public partial class AnnounceUsedSpaceResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -62,22 +62,22 @@ public partial class AnnounceUsedSpaceResponse : IResponse
public partial class GetRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -90,22 +90,22 @@ public partial class GetRequest : IRequest
public partial class GetResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -118,22 +118,22 @@ public partial class GetResponse : IResponse
public partial class PutRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -146,22 +146,22 @@ public partial class PutRequest : IRequest
public partial class PutResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -174,22 +174,22 @@ public partial class PutResponse : IResponse
public partial class DeleteRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -202,22 +202,22 @@ public partial class DeleteRequest : IRequest
public partial class DeleteResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -230,22 +230,22 @@ public partial class DeleteResponse : IResponse
public partial class ListRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -258,22 +258,22 @@ public partial class ListRequest : IRequest
public partial class ListResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -286,22 +286,22 @@ public partial class ListResponse : IResponse
public partial class SetExtendedACLRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -314,22 +314,22 @@ public partial class SetExtendedACLRequest : IRequest
public partial class SetExtendedACLResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -342,22 +342,22 @@ public partial class SetExtendedACLResponse : IResponse
public partial class GetExtendedACLRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -370,22 +370,22 @@ public partial class GetExtendedACLRequest : IRequest
public partial class GetExtendedACLResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
diff --git a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs
index 207568d..ba2d11e 100644
--- a/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/netmap/Extension.Message.cs
@@ -5,22 +5,22 @@ namespace FrostFS.Netmap;
public partial class LocalNodeInfoRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -33,22 +33,22 @@ public partial class LocalNodeInfoRequest : IRequest
public partial class LocalNodeInfoResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -61,22 +61,22 @@ public partial class LocalNodeInfoResponse : IResponse
public partial class NetworkInfoRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -89,22 +89,22 @@ public partial class NetworkInfoRequest : IRequest
public partial class NetworkInfoResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -114,3 +114,60 @@ public partial class NetworkInfoResponse : IResponse
return Body;
}
}
+
+
+public partial class NetmapSnapshotRequest : IRequest
+{
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
+ {
+ return MetaHeader;
+ }
+
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
+ {
+ return VerifyHeader;
+ }
+
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ {
+ MetaHeader = (RequestMetaHeader)metaHeader;
+ }
+
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ {
+ VerifyHeader = (RequestVerificationHeader)verificationHeader;
+ }
+
+ public IMessage GetBody()
+ {
+ return Body;
+ }
+}
+
+public partial class NetmapSnapshotResponse : IResponse
+{
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
+ {
+ return MetaHeader;
+ }
+
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
+ {
+ return VerifyHeader;
+ }
+
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ {
+ MetaHeader = (ResponseMetaHeader)metaHeader;
+ }
+
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ {
+ VerifyHeader = (ResponseVerificationHeader)verificationHeader;
+ }
+
+ public IMessage GetBody()
+ {
+ return Body;
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
index a7cd446..de933fb 100644
--- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
@@ -6,22 +6,22 @@ namespace FrostFS.Object
{
public partial class GetRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -34,22 +34,22 @@ namespace FrostFS.Object
public partial class GetResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -62,22 +62,22 @@ namespace FrostFS.Object
public partial class PutRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -90,22 +90,22 @@ namespace FrostFS.Object
public partial class PutResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -118,22 +118,22 @@ namespace FrostFS.Object
public partial class PutSingleRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -146,22 +146,22 @@ namespace FrostFS.Object
public partial class PutSingleResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -174,22 +174,22 @@ namespace FrostFS.Object
public partial class DeleteRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -203,25 +203,25 @@ namespace FrostFS.Object
public partial class DeleteResponse : IResponse
{
[DebuggerStepThrough]
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
[DebuggerStepThrough]
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
[DebuggerStepThrough]
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
[DebuggerStepThrough]
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -235,22 +235,22 @@ namespace FrostFS.Object
public partial class HeadRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -263,22 +263,22 @@ namespace FrostFS.Object
public partial class HeadResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -290,22 +290,22 @@ namespace FrostFS.Object
}
public partial class SearchRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -318,22 +318,22 @@ namespace FrostFS.Object
public partial class SearchResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -346,22 +346,22 @@ namespace FrostFS.Object
public partial class GetRangeRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -374,22 +374,22 @@ namespace FrostFS.Object
public partial class GetRangeResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -402,22 +402,22 @@ namespace FrostFS.Object
public partial class GetRangeHashRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
@@ -430,22 +430,22 @@ namespace FrostFS.Object
public partial class GetRangeHashResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
diff --git a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs
index fd78adc..937af24 100644
--- a/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/session/Extension.Message.cs
@@ -4,22 +4,22 @@ namespace FrostFS.Session;
public partial class CreateResponse : IResponse
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
@@ -32,22 +32,22 @@ public partial class CreateResponse : IResponse
public partial class CreateRequest : IRequest
{
- IMetaHeader IVerificableMessage.GetMetaHeader()
+ IMetaHeader IVerifiableMessage.GetMetaHeader()
{
return MetaHeader;
}
- IVerificationHeader IVerificableMessage.GetVerificationHeader()
+ IVerificationHeader IVerifiableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
- void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
+ void IVerifiableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (RequestMetaHeader)metaHeader;
}
- void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
+ void IVerifiableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (RequestVerificationHeader)verificationHeader;
}
diff --git a/src/FrostFS.SDK.Tests/ClientTest.cs b/src/FrostFS.SDK.Tests/ClientTest.cs
new file mode 100644
index 0000000..a95f1d9
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ClientTest.cs
@@ -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()
+ // {
+ // }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/ClientTestLive.cs
new file mode 100644
index 0000000..7a849df
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ClientTestLive.cs
@@ -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 GetOptions(string key, string url)
+ {
+ var settings = new ClientSettings
+ {
+ Key = key,
+ Host = url
+ };
+
+ return Options.Create(settings);
+ }
+
+ static async Task Cleanup(IFrostFSClient fsClient)
+ {
+ await foreach (var cid in fsClient.ListContainersAsync())
+ {
+ await fsClient.DeleteContainerAsync(cid);
+ }
+ }
+
+ static async Task GetContainer(IFrostFSClient fsClient, ContainerId id, Context ctx)
+ {
+ while (true)
+ {
+ try
+ {
+ await Task.Delay(100);
+ return await fsClient.GetContainerAsync(id, ctx);
+ }
+ catch (ApplicationException)
+ {
+ if (DateTime.UtcNow >= ctx.Deadline)
+ throw new TimeoutException();
+ }
+ catch (Grpc.Core.RpcException)
+ {
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs
new file mode 100644
index 0000000..bbf1476
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs
@@ -0,0 +1,28 @@
+using System.Security.Cryptography;
+using FrostFS.Container;
+using Moq;
+
+using FrostFS.SDK.Cryptography;
+using FrostFS.SDK.ModelsV2.Enums;
+using FrostFS.SDK.ModelsV2.Netmap;
+
+namespace FrostFS.SDK.Tests;
+
+public abstract class ServiceBase(string key)
+{
+ public ECDsa Key { get; private set; } = key.LoadWif();
+ public ModelsV2.Version Version { get; set; } = DefaultVersion;
+ public BasicAcl Acl { get; set; } = DefaultAcl;
+ public PlacementPolicy PlacementPolicy { get; set; } = DefaultPlacementPolicy;
+
+ public static ModelsV2.Version DefaultVersion { get; } = new(2, 13);
+ public static BasicAcl DefaultAcl { get; } = BasicAcl.PublicRW;
+ public static PlacementPolicy DefaultPlacementPolicy { get; } = new PlacementPolicy(true, new Replica(1));
+}
+
+public abstract class ContainerServiceBase(string key) : ServiceBase (key)
+{
+ public Guid ContainerGuid { get; set; } = Guid.NewGuid();
+
+ public abstract Mock GetMock();
+}
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs
new file mode 100644
index 0000000..6639645
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs
@@ -0,0 +1,69 @@
+using FrostFS.Container;
+using FrostFS.Session;
+using Google.Protobuf;
+using Grpc.Core;
+using Moq;
+
+namespace FrostFS.SDK.Tests;
+
+public class DeleteContainerMock(string key) : ContainerServiceBase(key)
+{
+ public override Mock GetMock()
+ {
+ var mock = new Mock();
+
+ var v = mock.Setup(x => x.DeleteAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()));
+
+
+ v.Returns((Object.DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
+ {
+ var deleteResponse = new Object.DeleteResponse
+ {
+ Body = new Object.DeleteResponse.Types.Body
+ {
+ Tombstone = new Refs.Address
+ {
+ ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ }
+ },
+ MetaHeader = new ResponseMetaHeader()
+ };
+
+ var metadata = new Metadata();
+
+ return new AsyncUnaryCall(
+ Task.FromResult(deleteResponse),
+ Task.FromResult(metadata),
+ () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
+ () => metadata,
+ () => Console.WriteLine("disposed"));
+ });
+
+ return mock;
+ }
+}
+
+
+ // objectServiceClientMock.Setup(x => x.Head(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ // .Returns((Object.DeleteRequest r, Metadata m, DateTime dt, CancellationToken ct) =>
+ // {
+ // return new
+ // {
+ // Body = new Object.DeleteResponse.Types.Body
+ // {
+ // Tombstone = new Refs.Address
+ // {
+ // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ // }
+ // }
+ // };
+ // });
+
+
+
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs
new file mode 100644
index 0000000..61feb5e
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs
@@ -0,0 +1,163 @@
+using FrostFS.Container;
+using FrostFS.Session;
+using Google.Protobuf;
+using Grpc.Core;
+using Moq;
+
+using FrostFS.SDK.Cryptography;
+using FrostFS.SDK.ClientV2;
+using FrostFS.SDK.ModelsV2.Netmap;
+using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+using FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+namespace FrostFS.SDK.Tests;
+
+public class GetContainerMock(string key) : ContainerServiceBase(key)
+{
+ public override Mock GetMock()
+ {
+ var mock = new Mock();
+
+ var grpcVersion = Version.ToGrpcMessage();
+
+ var getResponse = new GetResponse
+ {
+ Body = new GetResponse.Types.Body
+ {
+ Container = new Container.Container
+ {
+ Version = grpcVersion,
+ Nonce = ByteString.CopyFrom(ContainerGuid.ToBytes()),
+ BasicAcl = (uint)Acl,
+ PlacementPolicy = PlacementPolicy.ToGrpcMessage()
+ }
+ },
+ MetaHeader = new ResponseMetaHeader
+ {
+ Version = grpcVersion,
+ Epoch = 100,
+ Ttl = 1
+ }
+ };
+
+ getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse);
+
+ var metadata = new Metadata();
+ var getContainerResponse = new AsyncUnaryCall(
+ Task.FromResult(getResponse),
+ Task.FromResult(metadata),
+ () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
+ () => metadata,
+ () => { });
+
+ mock.Setup(x => x.GetAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .Returns((GetRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
+ {
+ Verifier.CheckRequest(r);
+
+ return getContainerResponse;
+ });
+
+ return mock;
+ }
+
+ private ResponseVerificationHeader GetResponseVerificationHeader(GetResponse response)
+ {
+ var verifyHeader = new ResponseVerificationHeader
+ {
+ MetaSignature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray()))
+ },
+ BodySignature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray()))
+ },
+ OriginSignature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = FrostFS.Refs.SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData([]))
+ }
+ };
+
+ return verifyHeader;
+ }
+}
+
+// objectServiceClientMock.Setup(
+// x => x.Put(It.IsAny(), It.IsAny(), It.IsAny()))
+// .Returns((Metadata m, DateTime dt, CancellationToken ct) =>
+// {
+// return new AsyncClientStreamingCall(null, null, null, null, null, null);
+
+// //IClientStreamWriter requestStream, Task responseAsync, Task responseHeadersAsync, Func getStatusFunc, Func getTrailersFunc, Action disposeAction
+// });
+
+// return objectServiceClientMock;
+// }
+
+
+// }
+
+
+ // public virtual global::FrostFS.Object.HeadResponse Head(global::FrostFS.Object.HeadRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+ // {
+ // return Head(request, new grpc::CallOptions(headers, deadline, cancellationToken));
+ // }
+
+ // objectServiceClientMock.Setup(x => x.Head(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ // .Returns((Object.DeleteRequest r, Metadata m, DateTime dt, CancellationToken ct) =>
+ // {
+ // return new
+ // {
+ // Body = new Object.DeleteResponse.Types.Body
+ // {
+ // Tombstone = new Refs.Address
+ // {
+ // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ // }
+ // }
+ // };
+ // });
+
+
+ // objectServiceClientMock.Setup(x => x.Delete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ // .Returns((Object.DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
+ // {
+ // return new Object.DeleteResponse
+ // {
+ // Body = new Object.DeleteResponse.Types.Body
+ // {
+ // Tombstone = new Refs.Address
+ // {
+ // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ // }
+ // },
+ // MetaHeader = new ResponseMetaHeader()
+ // {
+ // },
+ // VerifyHeader = new ResponseVerificationHeader()
+ // {
+ // MetaSignature = new Refs.Signature
+ // {
+ // Key = ByteString.CopyFrom(_key.PublicKey()),
+ // Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256,
+ // Sign = ByteString.CopyFrom(_key.SignData(Array.Empty()))
+
+ // // ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())),
+ // }
+ // }
+
+ // };
+ // });
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs
new file mode 100644
index 0000000..3243bdf
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/PutContainerMock.cs
@@ -0,0 +1,88 @@
+using FrostFS.Container;
+using FrostFS.Session;
+using Google.Protobuf;
+using Grpc.Core;
+using Moq;
+using FrostFS.SDK.ClientV2;
+using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+using FrostFS.SDK.ClientV2.Mappers.GRPC;
+using FrostFS.SDK.Cryptography;
+using FrostFS.Refs;
+
+namespace FrostFS.SDK.Tests;
+
+public class PutContainerMock(string key) : ContainerServiceBase(key)
+{
+ public override Mock GetMock()
+ {
+ var mock = new Mock();
+
+ PutResponse response = new()
+ {
+ Body = new PutResponse.Types.Body
+ {
+ ContainerId = new ContainerID
+ {
+ Value = ByteString.CopyFrom(ContainerGuid.ToBytes())
+ }
+ },
+ MetaHeader = new ResponseMetaHeader
+ {
+ Version = Version is null ? DefaultVersion.ToGrpcMessage() : Version.ToGrpcMessage(),
+ Epoch = 100,
+ Ttl = 1
+ }
+ };
+
+ response.VerifyHeader = PutResponseVerificationHeader(response);
+
+ var metadata = new Metadata();
+ var putContainerResponse = new AsyncUnaryCall(
+ Task.FromResult(response),
+ Task.FromResult(metadata),
+ () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
+ () => metadata,
+ () => { });
+
+ mock.Setup(x => x.PutAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()))
+ .Returns((PutRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
+ {
+ Verifier.CheckRequest(r);
+
+ return putContainerResponse;
+ });
+
+ return mock;
+ }
+
+ private ResponseVerificationHeader PutResponseVerificationHeader(PutResponse response)
+ {
+ var verifyHeader = new ResponseVerificationHeader
+ {
+ MetaSignature = new Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray()))
+ },
+ BodySignature = new Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray()))
+ },
+ OriginSignature = new Signature
+ {
+ Key = ByteString.CopyFrom(Key.PublicKey()),
+ Scheme = SignatureScheme.EcdsaRfc6979Sha256,
+ Sign = ByteString.CopyFrom(Key.SignData([]))
+ }
+ };
+
+ return verifyHeader;
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/NetmapMock.cs b/src/FrostFS.SDK.Tests/NetmapMock.cs
new file mode 100644
index 0000000..0cc3889
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/NetmapMock.cs
@@ -0,0 +1,14 @@
+using Moq;
+using FrostFS.Netmap;
+
+namespace FrostFS.SDK.Tests;
+
+public class NetmapMock(string key) : ServiceBase(key)
+{
+ public Mock GetMock()
+ {
+ var mock = new Mock();
+
+ return mock;
+ }
+}
diff --git a/src/FrostFS.SDK.Tests/ObjectMock.cs b/src/FrostFS.SDK.Tests/ObjectMock.cs
new file mode 100644
index 0000000..96296f8
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ObjectMock.cs
@@ -0,0 +1,14 @@
+using Moq;
+using FrostFS.Object;
+
+namespace FrostFS.SDK.Tests;
+
+public class ObjectMock(string key) : ServiceBase(key)
+{
+ public Mock GetMock()
+ {
+ var mock = new Mock();
+
+ return mock;
+ }
+}
diff --git a/src/FrostFS.SDK.Tests/SessionMock.cs b/src/FrostFS.SDK.Tests/SessionMock.cs
new file mode 100644
index 0000000..8309fde
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/SessionMock.cs
@@ -0,0 +1,14 @@
+using Moq;
+using FrostFS.Session;
+
+namespace FrostFS.SDK.Tests;
+
+public class SessionMock(string key) : ServiceBase(key)
+{
+ public Mock GetMock()
+ {
+ var mock = new Mock();
+
+ return mock;
+ }
+}
--
2.45.2