[#19] Client: Use specific classes for searching #20

Merged
PavelGrossSpb merged 1 commit from PavelGrossSpb/frostfs-sdk-csharp:misc/searching into master 2024-07-29 09:04:03 +00:00
27 changed files with 320 additions and 123 deletions
Showing only changes of commit 35fe791406 - Show all commits

View file

@ -7,7 +7,7 @@ namespace FrostFS.SDK.ClientV2.Mappers.GRPC;
public static class ObjectFilterMapper 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 var objMatchTypeName = filter.MatchType switch
{ {
@ -24,7 +24,7 @@ public static class ObjectFilterMapper
{ {
MatchType = objMatchTypeName, MatchType = objMatchTypeName,
Key = filter.Key, Key = filter.Key,
Value = filter.Value Value = filter.GetSerializedValue()
}; };
} }
} }

View file

@ -74,7 +74,7 @@ public static class ObjectHeaderMapper
if (header.Split != null) 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(), Parent = header.Split.Parent?.ToModel(),
ParentHeader = header.Split.ParentHeader?.ToModel(), ParentHeader = header.Split.ParentHeader?.ToModel(),

View file

@ -4,8 +4,6 @@ namespace FrostFS.SDK.ClientV2.Parameters;
public sealed class PrmContainerGetAll() : IContext public sealed class PrmContainerGetAll() : IContext
{ {
public string SessionToken { get; set; } = string.Empty;
/// <summary> /// <summary>
/// FrostFS request X-Headers /// FrostFS request X-Headers
/// </summary> /// </summary>

View file

@ -3,15 +3,19 @@ using System.Collections.Specialized;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Parameters; 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
{ {
/// <summary>
/// Defines container for the search
/// </summary>
/// <value></value>
public ContainerId ContainerId { get; set; } = containerId; public ContainerId ContainerId { get; set; } = containerId;
/// <summary> /// <summary>
/// Defines the search criteria /// Defines the search criteria
/// </summary> /// </summary>
/// <value>Collection of filters</value> /// <value>Collection of filters</value>
public IEnumerable<ObjectFilter> Filters { get; set; } = filters; public IEnumerable<IObjectFilter> Filters { get; set; } = filters;
/// <summary> /// <summary>
/// FrostFS request X-Headers /// FrostFS request X-Headers

View file

@ -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.SDK.ClientV2.Mappers.GRPC.Netmap;
using FrostFS.Container; using FrostFS.Container;
using FrostFS.SDK.ClientV2.Mappers.GRPC; using FrostFS.SDK.ClientV2.Mappers.GRPC;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using System.Collections.Generic;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.Refs;
using System;
using FrostFS.SDK.ClientV2.Parameters; using FrostFS.SDK.ClientV2.Parameters;
using System.Collections.Specialized; using FrostFS.Refs;
namespace FrostFS.SDK.ClientV2; 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<ModelsV2.Container> GetContainerAsync(PrmContainerGet args) internal async Task<ModelsV2.Container> GetContainerAsync(PrmContainerGet args)
{ {
GetRequest request = GetContainerRequest(args.ContainerId.ToGrpcMessage(), args.XHeaders); 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); Verifier.CheckResponse(response);
@ -49,7 +41,7 @@ internal class ContainerServiceProvider : ContextAccessor
request.AddMetaHeader(args.XHeaders); request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key); 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); Verifier.CheckResponse(response);
@ -78,7 +70,7 @@ internal class ContainerServiceProvider : ContextAccessor
request.AddMetaHeader(args.XHeaders); request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key); 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); Verifier.CheckResponse(response);
@ -100,9 +92,10 @@ internal class ContainerServiceProvider : ContextAccessor
}; };
request.AddMetaHeader(args.XHeaders); request.AddMetaHeader(args.XHeaders);
request.Sign(Context.Key); 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); await WaitForContainer(WaitExpects.Removed, request.Body.ContainerId, args.WaitParams, ctx);
@ -137,7 +130,7 @@ internal class ContainerServiceProvider : ContextAccessor
async Task action() 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); Verifier.CheckResponse(response);
} }
@ -161,6 +154,9 @@ internal class ContainerServiceProvider : ContextAccessor
if (expect == WaitExpects.Exists) if (expect == WaitExpects.Exists)
return; return;
if (DateTime.UtcNow >= deadLine)
throw new TimeoutException();
await Task.Delay(waitParams.PollInterval); await Task.Delay(waitParams.PollInterval);
} }
catch (ResponseException ex) catch (ResponseException ex)
@ -179,5 +175,3 @@ internal class ContainerServiceProvider : ContextAccessor
} }
} }
} }

View file

@ -12,14 +12,19 @@ using FrostFS.SDK.Cryptography;
using FrostFS.Session; using FrostFS.Session;
using FrostFS.SDK.ModelsV2; using FrostFS.SDK.ModelsV2;
using FrostFS.SDK.ClientV2.Extensions; using FrostFS.SDK.ClientV2.Extensions;
using System.Threading;
using FrostFS.SDK.ClientV2.Parameters; using FrostFS.SDK.ClientV2.Parameters;
namespace FrostFS.SDK.ClientV2; 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 ObjectTools tools = new(ctx);
readonly SessionProvider sessions = new (ctx);
public async ValueTask<Session.SessionToken> GetOrCreateSession(ISessionToken args, Context ctx)
{
return await sessions.GetOrCreateSession(args, ctx);
}
internal async Task<ObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args) internal async Task<ObjectHeader> GetObjectHeadAsync(PrmObjectHeadGet args)
{ {
@ -193,8 +198,6 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return ObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray()); return ObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray());
} }
static readonly AsyncLocal<Session.SessionToken> asyncLocalSession = new ();
private async Task<ObjectId> PutClientCutObject(PrmObjectPut args) private async Task<ObjectId> PutClientCutObject(PrmObjectPut args)
{ {
var ctx = args.Context!; var ctx = args.Context!;
@ -274,7 +277,9 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return tools.CalculateObjectId(largeObject.Header); return tools.CalculateObjectId(largeObject.Header);
} }
currentObject.AddAttributes(args.Header!.Attributes); currentObject
.SetSplit(null)
.AddAttributes(args.Header!.Attributes);
return await PutSingleObjectAsync(new PrmSingleObjectPut(currentObject, ctx)); return await PutSingleObjectAsync(new PrmSingleObjectPut(currentObject, ctx));
} }
@ -421,14 +426,4 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return new SearchReader(call); return new SearchReader(call);
} }
private async ValueTask<Session.SessionToken> 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);
}
} }

View file

@ -0,0 +1,23 @@
using System.Threading.Tasks;
using FrostFS.SDK.ClientV2.Parameters;
namespace FrostFS.SDK.ClientV2;
internal interface ISessionProvider
{
ValueTask<Session.SessionToken> GetOrCreateSession(ISessionToken args, Context ctx);
}
internal class SessionProvider(ClientEnvironment env)
{
public async ValueTask<Session.SessionToken> 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);
}
}

View file

@ -37,7 +37,7 @@ public static class ObjectExtensions
return obj; return obj;
} }
public static FrostFsObject SetSplit(this FrostFsObject obj, Split split) public static FrostFsObject SetSplit(this FrostFsObject obj, Split? split)
{ {
obj.Header.Split = split; obj.Header.Split = split;
return obj; return obj;

View file

@ -80,9 +80,7 @@ internal class ObjectTools(ClientEnvironment ctx) : ContextAccessor (ctx)
grpcHeader.OwnerId = Context.Owner.ToGrpcMessage(); grpcHeader.OwnerId = Context.Owner.ToGrpcMessage();
grpcHeader.Version = Context.Version.ToGrpcMessage(); grpcHeader.Version = Context.Version.ToGrpcMessage();
if (header.PayloadCheckSum != null) if (payload != null)
grpcHeader.PayloadHash = Sha256Checksum(header.PayloadCheckSum);
else if (payload != null)
grpcHeader.PayloadHash = Sha256Checksum(payload); grpcHeader.PayloadHash = Sha256Checksum(payload);
return grpcHeader; return grpcHeader;

View file

@ -7,18 +7,49 @@ public static class UUIDExtension
{ {
public static Guid ToUuid(this ByteString id) 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);
} }
/// <summary>
/// Serializes Guid to binary representation in direct order bytes format
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static byte[] ToBytes(this Guid id) public static byte[] ToBytes(this Guid id)
{ {
var str = id.ToString("N"); var bytes = id.ToByteArray();
var len = str.Length;
var bytes = new byte[len/2];
for (int i = 0; i < len; i += 2) var orderedBytes = GetGuidBytesDirectOrder(bytes);
bytes[i/2] = Convert.ToByte(str.Substring(i, 2), 16);
return 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]
];
} }
} }

View file

@ -1,10 +1,11 @@
using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class CheckSum public class CheckSum
{ {
// type is always Sha256
public byte[]? Hash { get; set; } public byte[]? Hash { get; set; }
public static byte[] GetHash(byte[] content) public static byte[] GetHash(byte[] content)
@ -20,6 +21,6 @@ public class CheckSum
public override string ToString() public override string ToString()
{ {
return Encoding.UTF8.GetString(Hash); return BitConverter.ToString(Hash).Replace("-", "");
} }
} }

View file

@ -69,19 +69,6 @@ public class LargeObject(ContainerId container) : FrostFsObject(container)
{ {
private readonly SHA256 payloadHash = SHA256.Create(); 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 public ulong PayloadLength
{ {
get { return Header!.PayloadLength; } get { return Header!.PayloadLength; }

View file

@ -2,37 +2,112 @@ using FrostFS.SDK.ModelsV2.Enums;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
public class ObjectFilter public interface IObjectFilter
{ {
private const string HeaderPrefix = "$Object:";
public ObjectMatchType MatchType { get; set; } public ObjectMatchType MatchType { get; set; }
public string Key { get; set; } public string Key { get; set; }
public string Value { get; set; }
public ObjectFilter(ObjectMatchType matchType, string key, string value) string? GetSerializedValue();
{ }
MatchType = matchType;
Key = key;
Value = value;
}
public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId) public abstract class ObjectFilter<T>(ObjectMatchType matchType, string key, T value) : IObjectFilter
{ {
return new ObjectFilter(matchType, HeaderPrefix + "objectID", objectId.Value); public ObjectMatchType MatchType { get; set; } = matchType;
} public string Key { get; set; } = key;
public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId) public T Value { get; set; } = value;
{
return new ObjectFilter(matchType, HeaderPrefix + "ownerID", ownerId.Value);
}
public static ObjectFilter RootFilter() public string? GetSerializedValue()
{ {
return new ObjectFilter(ObjectMatchType.Unspecified, HeaderPrefix + "ROOT", ""); return Value?.ToString();
}
public static ObjectFilter VersionFilter(ObjectMatchType matchType, Version version)
{
return new ObjectFilter(matchType, HeaderPrefix + "version", version.ToString());
} }
} }
/// <summary>
/// Creates filter to search by Attribute
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="key">Attribute key</param>
/// <param name="value">Attribute value</param>
public class FilterByAttribute(ObjectMatchType matchType, string key, string value) : ObjectFilter<string>(matchType, key, value) { }
/// <summary>
/// Creates filter to search by ObjectId
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="objectId">ObjectId</param>
public class FilterByObjectId(ObjectMatchType matchType, ObjectId objectId) : ObjectFilter<ObjectId>(matchType, Constants.FilterHeaderObjectID, objectId) { }
/// <summary>
/// Creates filter to search by OwnerId
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="ownerId">ObjectId</param>
public class FilterByOwnerId(ObjectMatchType matchType, OwnerId ownerId) : ObjectFilter<OwnerId>(matchType, Constants.FilterHeaderOwnerID, ownerId) {}
/// <summary>
/// Creates filter to search by Version
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="version">Version</param>
public class FilterByVersion(ObjectMatchType matchType, Version version) : ObjectFilter<Version>(matchType, Constants.FilterHeaderVersion, version) {}
/// <summary>
/// Creates filter to search by ContainerId
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="containerId">ContainerId</param>
public class FilterByContainerId(ObjectMatchType matchType, ContainerId containerId) : ObjectFilter<ContainerId>(matchType, Constants.FilterHeaderContainerID, containerId) {}
/// <summary>
/// Creates filter to search by creation Epoch
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="epoch">Creation Epoch</param>
public class FilterByEpoch(ObjectMatchType matchType, ulong epoch) : ObjectFilter<ulong>(matchType, Constants.FilterHeaderCreationEpoch, epoch) {}
/// <summary>
/// Creates filter to search by Payload Length
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="payloadLength">Payload Length</param>
public class FilterByPayloadLength(ObjectMatchType matchType, ulong payloadLength) : ObjectFilter<ulong>(matchType, Constants.FilterHeaderPayloadLength, payloadLength) {}
/// <summary>
/// Creates filter to search by Payload Hash
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="payloadHash">Payload Hash</param>
public class FilterByPayloadHash(ObjectMatchType matchType, CheckSum payloadHash) : ObjectFilter<CheckSum>(matchType, Constants.FilterHeaderPayloadHash, payloadHash) {}
/// <summary>
/// Creates filter to search by Parent
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="parentId">Parent</param>
public class FilterByParent(ObjectMatchType matchType, ObjectId parentId) : ObjectFilter<ObjectId>(matchType, Constants.FilterHeaderParent, parentId) {}
/// <summary>
/// Creates filter to search by SplitId
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="splitId">SplitId</param>
public class FilterBySplitId(ObjectMatchType matchType, SplitId splitId) : ObjectFilter<SplitId>(matchType, Constants.FilterHeaderSplitID, splitId) {}
/// <summary>
/// Creates filter to search by Payload Hash
/// </summary>
/// <param name="matchType">Match type</param>
/// <param name="ecParentId">Payload Hash</param>
public class FilterByECParent(ObjectMatchType matchType, ObjectId ecParentId) : ObjectFilter<ObjectId>(matchType, Constants.FilterHeaderECParent, ecParentId) {}
/// <summary>
/// Creates filter to search Root objects
/// </summary>
public class FilterByRootObject() : ObjectFilter<string>(ObjectMatchType.Unspecified, Constants.FilterHeaderRoot, string.Empty) {}
/// <summary>
/// Creates filter to search objects that are physically stored on the server
/// </summary
public class FilterByPhysicallyStored() : ObjectFilter<string>(ObjectMatchType.Unspecified, Constants.FilterHeaderPhy, string.Empty) {}

View file

@ -17,4 +17,9 @@ public class OwnerId(string id)
{ {
return Base58.Decode(Value); return Base58.Decode(Value);
} }
public override string ToString()
{
return Value;
}
} }

View file

@ -1,4 +1,5 @@
using System; using FrostFS.SDK.Cryptography;
using System;
namespace FrostFS.SDK.ModelsV2; namespace FrostFS.SDK.ModelsV2;
@ -10,6 +11,7 @@ public class SplitId
{ {
this.id = Guid.NewGuid(); this.id = Guid.NewGuid();
} }
public SplitId(Guid guid) public SplitId(Guid guid)
{ {
this.id = guid; this.id = guid;
@ -28,7 +30,7 @@ public class SplitId
public static SplitId CreateFromBinary(byte[] binaryData) public static SplitId CreateFromBinary(byte[] binaryData)
{ {
return new SplitId(binaryData); return new SplitId(binaryData);
} }
public static SplitId CreateFromString(string stringData) public static SplitId CreateFromString(string stringData)
{ {
@ -45,6 +47,6 @@ public class SplitId
if (this.id == Guid.Empty) if (this.id == Guid.Empty)
return null; return null;
return this.id.ToByteArray(); return this.id.ToBytes();
} }
} }

View file

@ -1 +0,0 @@
namespace FrostFS.SDK.ModelsV2;

View file

@ -115,7 +115,7 @@ public class SmokeTests
{ {
Header = new ObjectHeader( Header = new ObjectHeader(
containerId: containerId, containerId: containerId,
type: ObjectType.Regular, type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")), new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes), Payload = new MemoryStream(bytes),
ClientCut = false, ClientCut = false,
@ -140,6 +140,88 @@ public class SmokeTests
await Cleanup(client); 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] [Theory]
[InlineData(1)] [InlineData(1)]
[InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB [InlineData(3 * 1024 * 1024)] // exactly one chunk size - 3MB
@ -153,7 +235,7 @@ public class SmokeTests
bool callbackInvoked = false; bool callbackInvoked = false;
var ctx = new Context var ctx = new Context
{ {
Timeout = TimeSpan.FromSeconds(20), // Timeout = TimeSpan.FromSeconds(20),
Callback = new((CallStatistics cs) => Callback = new((CallStatistics cs) =>
{ {
callbackInvoked = true; callbackInvoked = true;
@ -179,7 +261,7 @@ public class SmokeTests
{ {
Header = new ObjectHeader( Header = new ObjectHeader(
containerId: containerId, containerId: containerId,
type: ObjectType.Regular, type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")), new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes), Payload = new MemoryStream(bytes),
ClientCut = false, ClientCut = false,
@ -191,7 +273,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param); 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; bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter] })) await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter] }))
@ -263,7 +345,7 @@ public class SmokeTests
{ {
Header = new ObjectHeader( Header = new ObjectHeader(
containerId: containerId, containerId: containerId,
type: ObjectType.Regular, type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")), new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes), Payload = new MemoryStream(bytes),
ClientCut = false, ClientCut = false,
@ -276,7 +358,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param); 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; bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter], SessionToken = token })) 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(64 * 1024 * 1024 + 1)] [InlineData(64 * 1024 * 1024 + 1)]
[InlineData(2 * 64 * 1024 * 1024 + 256)] [InlineData(2 * 64 * 1024 * 1024 + 256)]
[InlineData(200)]
public async void ClientCutScenarioTest(int objectSize) public async void ClientCutScenarioTest(int objectSize)
{ {
using var client = Client.GetInstance(GetOptions(this.key, this.url)); using var client = Client.GetInstance(GetOptions(this.key, this.url));
@ -348,7 +431,7 @@ public class SmokeTests
{ {
Header = new ObjectHeader( Header = new ObjectHeader(
containerId: containerId, containerId: containerId,
type: ObjectType.Regular, type: ModelsV2.Enums.ObjectType.Regular,
new ObjectAttribute("fileName", "test")), new ObjectAttribute("fileName", "test")),
Payload = new MemoryStream(bytes), Payload = new MemoryStream(bytes),
ClientCut = true ClientCut = true
@ -356,7 +439,7 @@ public class SmokeTests
var objectId = await client.PutObjectAsync(param); 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; bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, filter))) 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)); Assert.Equal(MD5.HashData(bytes), MD5.HashData(downloadedBytes));
await CheckFilter(client, containerId, new FilterByRootObject());
await Cleanup(client); await Cleanup(client);
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
@ -392,14 +477,14 @@ public class SmokeTests
IAsyncEnumerator<ContainerId>? enumerator = null; IAsyncEnumerator<ContainerId>? enumerator = null;
do do
{ {
if (deadline <= DateTime.UtcNow) if (deadline <= DateTime.UtcNow)
{ {
Assert.Fail("Containers exist"); Assert.Fail("Containers exist");
break; break;
} }
enumerator = client.ListContainersAsync().GetAsyncEnumerator(); enumerator = client.ListContainersAsync().GetAsyncEnumerator();
await Task.Delay(500); await Task.Delay(500);
} }
while (await enumerator!.MoveNextAsync()); while (await enumerator!.MoveNextAsync());
} }