From 545e647d7b01b04b86133a7ec519ce3229aeeeee Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Mon, 10 Jun 2024 11:31:36 +0300
Subject: [PATCH 01/65] [#4] infrastructure and sample Client Cut
Signed-off-by: Pavel Gross
---
FrostFS.SDK.sln | 7 +-
README.md | 77 ++++-
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 +-
22 files changed, 717 insertions(+), 193 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 d7d789e9..67e5eb3f 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/README.md b/README.md
index 832b42ff..542a5443 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
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 7cb47d68..7e9e29ca 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 00000000..93d8d5fc
--- /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 02081782..d446e0eb 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 8412c6cc..5b82d1b0 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 1bfd6146..920c49a9 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 b6e68f67..ef212026 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 4c280350..bfd568d5 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 3747c921..23f8eb68 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 886768ca..75664927 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 180e3513..bc98dc8a 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 36711c07..d9d91cfa 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 93058d94..266a1937 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 8bfbc54c..349eb1c9 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 00000000..58106991
--- /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 00000000..b114f2b6
--- /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 7db6289c..5fdb54f2 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 00000000..8f51160c
--- /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 00000000..f48f9039
--- /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 243ab006..6334c71d 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 f8f26958..c0c5b253 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";
}
From b69d22966f0335d64946cdb3cfba94b042d5b9a8 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Fri, 14 Jun 2024 11:58:29 +0300
Subject: [PATCH 02/65] [#7] Client cut internal
---
FrostFS.SDK.sln | 3 +
README.md | 22 +++--
src/FrostFS.SDK.ClientV2/Client.cs | 40 +++++++-
src/FrostFS.SDK.ClientV2/Extensions/Object.cs | 2 +-
.../Interfaces/IFrostFSClient.cs | 8 +-
.../Mappers/GRPC/Object.cs | 25 ++++-
.../Mappers/GRPC/OwnerId.cs | 5 +
src/FrostFS.SDK.ClientV2/Services/Object.cs | 99 +++++++++++++++++--
.../FrostFS.SDK.Cryptography.csproj | 2 +-
src/FrostFS.SDK.Cryptography/Helper.cs | 1 +
src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs | 2 +-
src/FrostFS.SDK.ModelsV2/Object/Object.cs | 5 +-
.../Object/ObjectHeader.cs | 4 +-
.../object/Extension.Message.cs | 6 ++
.../FrostFS.SDK.Tests.csproj | 28 ++++++
15 files changed, 216 insertions(+), 36 deletions(-)
create mode 100644 src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj
diff --git a/FrostFS.SDK.sln b/FrostFS.SDK.sln
index 67e5eb3f..fcfe7324 100644
--- a/FrostFS.SDK.sln
+++ b/FrostFS.SDK.sln
@@ -1,4 +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}"
diff --git a/README.md b/README.md
index 542a5443..89ab89ac 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,7 @@ static async Task PutObjectClientCut(IFrostFSClient fsClient, Contain
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)
{
@@ -121,7 +121,7 @@ static async Task PutObjectClientCut(IFrostFSClient fsClient, Contain
largeObject.AppendBlock(buffer, bytesCount);
- currentObject = new FrostFS.SDK.ModelsV2.Object(containerId, buffer)
+ currentObject = new FrostFS.SDK.ModelsV2.Object(containerId, bytesCount < partSize ? buffer.Take(bytesCount).ToArray() : buffer)
.AddAttribute(fileNameAttribute)
.SetSplit(split);
@@ -134,15 +134,19 @@ static async Task PutObjectClientCut(IFrostFSClient fsClient, Contain
if (sentObjectIds.Any())
{
- largeObject.CalculateHash();
-
- var linkObject = new LinkObject(containerId, split.SplitId, largeObject)
- .AddChildren(sentObjectIds);
-
- _ = await fsClient.PutSingleObjectAsync(linkObject);
+ largeObject.CalculateHash()
+ .AddAttribute(fileNameAttribute);
currentObject.SetParent(largeObject);
- _ = await fsClient.PutSingleObjectAsync(currentObject);
+
+ 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();
}
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 7e9e29ca..96fb2271 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -1,5 +1,7 @@
using System;
+using System.Collections.Generic;
using System.Security.Cryptography;
+using System.Text;
using System.Threading.Tasks;
using FrostFS.Container;
using FrostFS.Netmap;
@@ -10,6 +12,7 @@ using FrostFS.SDK.ModelsV2;
using FrostFS.Session;
using Grpc.Core;
using Grpc.Net.Client;
+using static FrostFS.Netmap.NetworkConfig.Types;
using Version = FrostFS.SDK.ModelsV2.Version;
namespace FrostFS.SDK.ClientV2;
@@ -21,6 +24,8 @@ public partial class Client: IFrostFSClient
public readonly OwnerId OwnerId;
public readonly Version Version = new(2, 13);
+ private readonly Dictionary NetworkSettings = [];
+
private ContainerService.ContainerServiceClient? _containerServiceClient;
private NetmapService.NetmapServiceClient? _netmapServiceClient;
private ObjectService.ObjectServiceClient? _objectServiceClient;
@@ -42,6 +47,33 @@ public partial class Client: IFrostFSClient
InitObjectClient();
InitSessionClient();
CheckFrostFsVersionSupport();
+
+ InitNetworkInfoAsync();
+ }
+
+ private async void InitNetworkInfoAsync()
+ {
+ var info = await GetNetworkInfoAsync();
+
+ foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
+ {
+ SetNetworksParam(param);
+ }
+ }
+
+ 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()
@@ -84,7 +116,11 @@ public partial class Client: IFrostFSClient
throw new ArgumentException(msg);
}
- _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions { Credentials = grpcCredentials });
+ _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
+ {
+ Credentials = grpcCredentials,
+ HttpHandler = new System.Net.Http.HttpClientHandler()
+ });
}
private void InitContainerClient()
@@ -106,4 +142,4 @@ public partial class Client: IFrostFSClient
{
_sessionServiceClient = new SessionService.SessionServiceClient(_channel);
}
-}
\ No newline at end of file
+}
diff --git a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs b/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
index 93d8d5fc..3765e71c 100644
--- a/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Extensions/Object.cs
@@ -38,7 +38,7 @@ public static class Extensions
public static LinkObject AddChildren(this LinkObject linkObject, IEnumerable objectIds)
{
- linkObject.Header.Split.Children.AddRange(objectIds);
+ linkObject.Header.Split!.Children.AddRange(objectIds);
return linkObject;
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
index 5b82d1b0..9ae68333 100644
--- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
+++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using FrostFS.SDK.ModelsV2;
@@ -18,13 +19,14 @@ public interface IFrostFSClient
Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId);
+
Task GetObjectAsync(ContainerId containerId, ObjectId objectId);
- Task PutObjectAsync(ObjectHeader header, Stream payload);
+ Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default);
- Task PutObjectAsync(ObjectHeader header, byte[] payload);
+ Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default);
- Task PutSingleObjectAsync(ModelsV2.Object obj);
+ Task PutSingleObjectAsync(ModelsV2.Object obj, CancellationToken cancellationToken = default);
Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId);
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
index ef212026..15a7b1cc 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
@@ -88,8 +88,8 @@ public static class ObjectHeaderMapper
if (split.Children != null && split.Children.Any())
head.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
- }
-
+ }
+
return head;
}
@@ -103,15 +103,31 @@ public static class ObjectHeaderMapper
_ => throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.")
};
- return new ObjectHeader(
+ var model = new ObjectHeader(
new ContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())),
objTypeName,
header.Attributes.Select(attribute => attribute.ToModel()).ToArray()
)
{
PayloadLength = header.PayloadLength,
- Version = header.Version.ToModel()
+ Version = header.Version.ToModel(),
+ OwnerId = header.OwnerId.ToModel()
};
+
+ if (header.Split != null)
+ {
+ model.Split = new Split(SplitId.CrateFromBinary(header.Split.SplitId.ToByteArray()))
+ {
+ Parent = header.Split.Parent?.ToModel(),
+ ParentHeader = header.Split.ParentHeader?.ToModel(),
+ Previous = header.Split.Previous?.ToModel()
+ };
+
+ if (header.Split.Children.Any())
+ model.Split.Children.AddRange(header.Split.Children.Select(x => x.ToModel()));
+ }
+
+ return model;
}
}
@@ -148,4 +164,3 @@ public static class SignatureMapper
};
}
}
-
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs
index 4bea66dd..b4f332e1 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs
@@ -13,4 +13,9 @@ public static class OwnerIdMapper
Value = ByteString.CopyFrom(ownerId.ToHash())
};
}
+
+ public static OwnerId ToModel(this OwnerID ownerId)
+ {
+ return new OwnerId(ownerId.ToString());
+ }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/Object.cs
index bc98dc8a..004df1de 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/Object.cs
@@ -13,6 +13,8 @@ using FrostFS.SDK.Cryptography;
using FrostFS.Session;
using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ClientV2.Extensions;
+using System.Threading;
namespace FrostFS.SDK.ClientV2;
@@ -32,6 +34,7 @@ public partial class Client
}
};
+
request.AddMetaHeader();
request.Sign(_key);
var response = await _objectServiceClient!.HeadAsync(request);
@@ -70,18 +73,83 @@ public partial class Client
return obj.ToModel();
}
- public async Task PutObjectAsync(ObjectHeader header, Stream payload)
+ public async Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default)
{
- return await PutObject(header, payload);
+ return await PutObject(header, payload, cancellationToken);
}
- public async Task PutObjectAsync(ObjectHeader header, byte[] payload)
+ public async Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default)
{
using var stream = new MemoryStream(payload);
- return await PutObject(header, stream);
+ return await PutObject(header, stream, cancellationToken);
}
- private async Task PutObject(ObjectHeader header, Stream payload)
+ private Task PutObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
+ {
+ if (header.ClientCut)
+ return PutClientCutObject(header, payload, cancellationToken);
+ else
+ return PutStreamObject(header, payload, cancellationToken);
+ }
+
+ private async Task PutClientCutObject(ObjectHeader header, Stream payloadStream, CancellationToken cancellationToken)
+ {
+ ObjectId? objectId = null;
+ List sentObjectIds = [];
+ ModelsV2.Object? currentObject;
+
+ var partSize = (int)NetworkSettings["MaxObjectSize"];
+ var buffer = new byte[partSize];
+
+ var largeObject = new LargeObject(header.ContainerId);
+
+ var split = new Split();
+
+ var fullLength = (ulong)payloadStream.Length;
+
+ while (true)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize);
+
+ split.Previous = sentObjectIds.LastOrDefault();
+
+ largeObject.AppendBlock(buffer, bytesCount);
+
+ currentObject = new ModelsV2.Object(header.ContainerId, bytesCount < partSize ? buffer.Take(bytesCount).ToArray() : buffer)
+ .AddAttributes(header.Attributes)
+ .SetSplit(split);
+
+ if (largeObject.PayloadLength == fullLength)
+ break;
+
+ objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+
+ sentObjectIds.Add(objectId!);
+ }
+
+ if (sentObjectIds.Any())
+ {
+ largeObject.CalculateHash();
+
+ currentObject.SetParent(largeObject);
+
+ objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+ sentObjectIds.Add(objectId);
+
+ var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject)
+ .AddChildren(sentObjectIds);
+
+ _ = await PutSingleObjectAsync(linkObject, cancellationToken);
+
+ return currentObject.GetParentId();
+ }
+
+ return await PutSingleObjectAsync(currentObject, cancellationToken);
+ }
+
+ private async Task PutStreamObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
{
var sessionToken = await CreateSessionAsync(uint.MaxValue);
var hdr = header.ToGrpcMessage();
@@ -120,6 +188,8 @@ public partial class Client
while (true)
{
+ cancellationToken.ThrowIfCancellationRequested();
+
var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
if (bufferLength == 0)
@@ -141,7 +211,7 @@ public partial class Client
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
}
- public async Task PutSingleObjectAsync(ModelsV2.Object @object)
+ public async Task PutSingleObjectAsync(ModelsV2.Object @object, CancellationToken cancellationToken = default)
{
var sessionToken = await CreateSessionAsync(uint.MaxValue);
@@ -163,7 +233,7 @@ public partial class Client
request.Sign(_key);
- var response = await _objectServiceClient!.PutSingleAsync(request);
+ var response = await _objectServiceClient!.PutSingleAsync(request, null, null, cancellationToken);
Verifier.CheckResponse(response);
return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
@@ -207,6 +277,8 @@ public partial class Client
split.Parent = grpcHeader.Split.Parent.ToModel();
}
+
+ grpcHeader.Split.Previous = split.Previous?.ToGrpcMessage();
}
var obj = new Object.Object
@@ -286,10 +358,12 @@ public partial class Client
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());
@@ -301,12 +375,14 @@ public partial class Client
using var stream = GetObjectInit(request);
var obj = await stream.ReadHeader();
var payload = new byte[obj.Header.PayloadLength];
- var offset = 0;
+ var offset = 0L;
var chunk = await stream.ReadChunk();
- while (chunk is not null)
+ while (chunk is not null && (ulong)offset < obj.Header.PayloadLength)
{
- chunk.CopyTo(payload, offset);
+ 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();
}
@@ -321,6 +397,7 @@ public partial class Client
if (initRequest is null)
throw new ArgumentNullException(nameof(initRequest));
+
return new ObjectReader
{
Call = _objectServiceClient!.Get(initRequest)
@@ -376,3 +453,5 @@ public partial class Client
}
+
+
diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
index 05347202..aa2afc98 100644
--- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
+++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj
@@ -7,8 +7,8 @@
+
-
diff --git a/src/FrostFS.SDK.Cryptography/Helper.cs b/src/FrostFS.SDK.Cryptography/Helper.cs
index 266a1937..198dcb56 100644
--- a/src/FrostFS.SDK.Cryptography/Helper.cs
+++ b/src/FrostFS.SDK.Cryptography/Helper.cs
@@ -11,6 +11,7 @@ public static class Helper
internal static byte[] RIPEMD160(this byte[] value)
{
var hash = new byte[20];
+
var digest = new RipeMD160Digest();
digest.BlockUpdate(value, 0, value.Length);
digest.DoFinal(hash, 0);
diff --git a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
index 7e86912b..d67d987b 100644
--- a/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
+++ b/src/FrostFS.SDK.ModelsV2/Netmap/NodeInfo.cs
@@ -5,5 +5,5 @@ namespace FrostFS.SDK.ModelsV2.Netmap;
public class NodeInfo
{
public NodeState State { get; set; }
- public Version Version { get; set; }
+ public Version? Version { get; set; }
}
\ 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 58106991..b4236b12 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
@@ -22,7 +22,7 @@ public class Object
public void SetParent(LargeObject largeObject)
{
- Header.Split!.ParentHeader = largeObject.Header;
+ Header.Split.ParentHeader = largeObject.Header;
}
public ObjectId? GetParentId()
@@ -41,10 +41,11 @@ public class LargeObject(ContainerId container) : Object(container, [])
this.payloadHash.TransformBlock(bytes, 0, count, bytes, 0);
}
- public void CalculateHash()
+ public LargeObject CalculateHash()
{
this.payloadHash.TransformFinalBlock([], 0, 0);
Header.PayloadCheckSum = this.payloadHash.Hash;
+ return this;
}
public ulong PayloadLength
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
index 8f51160c..db9b8676 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectHeader.cs
@@ -6,7 +6,7 @@ namespace FrostFS.SDK.ModelsV2;
public class ObjectHeader
{
- public OwnerId OwnerId { get; set; }
+ public OwnerId? OwnerId { get; set; }
public List Attributes { get; set; }
@@ -18,7 +18,7 @@ public class ObjectHeader
public ObjectType ObjectType { get; set; }
- public Version Version { get; set; }
+ public Version? Version { get; set; }
public Split? Split { get; set; }
diff --git a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
index 6334c71d..a7cd4466 100644
--- a/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
+++ b/src/FrostFS.SDK.ProtosV2/object/Extension.Message.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using FrostFS.Session;
using Google.Protobuf;
@@ -201,26 +202,31 @@ namespace FrostFS.Object
public partial class DeleteResponse : IResponse
{
+ [DebuggerStepThrough]
IMetaHeader IVerificableMessage.GetMetaHeader()
{
return MetaHeader;
}
+ [DebuggerStepThrough]
IVerificationHeader IVerificableMessage.GetVerificationHeader()
{
return VerifyHeader;
}
+ [DebuggerStepThrough]
void IVerificableMessage.SetMetaHeader(IMetaHeader metaHeader)
{
MetaHeader = (ResponseMetaHeader)metaHeader;
}
+ [DebuggerStepThrough]
void IVerificableMessage.SetVerificationHeader(IVerificationHeader verificationHeader)
{
VerifyHeader = (ResponseVerificationHeader)verificationHeader;
}
+ [DebuggerStepThrough]
public IMessage GetBody()
{
return Body;
diff --git a/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj
new file mode 100644
index 00000000..931a0723
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/FrostFS.SDK.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From c988ff3c767d37f5653d17b2e798d830ce503eb0 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Wed, 26 Jun 2024 12:29:33 +0300
Subject: [PATCH 03/65] [#11] Add Network Snapshot
Signed-off-by: Pavel Gross
---
.gitignore | 1 +
FrostFS.SDK.sln | 11 +-
src/FrostFS.SDK.ClientV2/Client.cs | 292 +++++++----
.../FrostFS.SDK.ClientV2.csproj | 7 +-
.../Interfaces/IFrostFSClient.cs | 37 +-
.../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} | 78 +--
.../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 | 113 ++++
src/FrostFS.SDK.ClientV2/Services/Netmap.cs | 38 --
.../Services/NetmapServiceProvider.cs | 132 +++++
.../Services/ObjectReader.cs | 10 +-
.../{Object.cs => ObjectServiceProvider.cs} | 489 +++++++++---------
.../Services/SearchReader.cs | 10 +-
.../{Session.cs => SessionServiceProvider.cs} | 28 +-
.../Tools/ClientEnvironment.cs | 40 ++
.../Tools/ContextAccessor.cs | 8 +
.../Tools/NetworkSettings.cs | 21 +
.../{Extensions => Tools}/Object.cs | 14 +-
src/FrostFS.SDK.ClientV2/{ => Tools}/Range.cs | 32 +-
.../{ => Tools}/RequestConstructor.cs | 7 +-
.../{ => 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 +-
.../Object/IObjectReader.cs | 9 +
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 | 87 ++++
src/FrostFS.SDK.Tests/ClientTestLive.cs | 229 ++++++++
.../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, 2238 insertions(+), 933 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} (53%)
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} (58%)
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 (90%)
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
create mode 100644 src/FrostFS.SDK.ModelsV2/Object/IObjectReader.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 cf6e05ab..ae0b81f4 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 fcfe7324..c192375d 100644
--- a/FrostFS.SDK.sln
+++ b/FrostFS.SDK.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
#
VisualStudioVersion = 17.5.002.0
-MinimumVisualStudioVersion =
+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ClientV2", "src\FrostFS.SDK.ClientV2\FrostFS.SDK.ClientV2.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}"
@@ -10,6 +10,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ModelsV2", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.ProtosV2", "src\FrostFS.SDK.ProtosV2\FrostFS.SDK.ProtosV2.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -32,8 +34,9 @@ Global
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 96fb2271..2b87c46e 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -1,93 +1,223 @@
-using System;
-using System.Collections.Generic;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
-using FrostFS.Container;
+using FrostFS.Container;
using FrostFS.Netmap;
using FrostFS.Object;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.Session;
using Grpc.Core;
using Grpc.Net.Client;
-using static FrostFS.Netmap.NetworkConfig.Types;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
using Version = FrostFS.SDK.ModelsV2.Version;
namespace FrostFS.SDK.ClientV2;
-public partial class Client: IFrostFSClient
+public class Client : IFrostFSClient
{
- private GrpcChannel? _channel;
- private readonly ECDsa _key;
- public readonly OwnerId OwnerId;
- public readonly Version Version = new(2, 13);
+ private bool isDisposed;
- private readonly Dictionary NetworkSettings = [];
+ internal ClientEnvironment ClientCtx { get; set; }
- private ContainerService.ContainerServiceClient? _containerServiceClient;
- private NetmapService.NetmapServiceClient? _netmapServiceClient;
- private ObjectService.ObjectServiceClient? _objectServiceClient;
- private SessionService.SessionServiceClient? _sessionServiceClient;
-
- public static IFrostFSClient GetInstance(string key, string host)
+ public static IFrostFSClient GetInstance(IOptions clientOptions, GrpcChannelOptions channelOptions)
{
- return new Client(key, host);
+ return new Client(clientOptions, channelOptions);
}
- private Client(string key, string host)
+ ///
+ /// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
+ ///
+ /// Global setting for client
+ /// Setting for gRPC cjannel
+ /// ContainerService.ContainerServiceClient implementation
+ /// Netmap.NetmapService.NetmapServiceClient implementation
+ /// Session.SessionService.SessionServiceClient implementation
+ /// Object.ObjectService.ObjectServiceClient implementation
+ ///
+ public static IFrostFSClient GetTestInstance(
+ IOptions clientOptions,
+ GrpcChannelOptions? channelOptions,
+ NetmapService.NetmapServiceClient netmapService,
+ SessionService.SessionServiceClient sessionService,
+ ContainerService.ContainerServiceClient containerService,
+ ObjectService.ObjectServiceClient objectService)
{
- // TODO: Развязать клиент и реализацию GRPC
- _key = key.LoadWif();
- OwnerId = OwnerId.FromKey(_key);
- InitGrpcChannel(host);
- InitContainerClient();
- InitNetmapClient();
- InitObjectClient();
- InitSessionClient();
+ return new Client(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService);
+ }
+
+ private Client(
+ IOptions settings,
+ GrpcChannelOptions? channelOptions,
+ ContainerService.ContainerServiceClient containerService,
+ NetmapService.NetmapServiceClient netmapService,
+ SessionService.SessionServiceClient sessionService,
+ ObjectService.ObjectServiceClient objectService)
+ {
+ var ecdsaKey = settings.Value.Key.LoadWif();
+ OwnerId.FromKey(ecdsaKey);
+
+ ClientCtx = new ClientEnvironment(
+ key: ecdsaKey,
+ owner: OwnerId.FromKey(ecdsaKey),
+ channel: InitGrpcChannel(settings.Value.Host, channelOptions),
+ version: new Version(2, 13));
+
+ ClientCtx.ContainerService = new ContainerServiceProvider(containerService, ClientCtx);
+ ClientCtx.NetmapService = new NetmapServiceProvider(netmapService, ClientCtx);
+ ClientCtx.SessionService = new SessionServiceProvider(sessionService, ClientCtx);
+ ClientCtx.ObjectService = new ObjectServiceProvider(objectService, ClientCtx);
+ }
+
+ private Client(IOptions options, GrpcChannelOptions channelOptions)
+ {
+ var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
+
+ clientSettings.Validate();
+
+ var ecdsaKey = clientSettings.Key.LoadWif();
+
+ var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
+
+ ClientCtx = new ClientEnvironment(
+ key: ecdsaKey,
+ owner: OwnerId.FromKey(ecdsaKey),
+ channel: channel,
+ version: new Version(2, 13));
+
+ ClientCtx.ContainerService = new ContainerServiceProvider(new ContainerService.ContainerServiceClient(channel), ClientCtx);
+ ClientCtx.NetmapService = new NetmapServiceProvider(new NetmapService.NetmapServiceClient(channel), ClientCtx);
+ ClientCtx.SessionService = new SessionServiceProvider(new SessionService.SessionServiceClient(channel), ClientCtx);
+ ClientCtx.ObjectService = new ObjectServiceProvider(new ObjectService.ObjectServiceClient(channel), ClientCtx);
+
CheckFrostFsVersionSupport();
-
- InitNetworkInfoAsync();
}
- private async void InitNetworkInfoAsync()
+ public void Dispose()
{
- var info = await GetNetworkInfoAsync();
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing && !isDisposed)
{
- SetNetworksParam(param);
+ ClientCtx.Dispose();
+ isDisposed = true;
}
}
- private void SetNetworksParam(Parameter param)
- {
- var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
+ public GrpcChannel Channel => ClientCtx.Channel;
- var encodedValue = param.Value.ToByteArray();
-
- ulong val = 0;
- for (var i = encodedValue.Length - 1; i >= 0; i--)
- {
- val = (val << 8) + encodedValue[i];
- }
-
- NetworkSettings.Add(key, val);
- }
-
- private async void CheckFrostFsVersionSupport()
+ public Task GetContainerAsync(ContainerId containerId, Context? ctx = null)
{
- var localNodeInfo = await GetLocalNodeInfoAsync();
- if (!localNodeInfo.Version.IsSupported(Version))
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!);
+ }
+
+ public IAsyncEnumerable ListContainersAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.ListContainersAsync(ctx!);
+ }
+
+ public Task CreateContainerAsync(ModelsV2.Container container, Context? ctx = null)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.CreateContainerAsync(container, ctx!);
+ }
+
+ public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ContainerService!.DeleteContainerAsync(containerId, ctx!);
+ }
+
+ public Task GetNetmapSnapshotAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!);
+ }
+
+ public Task GetNodeInfoAsync(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
+ }
+
+ public Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!);
+ }
+
+ public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!);
+ }
+
+ public Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!);
+ }
+
+ public Task PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.PutSingleObjectAsync(obj, ctx!);
+ }
+
+ public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.DeleteObjectAsync(containerId, objectId, ctx!);
+ }
+
+ public IAsyncEnumerable SearchObjectsAsync(
+ ContainerId containerId,
+ IEnumerable filters,
+ Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ return ClientCtx.ObjectService!.SearchObjectsAsync(containerId, filters, ctx!);
+ }
+
+ public ObjectId CalculateObjectId(ObjectHeader header)
+ {
+ return ClientCtx.ObjectService!.CalculateObjectId(header);
+ }
+
+ private async void CheckFrostFsVersionSupport(Context? ctx = default)
+ {
+ ValidateEnvironment(ref ctx);
+ var localNodeInfo = await ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
+
+ if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
{
var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
Console.WriteLine(msg);
throw new ApplicationException(msg);
}
}
-
- private void InitGrpcChannel(string host)
+
+ private void ValidateEnvironment(ref Context? ctx)
+ {
+ if (isDisposed)
+ throw new Exception("Client is disposed.");
+
+ if (ClientCtx == null || !ClientCtx.Initialized)
+ throw new Exception("Client is not initialized.");
+
+ ctx ??= new Context();
+ }
+
+ private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
{
Uri uri;
try
@@ -97,49 +227,25 @@ public partial class Client: IFrostFSClient
catch (UriFormatException e)
{
var msg = $"Host '{host}' has invalid format. Error: {e.Message}";
- Console.WriteLine(msg);
throw new ArgumentException(msg);
}
- ChannelCredentials grpcCredentials;
- switch (uri.Scheme)
+ ChannelCredentials grpcCredentials = uri.Scheme switch
{
- case "https":
- grpcCredentials = ChannelCredentials.SecureSsl;
- break;
- case "http":
- grpcCredentials = ChannelCredentials.Insecure;
- break;
- default:
- var msg = $"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.";
- Console.WriteLine(msg);
- throw new ArgumentException(msg);
+ "https" => ChannelCredentials.SecureSsl,
+ "http" => ChannelCredentials.Insecure,
+ _ => throw new ArgumentException($"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.")
+ };
+
+ if (channelOptions != null)
+ {
+ return GrpcChannel.ForAddress(uri, channelOptions);
}
-
- _channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
- {
- Credentials = grpcCredentials,
- HttpHandler = new System.Net.Http.HttpClientHandler()
- });
- }
-
- private void InitContainerClient()
- {
- _containerServiceClient = new ContainerService.ContainerServiceClient(_channel);
- }
-
- private void InitNetmapClient()
- {
- _netmapServiceClient = new NetmapService.NetmapServiceClient(_channel);
- }
-
- private void InitObjectClient()
- {
- _objectServiceClient = new ObjectService.ObjectServiceClient(_channel);
- }
-
- private void InitSessionClient()
- {
- _sessionServiceClient = new SessionService.SessionServiceClient(_channel);
+
+ return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
+ {
+ Credentials = grpcCredentials,
+ HttpHandler = new HttpClientHandler()
+ });
}
}
diff --git a/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj b/src/FrostFS.SDK.ClientV2/FrostFS.SDK.ClientV2.csproj
index d446e0eb..818bce22 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 9ae68333..4234a1d3 100644
--- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
+++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
@@ -1,34 +1,41 @@
+using System;
using System.Collections.Generic;
-using System.IO;
-using System.Threading;
using System.Threading.Tasks;
using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Netmap;
+using Grpc.Net.Client;
namespace FrostFS.SDK.ClientV2.Interfaces;
-public interface IFrostFSClient
+public interface IFrostFSClient : IDisposable
{
- Task GetContainerAsync(ContainerId containerId);
+ Task GetContainerAsync(ContainerId containerId, Context? context = default);
- IAsyncEnumerable ListContainersAsync();
+ IAsyncEnumerable ListContainersAsync(Context? context = default);
- Task CreateContainerAsync(ModelsV2.Container container);
+ Task CreateContainerAsync(ModelsV2.Container container, Context? context = default);
- Task DeleteContainerAsync(ContainerId containerId);
+ Task DeleteContainerAsync(ContainerId containerId, Context? context = default);
- Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId);
+ Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
+ Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
- Task GetObjectAsync(ContainerId containerId, ObjectId objectId);
+ Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default);
+
+ Task PutSingleObjectAsync(ModelsV2.Object obj, Context? context = default);
- Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default);
+ Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
- Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default);
+ IAsyncEnumerable SearchObjectsAsync(ContainerId cid, IEnumerable filters, Context? context = default);
- Task PutSingleObjectAsync(ModelsV2.Object obj, CancellationToken cancellationToken = default);
+ Task GetNetmapSnapshotAsync(Context? context = default);
- Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId);
+ Task GetNodeInfoAsync(Context? context = default);
+
+ ObjectId CalculateObjectId(ObjectHeader header);
+
+ GrpcChannel Channel { get; }
+}
- IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters);
-}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs b/src/FrostFS.SDK.ClientV2/Mappers/Container.cs
similarity index 75%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Container.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Container.cs
index 93eac3fe..3d1207fe 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 f8f2d56a..00000000
--- 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 00000000..359041c2
--- /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 00000000..da87dfd9
--- /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 00000000..be61b13a
--- /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 00000000..75c4bb8a
--- /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 00000000..ee819c69
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
@@ -0,0 +1,30 @@
+using System;
+using FrostFS.Object;
+using FrostFS.SDK.ModelsV2;
+using MatchType = FrostFS.Object.MatchType;
+
+namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
+
+public static class ObjectFilterMapper
+{
+ public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
+ {
+ var objMatchTypeName = filter.MatchType switch
+ {
+ ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified,
+ ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual,
+ ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual,
+ ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent,
+ ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix,
+
+ _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.")
+ };
+
+ return new SearchRequest.Types.Body.Types.Filter
+ {
+ MatchType = objMatchTypeName,
+ Key = filter.Key,
+ Value = filter.Value
+ };
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
similarity index 53%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
index 15a7b1cc..6ded9d2b 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
@@ -4,52 +4,10 @@ using FrostFS.Object;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using Google.Protobuf;
-using MatchType = FrostFS.Object.MatchType;
using ObjectType = FrostFS.Object.ObjectType;
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
-public static class ObjectAttributeMapper
-{
- public static Header.Types.Attribute ToGrpcMessage(this ObjectAttribute attribute)
- {
- return new Header.Types.Attribute
- {
- Key = attribute.Key,
- Value = attribute.Value
- };
- }
-
- public static ObjectAttribute ToModel(this Header.Types.Attribute attribute)
- {
- return new ObjectAttribute(attribute.Key, attribute.Value);
- }
-}
-
-public static class ObjectFilterMapper
-{
- public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
- {
- var objMatchTypeName = filter.MatchType switch
- {
- ModelsV2.Enums.ObjectMatchType.Unspecified => MatchType.Unspecified,
- ModelsV2.Enums.ObjectMatchType.Equals => MatchType.StringEqual,
- ModelsV2.Enums.ObjectMatchType.NotEquals => MatchType.StringNotEqual,
- ModelsV2.Enums.ObjectMatchType.KeyAbsent => MatchType.NotPresent,
- ModelsV2.Enums.ObjectMatchType.StartsWith => MatchType.CommonPrefix,
-
- _ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.")
- };
-
- return new SearchRequest.Types.Body.Types.Filter
- {
- MatchType = objMatchTypeName,
- Key = filter.Key,
- Value = filter.Value
- };
- }
-}
-
public static class ObjectHeaderMapper
{
public static Header ToGrpcMessage(this ObjectHeader header)
@@ -116,7 +74,7 @@ public static class ObjectHeaderMapper
if (header.Split != null)
{
- model.Split = new Split(SplitId.CrateFromBinary(header.Split.SplitId.ToByteArray()))
+ model.Split = new Split(SplitId.CreateFromBinary(header.Split.SplitId.ToByteArray()))
{
Parent = header.Split.Parent?.ToModel(),
ParentHeader = header.Split.ParentHeader?.ToModel(),
@@ -130,37 +88,3 @@ public static class ObjectHeaderMapper
return model;
}
}
-
-public static class ObjectMapper
-{
- public static ModelsV2.Object ToModel(this Object.Object obj)
- {
- return new ModelsV2.Object()
- {
- Header = obj.Header.ToModel(),
- ObjectId = ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
- Payload = obj.Payload.ToByteArray()
- };
- }
-}
-
-public static class SignatureMapper
-{
- public static Refs.Signature ToGrpcMessage(this Signature signature)
- {
- var scheme = signature.Scheme switch
- {
- SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256,
- SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect,
- SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512,
- _ => throw new ArgumentException(message: $"Unexpected enum value: {signature.Scheme}", paramName: nameof(signature.Scheme))
- };
-
- return new Refs.Signature
- {
- Key = ByteString.CopyFrom(signature.Key),
- Scheme = scheme,
- Sign = ByteString.CopyFrom(signature.Sign)
- };
- }
-}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/ObjectId.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectId.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs b/src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Mappers/GRPC/OwnerId.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/OwnerId.cs
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
new file mode 100644
index 00000000..402f7e12
--- /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 00000000..2da358a4
--- /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 75664927..00000000
--- 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 00000000..b2d72ee7
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
@@ -0,0 +1,113 @@
+using System.Threading.Tasks;
+using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+using FrostFS.SDK.ModelsV2;
+using FrostFS.Container;
+
+using FrostFS.SDK.ClientV2.Mappers.GRPC;
+using FrostFS.SDK.Cryptography;
+using System.Collections.Generic;
+
+namespace FrostFS.SDK.ClientV2;
+
+internal class ContainerServiceProvider : ContextAccessor
+{
+ private readonly ContainerService.ContainerServiceClient containerServiceClient;
+
+ internal ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context)
+ : base(context)
+ {
+ containerServiceClient = service;
+ }
+
+ internal async Task GetContainerAsync(ContainerId cid, Context context)
+ {
+ var request = new GetRequest
+ {
+ Body = new GetRequest.Types.Body
+ {
+ ContainerId = cid.ToGrpcMessage()
+ },
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await containerServiceClient.GetAsync(request, null, context.Deadline, context.CancellationToken);
+
+ Verifier.CheckResponse(response);
+
+ return response.Body.Container.ToModel();
+ }
+
+ internal async IAsyncEnumerable ListContainersAsync(Context ctx)
+ {
+ var request = new ListRequest
+ {
+ Body = new ListRequest.Types.Body
+ {
+ OwnerId = Context.Owner.ToGrpcMessage()
+ }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await containerServiceClient.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+
+ foreach (var cid in response.Body.ContainerIds)
+ {
+ yield return new ContainerId(Base58.Encode(cid.Value.ToByteArray()));
+ }
+ }
+
+ internal async Task CreateContainerAsync(ModelsV2.Container container, Context ctx)
+ {
+ var grpcContainer = container.ToGrpcMessage();
+ grpcContainer.OwnerId = Context.Owner.ToGrpcMessage();
+ grpcContainer.Version = Context.Version.ToGrpcMessage();
+
+ var request = new PutRequest
+ {
+ Body = new PutRequest.Types.Body
+ {
+ Container = grpcContainer,
+ Signature = Context.Key.SignRFC6979(grpcContainer),
+ }
+ };
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ //var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ // containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ // nameof(containerServiceClient.PutAsync));
+
+ Verifier.CheckResponse(response);
+
+ return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray()));
+ }
+
+ internal async Task DeleteContainerAsync(ContainerId cid, Context ctx)
+ {
+ var request = new DeleteRequest
+ {
+ Body = new DeleteRequest.Types.Body
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ Signature = Context.Key.SignRFC6979(cid.ToGrpcMessage().Value)
+ }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await containerServiceClient.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+ }
+}
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Services/Netmap.cs b/src/FrostFS.SDK.ClientV2/Services/Netmap.cs
deleted file mode 100644
index ce439ccb..00000000
--- 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 00000000..1060a20d
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
@@ -0,0 +1,132 @@
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FrostFS.Netmap;
+using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
+using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo;
+
+using FrostFS.SDK.ModelsV2.Netmap;
+using static FrostFS.Netmap.NetworkConfig.Types;
+
+namespace FrostFS.SDK.ClientV2;
+
+internal class NetmapServiceProvider : ContextAccessor
+{
+ private readonly NetmapService.NetmapServiceClient netmapServiceClient;
+
+ internal NetmapServiceProvider(NetmapService.NetmapServiceClient netmapServiceClient, ClientEnvironment context)
+ : base(context)
+ {
+ this.netmapServiceClient = netmapServiceClient;
+ }
+
+ internal async Task GetNetworkSettingsAsync(Context ctx)
+ {
+ if (Context.NetworkSettings != null)
+ return Context.NetworkSettings;
+
+ var info = await GetNetworkInfoAsync(ctx);
+
+ var settings = new NetworkSettings();
+
+ foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
+ {
+ SetNetworksParam(param, settings);
+ }
+
+ Context.NetworkSettings = settings;
+
+ return settings;
+ }
+
+ internal async Task GetLocalNodeInfoAsync(Context ctx)
+ {
+ var request = new LocalNodeInfoRequest
+ {
+ Body = new LocalNodeInfoRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ //var response = await Context.InvokeAsyncUnaryWithMetrics(() =>
+ // netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken),
+ // nameof(netmapServiceClient.LocalNodeInfoAsync));
+
+ Verifier.CheckResponse(response);
+
+ return response.Body.ToModel();
+ }
+
+ internal async Task GetNetworkInfoAsync(Context ctx)
+ {
+ var request = new NetworkInfoRequest
+ {
+ Body = new NetworkInfoRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+
+ return response;
+ }
+
+ internal async Task GetNetmapSnapshotAsync(Context ctx)
+ {
+ var request = new NetmapSnapshotRequest
+ {
+ Body = new NetmapSnapshotRequest.Types.Body { }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+
+ return response.ToModel();
+ }
+
+ private static bool GetBoolValue(byte[] bytes)
+ {
+ return bytes.Any(b => b != 0);
+ }
+
+ private static ulong GetLongValue(byte[] bytes)
+ {
+ ulong val = 0;
+ for (var i = bytes.Length - 1; i >= 0; i--)
+ val = (val << 8) + bytes[i];
+
+ return val;
+ }
+
+ private static void SetNetworksParam(Parameter param, NetworkSettings settings)
+ {
+ var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
+
+ var valueBytes = param.Value.ToByteArray();
+ switch (key)
+ {
+ case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break;
+ case "EpochDuration": settings.EpochDuration = GetLongValue(valueBytes); break;
+ case "IRCandidateFee": settings.IRCandidateFee = GetLongValue(valueBytes); break;
+ case "MaxECDataCount": settings.MaxECDataCount = GetLongValue(valueBytes); break;
+ case "MaxECParityCount": settings.MaxECParityCount = GetLongValue(valueBytes); break;
+ case "MaxObjectSize": settings.MaxObjectSize = GetLongValue(valueBytes); break;
+ case "WithdrawalFee": settings.WithdrawalFee = GetLongValue(valueBytes); break;
+ case "HomomorphicHashingDisabled": settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); break;
+ case "MaintenanceModeAllowed": settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); break;
+ default: settings.UnnamedSettings.Add(key, valueBytes); break;
+ }
+ }
+}
+
+
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
index 415c0014..73e982af 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
@@ -7,20 +7,20 @@ using FrostFS.Object;
namespace FrostFS.SDK.ClientV2;
-internal class ObjectReader : IDisposable
+internal class ObjectReader(AsyncServerStreamingCall call) : IDisposable
{
- public AsyncServerStreamingCall Call { get; set; }
+ public AsyncServerStreamingCall Call { get; private set; } = call;
public async Task ReadHeader()
{
if (!await Call.ResponseStream.MoveNext())
- throw new InvalidOperationException("unexpect end of stream");
+ throw new InvalidOperationException("unexpected end of stream");
var response = Call.ResponseStream.Current;
Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Init)
- throw new InvalidOperationException("unexpect message type");
+ throw new InvalidOperationException("unexpected message type");
return new Object.Object
{
@@ -38,7 +38,7 @@ internal class ObjectReader : IDisposable
Verifier.CheckResponse(response);
if (response.Body.ObjectPartCase != GetResponse.Types.Body.ObjectPartOneofCase.Chunk)
- throw new InvalidOperationException("unexpect message type");
+ throw new InvalidOperationException("unexpected message type");
return response.Body.Chunk.ToByteArray();
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/Object.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
similarity index 58%
rename from src/FrostFS.SDK.ClientV2/Services/Object.cs
rename to src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index 004df1de..8619cddc 100644
--- a/src/FrostFS.SDK.ClientV2/Services/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -1,26 +1,33 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Threading.Tasks;
+
using Google.Protobuf;
+using System.Threading.Tasks;
+
using FrostFS.Object;
using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using FrostFS.Session;
-
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ClientV2.Extensions;
-using System.Threading;
namespace FrostFS.SDK.ClientV2;
-public partial class Client
+internal class ObjectServiceProvider : ContextAccessor
{
- public async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid)
+ private readonly ObjectService.ObjectServiceClient objectServiceClient;
+
+ internal ObjectServiceProvider(ObjectService.ObjectServiceClient objectServiceClient, ClientEnvironment context)
+ : base (context)
+ {
+ this.objectServiceClient = objectServiceClient;
+ }
+
+ internal async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid, Context ctx)
{
var request = new HeadRequest
{
@@ -34,18 +41,20 @@ public partial class Client
}
};
-
request.AddMetaHeader();
- request.Sign(_key);
- var response = await _objectServiceClient!.HeadAsync(request);
+ request.Sign(Context.Key);
+
+ var response = await objectServiceClient!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
Verifier.CheckResponse(response);
return response.Body.Header.Header.ToModel();
}
- public async Task GetObjectAsync(ContainerId cid, ObjectId oid)
+ internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
+ var sessionToken = await GetOrCreateSession(ctx);
+
var request = new GetRequest
{
Body = new GetRequest.Types.Body
@@ -64,41 +73,131 @@ public partial class Client
cid.ToGrpcMessage(),
oid.ToGrpcMessage(),
ObjectSessionContext.Types.Verb.Get,
- _key
+ Context.Key
);
- request.Sign(_key);
- var obj = await GetObject(request);
+ request.Sign(Context.Key);
+
+ var obj = await GetObject(request, ctx);
return obj.ToModel();
}
- public async Task PutObjectAsync(ObjectHeader header, Stream payload, CancellationToken cancellationToken = default)
+ internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx)
{
- return await PutObject(header, payload, cancellationToken);
- }
-
- public async Task PutObjectAsync(ObjectHeader header, byte[] payload, CancellationToken cancellationToken = default)
- {
- using var stream = new MemoryStream(payload);
- return await PutObject(header, stream, cancellationToken);
- }
-
- private Task PutObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
- {
- if (header.ClientCut)
- return PutClientCutObject(header, payload, cancellationToken);
+ if (parameters.Header == null)
+ throw new ArgumentException("Value cannot be null", nameof(parameters.Header));
+
+ if (parameters.Payload == null)
+ throw new ArgumentException("Value cannot be null", nameof(parameters.Payload));
+
+ if (parameters.ClientCut)
+ return PutClientCutObject(parameters, ctx);
else
- return PutStreamObject(header, payload, cancellationToken);
+ return PutStreamObject(parameters, ctx);
+ }
+
+ internal async Task PutSingleObjectAsync(ModelsV2.Object @object, Context ctx)
+ {
+ var sessionToken = await GetOrCreateSession(ctx);
+
+ var obj = CreateObject(@object);
+
+ var request = new PutSingleRequest
+ {
+ Body = new PutSingleRequest.Types.Body()
+ {
+ Object = obj
+ }
+ };
+
+ request.AddMetaHeader();
+ request.AddObjectSessionToken(
+ sessionToken,
+ obj.Header.ContainerId,
+ obj.ObjectId,
+ ObjectSessionContext.Types.Verb.Put,
+ Context.Key
+ );
+
+ request.Sign(Context.Key);
+
+ var response = await objectServiceClient!.PutSingleAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+
+ return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
}
- private async Task PutClientCutObject(ObjectHeader header, Stream payloadStream, CancellationToken cancellationToken)
+ internal ObjectId CalculateObjectId(ObjectHeader header)
{
- ObjectId? objectId = null;
+ var grpcHeader = CreateHeader(header, []);
+
+ return new ObjectID { Value = grpcHeader.Sha256() }.ToModel();
+ }
+
+ internal async Task DeleteObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
+ {
+ var request = new DeleteRequest
+ {
+ Body = new DeleteRequest.Types.Body
+ {
+ Address = new Address
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ ObjectId = oid.ToGrpcMessage()
+ }
+ }
+ };
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var response = await objectServiceClient!.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
+ }
+
+ internal async IAsyncEnumerable SearchObjectsAsync(
+ ContainerId cid,
+ IEnumerable filters,
+ Context ctx)
+ {
+ var request = new SearchRequest
+ {
+ Body = new SearchRequest.Types.Body
+ {
+ ContainerId = cid.ToGrpcMessage(),
+ Filters = { },
+ Version = 1 // TODO: clarify this param
+ }
+ };
+
+ request.Body.Filters.AddRange(filters.Select(f => f.ToGrpcMessage()));
+
+ request.AddMetaHeader();
+ request.Sign(Context.Key);
+
+ var objectsIds = SearchObjects(request, ctx);
+
+ await foreach (var oid in objectsIds)
+ {
+ yield return ObjectId.FromHash(oid.Value.ToByteArray());
+ }
+ }
+
+ private async Task PutClientCutObject(PutObjectParameters parameters, Context ctx)
+ {
+ var payloadStream = parameters.Payload!;
+ var header = parameters.Header!;
+
+ ObjectId? objectId;
List sentObjectIds = [];
ModelsV2.Object? currentObject;
- var partSize = (int)NetworkSettings["MaxObjectSize"];
+ var networkSettings = await Context.NetmapService!.GetNetworkSettingsAsync(ctx);
+
+ var partSize = (int)networkSettings.MaxObjectSize;
var buffer = new byte[partSize];
var largeObject = new LargeObject(header.ContainerId);
@@ -109,8 +208,6 @@ public partial class Client
while (true)
{
- cancellationToken.ThrowIfCancellationRequested();
-
var bytesCount = await payloadStream.ReadAsync(buffer, 0, partSize);
split.Previous = sentObjectIds.LastOrDefault();
@@ -124,37 +221,41 @@ public partial class Client
if (largeObject.PayloadLength == fullLength)
break;
- objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+ objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId!);
}
- if (sentObjectIds.Any())
+ if (sentObjectIds.Count != 0)
{
largeObject.CalculateHash();
currentObject.SetParent(largeObject);
- objectId = await PutSingleObjectAsync(currentObject, cancellationToken);
+ objectId = await PutSingleObjectAsync(currentObject, ctx);
sentObjectIds.Add(objectId);
var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject)
.AddChildren(sentObjectIds);
- _ = await PutSingleObjectAsync(linkObject, cancellationToken);
+ _ = await PutSingleObjectAsync(linkObject, ctx);
- return currentObject.GetParentId();
+ return CalculateObjectId(largeObject.Header);
}
- return await PutSingleObjectAsync(currentObject, cancellationToken);
+ return await PutSingleObjectAsync(currentObject, ctx);
}
- private async Task PutStreamObject(ObjectHeader header, Stream payload, CancellationToken cancellationToken)
+ private async Task PutStreamObject(PutObjectParameters parameters, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
+ var payload = parameters.Payload!;
+ var header = parameters.Header!;
+
+ var sessionToken = await GetOrCreateSession(ctx);
+
var hdr = header.ToGrpcMessage();
- hdr.OwnerId = OwnerId.ToGrpcMessage();
- hdr.Version = Version.ToGrpcMessage();
+ hdr.OwnerId = Context.Owner.ToGrpcMessage();
+ hdr.Version = Context.Version.ToGrpcMessage();
var oid = new ObjectID
{
@@ -178,19 +279,17 @@ public partial class Client
hdr.ContainerId,
oid,
ObjectSessionContext.Types.Verb.Put,
- _key
+ Context.Key
);
- request.Sign(_key);
+ request.Sign(Context.Key);
- using var stream = await PutObjectInit(request);
+ using var stream = await PutObjectInit(request, ctx);
var buffer = new byte[Constants.ObjectChunkSize];
while (true)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize);
+ var bufferLength = await payload.ReadAsync(buffer, 0, Constants.ObjectChunkSize, ctx.CancellationToken);
if (bufferLength == 0)
break;
@@ -201,7 +300,7 @@ public partial class Client
};
request.VerifyHeader = null;
- request.Sign(_key);
+ request.Sign(Context.Key);
await stream.Write(request);
}
@@ -211,168 +310,11 @@ public partial class Client
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
}
- public async Task PutSingleObjectAsync(ModelsV2.Object @object, CancellationToken cancellationToken = default)
+ // TODO: add implementation with stream writer!
+ private async Task GetObject(GetRequest request, Context ctx)
{
- var sessionToken = await CreateSessionAsync(uint.MaxValue);
-
- var obj = CreateObject(@object);
-
- var request = new PutSingleRequest
- {
- Body = new () { Object = obj }
- };
-
- request.AddMetaHeader();
- request.AddObjectSessionToken(
- sessionToken,
- obj.Header.ContainerId,
- obj.ObjectId,
- ObjectSessionContext.Types.Verb.Put,
- _key
- );
-
- request.Sign(_key);
-
- var response = await _objectServiceClient!.PutSingleAsync(request, null, null, cancellationToken);
- Verifier.CheckResponse(response);
-
- return ObjectId.FromHash(obj.ObjectId.Value.ToByteArray());
- }
-
- public Object.Object CreateObject(ModelsV2.Object @object)
- {
- var grpcHeader = @object.Header.ToGrpcMessage();
-
- grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
- grpcHeader.Version = Version.ToGrpcMessage();
-
- if (@object.Payload != null)
- {
- grpcHeader.PayloadLength = (ulong)@object.Payload.Length;
- grpcHeader.PayloadHash = Sha256Checksum(@object.Payload);
- }
-
- var split = @object.Header.Split;
- if (split != null)
- {
- grpcHeader.Split = new Header.Types.Split
- {
- SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null
- };
-
- if (split.Children != null && split.Children.Any())
- grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
-
- if (split.ParentHeader is not null)
- {
- var grpcParentHeader = CreateHeader(split.ParentHeader, []);
-
- grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() };
- grpcHeader.Split.ParentHeader = grpcParentHeader;
- grpcHeader.Split.ParentSignature = new Refs.Signature
- {
- Key = ByteString.CopyFrom(_key.PublicKey()),
- Sign = ByteString.CopyFrom(_key.SignData(grpcHeader.Split.Parent.ToByteArray())),
- };
-
- split.Parent = grpcHeader.Split.Parent.ToModel();
- }
-
- grpcHeader.Split.Previous = split.Previous?.ToGrpcMessage();
- }
-
- var obj = new Object.Object
- {
- Header = grpcHeader,
- ObjectId = new ObjectID { Value = grpcHeader.Sha256() },
- Payload = ByteString.CopyFrom(@object.Payload)
- };
-
- obj.Signature = new Refs.Signature
- {
- Key = ByteString.CopyFrom(_key.PublicKey()),
- Sign = ByteString.CopyFrom(_key.SignData(obj.ObjectId.ToByteArray())),
- };
+ using var stream = GetObjectInit(request, ctx);
- return obj;
- }
-
- public Header CreateHeader(ObjectHeader header, byte[]? payload)
- {
- var grpcHeader = header.ToGrpcMessage();
-
- grpcHeader.OwnerId = OwnerId.ToGrpcMessage();
- grpcHeader.Version = Version.ToGrpcMessage();
-
- if (header.PayloadCheckSum != null)
- {
- grpcHeader.PayloadHash = new Checksum
- {
- Type = ChecksumType.Sha256,
- Sum = ByteString.CopyFrom(header.PayloadCheckSum)
- };
- }
- else
- {
- if (payload != null)
- grpcHeader.PayloadHash = Sha256Checksum(payload);
- }
-
- return grpcHeader;
- }
-
- public async Task DeleteObjectAsync(ContainerId cid, ObjectId oid)
- {
- var request = new DeleteRequest
- {
- Body = new DeleteRequest.Types.Body
- {
- Address = new Address
- {
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
- }
- }
- };
-
- request.AddMetaHeader();
- request.Sign(_key);
- var response = await _objectServiceClient!.DeleteAsync(request);
- Verifier.CheckResponse(response);
- }
-
- public async IAsyncEnumerable SearchObjectsAsync(ContainerId cid, params ObjectFilter[] filters)
- {
- var request = new SearchRequest
- {
- Body = new SearchRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage(),
- Filters = { },
- Version = 1
- }
- };
-
- foreach (var filter in filters)
- {
- request.Body.Filters.Add(filter.ToGrpcMessage());
- }
-
-
- request.AddMetaHeader();
- request.Sign(_key);
- var objectsIds = SearchObjects(request);
-
-
- await foreach (var oid in objectsIds)
- {
- yield return ObjectId.FromHash(oid.Value.ToByteArray());
- }
- }
-
- private async Task GetObject(GetRequest request)
- {
- using var stream = GetObjectInit(request);
var obj = await stream.ReadHeader();
var payload = new byte[obj.Header.PayloadLength];
var offset = 0L;
@@ -392,57 +334,134 @@ public partial class Client
return obj;
}
- private ObjectReader GetObjectInit(GetRequest initRequest)
+ private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx)
{
if (initRequest is null)
throw new ArgumentNullException(nameof(initRequest));
-
- return new ObjectReader
- {
- Call = _objectServiceClient!.Get(initRequest)
- };
+ var call = objectServiceClient!.Get(initRequest, null, ctx.Deadline, ctx.CancellationToken);
+
+ return new ObjectReader(call);
}
- private async Task PutObjectInit(PutRequest initRequest)
+ private async Task PutObjectInit(PutRequest initRequest, Context ctx)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
- var call = _objectServiceClient!.Put();
+ var call = objectServiceClient!.Put(null, ctx.Deadline, ctx.CancellationToken);
+
await call.RequestStream.WriteAsync(initRequest);
return new ObjectStreamer(call);
}
- private async IAsyncEnumerable SearchObjects(SearchRequest request)
+ private async IAsyncEnumerable SearchObjects(SearchRequest request, Context ctx)
{
- using var stream = GetSearchReader(request);
- var ids = await stream.Read();
- while (ids is not null)
+ using var stream = GetSearchReader(request, ctx);
+
+ while (true)
{
+ var ids = await stream.Read(ctx.CancellationToken);
+
+ if (ids == null)
+ break;
+
foreach (var oid in ids)
{
yield return oid;
}
-
- ids = await stream.Read();
}
}
- private SearchReader GetSearchReader(SearchRequest initRequest)
+ private SearchReader GetSearchReader(SearchRequest initRequest, Context ctx)
{
if (initRequest is null)
{
throw new ArgumentNullException(nameof(initRequest));
}
- return new SearchReader(_objectServiceClient!.Search(initRequest));
+ var call = objectServiceClient!.Search(initRequest, null, ctx.Deadline, ctx.CancellationToken);
+
+ return new SearchReader(call);
+ }
+
+ private Object.Object CreateObject(ModelsV2.Object @object)
+ {
+ var grpcHeader = @object.Header.ToGrpcMessage();
+
+ grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
+ grpcHeader.Version = Context.Version.ToGrpcMessage();
+
+ if (@object.Payload != null)
+ {
+ grpcHeader.PayloadLength = (ulong)@object.Payload.Length;
+ grpcHeader.PayloadHash = Sha256Checksum(@object.Payload);
+ }
+
+ var split = @object.Header.Split;
+ if (split != null)
+ {
+ grpcHeader.Split = new Header.Types.Split
+ {
+ SplitId = split.SplitId != null ? ByteString.CopyFrom(split.SplitId.ToBinary()) : null
+ };
+
+ if (split.Children != null && split.Children.Count != 0)
+ grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToGrpcMessage()));
+
+ if (split.ParentHeader is not null)
+ {
+ var grpcParentHeader = CreateHeader(split.ParentHeader, []);
+
+ grpcHeader.Split.Parent = new ObjectID { Value = grpcParentHeader.Sha256() };
+ grpcHeader.Split.ParentHeader = grpcParentHeader;
+ grpcHeader.Split.ParentSignature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(Context.Key.PublicKey()),
+ Sign = ByteString.CopyFrom(Context.Key.SignData(grpcHeader.Split.Parent.ToByteArray())),
+ };
+
+ split.Parent = grpcHeader.Split.Parent.ToModel();
+ }
+
+ grpcHeader.Split.Previous = split.Previous?.ToGrpcMessage();
+ }
+
+ var obj = new Object.Object
+ {
+ Header = grpcHeader,
+ ObjectId = new ObjectID { Value = grpcHeader.Sha256() },
+ Payload = ByteString.CopyFrom(@object.Payload)
+ };
+
+ obj.Signature = new Refs.Signature
+ {
+ Key = ByteString.CopyFrom(Context.Key.PublicKey()),
+ Sign = ByteString.CopyFrom(Context.Key.SignData(obj.ObjectId.ToByteArray())),
+ };
+
+ return obj;
}
- public Checksum Sha256Checksum(byte[] data)
+ private Header CreateHeader(ObjectHeader header, byte[]? payload)
+ {
+ var grpcHeader = header.ToGrpcMessage();
+
+ grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
+ grpcHeader.Version = Context.Version.ToGrpcMessage();
+
+ if (header.PayloadCheckSum != null)
+ grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum);
+ else if (payload != null)
+ grpcHeader.PayloadHash = Sha256Checksum(payload);
+
+ return grpcHeader;
+ }
+
+ private static Checksum Sha256Checksum(byte[] data)
{
return new Checksum
{
@@ -450,8 +469,14 @@ public partial class Client
Sum = ByteString.CopyFrom(data.Sha256())
};
}
+
+ private async Task GetOrCreateSession(Context ctx)
+ {
+ if (string.IsNullOrEmpty(ctx.SessionToken))
+ {
+ return await Context.SessionService!.CreateSessionAsync(uint.MaxValue, ctx);
+ }
+
+ return Convert.FromBase64String(ctx.SessionToken).DeserializeSessionToken();
+ }
}
-
-
-
-
diff --git a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs
index c3d425ad..6bc72dfb 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 3dc52a16..c8c3c47f 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 00000000..037078bf
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Tools/ClientEnvironment.cs
@@ -0,0 +1,40 @@
+using FrostFS.SDK.ModelsV2;
+using Grpc.Net.Client;
+using System;
+using System.Security.Cryptography;
+
+namespace FrostFS.SDK.ClientV2;
+
+public class ClientEnvironment(ECDsa key, OwnerId owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable
+{
+ internal OwnerId Owner { get; } = owner;
+ internal GrpcChannel Channel { get; private set; } = channel;
+ internal ECDsa Key { get; } = key;
+ internal ModelsV2.Version Version { get; } = version;
+ internal NetworkSettings? NetworkSettings { get; set; }
+
+ internal ContainerServiceProvider? ContainerService { get; set; }
+ internal NetmapServiceProvider? NetmapService { get; set; }
+ internal SessionServiceProvider? SessionService { get; set; }
+ internal ObjectServiceProvider? ObjectService { get; set; }
+
+ internal bool Initialized =>
+ ContainerService != null
+ && NetmapService != null
+ && SessionService != null
+ && ObjectService != null;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ Channel.Dispose();
+ }
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs b/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs
new file mode 100644
index 00000000..75edcc68
--- /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 00000000..03067c61
--- /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 3765e71c..e7e59f15 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 81583145..9c989c10 100644
--- a/src/FrostFS.SDK.ClientV2/Range.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Range.cs
@@ -41,10 +41,10 @@ namespace System
}
/// Create an Index pointing at first element.
- public static Index Start => new Index(0);
+ public static Index Start => new(0);
/// Create an Index pointing at beyond last element.
- public static Index End => new Index(~0);
+ public static Index End => new(~0);
/// Create an Index from the start at the position indicated by the value.
/// The index value from the start.
@@ -116,7 +116,7 @@ namespace System
/// Indicates whether the current Index object is equal to another object of the same type.
/// An object to compare with this object
- public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value;
+ public override bool Equals(object? value) => value is Index index && _value == index._value;
/// Indicates whether the current Index object is equal to another Index object.
/// An object to compare with this object
@@ -147,22 +147,16 @@ namespace System
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
///
///
- internal readonly struct Range : IEquatable
+ /// Construct a Range object using the start and end indexes.
+ /// Represent the inclusive start index of the range.
+ /// Represent the exclusive end index of the range.
+ internal readonly struct Range(Index start, Index end) : IEquatable
{
/// Represent the inclusive start index of the Range.
- public Index Start { get; }
+ public Index Start { get; } = start;
/// Represent the exclusive end index of the Range.
- public Index End { get; }
-
- /// Construct a Range object using the start and end indexes.
- /// Represent the inclusive start index of the range.
- /// Represent the exclusive end index of the range.
- public Range(Index start, Index end)
- {
- Start = start;
- End = end;
- }
+ public Index End { get; } = end;
/// Indicates whether the current Range object is equal to another object of the same type.
/// An object to compare with this object
@@ -188,13 +182,13 @@ namespace System
}
/// Create a Range object starting from start index to the end of the collection.
- public static Range StartAt(Index start) => new Range(start, Index.End);
+ public static Range StartAt(Index start) => new(start, Index.End);
/// Create a Range object starting from first element in the collection to the end Index.
- public static Range EndAt(Index end) => new Range(Index.Start, end);
+ public static Range EndAt(Index end) => new(Index.Start, end);
/// Create a Range object starting from first element to the end.
- public static Range All => new Range(Index.Start, Index.End);
+ public static Range All => new(Index.Start, Index.End);
/// Calculate the start offset and length of range object using a collection length.
/// The length of the collection that the range will be used with. length has to be a positive value.
@@ -252,7 +246,7 @@ namespace System.Runtime.CompilerServices
if (length == 0)
{
- return Array.Empty();
+ return [];
}
var dest = new T[length];
diff --git a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
similarity index 90%
rename from src/FrostFS.SDK.ClientV2/RequestConstructor.cs
rename to src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
index fb632eea..6b1d4cf2 100644
--- a/src/FrostFS.SDK.ClientV2/RequestConstructor.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
@@ -17,13 +17,15 @@ public static class RequestConstructor
public static void AddObjectSessionToken(
this IRequest request,
- SessionToken sessionToken,
+ Session.SessionToken sessionToken,
ContainerID cid,
ObjectID oid,
ObjectSessionContext.Types.Verb verb,
ECDsa key)
{
- if (request.MetaHeader.SessionToken is not null) return;
+ if (request.MetaHeader.SessionToken is not null)
+ return;
+
request.MetaHeader.SessionToken = sessionToken;
var ctx = new ObjectSessionContext
{
@@ -34,6 +36,7 @@ public static class RequestConstructor
},
Verb = verb
};
+
request.MetaHeader.SessionToken.Body.Object = ctx;
request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body);
}
diff --git a/src/FrostFS.SDK.ClientV2/RequestSigner.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs
similarity index 95%
rename from src/FrostFS.SDK.ClientV2/RequestSigner.cs
rename to src/FrostFS.SDK.ClientV2/Tools/RequestSigner.cs
index 23f8eb68..6016ddf3 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 ad6b3e6a..5811ff1f 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 a593c3a0..a75808b0 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 d9d91cfa..f33794a7 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 198dcb56..c27f5ef4 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 aa2afc98..e2768482 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 56f71cb1..de96791e 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 81583145..7313a64e 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 00000000..4268d531
--- /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 d32ff8f7..4533325a 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 00000000..302dafca
--- /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 5c222fc2..94180394 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 96a477c5..0f476767 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 00000000..71ac2769
--- /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 68abbb38..d2b84a22 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 00000000..fb8dba1f
--- /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 d67d987b..9433c042 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 b638bb63..bb7b1a3b 100644
--- a/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs
+++ b/src/FrostFS.SDK.ModelsV2/Netmap/PlacementPolicy.cs
@@ -1,13 +1,28 @@
+using System;
+using System.Linq;
+
namespace FrostFS.SDK.ModelsV2.Netmap;
-public class PlacementPolicy
+public class PlacementPolicy(bool unique, params Replica[] replicas) : IComparable
{
- public Replica[] Replicas { get; private set; }
- public bool Unique { get; private set; }
+ public Replica[] Replicas { get; private set; } = replicas;
+ public bool Unique { get; private set; } = unique;
- public PlacementPolicy(bool unique, params Replica[] replicas)
+ public int CompareTo(PlacementPolicy other)
{
- Replicas = replicas;
- Unique = unique;
+ var notEqual = other == null
+ || Unique != other.Unique
+ || Replicas.Length != other.Replicas.Length;
+
+ if (notEqual)
+ return 1;
+
+ foreach (var replica in Replicas)
+ {
+ if (!other!.Replicas.Any(r => r.Count == replica.Count && r.Selector == replica.Selector))
+ return 1;
+ }
+
+ return 0;
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs
new file mode 100644
index 00000000..3822dcec
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs
@@ -0,0 +1,9 @@
+using System;
+using System.Threading.Tasks;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public interface IObjectReader : IDisposable
+{
+ Task ReadChunk();
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
index b4236b12..51c46308 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 db9b8676..83bee875 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 85cf2391..87465729 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 00000000..23cbc38d
--- /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 00000000..6fde99e3
--- /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 00000000..1d36e1c5
--- /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 00000000..bd6c41d8
--- /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 00000000..eddbd143
--- /dev/null
+++ b/src/FrostFS.SDK.ModelsV2/SplitId.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace FrostFS.SDK.ModelsV2;
+
+public class SplitId
+{
+ private Guid id;
+
+ public SplitId()
+ {
+ this.id = Guid.NewGuid();
+ }
+ public SplitId(Guid guid)
+ {
+ this.id = guid;
+ }
+
+ private SplitId(byte[] binary)
+ {
+ this.id = new Guid(binary);
+ }
+
+ private SplitId(string str)
+ {
+ this.id = new Guid(str);
+ }
+
+ public static SplitId CreateFromBinary(byte[] binaryData)
+ {
+ return new SplitId(binaryData);
+ }
+
+ public static SplitId CreateFromString(string stringData)
+ {
+ return new SplitId(stringData);
+ }
+
+ public override string ToString()
+ {
+ return this.id.ToString();
+ }
+
+ public byte[]? ToBinary()
+ {
+ if (this.id == Guid.Empty)
+ return null;
+
+ return this.id.ToByteArray();
+ }
+}
diff --git a/src/FrostFS.SDK.ModelsV2/Splitter.cs b/src/FrostFS.SDK.ModelsV2/Splitter.cs
index f48f9039..d4622df9 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 2d564418..d52d1890 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 746a8b49..0a559edd 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 f75db43b..7efad420 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 609be8e3..ac6382ae 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 39037578..57fce64f 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 a46ddc20..d8bd6014 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 207568d6..ba2d11e7 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 a7cd4466..de933fb9 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 fd78adcf..937af24a 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 00000000..e543b7f6
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ClientTest.cs
@@ -0,0 +1,87 @@
+using FrostFS.SDK.ClientV2;
+using FrostFS.SDK.Cryptography;
+using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Enums;
+using FrostFS.SDK.ModelsV2.Netmap;
+using Microsoft.Extensions.Options;
+
+namespace FrostFS.SDK.Tests;
+
+public class ClientTest
+{
+ private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
+
+ [Fact]
+ public async void CreateContainerTest()
+ {
+ var factory = new PutContainerMock(this.key)
+ {
+ PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
+ Version = new ModelsV2.Version(2, 13),
+ ContainerGuid = Guid.NewGuid()
+ };
+
+ var settings = Options.Create(new ClientSettings
+ {
+ Key = key,
+ Host = "http://localhost:8080"
+ });
+
+ var fsClient = Client.GetTestInstance(
+ settings,
+ null,
+ new NetmapMock(this.key).GetMock().Object,
+ new SessionMock(this.key).GetMock().Object,
+ factory.GetMock().Object,
+ new ObjectMock(this.key).GetMock().Object);
+
+ var result = await fsClient.CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, factory.PlacementPolicy));
+
+ Assert.NotNull(result);
+ Assert.NotNull(result.Value);
+ Assert.True(Base58.Encode(factory.ContainerGuid.ToBytes()) == result.Value);
+ }
+
+ [Fact]
+ public async void GetContainerTest()
+ {
+ var factory = new GetContainerMock(this.key)
+ {
+ PlacementPolicy = new PlacementPolicy(true, new Replica(1)),
+ Version = new ModelsV2.Version(2, 13),
+ Acl = BasicAcl.PublicRO,
+ ContainerGuid = Guid.NewGuid(),
+ };
+
+ var settings = Options.Create(new ClientSettings
+ {
+ Key = key,
+ Host = "http://localhost:8080"
+ });
+
+ var fsClient = Client.GetTestInstance(
+ settings,
+ null,
+ new NetmapMock(this.key).GetMock().Object,
+ new SessionMock(this.key).GetMock().Object,
+ factory.GetMock().Object,
+ new ObjectMock(this.key).GetMock().Object);
+
+ var cid = new ContainerId(Base58.Encode(factory.ContainerGuid.ToBytes()));
+
+ var context = new Context();
+
+ var result = await fsClient.GetContainerAsync(cid, context);
+
+ Assert.NotNull(result);
+ Assert.Equal(factory.Acl, result.BasicAcl);
+ Assert.Equal(factory.ContainerGuid, result.Nonce);
+ Assert.Equal(0, factory.PlacementPolicy.CompareTo(result.PlacementPolicy));
+ Assert.Equal(factory.Version.ToString(), result.Version!.ToString());
+ }
+
+ // [Fact]
+ // public async void DeleteObjectAsyncTest()
+ // {
+ // }
+}
diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/ClientTestLive.cs
new file mode 100644
index 00000000..70dda660
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ClientTestLive.cs
@@ -0,0 +1,229 @@
+using FrostFS.SDK.ClientV2;
+using FrostFS.SDK.ClientV2.Interfaces;
+using FrostFS.SDK.ModelsV2;
+using FrostFS.SDK.ModelsV2.Enums;
+using FrostFS.SDK.ModelsV2.Netmap;
+using Grpc.Core;
+using Grpc.Net.Client;
+using Microsoft.Extensions.Options;
+
+namespace FrostFS.SDK.Tests;
+
+public class ClientTestLive
+{
+ private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
+ private readonly string url = "http://172.29.238.97:8080";
+
+ [Fact]
+ public async void NetworkMapTest()
+ {
+ var channelOptions = new GrpcChannelOptions
+ {
+ Credentials = ChannelCredentials.Insecure,
+ HttpHandler = new HttpClientHandler()
+ };
+
+ using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions);
+
+ var result = await fsClient.GetNetmapSnapshotAsync();
+
+ Assert.True(result.Epoch > 0);
+ Assert.Single(result.NodeInfoCollection);
+
+ var item = result.NodeInfoCollection[0];
+ Assert.Equal(2, item.Version.Major);
+ Assert.Equal(13, item.Version.Minor);
+ Assert.Equal(NodeState.Online, item.State);
+ Assert.True(item.PublicKey.Length > 0);
+ Assert.Single(item.Addresses);
+ Assert.Equal(9, item.Attributes.Count);
+ }
+
+ [Fact]
+ public async void NodeInfoTest()
+ {
+ var channelOptions = new GrpcChannelOptions
+ {
+ Credentials = ChannelCredentials.Insecure,
+ HttpHandler = new HttpClientHandler()
+ };
+
+ using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions);
+
+ var result = await fsClient.GetNodeInfoAsync();
+
+ Assert.Equal(2, result.Version.Major);
+ Assert.Equal(13, result.Version.Minor);
+ Assert.Equal(NodeState.Online, result.State);
+ Assert.True(result.PublicKey.Length > 0);
+ Assert.Single(result.Addresses);
+ Assert.Equal(9, result.Attributes.Count);
+ }
+
+ [Fact]
+ public async void SimpleScenarioTest()
+ {
+ var channelOptions = new GrpcChannelOptions
+ {
+ Credentials = ChannelCredentials.Insecure,
+ HttpHandler = new HttpClientHandler()
+ };
+
+ using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions);
+
+ await Cleanup(fsClient);
+
+ var containerId = await fsClient.CreateContainerAsync(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))));
+
+ var context = new Context { Timeout = TimeSpan.FromSeconds(10) };
+
+ var container = await GetContainer(fsClient, containerId, context);
+
+ Assert.NotNull(container);
+
+ var param = new PutObjectParameters
+ {
+ Header = new ObjectHeader(
+ containerId: containerId,
+ type: ObjectType.Regular,
+ new ObjectAttribute("fileName", "test")),
+ Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
+ ClientCut = false
+ };
+
+ var objectId = await fsClient.PutObjectAsync(param);
+
+ var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+
+ bool hasObject = false;
+ await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter]))
+ {
+ hasObject = true;
+
+ var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId);
+ Assert.Equal(10u, objHeader.PayloadLength);
+ Assert.Single(objHeader.Attributes);
+ Assert.Equal("fileName", objHeader.Attributes.First().Key);
+ Assert.Equal("test", objHeader.Attributes.First().Value);
+ }
+
+ Assert.True(hasObject);
+
+ var @object = await fsClient.GetObjectAsync(containerId, objectId!);
+
+ Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload);
+
+ await Cleanup(fsClient);
+
+ await Task.Delay(2000);
+
+ await foreach (var _ in fsClient.ListContainersAsync())
+ {
+ Assert.Fail("Containers exist");
+ }
+ }
+
+ [Fact]
+ public async void ClientCutScenarioTest()
+ {
+ var channelOptions = new GrpcChannelOptions
+ {
+ Credentials = ChannelCredentials.Insecure,
+ HttpHandler = new HttpClientHandler()
+ };
+
+ using var fsClient = Client.GetInstance(GetOptions(this.key, this.url), channelOptions);
+
+ await Cleanup(fsClient);
+
+ var containerId = await fsClient.CreateContainerAsync(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))));
+
+ var context = new Context { Timeout = TimeSpan.FromSeconds(10) };
+ var container = await GetContainer(fsClient, containerId, context);
+
+ Assert.NotNull(container);
+
+ var param = new PutObjectParameters
+ {
+ Header = new ObjectHeader(
+ containerId: containerId,
+ type: ObjectType.Regular,
+ new ObjectAttribute("fileName", "test")),
+ Payload = new MemoryStream([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
+ ClientCut = true
+ };
+
+ var objectId = await fsClient.PutObjectAsync(param);
+
+ var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+
+ bool hasObject = false;
+ await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter]))
+ {
+ hasObject = true;
+
+ var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId);
+ Assert.Equal(10u, objHeader.PayloadLength);
+ Assert.Single(objHeader.Attributes);
+ Assert.Equal("fileName", objHeader.Attributes.First().Key);
+ Assert.Equal("test", objHeader.Attributes.First().Value);
+ }
+
+ Assert.True(hasObject);
+
+ var @object = await fsClient.GetObjectAsync(containerId, objectId!);
+
+ Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload);
+
+ await Cleanup(fsClient);
+
+ await Task.Delay(2000);
+
+ await foreach (var _ in fsClient.ListContainersAsync())
+ {
+ Assert.Fail("Containers exist");
+ }
+ }
+
+ private static IOptions GetOptions(string key, string url)
+ {
+ var settings = new ClientSettings
+ {
+ Key = key,
+ Host = url
+ };
+
+ return Options.Create(settings);
+ }
+
+ static async Task Cleanup(IFrostFSClient fsClient)
+ {
+ await foreach (var cid in fsClient.ListContainersAsync())
+ {
+ await fsClient.DeleteContainerAsync(cid);
+ }
+ }
+
+ static async Task GetContainer(IFrostFSClient fsClient, ContainerId id, Context ctx)
+ {
+ while (true)
+ {
+ try
+ {
+ await Task.Delay(100);
+ return await fsClient.GetContainerAsync(id, ctx);
+ }
+ catch (ApplicationException)
+ {
+ if (DateTime.UtcNow >= ctx.Deadline)
+ throw new TimeoutException();
+ }
+ catch (Grpc.Core.RpcException)
+ {
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs
new file mode 100644
index 00000000..bbf14760
--- /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 00000000..a752d104
--- /dev/null
+++ b/src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs
@@ -0,0 +1,69 @@
+using FrostFS.Container;
+using FrostFS.Session;
+using Google.Protobuf;
+using Grpc.Core;
+using Moq;
+
+namespace FrostFS.SDK.Tests;
+
+public class DeleteContainerMock(string key) : ContainerServiceBase(key)
+{
+ public override Mock GetMock()
+ {
+ var mock = new Mock();
+
+ var v = mock.Setup(x => x.DeleteAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()));
+
+
+ v.Returns((Object.DeleteRequest r, Metadata m, DateTime? dt, CancellationToken ct) =>
+ {
+ var deleteResponse = new Object.DeleteResponse
+ {
+ Body = new Object.DeleteResponse.Types.Body
+ {
+ Tombstone = new Refs.Address
+ {
+ ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ }
+ },
+ MetaHeader = new ResponseMetaHeader()
+ };
+
+ var metadata = new Metadata();
+
+ return new AsyncUnaryCall(
+ Task.FromResult(deleteResponse),
+ Task.FromResult(metadata),
+ () => new Grpc.Core.Status(StatusCode.OK, string.Empty),
+ () => metadata,
+ () => { });
+ });
+
+ return mock;
+ }
+}
+
+
+ // objectServiceClientMock.Setup(x => x.Head(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ // .Returns((Object.DeleteRequest r, Metadata m, DateTime dt, CancellationToken ct) =>
+ // {
+ // return new
+ // {
+ // Body = new Object.DeleteResponse.Types.Body
+ // {
+ // Tombstone = new Refs.Address
+ // {
+ // ContainerId = new Refs.ContainerID { Value = ByteString.CopyFrom([1, 2, 3]) },
+ // ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom([4, 5, 6]) }
+ // }
+ // }
+ // };
+ // });
+
+
+
\ No newline at end of file
diff --git a/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs
new file mode 100644
index 00000000..61feb5e6
--- /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 00000000..3243bdf3
--- /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 00000000..0cc3889e
--- /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 00000000..96296f8d
--- /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 00000000..8309fde3
--- /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;
+ }
+}
From f5d1899dd24696c7c3dc3825c2f42e3b375e9291 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Wed, 26 Jun 2024 15:15:58 +0300
Subject: [PATCH 04/65] [#13] Change GetObject result to stream
Signed-off-by: Pavel Gross
---
.../Services/ObjectReader.cs | 15 +++++--
.../Services/ObjectServiceProvider.cs | 43 ++++++++++++-------
src/FrostFS.SDK.ModelsV2/Object/Object.cs | 2 +
.../Object/ObjectAttribute.cs | 12 ++----
src/FrostFS.SDK.Tests/ClientTestLive.cs | 37 +++++++++++++---
5 files changed, 75 insertions(+), 34 deletions(-)
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
index 73e982af..19c65d8d 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
@@ -4,14 +4,17 @@ using System.Threading.Tasks;
using Grpc.Core;
using FrostFS.Object;
+using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2;
-internal class ObjectReader(AsyncServerStreamingCall call) : IDisposable
+public class ObjectReader(AsyncServerStreamingCall call) : IObjectReader
{
+ private bool disposed = false;
+
public AsyncServerStreamingCall Call { get; private set; } = call;
- public async Task ReadHeader()
+ internal async Task ReadHeader()
{
if (!await Call.ResponseStream.MoveNext())
throw new InvalidOperationException("unexpected end of stream");
@@ -45,6 +48,12 @@ internal class ObjectReader(AsyncServerStreamingCall call) : IDispo
public void Dispose()
{
- Call.Dispose();
+ if (!disposed)
+ {
+ Call.Dispose();
+ GC.SuppressFinalize(this);
+
+ disposed = true;
+ }
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index 8619cddc..bf35ad75 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -80,7 +80,7 @@ internal class ObjectServiceProvider : ContextAccessor
var obj = await GetObject(request, ctx);
- return obj.ToModel();
+ return obj;
}
internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx)
@@ -311,27 +311,38 @@ internal class ObjectServiceProvider : ContextAccessor
}
// TODO: add implementation with stream writer!
- private async Task GetObject(GetRequest request, Context ctx)
+ private async Task GetObject(GetRequest request, Context ctx)
{
- using var stream = GetObjectInit(request, ctx);
+ var reader = GetObjectInit(request, ctx);
- var obj = await stream.ReadHeader();
- var payload = new byte[obj.Header.PayloadLength];
- var offset = 0L;
- var chunk = await stream.ReadChunk();
+ var obj = await reader.ReadHeader();
- while (chunk is not null && (ulong)offset < obj.Header.PayloadLength)
- {
- var length = Math.Min((long)obj.Header.PayloadLength - offset, chunk.Length);
+ var @object = obj.ToModel();
- Array.Copy(chunk, 0, payload, offset, length);
- offset += chunk.Length;
- chunk = await stream.ReadChunk();
- }
+ @object.ObjectReader = reader;
- obj.Payload = ByteString.CopyFrom(payload);
+ return @object;
+
+ // obj.
- return obj;
+ // return obj.ToModel();
+
+ // 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, Context ctx)
diff --git a/src/FrostFS.SDK.ModelsV2/Object/Object.cs b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
index 51c46308..e1296012 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/Object.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/Object.cs
@@ -27,6 +27,8 @@ public class Object
}
public byte[] Payload { get; set; }
+
+ public IObjectReader? ObjectReader { get; set; }
public Signature? Signature { get; set; }
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
index b114f2b6..92d38b21 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectAttribute.cs
@@ -1,13 +1,7 @@
namespace FrostFS.SDK.ModelsV2;
-public class ObjectAttribute
+public class ObjectAttribute(string key, string value)
{
- public string Key { get; set; }
- public string Value { get; set; }
-
- public ObjectAttribute(string key, string value)
- {
- Key = key;
- Value = value;
- }
+ public string Key { get; set; } = key;
+ public string Value { get; set; } = value;
}
diff --git a/src/FrostFS.SDK.Tests/ClientTestLive.cs b/src/FrostFS.SDK.Tests/ClientTestLive.cs
index 70dda660..f596472f 100644
--- a/src/FrostFS.SDK.Tests/ClientTestLive.cs
+++ b/src/FrostFS.SDK.Tests/ClientTestLive.cs
@@ -1,3 +1,4 @@
+using System.Security.Cryptography;
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ModelsV2;
@@ -82,13 +83,17 @@ public class ClientTestLive
Assert.NotNull(container);
+ Random rnd = new();
+ var bytes = new byte[6*1024*1024 + 100];
+ rnd.NextBytes(bytes);
+
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]),
+ Payload = new MemoryStream(bytes),
ClientCut = false
};
@@ -102,7 +107,7 @@ public class ClientTestLive
hasObject = true;
var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId);
- Assert.Equal(10u, objHeader.PayloadLength);
+ Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength);
Assert.Single(objHeader.Attributes);
Assert.Equal("fileName", objHeader.Attributes.First().Key);
Assert.Equal("test", objHeader.Attributes.First().Value);
@@ -112,7 +117,16 @@ public class ClientTestLive
var @object = await fsClient.GetObjectAsync(containerId, objectId!);
- Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload);
+ var downloadedBytes = new byte[@object.Header.PayloadLength];
+ MemoryStream ms = new(downloadedBytes);
+
+ byte[]? chunk = null;
+ while ((chunk = await @object.ObjectReader.ReadChunk()) != null)
+ {
+ ms.Write(chunk);
+ }
+
+ Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
await Cleanup(fsClient);
@@ -145,13 +159,17 @@ public class ClientTestLive
Assert.NotNull(container);
+ Random rnd = new();
+ var bytes = new byte[6*1024*1024 + 100];
+ rnd.NextBytes(bytes);
+
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]),
+ Payload = new MemoryStream(bytes),
ClientCut = true
};
@@ -165,7 +183,7 @@ public class ClientTestLive
hasObject = true;
var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId);
- Assert.Equal(10u, objHeader.PayloadLength);
+ Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength);
Assert.Single(objHeader.Attributes);
Assert.Equal("fileName", objHeader.Attributes.First().Key);
Assert.Equal("test", objHeader.Attributes.First().Value);
@@ -175,7 +193,14 @@ public class ClientTestLive
var @object = await fsClient.GetObjectAsync(containerId, objectId!);
- Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], @object.Payload);
+ var downloadedBytes = new byte[@object.Header.PayloadLength];
+ MemoryStream ms = new(downloadedBytes);
+
+ byte[]? chunk = null;
+ while ((chunk = await @object.ObjectReader.ReadChunk()) != null)
+ {
+ ms.Write(chunk);
+ }
await Cleanup(fsClient);
From 17492ee871b5479d2a1946218f6102b7c5ff28f6 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Wed, 26 Jun 2024 15:24:15 +0300
Subject: [PATCH 05/65] [#13] Add cancellation token to GetChank method
Signed-off-by: Pavel Gross
---
src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs | 7 ++++---
src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs | 3 ++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
index 19c65d8d..647a7ad0 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
@@ -5,13 +5,14 @@ using Grpc.Core;
using FrostFS.Object;
using FrostFS.SDK.ModelsV2;
+using System.Threading;
namespace FrostFS.SDK.ClientV2;
public class ObjectReader(AsyncServerStreamingCall call) : IObjectReader
{
private bool disposed = false;
-
+
public AsyncServerStreamingCall Call { get; private set; } = call;
internal async Task ReadHeader()
@@ -32,9 +33,9 @@ public class ObjectReader(AsyncServerStreamingCall call) : IObjectR
};
}
- public async Task ReadChunk()
+ public async Task ReadChunk(CancellationToken cancellationToken = default)
{
- if (!await Call.ResponseStream.MoveNext())
+ if (!await Call.ResponseStream.MoveNext(cancellationToken))
return null;
var response = Call.ResponseStream.Current;
diff --git a/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs
index 3822dcec..4c5ce3da 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/IObjectReader.cs
@@ -1,9 +1,10 @@
using System;
+using System.Threading;
using System.Threading.Tasks;
namespace FrostFS.SDK.ModelsV2;
public interface IObjectReader : IDisposable
{
- Task ReadChunk();
+ Task ReadChunk(CancellationToken cancellationToken = default);
}
From 605463ec24f98d1245717e12e5257bcdc129977b Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Thu, 27 Jun 2024 12:38:14 +0300
Subject: [PATCH 06/65] [#13] Drop comments
Signed-off-by: Pavel Gross
---
.../Services/ObjectServiceProvider.cs | 24 +------------------
1 file changed, 1 insertion(+), 23 deletions(-)
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index bf35ad75..01999a52 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -310,7 +310,6 @@ internal class ObjectServiceProvider : ContextAccessor
return ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray());
}
- // TODO: add implementation with stream writer!
private async Task GetObject(GetRequest request, Context ctx)
{
var reader = GetObjectInit(request, ctx);
@@ -321,28 +320,7 @@ internal class ObjectServiceProvider : ContextAccessor
@object.ObjectReader = reader;
- return @object;
-
- // obj.
-
- // return obj.ToModel();
-
- // 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;
+ return @object;
}
private ObjectReader GetObjectInit(GetRequest initRequest, Context ctx)
From ae67b12313b0e8b5ee4ee4aa2879fc48c2aaf235 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Mon, 1 Jul 2024 11:56:47 +0300
Subject: [PATCH 07/65] [#14] Add interceptors
Signed-off-by: Pavel Gross
---
src/FrostFS.SDK.ClientV2/Client.cs | 196 +++++++++++++-----
.../Interceptors/MetricsInterceptor.cs | 73 +++++++
.../Interfaces/IFrostFSClient.cs | 29 ++-
.../Mappers/Session/Session.cs | 6 +-
.../Services/ContainerServiceProvider.cs | 4 -
.../Services/ObjectServiceProvider.cs | 160 +++-----------
.../Services/ObjectTools.cs | 104 ++++++++++
.../Tools/ClientEnvironment.cs | 14 +-
src/FrostFS.SDK.ModelsV2/CallStatistics.cs | 7 +
src/FrostFS.SDK.ModelsV2/Context.cs | 13 ++
.../FrostFS.SDK.ModelsV2.csproj | 4 +
.../FrostFS.SDK.ProtosV2.csproj | 2 +-
src/FrostFS.SDK.Tests/ClientTestLive.cs | 108 ++++++----
.../ContainerServiceBase.cs | 28 ---
.../DeleteContainerMock.cs | 69 ------
.../ContainerServiceMocks/GetContainerMock.cs | 163 ---------------
.../{ClientTest.cs => ContainerTest.cs} | 58 ++++--
.../ContainerServiceBase.cs | 78 +++++++
.../DeleteContainerMock.cs | 54 +++++
.../GetContainerMock copy.cs | 13 ++
.../ContainerServiceMocks/GetContainerMock.cs | 58 ++++++
.../ContainerServiceMocks/PutContainerMock.cs | 2 +-
.../{ => Mocks}/NetmapMock.cs | 2 +-
src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs | 101 +++++++++
src/FrostFS.SDK.Tests/Mocks/SessionMock.cs | 57 +++++
src/FrostFS.SDK.Tests/ObjectMock.cs | 14 --
src/FrostFS.SDK.Tests/ObjectTest.cs | 66 ++++++
src/FrostFS.SDK.Tests/SessionMock.cs | 14 --
28 files changed, 943 insertions(+), 554 deletions(-)
create mode 100644 src/FrostFS.SDK.ClientV2/Interceptors/MetricsInterceptor.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs
create mode 100644 src/FrostFS.SDK.ModelsV2/CallStatistics.cs
delete mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/ContainerServiceBase.cs
delete mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/DeleteContainerMock.cs
delete mode 100644 src/FrostFS.SDK.Tests/ContainerServiceMocks/GetContainerMock.cs
rename src/FrostFS.SDK.Tests/{ClientTest.cs => ContainerTest.cs} (57%)
create mode 100644 src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs
create mode 100644 src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/DeleteContainerMock.cs
create mode 100644 src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock copy.cs
create mode 100644 src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs
rename src/FrostFS.SDK.Tests/{ => Mocks}/ContainerServiceMocks/PutContainerMock.cs (97%)
rename src/FrostFS.SDK.Tests/{ => Mocks}/NetmapMock.cs (78%)
create mode 100644 src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs
create mode 100644 src/FrostFS.SDK.Tests/Mocks/SessionMock.cs
delete mode 100644 src/FrostFS.SDK.Tests/ObjectMock.cs
create mode 100644 src/FrostFS.SDK.Tests/ObjectTest.cs
delete mode 100644 src/FrostFS.SDK.Tests/SessionMock.cs
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 2b87c46e..ca1acea0 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -7,11 +7,13 @@ using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.Session;
using Grpc.Core;
+using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Security.Cryptography;
using System.Threading.Tasks;
using Version = FrostFS.SDK.ModelsV2.Version;
@@ -20,10 +22,15 @@ namespace FrostFS.SDK.ClientV2;
public class Client : IFrostFSClient
{
private bool isDisposed;
+
+ internal ContainerService.ContainerServiceClient? ContainerServiceClient { get; set; }
+ internal NetmapService.NetmapServiceClient? NetmapServiceClient { get; set; }
+ internal SessionService.SessionServiceClient? SessionServiceClient { get; set; }
+ internal ObjectService.ObjectServiceClient? ObjectServiceClient { get; set; }
internal ClientEnvironment ClientCtx { get; set; }
- public static IFrostFSClient GetInstance(IOptions