Skip to content

Commit

Permalink
refactor: migrate r2 commands to new command registration setup (#7311)
Browse files Browse the repository at this point in the history
* register r2 namespace

* r2 object get

* r2 object put

* r2 object delete

* register r2 bucket namespace

* r2 bucket create

* r2 bucket update storage-class

* r2 bucket list

* r2 bucket delete

* register r2 bucket sippy namespace

* r2 bucket sippy enable

* r2 bucket sippy disable

* r2 bucket sippy get

* register r2 bucket notification namespace

* r2 bucket notification list

* r2 bucket notification create

* r2 bucket notification delete

* register r2 bucket domain namespace

* r2 bucket domain list

* r2 bucket domain add

* r2 bucket domain remove

* r2 bucket domain update

* register r2 bucket dev-url namespace

* r2 bucket dev-url get

* r2 bucket dev-url enable

* r2 bucket dev-url disable

* extract r2 object commands

* extract r2 bucket commands

* update tests to reflect expecation when only namespace is provided

* fix mistmatch between command and postinoal arg options

* fix r2 bucket create command

* remove duplicated banner

* fix import order

* fix r2 bucket delete positional argument

* ensure r2 bucket list handler reflecting latest changes from #7212

* r2 bucket info

* r2 bucket lifecycle list

* r2 bucket lifecycle add

* r2 bucket lifecycle remove

* r2 bucket lifecycle set
  • Loading branch information
edmundhung authored Nov 22, 2024
1 parent 3e52c7c commit c650cc9
Show file tree
Hide file tree
Showing 14 changed files with 1,590 additions and 1,649 deletions.
24 changes: 6 additions & 18 deletions packages/wrangler/src/__tests__/r2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,10 @@ describe("r2", () => {
mockApiToken();

it("should show help when the bucket command is passed", async () => {
await expect(() => runWrangler("r2 bucket")).rejects.toThrow(
"Not enough non-option arguments: got 0, need at least 1"
);
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] Not enough non-option arguments: got 0, need at least 1
"`);
await runWrangler("r2 bucket");
await endEventLoop();
expect(std.out).toMatchInlineSnapshot(`
"
wrangler r2 bucket
"wrangler r2 bucket
Manage R2 buckets
Expand Down Expand Up @@ -2110,16 +2104,10 @@ binding = \\"testBucket\\""

describe("r2 object", () => {
it("should show help when the object command is passed", async () => {
await expect(() => runWrangler("r2 object")).rejects.toThrow(
"Not enough non-option arguments: got 0, need at least 1"
);
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] Not enough non-option arguments: got 0, need at least 1
"`);
await runWrangler("r2 object");
await endEventLoop();
expect(std.out).toMatchInlineSnapshot(`
"
wrangler r2 object
"wrangler r2 object
Manage R2 objects
Expand Down
6 changes: 2 additions & 4 deletions packages/wrangler/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { initHandler, initOptions } from "./init";
import "./docs";
import "./dev";
import "./kv";
import "./r2";
import "./workflows";
import "./user/commands";
import { demandSingleValue } from "./core";
Expand All @@ -58,7 +59,6 @@ import { APIError, formatMessage, ParseError } from "./parse";
import { pipelines } from "./pipelines";
import { pubSubCommands } from "./pubsub/pubsub-commands";
import { queues } from "./queues/cli/commands";
import { r2 } from "./r2";
import { secret, secretBulkHandler, secretBulkOptions } from "./secret";
import {
addBreadcrumb,
Expand Down Expand Up @@ -498,9 +498,7 @@ export function createCLIParser(argv: string[]) {
});

// r2
wrangler.command("r2", "📦 Manage R2 buckets & objects", (r2Yargs) => {
return r2(r2Yargs, subHelp);
});
register.registerNamespace("r2");

// d1
wrangler.command("d1", `🗄 Manage Workers D1 databases`, (d1Yargs) => {
Expand Down
272 changes: 272 additions & 0 deletions packages/wrangler/src/r2/bucket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import { defineCommand, defineNamespace } from "../core";
import { UserError } from "../errors";
import { logger } from "../logger";
import * as metrics from "../metrics";
import { requireAuth } from "../user";
import { getValidBindingName } from "../utils/getValidBindingName";
import formatLabelledValues from "../utils/render-labelled-values";
import { LOCATION_CHOICES } from "./constants";
import {
createR2Bucket,
deleteR2Bucket,
getR2Bucket,
getR2BucketMetrics,
isValidR2BucketName,
listR2Buckets,
tablefromR2BucketsListResponse,
updateR2BucketStorageClass,
} from "./helpers";

defineNamespace({
command: "wrangler r2 bucket",
metadata: {
description: "Manage R2 buckets",
status: "stable",
owner: "Product: R2",
},
});

defineCommand({
command: "wrangler r2 bucket create",
metadata: {
description: "Create a new R2 bucket",
status: "stable",
owner: "Product: R2",
},
positionalArgs: ["name"],
args: {
name: {
describe: "The name of the new bucket",
type: "string",
demandOption: true,
},
location: {
describe:
"The optional location hint that determines geographic placement of the R2 bucket",
choices: LOCATION_CHOICES,
requiresArg: true,
type: "string",
},
"storage-class": {
describe: "The default storage class for objects uploaded to this bucket",
alias: "s",
requiresArg: false,
type: "string",
},
jurisdiction: {
describe: "The jurisdiction where the new bucket will be created",
alias: "J",
requiresArg: true,
type: "string",
},
},
async handler(args, { config }) {
const accountId = await requireAuth(config);
const { name, location, storageClass, jurisdiction } = args;

if (!isValidR2BucketName(name)) {
throw new UserError(
`The bucket name "${name}" is invalid. Bucket names can only have alphanumeric and - characters.`
);
}

if (jurisdiction && location) {
throw new UserError(
"Provide either a jurisdiction or location hint - not both."
);
}

let fullBucketName = `${name}`;
if (jurisdiction !== undefined) {
fullBucketName += ` (${jurisdiction})`;
}

logger.log(`Creating bucket '${fullBucketName}'...`);
await createR2Bucket(accountId, name, location, jurisdiction, storageClass);
logger.log(
`✅ Created bucket '${fullBucketName}' with${
location ? ` location hint ${location} and` : ``
} default storage class of ${storageClass ? storageClass : `Standard`}.\n\n` +
"Configure your Worker to write objects to this bucket:\n\n" +
"[[r2_buckets]]\n" +
`bucket_name = "${args.name}"\n` +
`binding = "${getValidBindingName(args.name, "r2")}"`
);

await metrics.sendMetricsEvent("create r2 bucket", {
sendMetrics: config.send_metrics,
});
},
});

defineNamespace({
command: "wrangler r2 bucket update",
metadata: {
description: "Update bucket state",
status: "stable",
owner: "Product: R2",
},
});

defineCommand({
command: "wrangler r2 bucket update storage-class",
metadata: {
description: "Update the default storage class of an existing R2 bucket",
status: "stable",
owner: "Product: R2",
},
positionalArgs: ["name"],
args: {
name: {
describe: "The name of the existing bucket",
type: "string",
demandOption: true,
},
jurisdiction: {
describe: "The jurisdiction of the bucket to be updated",
alias: "J",
requiresArg: true,
type: "string",
},
"storage-class": {
describe: "The new default storage class for this bucket",
alias: "s",
demandOption: true,
requiresArg: true,
type: "string",
},
},
async handler(args, { config }) {
const accountId = await requireAuth(config);

let fullBucketName = `${args.name}`;
if (args.jurisdiction !== undefined) {
fullBucketName += ` (${args.jurisdiction})`;
}
logger.log(
`Updating bucket ${fullBucketName} to ${args.storageClass} default storage class.`
);
await updateR2BucketStorageClass(
accountId,
args.name,
args.storageClass,
args.jurisdiction
);
logger.log(
`Updated bucket ${fullBucketName} to ${args.storageClass} default storage class.`
);
},
});

defineCommand({
command: "wrangler r2 bucket list",
metadata: {
description: "List R2 buckets",
status: "stable",
owner: "Product: R2",
},
args: {
jurisdiction: {
describe: "The jurisdiction to list",
alias: "J",
requiresArg: true,
type: "string",
},
},
async handler(args, { config }) {
const accountId = await requireAuth(config);

logger.log(`Listing buckets...`);

const buckets = await listR2Buckets(accountId, args.jurisdiction);
const tableOutput = tablefromR2BucketsListResponse(buckets);
logger.log(tableOutput.map((x) => formatLabelledValues(x)).join("\n\n"));
},
});

defineCommand({
command: "wrangler r2 bucket info",
metadata: {
description: "Get information about an R2 bucket",
status: "stable",
owner: "Product: R2",
},
positionalArgs: ["bucket"],
args: {
bucket: {
describe: "The name of the bucket to delete",
type: "string",
demandOption: true,
},
jurisdiction: {
describe: "The jurisdiction where the bucket exists",
alias: "J",
requiresArg: true,
type: "string",
},
},
async handler(args, { config }) {
const accountId = await requireAuth(config);

logger.log(`Getting info for '${args.bucket}'...`);

const bucketInfo = await getR2Bucket(
accountId,
args.bucket,
args.jurisdiction
);
const bucketMetrics = await getR2BucketMetrics(
accountId,
args.bucket,
args.jurisdiction
);

const output = {
name: bucketInfo.name,
created: bucketInfo.creation_date,
location: bucketInfo.location || "(unknown)",
default_storage_class: bucketInfo.storage_class || "(unknown)",
object_count: bucketMetrics.objectCount.toLocaleString(),
bucket_size: bucketMetrics.totalSize,
};

logger.log(formatLabelledValues(output));
},
});

defineCommand({
command: "wrangler r2 bucket delete",
metadata: {
description: "Delete an R2 bucket",
status: "stable",
owner: "Product: R2",
},
positionalArgs: ["bucket"],
args: {
bucket: {
describe: "The name of the bucket to delete",
type: "string",
demandOption: true,
},
jurisdiction: {
describe: "The jurisdiction where the bucket exists",
alias: "J",
requiresArg: true,
type: "string",
},
},
async handler(args, { config }) {
const accountId = await requireAuth(config);

let fullBucketName = `${args.bucket}`;
if (args.jurisdiction !== undefined) {
fullBucketName += ` (${args.jurisdiction})`;
}
logger.log(`Deleting bucket ${fullBucketName}.`);
await deleteR2Bucket(accountId, args.bucket, args.jurisdiction);
logger.log(`Deleted bucket ${fullBucketName}.`);
await metrics.sendMetricsEvent("delete r2 bucket", {
sendMetrics: config.send_metrics,
});
},
});
Loading

0 comments on commit c650cc9

Please sign in to comment.