From b149c86df9e05bf446482909fd74de650f761c42 Mon Sep 17 00:00:00 2001 From: Yi-Pin Chen Date: Wed, 23 Oct 2024 11:12:50 -0700 Subject: [PATCH] Java: add JSON.TOGGLE command (#2504) * Java: add JSON.TOGGLE command --------- Signed-off-by: Yi-Pin Chen Signed-off-by: BoazBD --- CHANGELOG.md | 1 + .../api/commands/servermodules/Json.java | 111 ++++++++++++++++++ .../test/java/glide/modules/JsonTests.java | 33 ++++++ 3 files changed, 145 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6784be9831..4f251795af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * Node: Added `JSON.SET` and `JSON.GET` ([#2427](https://github.com/valkey-io/valkey-glide/pull/2427)) * Java: Added `JSON.ARRAPPEND` ([#2489](https://github.com/valkey-io/valkey-glide/pull/2489)) * Node: Added `JSON.TOGGLE` ([#2491](https://github.com/valkey-io/valkey-glide/pull/2491)) +* Java: Added `JSON.TOGGLE` ([#2504](https://github.com/valkey-io/valkey-glide/pull/2504)) #### Breaking Changes diff --git a/java/client/src/main/java/glide/api/commands/servermodules/Json.java b/java/client/src/main/java/glide/api/commands/servermodules/Json.java index 4f43acafd4..7beb5fbab5 100644 --- a/java/client/src/main/java/glide/api/commands/servermodules/Json.java +++ b/java/client/src/main/java/glide/api/commands/servermodules/Json.java @@ -27,6 +27,7 @@ public class Json { private static final String JSON_ARRLEN = JSON_PREFIX + "ARRLEN"; private static final String JSON_DEL = JSON_PREFIX + "DEL"; private static final String JSON_FORGET = JSON_PREFIX + "FORGET"; + private static final String JSON_TOGGLE = JSON_PREFIX + "TOGGLE"; private Json() {} @@ -849,6 +850,116 @@ public static CompletableFuture forget( return executeCommand(client, new GlideString[] {gs(JSON_FORGET), key, path}); } + /** + * Toggles a Boolean value stored at the root within the JSON document stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return Returns the toggled boolean value at the root of the document, or null for + * JSON values matching the root that are not boolean. If key doesn't exist, + * returns null. + * @example + *
{@code
+     * Json.set(client, "doc", ".", true).get();
+     * var res = Json.toggle(client, "doc").get();
+     * assert res.equals(false);
+     * res = Json.toggle(client, "doc").get();
+     * assert res.equals(true);
+     * }
+ */ + public static CompletableFuture toggle(@NonNull BaseClient client, @NonNull String key) { + return executeCommand(client, new String[] {JSON_TOGGLE, key}); + } + + /** + * Toggles a Boolean value stored at the root within the JSON document stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return Returns the toggled boolean value at the root of the document, or null for + * JSON values matching the root that are not boolean. If key doesn't exist, + * returns null. + * @example + *
{@code
+     * Json.set(client, "doc", ".", true).get();
+     * var res = Json.toggle(client, gs("doc")).get();
+     * assert res.equals(false);
+     * res = Json.toggle(client, gs("doc")).get();
+     * assert res.equals(true);
+     * }
+ */ + public static CompletableFuture toggle( + @NonNull BaseClient client, @NonNull GlideString key) { + return executeCommand(client, new ArgsBuilder().add(gs(JSON_TOGGLE)).add(key).toArray()); + } + + /** + * Toggles a Boolean value stored at the specified path within the JSON document + * stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns a Boolean[] with the toggled boolean value for every possible + * path, or null for JSON values matching the path that are not boolean. + *
  • For legacy path (path doesn't start with $):
    + * Returns the value of the toggled boolean in path. If path + * doesn't exist or the value at path isn't a boolean, an error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "{\"bool\": true, \"nested\": {\"bool\": false, \"nested\": {\"bool\": 10}}}").get();
+     * var res = Json.toggle(client, "doc", "$..bool").get();
+     * assert Arrays.equals((Boolean[]) res, new Boolean[] {false, true, null});
+     * res = Json.toggle(client, "doc", "bool").get();
+     * assert res.equals(true);
+     * var getResult = Json.get(client, "doc", "$").get();
+     * assert getResult.equals("{\"bool\": true, \"nested\": {\"bool\": true, \"nested\": {\"bool\": 10}}}");
+     * }
+ */ + public static CompletableFuture toggle( + @NonNull BaseClient client, @NonNull String key, @NonNull String path) { + return executeCommand(client, new String[] {JSON_TOGGLE, key, path}); + } + + /** + * Toggles a Boolean value stored at the specified path within the JSON document + * stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns a Boolean[] with the toggled boolean value for every possible + * path, or null for JSON values matching the path that are not boolean. + *
  • For legacy path (path doesn't start with $):
    + * Returns the value of the toggled boolean in path. If path + * doesn't exist or the value at path isn't a boolean, an error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "{\"bool\": true, \"nested\": {\"bool\": false, \"nested\": {\"bool\": 10}}}").get();
+     * var res = Json.toggle(client, gs("doc"), gs("$..bool")).get();
+     * assert Arrays.equals((Boolean[]) res, new Boolean[] {false, true, null});
+     * res = Json.toggle(client, gs("doc"), gs("bool")).get();
+     * assert res.equals(true);
+     * var getResult = Json.get(client, "doc", "$").get();
+     * assert getResult.equals("{\"bool\": true, \"nested\": {\"bool\": true, \"nested\": {\"bool\": 10}}}");
+     * }
+ */ + public static CompletableFuture toggle( + @NonNull BaseClient client, @NonNull GlideString key, @NonNull GlideString path) { + return executeCommand( + client, new ArgsBuilder().add(gs(JSON_TOGGLE)).add(key).add(path).toArray()); + } + /** * A wrapper for custom command API. * diff --git a/java/integTest/src/test/java/glide/modules/JsonTests.java b/java/integTest/src/test/java/glide/modules/JsonTests.java index ad5ae55489..5a5d38c9fa 100644 --- a/java/integTest/src/test/java/glide/modules/JsonTests.java +++ b/java/integTest/src/test/java/glide/modules/JsonTests.java @@ -360,4 +360,37 @@ public void json_forget() { assertEquals(0L, Json.forget(client, key).get()); assertNull(Json.get(client, key, new String[] {"$"}).get()); } + + @Test + @SneakyThrows + public void toggle() { + String key = UUID.randomUUID().toString(); + String key2 = UUID.randomUUID().toString(); + String doc = "{\"bool\": true, \"nested\": {\"bool\": false, \"nested\": {\"bool\": 10}}}"; + + assertEquals("OK", Json.set(client, key, "$", doc).get()); + + assertArrayEquals( + new Object[] {false, true, null}, (Object[]) Json.toggle(client, key, "$..bool").get()); + + assertEquals(true, Json.toggle(client, gs(key), gs("bool")).get()); + + assertArrayEquals(new Object[] {}, (Object[]) Json.toggle(client, key, "$.non_existing").get()); + assertArrayEquals(new Object[] {null}, (Object[]) Json.toggle(client, key, "$.nested").get()); + + // testing behaviour with default path + assertEquals("OK", Json.set(client, key2, ".", "true").get()); + assertEquals(false, Json.toggle(client, key2).get()); + assertEquals(true, Json.toggle(client, gs(key2)).get()); + + // expect request errors + var exception = + assertThrows(ExecutionException.class, () -> Json.toggle(client, key, "nested").get()); + exception = + assertThrows( + ExecutionException.class, () -> Json.toggle(client, key, ".non_existing").get()); + exception = + assertThrows( + ExecutionException.class, () -> Json.toggle(client, "non_existing_key", "$").get()); + } }