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