Skip to content

Commit

Permalink
Added the ability to list, add, remove, and update R2 bucket custom d…
Browse files Browse the repository at this point in the history
…omains (#7105)

* Added the ability to list, add, remove, and update R2 bucket custom domains.

* Add confirmation for removing custom domain from R2 bucket

* Remove enabled option from r2 bucket domain add and update. Added confirmation prompt to add.

* Update r2 domain tests to check for confirmation prompts
jonesphillip authored Nov 4, 2024

Verified

This commit was signed with the committer’s verified signature.
Byron Sebastian Thiel
1 parent 6948b70 commit a5f1779
Showing 7 changed files with 619 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/tame-dryers-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

Added the ability to list, add, remove, and update R2 bucket custom domains.
199 changes: 199 additions & 0 deletions packages/wrangler/src/__tests__/r2.test.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { actionsForEventCategories } from "../r2/helpers";
import { endEventLoop } from "./helpers/end-event-loop";
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
import { mockConsoleMethods } from "./helpers/mock-console";
import { mockConfirm } from "./helpers/mock-dialogs";
import { useMockIsTTY } from "./helpers/mock-istty";
import { createFetchResult, msw, mswR2handlers } from "./helpers/msw";
import { runInTempDir } from "./helpers/run-in-tmp";
@@ -97,6 +98,7 @@ describe("r2", () => {
wrangler r2 bucket delete <name> Delete an R2 bucket
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
wrangler r2 bucket domain Manage custom domains for an R2 bucket
GLOBAL FLAGS
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -131,6 +133,7 @@ describe("r2", () => {
wrangler r2 bucket delete <name> Delete an R2 bucket
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
wrangler r2 bucket domain Manage custom domains for an R2 bucket
GLOBAL FLAGS
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -1467,6 +1470,202 @@ describe("r2", () => {
});
});
});
describe("domain", () => {
const { setIsTTY } = useMockIsTTY();
mockAccountId();
mockApiToken();
describe("add", () => {
it("should add custom domain to the bucket as expected", async () => {
const bucketName = "my-bucket";
const domainName = "example.com";
const zoneId = "zone-id-123";

setIsTTY(true);
mockConfirm({
text:
`Are you sure you want to add the custom domain '${domainName}' to bucket '${bucketName}'? ` +
`The contents of your bucket will be made publicly available at 'https://${domainName}'`,
result: true,
});
msw.use(
http.post(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom",
async ({ request, params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketName).toEqual(bucketParam);
const requestBody = await request.json();
expect(requestBody).toEqual({
domain: domainName,
zoneId: zoneId,
enabled: true,
minTLS: "1.0",
});
return HttpResponse.json(createFetchResult({}));
},
{ once: true }
)
);
await runWrangler(
`r2 bucket domain add ${bucketName} --domain ${domainName} --zone-id ${zoneId}`
);
expect(std.out).toMatchInlineSnapshot(`
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
✨ Custom domain 'example.com' connected successfully."
`);
});

it("should error if domain and zone-id are not provided", async () => {
const bucketName = "my-bucket";
await expect(
runWrangler(`r2 bucket domain add ${bucketName}`)
).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Missing required arguments: domain, zone-id]`
);
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] Missing required arguments: domain, zone-id
"
`);
});
});
describe("list", () => {
it("should list custom domains for a bucket as expected", async () => {
const bucketName = "my-bucket";
const mockDomains = [
{
domain: "example.com",
enabled: true,
status: {
ownership: "verified",
ssl: "active",
},
minTLS: "1.2",
zoneId: "zone-id-123",
zoneName: "example-zone",
},
{
domain: "test.com",
enabled: false,
status: {
ownership: "pending",
ssl: "pending",
},
minTLS: "1.0",
zoneId: "zone-id-456",
zoneName: "test-zone",
},
];
msw.use(
http.get(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom",
async ({ params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
return HttpResponse.json(
createFetchResult({
domains: mockDomains,
})
);
},
{ once: true }
)
);
await runWrangler(`r2 bucket domain list ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Listing custom domains connected to bucket 'my-bucket'...
domain: example.com
enabled: Yes
ownership_status: verified
ssl_status: active
min_tls_version: 1.2
zone_id: zone-id-123
zone_name: example-zone
domain: test.com
enabled: No
ownership_status: pending
ssl_status: pending
min_tls_version: 1.0
zone_id: zone-id-456
zone_name: test-zone"
`);
});
});
describe("remove", () => {
it("should remove a custom domain as expected", async () => {
const bucketName = "my-bucket";
const domainName = "example.com";
setIsTTY(true);
mockConfirm({
text:
`Are you sure you want to remove the custom domain '${domainName}' from bucket '${bucketName}'? ` +
`Your bucket will no longer be available from 'https://${domainName}'`,
result: true,
});
msw.use(
http.delete(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom/:domainName",
async ({ params }) => {
const {
accountId,
bucketName: bucketParam,
domainName: domainParam,
} = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
expect(domainParam).toEqual(domainName);
return HttpResponse.json(createFetchResult({}));
},
{ once: true }
)
);
await runWrangler(
`r2 bucket domain remove ${bucketName} --domain ${domainName}`
);
expect(std.out).toMatchInlineSnapshot(`
"Removing custom domain 'example.com' from bucket 'my-bucket'...
Custom domain 'example.com' removed successfully."
`);
});
});
describe("update", () => {
it("should update a custom domain as expected", async () => {
const bucketName = "my-bucket";
const domainName = "example.com";
msw.use(
http.put(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom/:domainName",
async ({ request, params }) => {
const {
accountId,
bucketName: bucketParam,
domainName: domainParam,
} = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
expect(domainParam).toEqual(domainName);
const requestBody = await request.json();
expect(requestBody).toEqual({
domain: domainName,
minTLS: "1.3",
});
return HttpResponse.json(createFetchResult({}));
},
{ once: true }
)
);
await runWrangler(
`r2 bucket domain update ${bucketName} --domain ${domainName} --min-tls 1.3`
);
expect(std.out).toMatchInlineSnapshot(`
"Updating custom domain 'example.com' for bucket 'my-bucket'...
✨ Custom domain 'example.com' updated successfully."
`);
});
});
});
});

describe("r2 object", () => {
6 changes: 1 addition & 5 deletions packages/wrangler/src/__tests__/r2/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@ describe("event notifications", () => {

test("tableFromNotificationsGetResponse", async () => {
const bucketName = "my-bucket";
const config = { account_id: "my-account" };
const response: GetNotificationConfigResponse = {
bucketName,
queues: [
@@ -48,10 +47,7 @@ describe("event notifications", () => {
},
],
};
const tableOutput = await tableFromNotificationGetResponse(
config,
response
);
const tableOutput = tableFromNotificationGetResponse(response);
logger.log(tableOutput.map((x) => formatLabelledValues(x)).join("\n\n"));

await expect(std.out).toMatchInlineSnapshot(`
Loading

0 comments on commit a5f1779

Please sign in to comment.