From 35fe79140615dc463eab4a2aec944c8742351d98 Mon Sep 17 00:00:00 2001
From: Pavel Gross
Date: Thu, 25 Jul 2024 14:20:14 +0300
Subject: [PATCH] [#19] Client: Use specific classes for search
Signed-off-by: Pavel Gross
---
.../Mappers/Object/ObjectFilterMapper.cs | 4 +-
.../Mappers/Object/ObjectHeaderMapper.cs | 4 +-
.../Parameters/PrmContainerGetAll.cs | 2 -
.../Parameters/PrmObjectSearch.cs | 8 +-
.../Services/ContainerServiceProvider.cs | 48 +++----
.../Services/ObjectServiceProvider.cs | 27 ++--
.../Shared}/ContextAccessor.cs | 0
.../Services/Shared/SessionProvider.cs | 23 ++++
src/FrostFS.SDK.ClientV2/Tools/Object.cs | 2 +-
src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs | 4 +-
src/FrostFS.SDK.Cryptography/UUID.cs | 47 +++++--
.../{ => Client}/ClientSettings.cs | 0
.../{ => Client}/Context.cs | 0
.../{ => Enums}/SignatureScheme.cs | 0
.../{ => Misc}/CallStatistics.cs | 0
src/FrostFS.SDK.ModelsV2/Misc/CheckSum.cs | 5 +-
.../{ => Netmap}/Version.cs | 0
.../Object/FrostFsObject.cs | 13 --
.../Object/ObjectFilter.cs | 125 ++++++++++++++----
.../{ => Object}/OwnerId.cs | 5 +
.../{ => Object}/SplitId.cs | 8 +-
.../{ => Object}/Splitter.cs | 0
.../PutObjectParameters.cs | 1 -
.../{ => Response}/MetaHeader.cs | 0
.../{ => Response}/Signature.cs | 0
.../{ => Response}/Status.cs | 0
src/FrostFS.SDK.Tests/SmokeTests.cs | 117 +++++++++++++---
27 files changed, 320 insertions(+), 123 deletions(-)
rename src/FrostFS.SDK.ClientV2/{Tools => Services/Shared}/ContextAccessor.cs (100%)
create mode 100644 src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs
rename src/FrostFS.SDK.ModelsV2/{ => Client}/ClientSettings.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Client}/Context.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Enums}/SignatureScheme.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Misc}/CallStatistics.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Netmap}/Version.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Object}/OwnerId.cs (83%)
rename src/FrostFS.SDK.ModelsV2/{ => Object}/SplitId.cs (89%)
rename src/FrostFS.SDK.ModelsV2/{ => Object}/Splitter.cs (100%)
delete mode 100644 src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
rename src/FrostFS.SDK.ModelsV2/{ => Response}/MetaHeader.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Response}/Signature.cs (100%)
rename src/FrostFS.SDK.ModelsV2/{ => Response}/Status.cs (100%)
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
index ee819c6..2d9fed7 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectFilterMapper.cs
@@ -7,7 +7,7 @@ namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
public static class ObjectFilterMapper
{
- public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this ObjectFilter filter)
+ public static SearchRequest.Types.Body.Types.Filter ToGrpcMessage(this IObjectFilter filter)
{
var objMatchTypeName = filter.MatchType switch
{
@@ -24,7 +24,7 @@ public static class ObjectFilterMapper
{
MatchType = objMatchTypeName,
Key = filter.Key,
- Value = filter.Value
+ Value = filter.GetSerializedValue()
};
}
}
diff --git a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
index 2292811..12ff85c 100644
--- a/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
+++ b/src/FrostFS.SDK.ClientV2/Mappers/Object/ObjectHeaderMapper.cs
@@ -74,7 +74,7 @@ public static class ObjectHeaderMapper
if (header.Split != null)
{
- model.Split = new Split(SplitId.CreateFromBinary(header.Split.SplitId.ToByteArray()))
+ model.Split = new Split(new SplitId(header.Split.SplitId.ToUuid()))
{
Parent = header.Split.Parent?.ToModel(),
ParentHeader = header.Split.ParentHeader?.ToModel(),
@@ -86,5 +86,5 @@ public static class ObjectHeaderMapper
}
return model;
- }
+ }
}
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs
index 03cb084..77d954e 100644
--- a/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmContainerGetAll.cs
@@ -4,8 +4,6 @@ namespace FrostFS.SDK.ClientV2.Parameters;
public sealed class PrmContainerGetAll() : IContext
{
- public string SessionToken { get; set; } = string.Empty;
-
///
/// FrostFS request X-Headers
///
diff --git a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs
index 9ac4f8d..c4e8fc9 100644
--- a/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs
+++ b/src/FrostFS.SDK.ClientV2/Parameters/PrmObjectSearch.cs
@@ -3,15 +3,19 @@ using System.Collections.Specialized;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Parameters;
-public sealed class PrmObjectSearch(ContainerId containerId, params ObjectFilter[] filters) : IContext, ISessionToken
+public sealed class PrmObjectSearch(ContainerId containerId, params IObjectFilter[] filters) : IContext, ISessionToken
{
+ ///
+ /// Defines container for the search
+ ///
+ ///
public ContainerId ContainerId { get; set; } = containerId;
///
/// Defines the search criteria
///
/// Collection of filters
- public IEnumerable Filters { get; set; } = filters;
+ public IEnumerable Filters { get; set; } = filters;
///
/// FrostFS request X-Headers
diff --git a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
index 44cee16..2a15828 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ContainerServiceProvider.cs
@@ -1,33 +1,25 @@
-using System.Threading.Tasks;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Threading.Tasks;
+
using FrostFS.SDK.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.Container;
-
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;
+using FrostFS.Refs;
namespace FrostFS.SDK.ClientV2;
-internal class ContainerServiceProvider : ContextAccessor
+internal class ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context) : ContextAccessor(context)
{
- private readonly ContainerService.ContainerServiceClient containerServiceClient;
-
- internal ContainerServiceProvider(ContainerService.ContainerServiceClient service, ClientEnvironment context)
- : base(context)
- {
- containerServiceClient = service;
- }
-
internal async Task GetContainerAsync(PrmContainerGet args)
{
GetRequest request = GetContainerRequest(args.ContainerId.ToGrpcMessage(), args.XHeaders);
- var response = await containerServiceClient.GetAsync(request, null, args.Context!.Deadline, args.Context.CancellationToken);
+ var response = await service.GetAsync(request, null, args.Context!.Deadline, args.Context.CancellationToken);
Verifier.CheckResponse(response);
@@ -49,7 +41,7 @@ internal class ContainerServiceProvider : ContextAccessor
request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
- var response = await containerServiceClient.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ var response = await service.ListAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
@@ -74,11 +66,11 @@ internal class ContainerServiceProvider : ContextAccessor
Signature = Context.Key.SignRFC6979(grpcContainer)
}
};
-
+
request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key);
- var response = await containerServiceClient.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ var response = await service.PutAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
@@ -98,11 +90,12 @@ internal class ContainerServiceProvider : ContextAccessor
Signature = Context.Key.SignRFC6979(args.ContainerId.ToGrpcMessage().Value)
}
};
-
+
request.AddMetaHeader(args.XHeaders);
+
request.Sign(Context.Key);
- var response = await containerServiceClient.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ var response = await service.DeleteAsync(request, null, ctx.Deadline, ctx.CancellationToken);
await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx);
@@ -137,7 +130,7 @@ internal class ContainerServiceProvider : ContextAccessor
async Task action()
{
- var response = await containerServiceClient.GetAsync(request, null, ctx.Deadline, ctx.CancellationToken);
+ var response = await service.GetAsync(request, null, ctx.Deadline, ctx.CancellationToken);
Verifier.CheckResponse(response);
}
@@ -148,7 +141,7 @@ internal class ContainerServiceProvider : ContextAccessor
Func action,
WaitExpects expect,
PrmWait? waitParams)
- {
+ {
waitParams ??= PrmWait.DefaultParams;
var deadLine = waitParams.GetDeadline();
@@ -160,7 +153,10 @@ internal class ContainerServiceProvider : ContextAccessor
if (expect == WaitExpects.Exists)
return;
-
+
+ if (DateTime.UtcNow >= deadLine)
+ throw new TimeoutException();
+
await Task.Delay(waitParams.PollInterval);
}
catch (ResponseException ex)
@@ -173,11 +169,9 @@ internal class ContainerServiceProvider : ContextAccessor
if (expect == WaitExpects.Removed)
return;
-
+
await Task.Delay(waitParams.PollInterval);
}
}
}
}
-
-
diff --git a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
index 035faf7..1c8bf2f 100644
--- a/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
+++ b/src/FrostFS.SDK.ClientV2/Services/ObjectServiceProvider.cs
@@ -12,14 +12,19 @@ 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;
-internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientEnvironment ctx) : ContextAccessor(ctx)
+internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, ClientEnvironment ctx) : ContextAccessor(ctx), ISessionProvider
{
readonly ObjectTools tools = new(ctx);
+ readonly SessionProvider sessions = new (ctx);
+
+ public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx)
+ {
+ return await sessions.GetOrCreateSession(args, ctx);
+ }
internal async Task GetObjectHeadAsync(PrmObjectHeadGet args)
{
@@ -137,7 +142,7 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
Context.Key);
request.AddMetaHeader(args.XHeaders, sessionToken);
-
+
request.Sign(Context.Key);
var objectsIds = SearchObjects(request, ctx);
@@ -192,8 +197,6 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return ObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray());
}
-
- static readonly AsyncLocal asyncLocalSession = new ();
private async Task PutClientCutObject(PrmObjectPut args)
{
@@ -274,7 +277,9 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return tools.CalculateObjectId(largeObject.Header);
}
- currentObject.AddAttributes(args.Header!.Attributes);
+ currentObject
+ .SetSplit(null)
+ .AddAttributes(args.Header!.Attributes);
return await PutSingleObjectAsync(new PrmSingleObjectPut(currentObject, ctx));
}
@@ -421,14 +426,4 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return new SearchReader(call);
}
-
- private async ValueTask GetOrCreateSession(ISessionToken args, Context ctx)
- {
- if (args.SessionToken is null)
- {
- return await Context.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx));
- }
-
- return new Session.SessionToken().Deserialize(args.SessionToken.Token);
- }
}
diff --git a/src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs b/src/FrostFS.SDK.ClientV2/Services/Shared/ContextAccessor.cs
similarity index 100%
rename from src/FrostFS.SDK.ClientV2/Tools/ContextAccessor.cs
rename to src/FrostFS.SDK.ClientV2/Services/Shared/ContextAccessor.cs
diff --git a/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs
new file mode 100644
index 0000000..05e5723
--- /dev/null
+++ b/src/FrostFS.SDK.ClientV2/Services/Shared/SessionProvider.cs
@@ -0,0 +1,23 @@
+
+using System.Threading.Tasks;
+using FrostFS.SDK.ClientV2.Parameters;
+
+namespace FrostFS.SDK.ClientV2;
+
+internal interface ISessionProvider
+{
+ ValueTask GetOrCreateSession(ISessionToken args, Context ctx);
+}
+
+internal class SessionProvider(ClientEnvironment env)
+{
+ public async ValueTask GetOrCreateSession(ISessionToken args, Context ctx)
+ {
+ if (args.SessionToken is null)
+ {
+ return await env.Client.CreateSessionInternalAsync(new PrmSessionCreate(uint.MaxValue, ctx));
+ }
+
+ return new Session.SessionToken().Deserialize(args.SessionToken.Token);
+ }
+}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ClientV2/Tools/Object.cs b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
index 1bc66a3..0dd931e 100644
--- a/src/FrostFS.SDK.ClientV2/Tools/Object.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/Object.cs
@@ -37,7 +37,7 @@ public static class ObjectExtensions
return obj;
}
- public static FrostFsObject SetSplit(this FrostFsObject obj, Split split)
+ public static FrostFsObject SetSplit(this FrostFsObject obj, Split? split)
{
obj.Header.Split = split;
return obj;
diff --git a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
index fb7f987..f8d2c33 100644
--- a/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
+++ b/src/FrostFS.SDK.ClientV2/Tools/ObjectTools.cs
@@ -80,9 +80,7 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx)
grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
grpcHeader.Version = Context.Version.ToGrpcMessage();
- if (header.PayloadCheckSum != null)
- grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum);
- else if (payload != null)
+ if (payload != null)
grpcHeader.PayloadHash = Sha256Checksum(payload);
return grpcHeader;
diff --git a/src/FrostFS.SDK.Cryptography/UUID.cs b/src/FrostFS.SDK.Cryptography/UUID.cs
index 656acaf..fee2df9 100644
--- a/src/FrostFS.SDK.Cryptography/UUID.cs
+++ b/src/FrostFS.SDK.Cryptography/UUID.cs
@@ -7,18 +7,49 @@ public static class UUIDExtension
{
public static Guid ToUuid(this ByteString id)
{
- return Guid.Parse(BitConverter.ToString(id.ToByteArray()).Replace("-", ""));
+ var bytes = id.ToByteArray();
+
+ var orderedBytes = GetGuidBytesDirectOrder(bytes);
+
+ return new Guid(orderedBytes);
}
+ ///
+ /// Serializes Guid to binary representation in direct order bytes format
+ ///
+ ///
+ ///
public static byte[] ToBytes(this Guid id)
{
- var str = id.ToString("N");
- var len = str.Length;
- var bytes = new byte[len/2];
+ var bytes = id.ToByteArray();
- for (int i = 0; i < len; i += 2)
- bytes[i/2] = Convert.ToByte(str.Substring(i, 2), 16);
-
- return bytes;
+ var orderedBytes = GetGuidBytesDirectOrder(bytes);
+
+ return orderedBytes;
+ }
+
+ private static byte[] GetGuidBytesDirectOrder(byte[] source)
+ {
+ if (source.Length != 16)
+ throw new ArgumentException("Wrong uuid binary format");
+
+ return [
+ source[3],
+ source[2],
+ source[1],
+ source[0],
+ source[5],
+ source[4],
+ source[7],
+ source[6],
+ source[8],
+ source[9],
+ source[10],
+ source[11],
+ source[12],
+ source[13],
+ source[14],
+ source[15]
+ ];
}
}
diff --git a/src/FrostFS.SDK.ModelsV2/ClientSettings.cs b/src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/ClientSettings.cs
rename to src/FrostFS.SDK.ModelsV2/Client/ClientSettings.cs
diff --git a/src/FrostFS.SDK.ModelsV2/Context.cs b/src/FrostFS.SDK.ModelsV2/Client/Context.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/Context.cs
rename to src/FrostFS.SDK.ModelsV2/Client/Context.cs
diff --git a/src/FrostFS.SDK.ModelsV2/SignatureScheme.cs b/src/FrostFS.SDK.ModelsV2/Enums/SignatureScheme.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/SignatureScheme.cs
rename to src/FrostFS.SDK.ModelsV2/Enums/SignatureScheme.cs
diff --git a/src/FrostFS.SDK.ModelsV2/CallStatistics.cs b/src/FrostFS.SDK.ModelsV2/Misc/CallStatistics.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/CallStatistics.cs
rename to src/FrostFS.SDK.ModelsV2/Misc/CallStatistics.cs
diff --git a/src/FrostFS.SDK.ModelsV2/Misc/CheckSum.cs b/src/FrostFS.SDK.ModelsV2/Misc/CheckSum.cs
index b62654c..6b5526a 100644
--- a/src/FrostFS.SDK.ModelsV2/Misc/CheckSum.cs
+++ b/src/FrostFS.SDK.ModelsV2/Misc/CheckSum.cs
@@ -1,10 +1,11 @@
+using System;
using System.Security.Cryptography;
-using System.Text;
namespace FrostFS.SDK.ModelsV2;
public class CheckSum
{
+ // type is always Sha256
public byte[]? Hash { get; set; }
public static byte[] GetHash(byte[] content)
@@ -20,6 +21,6 @@ public class CheckSum
public override string ToString()
{
- return Encoding.UTF8.GetString(Hash);
+ return BitConverter.ToString(Hash).Replace("-", "");
}
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/Version.cs b/src/FrostFS.SDK.ModelsV2/Netmap/Version.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/Version.cs
rename to src/FrostFS.SDK.ModelsV2/Netmap/Version.cs
diff --git a/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
index 6b095a8..6ca2c22 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/FrostFsObject.cs
@@ -69,19 +69,6 @@ public class LargeObject(ContainerId container) : FrostFsObject(container)
{
private readonly SHA256 payloadHash = SHA256.Create();
- public void AppendBlock(byte[] bytes, int 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;
- return this;
- }
-
public ulong PayloadLength
{
get { return Header!.PayloadLength; }
diff --git a/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs b/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
index 5fdb54f..fbed30c 100644
--- a/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/ObjectFilter.cs
@@ -2,37 +2,112 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2;
-public class ObjectFilter
+public interface IObjectFilter
{
- private const string HeaderPrefix = "$Object:";
public ObjectMatchType MatchType { get; set; }
public string Key { get; set; }
- public string Value { get; set; }
+
+ string? GetSerializedValue();
+}
- public ObjectFilter(ObjectMatchType matchType, string key, string value)
- {
- MatchType = matchType;
- Key = key;
- Value = value;
- }
+public abstract class ObjectFilter(ObjectMatchType matchType, string key, T value) : IObjectFilter
+{
+ public ObjectMatchType MatchType { get; set; } = matchType;
+ public string Key { get; set; } = key;
- public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId)
- {
- return new ObjectFilter(matchType, HeaderPrefix + "objectID", objectId.Value);
- }
+ public T Value { get; set; } = value;
- public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId)
+ public string? GetSerializedValue()
{
- return new ObjectFilter(matchType, HeaderPrefix + "ownerID", ownerId.Value);
- }
-
- public static ObjectFilter RootFilter()
- {
- return new ObjectFilter(ObjectMatchType.Unspecified, HeaderPrefix + "ROOT", "");
- }
-
- public static ObjectFilter VersionFilter(ObjectMatchType matchType, Version version)
- {
- return new ObjectFilter(matchType, HeaderPrefix + "version", version.ToString());
+ return Value?.ToString();
}
}
+
+///
+/// Creates filter to search by Attribute
+///
+/// Match type
+/// Attribute key
+/// Attribute value
+public class FilterByAttribute(ObjectMatchType matchType, string key, string value) : ObjectFilter(matchType, key, value) { }
+
+///
+/// Creates filter to search by ObjectId
+///
+/// Match type
+/// ObjectId
+public class FilterByObjectId(ObjectMatchType matchType, ObjectId objectId) : ObjectFilter(matchType, Constants.FilterHeaderObjectID, objectId) { }
+
+///
+/// Creates filter to search by OwnerId
+///
+/// Match type
+/// ObjectId
+public class FilterByOwnerId(ObjectMatchType matchType, OwnerId ownerId) : ObjectFilter(matchType, Constants.FilterHeaderOwnerID, ownerId) {}
+
+///
+/// Creates filter to search by Version
+///
+/// Match type
+/// Version
+public class FilterByVersion(ObjectMatchType matchType, Version version) : ObjectFilter(matchType, Constants.FilterHeaderVersion, version) {}
+
+///
+/// Creates filter to search by ContainerId
+///
+/// Match type
+/// ContainerId
+public class FilterByContainerId(ObjectMatchType matchType, ContainerId containerId) : ObjectFilter(matchType, Constants.FilterHeaderContainerID, containerId) {}
+
+///
+/// Creates filter to search by creation Epoch
+///
+/// Match type
+/// Creation Epoch
+public class FilterByEpoch(ObjectMatchType matchType, ulong epoch) : ObjectFilter(matchType, Constants.FilterHeaderCreationEpoch, epoch) {}
+
+///
+/// Creates filter to search by Payload Length
+///
+/// Match type
+/// Payload Length
+public class FilterByPayloadLength(ObjectMatchType matchType, ulong payloadLength) : ObjectFilter(matchType, Constants.FilterHeaderPayloadLength, payloadLength) {}
+
+///
+/// Creates filter to search by Payload Hash
+///
+/// Match type
+/// Payload Hash
+public class FilterByPayloadHash(ObjectMatchType matchType, CheckSum payloadHash) : ObjectFilter(matchType, Constants.FilterHeaderPayloadHash, payloadHash) {}
+
+///
+/// Creates filter to search by Parent
+///
+/// Match type
+/// Parent
+public class FilterByParent(ObjectMatchType matchType, ObjectId parentId) : ObjectFilter(matchType, Constants.FilterHeaderParent, parentId) {}
+
+///
+/// Creates filter to search by SplitId
+///
+/// Match type
+/// SplitId
+public class FilterBySplitId(ObjectMatchType matchType, SplitId splitId) : ObjectFilter(matchType, Constants.FilterHeaderSplitID, splitId) {}
+
+///
+/// Creates filter to search by Payload Hash
+///
+/// Match type
+/// Payload Hash
+public class FilterByECParent(ObjectMatchType matchType, ObjectId ecParentId) : ObjectFilter(matchType, Constants.FilterHeaderECParent, ecParentId) {}
+
+///
+/// Creates filter to search Root objects
+///
+public class FilterByRootObject() : ObjectFilter(ObjectMatchType.Unspecified, Constants.FilterHeaderRoot, string.Empty) {}
+
+///
+/// Creates filter to search objects that are physically stored on the server
+/// (ObjectMatchType.Unspecified, Constants.FilterHeaderPhy, string.Empty) {}
+
diff --git a/src/FrostFS.SDK.ModelsV2/OwnerId.cs b/src/FrostFS.SDK.ModelsV2/Object/OwnerId.cs
similarity index 83%
rename from src/FrostFS.SDK.ModelsV2/OwnerId.cs
rename to src/FrostFS.SDK.ModelsV2/Object/OwnerId.cs
index 8746572..b930bb7 100644
--- a/src/FrostFS.SDK.ModelsV2/OwnerId.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/OwnerId.cs
@@ -17,4 +17,9 @@ public class OwnerId(string id)
{
return Base58.Decode(Value);
}
+
+ public override string ToString()
+ {
+ return Value;
+ }
}
\ No newline at end of file
diff --git a/src/FrostFS.SDK.ModelsV2/SplitId.cs b/src/FrostFS.SDK.ModelsV2/Object/SplitId.cs
similarity index 89%
rename from src/FrostFS.SDK.ModelsV2/SplitId.cs
rename to src/FrostFS.SDK.ModelsV2/Object/SplitId.cs
index eddbd14..111f632 100644
--- a/src/FrostFS.SDK.ModelsV2/SplitId.cs
+++ b/src/FrostFS.SDK.ModelsV2/Object/SplitId.cs
@@ -1,4 +1,5 @@
-using System;
+using FrostFS.SDK.Cryptography;
+using System;
namespace FrostFS.SDK.ModelsV2;
@@ -10,6 +11,7 @@ public class SplitId
{
this.id = Guid.NewGuid();
}
+
public SplitId(Guid guid)
{
this.id = guid;
@@ -28,7 +30,7 @@ public class SplitId
public static SplitId CreateFromBinary(byte[] binaryData)
{
return new SplitId(binaryData);
- }
+ }
public static SplitId CreateFromString(string stringData)
{
@@ -45,6 +47,6 @@ public class SplitId
if (this.id == Guid.Empty)
return null;
- return this.id.ToByteArray();
+ return this.id.ToBytes();
}
}
diff --git a/src/FrostFS.SDK.ModelsV2/Splitter.cs b/src/FrostFS.SDK.ModelsV2/Object/Splitter.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/Splitter.cs
rename to src/FrostFS.SDK.ModelsV2/Object/Splitter.cs
diff --git a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs b/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
deleted file mode 100644
index 0a8d64c..0000000
--- a/src/FrostFS.SDK.ModelsV2/PutObjectParameters.cs
+++ /dev/null
@@ -1 +0,0 @@
-namespace FrostFS.SDK.ModelsV2;
diff --git a/src/FrostFS.SDK.ModelsV2/MetaHeader.cs b/src/FrostFS.SDK.ModelsV2/Response/MetaHeader.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/MetaHeader.cs
rename to src/FrostFS.SDK.ModelsV2/Response/MetaHeader.cs
diff --git a/src/FrostFS.SDK.ModelsV2/Signature.cs b/src/FrostFS.SDK.ModelsV2/Response/Signature.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/Signature.cs
rename to src/FrostFS.SDK.ModelsV2/Response/Signature.cs
diff --git a/src/FrostFS.SDK.ModelsV2/Status.cs b/src/FrostFS.SDK.ModelsV2/Response/Status.cs
similarity index 100%
rename from src/FrostFS.SDK.ModelsV2/Status.cs
rename to src/FrostFS.SDK.ModelsV2/Response/Status.cs
diff --git a/src/FrostFS.SDK.Tests/SmokeTests.cs b/src/FrostFS.SDK.Tests/SmokeTests.cs
index 406e948..f827677 100644
--- a/src/FrostFS.SDK.Tests/SmokeTests.cs
+++ b/src/FrostFS.SDK.Tests/SmokeTests.cs
@@ -21,7 +21,7 @@ public class SmokeTests
private static readonly PrmWait lightWait = new (100, 1);
private readonly string key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
private readonly string url = "http://172.23.32.4:8080";
-
+
[Fact]
public async void NetworkMapTest()
{
@@ -115,7 +115,7 @@ public class SmokeTests
{
Header = new ObjectHeader(
containerId: containerId,
- type: ObjectType.Regular,
+ type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes),
ClientCut = false,
@@ -140,6 +140,88 @@ public class SmokeTests
await Cleanup(client);
}
+ [Fact]
+ public async void FilterTest()
+ {
+ using var client = Client.GetInstance(GetOptions(this.key, this.url));
+
+ await Cleanup(client);
+
+ var createContainerParam = new PrmContainerCreate(
+ new ModelsV2.Container(BasicAcl.PublicRW, new PlacementPolicy(true, new Replica(1))))
+ {
+ WaitParams = lightWait
+ };
+
+ var containerId = await client.CreateContainerAsync(createContainerParam);
+
+ var bytes = new byte[] { 1, 2, 3 };
+
+ var ParentHeader = new ObjectHeader(
+ containerId: containerId,
+ type: ModelsV2.Enums.ObjectType.Regular)
+ {
+ PayloadLength = 3
+ };
+
+ var param = new PrmObjectPut
+ {
+ Header = new ObjectHeader(
+ containerId: containerId,
+ type: ModelsV2.Enums.ObjectType.Regular,
+ new ObjectAttribute("fileName", "test"))
+ {
+ Split = new Split(),
+ },
+ Payload = new MemoryStream(bytes),
+ ClientCut = false
+ };
+
+ var objectId = await client.PutObjectAsync(param);
+
+ var head = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objectId));
+
+ var ecdsaKey = this.key.LoadWif();
+
+ var networkInfo = await client.GetNetmapSnapshotAsync();
+
+ await CheckFilter(client, containerId, new FilterByContainerId(ObjectMatchType.Equals, containerId));
+
+ await CheckFilter(client, containerId, new FilterByOwnerId(ObjectMatchType.Equals, OwnerId.FromKey(ecdsaKey)));
+
+ await CheckFilter(client, containerId, new FilterBySplitId(ObjectMatchType.Equals, param.Header.Split.SplitId));
+
+ await CheckFilter(client, containerId, new FilterByAttribute(ObjectMatchType.Equals, "fileName", "test"));
+
+ await CheckFilter(client, containerId, new FilterByObjectId(ObjectMatchType.Equals, objectId));
+
+ await CheckFilter(client, containerId, new FilterByVersion(ObjectMatchType.Equals, networkInfo.NodeInfoCollection[0].Version));
+
+ await CheckFilter(client, containerId, new FilterByEpoch(ObjectMatchType.Equals, networkInfo.Epoch));
+
+ await CheckFilter(client, containerId, new FilterByPayloadLength(ObjectMatchType.Equals, 3));
+
+ var checkSum = CheckSum.CreateCheckSum(bytes);
+
+ await CheckFilter(client, containerId, new FilterByPayloadHash(ObjectMatchType.Equals, checkSum));
+
+ await CheckFilter(client, containerId, new FilterByPhysicallyStored());
+ }
+
+ private static async Task CheckFilter(IFrostFSClient client, ContainerId containerId, IObjectFilter filter)
+ {
+ var resultObjectsCount = 0;
+
+ PrmObjectSearch searchParam = new(containerId) { Filters = [filter] };
+
+ await foreach (var objId in client.SearchObjectsAsync(searchParam))
+ {
+ resultObjectsCount++;
+ }
+
+ Assert.True(1 == resultObjectsCount, $"Filter for {filter.Key} doesn't work");
+ }
+
[Theory]
[InlineData(1)]
[InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB
@@ -153,7 +235,7 @@ public class SmokeTests
bool callbackInvoked = false;
var ctx = new Context
{
- Timeout = TimeSpan.FromSeconds(20),
+ // Timeout = TimeSpan.FromSeconds(20),
Callback = new((CallStatistics cs) =>
{
callbackInvoked = true;
@@ -179,7 +261,7 @@ public class SmokeTests
{
Header = new ObjectHeader(
containerId: containerId,
- type: ObjectType.Regular,
+ type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes),
ClientCut = false,
@@ -191,7 +273,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param);
- var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+ var filter = new FilterByAttribute(ObjectMatchType.Equals, "fileName", "test");
bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter] }))
@@ -263,7 +345,7 @@ public class SmokeTests
{
Header = new ObjectHeader(
containerId: containerId,
- type: ObjectType.Regular,
+ type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes),
ClientCut = false,
@@ -276,7 +358,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param);
- var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+ var filter = new FilterByAttribute(ObjectMatchType.Equals, "fileName", "test");
bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter], SessionToken = token }))
@@ -319,6 +401,7 @@ public class SmokeTests
[InlineData(64 * 1024 * 1024 - 1)]
[InlineData(64 * 1024 * 1024 + 1)]
[InlineData(2 * 64 * 1024 * 1024 + 256)]
+ [InlineData(200)]
public async void ClientCutScenarioTest(int objectSize)
{
using var client = Client.GetInstance(GetOptions(this.key, this.url));
@@ -348,7 +431,7 @@ public class SmokeTests
{
Header = new ObjectHeader(
containerId: containerId,
- type: ObjectType.Regular,
+ type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes),
ClientCut = true
@@ -356,7 +439,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param);
- var filter = new ObjectFilter(ObjectMatchType.Equals, "fileName", "test");
+ var filter = new FilterByAttribute(ObjectMatchType.Equals, "fileName", "test");
bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, filter)))
@@ -385,6 +468,8 @@ public class SmokeTests
Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
+ await CheckFilter(client, containerId, new FilterByRootObject());
+
await Cleanup(client);
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
@@ -392,14 +477,14 @@ public class SmokeTests
IAsyncEnumerator? enumerator = null;
do
{
- if (deadline <= DateTime.UtcNow)
- {
- Assert.Fail("Containers exist");
- break;
- }
+ if (deadline <= DateTime.UtcNow)
+ {
+ Assert.Fail("Containers exist");
+ break;
+ }
- enumerator = client.ListContainersAsync().GetAsyncEnumerator();
- await Task.Delay(500);
+ enumerator = client.ListContainersAsync().GetAsyncEnumerator();
+ await Task.Delay(500);
}
while (await enumerator!.MoveNextAsync());
}
--
2.45.2