Skip to content

Commit

Permalink
Java: add JSON.TOGGLE command (valkey-io#2504)
Browse files Browse the repository at this point in the history
* Java: add JSON.TOGGLE command
---------
Signed-off-by: Yi-Pin Chen <[email protected]>
Signed-off-by: BoazBD <[email protected]>
  • Loading branch information
yipin-chen authored and BoazBD committed Oct 27, 2024
1 parent 98f63a2 commit b149c86
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
111 changes: 111 additions & 0 deletions java/client/src/main/java/glide/api/commands/servermodules/Json.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

Expand Down Expand Up @@ -849,6 +850,116 @@ public static CompletableFuture<Long> 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 <code>key</code>.
*
* @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 <code>null</code> for
* JSON values matching the root that are not boolean. If <code>key</code> doesn't exist,
* returns <code>null</code>.
* @example
* <pre>{@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);
* }</pre>
*/
public static CompletableFuture<Boolean> 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 <code>key</code>.
*
* @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 <code>null</code> for
* JSON values matching the root that are not boolean. If <code>key</code> doesn't exist,
* returns <code>null</code>.
* @example
* <pre>{@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);
* }</pre>
*/
public static CompletableFuture<Boolean> 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 <code>path</code> within the JSON document
* stored at <code>key</code>.
*
* @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
* <ul>
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
* Returns a <code>Boolean[]</code> with the toggled boolean value for every possible
* path, or <code>null</code> for JSON values matching the path that are not boolean.
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
* Returns the value of the toggled boolean in <code>path</code>. If <code>path</code>
* doesn't exist or the value at <code>path</code> isn't a boolean, an error is raised.
* </ul>
* If <code>key</code> doesn't exist, returns <code>null</code>.
* @example
* <pre>{@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}}}");
* }</pre>
*/
public static CompletableFuture<Object> 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 <code>path</code> within the JSON document
* stored at <code>key</code>.
*
* @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
* <ul>
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
* Returns a <code>Boolean[]</code> with the toggled boolean value for every possible
* path, or <code>null</code> for JSON values matching the path that are not boolean.
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
* Returns the value of the toggled boolean in <code>path</code>. If <code>path</code>
* doesn't exist or the value at <code>path</code> isn't a boolean, an error is raised.
* </ul>
* If <code>key</code> doesn't exist, returns <code>null</code>.
* @example
* <pre>{@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}}}");
* }</pre>
*/
public static CompletableFuture<Object> 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.
*
Expand Down
33 changes: 33 additions & 0 deletions java/integTest/src/test/java/glide/modules/JsonTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

0 comments on commit b149c86

Please sign in to comment.