Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
return convertAndReturn(delegate.copy(sourceKey, targetKey, replace), Converters.identityConverter());
}

@Override
public @Nullable String digest(byte @NonNull [] key) {
return convertAndReturn(delegate.digest(key), Converters.identityConverter());
}

@Override
public Long dbSize() {
return convertAndReturn(delegate.dbSize(), Converters.identityConverter());
Expand Down Expand Up @@ -1451,6 +1456,11 @@ public Boolean copy(String sourceKey, String targetKey, boolean replace) {
return copy(serialize(sourceKey), serialize(targetKey), replace);
}

@Override
public @Nullable String digest(@NonNull String key) {
return digest(serialize(key));
}

@Override
public Long decr(String key) {
return decr(serialize(key));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ default Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
return keyCommands().copy(sourceKey, targetKey, replace);
}

@Override
@Deprecated
default String digest(byte[] key) {
return keyCommands().digest(key);
}

/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,33 @@ default Mono<Boolean> copy(ByteBuffer sourceKey, ByteBuffer targetKey, boolean r
*/
Flux<BooleanResponse<CopyCommand>> copy(Publisher<CopyCommand> commands);

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param key must not be {@literal null}.
* @return {@link Mono} emitting the digest string.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
default Mono<String> digest(ByteBuffer key) {

Assert.notNull(key, "Key must not be null");

return digest(Mono.just(new KeyCommand(key))).next().map(CommandResponse::getOutput);
}

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link CommandResponse} holding the {@literal key} along with the digest.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
Flux<CommandResponse<KeyCommand, String>> digest(Publisher<KeyCommand> keys);

/**
* Determine if given {@literal key} exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public interface RedisKeyCommands {
*/
Boolean copy(byte @NonNull [] sourceKey, byte @NonNull [] targetKey, boolean replace);

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param key must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
@Nullable String digest(byte @NonNull [] key);

/**
* Determine if given {@code key} exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ interface StringTuple extends Tuple {
*/
Boolean copy(@NonNull String sourceKey, @NonNull String targetKey, boolean replace);

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param key must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
@Nullable String digest(@NonNull String key);

/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
* happens asynchronously.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ public Boolean copy(byte @NonNull [] sourceKey, byte @NonNull [] targetKey, bool
return connection.getCluster().copy(sourceKey, targetKey, replace);
}

@Override
public @Nullable String digest(byte @NonNull [] key) {

Assert.notNull(key, "Key must not be null");

return JedisConverters.toString(connection.getCluster().digestKey(key));
}

@Override
public Long del(byte @NonNull [] @NonNull... keys) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ public Boolean copy(byte @NonNull [] sourceKey, byte @NonNull [] targetKey, bool
replace);
}

@Override
public @Nullable String digest(byte @NonNull [] key) {

Assert.notNull(key, "Key must not be null");

return connection.invoke().from(JedisBinaryCommands::digestKey, PipelineBinaryCommands::digestKey, key)
.get(JedisConverters::toString);
}

@Override
public Long unlink(byte @NonNull [] @NonNull... keys) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ public Boolean copy(byte @NonNull [] sourceKey, byte @NonNull [] targetKey, bool
CopyArgs.Builder.replace(replace));
}

@Override
public @Nullable String digest(byte @NonNull [] key) {

Assert.notNull(key, "Key must not be null");

return connection.invoke().just(RedisKeyAsyncCommands::digestKey, key);
}

@Override
public Boolean exists(byte @NonNull [] key) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ public Flux<BooleanResponse<CopyCommand>> copy(Publisher<CopyCommand> commands)
}));
}

@Override
public Flux<CommandResponse<KeyCommand, String>> digest(Publisher<KeyCommand> keys) {

return connection.execute(cmd -> Flux.from(keys).concatMap((command) -> {

Assert.notNull(command.getKey(), "Key must not be null");

return cmd.digestKey(command.getKey()).map((value) -> new CommandResponse<>(command, value));
}));
}

@Override
public Flux<BooleanResponse<KeyCommand>> exists(Publisher<KeyCommand> commands) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ default Mono<Flux<? extends Message<String, V>>> listenToPatternLater(String...
*/
Mono<Boolean> copy(K sourceKey, K targetKey, boolean replace);

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param key must not be {@literal null}.
* @return string of the hash digest.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
Mono<String> getDigest(K key);

/**
* Determine if given {@code key} exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,14 @@ public Mono<Boolean> copy(K sourceKey, K targetKey, boolean replace) {
return doCreateMono(connection -> connection.keyCommands().copy(rawKey(sourceKey), rawKey(targetKey), replace));
}

@Override
public Mono<String> getDigest(K key) {

Assert.notNull(key, "Key must not be null");

return doCreateMono(connection -> connection.keyCommands().digest(rawKey(key)));
}

@Override
public Mono<Boolean> hasKey(K key) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public enum RedisCommand {
DECRBY("w", 2, 2), //
DEL("rw", 1), //
DELEX("w", 3), //
DIGEST("r", 1, 1), //
DISCARD("rw", 0, 0), //
DUMP("r", 1, 1), //

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ public interface RedisOperations<K, V> {
*/
Boolean copy(@NonNull K sourceKey, @NonNull K targetKey, boolean replace);

/**
* Get the hash digest for the value stored in the specified key as a hexadecimal string. This command is intended to
* be used with string values only.
*
* @param key must not be {@literal null}.
* @return the digest of the key. {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/digest">Redis Documentation: DIGEST</a>
* @since 4.1
*/
String getDigest(@NonNull K key);

/**
* Determine if given {@code key} exists.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,14 @@ public Boolean copy(K source, K target, boolean replace) {
return doWithKeys(connection -> connection.copy(sourceKey, targetKey, replace));
}

@Override
public @Nullable String getDigest(@NonNull K key) {

byte[] rawKey = rawKey(key);

return doWithKeys(connection -> connection.digest(rawKey));
}

@Override
public Boolean hasKey(K key) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,11 @@ suspend fun <K : Any, V : Any> ReactiveRedisOperations<K, V>.moveAndAwait(key: K
* @since 2.2
*/
suspend fun <K : Any, V : Any> ReactiveRedisOperations<K, V>.getExpireAndAwait(key: K): Duration? = getExpire(key).awaitFirstOrNull()

/**
* Coroutines variant of [ReactiveRedisOperations.getDigest].
*
* @author Yordan Tsintsov
* @since 4.1
*/
suspend fun <K : Any, V : Any> ReactiveRedisOperations<K, V>.getDigestAndAwait(key: K): String? = getDigest(key).awaitFirstOrNull()
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,46 @@ void testCopy() {
assertThat(connection.exists("foo")).isTrue();
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnDigestForExistingKey() {

String key = "digest-" + UUID.randomUUID();
actual.add(connection.set(key, "bar"));
actual.add(connection.digest(key));

List<Object> results = getResults();
assertThat(results.get(0)).isEqualTo(Boolean.TRUE);
assertThat(results.get(1)).isNotNull();
assertThat(results.get(1)).isInstanceOf(String.class);
assertThat(((String) results.get(1))).hasSize(16);
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnNullForNonExistingKey() {

actual.add(connection.digest("nonexistent"));

List<Object> results = getResults();
assertThat(results.get(0)).isNull();
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnConsistentValueForSameContent() {

String key1 = "digest-1-" + UUID.randomUUID();
String key2 = "digest-2-" + UUID.randomUUID();
actual.add(connection.set(key1, "same-value"));
actual.add(connection.set(key2, "same-value"));
actual.add(connection.digest(key1));
actual.add(connection.digest(key2));

List<Object> results = getResults();
assertThat(results.get(2)).isEqualTo(results.get(3));
}

@Test
public void testInfo() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,45 @@ void existsKeyReturnsZeroWhenKeysDoNotExist() {
.expectNext(0L).verifyComplete();
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnDigestForExistingKey() {

nativeCommands.set(KEY_1, VALUE_1);

connection.keyCommands().digest(KEY_1_BBUFFER).as(StepVerifier::create)
.assertNext(digest -> {
assertThat(digest).isNotNull();
assertThat(digest).isInstanceOf(String.class);
assertThat(digest).hasSize(16);
})
.verifyComplete();
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnEmptyForNonExistingKey() {

connection.keyCommands().digest(KEY_1_BBUFFER).as(StepVerifier::create)
.verifyComplete();
}

@Test
@EnabledOnCommand("DIGEST")
void digestShouldReturnSameValueForSameContent() {

nativeCommands.set(KEY_1, "same-value");
nativeCommands.set(KEY_2, "same-value");

Mono<String> digest1 = connection.keyCommands().digest(KEY_1_BBUFFER);
Mono<String> digest2 = connection.keyCommands().digest(KEY_2_BBUFFER);

Mono.zip(digest1, digest2)
.as(StepVerifier::create)
.assertNext(tuple -> assertThat(tuple.getT1()).isEqualTo(tuple.getT2()))
.verifyComplete();
}

@Test // DATAREDIS-525
void typeShouldReturnTypeCorrectly() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,23 @@ void copy() {
redisTemplate.opsForValue().get(targetKey).as(StepVerifier::create).expectNext(nextValue).verifyComplete();
}

@Test
@EnabledOnCommand("DIGEST")
void digest() {

K key = keyFactory.instance();
V value = valueFactory.instance();

redisTemplate.opsForValue().set(key, value).as(StepVerifier::create).expectNext(true).verifyComplete();
redisTemplate.getDigest(key).as(StepVerifier::create).assertNext(digest -> {
assertThat(digest).isNotNull();
assertThat(digest).hasSize(16);
}).verifyComplete();

K nonExistingKey = keyFactory.instance();
redisTemplate.getDigest(nonExistingKey).as(StepVerifier::create).verifyComplete();
}

@Test // DATAREDIS-602
void exists() {

Expand Down
Loading
Loading