diff --git a/.changeset/plenty-garlics-jump.md b/.changeset/plenty-garlics-jump.md new file mode 100644 index 000000000000..9086f0f0d4e9 --- /dev/null +++ b/.changeset/plenty-garlics-jump.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +feature: allowing users to specify a description when creating an event notification rule diff --git a/packages/wrangler/src/__tests__/r2.test.ts b/packages/wrangler/src/__tests__/r2.test.ts index 08304e59dc64..69a5d7e7dbbd 100644 --- a/packages/wrangler/src/__tests__/r2.test.ts +++ b/packages/wrangler/src/__tests__/r2.test.ts @@ -998,6 +998,258 @@ describe("r2", () => { `); }); + it("follows happy path as expected with prefix", async () => { + const eventTypes: R2EventType[] = ["object-create", "object-delete"]; + const actions: R2EventableOperation[] = []; + const bucketName = "my-bucket"; + const queue = "my-queue"; + + const config: PutNotificationRequestBody = { + rules: [ + { + actions: eventTypes.reduce( + (acc, et) => acc.concat(actionsForEventCategories[et]), + actions + ), + prefix: "ruleprefix", + }, + ], + }; + msw.use( + http.put( + "*/accounts/:accountId/event_notifications/r2/:bucketName/configuration/queues/:queueUUID", + async ({ request, params }) => { + const { accountId } = params; + expect(accountId).toEqual("some-account-id"); + expect(await request.json()).toEqual({ + ...config, + // We fill in `prefix` & `suffix` with empty strings if not + // provided + rules: [{ ...config.rules[0], suffix: "" }], + }); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json(createFetchResult({})); + }, + { once: true } + ), + http.get( + "*/accounts/:accountId/queues?*", + async ({ request, params }) => { + const url = new URL(request.url); + const { accountId } = params; + const nameParams = url.searchParams.getAll("name"); + + expect(accountId).toEqual("some-account-id"); + expect(nameParams[0]).toEqual(queue); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json({ + success: true, + errors: [], + messages: [], + result: [ + { + queue_id: "queue-id", + queue_name: queue, + created_on: "", + producers: [], + consumers: [], + producers_total_count: 1, + consumers_total_count: 0, + modified_on: "", + }, + ], + }); + }, + { once: true } + ) + ); + await expect( + runWrangler( + `r2 bucket notification create ${bucketName} --queue ${queue} --event-types ${eventTypes.join( + " " + )} --prefix "ruleprefix"` + ) + ).resolves.toBe(undefined); + expect(std.out).toMatchInlineSnapshot(` + "Creating event notification rule for object creation and deletion (PutObject,CompleteMultipartUpload,CopyObject,DeleteObject,LifecycleDeletion) + Event notification rule created successfully!" + `); + }); + + it("follows happy path as expected with suffix", async () => { + const eventTypes: R2EventType[] = ["object-create", "object-delete"]; + const actions: R2EventableOperation[] = []; + const bucketName = "my-bucket"; + const queue = "my-queue"; + + const config: PutNotificationRequestBody = { + rules: [ + { + actions: eventTypes.reduce( + (acc, et) => acc.concat(actionsForEventCategories[et]), + actions + ), + suffix: "rulesuffix", + }, + ], + }; + msw.use( + http.put( + "*/accounts/:accountId/event_notifications/r2/:bucketName/configuration/queues/:queueUUID", + async ({ request, params }) => { + const { accountId } = params; + expect(accountId).toEqual("some-account-id"); + expect(await request.json()).toEqual({ + ...config, + // We fill in `prefix` & `suffix` with empty strings if not + // provided + rules: [{ ...config.rules[0], prefix: "" }], + }); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json(createFetchResult({})); + }, + { once: true } + ), + http.get( + "*/accounts/:accountId/queues?*", + async ({ request, params }) => { + const url = new URL(request.url); + const { accountId } = params; + const nameParams = url.searchParams.getAll("name"); + + expect(accountId).toEqual("some-account-id"); + expect(nameParams[0]).toEqual(queue); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json({ + success: true, + errors: [], + messages: [], + result: [ + { + queue_id: "queue-id", + queue_name: queue, + created_on: "", + producers: [], + consumers: [], + producers_total_count: 1, + consumers_total_count: 0, + modified_on: "", + }, + ], + }); + }, + { once: true } + ) + ); + await expect( + runWrangler( + `r2 bucket notification create ${bucketName} --queue ${queue} --event-types ${eventTypes.join( + " " + )} --suffix "rulesuffix"` + ) + ).resolves.toBe(undefined); + expect(std.out).toMatchInlineSnapshot(` + "Creating event notification rule for object creation and deletion (PutObject,CompleteMultipartUpload,CopyObject,DeleteObject,LifecycleDeletion) + Event notification rule created successfully!" + `); + }); + + it("follows happy path as expected with description", async () => { + const eventTypes: R2EventType[] = ["object-create", "object-delete"]; + const actions: R2EventableOperation[] = []; + const bucketName = "my-bucket"; + const queue = "my-queue"; + + const config: PutNotificationRequestBody = { + rules: [ + { + actions: eventTypes.reduce( + (acc, et) => acc.concat(actionsForEventCategories[et]), + actions + ), + description: "rule description", + }, + ], + }; + msw.use( + http.put( + "*/accounts/:accountId/event_notifications/r2/:bucketName/configuration/queues/:queueUUID", + async ({ request, params }) => { + const { accountId } = params; + expect(accountId).toEqual("some-account-id"); + expect(await request.json()).toEqual({ + ...config, + // We fill in `prefix` & `suffix` with empty strings if not + // provided + rules: [ + { + ...config.rules[0], + prefix: "", + suffix: "", + }, + ], + }); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json(createFetchResult({})); + }, + { once: true } + ), + http.get( + "*/accounts/:accountId/queues?*", + async ({ request, params }) => { + const url = new URL(request.url); + const { accountId } = params; + const nameParams = url.searchParams.getAll("name"); + + expect(accountId).toEqual("some-account-id"); + expect(nameParams[0]).toEqual(queue); + expect(request.headers.get("authorization")).toEqual( + "Bearer some-api-token" + ); + return HttpResponse.json({ + success: true, + errors: [], + messages: [], + result: [ + { + queue_id: "queue-id", + queue_name: queue, + created_on: "", + producers: [], + consumers: [], + producers_total_count: 1, + consumers_total_count: 0, + modified_on: "", + }, + ], + }); + }, + { once: true } + ) + ); + await expect( + runWrangler( + `r2 bucket notification create ${bucketName} --queue ${queue} --event-types ${eventTypes.join( + " " + )} --description "rule description"` + ) + ).resolves.toBe(undefined); + expect(std.out).toMatchInlineSnapshot(` + "Creating event notification rule for object creation and deletion (PutObject,CompleteMultipartUpload,CopyObject,DeleteObject,LifecycleDeletion) + Event notification rule created successfully!" + `); + }); + it("errors if required options are not provided", async () => { await expect( runWrangler("r2 bucket notification create notification-test-001") @@ -1024,7 +1276,8 @@ describe("r2", () => { --event-types, --event-type The type of event(s) that will emit event notifications [array] [required] [choices: \\"object-create\\", \\"object-delete\\"] --prefix The prefix that an object must match to emit event notifications (note: regular expressions not supported) [string] --suffix The suffix that an object must match to emit event notifications (note: regular expressions not supported) [string] - --queue The name of the queue that will receive event notification messages [string] [required]" + --queue The name of the queue that will receive event notification messages [string] [required] + --description Description of the rule [string]" `); }); }); diff --git a/packages/wrangler/src/r2/helpers.ts b/packages/wrangler/src/r2/helpers.ts index afebf7f2f13d..8ecfcffa3660 100644 --- a/packages/wrangler/src/r2/helpers.ts +++ b/packages/wrangler/src/r2/helpers.ts @@ -379,6 +379,7 @@ type NotificationRule = { prefix?: string; suffix?: string; actions: R2EventableOperation[]; + description?: string; }; type GetNotificationRule = { ruleId: string; @@ -544,7 +545,8 @@ export async function putEventNotificationConfig( queueName: string, eventTypes: R2EventType[], prefix?: string, - suffix?: string + suffix?: string, + description?: string ): Promise { const queue = await getQueue(config, queueName); const headers = eventNotificationHeaders(apiCredentials); @@ -554,9 +556,14 @@ export async function putEventNotificationConfig( actions = actions.concat(actionsForEventCategories[et]); } - const body: PutNotificationRequestBody = { - rules: [{ prefix, suffix, actions }], - }; + const body: PutNotificationRequestBody = + description === undefined + ? { + rules: [{ prefix, suffix, actions }], + } + : { + rules: [{ prefix, suffix, actions, description }], + }; const ruleFor = eventTypes.map((et) => et === "object-create" ? "creation" : "deletion" ); diff --git a/packages/wrangler/src/r2/notification.ts b/packages/wrangler/src/r2/notification.ts index 263d7a5b645e..572997d23866 100644 --- a/packages/wrangler/src/r2/notification.ts +++ b/packages/wrangler/src/r2/notification.ts @@ -79,6 +79,10 @@ export function CreateOptions(yargs: CommonYargsArgv) { demandOption: true, requiresArg: true, type: "string", + }) + .option("description", { + describe: "Description of the rule", + type: "string", }); } @@ -89,7 +93,14 @@ export async function CreateHandler( const config = readConfig(args.config, args); const accountId = await requireAuth(config); const apiCreds = requireApiToken(); - const { bucket, queue, eventTypes, prefix = "", suffix = "" } = args; + const { + bucket, + queue, + eventTypes, + prefix = "", + suffix = "", + description, + } = args; await putEventNotificationConfig( config, apiCreds, @@ -98,7 +109,8 @@ export async function CreateHandler( queue, eventTypes as R2EventType[], prefix, - suffix + suffix, + description ); logger.log("Event notification rule created successfully!"); }