Skip to content

Commit

Permalink
Added Incr, IncrBy and IncrByFloat commands. (String Commands)
Browse files Browse the repository at this point in the history
  • Loading branch information
SanHalacogluImproving committed Feb 9, 2024
1 parent 8f35f5a commit 8b836a5
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 0 deletions.
28 changes: 28 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

import static glide.ffi.resolvers.SocketListenerResolver.getSocket;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.Incr;
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;

Expand Down Expand Up @@ -145,6 +148,14 @@ protected String handleStringOrNullResponse(Response response) throws RedisExcep
return handleRedisResponse(String.class, true, response);
}

protected Long handleLongResponse(Response response) throws RedisException {
return handleRedisResponse(Long.class, false, response);
}

protected Double handleDoubleResponse(Response response) throws RedisException {
return handleRedisResponse(Double.class, false, response);
}

@Override
public CompletableFuture<String> ping() {
return commandManager.submitNewCommand(Ping, new String[0], this::handleStringResponse);
Expand Down Expand Up @@ -173,4 +184,21 @@ public CompletableFuture<String> set(
String[] arguments = ArrayUtils.addAll(new String[] {key, value}, options.toArgs());
return commandManager.submitNewCommand(SetString, arguments, this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<Long> incr(@NonNull String key) {
return commandManager.submitNewCommand(Incr, new String[] {key}, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> incrBy(@NonNull String key, long amount) {
return commandManager.submitNewCommand(
IncrBy, new String[] {key, Long.toString(amount)}, this::handleLongResponse);
}

@Override
public CompletableFuture<Double> incrByFloat(@NonNull String key, double amount) {
return commandManager.submitNewCommand(
IncrByFloat, new String[] {key, Double.toString(amount)}, this::handleDoubleResponse);
}
}
40 changes: 40 additions & 0 deletions java/client/src/main/java/glide/api/commands/StringCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,44 @@ public interface StringCommands {
* is set, return the old value as a <code>String</code>.
*/
CompletableFuture<String> set(String key, String value, SetOptions options);

/**
* Increments the number stored at <code>key</code> by one. If <code>key</code> does not exist, it
* is set to 0 before performing the operation.
*
* @see <a href="https://redis.io/commands/incr/">redis.io</a> for details.
* @param key The key to increment its value.
* @return The value of <code>key</code> after the increment. An error is raised if <code>key
* </code> contains a value of the wrong type or contains a string that can not be represented
* as integer.
*/
CompletableFuture<Long> incr(String key);

/**
* Increments the number stored at <code>key</code> by <code>amount</code>. If <code>key</code>
* does not exist, it is set to 0 before performing the operation.
*
* @see <a href="https://redis.io/commands/incrby/">redis.io</a> for details.
* @param key The key to increment its value.
* @param amount The amount to increment.
* @return The value of <code>key</code> after the increment, An error is raised if <code>key
* </code> contains a value of the wrong type or contains a string that cannot be represented
* as integer.
*/
CompletableFuture<Long> incrBy(String key, long amount);

/**
* Increment the string representing a floating point number stored at <code>key</code> by <code>
* amount</code>. By using a negative increment value, the result is that the value stored at
* <code>key</code> is decremented. If <code>key</code> does not exist, it is set to 0 before
* performing the operation.
*
* @see <a href="https://redis.io/commands/incrbyfloat/">redis.io</a> for details.
* @param key The key to increment its value.
* @param amount The amount to increment.
* @return The value of <code>key</code> after the increment. An error is raised if <code>key
* </code> contains a value of the wrong type, or the current key content is not parsable as a
* double precision floating point number.
*/
CompletableFuture<Double> incrByFloat(String key, double amount);
}
76 changes: 76 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import static org.mockito.Mockito.when;
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.Incr;
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;

Expand Down Expand Up @@ -201,4 +204,77 @@ public void set_with_SetOptions_OnlyIfDoesNotExist_returns_success() {
assertNotNull(response);
assertEquals(value, response.get());
}

@SneakyThrows
@Test
public void incr_returns_success() {
// setup
String key = "testKey";
Long value = 10L;

CompletableFuture testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);

// match on protobuf request
when(commandManager.<String>submitNewCommand(eq(Incr), eq(new String[] {key}), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.incr(key);
Long payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void incrBy_returns_success() {
// setup
String key = "testKey";
long amount = 1L;
Long value = 10L;

CompletableFuture testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);

// match on protobuf request
when(commandManager.<String>submitNewCommand(
eq(IncrBy), eq(new String[] {key, Long.toString(amount)}), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.incrBy(key, amount);
Long payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void incrByFloat_returns_success() {
// setup
String key = "testKey";
double amount = 1.1;
Double value = 10.1;

CompletableFuture testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);

// match on protobuf request
when(commandManager.<String>submitNewCommand(
eq(IncrByFloat), eq(new String[] {key, Double.toString(amount)}), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Double> response = service.incrByFloat(key, amount);
Double payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}
}
2 changes: 2 additions & 0 deletions java/integTest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ repositories {
}

dependencies {
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.13.0'

// client
implementation project(':client')

Expand Down
69 changes: 69 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import static glide.api.models.commands.SetOptions.ConditionalSet.ONLY_IF_DOES_NOT_EXIST;
import static glide.api.models.commands.SetOptions.ConditionalSet.ONLY_IF_EXISTS;
import static glide.api.models.commands.SetOptions.Expiry.Milliseconds;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import glide.api.BaseClient;
import glide.api.RedisClient;
Expand All @@ -18,9 +20,12 @@
import glide.api.models.configuration.NodeAddress;
import glide.api.models.configuration.RedisClientConfiguration;
import glide.api.models.configuration.RedisClusterClientConfiguration;
import glide.api.models.exceptions.RequestException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Timeout;
Expand Down Expand Up @@ -247,4 +252,68 @@ public void set_missing_value_and_returnOldValue_is_null(BaseClient client) {
String data = client.set("another", ANOTHER_VALUE, options).get();
assertNull(data);
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void incr_commands_existing_key(BaseClient client) {
String key = RandomStringUtils.randomAlphabetic(10);

assertEquals(OK, client.set(key, "10").get(10, SECONDS));

assertEquals(11, client.incr(key).get(10, SECONDS));
assertEquals("11", client.get(key).get(10, SECONDS));

assertEquals(15, client.incrBy(key, 4).get(10, SECONDS));
assertEquals("15", client.get(key).get(10, SECONDS));

assertEquals(20.5, client.incrByFloat(key, 5.5).get(10, SECONDS));
assertEquals("20.5", client.get(key).get(10, SECONDS));
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void incr_commands_non_existing_key(BaseClient client) {
String key1 = RandomStringUtils.randomAlphabetic(10);
String key2 = RandomStringUtils.randomAlphabetic(10);
String key3 = RandomStringUtils.randomAlphabetic(10);

assertNull(client.get(key1).get(10, SECONDS));
assertEquals(1, client.incr(key1).get(10, SECONDS));
assertEquals("1", client.get(key1).get(10, SECONDS));

assertNull(client.get(key2).get(10, SECONDS));
assertEquals(3, client.incrBy(key2, 3).get(10, SECONDS));
assertEquals("3", client.get(key2).get(10, SECONDS));

assertNull(client.get(key3).get(10, SECONDS));
assertEquals(0.5, client.incrByFloat(key3, 0.5).get(10, SECONDS));
assertEquals("0.5", client.get(key3).get(10, SECONDS));
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void test_incr_commands_type_error(BaseClient client) {
String key1 = RandomStringUtils.randomAlphabetic(10);

assertEquals(OK, client.set(key1, "foo").get(10, SECONDS));

Exception incrException =
assertThrows(ExecutionException.class, () -> client.incr(key1).get(10, SECONDS));
assertTrue(incrException.getCause() instanceof RequestException);
assertTrue(incrException.getCause().getMessage().contains("value is not an integer"));

Exception incrByException =
assertThrows(ExecutionException.class, () -> client.incrBy(key1, 3).get(10, SECONDS));
assertTrue(incrByException.getCause() instanceof RequestException);
assertTrue(incrByException.getCause().getMessage().contains("value is not an integer"));

Exception incrByFloatException =
assertThrows(
ExecutionException.class, () -> client.incrByFloat(key1, 3.5).get(10, SECONDS));
assertTrue(incrByFloatException.getCause() instanceof RequestException);
assertTrue(incrByFloatException.getCause().getMessage().contains("value is not a valid float"));
}
}

0 comments on commit 8b836a5

Please sign in to comment.