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

Java: add GETEX #1609

Merged
merged 17 commits into from
Jun 20, 2024
14 changes: 14 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetBit;
import static redis_request.RedisRequestOuterClass.RequestType.GetDel;
import static redis_request.RedisRequestOuterClass.RequestType.GetEx;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
import static redis_request.RedisRequestOuterClass.RequestType.HDel;
import static redis_request.RedisRequestOuterClass.RequestType.HExists;
Expand Down Expand Up @@ -174,6 +175,7 @@
import glide.api.models.GlideString;
import glide.api.models.Script;
import glide.api.models.commands.ExpireOptions;
import glide.api.models.commands.GetExOptions;
import glide.api.models.commands.LInsertOptions.InsertPosition;
import glide.api.models.commands.LPosOptions;
import glide.api.models.commands.ListDirection;
Expand Down Expand Up @@ -506,6 +508,18 @@ public CompletableFuture<String> getdel(@NonNull String key) {
GetDel, new String[] {key}, this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<String> getex(@NonNull String key) {
return commandManager.submitNewCommand(
GetEx, new String[] {key}, this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<String> getex(@NonNull String key, @NonNull GetExOptions options) {
String[] arguments = ArrayUtils.addFirst(options.toArgs(), key);
return commandManager.submitNewCommand(GetEx, arguments, this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<GlideString> getdel(@NonNull GlideString key) {
return commandManager.submitNewCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package glide.api.commands;

import glide.api.models.GlideString;
import glide.api.models.commands.GetExOptions;
import glide.api.models.commands.SetOptions;
import glide.api.models.commands.SetOptions.ConditionalSet;
import glide.api.models.commands.SetOptions.SetOptionsBuilder;
Expand Down Expand Up @@ -75,6 +76,41 @@ public interface StringBaseCommands {
*/
CompletableFuture<String> getdel(String key);

/**
* Gets the value associated with the given <code>key</code>.
*
* @since Redis 6.2.0.
* @see <a href="https://redis.io/docs/latest/commands/getex/">redis.io</a> for details.
* @param key The <code>key</code> to retrieve from the database.
* @return If <code>key</code> exists, return the <code>value</code> of the <code>key</code>.
* Otherwise, return <code>null</code>.
* @example
* <pre>{@code
* String value = client.getex("key").get();
* assert value.equals("value");
* }</pre>
*/
CompletableFuture<String> getex(String key);

/**
* Gets the value associated with the given <code>key</code>.
*
* @since Redis 6.2.0.
* @see <a href="https://redis.io/docs/latest/commands/getex/">redis.io</a> for details.
* @param key The <code>key</code> to retrieve from the database.
* @param options The {@link GetExOptions} options.
* @return If <code>key</code> exists, return the <code>value</code> of the <code>key</code>.
* Otherwise, return <code>null</code>.
* @example
* <pre>{@code
* String response = client.set("key", "value").get();
* assert response.equals(OK);
* String value = client.getex("key", GetExOptions.Seconds(10L)).get();
* assert value.equals("value");
* }</pre>
*/
CompletableFuture<String> getex(String key, GetExOptions options);

/**
* Gets a string value associated with the given <code>key</code> and deletes the key.
*
Expand Down
33 changes: 33 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetBit;
import static redis_request.RedisRequestOuterClass.RequestType.GetDel;
import static redis_request.RedisRequestOuterClass.RequestType.GetEx;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
import static redis_request.RedisRequestOuterClass.RequestType.HDel;
import static redis_request.RedisRequestOuterClass.RequestType.HExists;
Expand Down Expand Up @@ -190,6 +191,7 @@
import com.google.protobuf.ByteString;
import glide.api.models.commands.ExpireOptions;
import glide.api.models.commands.FlushMode;
import glide.api.models.commands.GetExOptions;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.InfoOptions.Section;
import glide.api.models.commands.LInsertOptions.InsertPosition;
Expand Down Expand Up @@ -392,6 +394,37 @@ public T getdel(@NonNull String key) {
return getThis();
}

/**
* Gets the value associated with the given <code>key</code>.
*
* @since Redis 6.2.0.
* @see <a href="https://redis.io/docs/latest/commands/getex/">redis.io</a> for details.
* @param key The <code>key</code> to retrieve from the database.
* @return Command Response - If <code>key</code> exists, return the <code>value</code> of the
* <code>key</code>. Otherwise, return <code>null</code>.
*/
public T getex(@NonNull String key) {
ArgsArray commandArgs = buildArgs(key);
protobufTransaction.addCommands(buildCommand(GetEx, commandArgs));
return getThis();
}

/**
* Gets the value associated with the given <code>key</code>.
*
* @since Redis 6.2.0.
* @see <a href="https://redis.io/docs/latest/commands/getex/">redis.io</a> for details.
* @param key The <code>key</code> to retrieve from the database.
* @param options The {@link GetExOptions} options.
* @return Command Response - If <code>key</code> exists, return the <code>value</code> of the
* <code>key</code>. Otherwise, return <code>null</code>.
*/
public T getex(@NonNull String key, @NonNull GetExOptions options) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(options.toArgs(), key));
protobufTransaction.addCommands(buildCommand(GetEx, commandArgs));
return getThis();
}

/**
* Sets the given key with the given value.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models.commands;

import static glide.api.models.commands.GetExOptions.ExpiryType.MILLISECONDS;
import static glide.api.models.commands.GetExOptions.ExpiryType.PERSIST;
import static glide.api.models.commands.GetExOptions.ExpiryType.SECONDS;
import static glide.api.models.commands.GetExOptions.ExpiryType.UNIX_MILLISECONDS;
import static glide.api.models.commands.GetExOptions.ExpiryType.UNIX_SECONDS;

import glide.api.commands.StringBaseCommands;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;

/**
* Optional arguments to {@link StringBaseCommands#getex(String, GetExOptions)} command.
*
* @see <a href="https://redis.io/docs/latest/commands/getex/">redis.io</a>
*/
public class GetExOptions {

/** Expiry type for the time to live */
private final ExpiryType type;

/** The amount of time to live before the key expires. */
private Long count;

private GetExOptions(ExpiryType type) {
this.type = type;
}

private GetExOptions(ExpiryType type, Long count) {
this.type = type;
this.count = count;
}

/**
* Set the specified expire time, in seconds. Equivalent to <code>EX</code> in the Redis API.
*
* @param seconds The time to expire, in seconds.
* @return The options specifying the given expiry.
*/
public static GetExOptions Seconds(Long seconds) {
return new GetExOptions(SECONDS, seconds);
}

/**
* Set the specified expire time, in milliseconds. Equivalent to <code>PX</code> in the Redis API.
*
* @param milliseconds The time to expire, in milliseconds.
* @return The options specifying the given expiry.
*/
public static GetExOptions Milliseconds(Long milliseconds) {
return new GetExOptions(MILLISECONDS, milliseconds);
}

/**
* Set the specified Unix time at which the key will expire, in seconds. Equivalent to <code>
* EXAT</code> in the Redis API.
*
* @param unixSeconds The <code>UNIX TIME</code> to expire, in seconds.
* @return The options specifying the given expiry.
*/
public static GetExOptions UnixSeconds(Long unixSeconds) {
return new GetExOptions(UNIX_SECONDS, unixSeconds);
}

/**
* Set the specified Unix time at which the key will expire, in milliseconds. Equivalent to <code>
* PXAT</code> in the Redis API.
*
* @param unixMilliseconds The <code>UNIX TIME</code> to expire, in milliseconds.
* @return The options specifying the given expiry.
*/
public static GetExOptions UnixMilliseconds(Long unixMilliseconds) {
return new GetExOptions(UNIX_MILLISECONDS, unixMilliseconds);
}

/** Remove the time to live associated with the key. */
public static GetExOptions Persist() {
return new GetExOptions(PERSIST);
}

/** Types of value expiration configuration. */
@RequiredArgsConstructor
protected enum ExpiryType {
SECONDS("EX"),
MILLISECONDS("PX"),
UNIX_SECONDS("EXAT"),
UNIX_MILLISECONDS("PXAT"),
PERSIST("PERSIST");

private final String redisApi;
}

/**
* Converts GetExOptions into a String[] to pass to the <code>GETEX</code> command.
*
* @return String[]
*/
public String[] toArgs() {
List<String> optionArgs = new ArrayList<>();

optionArgs.add(type.redisApi);
if (count != null) {
optionArgs.add(String.valueOf(count));
}
System.out.println(optionArgs);
return optionArgs.toArray(new String[0]);
}
}
54 changes: 54 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetBit;
import static redis_request.RedisRequestOuterClass.RequestType.GetDel;
import static redis_request.RedisRequestOuterClass.RequestType.GetEx;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
import static redis_request.RedisRequestOuterClass.RequestType.HDel;
import static redis_request.RedisRequestOuterClass.RequestType.HExists;
Expand Down Expand Up @@ -232,6 +233,7 @@
import glide.api.models.commands.ConditionalChange;
import glide.api.models.commands.ExpireOptions;
import glide.api.models.commands.FlushMode;
import glide.api.models.commands.GetExOptions;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.LPosOptions;
import glide.api.models.commands.ListDirection;
Expand Down Expand Up @@ -527,6 +529,58 @@ public void getdel() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void getex() {
// setup
String key = "testKey";
String value = "testValue";
CompletableFuture<String> testResponse = new CompletableFuture<>();
testResponse.complete(value);
when(commandManager.<String>submitNewCommand(eq(GetEx), eq(new String[] {key}), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.getex(key);
String payload = response.get();

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

private static List<Arguments> getGetExOptions() {
return List.of(
Arguments.of(
// seconds
"test_with_seconds", GetExOptions.Seconds(10L), new String[] {"EX", "10"}),
Arguments.of(
// milliseconds
"test_with_milliseconds",
GetExOptions.Milliseconds(1000L),
new String[] {"PX", "1000"}),
Arguments.of(
// unix seconds
"test_with_unix_seconds", GetExOptions.UnixSeconds(10L), new String[] {"EXAT", "10"}),
Arguments.of(
// unix milliseconds
"test_with_unix_milliseconds",
GetExOptions.UnixMilliseconds(1000L),
new String[] {"PXAT", "1000"}),
Arguments.of(
// persist
"test_with_persist", GetExOptions.Persist(), new String[] {"PERSIST"}));
}

@SneakyThrows
@ParameterizedTest(name = "{0}")
@MethodSource("getGetExOptions")
public void getex_options(String testName, GetExOptions options, String[] expectedArgs) {
assertArrayEquals(
expectedArgs, options.toArgs(), "Expected " + testName + " toArgs() to pass.");
System.out.println(expectedArgs);
}

@SneakyThrows
@Test
public void set_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetBit;
import static redis_request.RedisRequestOuterClass.RequestType.GetDel;
import static redis_request.RedisRequestOuterClass.RequestType.GetEx;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
import static redis_request.RedisRequestOuterClass.RequestType.HDel;
import static redis_request.RedisRequestOuterClass.RequestType.HExists;
Expand Down Expand Up @@ -205,6 +206,7 @@

import com.google.protobuf.ByteString;
import glide.api.models.commands.ConditionalChange;
import glide.api.models.commands.GetExOptions;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.LPosOptions;
import glide.api.models.commands.ListDirection;
Expand Down Expand Up @@ -268,6 +270,12 @@ public void transaction_builds_protobuf_request(BaseTransaction<?> transaction)
transaction.get("key");
results.add(Pair.of(Get, buildArgs("key")));

transaction.getex("key");
results.add(Pair.of(GetEx, buildArgs("key")));

transaction.getex("key", GetExOptions.Seconds(10L));
results.add(Pair.of(GetEx, buildArgs("key", "EX", "10")));

transaction.set("key", "value");
results.add(Pair.of(Set, buildArgs("key", "value")));

Expand Down
Loading
Loading