Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/plenty-garlics-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

feature: allowing users to specify a description when creating an event notification rule
255 changes: 254 additions & 1 deletion packages/wrangler/src/__tests__/r2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,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")
Expand Down Expand Up @@ -1028,7 +1280,8 @@ describe("r2", () => {
--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]
-J, --jurisdiction The jurisdiction where the bucket exists [string]"
-J, --jurisdiction The jurisdiction where the bucket exists [string]
--description A description that can be used to identify the event notification rule after creation [string]"
`);
});
});
Expand Down
15 changes: 11 additions & 4 deletions packages/wrangler/src/r2/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ type NotificationRule = {
prefix?: string;
suffix?: string;
actions: R2EventableOperation[];
description?: string;
};
type GetNotificationRule = {
ruleId: string;
Expand Down Expand Up @@ -550,7 +551,8 @@ export async function putEventNotificationConfig(
queueName: string,
eventTypes: R2EventType[],
prefix?: string,
suffix?: string
suffix?: string,
description?: string
): Promise<void> {
const queue = await getQueue(config, queueName);
const headers = eventNotificationHeaders(apiCredentials, jurisdiction);
Expand All @@ -560,9 +562,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"
);
Expand Down
9 changes: 8 additions & 1 deletion packages/wrangler/src/r2/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export function CreateOptions(yargs: CommonYargsArgv) {
alias: "J",
requiresArg: true,
type: "string",
})
.option("description", {
describe:
"A description that can be used to identify the event notification rule after creation",
type: "string",
});
}

Expand All @@ -111,6 +116,7 @@ export async function CreateHandler(
prefix = "",
suffix = "",
jurisdiction = "",
description,
} = args;
await putEventNotificationConfig(
config,
Expand All @@ -121,7 +127,8 @@ export async function CreateHandler(
queue,
eventTypes as R2EventType[],
prefix,
suffix
suffix,
description
);
logger.log("Event notification rule created successfully!");
}
Expand Down
Loading