Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement SetRemoveElementAsync and SetRemoveElementsAsync #121

Merged
merged 2 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions IncubatingIntegrationTest/SetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,177 @@ public async Task SetAddBatchAsync_ElementsAreStringEnumerable_RefreshTtl()
Assert.Contains(element, set);
}

[Theory]
[InlineData(null, "my-set", new byte[] { 0x00 })]
[InlineData("cache", null, new byte[] { 0x00 })]
[InlineData("cache", "my-set", null)]
malandis marked this conversation as resolved.
Show resolved Hide resolved
public async Task SetRemoveElementAsync_NullChecksByteArray_ThrowsException(string cacheName, string setName, byte[] element)
{
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementAsync(cacheName, setName, element));
}

[Fact]
public async Task SetRemoveElementAsync_ElementIsByteArray_HappyPath()
{
var setName = Utils.NewGuidString();
var element = Utils.NewGuidByteArray();

await client.SetAddAsync(cacheName, setName, element, false);

// Remove element that is not there -- no-op
await client.SetRemoveElementAsync(cacheName, setName, Utils.NewGuidByteArray());
// Fetch the whole set and make sure response has element we expect
var fetchResponse = await client.SetFetchAsync(cacheName, setName);
malandis marked this conversation as resolved.
Show resolved Hide resolved
Assert.Equal(CacheGetStatus.HIT, fetchResponse.Status);
var set = fetchResponse.ByteArraySet;
Assert.Single(set);
Assert.Contains(element, set);

// Remove element
await client.SetRemoveElementAsync(cacheName, setName, element);
fetchResponse = await client.SetFetchAsync(cacheName, setName);
Assert.Equal(CacheGetStatus.MISS, fetchResponse.Status);
}

[Fact]
public async Task SetRemoveElementAsync_SetIsMissingElementIsByteArray_Noop()
{
var setName = Utils.NewGuidString();
var element = Utils.NewGuidString();

// Pre-condition: set is missing
Assert.Equal(CacheGetStatus.MISS, (await client.SetFetchAsync(cacheName, setName)).Status);

// Remove element that is not there -- no-op
await client.SetRemoveElementAsync(cacheName, setName, Utils.NewGuidByteArray());

// Post-condition: set is still missing
Assert.Equal(CacheGetStatus.MISS, (await client.SetFetchAsync(cacheName, setName)).Status);
}

[Theory]
[InlineData(null, "my-set", "my-element")]
[InlineData("cache", null, "my-element")]
[InlineData("cache", "my-set", null)]
public async Task SetRemoveElementAsync_NullChecksString_ThrowsException(string cacheName, string setName, string element)
{
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementAsync(cacheName, setName, element));
}

[Fact]
public async Task SetRemoveElementAsync_ElementIsString_HappyPath()
{
var setName = Utils.NewGuidString();
var element = Utils.NewGuidString();

await client.SetAddAsync(cacheName, setName, element, false);

// Remove element that is not there -- no-op
await client.SetRemoveElementAsync(cacheName, setName, Utils.NewGuidString());
var fetchResponse = await client.SetFetchAsync(cacheName, setName);
Assert.Equal(CacheGetStatus.HIT, fetchResponse.Status);
var set = fetchResponse.StringSet();
Assert.Single(set);
Assert.Contains(element, set);

// Remove element
await client.SetRemoveElementAsync(cacheName, setName, element);
fetchResponse = await client.SetFetchAsync(cacheName, setName);
Assert.Equal(CacheGetStatus.MISS, fetchResponse.Status);
}

[Fact]
public async Task SetRemoveElementAsync_SetIsMissingElementIsString_Noop()
{
var setName = Utils.NewGuidString();
var element = Utils.NewGuidString();

// Pre-condition: set is missing
Assert.Equal(CacheGetStatus.MISS, (await client.SetFetchAsync(cacheName, setName)).Status);

// Remove element that is not there -- no-op
await client.SetRemoveElementAsync(cacheName, setName, Utils.NewGuidString());

// Post-condition: set is still missing
Assert.Equal(CacheGetStatus.MISS, (await client.SetFetchAsync(cacheName, setName)).Status);
}

[Fact]
public async Task SetRemoveElementsAsync_NullChecksElementsAreByteArray_ThrowsException()
{
var setName = Utils.NewGuidString();
var testData = new byte[][][] { new byte[][] { Utils.NewGuidByteArray(), Utils.NewGuidByteArray() }, new byte[][] { Utils.NewGuidByteArray(), null! } };

await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(null!, setName, testData[0]));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, null!, testData[0]));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, (byte[][])null!));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, testData[1]));
eaddingtonwhite marked this conversation as resolved.
Show resolved Hide resolved

var fieldsList = new List<byte[]>(testData[0]);
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(null!, setName, fieldsList));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, null!, fieldsList));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, (List<byte[]>)null!));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, new List<byte[]>(testData[1])));
}

[Fact]
public async Task SetRemoveElementsAsync_FieldsAreByteArray_HappyPath()
{
var setName = Utils.NewGuidString();
var elements = new byte[][] { Utils.NewGuidByteArray(), Utils.NewGuidByteArray() };
var otherElement = Utils.NewGuidByteArray();

// Test enumerable
await client.SetAddAsync(cacheName, setName, elements[0], false);
await client.SetAddAsync(cacheName, setName, elements[1], false);
await client.SetAddAsync(cacheName, setName, otherElement, false);

var elementsList = new List<byte[]>(elements);
await client.SetRemoveElementsAsync(cacheName, setName, elementsList);
var fetchResponse = await client.SetFetchAsync(cacheName, setName);
Assert.Equal(CacheGetStatus.HIT, fetchResponse.Status);
Assert.Single(fetchResponse.ByteArraySet!);
Assert.Contains(otherElement, fetchResponse.ByteArraySet!);
}

[Fact]
public async Task SetRemoveElementsAsync_NullChecksElementsAreString_ThrowsException()
{
var setName = Utils.NewGuidString();
var testData = new string[][] { new string[] { Utils.NewGuidString(), Utils.NewGuidString() }, new string[] { Utils.NewGuidString(), null! } };

await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(null!, setName, testData[0]));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, null!, testData[0]));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, (byte[][])null!));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, testData[1]));

var elementsList = new List<string>(testData[0]);
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(null!, setName, elementsList));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, null!, elementsList));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, (List<string>)null!));
await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetRemoveElementsAsync(cacheName, setName, new List<string>(testData[1])));
}

[Fact]
public async Task SetRemoveElementsAsync_FieldsAreString_HappyPath()
{
var setName = Utils.NewGuidString();
var elements = new string[] { Utils.NewGuidString(), Utils.NewGuidString() };
var otherElement = Utils.NewGuidByteArray();

// Test enumerable
await client.SetAddAsync(cacheName, setName, elements[0], false);
await client.SetAddAsync(cacheName, setName, elements[1], false);
await client.SetAddAsync(cacheName, setName, otherElement, false);

var elementsList = new List<string>(elements);
await client.SetRemoveElementsAsync(cacheName, setName, elementsList);
var fetchResponse = await client.SetFetchAsync(cacheName, setName);
Assert.Equal(CacheGetStatus.HIT, fetchResponse.Status);
Assert.Single(fetchResponse.ByteArraySet!);
Assert.Contains(otherElement, fetchResponse.ByteArraySet!);
}

[Theory]
[InlineData(null, "my-set")]
[InlineData("cache", null)]
Expand Down
43 changes: 43 additions & 0 deletions Momento/Incubating/Internal/ScsDataClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,49 @@ public async Task SendSetAddBatchAsync(string cacheName, string setName, IEnumer
}
}

public async Task<CacheSetRemoveElementResponse> SetRemoveElementAsync(string cacheName, string setName, byte[] element)
{
await SendSetRemoveElementsAsync(cacheName, setName, element.ToSingletonByteString());
return new CacheSetRemoveElementResponse();
}

public async Task<CacheSetRemoveElementResponse> SetRemoveElementAsync(string cacheName, string setName, string element)
{
await SendSetRemoveElementsAsync(cacheName, setName, element.ToSingletonByteString());
return new CacheSetRemoveElementResponse();
}

public async Task<CacheSetRemoveElementsResponse> SetRemoveElementsAsync(string cacheName, string setName, IEnumerable<byte[]> elements)
{
await SendSetRemoveElementsAsync(cacheName, setName, elements.ToEnumerableByteString());
return new CacheSetRemoveElementsResponse();
}

public async Task<CacheSetRemoveElementsResponse> SetRemoveElementsAsync(string cacheName, string setName, IEnumerable<string> elements)
{
await SendSetRemoveElementsAsync(cacheName, setName, elements.ToEnumerableByteString());
return new CacheSetRemoveElementsResponse();
}

public async Task SendSetRemoveElementsAsync(string cacheName, string setName, IEnumerable<ByteString> elements)
{
_SetDifferenceRequest request = new()
{
SetName = setName.ToByteString(),
Subtrahend = new() { Set = new() }
malandis marked this conversation as resolved.
Show resolved Hide resolved
};
request.Subtrahend.Set.Elements.Add(elements);

try
{
await this.grpcManager.Client.SetDifferenceAsync(request, MetadataWithCache(cacheName), deadline: CalculateDeadline());
}
catch (Exception e)
{
throw CacheExceptionMapper.Convert(e);
}
}

public async Task<CacheSetFetchResponse> SetFetchAsync(string cacheName, string setName)
{
_SetFetchRequest request = new() { SetName = setName.ToByteString() };
Expand Down
5 changes: 5 additions & 0 deletions Momento/Incubating/Responses/CacheSetRemoveElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Momento.Sdk.Incubating.Responses;

public class CacheSetRemoveElementResponse
{
}
5 changes: 5 additions & 0 deletions Momento/Incubating/Responses/CacheSetRemoveElements.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Momento.Sdk.Incubating.Responses;

public class CacheSetRemoveElementsResponse
{
}
78 changes: 78 additions & 0 deletions Momento/Incubating/SimpleCacheClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,84 @@ public async Task<CacheSetAddBatchResponse> SetAddBatchAsync(string cacheName, s
return await this.dataClient.SetAddBatchAsync(cacheName, setName, elements, refreshTtl, ttlSeconds);
}

/// <summary>
/// Remove an element from a set.
///
/// Performs a no-op if `setName` or `element` does not exist.
/// </summary>
/// <param name="cacheName">Name of the cache to lookup the set in.</param>
/// <param name="setName">The set to remove the element from.</param>
/// <param name="element">The data to remove from the set.</param>
/// <returns>Task representing the result of the cache operation.</returns>
/// <exception cref="ArgumentNullException">Any of `cacheName`, `setName`, `element` is `null`.</exception>
public async Task<CacheSetRemoveElementResponse> SetRemoveElementAsync(string cacheName, string setName, byte[] element)
{
Utils.ArgumentNotNull(cacheName, nameof(cacheName));
Utils.ArgumentNotNull(setName, nameof(setName));
Utils.ArgumentNotNull(element, nameof(element));

return await this.dataClient.SetRemoveElementAsync(cacheName, setName, element);
}

/// <summary>
/// Remove an element from a set.
///
/// Performs a no-op if `setName` or `element` does not exist.
/// </summary>
/// <param name="cacheName">Name of the cache to lookup the set in.</param>
/// <param name="setName">The set to remove the element from.</param>
/// <param name="element">The data to remove from the set.</param>
/// <returns>Task representing the result of the cache operation.</returns>
/// <exception cref="ArgumentNullException">Any of `cacheName`, `setName`, `element` is `null`.</exception>
public async Task<CacheSetRemoveElementResponse> SetRemoveElementAsync(string cacheName, string setName, string element)
{
Utils.ArgumentNotNull(cacheName, nameof(cacheName));
Utils.ArgumentNotNull(setName, nameof(setName));
Utils.ArgumentNotNull(element, nameof(element));

return await this.dataClient.SetRemoveElementAsync(cacheName, setName, element);
}

/// <summary>
/// Remove elements from a set.
///
/// Performs a no-op if `setName` or any of `elements` do not exist.
/// </summary>
/// <param name="cacheName">Name of the cache to lookup the set in.</param>
/// <param name="setName">The set to remove the elements from.</param>
/// <param name="elements">The data to remove from the set.</param>
/// <returns>Task representing the result of the cache operation.</returns>
/// <exception cref="ArgumentNullException">Any of `cacheName`, `setName`, `elements` is `null`.</exception>
public async Task<CacheSetRemoveElementsResponse> SetRemoveElementsAsync(string cacheName, string setName, IEnumerable<byte[]> elements)
{
Utils.ArgumentNotNull(cacheName, nameof(cacheName));
Utils.ArgumentNotNull(setName, nameof(setName));
Utils.ArgumentNotNull(elements, nameof(elements));
Utils.ElementsNotNull(elements, nameof(elements));

return await this.dataClient.SetRemoveElementsAsync(cacheName, setName, elements);
}

/// <summary>
/// Remove elements from a set.
///
/// Performs a no-op if `setName` or any of `elements` do not exist.
/// </summary>
/// <param name="cacheName">Name of the cache to lookup the set in.</param>
/// <param name="setName">The set to remove the elements from.</param>
/// <param name="elements">The data to remove from the set.</param>
/// <returns>Task representing the result of the cache operation.</returns>
/// <exception cref="ArgumentNullException">Any of `cacheName`, `setName`, `elements` is `null`.</exception>
public async Task<CacheSetRemoveElementsResponse> SetRemoveElementsAsync(string cacheName, string setName, IEnumerable<string> elements)
{
Utils.ArgumentNotNull(cacheName, nameof(cacheName));
Utils.ArgumentNotNull(setName, nameof(setName));
Utils.ArgumentNotNull(elements, nameof(elements));
Utils.ElementsNotNull(elements, nameof(elements));

return await this.dataClient.SetRemoveElementsAsync(cacheName, setName, elements);
}

/// <summary>
/// Fetch the entire set from the cache.
/// </summary>
Expand Down