diff --git a/.changeset/swift-bikes-doubt.md b/.changeset/swift-bikes-doubt.md new file mode 100644 index 000000000000..faed82632329 --- /dev/null +++ b/.changeset/swift-bikes-doubt.md @@ -0,0 +1,11 @@ +--- +"wrangler": minor +--- + +feat: implement queues info command + +This command allows users to get information on individual queues. + +To run this command use the queues info command with the name of a queue in the user's account. + +`wrangler queues info my-queue-name` diff --git a/packages/wrangler/src/__tests__/queues.test.ts b/packages/wrangler/src/__tests__/queues.test.ts index 27bb4642637d..272ccdb2ce5e 100644 --- a/packages/wrangler/src/__tests__/queues.test.ts +++ b/packages/wrangler/src/__tests__/queues.test.ts @@ -30,6 +30,7 @@ describe("wrangler", () => { wrangler queues list List Queues wrangler queues create Create a Queue wrangler queues delete Delete a Queue + wrangler queues info Get Queue information wrangler queues consumer Configure Queue consumers GLOBAL FLAGS @@ -1406,5 +1407,124 @@ describe("wrangler", () => { }); }); }); + + describe("info", () => { + const mockQueue = { + queue_id: "1234567", + queue_name: expectedQueueName, + created_on: "2024-05-20T14:43:56.70498Z", + producers: [ + { + namespace: "testnamespace", + script: "test-producer1", + type: "worker", + }, + { + namespace: "testnamespace", + script: "test-producer2", + type: "worker", + }, + ], + consumers: [ + { + dead_letter_queue: "testdlq", + settings: { batch_size: 10 }, + consumer_id: "111", + type: "worker", + script: "test-consumer", + }, + ], + producers_total_count: 2, + consumers_total_count: 1, + modified_on: "2024-07-19T14:43:56.70498Z", + }; + + it("should return the documentation for the info command when using the --help param", async () => { + await runWrangler("queues info --help"); + expect(std.err).toMatchInlineSnapshot(`""`); + expect(std.out).toMatchInlineSnapshot(` + "wrangler queues info + + Get Queue information + + POSITIONALS + name The name of the queue [string] [required] + + GLOBAL FLAGS + -c, --config Path to Wrangler configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean]" + `); + }); + it("should return queue info with worker producers when the queue has workers configured as producers", async () => { + mockGetQueueByNameRequest(expectedQueueName, mockQueue); + await runWrangler("queues info testQueue"); + expect(std.out).toMatchInlineSnapshot(` + "Queue Name: testQueue + Queue ID: 1234567 + Created On: 2024-05-20T14:43:56.70498Z + Last Modified: 2024-07-19T14:43:56.70498Z + Number of Producers: 2 + Producers: worker:test-producer1, worker:test-producer2 + Number of Consumers: 1 + Consumers: worker:test-consumer" + `); + }); + it('should return "http consumer" and a curl command when the consumer type is http_pull', async () => { + const mockHTTPPullQueue = { + ...mockQueue, + consumers: [{ ...mockQueue.consumers[0], type: "http_pull" }], + }; + mockGetQueueByNameRequest(expectedQueueName, mockHTTPPullQueue); + await runWrangler("queues info testQueue"); + expect(std.out).toMatchInlineSnapshot(` + "Queue Name: testQueue + Queue ID: 1234567 + Created On: 2024-05-20T14:43:56.70498Z + Last Modified: 2024-07-19T14:43:56.70498Z + Number of Producers: 2 + Producers: worker:test-producer1, worker:test-producer2 + Number of Consumers: 1 + Consumers: HTTP Pull Consumer. + Pull messages using: + curl \\"https://api.cloudflare.com/client/v4/accounts/some-account-id/queues/1234567/messages/pull\\" / + --header \\"Authorization: Bearer \\" / + --header \\"Content-Type: application/json\\" / + --data '{ \\"visibility_timeout\\": 10000, \\"batch_size\\": 2 }'" + `); + }); + it("should return the list of r2 bucket producers when the queue is used in an r2 event notification", async () => { + const mockEventNotificationQueue = { + ...mockQueue, + producers: [ + { type: "r2_bucket", bucket_name: "test-bucket1" }, + { type: "r2_bucket", bucket_name: "test-bucket2" }, + ], + consumers: [ + { + ...mockQueue.consumers[0], + type: "r2_bucket", + bucket_name: "bucket-consumer", + }, + ], + }; + mockGetQueueByNameRequest( + expectedQueueName, + mockEventNotificationQueue + ); + await runWrangler("queues info testQueue"); + expect(std.out).toMatchInlineSnapshot(` + "Queue Name: testQueue + Queue ID: 1234567 + Created On: 2024-05-20T14:43:56.70498Z + Last Modified: 2024-07-19T14:43:56.70498Z + Number of Producers: 2 + Producers: r2_bucket:test-bucket1, r2_bucket:test-bucket2 + Number of Consumers: 1 + Consumers: r2_bucket:bucket-consumer" + `); + }); + }); }); }); diff --git a/packages/wrangler/src/queues/cli/commands/index.ts b/packages/wrangler/src/queues/cli/commands/index.ts index 2da413fe30ca..0383fff80f56 100644 --- a/packages/wrangler/src/queues/cli/commands/index.ts +++ b/packages/wrangler/src/queues/cli/commands/index.ts @@ -2,6 +2,7 @@ import { HandleUnauthorizedError } from "../../utils"; import { consumers } from "./consumer/index"; import { handler as createHandler, options as createOptions } from "./create"; import { handler as deleteHandler, options as deleteOptions } from "./delete"; +import { handler as infoHandler, options as infoOptions } from "./info"; import { handler as listHandler, options as listOptions } from "./list"; import type { CommonYargsArgv } from "../../../yargs-types"; @@ -22,6 +23,13 @@ export function queues(yargs: CommonYargsArgv) { deleteHandler ); + yargs.command( + "info ", + "Get Queue information", + infoOptions, + infoHandler + ); + yargs.command( "consumer", "Configure Queue consumers", diff --git a/packages/wrangler/src/queues/cli/commands/info.ts b/packages/wrangler/src/queues/cli/commands/info.ts new file mode 100644 index 000000000000..8baaa4c2d70a --- /dev/null +++ b/packages/wrangler/src/queues/cli/commands/info.ts @@ -0,0 +1,57 @@ +import { readConfig } from "../../../config"; +import { logger } from "../../../logger"; +import { printWranglerBanner } from "../../../update-check"; +import { requireAuth } from "../../../user"; +import { getQueue } from "../../client"; +import type { + CommonYargsArgv, + StrictYargsOptionsToInterface, +} from "../../../yargs-types"; +import type { Consumer, Producer, QueueResponse } from "../../client"; + +export function options(yargs: CommonYargsArgv) { + return yargs.positional("name", { + type: "string", + demandOption: true, + description: "The name of the queue", + }); +} + +export async function handler( + args: StrictYargsOptionsToInterface +) { + const config = readConfig(args.config, args); + const queue: QueueResponse = await getQueue(config, args.name); + const accountId = await requireAuth(config); + + await printWranglerBanner(); + logger.log(`Queue Name: ${queue.queue_name}`); + logger.log(`Queue ID: ${queue.queue_id}`); + logger.log(`Created On: ${queue.created_on}`); + logger.log(`Last Modified: ${queue.modified_on}`); + logger.log(`Number of Producers: ${queue.producers_total_count}`); + queue.producers_total_count > 0 && + logger.log( + `Producers:${queue.producers.map((p: Producer) => (p.type === "r2_bucket" ? ` ${p.type}:${p.bucket_name}` : ` ${p.type}:${p.script}`)).toString()}` + ); + logger.log(`Number of Consumers: ${queue.consumers_total_count}`); + queue.consumers_total_count > 0 && + logger.log( + `Consumers: ${queue.consumers + .map((c: Consumer) => { + if (c.type === "r2_bucket") { + return `${c.type}:${c.bucket_name}`; + } + if (c.type === "http_pull") { + return `HTTP Pull Consumer. +Pull messages using: +curl "https://api.cloudflare.com/client/v4/accounts/${accountId || ""}/queues/${queue.queue_id || ""}/messages/pull" \\ + --header "Authorization: Bearer " \\ + --header "Content-Type: application/json" \\ + --data '{ "visibility_timeout": 10000, "batch_size": 2 }'`; + } + return `${c.type}:${c.script}`; + }) + .toString()}` + ); +} diff --git a/packages/wrangler/src/queues/client.ts b/packages/wrangler/src/queues/client.ts index 73d5690b16ef..b1d13d96235f 100644 --- a/packages/wrangler/src/queues/client.ts +++ b/packages/wrangler/src/queues/client.ts @@ -34,7 +34,7 @@ export interface QueueResponse { queue_name: string; created_on: string; modified_on: string; - producers: ScriptReference[]; + producers: Producer[]; producers_total_count: number; consumers: Consumer[]; consumers_total_count: number; @@ -48,6 +48,11 @@ export interface ScriptReference { environment?: string; } +export type Producer = ScriptReference & { + type: string; + bucket_name?: string; +}; + export type Consumer = ScriptReference & { dead_letter_queue?: string; settings: ConsumerSettings;