From e8ec3a9b9f0151c6a8abccf4e30f841a33021146 Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Tue, 7 Feb 2023 14:04:50 +0100 Subject: [PATCH 1/4] feat: add json commands --- examples/nextjs/components/Header.tsx | 4 +- examples/nextjs/components/ReadBlogPost.tsx | 2 +- examples/nextjs_edge/components/Header.tsx | 4 +- .../nextjs_edge/components/ReadBlogPost.tsx | 2 +- examples/nextjs_export/components/Header.tsx | 4 +- .../nextjs_export/components/ReadBlogPost.tsx | 2 +- pkg/commands/command.ts | 23 ++- pkg/commands/json_arrappend.test.ts | 40 +++++ pkg/commands/json_arrappend.ts | 14 ++ pkg/commands/json_arrindex.test.ts | 59 +++++++ pkg/commands/json_arrindex.ts | 20 +++ pkg/commands/json_arrinsert.test.ts | 53 ++++++ pkg/commands/json_arrinsert.ts | 14 ++ pkg/commands/json_arrlen.test.ts | 27 +++ pkg/commands/json_arrlen.ts | 14 ++ pkg/commands/json_arrpop.test.ts | 28 +++ pkg/commands/json_arrpop.ts | 14 ++ pkg/commands/json_arrtrim.test.ts | 27 +++ pkg/commands/json_arrtrim.ts | 18 ++ pkg/commands/json_clear.test.ts | 33 ++++ pkg/commands/json_clear.ts | 13 ++ pkg/commands/json_del.test.ts | 25 +++ pkg/commands/json_del.ts | 13 ++ pkg/commands/json_forget.test.ts | 25 +++ pkg/commands/json_forget.ts | 13 ++ pkg/commands/json_get.test.ts | 25 +++ pkg/commands/json_get.ts | 45 +++++ pkg/commands/json_mget.test.ts | 33 ++++ pkg/commands/json_mget.ts | 15 ++ pkg/commands/json_numincrby.test.ts | 24 +++ pkg/commands/json_numincrby.ts | 14 ++ pkg/commands/json_nummultby.test.ts | 24 +++ pkg/commands/json_nummultby.ts | 14 ++ pkg/commands/json_objkeys.test.ts | 26 +++ pkg/commands/json_objkeys.ts | 14 ++ pkg/commands/json_objlen.test.ts | 22 +++ pkg/commands/json_objlen.ts | 14 ++ pkg/commands/json_resp.test.ts | 27 +++ pkg/commands/json_resp.ts | 14 ++ pkg/commands/json_set.test.ts | 46 +++++ pkg/commands/json_set.ts | 33 ++++ pkg/commands/json_strappend.test.ts | 32 ++++ pkg/commands/json_strappend.ts | 14 ++ pkg/commands/json_strlen.test.ts | 23 +++ pkg/commands/json_strlen.ts | 14 ++ pkg/commands/json_toggle.test.ts | 28 +++ pkg/commands/json_toggle.ts | 13 ++ pkg/commands/json_type.test.ts | 25 +++ pkg/commands/json_type.ts | 13 ++ pkg/commands/lpos.test.ts | 2 +- pkg/commands/mod.ts | 23 ++- pkg/commands/zadd.test.ts | 16 +- pkg/commands/zinterstore.test.ts | 24 +-- pkg/commands/zunionstore.test.ts | 24 +-- pkg/pipeline.test.ts | 5 +- pkg/pipeline.ts | 154 +++++++++++++++++ pkg/redis.ts | 163 ++++++++++++++++-- pkg/types.ts | 11 ++ 58 files changed, 1401 insertions(+), 64 deletions(-) create mode 100644 pkg/commands/json_arrappend.test.ts create mode 100644 pkg/commands/json_arrappend.ts create mode 100644 pkg/commands/json_arrindex.test.ts create mode 100644 pkg/commands/json_arrindex.ts create mode 100644 pkg/commands/json_arrinsert.test.ts create mode 100644 pkg/commands/json_arrinsert.ts create mode 100644 pkg/commands/json_arrlen.test.ts create mode 100644 pkg/commands/json_arrlen.ts create mode 100644 pkg/commands/json_arrpop.test.ts create mode 100644 pkg/commands/json_arrpop.ts create mode 100644 pkg/commands/json_arrtrim.test.ts create mode 100644 pkg/commands/json_arrtrim.ts create mode 100644 pkg/commands/json_clear.test.ts create mode 100644 pkg/commands/json_clear.ts create mode 100644 pkg/commands/json_del.test.ts create mode 100644 pkg/commands/json_del.ts create mode 100644 pkg/commands/json_forget.test.ts create mode 100644 pkg/commands/json_forget.ts create mode 100644 pkg/commands/json_get.test.ts create mode 100644 pkg/commands/json_get.ts create mode 100644 pkg/commands/json_mget.test.ts create mode 100644 pkg/commands/json_mget.ts create mode 100644 pkg/commands/json_numincrby.test.ts create mode 100644 pkg/commands/json_numincrby.ts create mode 100644 pkg/commands/json_nummultby.test.ts create mode 100644 pkg/commands/json_nummultby.ts create mode 100644 pkg/commands/json_objkeys.test.ts create mode 100644 pkg/commands/json_objkeys.ts create mode 100644 pkg/commands/json_objlen.test.ts create mode 100644 pkg/commands/json_objlen.ts create mode 100644 pkg/commands/json_resp.test.ts create mode 100644 pkg/commands/json_resp.ts create mode 100644 pkg/commands/json_set.test.ts create mode 100644 pkg/commands/json_set.ts create mode 100644 pkg/commands/json_strappend.test.ts create mode 100644 pkg/commands/json_strappend.ts create mode 100644 pkg/commands/json_strlen.test.ts create mode 100644 pkg/commands/json_strlen.ts create mode 100644 pkg/commands/json_toggle.test.ts create mode 100644 pkg/commands/json_toggle.ts create mode 100644 pkg/commands/json_type.test.ts create mode 100644 pkg/commands/json_type.ts diff --git a/examples/nextjs/components/Header.tsx b/examples/nextjs/components/Header.tsx index 52b95aac..4c1e8415 100644 --- a/examples/nextjs/components/Header.tsx +++ b/examples/nextjs/components/Header.tsx @@ -8,9 +8,9 @@ export type HeaderProps = { export default function Header({ breadcrumbOptions }: HeaderProps) { return ( -
+
-
+
diff --git a/examples/nextjs/components/ReadBlogPost.tsx b/examples/nextjs/components/ReadBlogPost.tsx index 453de8d5..e5f2697b 100644 --- a/examples/nextjs/components/ReadBlogPost.tsx +++ b/examples/nextjs/components/ReadBlogPost.tsx @@ -5,5 +5,5 @@ export default function ReadBlogPost({ }: { children: React.ReactNode; }) { - return
{children}
; + return
{children}
; } diff --git a/examples/nextjs_edge/components/Header.tsx b/examples/nextjs_edge/components/Header.tsx index 52b95aac..4c1e8415 100644 --- a/examples/nextjs_edge/components/Header.tsx +++ b/examples/nextjs_edge/components/Header.tsx @@ -8,9 +8,9 @@ export type HeaderProps = { export default function Header({ breadcrumbOptions }: HeaderProps) { return ( -
+
-
+
diff --git a/examples/nextjs_edge/components/ReadBlogPost.tsx b/examples/nextjs_edge/components/ReadBlogPost.tsx index 453de8d5..e5f2697b 100644 --- a/examples/nextjs_edge/components/ReadBlogPost.tsx +++ b/examples/nextjs_edge/components/ReadBlogPost.tsx @@ -5,5 +5,5 @@ export default function ReadBlogPost({ }: { children: React.ReactNode; }) { - return
{children}
; + return
{children}
; } diff --git a/examples/nextjs_export/components/Header.tsx b/examples/nextjs_export/components/Header.tsx index 52b95aac..4c1e8415 100644 --- a/examples/nextjs_export/components/Header.tsx +++ b/examples/nextjs_export/components/Header.tsx @@ -8,9 +8,9 @@ export type HeaderProps = { export default function Header({ breadcrumbOptions }: HeaderProps) { return ( -
+
-
+
diff --git a/examples/nextjs_export/components/ReadBlogPost.tsx b/examples/nextjs_export/components/ReadBlogPost.tsx index 453de8d5..e5f2697b 100644 --- a/examples/nextjs_export/components/ReadBlogPost.tsx +++ b/examples/nextjs_export/components/ReadBlogPost.tsx @@ -5,5 +5,5 @@ export default function ReadBlogPost({ }: { children: React.ReactNode; }) { - return
{children}
; + return
{children}
; } diff --git a/pkg/commands/command.ts b/pkg/commands/command.ts index c164db80..fb86932a 100644 --- a/pkg/commands/command.ts +++ b/pkg/commands/command.ts @@ -2,11 +2,20 @@ import { UpstashError } from "../error.ts"; import { Requester } from "../http.ts"; import { parseResponse } from "../util.ts"; -type Serialize = (data: unknown) => string; +type Serialize = (data: unknown) => string | number | boolean; type Deserialize = (result: TResult) => TData; -const defaultSerializer: Serialize = (c: unknown) => - typeof c === "string" ? c : JSON.stringify(c); +const defaultSerializer: Serialize = (c: unknown) => { + switch (typeof c) { + case "string": + case "number": + case "boolean": + return c; + + default: + return JSON.stringify(c); + } +}; export type CommandOptions = { /** @@ -27,7 +36,7 @@ export type CommandOptions = { * TResult is the raw data returned from upstash, which may need to be transformed or parsed. */ export class Command { - public readonly command: string[]; + public readonly command: (string | number | boolean)[]; public readonly serialize: Serialize; public readonly deserialize: Deserialize; /** @@ -36,7 +45,7 @@ export class Command { * You can define a custom `deserialize` function. By default we try to deserialize as json. */ constructor( - command: (string | unknown)[], + command: (string | boolean | number | unknown)[], opts?: CommandOptions, ) { this.serialize = defaultSerializer; @@ -45,7 +54,7 @@ export class Command { ? opts?.deserialize ?? parseResponse : (x) => x as unknown as TData; - this.command = command.map(this.serialize); + this.command = command.map((c) => this.serialize(c)); } /** @@ -59,7 +68,7 @@ export class Command { throw new UpstashError(error); } if (typeof result === "undefined") { - throw new Error(`Request did not return a result`); + throw new Error("Request did not return a result"); } return this.deserialize(result); diff --git a/pkg/commands/json_arrappend.test.ts b/pkg/commands/json_arrappend.test.ts new file mode 100644 index 00000000..65b94a98 --- /dev/null +++ b/pkg/commands/json_arrappend.test.ts @@ -0,0 +1,40 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonArrAppendCommand } from "./json_arrappend.ts"; +import { JsonGetCommand } from "./json_get.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Add a new color to a list of product colors", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver"], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonArrAppendCommand([key, "$.colors", '"blue"']).exec( + client, + ); + assertEquals(res2, [3]); + const res3 = await new JsonGetCommand([key]).exec(client); + assertEquals(res3, { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver", "blue"], + }); +}); diff --git a/pkg/commands/json_arrappend.ts b/pkg/commands/json_arrappend.ts new file mode 100644 index 00000000..fd6f3bb8 --- /dev/null +++ b/pkg/commands/json_arrappend.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrappend + */ +export class JsonArrAppendCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path: string, ...values: TData], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.ARRAPPEND", ...cmd], opts); + } +} diff --git a/pkg/commands/json_arrindex.test.ts b/pkg/commands/json_arrindex.test.ts new file mode 100644 index 00000000..d5b186f5 --- /dev/null +++ b/pkg/commands/json_arrindex.test.ts @@ -0,0 +1,59 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonArrAppendCommand } from "./json_arrappend.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { JsonArrInsertCommand } from "./json_arrinsert.ts"; +import { JsonArrIndexCommand } from "./json_arrindex.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Find the specific place of a color in a list of product colors", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver"], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonArrAppendCommand([key, "$.colors", '"blue"']).exec( + client, + ); + assertEquals(res2, [3]); + const res3 = await new JsonGetCommand([key]).exec(client); + assertEquals(res3, { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver", "blue"], + }); + const res4 = await new JsonGetCommand([key, "$.colors[*]"]).exec(client); + assertEquals(res4, ["black", "silver", "blue"]); + + const res5 = await new JsonArrInsertCommand([ + key, + "$.colors", + 2, + '"yellow"', + '"gold"', + ]).exec(client); + assertEquals(res5, [5]); + const res6 = await new JsonGetCommand([key, "$.colors"]).exec(client); + assertEquals(res6, [["black", "silver", "yellow", "gold", "blue"]]); + + const res7 = await new JsonArrIndexCommand([key, "$..colors", '"silver"']) + .exec(client); + assertEquals(res7, [1]); +}); diff --git a/pkg/commands/json_arrindex.ts b/pkg/commands/json_arrindex.ts new file mode 100644 index 00000000..9bcc2de4 --- /dev/null +++ b/pkg/commands/json_arrindex.ts @@ -0,0 +1,20 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrindex + */ +export class JsonArrIndexCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [ + key: string, + path: string, + value: TValue, + start?: number, + stop?: number, + ], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.ARRINDEX", ...cmd], opts); + } +} diff --git a/pkg/commands/json_arrinsert.test.ts b/pkg/commands/json_arrinsert.test.ts new file mode 100644 index 00000000..a42bffe5 --- /dev/null +++ b/pkg/commands/json_arrinsert.test.ts @@ -0,0 +1,53 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonArrAppendCommand } from "./json_arrappend.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { JsonArrInsertCommand } from "./json_arrinsert.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Add new colors to a specific place in a list of product colors", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver"], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonArrAppendCommand([key, "$.colors", '"blue"']).exec( + client, + ); + assertEquals(res2, [3]); + const res3 = await new JsonGetCommand([key]).exec(client); + assertEquals(res3, { + "name": "Noise-cancelling Bluetooth headphones", + "description": + "Wireless Bluetooth headphones with noise-cancelling technology", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 99.98, + "stock": 25, + "colors": ["black", "silver", "blue"], + }); + const res4 = await new JsonGetCommand([key, "$.colors"]).exec(client); + assertEquals(res4, [["black", "silver", "blue"]]); + const res5 = await new JsonArrInsertCommand([ + key, + "$.colors", + 2, + '"yellow"', + '"gold"', + ]).exec(client); + assertEquals(res5, [5]); + const res6 = await new JsonGetCommand([key, "$.colors"]).exec(client); + assertEquals(res6, [["black", "silver", "yellow", "gold", "blue"]]); +}); diff --git a/pkg/commands/json_arrinsert.ts b/pkg/commands/json_arrinsert.ts new file mode 100644 index 00000000..8ca7e360 --- /dev/null +++ b/pkg/commands/json_arrinsert.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrinsert + */ +export class JsonArrInsertCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path: string, index: number, ...values: TData], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.ARRINSERT", ...cmd], opts); + } +} diff --git a/pkg/commands/json_arrlen.test.ts b/pkg/commands/json_arrlen.test.ts new file mode 100644 index 00000000..25b3f62f --- /dev/null +++ b/pkg/commands/json_arrlen.test.ts @@ -0,0 +1,27 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonArrLenCommand } from "./json_arrlen.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Get lengths of JSON arrays in a document", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "name": "Wireless earbuds", + "description": "Wireless Bluetooth in-ear headphones", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 64.99, + "stock": 17, + "colors": ["black", "white"], + "max_level": [80, 100, 120], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonArrLenCommand([key, "$.max_level"]).exec(client); + assertEquals(res2, [3]); +}); diff --git a/pkg/commands/json_arrlen.ts b/pkg/commands/json_arrlen.ts new file mode 100644 index 00000000..9f11943d --- /dev/null +++ b/pkg/commands/json_arrlen.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrlen + */ +export class JsonArrLenCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.ARRLEN", cmd[0], cmd[1] ?? "$"], opts); + } +} diff --git a/pkg/commands/json_arrpop.test.ts b/pkg/commands/json_arrpop.test.ts new file mode 100644 index 00000000..7b6b742f --- /dev/null +++ b/pkg/commands/json_arrpop.test.ts @@ -0,0 +1,28 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { JsonArrPopCommand } from "./json_arrpop.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Pop a value from an index and insert a new value", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "max_level": [80, 90, 100, 120], + }]).exec(client); + assertEquals(res1, "OK"); + + const res2 = await new JsonArrPopCommand([key, "$.max_level", 0]).exec( + client, + ); + assertEquals(res2, [80]); + + const res3 = await new JsonGetCommand([key, "$.max_level"]).exec(client); + assertEquals(res3, [[90, 100, 120]]); +}); diff --git a/pkg/commands/json_arrpop.ts b/pkg/commands/json_arrpop.ts new file mode 100644 index 00000000..bda54ea5 --- /dev/null +++ b/pkg/commands/json_arrpop.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrpop + */ +export class JsonArrPopCommand + extends Command<(null | string)[], (TData | null)[]> { + constructor( + cmd: [key: string, path?: string, index?: number], + opts?: CommandOptions<(null | string)[], (TData | null)[]>, + ) { + super(["JSON.ARRPOP", ...cmd], opts); + } +} diff --git a/pkg/commands/json_arrtrim.test.ts b/pkg/commands/json_arrtrim.test.ts new file mode 100644 index 00000000..30237e88 --- /dev/null +++ b/pkg/commands/json_arrtrim.test.ts @@ -0,0 +1,27 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonArrAppendCommand } from "./json_arrappend.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { JsonArrTrimCommand } from "./json_arrtrim.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Trim an array to a specific set of values", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + a: [1], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonArrAppendCommand([key, "$.a", 2]).exec(client); + assertEquals(res2.sort(), [2]); + const res3 = await new JsonArrTrimCommand([key, "$.a", 1, 1]).exec(client); + assertEquals(res3, [1]); + const res4 = await new JsonGetCommand([key, "$.a"]).exec(client); + assertEquals(res4, [[2]]); +}); diff --git a/pkg/commands/json_arrtrim.ts b/pkg/commands/json_arrtrim.ts new file mode 100644 index 00000000..d306a2d2 --- /dev/null +++ b/pkg/commands/json_arrtrim.ts @@ -0,0 +1,18 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.arrtrim + */ +export class JsonArrTrimCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path?: string, start?: number, stop?: number], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + const path = cmd[1] ?? "$"; + const start = cmd[2] ?? 0; + const stop = cmd[3] ?? 0; + + super(["JSON.ARRTRIM", cmd[0], path, start, stop], opts); + } +} diff --git a/pkg/commands/json_clear.test.ts b/pkg/commands/json_clear.test.ts new file mode 100644 index 00000000..85a0cf0d --- /dev/null +++ b/pkg/commands/json_clear.test.ts @@ -0,0 +1,33 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonClearCommand } from "./json_clear.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Clear container values and set numeric values to 0", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([ + key, + "$", + '{"obj":{"a":1, "b":2}, "arr":[1,2,3], "str": "foo", "bool": true, "int": 42, "float": 3.14}', + ]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonClearCommand([key, "$.*"]).exec(client); + assertEquals(res2, 4); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ + obj: {}, + arr: [], + str: "foo", + bool: true, + int: 0, + float: 0, + }]); +}); diff --git a/pkg/commands/json_clear.ts b/pkg/commands/json_clear.ts new file mode 100644 index 00000000..a21b0955 --- /dev/null +++ b/pkg/commands/json_clear.ts @@ -0,0 +1,13 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.clear + */ +export class JsonClearCommand extends Command { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions, + ) { + super(["JSON.CLEAR", ...cmd], opts); + } +} diff --git a/pkg/commands/json_del.test.ts b/pkg/commands/json_del.test.ts new file mode 100644 index 00000000..6fcf0211 --- /dev/null +++ b/pkg/commands/json_del.test.ts @@ -0,0 +1,25 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonDelCommand } from "./json_del.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Delete a value", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + a: 1, + nested: { a: 2, b: 3 }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonDelCommand([key, "$..a"]).exec(client); + assertEquals(res2, 2); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ nested: { b: 3 } }]); +}); diff --git a/pkg/commands/json_del.ts b/pkg/commands/json_del.ts new file mode 100644 index 00000000..0727859d --- /dev/null +++ b/pkg/commands/json_del.ts @@ -0,0 +1,13 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.del + */ +export class JsonDelCommand extends Command { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions, + ) { + super(["JSON.DEL", ...cmd], opts); + } +} diff --git a/pkg/commands/json_forget.test.ts b/pkg/commands/json_forget.test.ts new file mode 100644 index 00000000..721b93a0 --- /dev/null +++ b/pkg/commands/json_forget.test.ts @@ -0,0 +1,25 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonForgetCommand } from "./json_forget.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Delete a value", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + a: 1, + nested: { a: 2, b: 3 }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonForgetCommand([key, "$..a"]).exec(client); + assertEquals(res2, 2); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ nested: { b: 3 } }]); +}); diff --git a/pkg/commands/json_forget.ts b/pkg/commands/json_forget.ts new file mode 100644 index 00000000..b427bc32 --- /dev/null +++ b/pkg/commands/json_forget.ts @@ -0,0 +1,13 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.forget + */ +export class JsonForgetCommand extends Command { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions, + ) { + super(["JSON.DEL", ...cmd], opts); + } +} diff --git a/pkg/commands/json_get.test.ts b/pkg/commands/json_get.test.ts new file mode 100644 index 00000000..1b058a83 --- /dev/null +++ b/pkg/commands/json_get.test.ts @@ -0,0 +1,25 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Return the value at path in JSON serialized form", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": 2, + "b": 3, + "nested": { "a": 4, "b": null }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonGetCommand([key, "$..b"]).exec(client); + assertEquals(res2, [null, 3]); + const res3 = await new JsonGetCommand([key, "..a", "$..b"]).exec(client); + assertEquals(res3, { "$..b": [null, 3], "..a": [4, 2] }); +}); diff --git a/pkg/commands/json_get.ts b/pkg/commands/json_get.ts new file mode 100644 index 00000000..763735d2 --- /dev/null +++ b/pkg/commands/json_get.ts @@ -0,0 +1,45 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.get + */ +export class JsonGetCommand< + TData extends + | (unknown | Record) + | (unknown | Record)[], +> extends Command { + constructor( + cmd: + | [ + key: string, + opts?: { indent?: string; newline?: string; space?: string }, + ...path: string[], + ] + | [key: string, ...path: string[]], + opts?: CommandOptions, + ) { + const command = ["JSON.GET"]; + if (typeof cmd[1] === "string") { + // @ts-ignore - we know this is a string + command.push(...cmd); + } else { + command.push(cmd[0]); + + if (cmd[1]) { + if (cmd[1].indent) { + command.push("INDENT", cmd[1].indent); + } + if (cmd[1].newline) { + command.push("NEWLINE", cmd[1].newline); + } + if (cmd[1].space) { + command.push("SPACE", cmd[1].space); + } + } + // @ts-ignore - we know this is a string + command.push(...cmd.slice(2)); + } + + super(command, opts); + } +} diff --git a/pkg/commands/json_mget.test.ts b/pkg/commands/json_mget.test.ts new file mode 100644 index 00000000..84d0103c --- /dev/null +++ b/pkg/commands/json_mget.test.ts @@ -0,0 +1,33 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonMGetCommand } from "./json_mget.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Return the values at path from multiple key arguments", async () => { + const key1 = newKey(); + const key2 = newKey(); + const res1 = await new JsonSetCommand([key1, "$", { + a: 1, + b: 2, + nested: { a: 3 }, + c: null, + }]).exec(client); + assertEquals(res1, "OK"); + + const res2 = await new JsonSetCommand([key2, "$", { + a: 4, + b: 5, + nested: { a: 6 }, + c: null, + }]).exec(client); + assertEquals(res2, "OK"); + const res3 = await new JsonMGetCommand([[key1, key2], "$..a"]).exec(client); + assertEquals(res3, [[3, 1], [6, 4]]); +}); diff --git a/pkg/commands/json_mget.ts b/pkg/commands/json_mget.ts new file mode 100644 index 00000000..6cc0973a --- /dev/null +++ b/pkg/commands/json_mget.ts @@ -0,0 +1,15 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.mget + */ +export class JsonMGetCommand< + TData extends (unknown | Record)[], +> extends Command { + constructor( + cmd: [keys: string[], path: string], + opts?: CommandOptions, + ) { + super(["JSON.MGET", ...cmd[0], cmd[1]], opts); + } +} diff --git a/pkg/commands/json_numincrby.test.ts b/pkg/commands/json_numincrby.test.ts new file mode 100644 index 00000000..7d09a00d --- /dev/null +++ b/pkg/commands/json_numincrby.test.ts @@ -0,0 +1,24 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonNumIncrByCommand } from "./json_numincrby.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the length", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": "b", + "b": [{ "a": 2 }, { "a": 5 }, { "a": "c" }], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonNumIncrByCommand([key, "$.a", 2]).exec(client); + assertEquals(res2.sort(), [null]); + const res3 = await new JsonNumIncrByCommand([key, "$..a", 2]).exec(client); + assertEquals(res3.sort(), [4, 7, null, null]); +}); diff --git a/pkg/commands/json_numincrby.ts b/pkg/commands/json_numincrby.ts new file mode 100644 index 00000000..0aeda08c --- /dev/null +++ b/pkg/commands/json_numincrby.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.numincrby + */ +export class JsonNumIncrByCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path: string, value: number], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.NUMINCRBY", ...cmd], opts); + } +} diff --git a/pkg/commands/json_nummultby.test.ts b/pkg/commands/json_nummultby.test.ts new file mode 100644 index 00000000..c6481b7f --- /dev/null +++ b/pkg/commands/json_nummultby.test.ts @@ -0,0 +1,24 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonNumMultByCommand } from "./json_nummultby.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the length", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": "b", + "b": [{ "a": 2 }, { "a": 5 }, { "a": "c" }], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonNumMultByCommand([key, "$.a", 2]).exec(client); + assertEquals(res2.sort(), [null]); + const res3 = await new JsonNumMultByCommand([key, "$..a", 2]).exec(client); + assertEquals(res3.sort(), [10, 4, null, null]); +}); diff --git a/pkg/commands/json_nummultby.ts b/pkg/commands/json_nummultby.ts new file mode 100644 index 00000000..ccd552f4 --- /dev/null +++ b/pkg/commands/json_nummultby.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.nummultby + */ +export class JsonNumMultByCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path: string, value: number], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.NUMMULTBY", ...cmd], opts); + } +} diff --git a/pkg/commands/json_objkeys.test.ts b/pkg/commands/json_objkeys.test.ts new file mode 100644 index 00000000..5e31f020 --- /dev/null +++ b/pkg/commands/json_objkeys.test.ts @@ -0,0 +1,26 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonObjKeysCommand } from "./json_objkeys.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the keys", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": [3], + "nested": { "a": { "b": 2, "c": 1 } }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonObjKeysCommand([key, "$..a"]).exec(client); + for (const e of res2.sort()) { + if (e === null) continue; + e.sort(); + } + assertEquals(res2, [["b", "c"], null]); +}); diff --git a/pkg/commands/json_objkeys.ts b/pkg/commands/json_objkeys.ts new file mode 100644 index 00000000..efd2ad94 --- /dev/null +++ b/pkg/commands/json_objkeys.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.objkeys + */ +export class JsonObjKeysCommand + extends Command<(string[] | null)[], (string[] | null)[]> { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions<(string[] | null)[], (string[] | null)[]>, + ) { + super(["JSON.OBJKEYS", ...cmd], opts); + } +} diff --git a/pkg/commands/json_objlen.test.ts b/pkg/commands/json_objlen.test.ts new file mode 100644 index 00000000..adbc4164 --- /dev/null +++ b/pkg/commands/json_objlen.test.ts @@ -0,0 +1,22 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonObjLenCommand } from "./json_objlen.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the length", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": [3], + "nested": { "a": { "b": 2, "c": 1 } }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonObjLenCommand([key, "$..a"]).exec(client); + assertEquals(res2, [2, null]); +}); diff --git a/pkg/commands/json_objlen.ts b/pkg/commands/json_objlen.ts new file mode 100644 index 00000000..fef70ce8 --- /dev/null +++ b/pkg/commands/json_objlen.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.objlen + */ +export class JsonObjLenCommand + extends Command<(number | null)[], (number | null)[]> { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions<(number | null)[], (number | null)[]>, + ) { + super(["JSON.OBJLEN", ...cmd], opts); + } +} diff --git a/pkg/commands/json_resp.test.ts b/pkg/commands/json_resp.test.ts new file mode 100644 index 00000000..608a34df --- /dev/null +++ b/pkg/commands/json_resp.test.ts @@ -0,0 +1,27 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonRespCommand } from "./json_resp.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Return an array of RESP details about a document", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "name": "Wireless earbuds", + "description": "Wireless Bluetooth in-ear headphones", + "connection": { "wireless": true, "type": "Bluetooth" }, + "price": 64.99, + "stock": 17, + "colors": ["black", "white"], + "max_level": [80, 100, 120], + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonRespCommand([key]).exec(client); + assertEquals(res2.length, 15); +}); diff --git a/pkg/commands/json_resp.ts b/pkg/commands/json_resp.ts new file mode 100644 index 00000000..9216e766 --- /dev/null +++ b/pkg/commands/json_resp.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.resp + */ +export class JsonRespCommand + extends Command { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions, + ) { + super(["JSON.RESP", ...cmd], opts); + } +} diff --git a/pkg/commands/json_set.test.ts b/pkg/commands/json_set.test.ts new file mode 100644 index 00000000..8aa0c28a --- /dev/null +++ b/pkg/commands/json_set.test.ts @@ -0,0 +1,46 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("replcae an existing value", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { a: 2 }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonSetCommand([key, "$.a", 3]).exec(client); + assertEquals(res2, "OK"); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ a: 3 }]); +}); + +Deno.test("add a new value", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { a: 2 }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonSetCommand([key, "$.b", 8]).exec(client); + assertEquals(res2, "OK"); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ a: 2, b: 8 }]); +}); + +Deno.test("update multi-paths", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + f1: { a: 1 }, + f2: { a: 2 }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonSetCommand([key, "$..a", 3]).exec(client); + assertEquals(res2, "OK"); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + // TODO: should be [{ f1: { a: 3 }, f2: { a: 3 } }] + // I reported this to mehmet + assertEquals(res3, [{ f1: { a: 3 }, f2: { a: 3 }, a: 3 }]); +}); diff --git a/pkg/commands/json_set.ts b/pkg/commands/json_set.ts new file mode 100644 index 00000000..e21b36d2 --- /dev/null +++ b/pkg/commands/json_set.ts @@ -0,0 +1,33 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.set + */ +export class JsonSetCommand< + TData extends + | number + | string + | boolean + | Record + | (number | string | boolean | Record)[], +> extends Command<"OK" | null, "OK" | null> { + constructor( + cmd: [ + key: string, + path: string, + value: TData, + opts?: { nx: true; xx?: never } | { nx?: never; xx: true }, + ], + opts?: CommandOptions<"OK" | null, "OK" | null>, + ) { + const command = ["JSON.SET", cmd[0], cmd[1], cmd[2]]; + if (cmd[3]) { + if (cmd[3].nx) { + command.push("NX"); + } else if (cmd[3].xx) { + command.push("XX"); + } + } + super(command, opts); + } +} diff --git a/pkg/commands/json_strappend.test.ts b/pkg/commands/json_strappend.test.ts new file mode 100644 index 00000000..d9c2e967 --- /dev/null +++ b/pkg/commands/json_strappend.test.ts @@ -0,0 +1,32 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonGetCommand } from "./json_get.ts"; +import { JsonStrAppendCommand } from "./json_strappend.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Add 'baz' to existing string", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": "foo", + "nested": { "a": "hello" }, + "nested2": { "a": 31 }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonStrAppendCommand([key, "$..a", '"baz"']).exec( + client, + ); + assertEquals(res2.sort(), [6, 8, null]); + const res3 = await new JsonGetCommand([key]).exec(client); + assertEquals(res3, { + "a": "foobaz", + "nested": { "a": "hellobaz" }, + "nested2": { "a": 31 }, + }); +}); diff --git a/pkg/commands/json_strappend.ts b/pkg/commands/json_strappend.ts new file mode 100644 index 00000000..badc3ea2 --- /dev/null +++ b/pkg/commands/json_strappend.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.strappend + */ +export class JsonStrAppendCommand + extends Command<(null | string)[], (null | number)[]> { + constructor( + cmd: [key: string, path: string, value: string], + opts?: CommandOptions<(null | string)[], (null | number)[]>, + ) { + super(["JSON.STRAPPEND", ...cmd], opts); + } +} diff --git a/pkg/commands/json_strlen.test.ts b/pkg/commands/json_strlen.test.ts new file mode 100644 index 00000000..bbbb7bb4 --- /dev/null +++ b/pkg/commands/json_strlen.test.ts @@ -0,0 +1,23 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonStrLenCommand } from "./json_strlen.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the length", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": "foo", + "nested": { "a": "hello" }, + "nested2": { "a": 31 }, + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonStrLenCommand([key, "$..a"]).exec(client); + assertEquals(res2.sort(), [3, 5, null]); +}); diff --git a/pkg/commands/json_strlen.ts b/pkg/commands/json_strlen.ts new file mode 100644 index 00000000..816c02d4 --- /dev/null +++ b/pkg/commands/json_strlen.ts @@ -0,0 +1,14 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.strlen + */ +export class JsonStrLenCommand + extends Command<(number | null)[], (number | null)[]> { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions<(number | null)[], (number | null)[]>, + ) { + super(["JSON.STRLEN", ...cmd], opts); + } +} diff --git a/pkg/commands/json_toggle.test.ts b/pkg/commands/json_toggle.test.ts new file mode 100644 index 00000000..b3a3af48 --- /dev/null +++ b/pkg/commands/json_toggle.test.ts @@ -0,0 +1,28 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonToggleCommand } from "./json_toggle.ts"; +import { JsonGetCommand } from "./json_get.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("Toogle a Boolean value stored at path", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { "bool": true }]).exec( + client, + ); + assertEquals(res1, "OK"); + const res2 = await new JsonToggleCommand([key, "$.bool"]).exec(client); + assertEquals(res2, [0]); + const res3 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res3, [{ "bool": false }]); + const res4 = await new JsonToggleCommand([key, "$.bool"]).exec(client); + assertEquals(res4, [1]); + const res5 = await new JsonGetCommand([key, "$"]).exec(client); + assertEquals(res5, [{ "bool": true }]); +}); diff --git a/pkg/commands/json_toggle.ts b/pkg/commands/json_toggle.ts new file mode 100644 index 00000000..6252b78e --- /dev/null +++ b/pkg/commands/json_toggle.ts @@ -0,0 +1,13 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.toggle + */ +export class JsonToggleCommand extends Command { + constructor( + cmd: [key: string, path: string], + opts?: CommandOptions, + ) { + super(["JSON.TOGGLE", ...cmd], opts); + } +} diff --git a/pkg/commands/json_type.test.ts b/pkg/commands/json_type.test.ts new file mode 100644 index 00000000..d0dd0bee --- /dev/null +++ b/pkg/commands/json_type.test.ts @@ -0,0 +1,25 @@ +import { keygen, newHttpClient } from "../test-utils.ts"; +import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; + +import { JsonSetCommand } from "./json_set.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { JsonTypeCommand } from "./json_type.ts"; + +const client = newHttpClient(); + +const { newKey, cleanup } = keygen(); +afterAll(cleanup); + +Deno.test("return the length", async () => { + const key = newKey(); + const res1 = await new JsonSetCommand([key, "$", { + "a": 2, + "nested": { "a": true }, + "foo": "bar", + }]).exec(client); + assertEquals(res1, "OK"); + const res2 = await new JsonTypeCommand([key, "$..foo"]).exec(client); + assertEquals(res2.sort(), ["string"]); + const res3 = await new JsonTypeCommand([key, "$..a"]).exec(client); + assertEquals(res3.sort(), ["boolean", "integer"]); +}); diff --git a/pkg/commands/json_type.ts b/pkg/commands/json_type.ts new file mode 100644 index 00000000..e01e15ab --- /dev/null +++ b/pkg/commands/json_type.ts @@ -0,0 +1,13 @@ +import { Command, CommandOptions } from "./command.ts"; + +/** + * @see https://redis.io/commands/json.type + */ +export class JsonTypeCommand extends Command { + constructor( + cmd: [key: string, path?: string], + opts?: CommandOptions, + ) { + super(["JSON.TYPE", ...cmd], opts); + } +} diff --git a/pkg/commands/lpos.test.ts b/pkg/commands/lpos.test.ts index dee87ddc..bfaedb1e 100644 --- a/pkg/commands/lpos.test.ts +++ b/pkg/commands/lpos.test.ts @@ -28,7 +28,7 @@ Deno.test("with rank", async (t) => { client, ); const cmd = new LPosCommand([key, "c", { rank: 2 }]); - assertEquals(cmd.command, ["lpos", key, "c", "rank", "2"]); + assertEquals(cmd.command, ["lpos", key, "c", "rank", 2]); const res = await cmd.exec(client); assertEquals(res, 6); }); diff --git a/pkg/commands/mod.ts b/pkg/commands/mod.ts index 6c2ea14e..30d8b62c 100644 --- a/pkg/commands/mod.ts +++ b/pkg/commands/mod.ts @@ -23,7 +23,6 @@ export * from "./getset.ts"; export * from "./hdel.ts"; export * from "./hexists.ts"; export * from "./hget.ts"; -export * from "./smismember.ts"; export * from "./hgetall.ts"; export * from "./hincrby.ts"; export * from "./hincrbyfloat.ts"; @@ -40,6 +39,27 @@ export * from "./hvals.ts"; export * from "./incr.ts"; export * from "./incrby.ts"; export * from "./incrbyfloat.ts"; +export * from "./json_arrappend.ts"; +export * from "./json_arrindex.ts"; +export * from "./json_arrinsert.ts"; +export * from "./json_arrlen.ts"; +export * from "./json_arrpop.ts"; +export * from "./json_arrtrim.ts"; +export * from "./json_clear.ts"; +export * from "./json_del.ts"; +export * from "./json_forget.ts"; +export * from "./json_get.ts"; +export * from "./json_mget.ts"; +export * from "./json_numincrby.ts"; +export * from "./json_nummultby.ts"; +export * from "./json_objkeys.ts"; +export * from "./json_objlen.ts"; +export * from "./json_resp.ts"; +export * from "./json_set.ts"; +export * from "./json_strappend.ts"; +export * from "./json_strlen.ts"; +export * from "./json_toggle.ts"; +export * from "./json_type.ts"; export * from "./keys.ts"; export * from "./lindex.ts"; export * from "./linsert.ts"; @@ -86,6 +106,7 @@ export * from "./sinter.ts"; export * from "./sinterstore.ts"; export * from "./sismember.ts"; export * from "./smembers.ts"; +export * from "./smismember.ts"; export * from "./smove.ts"; export * from "./spop.ts"; export * from "./srandmember.ts"; diff --git a/pkg/commands/zadd.test.ts b/pkg/commands/zadd.test.ts index 5f75c8ee..8526046e 100644 --- a/pkg/commands/zadd.test.ts +++ b/pkg/commands/zadd.test.ts @@ -15,7 +15,7 @@ Deno.test("command format", async (t) => { await t.step("build the correct command", () => { assertEquals( new ZAddCommand(["key", { score: 0, member: "member" }]).command, - ["zadd", "key", "0", "member"], + ["zadd", "key", 0, "member"], ); }); }); @@ -24,7 +24,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZAddCommand(["key", { nx: true }, { score: 0, member: "member" }]) .command, - ["zadd", "key", "nx", "0", "member"], + ["zadd", "key", "nx", 0, "member"], ); }); }); @@ -33,7 +33,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZAddCommand(["key", { xx: true }, { score: 0, member: "member" }]) .command, - ["zadd", "key", "xx", "0", "member"], + ["zadd", "key", "xx", 0, "member"], ); }); }); @@ -42,7 +42,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZAddCommand(["key", { ch: true }, { score: 0, member: "member" }]) .command, - ["zadd", "key", "ch", "0", "member"], + ["zadd", "key", "ch", 0, "member"], ); }); }); @@ -51,7 +51,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZAddCommand(["key", { incr: true }, { score: 0, member: "member" }]) .command, - ["zadd", "key", "incr", "0", "member"], + ["zadd", "key", "incr", 0, "member"], ); }); }); @@ -63,7 +63,7 @@ Deno.test("command format", async (t) => { { nx: true, ch: true }, { score: 0, member: "member" }, ]).command, - ["zadd", "key", "nx", "ch", "0", "member"], + ["zadd", "key", "nx", "ch", 0, "member"], ); }); }); @@ -75,7 +75,7 @@ Deno.test("command format", async (t) => { { nx: true, ch: true, incr: true }, { score: 0, member: "member" }, ]).command, - ["zadd", "key", "nx", "ch", "incr", "0", "member"], + ["zadd", "key", "nx", "ch", "incr", 0, "member"], ); }); }); @@ -88,7 +88,7 @@ Deno.test("command format", async (t) => { { score: 0, member: "member" }, { score: 1, member: "member1" }, ]).command, - ["zadd", "key", "nx", "0", "member", "1", "member1"], + ["zadd", "key", "nx", 0, "member", 1, "member1"], ); }); }); diff --git a/pkg/commands/zinterstore.test.ts b/pkg/commands/zinterstore.test.ts index 28312a0d..d31230e7 100644 --- a/pkg/commands/zinterstore.test.ts +++ b/pkg/commands/zinterstore.test.ts @@ -16,7 +16,7 @@ Deno.test("command format", async (t) => { assertEquals(new ZInterStoreCommand(["destination", 1, "key"]).command, [ "zinterstore", "destination", - "1", + 1, "key", ]); }); @@ -25,7 +25,7 @@ Deno.test("command format", async (t) => { await t.step("builds the correct command", () => { assertEquals( new ZInterStoreCommand(["destination", 2, ["key1", "key2"]]).command, - ["zinterstore", "destination", "2", "key1", "key2"], + ["zinterstore", "destination", 2, "key1", "key2"], ); }); }); @@ -34,7 +34,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZInterStoreCommand(["destination", 1, "key", { weight: 4 }]) .command, - ["zinterstore", "destination", "1", "key", "weights", "4"], + ["zinterstore", "destination", 1, "key", "weights", 4], ); }); }); @@ -47,12 +47,12 @@ Deno.test("command format", async (t) => { [ "zinterstore", "destination", - "2", + 2, "key1", "key2", "weights", - "2", - "3", + 2, + 3, ], ); }); @@ -63,7 +63,7 @@ Deno.test("command format", async (t) => { new ZInterStoreCommand(["destination", 1, "key", { aggregate: "sum", }]).command, - ["zinterstore", "destination", "1", "key", "aggregate", "sum"], + ["zinterstore", "destination", 1, "key", "aggregate", "sum"], ); }); }); @@ -73,7 +73,7 @@ Deno.test("command format", async (t) => { new ZInterStoreCommand(["destination", 1, "key", { aggregate: "min", }]).command, - ["zinterstore", "destination", "1", "key", "aggregate", "min"], + ["zinterstore", "destination", 1, "key", "aggregate", "min"], ); }); }); @@ -83,7 +83,7 @@ Deno.test("command format", async (t) => { new ZInterStoreCommand(["destination", 1, "key", { aggregate: "max", }]).command, - ["zinterstore", "destination", "1", "key", "aggregate", "max"], + ["zinterstore", "destination", 1, "key", "aggregate", "max"], ); }); }); @@ -98,12 +98,12 @@ Deno.test("command format", async (t) => { [ "zinterstore", "destination", - "2", + 2, "key1", "key2", "weights", - "4", - "2", + 4, + 2, "aggregate", "max", ], diff --git a/pkg/commands/zunionstore.test.ts b/pkg/commands/zunionstore.test.ts index d948f832..cbfdb5fd 100644 --- a/pkg/commands/zunionstore.test.ts +++ b/pkg/commands/zunionstore.test.ts @@ -16,7 +16,7 @@ Deno.test("command format", async (t) => { assertEquals(new ZUnionStoreCommand(["destination", 1, "key"]).command, [ "zunionstore", "destination", - "1", + 1, "key", ]); }); @@ -25,7 +25,7 @@ Deno.test("command format", async (t) => { await t.step("builds the correct command", () => { assertEquals( new ZUnionStoreCommand(["destination", 2, ["key1", "key2"]]).command, - ["zunionstore", "destination", "2", "key1", "key2"], + ["zunionstore", "destination", 2, "key1", "key2"], ); }); }); @@ -34,7 +34,7 @@ Deno.test("command format", async (t) => { assertEquals( new ZUnionStoreCommand(["destination", 1, "key", { weight: 4 }]) .command, - ["zunionstore", "destination", "1", "key", "weights", "4"], + ["zunionstore", "destination", 1, "key", "weights", 4], ); }); }); @@ -47,12 +47,12 @@ Deno.test("command format", async (t) => { [ "zunionstore", "destination", - "2", + 2, "key1", "key2", "weights", - "2", - "3", + 2, + 3, ], ); }); @@ -63,7 +63,7 @@ Deno.test("command format", async (t) => { new ZUnionStoreCommand(["destination", 1, "key", { aggregate: "sum", }]).command, - ["zunionstore", "destination", "1", "key", "aggregate", "sum"], + ["zunionstore", "destination", 1, "key", "aggregate", "sum"], ); }); }); @@ -73,7 +73,7 @@ Deno.test("command format", async (t) => { new ZUnionStoreCommand(["destination", 1, "key", { aggregate: "min", }]).command, - ["zunionstore", "destination", "1", "key", "aggregate", "min"], + ["zunionstore", "destination", 1, "key", "aggregate", "min"], ); }); }); @@ -83,7 +83,7 @@ Deno.test("command format", async (t) => { new ZUnionStoreCommand(["destination", 1, "key", { aggregate: "max", }]).command, - ["zunionstore", "destination", "1", "key", "aggregate", "max"], + ["zunionstore", "destination", 1, "key", "aggregate", "max"], ); }); }); @@ -98,12 +98,12 @@ Deno.test("command format", async (t) => { [ "zunionstore", "destination", - "2", + 2, "key1", "key2", "weights", - "4", - "2", + 4, + 2, "aggregate", "max", ], diff --git a/pkg/pipeline.test.ts b/pkg/pipeline.test.ts index 4d8f56f6..bcb9aa31 100644 --- a/pkg/pipeline.test.ts +++ b/pkg/pipeline.test.ts @@ -216,9 +216,10 @@ Deno.test("use all the things", async (t) => { .zrevrank(newKey(), "member") .zscan(newKey(), 0) .zscore(newKey(), "member") - .zunionstore(newKey(), 1, [newKey()]); + .zunionstore(newKey(), 1, [newKey()]) + .json.set(newKey(), "$", { hello: "world" }); const res = await p.exec(); - assertEquals(res.length, 119); + assertEquals(res.length, 120); }); }); diff --git a/pkg/pipeline.ts b/pkg/pipeline.ts index 0f7c736e..677c5fd9 100644 --- a/pkg/pipeline.ts +++ b/pkg/pipeline.ts @@ -38,6 +38,27 @@ import { IncrByCommand, IncrByFloatCommand, IncrCommand, + JsonArrAppendCommand, + JsonArrIndexCommand, + JsonArrInsertCommand, + JsonArrLenCommand, + JsonArrPopCommand, + JsonArrTrimCommand, + JsonClearCommand, + JsonDelCommand, + JsonForgetCommand, + JsonGetCommand, + JsonMGetCommand, + JsonNumIncrByCommand, + JsonNumMultByCommand, + JsonObjKeysCommand, + JsonObjLenCommand, + JsonRespCommand, + JsonSetCommand, + JsonStrAppendCommand, + JsonStrLenCommand, + JsonToggleCommand, + JsonTypeCommand, KeysCommand, LIndexCommand, LInsertCommand, @@ -1043,4 +1064,137 @@ export class Pipeline { */ zunionstore = (...args: CommandArgs) => this.chain(new ZUnionStoreCommand(args, this.commandOptions)); + + /** + * @see https://redis.io/commands/?group=json + */ + get json() { + return { + /** + * @see https://redis.io/commands/json.arrappend + */ + arrappend: (...args: CommandArgs) => + this.chain(new JsonArrAppendCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.arrindex + */ + arrindex: (...args: CommandArgs) => + this.chain(new JsonArrIndexCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.arrinsert + */ + arrinsert: (...args: CommandArgs) => + this.chain(new JsonArrInsertCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.arrlen + */ + arrlen: (...args: CommandArgs) => + this.chain(new JsonArrLenCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.arrpop + */ + arrpop: (...args: CommandArgs) => + this.chain(new JsonArrPopCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.arrtrim + */ + arrtrim: (...args: CommandArgs) => + this.chain(new JsonArrTrimCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.clear + */ + clear: (...args: CommandArgs) => + this.chain(new JsonClearCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.del + */ + del: (...args: CommandArgs) => + this.chain(new JsonDelCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.forget + */ + forget: (...args: CommandArgs) => + this.chain(new JsonForgetCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.get + */ + get: (...args: CommandArgs) => + this.chain(new JsonGetCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.mget + */ + mget: (...args: CommandArgs) => + this.chain(new JsonMGetCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.numincrby + */ + numincrby: (...args: CommandArgs) => + this.chain(new JsonNumIncrByCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.nummultby + */ + nummultby: (...args: CommandArgs) => + this.chain(new JsonNumMultByCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.objkeys + */ + objkeys: (...args: CommandArgs) => + this.chain(new JsonObjKeysCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.objlen + */ + objlen: (...args: CommandArgs) => + this.chain(new JsonObjLenCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.resp + */ + resp: (...args: CommandArgs) => + this.chain(new JsonRespCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.set + */ + set: (...args: CommandArgs) => + this.chain(new JsonSetCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.strappend + */ + strappend: (...args: CommandArgs) => + this.chain(new JsonStrAppendCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.strlen + */ + strlen: (...args: CommandArgs) => + this.chain(new JsonStrLenCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.toggle + */ + toggle: (...args: CommandArgs) => + this.chain(new JsonToggleCommand(args, this.commandOptions)), + + /** + * @see https://redis.io/commands/json.type + */ + type: (...args: CommandArgs) => + this.chain(new JsonTypeCommand(args, this.commandOptions)), + }; + } } diff --git a/pkg/redis.ts b/pkg/redis.ts index 1381734d..b8571584 100644 --- a/pkg/redis.ts +++ b/pkg/redis.ts @@ -40,6 +40,27 @@ import { IncrByCommand, IncrByFloatCommand, IncrCommand, + JsonArrAppendCommand, + JsonArrIndexCommand, + JsonArrInsertCommand, + JsonArrLenCommand, + JsonArrPopCommand, + JsonArrTrimCommand, + JsonClearCommand, + JsonDelCommand, + JsonForgetCommand, + JsonGetCommand, + JsonMGetCommand, + JsonNumIncrByCommand, + JsonNumMultByCommand, + JsonObjKeysCommand, + JsonObjLenCommand, + JsonRespCommand, + JsonSetCommand, + JsonStrAppendCommand, + JsonStrLenCommand, + JsonToggleCommand, + JsonTypeCommand, KeysCommand, LIndexCommand, LInsertCommand, @@ -130,18 +151,7 @@ import type { CommandArgs } from "./types.ts"; import { Script } from "./script.ts"; import { ZMScoreCommand } from "./commands/zmscore.ts"; import { ZDiffStoreCommand } from "./commands/zdiffstore.ts"; -import { Telemetry } from "./types.ts"; - -export type RedisOptions = { - /** - * Automatically try to deserialize the returned data from upstash using `JSON.deserialize` - * - * @default true - */ - automaticDeserialization?: boolean; - - enableTelemetry?: boolean; -}; +import { RedisOptions, Telemetry } from "./types.ts"; /** * Serverless redis client for upstash. @@ -168,6 +178,135 @@ export class Redis { this.enableTelemetry = opts?.enableTelemetry ?? true; } + get json() { + return { + /** + * @see https://redis.io/commands/json.arrappend + */ + arrappend: (...args: CommandArgs) => + new JsonArrAppendCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.arrindex + */ + arrindex: (...args: CommandArgs) => + new JsonArrIndexCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.arrinsert + */ + arrinsert: (...args: CommandArgs) => + new JsonArrInsertCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.arrlen + */ + arrlen: (...args: CommandArgs) => + new JsonArrLenCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.arrpop + */ + arrpop: (...args: CommandArgs) => + new JsonArrPopCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.arrtrim + */ + arrtrim: (...args: CommandArgs) => + new JsonArrTrimCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.clear + */ + clear: (...args: CommandArgs) => + new JsonClearCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.del + */ + del: (...args: CommandArgs) => + new JsonDelCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.forget + */ + forget: (...args: CommandArgs) => + new JsonForgetCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.get + */ + get: (...args: CommandArgs) => + new JsonGetCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.mget + */ + mget: (...args: CommandArgs) => + new JsonMGetCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.numincrby + */ + numincrby: (...args: CommandArgs) => + new JsonNumIncrByCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.nummultby + */ + nummultby: (...args: CommandArgs) => + new JsonNumMultByCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.objkeys + */ + objkeys: (...args: CommandArgs) => + new JsonObjKeysCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.objlen + */ + objlen: (...args: CommandArgs) => + new JsonObjLenCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.resp + */ + resp: (...args: CommandArgs) => + new JsonRespCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.set + */ + set: (...args: CommandArgs) => + new JsonSetCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.strappend + */ + strappend: (...args: CommandArgs) => + new JsonStrAppendCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.strlen + */ + strlen: (...args: CommandArgs) => + new JsonStrLenCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.toggle + */ + toggle: (...args: CommandArgs) => + new JsonToggleCommand(args, this.opts).exec(this.client), + + /** + * @see https://redis.io/commands/json.type + */ + type: (...args: CommandArgs) => + new JsonTypeCommand(args, this.opts).exec(this.client), + }; + } /** * Wrap a new middleware around the HTTP client. */ diff --git a/pkg/types.ts b/pkg/types.ts index 64998f02..b15af14a 100644 --- a/pkg/types.ts +++ b/pkg/types.ts @@ -20,3 +20,14 @@ export type Telemetry = { */ runtime?: string; }; + +export type RedisOptions = { + /** + * Automatically try to deserialize the returned data from upstash using `JSON.deserialize` + * + * @default true + */ + automaticDeserialization?: boolean; + + enableTelemetry?: boolean; +}; From 8677204ae96d899970c4e45d0261f087fb101a90 Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Wed, 8 Feb 2023 09:42:32 +0100 Subject: [PATCH 2/4] fix: punctuation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aeeeec3a..7c74a538 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ supported. ### Install -#### npm +#### Node.js ```bash npm install @upstash/redis From cfa8633acfc9a9fa69b3446b81d54f2bd7f49c3c Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Wed, 8 Feb 2023 09:49:09 +0100 Subject: [PATCH 3/4] test: increase timeout --- pkg/commands/set.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/set.test.ts b/pkg/commands/set.test.ts index 24c6029b..615a9996 100644 --- a/pkg/commands/set.test.ts +++ b/pkg/commands/set.test.ts @@ -57,12 +57,12 @@ Deno.test("exat", async (t) => { const value = randomID(); const res = await new SetCommand([key, value, { - exat: Math.floor(Date.now() / 1000) + 1, + exat: Math.floor(Date.now() / 1000) + 2, }]).exec(client); assertEquals(res, "OK"); const res2 = await new GetCommand([key]).exec(client); assertEquals(res2, value); - await new Promise((res) => setTimeout(res, 2000)); + await new Promise((res) => setTimeout(res, 3000)); const res3 = await new GetCommand([key]).exec(client); From fcc66bca3f2567409efa60100a6eaee40bede601 Mon Sep 17 00:00:00 2001 From: Andreas Thomas Date: Wed, 8 Feb 2023 11:02:18 +0100 Subject: [PATCH 4/4] fix: pipeline json sub commands --- deno.lock | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ pkg/pipeline.ts | 27 +++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/deno.lock b/deno.lock index 2467454f..0b9cb0e9 100644 --- a/deno.lock +++ b/deno.lock @@ -24,10 +24,17 @@ "https://deno.land/std@0.111.0/streams/conversion.ts": "fe0059ed9d3c53eda4ba44eb71a6a9acb98c5fdb5ba1b6c6ab28004724c7641b", "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", + "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", + "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", + "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf", + "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", "https://deno.land/std@0.140.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", "https://deno.land/std@0.140.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", + "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8", + "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", + "https://deno.land/std@0.140.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", @@ -37,6 +44,7 @@ "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44", "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", + "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", "https://deno.land/std@0.143.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", "https://deno.land/std@0.143.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", "https://deno.land/std@0.143.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", @@ -62,6 +70,22 @@ "https://deno.land/std@0.152.0/testing/_test_suite.ts": "ad453767aeb8c300878a6b7920e20370f4ce92a7b6c8e8a5d1ac2b7c14a09acb", "https://deno.land/std@0.152.0/testing/asserts.ts": "093735c88f52bbead7f60a1f7a97a2ce4df3c2d5fab00a46956f20b4a5793ccd", "https://deno.land/std@0.152.0/testing/bdd.ts": "311d19d872088e254ead39eb7742630f7b17547ca4847dbd670821c02789a0f9", + "https://deno.land/std@0.171.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.171.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.171.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", + "https://deno.land/std@0.171.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32", + "https://deno.land/std@0.171.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.171.0/fs/expand_glob.ts": "536055845aafc32de7e7a46c3b778a741825d5e2ed8580d9851a01ec7a5adf2e", + "https://deno.land/std@0.171.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32", + "https://deno.land/std@0.171.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.171.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.171.0/path/_util.ts": "86c2375a996c1931b2f2ac71fefd5ddf0cf0e579fa4ab12d3e4c552d4223b8d8", + "https://deno.land/std@0.171.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.171.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.171.0/path/mod.ts": "4b83694ac500d7d31b0cdafc927080a53dc0c3027eb2895790fb155082b0d232", + "https://deno.land/std@0.171.0/path/posix.ts": "2ecc259e6f34013889b7638ff90339a82d8178f629155761ce6001e41af55a43", + "https://deno.land/std@0.171.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.171.0/path/win32.ts": "99170a0eb0e2b1ce41499c1e86bb55320cb6606299ad947f57ee0a291cdb93d5", "https://deno.land/std@0.177.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", "https://deno.land/std@0.177.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", "https://deno.land/std@0.177.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", @@ -73,6 +97,8 @@ "https://deno.land/x/code_block_writer@11.0.0/comment_char.ts": "22b66890bbdf7a2d59777ffd8231710c1fda1c11fadada67632a596937a1a314", "https://deno.land/x/code_block_writer@11.0.0/mod.ts": "dc43d56c3487bae02886a09754fb09c607da4ea866817e80f3e60632f3391d70", "https://deno.land/x/code_block_writer@11.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", + "https://deno.land/x/code_block_writer@11.0.3/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5", + "https://deno.land/x/code_block_writer@11.0.3/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff", "https://deno.land/x/deno_cache@0.2.1/auth_tokens.ts": "01b94d25abd974153a3111653998b9a43c66d84a0e4b362fc5f4bbbf40a6e0f7", "https://deno.land/x/deno_cache@0.2.1/cache.ts": "67e301c20161546fea45405316314f4c3d85cc7a367b2fb72042903f308f55b7", "https://deno.land/x/deno_cache@0.2.1/deno_dir.ts": "e4dc68da5641aa337bcc06fb1df28fcb086b366dcbea7d8aaed7ac7c853fedb1", @@ -83,6 +109,22 @@ "https://deno.land/x/deno_cache@0.2.1/http_cache.ts": "af1500149496e2d0acadec24569e2a9c86a3f600cceef045dcf6f5ce8de72b3a", "https://deno.land/x/deno_cache@0.2.1/mod.ts": "709ab9d1068be5fd77b020b33e7a9394f1e9b453553b1e2336b72c90283cf3c0", "https://deno.land/x/deno_cache@0.2.1/util.ts": "652479928551259731686686ff2df6f26bc04e8e4d311137b2bf3bc10f779f48", + "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e", + "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195", + "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f", + "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343", + "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9", + "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9", + "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7", + "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd", + "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195", + "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe", + "https://deno.land/x/deno_graph@0.26.0/lib/deno_graph.generated.js": "2f7ca85b2ceb80ec4b3d1b7f3a504956083258610c7b9a1246238c5b7c68f62d", + "https://deno.land/x/deno_graph@0.26.0/lib/loader.ts": "380e37e71d0649eb50176a9786795988fc3c47063a520a54b616d7727b0f8629", + "https://deno.land/x/deno_graph@0.26.0/lib/media_type.ts": "222626d524fa2f9ebcc0ec7c7a7d5dfc74cc401cc46790f7c5e0eab0b0787707", + "https://deno.land/x/deno_graph@0.26.0/lib/snippets/deno_graph-de651bc9c240ed8d/src/deno_apis.js": "41192baaa550a5c6a146280fae358cede917ae16ec4e4315be51bef6631ca892", + "https://deno.land/x/deno_graph@0.26.0/lib/types.d.ts": "2bbdbf895321d1df8db511fab00160a0211c09c2e7cac56c522dd6e9ed6d2ef7", + "https://deno.land/x/deno_graph@0.26.0/mod.ts": "11131ae166580a1c7fa8506ff553751465a81c263d94443f18f353d0c320bc14", "https://deno.land/x/deno_graph@0.6.0/lib/deno_graph.generated.js": "3e1cccd6376d4ad0ea789d66aa0f6b19f737fa8da37b5e6185ef5c269c974f54", "https://deno.land/x/deno_graph@0.6.0/lib/loader.ts": "13a11c1dea0d85e0ad211be77217b8c06138bbb916afef6f50a04cca415084a9", "https://deno.land/x/deno_graph@0.6.0/lib/media_type.ts": "36be751aa63d6ae36475b90dca5fae8fd7c3a77cf13684c48cf23a85ee607b31", @@ -104,6 +146,21 @@ "https://deno.land/x/dnt@0.30.0/lib/utils.ts": "d2681d634dfa6bd4ad2a32ad15bd419f6f1f895e06c0bf479455fbf1c5f49cd9", "https://deno.land/x/dnt@0.30.0/mod.ts": "d8cbced96a00015d8fe820defd0636de4eda000d2bbe883e9b782c2a7df453b5", "https://deno.land/x/dnt@0.30.0/transform.ts": "a5f81749ecc145af0730e348d5bb3b9b9c7b922822453561feecbdb0d5462b34", + "https://deno.land/x/dnt@0.33.1/lib/compiler.ts": "dd589db479d6d7e69999865003ab83c41544e251ece4f21f2f2ee74557097ba6", + "https://deno.land/x/dnt@0.33.1/lib/compiler_transforms.ts": "cbb1fd5948f5ced1aa5c5aed9e45134e2357ce1e7220924c1d7bded30dcd0dd0", + "https://deno.land/x/dnt@0.33.1/lib/mod.deps.ts": "6648fb17b4a49677cb0c24f60ffb5067a86ad69ff97712d40fe0d62b281b1811", + "https://deno.land/x/dnt@0.33.1/lib/npm_ignore.ts": "ddc1a7a76b288ca471bf1a6298527887a0f9eb7e25008072fd9c9fa9bb28c71a", + "https://deno.land/x/dnt@0.33.1/lib/package_json.ts": "2d629dbaef8004971e38ce3661f04b915a35342b905c3d98ff4a25343c2a8293", + "https://deno.land/x/dnt@0.33.1/lib/pkg/dnt_wasm.generated.js": "00257fc2f03321bb5f2b9bc23cb85e79fe55eb49a325d5ab925b9fc81b4aa963", + "https://deno.land/x/dnt@0.33.1/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be", + "https://deno.land/x/dnt@0.33.1/lib/shims.ts": "7998851b149cb230f5183590d3b66c06f696fefb1c31c24eb5736f1ef12a4de1", + "https://deno.land/x/dnt@0.33.1/lib/test_runner/get_test_runner_code.ts": "2a4e26aa33120f3cc9e03b8538211a5047a4bad4c64e895944b87f2dcd55d904", + "https://deno.land/x/dnt@0.33.1/lib/test_runner/test_runner.ts": "b91d77d9d4b82984cb2ba7431ba6935756ba72f62e7dd4db22cd47a680ebd952", + "https://deno.land/x/dnt@0.33.1/lib/transform.deps.ts": "a6e138e380ebe4479bed1b00ae8dff1d4e6626cc53bc79dd98f907b43745d63a", + "https://deno.land/x/dnt@0.33.1/lib/types.ts": "34e45a3136c2f21f797173ea46d9ea5d1639eb7b834a5bd565aad4214fa32603", + "https://deno.land/x/dnt@0.33.1/lib/utils.ts": "d13b5b3148a2c71e9b2f1c84c7be7393b825ae972505e23c2f6b1e5287e96b43", + "https://deno.land/x/dnt@0.33.1/mod.ts": "691ea4b644cc61123b7beed19e66af301f25985483b81d21cfe49a0be2877fd9", + "https://deno.land/x/dnt@0.33.1/transform.ts": "1b127c5f22699c8ab2545b98aeca38c4e5c21405b0f5342ea17e9c46280ed277", "https://deno.land/x/sha1@v1.0.3/deps.ts": "2e1af51a48c8507017fdb057b950366601b177fb7e73d5de54c1b3e0e115d72e", "https://deno.land/x/sha1@v1.0.3/mod.ts": "146a101c9776cc9c807053c93f23e4b321ade5251f65745df418f4a75d5fd27b", "https://deno.land/x/ts_morph@15.1.0/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", @@ -115,6 +172,15 @@ "https://deno.land/x/ts_morph@15.1.0/common/ts_morph_common.js": "e43476019b9c74ca9116eb82b97a14e6bc2d1bba55b0575d933d98069c0c0c37", "https://deno.land/x/ts_morph@15.1.0/common/typescript.d.ts": "3fa86a2c33804b6a4221dda3714a6a2f8a2f26983ea6c63d2bf6f2839fbb778a", "https://deno.land/x/ts_morph@15.1.0/common/typescript.js": "4d7a5d2090a2ba739c02207bd8c49a60c9475dea7725a9cabfaf0f26cbf85f53", + "https://deno.land/x/ts_morph@17.0.1/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9", + "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.d.ts": "607e651c5ae5aa57c2ac4090759a6379e809c0cdc42114742ac67353b1a75038", + "https://deno.land/x/ts_morph@17.0.1/bootstrap/ts_morph_bootstrap.js": "91a954daa993c5acb3361aa5279394f81ea6fe18b3854345c86111b336491cfc", + "https://deno.land/x/ts_morph@17.0.1/common/DenoRuntime.ts": "537800e840d0994f9055164e11bf33eadf96419246af0d3c453793c3ae67bdb3", + "https://deno.land/x/ts_morph@17.0.1/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed", + "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.d.ts": "ee7767b0c68b23c65bb607c94b6cb3512e8237fbcb7d1d8383a33235cde2c068", + "https://deno.land/x/ts_morph@17.0.1/common/ts_morph_common.js": "49a79124b941ba2b35d81ac9eb90fc33c957b2640cdb97569c1941bac5a3bbdb", + "https://deno.land/x/ts_morph@17.0.1/common/typescript.d.ts": "57e52a0882af4e835473dda27e4316cc31149866970210f9f79b940e916b7838", + "https://deno.land/x/ts_morph@17.0.1/common/typescript.js": "5dd669eb199ee2a539924c63a92e23d95df43dfe2fbe3a9d68c871648be1ad5e", "https://denopkg.com/chiefbiiko/std-encoding@v1.0.0/mod.ts": "4a927e5cd1d9b080d72881eb285b3b94edb6dadc1828aeb194117645f4481ac0" } } \ No newline at end of file diff --git a/pkg/pipeline.ts b/pkg/pipeline.ts index 677c5fd9..2f365ab4 100644 --- a/pkg/pipeline.ts +++ b/pkg/pipeline.ts @@ -152,6 +152,8 @@ import { ZMScoreCommand } from "./commands/zmscore.ts"; import { HRandFieldCommand } from "./commands/hrandfield.ts"; import { ZDiffStoreCommand } from "./commands/zdiffstore.ts"; +type Chain = (command: Command) => Pipeline; + /** * Upstash REST API supports command pipelining to send multiple commands in * batch, instead of sending each command one by one and waiting for a response. @@ -1068,7 +1070,30 @@ export class Pipeline { /** * @see https://redis.io/commands/?group=json */ - get json() { + get json(): { + arrappend: (...args: CommandArgs) => Pipeline; + arrindex: (...args: CommandArgs) => Pipeline; + arrinsert: (...args: CommandArgs) => Pipeline; + arrlen: (...args: CommandArgs) => Pipeline; + arrpop: (...args: CommandArgs) => Pipeline; + arrtrim: (...args: CommandArgs) => Pipeline; + clear: (...args: CommandArgs) => Pipeline; + del: (...args: CommandArgs) => Pipeline; + forget: (...args: CommandArgs) => Pipeline; + get: (...args: CommandArgs) => Pipeline; + mget: (...args: CommandArgs) => Pipeline; + numincrby: (...args: CommandArgs) => Pipeline; + nummultby: (...args: CommandArgs) => Pipeline; + objkeys: (...args: CommandArgs) => Pipeline; + objlen: (...args: CommandArgs) => Pipeline; + resp: (...args: CommandArgs) => Pipeline; + set: (...args: CommandArgs) => Pipeline; + strappend: (...args: CommandArgs) => Pipeline; + strlen: (...args: CommandArgs) => Pipeline; + toggle: (...args: CommandArgs) => Pipeline; + type: (...args: CommandArgs) => Pipeline; + } { + // For some reason we needed to define the types manually, otherwise Deno wouldn't build it return { /** * @see https://redis.io/commands/json.arrappend