From 7b9c19f37cdcb9670fea27f0fb0cb5fc2feba115 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Thu, 18 Jul 2024 15:08:53 +0300
Subject: [PATCH] [#17] Client: Add extra parameter
API methods' parameters types with optional session, polling settings, xHeaders etc. and corresponding handlers have been added
Signed-off-by: Pavel Gross
---
src/FrostFS.SDK.ClientV2/Client.cs | 136 +++----
.../Exceptions/ResponseException.cs | 12 +
.../Interfaces/IFrostFSClient.cs | 32 +-
.../Mappers/Object/Object.cs | 12 +-
.../Session/{Session.cs => SessionMapper.cs} | 8 +-
.../Parameters/IContext.cs | 11 +
.../Parameters/ISessionToken.cs | 14 +
.../Parameters/PrmCreateContainer.cs | 22 ++
.../Parameters/PrmCreateSession.cs | 16 +
.../Parameters/PrmDeleteContainer.cs | 23 ++
.../Parameters/PrmDeleteObject.cs | 21 ++
.../Parameters/PrmGetContainer.cs | 17 +
.../Parameters/PrmGetNetmapSnapshot.cs | 14 +
.../Parameters/PrmGetNetworkSettings.cs | 14 +
.../Parameters/PrmGetNodeInfo.cs | 14 +
.../Parameters/PrmGetObject.cs | 21 ++
.../Parameters/PrmGetObjectHead.cs | 21 ++
.../Parameters/PrmListContainer.cs | 16 +
.../Parameters/PrmPutObject.cs | 44 +++
.../Parameters/PrmPutSingleObject.cs | 19 +
.../Parameters/PrmSearchObject.cs | 26 ++
.../Parameters/PrmWait.cs | 23 ++
.../Services/ContainerServiceProvider.cs | 120 ++++--
.../Services/NetmapServiceProvider.cs | 24 +-
.../Services/ObjectServiceProvider.cs | 190 ++++++----
.../Services/SessionServiceProvider.cs | 24 +-
src/FrostFS.SDK.ClientV2/Tools/Object.cs | 9 +-
.../{Services => Tools}/ObjectReader.cs | 0
.../{Services => Tools}/ObjectStreamer.cs | 0
.../{Services => Tools}/ObjectTools.cs | 2 +-
.../Tools/RequestConstructor.cs | 45 ++-
.../{Services => Tools}/SearchReader.cs | 0
src/FrostFS.SDK.ClientV2/Tools/Verifier.cs | 2 +-
src/FrostFS.SDK.ModelsV2/Context.cs | 2 -
.../FrostFS.SDK.ModelsV2.csproj | 5 +
.../Object/FrostFsObject.cs | 50 ++-
.../PutObjectParameters.cs | 13 -
src/FrostFS.SDK.ModelsV2/SessionToken.cs | 9 +-
src/FrostFS.SDK.Tests/ContainerTest.cs | 23 +-
.../ContainerServiceMocks/GetContainerMock.cs | 29 +-
src/FrostFS.SDK.Tests/ObjectTest.cs | 11 +-
src/FrostFS.SDK.Tests/SmokeTests.cs | 346 +++++++++++++-----
42 files changed, 1054 insertions(+), 386 deletions(-)
create mode 100644 src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs
rename src/FrostFS.SDK.ClientV2/Mappers/Session/{Session.cs => SessionMapper.cs} (62%)
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/IContext.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/ISessionToken.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmCreateContainer.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmCreateSession.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteContainer.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteObject.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetContainer.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetmapSnapshot.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetworkSettings.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetNodeInfo.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetObject.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmGetObjectHead.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmListContainer.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmPutObject.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmPutSingleObject.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmSearchObject.cs
create mode 100644 src/FrostFS.SDK.ClientV2/Parameters/PrmWait.cs
rename src/FrostFS.SDK.ClientV2/{Services => Tools}/ObjectReader.cs (100%)
rename src/FrostFS.SDK.ClientV2/{Services => Tools}/ObjectStreamer.cs (100%)
rename src/FrostFS.SDK.ClientV2/{Services => Tools}/ObjectTools.cs (98%)
rename src/FrostFS.SDK.ClientV2/{Services => Tools}/SearchReader.cs (100%)
diff --git a/src/FrostFS.SDK.ClientV2/Client.cs b/src/FrostFS.SDK.ClientV2/Client.cs
index 6e9cbd0..6d09283 100644
--- a/src/FrostFS.SDK.ClientV2/Client.cs
+++ b/src/FrostFS.SDK.ClientV2/Client.cs
@@ -2,6 +2,7 @@
using FrostFS.Netmap;
using FrostFS.Object;
using FrostFS.SDK.ClientV2.Interfaces;
+using FrostFS.SDK.ClientV2.Parameters;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Netmap;
@@ -96,7 +97,8 @@ public class Client : IFrostFSClient
channel: channel,
version: new Version(2, 13));
- CheckFrostFsVersionSupport();
+ // TODO: define timeout logic
+ CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20)});
}
public void Dispose()
@@ -115,105 +117,106 @@ public class Client : IFrostFSClient
}
#region ContainerImplementation
- public Task GetContainerAsync(ContainerId containerId, Context? ctx = null)
+ public Task GetContainerAsync(PrmGetContainer args)
{
- var service = GetContainerService(ref ctx);
- return service.GetContainerAsync(containerId, ctx!);
+ var service = GetContainerService(args);
+ return service.GetContainerAsync(args);
}
- public IAsyncEnumerable ListContainersAsync(Context? ctx = default)
+ public IAsyncEnumerable ListContainersAsync(PrmListContainer? args = null)
{
- var service = GetContainerService(ref ctx);
- return service.ListContainersAsync(ctx!);
+ args = args ?? new PrmListContainer();
+ var service = GetContainerService(args);
+ return service.ListContainersAsync(args);
}
- public Task CreateContainerAsync(ModelsV2.Container container, Context? ctx = null)
+ public Task CreateContainerAsync(PrmCreateContainer args)
{
- var service = GetContainerService(ref ctx);
- return service.CreateContainerAsync(container, ctx!);
+ var service = GetContainerService(args);
+ return service.CreateContainerAsync(args);
}
- public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default)
+ public Task DeleteContainerAsync(PrmDeleteContainer args)
{
- var service = GetContainerService(ref ctx);
- return service.DeleteContainerAsync(containerId, ctx!);
+ var service = GetContainerService(args);
+ return service.DeleteContainerAsync(args);
}
#endregion
#region NetworkImplementation
- public Task GetNetmapSnapshotAsync(Context? ctx = default)
+ public Task GetNetmapSnapshotAsync(PrmGetNetmapSnapshot? args)
{
- var service = GetNetmapService(ref ctx);
- return service.GetNetmapSnapshotAsync(ctx!);
+ args ??= new PrmGetNetmapSnapshot();
+ var service = GetNetmapService(args);
+ return service.GetNetmapSnapshotAsync(args);
}
- public Task GetNodeInfoAsync(Context? ctx = default)
+ public Task GetNodeInfoAsync(PrmGetNodeInfo? args)
{
- var service = GetNetmapService(ref ctx);
- return service.GetLocalNodeInfoAsync(ctx!);
+ args ??= new PrmGetNodeInfo();
+ var service = GetNetmapService(args);
+ return service.GetLocalNodeInfoAsync(args);
}
- public Task GetNetworkSettingsAsync(Context? ctx = default)
- {
- var service = GetNetmapService(ref ctx);
- return service.GetNetworkSettingsAsync(ctx!);
+ public Task GetNetworkSettingsAsync(PrmGetNetworkSettings? args)
+ {
+ args ??= new PrmGetNetworkSettings();
+ var service = GetNetmapService(args);
+ return service.GetNetworkSettingsAsync(args.Context!);
}
#endregion
#region ObjectImplementation
- public Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ public Task GetObjectHeadAsync(PrmGetObjectHead args)
{
- var service = GetObjectService(ref ctx);
- return service.GetObjectHeadAsync(containerId, objectId, ctx!);
+ var service = GetObjectService(args);
+ return service.GetObjectHeadAsync(args);
}
- public Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ public Task GetObjectAsync(PrmGetObject args)
{
- var service = GetObjectService(ref ctx);
- return service.GetObjectAsync(containerId, objectId, ctx!);
+ var service = GetObjectService(args);
+ return service.GetObjectAsync(args);
}
- public Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
+ public Task PutObjectAsync(PrmPutObject args)
{
- var service = GetObjectService(ref ctx);
- return service.PutObjectAsync(putObjectParameters, ctx!);
+ var service = GetObjectService(args);
+ return service.PutObjectAsync(args);
}
- public Task PutSingleObjectAsync(FrostFsObject obj, Context? ctx = default)
+ public Task PutSingleObjectAsync(PrmPutSingleObject args)
{
- var service = GetObjectService(ref ctx);
- return service.PutSingleObjectAsync(obj, ctx!);
+ var service = GetObjectService(args);
+ return service.PutSingleObjectAsync(args);
}
- public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
+ public Task DeleteObjectAsync(PrmDeleteObject args)
{
- var service = GetObjectService(ref ctx);
- return service.DeleteObjectAsync(containerId, objectId, ctx!);
+ var service = GetObjectService(args);
+ return service.DeleteObjectAsync(args);
}
- public IAsyncEnumerable SearchObjectsAsync(
- ContainerId containerId,
- IEnumerable filters,
- Context? ctx = default)
+ public IAsyncEnumerable SearchObjectsAsync(PrmSearchObject args)
{
- var service = GetObjectService(ref ctx);
- return service.SearchObjectsAsync(containerId, filters, ctx!);
+ var service = GetObjectService(args);
+ return service.SearchObjectsAsync(args);
}
#endregion
#region SessionImplementation
- public async Task CreateSessionAsync(ulong expiration, Context? ctx = null)
+ public async Task CreateSessionAsync(PrmCreateSession args)
{
- var session = await CreateSessionInternalAsync(expiration, ctx);
+ var session = await CreateSessionInternalAsync(args);
var token = session.Serialize();
- return new ModelsV2.SessionToken([], token);
+ return new ModelsV2.SessionToken(token);
}
- public Task CreateSessionInternalAsync(ulong expiration, Context? ctx = null)
+ internal Task CreateSessionInternalAsync(PrmCreateSession args)
{
- var service = GetSessionService(ref ctx);
- return service.CreateSessionAsync(expiration, ctx!);
+ var service = GetSessionService(args);
+ return service.CreateSessionAsync(args);
}
#endregion
@@ -226,8 +229,9 @@ public class Client : IFrostFSClient
private async void CheckFrostFsVersionSupport(Context? ctx = default)
{
- var service = GetNetmapService(ref ctx);
- var localNodeInfo = await service.GetLocalNodeInfoAsync(ctx!);
+ var args = new PrmGetNodeInfo(ctx);
+ var service = GetNetmapService(args);
+ var localNodeInfo = await service.GetLocalNodeInfoAsync(args);
if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
{
@@ -237,24 +241,24 @@ public class Client : IFrostFSClient
}
}
- private CallInvoker? SetupEnvironment(ref Context? ctx)
+ private CallInvoker? SetupEnvironment(IContext ctx)
{
if (isDisposed)
throw new Exception("Client is disposed.");
- ctx ??= new Context();
+ ctx.Context ??= new Context();
CallInvoker? callInvoker = null;
- if (ctx.Interceptors != null && ctx.Interceptors.Count > 0)
+ if (ctx.Context.Interceptors != null && ctx.Context.Interceptors.Count > 0)
{
- foreach (var interceptor in ctx.Interceptors)
+ foreach (var interceptor in ctx.Context.Interceptors)
{
callInvoker = AddInvoker(callInvoker, interceptor);
}
}
- if (ctx.Callback != null)
- callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Callback));
+ if (ctx.Context.Callback != null)
+ callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ctx.Context.Callback));
return callInvoker;
@@ -269,9 +273,9 @@ public class Client : IFrostFSClient
}
}
- private NetmapServiceProvider GetNetmapService(ref Context? ctx)
+ private NetmapServiceProvider GetNetmapService(IContext ctx)
{
- var callInvoker = SetupEnvironment(ref ctx);
+ var callInvoker = SetupEnvironment(ctx);
var client = NetmapServiceClient ?? (callInvoker != null
? new NetmapService.NetmapServiceClient(callInvoker)
: new NetmapService.NetmapServiceClient(ClientCtx.Channel));
@@ -279,9 +283,9 @@ public class Client : IFrostFSClient
return new NetmapServiceProvider(client, ClientCtx);
}
- private SessionServiceProvider GetSessionService(ref Context? ctx)
+ private SessionServiceProvider GetSessionService(IContext ctx)
{
- var callInvoker = SetupEnvironment(ref ctx);
+ var callInvoker = SetupEnvironment(ctx);
var client = SessionServiceClient ?? (callInvoker != null
? new SessionService.SessionServiceClient(callInvoker)
: new SessionService.SessionServiceClient(ClientCtx.Channel));
@@ -289,9 +293,9 @@ public class Client : IFrostFSClient
return new SessionServiceProvider(client, ClientCtx);
}
- private ContainerServiceProvider GetContainerService(ref Context? ctx)
+ private ContainerServiceProvider GetContainerService(IContext ctx)
{
- var callInvoker = SetupEnvironment(ref ctx);
+ var callInvoker = SetupEnvironment(ctx);
var client = ContainerServiceClient ?? (callInvoker != null
? new ContainerService.ContainerServiceClient(callInvoker)
: new ContainerService.ContainerServiceClient(ClientCtx.Channel));
@@ -299,9 +303,9 @@ public class Client : IFrostFSClient
return new ContainerServiceProvider(client, ClientCtx);
}
- private ObjectServiceProvider GetObjectService(ref Context? ctx)
+ private ObjectServiceProvider GetObjectService(IContext ctx)
{
- var callInvoker = SetupEnvironment(ref ctx);
+ var callInvoker = SetupEnvironment(ctx);
var client = ObjectServiceClient ?? (callInvoker != null
? new ObjectService.ObjectServiceClient(callInvoker)
: new ObjectService.ObjectServiceClient(ClientCtx.Channel));
diff --git a/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs b/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs
new file mode 100644
index 0000000..7772e1e
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Exceptions/ResponseException.cs
@@ -0,0 +1,12 @@
+using System;
+using FrostFS.SDK.ModelsV2;
+
+public class ResponseException : Exception
+{
+ public Status Status { get; set; }
+
+ public ResponseException(Status status) : base()
+ {
+ Status = status;
+ }
+}
\ 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 bed4136..a483abc 100644
--- a/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
+++ b/src/FrostFS.SDK.ClientV2/Interfaces/IFrostFSClient.cs
@@ -1,52 +1,50 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-
+using FrostFS.SDK.ClientV2.Parameters;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Netmap;
-
namespace FrostFS.SDK.ClientV2.Interfaces;
public interface IFrostFSClient : IDisposable
{
#region Network
- Task GetNetmapSnapshotAsync(Context? context = default);
+ Task GetNetmapSnapshotAsync(PrmGetNetmapSnapshot? args = null);
- Task GetNodeInfoAsync(Context? context = default);
+ Task GetNodeInfoAsync(PrmGetNodeInfo? args = null);
- Task GetNetworkSettingsAsync(Context? context = default);
+ Task GetNetworkSettingsAsync(PrmGetNetworkSettings? args = null);
#endregion
#region Session
- Task CreateSessionAsync(ulong expiration, Context? context = default);
+ Task CreateSessionAsync(PrmCreateSession args);
#endregion
#region Container
- Task GetContainerAsync(ContainerId containerId, Context? context = default);
+ Task GetContainerAsync(PrmGetContainer args);
- IAsyncEnumerable ListContainersAsync(Context? context = default);
+ IAsyncEnumerable ListContainersAsync(PrmListContainer? args = null);
- Task CreateContainerAsync(ModelsV2.Container container, Context? context = default);
+ Task CreateContainerAsync(PrmCreateContainer args);
- Task DeleteContainerAsync(ContainerId containerId, Context? context = default);
+ Task DeleteContainerAsync(PrmDeleteContainer args);
#endregion
#region Object
- Task GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
+ Task GetObjectHeadAsync(PrmGetObjectHead args);
- Task GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
+ Task GetObjectAsync(PrmGetObject args);
- Task PutObjectAsync(PutObjectParameters putObjectParameters, Context? context = default);
+ Task PutObjectAsync(PrmPutObject putObjectParameters);
- Task PutSingleObjectAsync(FrostFsObject obj, Context? context = default);
+ Task PutSingleObjectAsync(PrmPutSingleObject args);
- Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? context = default);
+ Task DeleteObjectAsync(PrmDeleteObject args);
- IAsyncEnumerable SearchObjectsAsync(ContainerId cid, IEnumerable filters, Context? context = default);
+ IAsyncEnumerable SearchObjectsAsync(PrmSearchObject args);
#endregion
#region Tools
ObjectId CalculateObjectId(ObjectHeader header);
#endregion
}
-
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
index 2804b65..bda31fe 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/Object.cs
@@ -2,13 +2,13 @@ using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
-public static class ObjectMapper
+internal static class ObjectMapper
{
- public static FrostFsObject ToModel(this Object.Object obj)
+ internal static FrostFsObject ToModel(this Object.Object obj)
{
- return new FrostFsObject(
- ObjectId.FromHash(obj.ObjectId.Value.ToByteArray()),
- obj.Header.ToModel(),
- obj.Payload.ToByteArray());
+ return new FrostFsObject(obj.Header.ToModel())
+ {
+ ObjectId = ObjectId.FromHash(obj.ObjectId.Value.ToByteArray())
+ };
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs b/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs
similarity index 62%
rename from src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
rename to src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs
index e565891..97b1c69 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/Session/Session.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Session/SessionMapper.cs
@@ -4,7 +4,7 @@ namespace FrostFS.SDK.ClientV2;
public static class SessionMapper
{
- internal static byte[] Serialize(this Session.SessionToken token)
+ public static byte[] Serialize(this Session.SessionToken token)
{
byte[] bytes = new byte[token.CalculateSize()];
CodedOutputStream stream = new(bytes);
@@ -13,11 +13,9 @@ public static class SessionMapper
return bytes;
}
- internal static Session.SessionToken DeserializeSessionToken(this byte[] bytes)
+ public static Session.SessionToken Deserialize(this Session.SessionToken token, byte[] bytes)
{
- Session.SessionToken token = new();
token.MergeFrom(bytes);
-
return token;
}
-}
\ No newline at end of file
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/IContext.cs b/src/FrostFS.SDK.ClientV2/Parameters/IContext.cs
new file mode 100644
index 0000000..c48dc8a
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/IContext.cs
@@ -0,0 +1,11 @@
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public interface IContext
+{
+ ///
+ /// The method call can be extended with additional behavior like canceling by timeout or user's request,
+ /// callbacks, interceptors.
+ ///
+ /// Additional parameters for calling the method
+ Context? Context { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/ISessionToken.cs b/src/FrostFS.SDK.ClientV2/Parameters/ISessionToken.cs
new file mode 100644
index 0000000..c5ac1da
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/ISessionToken.cs
@@ -0,0 +1,14 @@
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public interface ISessionToken
+{
+ ///
+ /// Object represents token of the FrostFS Object session. A session is opened between any two sides of the
+ /// system, and implements a mechanism for transferring the power of attorney of actions to another network
+ /// member. The session has a limited validity period, and applies to a strictly defined set of operations.
+ ///
+ /// Instance of the session obtained from the server
+ SessionToken? SessionToken { get; set; }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateContainer.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateContainer.cs
new file mode 100644
index 0000000..9209a03
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateContainer.cs
@@ -0,0 +1,22 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmCreateContainer(ModelsV2.Container container) : IContext
+{
+ public ModelsV2.Container Container { get; set; } = container;
+
+ ///
+ /// Since the container becomes available with some delay, it needs to poll the container status
+ ///
+ /// Rules for polling the result
+ public PrmWait? WaitParams { get; set; }
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateSession.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateSession.cs
new file mode 100644
index 0000000..870a481
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmCreateSession.cs
@@ -0,0 +1,16 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmCreateSession(ulong expiration, Context? context = default) : IContext
+{
+ public ulong Expiration { get; set; } = expiration;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = context;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteContainer.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteContainer.cs
new file mode 100644
index 0000000..4451b00
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteContainer.cs
@@ -0,0 +1,23 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmDeleteContainer(ContainerId containerId, Context? ctx = null) : IContext
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+
+ ///
+ /// Since the container is removed with some delay, it needs to poll the container status
+ ///
+ /// Rules for polling the result
+ public PrmWait? WaitParams { get; set; }
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = ctx;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteObject.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteObject.cs
new file mode 100644
index 0000000..8241f64
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmDeleteObject.cs
@@ -0,0 +1,21 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmDeleteObject(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+ public ObjectId ObjectId { get; set; } = objectId;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetContainer.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetContainer.cs
new file mode 100644
index 0000000..59350fe
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetContainer.cs
@@ -0,0 +1,17 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetContainer(ContainerId containerId, Context? ctx = null) : IContext
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = ctx;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetmapSnapshot.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetmapSnapshot.cs
new file mode 100644
index 0000000..e3e28c6
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetmapSnapshot.cs
@@ -0,0 +1,14 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetNetmapSnapshot(Context? context = default) : IContext
+{
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = context;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetworkSettings.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetworkSettings.cs
new file mode 100644
index 0000000..8db583e
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNetworkSettings.cs
@@ -0,0 +1,14 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetNetworkSettings(Context? context = default) : IContext
+{
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = context;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNodeInfo.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNodeInfo.cs
new file mode 100644
index 0000000..78a7697
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetNodeInfo.cs
@@ -0,0 +1,14 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetNodeInfo(Context? context = default) : IContext
+{
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = context;
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObject.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObject.cs
new file mode 100644
index 0000000..5ee1fd9
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObject.cs
@@ -0,0 +1,21 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetObject(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+ public ObjectId ObjectId { get; set; } = objectId;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObjectHead.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObjectHead.cs
new file mode 100644
index 0000000..88287a8
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmGetObjectHead.cs
@@ -0,0 +1,21 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmGetObjectHead(ContainerId containerId, ObjectId objectId) : IContext, ISessionToken
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+ public ObjectId ObjectId { get; set; } = objectId;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmListContainer.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmListContainer.cs
new file mode 100644
index 0000000..3d4c1a2
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmListContainer.cs
@@ -0,0 +1,16 @@
+using System.Collections.Specialized;
+
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmListContainer() : IContext
+{
+ public string SessionToken { get; set; } = string.Empty;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmPutObject.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmPutObject.cs
new file mode 100644
index 0000000..7e4999d
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmPutObject.cs
@@ -0,0 +1,44 @@
+using System.Collections.Specialized;
+using System.IO;
+using FrostFS.SDK.ModelsV2;
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmPutObject : IContext, ISessionToken
+{
+ ///
+ /// Need to provide values like ContainerId and ObjectType to create and object.
+ /// Optional parameters ike Attributes can be provided as well.
+ ///
+ /// Header with required parameters to create an object
+ public ObjectHeader? Header { get; set; }
+
+ ///
+ /// A stream with source data
+ ///
+ public Stream? Payload { get; set; }
+
+ ///
+ /// Object size is limited. In the data exceeds the limit, the object will be splitted.
+ /// If the parameter is true, the client side cut is applied. Otherwise, the data is transferred
+ /// as a stream and will be cut on server side.
+ ///
+ /// Is client cut is applied
+ public bool ClientCut { get; set; }
+
+ ///
+ /// Overrides default size of the buffer for stream transferring.
+ ///
+ /// Size of the buffer
+ public int BufferMaxSize { get; set; }
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmPutSingleObject.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmPutSingleObject.cs
new file mode 100644
index 0000000..95e514f
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmPutSingleObject.cs
@@ -0,0 +1,19 @@
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmPutSingleObject(FrostFsObject frostFsObject, Context? context = null) : IContext, ISessionToken
+{
+ public FrostFsObject FrostFsObject { get; set; } = frostFsObject;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; } = context;
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmSearchObject.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmSearchObject.cs
new file mode 100644
index 0000000..8c0ad8c
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmSearchObject.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using FrostFS.SDK.ModelsV2;
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public sealed class PrmSearchObject(ContainerId containerId, params ObjectFilter[] filters) : IContext, ISessionToken
+{
+ public ContainerId ContainerId { get; set; } = containerId;
+
+ ///
+ /// Defines the search criteria
+ ///
+ /// Collection of filters
+ public IEnumerable Filters { get; set; } = filters;
+
+ ///
+ /// FrostFS request X-Headers
+ ///
+ public NameValueCollection XHeaders { get; set; } = [];
+
+ ///
+ public Context? Context { get; set; }
+
+ ///
+ public SessionToken? SessionToken { get; set; }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmWait.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmWait.cs
new file mode 100644
index 0000000..86e32d7
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmWait.cs
@@ -0,0 +1,23 @@
+using System;
+namespace FrostFS.SDK.ClientV2.Parameters;
+
+public class PrmWait(TimeSpan timeout, TimeSpan pollInterval)
+{
+ private static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(120);
+ private static TimeSpan DefaultPollInterval = TimeSpan.FromSeconds(5);
+
+ public PrmWait(int timeout, int interval) : this(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(interval))
+ {
+ }
+
+ public static PrmWait DefaultParams { get; } = new PrmWait(DefaultTimeout, DefaultPollInterval);
+
+ public TimeSpan Timeout { get; set; } = timeout.Ticks == 0 ? DefaultTimeout : timeout;
+
+ public TimeSpan PollInterval { get; set; } = pollInterval.Ticks == 0 ? DefaultPollInterval : pollInterval;
+
+ public DateTime GetDeadline()
+ {
+ return DateTime.UtcNow.AddTicks(Timeout.Ticks);
+ }
+}
diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
index 6a3036b..dfbd614 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
@@ -6,6 +6,10 @@ using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography;
using System.Collections.Generic;
using FrostFS.SDK.ModelsV2;
+using FrostFS.Refs;
+using System;
+using FrostFS.SDK.ClientV2.Parameters;
+using System.Collections.Specialized;
namespace FrostFS.SDK.ClientV2;
@@ -19,28 +23,21 @@ internal class ContainerServiceProvider : ContextAccessor
containerServiceClient = service;
}
- internal async Task GetContainerAsync(ContainerId cid, Context context)
+ internal async Task GetContainerAsync(PrmGetContainer args)
{
- var request = new GetRequest
- {
- Body = new GetRequest.Types.Body
- {
- ContainerId = cid.ToGrpcMessage()
- },
- };
+ GetRequest request = GetContainerRequest(args.ContainerId.ToGrpcMessage(), args.XHeaders);
- request.AddMetaHeader();
- request.Sign(Context.Key);
-
- var response = await containerServiceClient.GetAsync(request, null, context.Deadline, context.CancellationToken);
+ var response = await containerServiceClient.GetAsync(request, null, args.Context!.Deadline, args.Context.CancellationToken);
Verifier.CheckResponse(response);
- return response.Body.Container.ToModel();
+ return response.Body.Container.ToModel();
}
- internal async IAsyncEnumerable ListContainersAsync(Context ctx)
+ internal async IAsyncEnumerable ListContainersAsync(PrmListContainer args)
{
+ var ctx = args.Context!;
+
var request = new ListRequest
{
Body = new ListRequest.Types.Body
@@ -49,7 +46,7 @@ internal class ContainerServiceProvider : ContextAccessor
}
};
- request.AddMetaHeader();
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
var response = await containerServiceClient.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
@@ -62,9 +59,10 @@ internal class ContainerServiceProvider : ContextAccessor
}
}
- internal async Task CreateContainerAsync(ModelsV2.Container container, Context ctx)
+ internal async Task CreateContainerAsync(PrmCreateContainer args)
{
- var grpcContainer = container.ToGrpcMessage();
+ var ctx = args.Context!;
+ var grpcContainer = args.Container.ToGrpcMessage();
grpcContainer.OwnerId = Context.Owner.ToGrpcMessage();
grpcContainer.Version = Context.Version.ToGrpcMessage();
@@ -73,37 +71,113 @@ internal class ContainerServiceProvider : ContextAccessor
Body = new PutRequest.Types.Body
{
Container = grpcContainer,
- Signature = Context.Key.SignRFC6979(grpcContainer),
+ Signature = Context.Key.SignRFC6979(grpcContainer)
}
};
- request.AddMetaHeader();
+
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
var response = await containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
+
+ await WaitForContainer(WaitExpects.Exists, response.Body.ContainerId, args.WaitParams, ctx);
return new ContainerId(Base58.Encode(response.Body.ContainerId.Value.ToByteArray()));
}
- internal async Task DeleteContainerAsync(ContainerId cid, Context ctx)
+ internal async Task DeleteContainerAsync(PrmDeleteContainer args)
{
+ var ctx = args.Context!;
var request = new DeleteRequest
{
Body = new DeleteRequest.Types.Body
{
- ContainerId = cid.ToGrpcMessage(),
- Signature = Context.Key.SignRFC6979(cid.ToGrpcMessage().Value)
+ ContainerId = args.ContainerId.ToGrpcMessage(),
+ Signature = Context.Key.SignRFC6979(args.ContainerId.ToGrpcMessage().Value)
}
};
- request.AddMetaHeader();
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
var response = await containerServiceClient.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx);
Verifier.CheckResponse(response);
}
+
+ private GetRequest GetContainerRequest(ContainerID id, NameValueCollection? xHeaders)
+ {
+ var request = new GetRequest
+ {
+ Body = new GetRequest.Types.Body
+ {
+ ContainerId = id
+ }
+ };
+
+ request.AddMetaHeader(xHeaders);
+ request.Sign(Context.Key);
+
+ return request;
+ }
+
+ private enum WaitExpects
+ {
+ Exists,
+ Removed
+ }
+
+ private async Task WaitForContainer(WaitExpects expect, ContainerID id, PrmWait? waitParams, Context ctx)
+ {
+ var request = GetContainerRequest(id, null);
+
+ async Task action()
+ {
+ var response = await containerServiceClient.GetAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ Verifier.CheckResponse(response);
+ }
+
+ await WaitFor(action, expect, waitParams);
+ }
+
+ private static async Task WaitFor(
+ Func action,
+ WaitExpects expect,
+ PrmWait? waitParams)
+ {
+ waitParams ??= PrmWait.DefaultParams;
+ var deadLine = waitParams.GetDeadline();
+
+ while (true)
+ {
+ try
+ {
+ await action();
+
+ if (expect == WaitExpects.Exists)
+ return;
+
+ await Task.Delay(waitParams.PollInterval);
+ }
+ catch (ResponseException ex)
+ {
+ if (DateTime.UtcNow >= deadLine)
+ throw new TimeoutException();
+
+ if (ex.Status.Code != ModelsV2.Enums.StatusCode.ContainerNotFound)
+ throw;
+
+ if (expect == WaitExpects.Removed)
+ return;
+
+ await Task.Delay(waitParams.PollInterval);
+ }
+ }
+ }
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
index aabc133..0d0a6d3 100644
--- a/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/NetmapServiceProvider.cs
@@ -7,6 +7,7 @@ using NodeInfo = FrostFS.SDK.ModelsV2.Netmap.NodeInfo;
using FrostFS.SDK.ModelsV2.Netmap;
using static FrostFS.Netmap.NetworkConfig.Types;
+using FrostFS.SDK.ClientV2.Parameters;
namespace FrostFS.SDK.ClientV2;
@@ -39,14 +40,15 @@ internal class NetmapServiceProvider : ContextAccessor
return settings;
}
- internal async Task GetLocalNodeInfoAsync(Context ctx)
+ internal async Task GetLocalNodeInfoAsync(PrmGetNodeInfo args)
{
+ var ctx = args.Context!;
var request = new LocalNodeInfoRequest
{
Body = new LocalNodeInfoRequest.Types.Body { }
};
- request.AddMetaHeader();
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
var response = await netmapServiceClient.LocalNodeInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
@@ -58,12 +60,9 @@ internal class NetmapServiceProvider : ContextAccessor
internal async Task GetNetworkInfoAsync(Context ctx)
{
- var request = new NetworkInfoRequest
- {
- Body = new NetworkInfoRequest.Types.Body { }
- };
+ var request = new NetworkInfoRequest();
- request.AddMetaHeader();
+ request.AddMetaHeader(null);
request.Sign(Context.Key);
var response = await netmapServiceClient.NetworkInfoAsync(request, null, ctx.Deadline, ctx.CancellationToken);
@@ -73,14 +72,13 @@ internal class NetmapServiceProvider : ContextAccessor
return response;
}
- internal async Task GetNetmapSnapshotAsync(Context ctx)
+ internal async Task GetNetmapSnapshotAsync(PrmGetNetmapSnapshot args)
{
- var request = new NetmapSnapshotRequest
- {
- Body = new NetmapSnapshotRequest.Types.Body { }
- };
+ var ctx = args.Context!;
- request.AddMetaHeader();
+ var request = new NetmapSnapshotRequest();
+
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
var response = await netmapServiceClient.NetmapSnapshotAsync(request, null, ctx.Deadline, ctx.CancellationToken);
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index bc38d7b..4d4cece 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -12,6 +12,8 @@ using FrostFS.SDK.Cryptography;
using FrostFS.Session;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ClientV2.Extensions;
+using System.Threading;
+using FrostFS.SDK.ClientV2.Parameters;
namespace FrostFS.SDK.ClientV2;
@@ -19,21 +21,30 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
{
readonly ObjectTools tools = new(ctx);
- internal async Task GetObjectHeadAsync(ContainerId cid, ObjectId oid, Context ctx)
+ internal async Task GetObjectHeadAsync(PrmGetObjectHead args)
{
+ var ctx = args.Context!;
var request = new HeadRequest
{
Body = new HeadRequest.Types.Body
{
Address = new Address
{
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
+ ContainerId = args.ContainerId.ToGrpcMessage(),
+ ObjectId = args.ObjectId.ToGrpcMessage()
}
}
};
- request.AddMetaHeader();
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ request.Body.Address,
+ ObjectSessionContext.Types.Verb.Head,
+ Context.Key);
+
+ request.AddMetaHeader(args.XHeaders, sessionToken);
+
request.Sign(Context.Key);
var response = await client!.HeadAsync(request, null, ctx.Deadline, ctx.CancellationToken);
@@ -43,51 +54,59 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return response.Body.Header.Header.ToModel();
}
- internal async Task GetObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
+ internal async Task GetObjectAsync(PrmGetObject args)
{
- var sessionToken = await GetOrCreateSession(ctx);
-
+ var ctx = args.Context!;
+
var request = new GetRequest
{
Body = new GetRequest.Types.Body
{
Address = new Address
{
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
+ ContainerId = args.ContainerId.ToGrpcMessage(),
+ ObjectId = args.ObjectId.ToGrpcMessage()
}
}
};
- request.AddMetaHeader();
- request.AddObjectSessionToken(
- sessionToken,
- cid.ToGrpcMessage(),
- oid.ToGrpcMessage(),
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ request.Body.Address,
ObjectSessionContext.Types.Verb.Get,
- Context.Key
- );
+ Context.Key);
+
+ request.AddMetaHeader(args.XHeaders, sessionToken);
request.Sign(Context.Key);
return await GetObject(request, ctx);
}
- internal async Task DeleteObjectAsync(ContainerId cid, ObjectId oid, Context ctx)
+ internal async Task DeleteObjectAsync(PrmDeleteObject args)
{
+ var ctx = args.Context!;
var request = new DeleteRequest
{
Body = new DeleteRequest.Types.Body
{
Address = new Address
{
- ContainerId = cid.ToGrpcMessage(),
- ObjectId = oid.ToGrpcMessage()
+ ContainerId = args.ContainerId.ToGrpcMessage(),
+ ObjectId = args.ObjectId.ToGrpcMessage()
}
}
};
- request.AddMetaHeader();
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ request.Body.Address,
+ ObjectSessionContext.Types.Verb.Delete,
+ Context.Key);
+
+ request.AddMetaHeader(args.XHeaders, sessionToken);
request.Sign(Context.Key);
var response = await client.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
@@ -95,24 +114,30 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
Verifier.CheckResponse(response);
}
- internal async IAsyncEnumerable SearchObjectsAsync(
- ContainerId cid,
- IEnumerable filters,
- Context ctx)
+ internal async IAsyncEnumerable SearchObjectsAsync(PrmSearchObject args)
{
+ var ctx = args.Context!;
var request = new SearchRequest
{
Body = new SearchRequest.Types.Body
{
- ContainerId = cid.ToGrpcMessage(),
+ ContainerId = args.ContainerId.ToGrpcMessage(),
Filters = { },
Version = 1 // TODO: clarify this param
}
};
- request.Body.Filters.AddRange(filters.Select(f => f.ToGrpcMessage()));
+ request.Body.Filters.AddRange(args.Filters.Select(f => f.ToGrpcMessage()));
- request.AddMetaHeader();
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ new Address { ContainerId = request.Body.ContainerId },
+ ObjectSessionContext.Types.Verb.Search,
+ Context.Key);
+
+ request.AddMetaHeader(args.XHeaders, sessionToken);
+
request.Sign(Context.Key);
var objectsIds = SearchObjects(request, ctx);
@@ -123,25 +148,24 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
}
}
- internal Task PutObjectAsync(PutObjectParameters parameters, Context ctx)
+ internal Task PutObjectAsync(PrmPutObject args)
{
- if (parameters.Header == null)
- throw new ArgumentException("Value cannot be null", nameof(parameters.Header));
+ if (args.Header == null)
+ throw new ArgumentException("Value cannot be null", nameof(args.Header));
- if (parameters.Payload == null)
- throw new ArgumentException("Value cannot be null", nameof(parameters.Payload));
+ if (args.Payload == null)
+ throw new ArgumentException("Value cannot be null", nameof(args.Payload));
- if (parameters.ClientCut)
- return PutClientCutObject(parameters, ctx);
+ if (args.ClientCut)
+ return PutClientCutObject(args);
else
- return PutStreamObject(parameters, ctx);
+ return PutStreamObject(args);
}
- internal async Task PutSingleObjectAsync(FrostFsObject modelObject, Context ctx)
+ internal async Task PutSingleObjectAsync(PrmPutSingleObject args)
{
- var sessionToken = await GetOrCreateSession(ctx);
-
- var grpcObject = tools.CreateObject(modelObject);
+ var ctx = args.Context!;
+ var grpcObject = tools.CreateObject(args.FrostFsObject);
var request = new PutSingleRequest
{
@@ -151,14 +175,14 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
}
};
- request.AddMetaHeader();
- request.AddObjectSessionToken(
- sessionToken,
- grpcObject.Header.ContainerId,
- grpcObject.ObjectId,
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ new Address { ContainerId = grpcObject.Header.ContainerId, ObjectId = grpcObject.ObjectId},
ObjectSessionContext.Types.Verb.Put,
- Context.Key
- );
+ Context.Key);
+
+ request.AddMetaHeader(args.XHeaders, sessionToken);
request.Sign(Context.Key);
@@ -169,17 +193,23 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return ObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray());
}
- private async Task PutClientCutObject(PutObjectParameters parameters, Context ctx)
+ static readonly AsyncLocal asyncLocalSession = new ();
+
+ private async Task PutClientCutObject(PrmPutObject args)
{
- var payloadStream = parameters.Payload!;
- var header = parameters.Header!;
+ var ctx = args.Context!;
+ var tokenRaw = await GetOrCreateSession(args, ctx);
+ var token = new ModelsV2.SessionToken(tokenRaw.Serialize());
+
+ var payloadStream = args.Payload!;
+ var header = args.Header!;
ObjectId? objectId;
List sentObjectIds = [];
FrostFsObject? currentObject;
- var networkSettings = await Context.Client.GetNetworkSettingsAsync(ctx);
+ var networkSettings = await Context.Client.GetNetworkSettingsAsync(new PrmGetNetworkSettings(ctx));
var objectSize = (int)networkSettings.MaxObjectSize;
@@ -208,27 +238,30 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
split.Previous = sentObjectIds.LastOrDefault();
- largeObject.AppendBlock(buffer, bytesCount);
+ largeObject.Header.PayloadLength += (ulong)bytesCount;
- currentObject = new FrostFsObject(header.ContainerId, bytesCount < objectSize ? buffer[..bytesCount] : buffer)
+ currentObject = new FrostFsObject(header.ContainerId)
+ .SetPayload(bytesCount < objectSize ? buffer[..bytesCount] : buffer)
.SetSplit(split);
if (largeObject.PayloadLength == fullLength)
break;
- objectId = await PutSingleObjectAsync(currentObject, ctx);
-
+ objectId = await PutSingleObjectAsync(new PrmPutSingleObject(currentObject, ctx) { SessionToken = token });
+
sentObjectIds.Add(objectId!);
}
if (sentObjectIds.Count != 0)
{
- largeObject.AddAttributes(parameters.Header!.Attributes);
- largeObject.CalculateHash();
-
+ largeObject.AddAttributes(args.Header!.Attributes);
+
currentObject.SetParent(largeObject);
- objectId = await PutSingleObjectAsync(currentObject, ctx);
+ var putSingleObjectParams = new PrmPutSingleObject(currentObject, ctx) { SessionToken = token };
+
+ objectId = await PutSingleObjectAsync(putSingleObjectParams);
+
sentObjectIds.Add(objectId);
var linkObject = new LinkObject(header.ContainerId, split.SplitId, largeObject)
@@ -236,22 +269,21 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
linkObject.Header.Attributes.Clear();
- _ = await PutSingleObjectAsync(linkObject, ctx);
+ _ = await PutSingleObjectAsync(new PrmPutSingleObject(linkObject, ctx){ SessionToken = token });
return tools.CalculateObjectId(largeObject.Header);
}
- currentObject.AddAttributes(parameters.Header!.Attributes);
+ currentObject.AddAttributes(args.Header!.Attributes);
- return await PutSingleObjectAsync(currentObject, ctx);
+ return await PutSingleObjectAsync(new PrmPutSingleObject(currentObject, ctx));
}
- private async Task PutStreamObject(PutObjectParameters parameters, Context ctx)
+ private async Task PutStreamObject(PrmPutObject args)
{
- var payload = parameters.Payload!;
- var header = parameters.Header!;
-
- var sessionToken = await GetOrCreateSession(ctx);
+ var ctx = args.Context!;
+ var payload = args.Payload!;
+ var header = args.Header!;
var hdr = header.ToGrpcMessage();
hdr.OwnerId = Context.Owner.ToGrpcMessage();
@@ -270,20 +302,21 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
}
};
- initRequest.AddMetaHeader();
- initRequest.AddObjectSessionToken(
- sessionToken,
- hdr.ContainerId,
- oid,
+ var sessionToken = await GetOrCreateSession(args, ctx);
+
+ sessionToken.CreateObjectTokenContext(
+ new Address { ContainerId = hdr.ContainerId, ObjectId = oid },
ObjectSessionContext.Types.Verb.Put,
Context.Key
);
+ initRequest.AddMetaHeader(args.XHeaders, sessionToken);
+
initRequest.Sign(Context.Key);
using var stream = await PutObjectInit(initRequest, ctx);
- var bufferSize = parameters.BufferMaxSize > 0 ? parameters.BufferMaxSize : Constants.ObjectChunkSize;
+ var bufferSize = args.BufferMaxSize > 0 ? args.BufferMaxSize : Constants.ObjectChunkSize;
if (payload.CanSeek)
{
@@ -307,12 +340,13 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
{
Body = new PutRequest.Types.Body
{
- Chunk = ByteString.CopyFrom(buffer[..bytesCount]),
+ Chunk = ByteString.CopyFrom(buffer.AsSpan()[..bytesCount]),
},
VerifyHeader = null
};
chunkRequest.Sign(Context.Key);
+
await stream.Write(chunkRequest);
}
@@ -388,13 +422,13 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return new SearchReader(call);
}
- private async Task GetOrCreateSession(Context ctx)
+ private async ValueTask GetOrCreateSession(ISessionToken args, Context ctx)
{
- if (string.IsNullOrEmpty(ctx.SessionToken))
+ if (args.SessionToken is null)
{
- return await Context.Client.CreateSessionInternalAsync(uint.MaxValue, ctx);
+ return await Context.Client.CreateSessionInternalAsync(new PrmCreateSession(uint.MaxValue, ctx));
}
-
- return Convert.FromBase64String(ctx.SessionToken).DeserializeSessionToken();
+
+ return new Session.SessionToken().Deserialize(args.SessionToken.Token);
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
index c8c3c47..64c6597 100644
--- a/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/SessionServiceProvider.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
-
using FrostFS.SDK.ClientV2.Mappers.GRPC;
+using FrostFS.SDK.ClientV2.Parameters;
using FrostFS.Session;
namespace FrostFS.SDK.ClientV2;
@@ -15,41 +15,41 @@ internal class SessionServiceProvider : ContextAccessor
_sessionServiceClient = sessionServiceClient;
}
- internal async Task CreateSessionAsync(ulong expiration, Context ctx)
+ internal async Task CreateSessionAsync(PrmCreateSession args)
{
var request = new CreateRequest
{
Body = new CreateRequest.Types.Body
{
OwnerId = Context.Owner.ToGrpcMessage(),
- Expiration = expiration
+ Expiration = args.Expiration
}
};
- request.AddMetaHeader();
+ request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
- var token = await CreateSession(request, ctx);
-
- return token;
+ return await CreateSession(request, args.Context!);
}
internal async Task CreateSession(CreateRequest request, Context ctx)
{
- var resp = await _sessionServiceClient!.CreateAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ var response = await _sessionServiceClient!.CreateAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+
+ Verifier.CheckResponse(response);
return new SessionToken
{
Body = new SessionToken.Types.Body
{
- Id = resp.Body.Id,
- SessionKey = resp.Body.SessionKey,
+ Id = response.Body.Id,
+ SessionKey = response.Body.SessionKey,
OwnerId = request.Body.OwnerId,
Lifetime = new SessionToken.Types.Body.Types.TokenLifetime
{
Exp = request.Body.Expiration,
- Iat = resp.MetaHeader.Epoch,
- Nbf = resp.MetaHeader.Epoch,
+ Iat = response.MetaHeader.Epoch,
+ Nbf = response.MetaHeader.Epoch,
}
}
};
diff --git a/src/FrostFS.SDK.ClientV2/Tools/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
index 0a0290e..1bc66a3 100644
--- a/src/FrostFS.SDK.ClientV2/Tools/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
@@ -13,6 +13,12 @@ public static class ObjectExtensions
return obj;
}
+ public static FrostFsObject SetPayload(this FrostFsObject obj, byte[] bytes)
+ {
+ obj.Payload = bytes;
+ return obj;
+ }
+
public static FrostFsObject AddAttribute(this FrostFsObject obj, string key, string value)
{
obj.AddAttribute(new ObjectAttribute(key, value));
@@ -45,9 +51,6 @@ public static class ObjectExtensions
public static FrostFsObject CalculateObjectId(this FrostFsObject obj)
{
- if (obj.Payload == null)
- throw new MissingFieldException("Payload cannot be null");
-
if (obj.Header == null)
throw new MissingFieldException("Header cannot be null");
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Services/ObjectReader.cs
rename to src/FrostFS.SDK.ClientV2/Tools/ObjectReader.cs
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectStreamer.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Services/ObjectStreamer.cs
rename to src/FrostFS.SDK.ClientV2/Tools/ObjectStreamer.cs
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
similarity index 98%
rename from src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs
rename to src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
index 48a1275..fb7f987 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectTools.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
@@ -93,7 +93,7 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx)
return new Checksum
{
Type = ChecksumType.Sha256,
- Sum = ByteString.CopyFrom(data.Sha256())
+ Sum = ByteString.CopyFrom(data.Sha256())
};
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
index 6e2a5dc..dccb35f 100644
--- a/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/RequestConstructor.cs
@@ -1,4 +1,7 @@
+using System.Collections.Specialized;
+using System.Linq;
using System.Security.Cryptography;
+
using FrostFS.Refs;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.ModelsV2;
@@ -9,36 +12,38 @@ namespace FrostFS.SDK.ClientV2;
public static class RequestConstructor
{
- public static void AddMetaHeader(this IRequest request, RequestMetaHeader? metaHeader = null)
+ public static void AddMetaHeader(this IRequest request, NameValueCollection? xHeaders, Session.SessionToken? sessionToken = null)
{
- if (request.MetaHeader is not null) return;
- metaHeader ??= MetaHeader.Default().ToGrpcMessage();
- request.MetaHeader = metaHeader;
+ if (request.MetaHeader is not null)
+ return;
+
+ request.MetaHeader = MetaHeader.Default().ToGrpcMessage();
+
+ if (sessionToken != null)
+ request.MetaHeader.SessionToken = sessionToken;
+
+ if (xHeaders != null && xHeaders.Count > 0)
+ request.MetaHeader.XHeaders.AddRange(
+ xHeaders.Cast().SelectMany(key => xHeaders.GetValues(key),
+ (k, v) => new XHeader { Key = k, Value = v }));
}
- public static void AddObjectSessionToken(
- this IRequest request,
- Session.SessionToken sessionToken,
- ContainerID cid,
- ObjectID oid,
+ public static void CreateObjectTokenContext(this Session.SessionToken sessionToken,
+ Address address,
ObjectSessionContext.Types.Verb verb,
ECDsa key)
{
- if (request.MetaHeader.SessionToken is not null)
- return;
+ ObjectSessionContext.Types.Target target = new() { Container = address.ContainerId };
- request.MetaHeader.SessionToken = sessionToken;
- var ctx = new ObjectSessionContext
+ if (address.ObjectId != null)
+ target.Objects.Add(address.ObjectId);
+
+ sessionToken.Body.Object = new()
{
- Target = new ObjectSessionContext.Types.Target
- {
- Container = cid,
- Objects = { oid }
- },
+ Target = target,
Verb = verb
};
- request.MetaHeader.SessionToken.Body.Object = ctx;
- request.MetaHeader.SessionToken.Signature = key.SignMessagePart(request.MetaHeader.SessionToken.Body);
+ sessionToken.Signature = key.SignMessagePart(sessionToken.Body);
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Services/SearchReader.cs b/src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Services/SearchReader.cs
rename to src/FrostFS.SDK.ClientV2/Tools/SearchReader.cs
diff --git a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
index 6e65c02..2b36a64 100644
--- a/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Verifier.cs
@@ -102,7 +102,7 @@ public static class Verifier
var status = resp.MetaHeader.Status.ToModel();
if (!status.IsSuccess)
- throw new ApplicationException(status.ToString());
+ throw new ResponseException(status);
}
///
diff --git a/src/FrostFS.SDK.ModelsV2/Context.cs b/src/FrostFS.SDK.ModelsV2/Context.cs
index b1038ec..4ac90a8 100644
--- a/src/FrostFS.SDK.ModelsV2/Context.cs
+++ b/src/FrostFS.SDK.ModelsV2/Context.cs
@@ -12,8 +12,6 @@ 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;
public Action? Callback { get; set; }
diff --git a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
index be180b0..d4993b7 100644
--- a/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
+++ b/src/FrostFS.SDK.ModelsV2/FrostFS.SDK.ModelsV2.csproj
@@ -18,4 +18,9 @@
+
+
+ <_Parameter1>FrostFS.SDK.ClientV2
+
+
diff --git a/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
index 4cbfaae..fa2586a 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
@@ -6,32 +6,56 @@ namespace FrostFS.SDK.ModelsV2;
public class FrostFsObject
{
- public FrostFsObject(ObjectId objectId, ObjectHeader header, byte[] payload)
+ ///
+ /// Creates new instance from ObjectHeader
+ ///
+ ///
+ public FrostFsObject(ObjectHeader header)
{
- ObjectId = objectId;
- Payload = payload;
Header = header;
}
- public FrostFsObject(ContainerId container, byte[] payload, ObjectType objectType = ObjectType.Regular)
+ ///
+ /// Creates new instance with specified parameters
+ ///
+ ///
+ ///
+ public FrostFsObject(ContainerId container, ObjectType objectType = ObjectType.Regular)
{
- Payload = payload;
- Header = new ObjectHeader(containerId: container, type: objectType, attributes: []);
+ Header = new ObjectHeader(containerId: container, type: objectType);
}
+ ///
+ /// Header contains metadata for the object
+ ///
+ ///
public ObjectHeader Header { get; set; }
+ ///
+ /// The value is calculated internally as a hash of ObjectHeader. Do not use pre-calculated value is the object has been changed.
+ ///
public ObjectId? ObjectId
{
get; internal set;
}
-
- public byte[] Payload { get; set; }
+ ///
+ /// The size of payload cannot exceed MaxObjectSize value from NetworkSettings
+ /// Used only for PutSingleObject method
+ ///
+ /// Buffer for output data
+ public byte[] Payload { get; set; } = [];
+
+ ///
+ /// A payload is obtained via stream reader
+ ///
+ /// Reader for received data
public IObjectReader? ObjectReader { get; set; }
- public Signature? Signature { get; set; }
-
+ ///
+ /// Applied only for the last Object in chain in case of manual multipart uploading
+ ///
+ /// Parent for multipart object
public void SetParent(LargeObject largeObject)
{
if (Header?.Split == null)
@@ -41,7 +65,7 @@ public class FrostFsObject
}
}
-public class LargeObject(ContainerId container) : FrostFsObject(container, [])
+public class LargeObject(ContainerId container) : FrostFsObject(container)
{
private readonly SHA256 payloadHash = SHA256.Create();
@@ -66,11 +90,11 @@ public class LargeObject(ContainerId container) : FrostFsObject(container, [])
public class LinkObject : FrostFsObject
{
- public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId, [])
+ public LinkObject(ContainerId containerId, SplitId splitId, LargeObject largeObject) : base (containerId)
{
Header!.Split = new Split(splitId)
{
- ParentHeader = largeObject.Header
+ ParentHeader = largeObject.Header
};
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
index 69a5a99..0a8d64c 100644
--- a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
+++ b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
@@ -1,14 +1 @@
-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; }
-
- public int BufferMaxSize { get; set; }
-}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/SessionToken.cs b/src/FrostFS.SDK.ModelsV2/SessionToken.cs
index 6fde99e..e7da09a 100644
--- a/src/FrostFS.SDK.ModelsV2/SessionToken.cs
+++ b/src/FrostFS.SDK.ModelsV2/SessionToken.cs
@@ -1,8 +1,11 @@
namespace FrostFS.SDK.ModelsV2;
-public class SessionToken(byte[] sessionKey, byte[] id)
+public class SessionToken
{
- public byte[] Id { get; } = id;
+ internal SessionToken(byte[] token)
+ {
+ Token = token;
+ }
- public byte[] SessionKey { get; } = sessionKey;
+ public byte[] Token { get; private set; }
}
diff --git a/src/FrostFS.SDK.Tests/ContainerTest.cs b/src/FrostFS.SDK.Tests/ContainerTest.cs
index 498a47a..9293579 100644
--- a/src/FrostFS.SDK.Tests/ContainerTest.cs
+++ b/src/FrostFS.SDK.Tests/ContainerTest.cs
@@ -8,10 +8,10 @@ using FrostFS.SDK.ModelsV2.Netmap;
using FrostFS.SDK.ModelsV2.Enums;
using Google.Protobuf;
using FrostFS.SDK.ClientV2.Interfaces;
+using FrostFS.SDK.ClientV2.Parameters;
namespace FrostFS.SDK.Tests;
-
public abstract class ContainerTestsBase
{
protected readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
@@ -38,12 +38,12 @@ public abstract class ContainerTestsBase
protected IFrostFSClient GetClient()
{
return ClientV2.Client.GetTestInstance(
- Settings,
- null,
- new NetworkMocker(this.key).GetMock().Object,
- new SessionMocker(this.key).GetMock().Object,
- Mocker.GetMock().Object,
- new ObjectMocker(this.key).GetMock().Object);
+ Settings,
+ null,
+ new NetworkMocker(this.key).GetMock().Object,
+ new SessionMocker(this.key).GetMock().Object,
+ Mocker.GetMock().Object,
+ new ObjectMocker(this.key).GetMock().Object);
}
}
@@ -52,7 +52,9 @@ public class ContainerTest : ContainerTestsBase
[Fact]
public async void CreateContainerTest()
{
- var result = await GetClient().CreateContainerAsync(new ModelsV2.Container(BasicAcl.PublicRW, Mocker.PlacementPolicy));
+ var param = new PrmCreateContainer(new ModelsV2.Container(BasicAcl.PublicRW, Mocker.PlacementPolicy));
+
+ var result = await GetClient().CreateContainerAsync(param);
Assert.NotNull(result);
Assert.NotNull(result.Value);
@@ -66,7 +68,7 @@ public class ContainerTest : ContainerTestsBase
Mocker.Acl = BasicAcl.PublicRO;
- var result = await GetClient().GetContainerAsync(cid);
+ var result = await GetClient().GetContainerAsync(new PrmGetContainer(cid));
Assert.NotNull(result);
Assert.Equal(Mocker.Acl, result.BasicAcl);
@@ -99,9 +101,10 @@ public class ContainerTest : ContainerTestsBase
[Fact]
public async void DeleteContainerAsyncTest()
{
+ Mocker.ReturnContainerRemoved = true;
var cid = new ContainerId(Base58.Encode(Mocker.ContainerGuid.ToBytes()));
- await GetClient().DeleteContainerAsync(cid);
+ await GetClient().DeleteContainerAsync(new PrmDeleteContainer(cid));
Assert.Single(Mocker.Requests);
diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs
index 2188795..aff566d 100644
--- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs
+++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/GetContainerMock.cs
@@ -77,6 +77,21 @@ public class ContainerMocker(string key) : ContainerServiceBase(key)
getResponse.VerifyHeader = GetResponseVerificationHeader(getResponse);
+ var getNoContainerResponse = new GetResponse
+ {
+ Body = new (),
+ MetaHeader = new ResponseMetaHeader
+ {
+ Status = new Status.Status
+ {
+ Code = 3072,
+ Message = "container not found"
+ }
+ }
+ };
+
+ getNoContainerResponse.VerifyHeader = GetResponseVerificationHeader(getNoContainerResponse);
+
mock.Setup(x => x.GetAsync(
It.IsAny(),
It.IsAny(),
@@ -86,12 +101,22 @@ public class ContainerMocker(string key) : ContainerServiceBase(key)
{
Verifier.CheckRequest(r);
+ if (ReturnContainerRemoved)
+ {
+ return new AsyncUnaryCall(
+ Task.FromResult(getNoContainerResponse),
+ Task.FromResult(ResponseMetaData),
+ () => new Grpc.Core.Status(StatusCode.NotFound, string.Empty),
+ () => ResponseMetaData,
+ () => { });
+ }
+
return new AsyncUnaryCall(
Task.FromResult(getResponse),
Task.FromResult(ResponseMetaData),
() => new Grpc.Core.Status(StatusCode.OK, string.Empty),
() => ResponseMetaData,
- () => { });
+ () => { });
});
var listResponse = new ListResponse
@@ -154,6 +179,8 @@ public class ContainerMocker(string key) : ContainerServiceBase(key)
return mock;
}
+ public bool ReturnContainerRemoved { get; set; }
+
public List ContainerIds { get; set; } = [];
public List> Requests { get; set; } = [];
diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs
index d524284..2513908 100644
--- a/src/FrostFS.SDK.Tests/ObjectTest.cs
+++ b/src/FrostFS.SDK.Tests/ObjectTest.cs
@@ -2,6 +2,7 @@ using FrostFS.Refs;
using FrostFS.SDK.ClientV2;
using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ClientV2.Mappers.GRPC;
+using FrostFS.SDK.ClientV2.Parameters;
using FrostFS.SDK.Cryptography;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
@@ -76,7 +77,7 @@ public class ObjectTest : ObjectTestsBase
Timeout = TimeSpan.FromSeconds(2)
};
- var result = await client.GetObjectAsync(ContainerId, objectId, context);
+ var result = await client.GetObjectAsync(new PrmGetObject(ContainerId, objectId) { Context = context });
Assert.NotNull(result);
@@ -97,7 +98,7 @@ public class ObjectTest : ObjectTestsBase
var bytes = new byte[1024];
rnd.NextBytes(bytes);
- var param = new PutObjectParameters
+ var param = new PrmPutObject
{
Header = Mocker.ObjectHeader,
Payload = new MemoryStream(bytes),
@@ -132,7 +133,7 @@ public class ObjectTest : ObjectTestsBase
byte[] bytes = File.ReadAllBytes(@".\..\..\..\TestData\cat.jpg");
var fileLength = bytes.Length;
- var param = new PutObjectParameters
+ var param = new PrmPutObject
{
Header = Mocker.ObjectHeader,
Payload = new MemoryStream(bytes),
@@ -196,7 +197,7 @@ public class ObjectTest : ObjectTestsBase
{
Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel();
- await GetClient().DeleteObjectAsync(ContainerId, Mocker.ObjectId);
+ await GetClient().DeleteObjectAsync(new PrmDeleteObject(ContainerId, Mocker.ObjectId));
var request = Mocker.DeleteRequests.FirstOrDefault();
Assert.NotNull(request);
@@ -209,7 +210,7 @@ public class ObjectTest : ObjectTestsBase
{
Mocker.ObjectId = new ObjectID { Value = ByteString.CopyFrom(SHA256.HashData(Encoding.UTF8.GetBytes("test"))) }.ToModel();
- var response = await GetClient().GetObjectHeadAsync(ContainerId, Mocker.ObjectId);
+ var response = await GetClient().GetObjectHeadAsync(new PrmGetObjectHead(ContainerId, Mocker.ObjectId));
var request = Mocker.HeadRequests.FirstOrDefault();
Assert.NotNull(request);
diff --git a/src/FrostFS.SDK.Tests/SmokeTests.cs b/src/FrostFS.SDK.Tests/SmokeTests.cs
index ad9f5ee..ba7e32c 100644
--- a/src/FrostFS.SDK.Tests/SmokeTests.cs
+++ b/src/FrostFS.SDK.Tests/SmokeTests.cs
@@ -4,24 +4,30 @@ using FrostFS.SDK.ClientV2.Interfaces;
using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ModelsV2.Enums;
using FrostFS.SDK.ModelsV2.Netmap;
+using FrostFS.SDK.Cryptography;
+
using Grpc.Core;
using Microsoft.Extensions.Options;
using Grpc.Core.Interceptors;
using System.Diagnostics;
+using static FrostFS.Session.SessionToken.Types.Body;
+using FrostFS.SDK.ClientV2.Parameters;
+
namespace FrostFS.SDK.SmokeTests;
public class SmokeTests
{
+ private static PrmWait lightWait = new (100, 1);
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
private readonly string url = "http://172.29.238.97:8080";
[Fact]
public async void NetworkMapTest()
{
- using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
- var result = await fsClient.GetNetmapSnapshotAsync();
+ var result = await client.GetNetmapSnapshotAsync();
Assert.True(result.Epoch > 0);
Assert.Single(result.NodeInfoCollection);
@@ -38,9 +44,9 @@ public class SmokeTests
[Fact]
public async void NodeInfoTest()
{
- using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
- var result = await fsClient.GetNodeInfoAsync();
+ var result = await client.GetNodeInfoAsync();
Assert.Equal(2, result.Version.Major);
Assert.Equal(13, result.Version.Minor);
@@ -51,80 +57,74 @@ public class SmokeTests
}
[Fact]
- public async void NodeInfo_Statictics_Test()
+ public async void NodeInfo_Statistics_Test()
{
var ctx = new Context
{
Callback = (cs) => Console.WriteLine($"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds")
};
- using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
- var result = await fsClient.GetNodeInfoAsync();
+ var result = await client.GetNodeInfoAsync();
}
- [Theory]
- [InlineData(1)]
- [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB
- [InlineData(6 * 1024 * 1024 + 100)]
- public async void SimpleScenarioTest(int objectSize)
+ [Fact]
+ public async void GetSessionTest()
{
- using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
- await Cleanup(fsClient);
+ var token = await client.CreateSessionAsync(new PrmCreateSession(100));
- var containerId = await fsClient.CreateContainerAsync(
- new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))),
- new Context
- {
- Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
- }
- );
+ var session = new Session.SessionToken().Deserialize(token.Token);
+
+ var ecdsaKey = this.key.LoadWif();
+ var owner = OwnerId.FromKey(ecdsaKey);
- var context = new Context
- {
- Timeout = TimeSpan.FromSeconds(10),
- Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
- };
+ var ownerHash = Base58.Decode(owner.Value);
+
+ Assert.NotNull(session);
+ Assert.Null(session.Body.Container);
+ Assert.Null(session.Body.Object);
+ Assert.Equal(16, session.Body.Id.Length);
+ Assert.Equal(100ul, session.Body.Lifetime.Exp);
+ Assert.Equal(ownerHash, session.Body.OwnerId.Value);
+ Assert.Equal(33, session.Body.SessionKey.Length);
+ Assert.Equal(ContextOneofCase.None, session.Body.ContextCase);
+ }
- var container = await GetContainer(fsClient, containerId, context);
+ [Fact]
+ public async void CreateObjectWithSessionToken()
+ {
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
+
+ await Cleanup(client);
- Assert.NotNull(container);
+ var token = await client.CreateSessionAsync(new PrmCreateSession(int.MaxValue));
- var bytes = GetRandomBytes(objectSize);
+ var createContainerParam = new PrmCreateContainer(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))));
- var param = new PutObjectParameters
+ createContainerParam.XHeaders.Add("key1", "value1");
+
+ var containerId = await client.CreateContainerAsync(createContainerParam);
+
+ var bytes = GetRandomBytes(1024);
+
+ var param = new PrmPutObject
{
Header = new ObjectHeader(
containerId: containerId,
type: ObjectType.Regular,
new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes),
- ClientCut = false
+ ClientCut = false,
+ SessionToken = token
};
+
+ var objectId = await client.PutObjectAsync(param);
- var objectId = await fsClient.PutObjectAsync(param, new Context
- {
- Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
- });
-
- 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((ulong)bytes.Length, 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!);
+ var @object = await client.GetObjectAsync(new PrmGetObject(containerId, objectId));
var downloadedBytes = new byte[@object.Header.PayloadLength];
MemoryStream ms = new(downloadedBytes);
@@ -137,11 +137,177 @@ public class SmokeTests
Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
- await Cleanup(fsClient);
+ await Cleanup(client);
+ }
- await Task.Delay(2000);
+ [Theory]
+ [InlineData(1)]
+ [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB
+ [InlineData(6 * 1024 * 1024 + 100)]
+ public async void SimpleScenarioTest(int objectSize)
+ {
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
+
+ await Cleanup(client);
- await foreach (var _ in fsClient.ListContainersAsync())
+ bool callbackInvoked = false;
+ var ctx = new Context
+ {
+ Timeout = TimeSpan.FromSeconds(20),
+ Callback = new((CallStatistics cs) =>
+ {
+ callbackInvoked = true;
+ Assert.True(cs.ElapsedMicroSeconds > 0);
+ })
+ };
+
+ var createContainerParam = new PrmCreateContainer(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))))
+ {
+ Context = ctx
+ };
+
+ var containerId = await client.CreateContainerAsync(createContainerParam);
+
+ var container = await client.GetContainerAsync(new PrmGetContainer(containerId,ctx));
+ Assert.NotNull(container);
+ Assert.True(callbackInvoked);
+
+ var bytes = GetRandomBytes(objectSize);
+
+ var param = new PrmPutObject
+ {
+ Header = new ObjectHeader(
+ containerId: containerId,
+ type: ObjectType.Regular,
+ new ObjectAttribute("fileName", "test")),
+ Payload = new MemoryStream(bytes),
+ ClientCut = false,
+ Context = new Context
+ {
+ Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
+ }
+ };
+
+ var objectId = await client.PutObjectAsync(param);
+
+ var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+
+ bool hasObject = false;
+ await foreach (var objId in client.SearchObjectsAsync(new PrmSearchObject(containerId) { Filters = [filter] }))
+ {
+ hasObject = true;
+
+ var objHeader = await client.GetObjectHeadAsync(new PrmGetObjectHead(containerId, objectId));
+ 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);
+ }
+
+ Assert.True(hasObject);
+
+ var @object = await client.GetObjectAsync(new PrmGetObject(containerId, objectId));
+
+ 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(client);
+
+ await foreach (var _ in client.ListContainersAsync())
+ {
+ Assert.Fail("Containers exist");
+ }
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB
+ [InlineData(6 * 1024 * 1024 + 100)]
+ public async void SimpleScenarioWithSessionTest(int objectSize)
+ {
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
+
+ var token = await client.CreateSessionAsync(new PrmCreateSession(int.MaxValue));
+
+ await Cleanup(client);
+
+ var ctx = new Context
+ {
+ Timeout = TimeSpan.FromSeconds(20),
+ Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
+ };
+
+ var createContainerParam = new PrmCreateContainer(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))))
+ {
+ Context = ctx
+ };
+
+ var containerId = await client.CreateContainerAsync(createContainerParam);
+
+ var container = await client.GetContainerAsync(new PrmGetContainer(containerId,ctx));
+ Assert.NotNull(container);
+
+ var bytes = GetRandomBytes(objectSize);
+
+ var param = new PrmPutObject
+ {
+ Header = new ObjectHeader(
+ containerId: containerId,
+ type: ObjectType.Regular,
+ new ObjectAttribute("fileName", "test")),
+ Payload = new MemoryStream(bytes),
+ ClientCut = false,
+ Context = new Context
+ {
+ Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
+ },
+ SessionToken = token
+ };
+
+ var objectId = await client.PutObjectAsync(param);
+
+ var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+
+ bool hasObject = false;
+ await foreach (var objId in client.SearchObjectsAsync(new PrmSearchObject(containerId) { Filters = [filter], SessionToken = token }))
+ {
+ hasObject = true;
+
+ var objHeader = await client.GetObjectHeadAsync(new PrmGetObjectHead(containerId, objectId) { SessionToken = token });
+ 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);
+ }
+
+ Assert.True(hasObject);
+
+ var @object = await client.GetObjectAsync(new PrmGetObject(containerId, objectId) { SessionToken = token });
+
+ 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(client);
+
+ await foreach (var _ in client.ListContainersAsync())
{
Assert.Fail("Containers exist");
}
@@ -155,13 +321,16 @@ public class SmokeTests
[InlineData(2 * 64 * 1024 * 1024 + 256)]
public async void ClientCutScenarioTest(int objectSize)
{
- using var fsClient = Client.GetInstance(GetOptions(this.key, this.url));
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
- await Cleanup(fsClient);
+ await Cleanup(client);
- var cnt = new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1)));
+ var createContainerParam = new PrmCreateContainer(new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))))
+ {
+ WaitParams = lightWait
+ };
- var containerId = await fsClient.CreateContainerAsync(cnt);
+ var containerId = await client.CreateContainerAsync(createContainerParam);
var context = new Context
{
@@ -169,13 +338,13 @@ public class SmokeTests
Interceptors = new([new MetricsInterceptor()])
};
- var container = await GetContainer(fsClient, containerId, context);
+ var container = await client.GetContainerAsync(new PrmGetContainer(containerId, context));
Assert.NotNull(container);
byte[] bytes = GetRandomBytes(objectSize);
- var param = new PutObjectParameters
+ var param = new PrmPutObject
{
Header = new ObjectHeader(
containerId: containerId,
@@ -185,16 +354,16 @@ public class SmokeTests
ClientCut = true
};
- var objectId = await fsClient.PutObjectAsync(param);
+ var objectId = await client.PutObjectAsync(param);
var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
bool hasObject = false;
- await foreach (var objId in fsClient.SearchObjectsAsync(containerId, [filter]))
+ await foreach (var objId in client.SearchObjectsAsync(new PrmSearchObject(containerId, filter)))
{
hasObject = true;
- var objHeader = await fsClient.GetObjectHeadAsync(containerId, objectId);
+ var objHeader = await client.GetObjectHeadAsync(new PrmGetObjectHead(containerId, objectId));
Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength);
Assert.Single(objHeader.Attributes);
Assert.Equal("fileName", objHeader.Attributes.First().Key);
@@ -203,7 +372,7 @@ public class SmokeTests
Assert.True(hasObject);
- var @object = await fsClient.GetObjectAsync(containerId, objectId!);
+ var @object = await client.GetObjectAsync(new PrmGetObject(containerId, objectId));
var downloadedBytes = new byte[@object.Header.PayloadLength];
MemoryStream ms = new(downloadedBytes);
@@ -216,14 +385,23 @@ public class SmokeTests
Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
- await Cleanup(fsClient);
+ await Cleanup(client);
- await Task.Delay(2000);
+ var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
- await foreach (var _ in fsClient.ListContainersAsync())
+ IAsyncEnumerator? enumerator = null;
+ do
{
- Assert.Fail("Containers exist");
+ if (deadline <= DateTime.UtcNow)
+ {
+ Assert.Fail("Containers exist");
+ break;
+ }
+
+ enumerator = client.ListContainersAsync().GetAsyncEnumerator();
+ await Task.Delay(500);
}
+ while (await enumerator!.MoveNextAsync());
}
private static byte[] GetRandomBytes(int size)
@@ -243,36 +421,16 @@ public class SmokeTests
});
}
- static async Task Cleanup(IFrostFSClient fsClient)
+ static async Task Cleanup(IFrostFSClient client)
{
- await foreach (var cid in fsClient.ListContainersAsync())
+ await foreach (var cid in client.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 (RpcException)
- {
- throw;
- }
+ await client.DeleteContainerAsync(new PrmDeleteContainer(cid) { WaitParams = lightWait });
}
}
}
+
public class MetricsInterceptor() : Interceptor
{
public override AsyncUnaryCall AsyncUnaryCall(