Skip to content

Commit 98976f2

Browse files
committed
Added the ability to list, add, remove, and update R2 bucket custom domains.
1 parent 0a38bba commit 98976f2

File tree

7 files changed

+585
-31
lines changed

7 files changed

+585
-31
lines changed

.changeset/tame-dryers-end.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Added the ability to list, add, remove, and update R2 bucket custom domains.

packages/wrangler/src/__tests__/r2.test.ts

+183
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ describe("r2", () => {
9797
wrangler r2 bucket delete <name> Delete an R2 bucket
9898
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
9999
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
100+
wrangler r2 bucket domain Manage custom domains for an R2 bucket
100101
101102
GLOBAL FLAGS
102103
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -131,6 +132,7 @@ describe("r2", () => {
131132
wrangler r2 bucket delete <name> Delete an R2 bucket
132133
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
133134
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
135+
wrangler r2 bucket domain Manage custom domains for an R2 bucket
134136
135137
GLOBAL FLAGS
136138
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -1443,6 +1445,187 @@ describe("r2", () => {
14431445
});
14441446
});
14451447
});
1448+
describe("domain", () => {
1449+
mockAccountId();
1450+
mockApiToken();
1451+
describe("add", () => {
1452+
it("should add custom domain to the bucket as expected", async () => {
1453+
const bucketName = "my-bucket";
1454+
const domainName = "example.com";
1455+
const zoneId = "zone-id-123";
1456+
msw.use(
1457+
http.post(
1458+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom",
1459+
async ({ request, params }) => {
1460+
const { accountId, bucketName: bucketParam } = params;
1461+
expect(accountId).toEqual("some-account-id");
1462+
expect(bucketName).toEqual(bucketParam);
1463+
const requestBody = await request.json();
1464+
expect(requestBody).toEqual({
1465+
domain: domainName,
1466+
zoneId: zoneId,
1467+
enabled: true,
1468+
minTLS: "1.0",
1469+
});
1470+
return HttpResponse.json(createFetchResult({}));
1471+
},
1472+
{ once: true }
1473+
)
1474+
);
1475+
await runWrangler(
1476+
`r2 bucket domain add ${bucketName} --domain ${domainName} --zone-id ${zoneId}`
1477+
);
1478+
expect(std.out).toMatchInlineSnapshot(`
1479+
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
1480+
✨ Custom domain 'example.com' connected successfully."
1481+
`);
1482+
});
1483+
1484+
it("should error if domain and zone-id are not provided", async () => {
1485+
const bucketName = "my-bucket";
1486+
await expect(
1487+
runWrangler(`r2 bucket domain add ${bucketName}`)
1488+
).rejects.toThrowErrorMatchingInlineSnapshot(
1489+
`[Error: Missing required arguments: domain, zone-id]`
1490+
);
1491+
expect(std.err).toMatchInlineSnapshot(`
1492+
"X [ERROR] Missing required arguments: domain, zone-id
1493+
1494+
"
1495+
`);
1496+
});
1497+
});
1498+
describe("list", () => {
1499+
it("should list custom domains for a bucket as expected", async () => {
1500+
const bucketName = "my-bucket";
1501+
const mockDomains = [
1502+
{
1503+
domain: "example.com",
1504+
enabled: true,
1505+
status: {
1506+
ownership: "verified",
1507+
ssl: "active",
1508+
},
1509+
minTLS: "1.2",
1510+
zoneId: "zone-id-123",
1511+
zoneName: "example-zone",
1512+
},
1513+
{
1514+
domain: "test.com",
1515+
enabled: false,
1516+
status: {
1517+
ownership: "pending",
1518+
ssl: "pending",
1519+
},
1520+
minTLS: "1.0",
1521+
zoneId: "zone-id-456",
1522+
zoneName: "test-zone",
1523+
},
1524+
];
1525+
msw.use(
1526+
http.get(
1527+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom",
1528+
async ({ params }) => {
1529+
const { accountId, bucketName: bucketParam } = params;
1530+
expect(accountId).toEqual("some-account-id");
1531+
expect(bucketParam).toEqual(bucketName);
1532+
return HttpResponse.json(
1533+
createFetchResult({
1534+
domains: mockDomains,
1535+
})
1536+
);
1537+
},
1538+
{ once: true }
1539+
)
1540+
);
1541+
await runWrangler(`r2 bucket domain list ${bucketName}`);
1542+
expect(std.out).toMatchInlineSnapshot(`
1543+
"Listing custom domains connected to bucket 'my-bucket'...
1544+
domain: example.com
1545+
enabled: Yes
1546+
ownership_status: verified
1547+
ssl_status: active
1548+
min_tls_version: 1.2
1549+
zone_id: zone-id-123
1550+
zone_name: example-zone
1551+
1552+
domain: test.com
1553+
enabled: No
1554+
ownership_status: pending
1555+
ssl_status: pending
1556+
min_tls_version: 1.0
1557+
zone_id: zone-id-456
1558+
zone_name: test-zone"
1559+
`);
1560+
});
1561+
});
1562+
describe("remove", () => {
1563+
it("should remove a custom domain as expected", async () => {
1564+
const bucketName = "my-bucket";
1565+
const domainName = "example.com";
1566+
msw.use(
1567+
http.delete(
1568+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom/:domainName",
1569+
async ({ params }) => {
1570+
const {
1571+
accountId,
1572+
bucketName: bucketParam,
1573+
domainName: domainParam,
1574+
} = params;
1575+
expect(accountId).toEqual("some-account-id");
1576+
expect(bucketParam).toEqual(bucketName);
1577+
expect(domainParam).toEqual(domainName);
1578+
return HttpResponse.json(createFetchResult({}));
1579+
},
1580+
{ once: true }
1581+
)
1582+
);
1583+
await runWrangler(
1584+
`r2 bucket domain remove ${bucketName} --domain ${domainName}`
1585+
);
1586+
expect(std.out).toMatchInlineSnapshot(`
1587+
"Removing custom domain 'example.com' from bucket 'my-bucket'...
1588+
Custom domain 'example.com' removed successfully."
1589+
`);
1590+
});
1591+
});
1592+
describe("update", () => {
1593+
it("should update a custom domain as expected", async () => {
1594+
const bucketName = "my-bucket";
1595+
const domainName = "example.com";
1596+
msw.use(
1597+
http.put(
1598+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/custom/:domainName",
1599+
async ({ request, params }) => {
1600+
const {
1601+
accountId,
1602+
bucketName: bucketParam,
1603+
domainName: domainParam,
1604+
} = params;
1605+
expect(accountId).toEqual("some-account-id");
1606+
expect(bucketParam).toEqual(bucketName);
1607+
expect(domainParam).toEqual(domainName);
1608+
const requestBody = await request.json();
1609+
expect(requestBody).toEqual({
1610+
domain: domainName,
1611+
enabled: false,
1612+
minTLS: "1.3",
1613+
});
1614+
return HttpResponse.json(createFetchResult({}));
1615+
},
1616+
{ once: true }
1617+
)
1618+
);
1619+
await runWrangler(
1620+
`r2 bucket domain update ${bucketName} --domain ${domainName} --enabled false --min-tls 1.3`
1621+
);
1622+
expect(std.out).toMatchInlineSnapshot(`
1623+
"Updating custom domain 'example.com' for bucket 'my-bucket'...
1624+
✨ Custom domain 'example.com' updated successfully."
1625+
`);
1626+
});
1627+
});
1628+
});
14461629
});
14471630

14481631
describe("r2 object", () => {

packages/wrangler/src/__tests__/r2/helpers.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ describe("event notifications", () => {
1313

1414
test("tableFromNotificationsGetResponse", async () => {
1515
const bucketName = "my-bucket";
16-
const config = { account_id: "my-account" };
1716
const response: GetNotificationConfigResponse = {
1817
bucketName,
1918
queues: [
@@ -48,10 +47,7 @@ describe("event notifications", () => {
4847
},
4948
],
5049
};
51-
const tableOutput = await tableFromNotificationGetResponse(
52-
config,
53-
response
54-
);
50+
const tableOutput = tableFromNotificationGetResponse(response);
5551
logger.log(tableOutput.map((x) => formatLabelledValues(x)).join("\n\n"));
5652

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

0 commit comments

Comments
 (0)