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: add sorted set remove elements #227

Merged
merged 2 commits into from
Oct 17, 2024
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
49 changes: 48 additions & 1 deletion src/Cache/CacheClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
use Momento\Cache\CacheOperationTypes\ListCachesResponse;
use Momento\Cache\CacheOperationTypes\SortedSetPutElementsResponse;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementResponse;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementsResponse;
use Momento\Cache\CacheOperationTypes\UpdateTtlResponse;
use Momento\Cache\Errors\InvalidArgumentError;
use Momento\Cache\Internal\IdleDataClientWrapper;
Expand Down Expand Up @@ -2010,7 +2011,53 @@ public function sortedSetRemoveElement(string $cacheName, string $sortedSetName,
return $this->sortedSetRemoveElementAsync($cacheName, $sortedSetName, $value)->wait();
}

// placeholder: sortedSetRemoveElements
/**
* Remove elements from a sorted set.
*
* @param string $cacheName Name of the cache that contains the sorted set.
* @param string $sortedSetName The set to remove the elements from.
* @param array $values The values to remove.
* @return ResponseFuture<SortedSetRemoveElementsResponse> A waitable future which
* will provide the result of the sorted set remove elements operation upon a blocking call to
* wait.
* <code>$response = $responseFuture->wait();</code><br />
* The response represents the result of the sorted set remove element operation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* The response represents the result of the sorted set remove element operation.
* The response represents the result of the sorted set remove elements operation.

* This result is resolved to a type-safe object of one of the following
* types:<br>
* * SortedSetRemoveElementsSuccess<br>
* * SortedSetRemoveElementsError<br>
* <code>
* if ($error = $response->asError()) {
* // handle error condition
* }
* </code>
* If inspection of the response is not required, one need not call wait as
* we implicitly wait for completion of the request on destruction of the
* response future.
*/
public function sortedSetRemoveElementsAsync(string $cacheName, string $sortedSetName, array $values): ResponseFuture
{
return $this->getNextDataClient()->sortedSetRemoveElements($cacheName, $sortedSetName, $values);
}

/**
* Remove elements from a sorted set.
*
* @param string $cacheName Name of the cache that contains the sorted set.
* @param string $sortedSetName The set to remove the elements from.
* @param array $values The values to remove.
* @return SortedSetRemoveElementsResponse Represents the result of the sorted set remove element operation.
malandis marked this conversation as resolved.
Show resolved Hide resolved
* This result is resolved to a type-safe object of one of the following types:<br>
* * SortedSetRemoveElementsSuccess<br>
* * SortedSetRemoveElementsError<br>
* if ($error = $response->asError()) {<br>
* &nbsp;&nbsp;// handle error condition<br>
* }</code>
*/
public function sortedSetRemoveElements(string $cacheName, string $sortedSetName, array $values): SortedSetRemoveElementsResponse
{
return $this->sortedSetRemoveElementsAsync($cacheName, $sortedSetName, $values)->wait();
}

/**
* Gets the cache values stored for given keys.
Expand Down
59 changes: 57 additions & 2 deletions src/Cache/CacheOperationTypes/CacheOperationTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3620,8 +3620,6 @@ class SortedSetRemoveElementSuccess extends SortedSetRemoveElementResponse
{
}

// placeholder: sortedSetRemoveElementsResponse

/**
* Contains information about an error returned from the request.
*/
Expand All @@ -3630,6 +3628,63 @@ class SortedSetRemoveElementError extends SortedSetRemoveElementResponse
use ErrorBody;
}

/**
* Parent response type for a sorted set remove elements request. The
* response object is resolved to a type-safe object of one of
* the following subtypes:
* SortedSetRemoveElementsSuccess
* SortedSetRemoveElementsError
*
* Pattern matching can be used to operate on the appropriate subtype.
* For example:
* <code>
* if ($response->asSuccess()) {
* // handle success as appropriate
* } elseif ($error = $response->asError())
* // handle error as appropriate
* }
* </code>
*/
abstract class SortedSetRemoveElementsResponse extends ResponseBase
{
/**
* @return SortedSetRemoveElementsSuccess|null Returns the success subtype if the request was successful and null otherwise.
*/
public function asSuccess(): ?SortedSetRemoveElementsSuccess
{
if ($this->isSuccess()) {
return $this;
}
return null;
}

/**
* @return SortedSetRemoveElementsError|null Returns the error subtype if the request returned an error and null otherwise.
*/
public function asError(): ?SortedSetRemoveElementsError
{
if ($this->isError()) {
return $this;
}
return null;
}
}

/**
* Indicates that the request that generated it was successful.
*/
class SortedSetRemoveElementsSuccess extends SortedSetRemoveElementsResponse
{
}

/**
* Contains information about an error returned from the request.
*/
class SortedSetRemoveElementsError extends SortedSetRemoveElementsResponse
{
use ErrorBody;
}

abstract class GetBatchResponse extends ResponseBase
{
/**
Expand Down
40 changes: 40 additions & 0 deletions src/Cache/Internal/ScsDataClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementError;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementResponse;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementSuccess;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementsError;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementsResponse;
use Momento\Cache\CacheOperationTypes\SortedSetRemoveElementsSuccess;
use Momento\Cache\CacheOperationTypes\UpdateTtlError;
use Momento\Cache\CacheOperationTypes\UpdateTtlMiss;
use Momento\Cache\CacheOperationTypes\UpdateTtlResponse;
Expand All @@ -231,6 +234,7 @@
use function Momento\Utilities\validateSortedSetElements;
use function Momento\Utilities\validateSortedSetName;
use function Momento\Utilities\validateSortedSetRanks;
use function Momento\Utilities\validateSortedSetValues;
use function Momento\Utilities\validateTruncateSize;
use function Momento\Utilities\validateTtl;
use function Momento\Utilities\validateValueName;
Expand Down Expand Up @@ -1738,6 +1742,42 @@ function () use ($call): SortedSetRemoveElementResponse {

}

public function sortedSetRemoveElements(string $cacheName, string $sortedSetName, array $values) : ResponseFuture
{
try {
validateCacheName($cacheName);
validateSortedSetName($sortedSetName);
validateSortedSetValues($values);
$sortedSetRemoveElementsRequest = new _SortedSetRemoveRequest();
$sortedSetRemoveElementsRequest->setSetName($sortedSetName);
$sortedSetRemoveElementsRequest->setSome(new _SortedSetRemoveRequest\_Some());
$sortedSetRemoveElementsRequest->getSome()->setValues($values);
$call = $this->grpcManager->client->SortedSetRemove(
$sortedSetRemoveElementsRequest,
["cache" => [$cacheName]],
["timeout" => $this->timeout],
);
} catch (SdkError $e) {
return ResponseFuture::createResolved(new SortedSetRemoveElementsError($e));
} catch (Exception $e) {
return ResponseFuture::createResolved(new SortedSetRemoveElementsError(new UnknownError($e->getMessage(), 0, $e)));
}

return ResponseFuture::createPending(
function () use ($call): SortedSetRemoveElementsResponse {
try {
$this->processCall($call);

return new SortedSetRemoveElementsSuccess();
} catch (SdkError $e) {
return new SortedSetRemoveElementsError($e);
} catch (Exception $e) {
return new SortedSetRemoveElementsError(new UnknownError($e->getMessage(), 0, $e));
}
}
);
}

/**
* @return ResponseFuture<SortedSetGetScoreResponse>
*/
Expand Down
14 changes: 14 additions & 0 deletions src/Utilities/_DataValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,17 @@ function validateSortedSetElements(array $elements): void
}
}
}

if (!function_exists('validateSortedSetValues')) {
function validateSortedSetValues(array $values): void
{
if (empty($values)) {
throw new InvalidArgumentError("sorted set values must be a non-empty array");
}
foreach ($values as $value) {
if (!is_string($value) || isNullOrEmpty($value)) {
throw new InvalidArgumentError("sorted set values must all be non-empty strings");
}
}
}
}
105 changes: 104 additions & 1 deletion tests/Cache/CacheClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3479,7 +3479,110 @@ public function testSortedSetRemoveElementWithEmptyValue_ThrowsException()
$this->assertEquals(MomentoErrorCode::INVALID_ARGUMENT_ERROR, $response->asError()->errorCode());
}

// placeholder: sortedSetRemoveElements
public function testSortedSetRemoveElements_HappyPath()
{
// 1. Remove elements from a sorted set that does not exist
$sortedSetName = uniqid();
$values = [uniqid(), uniqid()];
$response = $this->client->sortedSetRemoveElements($this->TEST_CACHE_NAME, $sortedSetName, $values);
$this->assertNull($response->asError());
$this->assertNotNull($response->asSuccess(), "Expected a success but got: $response");

$response = $this->client->sortedSetFetchByRank($this->TEST_CACHE_NAME, $sortedSetName);
$this->assertNull($response->asError());
$this->assertNotNull($response->asMiss(), "Expected a miss but got: $response");

// 2. Remove multiple elements from sorted set
// Set up: Add elements to the sorted set
$sortedSetName = uniqid();
$value1 = uniqid();
$value2 = uniqid();
$score1 = 1.0;
$score2 = 2.0;
$response = $this->client->sortedSetPutElement($this->TEST_CACHE_NAME, $sortedSetName, $value1, $score1);
$this->assertNull($response->asError());
$this->assertNotNull($response->asSuccess(), "Expected a success but got: $response");

$response = $this->client->sortedSetPutElement($this->TEST_CACHE_NAME, $sortedSetName, $value2, $score2);
$this->assertNull($response->asError());
$this->assertNotNull($response->asSuccess(), "Expected a success but got: $response");

// 2.1 Remove a different set of elements from the sorted set (only value1 should remain)
$response = $this->client->sortedSetRemoveElements($this->TEST_CACHE_NAME, $sortedSetName, [uniqid()]);
$this->assertNull($response->asError());
$this->assertNotNull($response->asSuccess(), "Expected a success but got: $response");

// Ensure the original elements are still in the sorted set
$response = $this->client->sortedSetFetchByRank($this->TEST_CACHE_NAME, $sortedSetName);
$this->assertNull($response->asError());
$hit = $response->asHit();
$this->assertNotNull($hit, "Expected a hit but got: $response");
$this->assertCount(2, $hit->valuesArray());
$this->assertArrayHasKey($value1, $hit->valuesArray());
$this->assertEquals($score1, $hit->valuesArray()[$value1]);
$this->assertArrayHasKey($value2, $hit->valuesArray());
$this->assertEquals($score2, $hit->valuesArray()[$value2]);

// 2.2 Remove both elements from the sorted set
$response = $this->client->sortedSetRemoveElements($this->TEST_CACHE_NAME, $sortedSetName, [$value1, $value2]);
$this->assertNull($response->asError());
$this->assertNotNull($response->asSuccess(), "Expected a success but got: $response");

// Ensure the sorted set is now empty
$response = $this->client->sortedSetFetchByRank($this->TEST_CACHE_NAME, $sortedSetName);
$this->assertNull($response->asError());
$this->assertNotNull($response->asMiss(), "Expected a miss but got: $response");
}

public function testSortedSetRemoveElementsWithNonexistentCache_ThrowsException()
{
$cacheName = uniqid();
$sortedSetName = uniqid();
$values = [uniqid(), uniqid()];

$response = $this->client->sortedSetRemoveElements($cacheName, $sortedSetName, $values);
$this->assertNotNull($response->asError(), "Expected error but got: $response");
$this->assertEquals(MomentoErrorCode::CACHE_NOT_FOUND_ERROR, $response->asError()->errorCode());
}

public function testSortedSetRemoveElementsWithNullCacheName_ThrowsException()
{
$sortedSetName = uniqid();
$values = [uniqid(), uniqid()];

$response = $this->client->sortedSetRemoveElements((string)null, $sortedSetName, $values);
$this->assertNotNull($response->asError(), "Expected error but got: $response");
$this->assertEquals(MomentoErrorCode::INVALID_ARGUMENT_ERROR, $response->asError()->errorCode());
}

public function testSortedSetRemoveElementsWithEmptyCacheName_ThrowsException()
{
$sortedSetName = uniqid();
$values = [uniqid(), uniqid()];

$response = $this->client->sortedSetRemoveElements("", $sortedSetName, $values);
$this->assertNotNull($response->asError(), "Expected error but got: $response");
$this->assertEquals(MomentoErrorCode::INVALID_ARGUMENT_ERROR, $response->asError()->errorCode());
}

public function testSortedSetRemoveElementsWithEmptyValuesArray_ThrowsException()
{
$sortedSetName = uniqid();
$values = [];

$response = $this->client->sortedSetRemoveElements($this->TEST_CACHE_NAME, $sortedSetName, $values);
$this->assertNotNull($response->asError(), "Expected error but got: $response");
$this->assertEquals(MomentoErrorCode::INVALID_ARGUMENT_ERROR, $response->asError()->errorCode());
}

public function testSortedSetRemoveElementsWithNullValuesArray_ThrowsException()
{
$sortedSetName = uniqid();

$response = $this->client->sortedSetRemoveElements($this->TEST_CACHE_NAME, $sortedSetName, [null]);
$this->assertNotNull($response->asError(), "Expected error but got: $response");
$this->assertEquals(MomentoErrorCode::INVALID_ARGUMENT_ERROR, $response->asError()->errorCode());
}

public function testSortedSetGetScore_HappyPath()
{
Expand Down
Loading