Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate r2 commands to new command registration setup #7311

Merged
merged 40 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2e9b886
register r2 namespace
edmundhung Nov 7, 2024
90055e0
r2 object get
edmundhung Nov 7, 2024
36b9f1c
r2 object put
edmundhung Nov 7, 2024
f143920
r2 object delete
edmundhung Nov 8, 2024
0c257ca
register r2 bucket namespace
edmundhung Nov 8, 2024
71f49f2
r2 bucket create
edmundhung Nov 8, 2024
91f3e94
r2 bucket update storage-class
edmundhung Nov 8, 2024
a044ce7
r2 bucket list
edmundhung Nov 8, 2024
d20e9ad
r2 bucket delete
edmundhung Nov 8, 2024
a066c79
register r2 bucket sippy namespace
edmundhung Nov 8, 2024
fbf3f4a
r2 bucket sippy enable
edmundhung Nov 8, 2024
3f4ef7d
r2 bucket sippy disable
edmundhung Nov 8, 2024
53df12f
r2 bucket sippy get
edmundhung Nov 8, 2024
9802f9b
register r2 bucket notification namespace
edmundhung Nov 8, 2024
2d172df
r2 bucket notification list
edmundhung Nov 8, 2024
01f6838
r2 bucket notification create
edmundhung Nov 8, 2024
491074a
r2 bucket notification delete
edmundhung Nov 8, 2024
d81fcd5
register r2 bucket domain namespace
edmundhung Nov 8, 2024
6f7f156
r2 bucket domain list
edmundhung Nov 8, 2024
76871c6
r2 bucket domain add
edmundhung Nov 8, 2024
2d09dd6
r2 bucket domain remove
edmundhung Nov 8, 2024
716c7f6
r2 bucket domain update
edmundhung Nov 8, 2024
791c53b
register r2 bucket dev-url namespace
edmundhung Nov 8, 2024
dcfd52f
r2 bucket dev-url get
edmundhung Nov 8, 2024
38a6d63
r2 bucket dev-url enable
edmundhung Nov 8, 2024
96a0e43
r2 bucket dev-url disable
edmundhung Nov 8, 2024
4afda2c
extract r2 object commands
edmundhung Nov 8, 2024
ddbab37
extract r2 bucket commands
edmundhung Nov 8, 2024
9b9e6b9
update tests to reflect expecation when only namespace is provided
edmundhung Nov 11, 2024
821ac71
fix mistmatch between command and postinoal arg options
edmundhung Nov 11, 2024
4303db0
fix r2 bucket create command
edmundhung Nov 11, 2024
411bfd2
remove duplicated banner
edmundhung Nov 11, 2024
13e3077
fix import order
edmundhung Nov 11, 2024
74db9ca
fix r2 bucket delete positional argument
edmundhung Nov 21, 2024
1f98a3e
ensure r2 bucket list handler reflecting latest changes from #7212
edmundhung Nov 21, 2024
347b3e6
r2 bucket info
edmundhung Nov 21, 2024
bff4056
r2 bucket lifecycle list
edmundhung Nov 21, 2024
c6ee3f0
r2 bucket lifecycle add
edmundhung Nov 21, 2024
50c8fb5
r2 bucket lifecycle remove
edmundhung Nov 21, 2024
f9e4fc8
r2 bucket lifecycle set
edmundhung Nov 21, 2024
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
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
Loading