From 204c0adda183f81d7316ab230007c9a04204c776 Mon Sep 17 00:00:00 2001 From: Samuel Macleod Date: Fri, 6 Dec 2024 13:25:15 +0000 Subject: [PATCH 1/2] Accept { name: string } in kv bulk delete --- .../wrangler/src/__tests__/kv.local.test.ts | 70 ++++++++++++++++++- packages/wrangler/src/__tests__/kv.test.ts | 24 ++++++- packages/wrangler/src/kv/index.ts | 23 +++--- 3 files changed, 106 insertions(+), 11 deletions(-) diff --git a/packages/wrangler/src/__tests__/kv.local.test.ts b/packages/wrangler/src/__tests__/kv.local.test.ts index a4b4631bcbf8..b076bc70b5a6 100644 --- a/packages/wrangler/src/__tests__/kv.local.test.ts +++ b/packages/wrangler/src/__tests__/kv.local.test.ts @@ -207,7 +207,7 @@ describe("wrangler", () => { `); }); - it("should delete local bulk kv storage", async () => { + it("should delete local bulk kv storage (string)", async () => { const keyValues = [ { key: "hello", @@ -268,6 +268,74 @@ describe("wrangler", () => { `); }); + it("should delete local bulk kv storage ({ name })", async () => { + const keyValues = [ + { + key: "hello", + value: "world", + }, + { + key: "test", + value: "value", + }, + ]; + writeFileSync("./keys.json", JSON.stringify(keyValues)); + await runWrangler( + `kv bulk put keys.json --namespace-id bulk-namespace-id --local` + ); + await runWrangler(`kv key list --namespace-id bulk-namespace-id --local`); + expect(std.out).toMatchInlineSnapshot(` + "Success! + [ + { + \\"name\\": \\"hello\\" + }, + { + \\"name\\": \\"test\\" + } + ]" + `); + const keys = [ + { + name: "hello", + }, + { + name: "test", + }, + ]; + writeFileSync("./keys.json", JSON.stringify(keys)); + await runWrangler( + `kv bulk delete keys.json --namespace-id bulk-namespace-id --local --force` + ); + expect(std.out).toMatchInlineSnapshot(` + "Success! + [ + { + \\"name\\": \\"hello\\" + }, + { + \\"name\\": \\"test\\" + } + ] + Success!" + `); + + await runWrangler(`kv key list --namespace-id bulk-namespace-id --local`); + expect(std.out).toMatchInlineSnapshot(` + "Success! + [ + { + \\"name\\": \\"hello\\" + }, + { + \\"name\\": \\"test\\" + } + ] + Success! + []" + `); + }); + it("should follow persist-to for local kv storage", async () => { await runWrangler( `kv:key put val value --namespace-id some-namespace-id --local` diff --git a/packages/wrangler/src/__tests__/kv.test.ts b/packages/wrangler/src/__tests__/kv.test.ts index bf63dd04aa83..3ffe1f59f581 100644 --- a/packages/wrangler/src/__tests__/kv.test.ts +++ b/packages/wrangler/src/__tests__/kv.test.ts @@ -1629,7 +1629,7 @@ describe("wrangler", () => { return requests; } - it("should delete the keys parsed from a file", async () => { + it("should delete the keys parsed from a file (string)", async () => { const keys = ["someKey1", "ns:someKey2"]; writeFileSync("./keys.json", JSON.stringify(keys)); mockConfirm({ @@ -1646,6 +1646,26 @@ describe("wrangler", () => { expect(std.err).toMatchInlineSnapshot(`""`); }); + it("should delete the keys parsed from a file ({ name })", async () => { + const keys = [{ name: "someKey1" }, { name: "ns:someKey2" }]; + writeFileSync("./keys.json", JSON.stringify(keys)); + mockConfirm({ + text: `Are you sure you want to delete all the keys read from "keys.json" from kv-namespace with id "some-namespace-id"?`, + result: true, + }); + const requests = mockDeleteRequest( + "some-namespace-id", + keys.map((k) => k.name) + ); + await runWrangler( + `kv bulk delete --namespace-id some-namespace-id keys.json` + ); + expect(requests.count).toEqual(1); + expect(std.out).toMatchInlineSnapshot(`"Success!"`); + expect(std.warn).toMatchInlineSnapshot(`""`); + expect(std.err).toMatchInlineSnapshot(`""`); + }); + it("should delete the keys in batches of 5000 parsed from a file", async () => { const keys = new Array(12000).fill("some-key"); writeFileSync("./keys.json", JSON.stringify(keys)); @@ -1754,7 +1774,7 @@ describe("wrangler", () => { ) ).rejects.toThrowErrorMatchingInlineSnapshot(` [Error: Unexpected JSON input from "keys.json". - Expected an array of strings. + Expected an array of strings or objects with a "name" key. The item at index 1 is type: "number" - 12354 The item at index 2 is type: "object" - {"key":"someKey"} The item at index 3 is type: "object" - null] diff --git a/packages/wrangler/src/kv/index.ts b/packages/wrangler/src/kv/index.ts index 1b4a946e428d..12c0b779c3f6 100644 --- a/packages/wrangler/src/kv/index.ts +++ b/packages/wrangler/src/kv/index.ts @@ -815,7 +815,10 @@ export const kvBulkDeleteCommand = createCommand({ } } - const content = parseJSON(readFileSync(filename), filename) as string[]; + const content = parseJSON(readFileSync(filename), filename) as ( + | string + | { name: string } + )[]; if (!Array.isArray(content)) { throw new UserError( @@ -825,21 +828,25 @@ export const kvBulkDeleteCommand = createCommand({ } const errors: string[] = []; - for (let i = 0; i < content.length; i++) { - const key = content[i]; + + const keysToDelete: string[] = []; + for (const [index, item] of content.entries()) { + const key = typeof item !== "string" ? item?.name : item; + if (typeof key !== "string") { errors.push( - `The item at index ${i} is type: "${typeof key}" - ${JSON.stringify( - key + `The item at index ${index} is type: "${typeof item}" - ${JSON.stringify( + item )}` ); } + keysToDelete.push(key); } if (errors.length > 0) { throw new UserError( `Unexpected JSON input from "${filename}".\n` + - `Expected an array of strings.\n` + + `Expected an array of strings or objects with a "name" key.\n` + errors.join("\n") ); } @@ -851,7 +858,7 @@ export const kvBulkDeleteCommand = createCommand({ config.configPath, namespaceId, async (namespace) => { - for (const key of content) { + for (const key of keysToDelete) { await namespace.delete(key); } } @@ -861,7 +868,7 @@ export const kvBulkDeleteCommand = createCommand({ } else { const accountId = await requireAuth(config); - await deleteKVBulkKeyValue(accountId, namespaceId, content); + await deleteKVBulkKeyValue(accountId, namespaceId, keysToDelete); metricEvent = "delete kv key-values (bulk)"; } From 8c0670fb4ea519d137b504557d765bc44be2bb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Somhairle=20MacLe=C3=B2id?= Date: Fri, 6 Dec 2024 13:32:04 +0000 Subject: [PATCH 2/2] Create wise-pens-walk.md --- .changeset/wise-pens-walk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wise-pens-walk.md diff --git a/.changeset/wise-pens-walk.md b/.changeset/wise-pens-walk.md new file mode 100644 index 000000000000..fd03f59646e4 --- /dev/null +++ b/.changeset/wise-pens-walk.md @@ -0,0 +1,5 @@ +--- +"wrangler": minor +--- + +Accept a JSON file of the format `{ name: string }[]` in `wrangler kv bulk delete`, as well as the current `string[]` format.