Skip to content

Commit

Permalink
support hsetnx, watch, type and unlink with GlideString (#1641)
Browse files Browse the repository at this point in the history
  • Loading branch information
alon-arenberg authored Jun 27, 2024
1 parent 2d499dc commit 9312976
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 11 deletions.
23 changes: 23 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,13 @@ public CompletableFuture<Boolean> hsetnx(
HSetNX, new String[] {key, field, value}, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Boolean> hsetnx(
@NonNull GlideString key, @NonNull GlideString field, @NonNull GlideString value) {
return commandManager.submitNewCommand(
HSetNX, new GlideString[] {key, field, value}, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Long> hdel(@NonNull String key, @NonNull String[] fields) {
String[] args = ArrayUtils.addFirst(fields, key);
Expand Down Expand Up @@ -1168,6 +1175,11 @@ public CompletableFuture<Long> unlink(@NonNull String[] keys) {
return commandManager.submitNewCommand(Unlink, keys, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> unlink(@NonNull GlideString[] keys) {
return commandManager.submitNewCommand(Unlink, keys, this::handleLongResponse);
}

@Override
public CompletableFuture<Boolean> expire(@NonNull String key, long seconds) {
return commandManager.submitNewCommand(
Expand Down Expand Up @@ -1987,6 +1999,12 @@ public CompletableFuture<String> type(@NonNull String key) {
return commandManager.submitNewCommand(Type, new String[] {key}, this::handleStringResponse);
}

@Override
public CompletableFuture<String> type(@NonNull GlideString key) {
return commandManager.submitNewCommand(
Type, new GlideString[] {key}, this::handleStringResponse);
}

@Override
public CompletableFuture<Long> linsert(
@NonNull String key,
Expand Down Expand Up @@ -2613,6 +2631,11 @@ public CompletableFuture<String> watch(@NonNull String[] keys) {
return commandManager.submitNewCommand(Watch, keys, this::handleStringResponse);
}

@Override
public CompletableFuture<String> watch(@NonNull GlideString[] keys) {
return commandManager.submitNewCommand(Watch, keys, this::handleStringResponse);
}

@Override
public CompletableFuture<Set<String>> sunion(@NonNull String[] keys) {
return commandManager.submitNewCommand(SUnion, keys, this::handleSetResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,31 @@ public interface GenericBaseCommands {
* @return The number of <code>keys</code> that were unlinked.
* @example
* <pre>{@code
* Long result = client.unlink("my_key").get();
* Long result = client.unlink(new String[] {"my_key"}).get();
* assert result == 1L;
* }</pre>
*/
CompletableFuture<Long> unlink(String[] keys);

/**
* Unlink (delete) multiple <code>keys</code> from the database. A key is ignored if it does not
* exist. This command, similar to <a href="https://redis.io/commands/del/">DEL</a>, removes
* specified keys and ignores non-existent ones. However, this command does not block the server,
* while <a href="https://redis.io/commands/del/">DEL</a> does.
*
* @apiNote When in cluster mode, the command may route to multiple nodes when <code>keys</code>
* map to different hash slots.
* @see <a href="https://redis.io/commands/unlink/">redis.io</a> for details.
* @param keys The list of keys to unlink.
* @return The number of <code>keys</code> that were unlinked.
* @example
* <pre>{@code
* Long result = client.unlink(new GlideString[] {gs("my_key")}).get();
* assert result == 1L;
* }</pre>
*/
CompletableFuture<Long> unlink(GlideString[] keys);

/**
* Sets a timeout on <code>key</code> in seconds. After the timeout has expired, the <code>key
* </code> will automatically be deleted.<br>
Expand Down Expand Up @@ -720,6 +739,24 @@ CompletableFuture<Boolean> pexpireAt(
*/
CompletableFuture<String> type(String key);

/**
* Returns the string representation of the type of the value stored at <code>key</code>.
*
* @see <a href="https://redis.io/commands/type/">redis.io</a> for details.
* @param key The <code>key</code> to check its data type.
* @return If the <code>key</code> exists, the type of the stored value is returned. Otherwise, a
* "none" string is returned.
* @example
* <pre>{@code
* String type = client.type(gs("StringKey")).get();
* assert type.equals("string");
*
* type = client.type(gs("ListKey")).get();
* assert type.equals("list");
* }</pre>
*/
CompletableFuture<String> type(GlideString key);

/**
* Returns the internal encoding for the Redis object stored at <code>key</code>.
*
Expand Down
23 changes: 23 additions & 0 deletions java/client/src/main/java/glide/api/commands/HashBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ public interface HashBaseCommands {
*/
CompletableFuture<Boolean> hsetnx(String key, String field, String value);

/**
* Sets <code>field</code> in the hash stored at <code>key</code> to <code>value</code>, only if
* <code>field</code> does not yet exist.<br>
* If <code>key</code> does not exist, a new key holding a hash is created.<br>
* If <code>field</code> already exists, this operation has no effect.
*
* @see <a href="https://redis.io/commands/hsetnx/">redis.io</a> for details.
* @param key The key of the hash.
* @param field The field to set the value for.
* @param value The value to set.
* @return <code>true</code> if the field was set, <code>false</code> if the field already existed
* and was not set.
* @example
* <pre>{@code
* Boolean payload1 = client.hsetnx(gs("myHash"), gs("field"), gs("value")).get();
* assert payload1; // Indicates that the field "field" was set successfully in the hash "myHash".
*
* Boolean payload2 = client.hsetnx(gs("myHash"), gs("field"), gs("newValue")).get();
* assert !payload2; // Indicates that the field "field" already existed in the hash "myHash" and was not set again.
* }</pre>
*/
CompletableFuture<Boolean> hsetnx(GlideString key, GlideString field, GlideString value);

/**
* Removes the specified fields from the hash stored at <code>key</code>. Specified fields that do
* not exist within this hash are ignored.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import glide.api.models.GlideString;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -34,4 +35,30 @@ public interface TransactionsBaseCommands {
* }</pre>
*/
CompletableFuture<String> watch(String[] keys);

/**
* Marks the given keys to be watched for conditional execution of a transaction. Transactions
* will only execute commands if the watched keys are not modified before execution of the
* transaction.
*
* @apiNote When in cluster mode, the command may route to multiple nodes when <code>keys</code>
* map to different hash slots.
* @see <a href="https://redis.io/docs/latest/commands/watch/">redis.io</a> for details.
* @param keys The keys to watch.
* @return <code>OK</code>.
* @example
* <pre>{@code
* assert client.watch(new GlideString[] {gs("sampleKey")}).get().equals("OK");
* transaction.set(gs("sampleKey"), gs("foobar"));
* Object[] result = client.exec(transaction).get();
* assert result != null; // Executes successfully and keys are unwatched.
*
* assert client.watch(new GlideString[] {gs("sampleKey")}).get().equals("OK");
* transaction.set(gs("sampleKey"), gs("foobar"));
* assert client.set(gs("sampleKey"), gs("hello world")).get().equals("OK");
* Object[] result = client.exec(transaction).get();
* assert result == null; // null is returned when the watched key is modified before transaction execution.
* }</pre>
*/
CompletableFuture<String> watch(GlideString[] keys);
}
94 changes: 94 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,28 @@ public void unlink_returns_long_success() {
assertEquals(numberUnlinked, result);
}

@SneakyThrows
@Test
public void unlink_binary_returns_long_success() {
// setup
GlideString[] keys = new GlideString[] {gs("testKey1"), gs("testKey2")};
Long numberUnlinked = 1L;
CompletableFuture<Long> testResponse = new CompletableFuture<>();
testResponse.complete(numberUnlinked);

// match on protobuf request
when(commandManager.<Long>submitNewCommand(eq(Unlink), eq(keys), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.unlink(keys);
Long result = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(numberUnlinked, result);
}

@SneakyThrows
@Test
public void get_returns_success() {
Expand Down Expand Up @@ -1823,6 +1845,31 @@ public void hsetnx_success() {
assertTrue(payload);
}

@SneakyThrows
@Test
public void hsetnx_binary_success() {
// setup
GlideString key = gs("testKey");
GlideString field = gs("testField");
GlideString value = gs("testValue");
GlideString[] args = new GlideString[] {key, field, value};

CompletableFuture<Boolean> testResponse = new CompletableFuture<>();
testResponse.complete(Boolean.TRUE);

// match on protobuf request
when(commandManager.<Boolean>submitNewCommand(eq(HSetNX), eq(args), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Boolean> response = service.hsetnx(key, field, value);
Boolean payload = response.get();

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

@SneakyThrows
@Test
public void hdel_success() {
Expand Down Expand Up @@ -5770,6 +5817,30 @@ public void type_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void type_binary_returns_success() {
// setup
GlideString key = gs("testKey");
GlideString[] arguments = new GlideString[] {key};
String value = "none";

CompletableFuture<String> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<String>submitNewCommand(eq(Type), eq(arguments), any()))
.thenReturn(testResponse);

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

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

@SneakyThrows
@Test
public void randomKey() {
Expand Down Expand Up @@ -8383,6 +8454,29 @@ public void watch_returns_success() {
assertEquals(OK, payload);
}

@SneakyThrows
@Test
public void watch_binary_returns_success() {
// setup
GlideString key1 = gs("testKey1");
GlideString key2 = gs("testKey2");
GlideString[] arguments = new GlideString[] {key1, key2};
CompletableFuture<String> testResponse = new CompletableFuture<>();
testResponse.complete(OK);

// match on protobuf request
when(commandManager.<String>submitNewCommand(eq(Watch), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.watch(arguments);
String payload = response.get();

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

@SneakyThrows
@Test
public void unwatch_returns_success() {
Expand Down
Loading

0 comments on commit 9312976

Please sign in to comment.