From 26855f39ae635feebb9d5768b64494e73d979b47 Mon Sep 17 00:00:00 2001 From: Mikkel Ricafrente <37487746+helloimalastair@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:41:06 +0100 Subject: [PATCH] Add `demandCommand()` to R2 Commands (#6105) * Add help messages to all invalid `r2` commands --- .changeset/chilled-ties-occur.md | 5 + packages/wrangler/src/__tests__/r2.test.ts | 109 +++++++++++++++++++++ packages/wrangler/src/index.ts | 2 +- packages/wrangler/src/r2/index.ts | 7 +- 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 .changeset/chilled-ties-occur.md diff --git a/.changeset/chilled-ties-occur.md b/.changeset/chilled-ties-occur.md new file mode 100644 index 000000000000..1004fbec6ace --- /dev/null +++ b/.changeset/chilled-ties-occur.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +feat: Add help messages to all invalid `r2` commands diff --git a/packages/wrangler/src/__tests__/r2.test.ts b/packages/wrangler/src/__tests__/r2.test.ts index cd7552eecd79..9875f90e194e 100644 --- a/packages/wrangler/src/__tests__/r2.test.ts +++ b/packages/wrangler/src/__tests__/r2.test.ts @@ -2,6 +2,7 @@ import * as fs from "node:fs"; import { http, HttpResponse } from "msw"; import { MAX_UPLOAD_SIZE } from "../r2/constants"; 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 { useMockIsTTY } from "./helpers/mock-istty"; @@ -21,10 +22,91 @@ describe("r2", () => { runInTempDir(); + it("should show help when no argument is passed", async () => { + await runWrangler("r2"); + await endEventLoop(); + expect(std.out).toMatchInlineSnapshot(` + "wrangler r2 + + 📦 Manage R2 buckets & objects + + COMMANDS + wrangler r2 object Manage R2 objects + wrangler r2 bucket Manage R2 buckets + + GLOBAL FLAGS + -j, --experimental-json-config Experimental: support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean]" + `); + }); + + it("should show help when an invalid argument is passed", async () => { + await expect(() => runWrangler("r2 asdf")).rejects.toThrow( + "Unknown argument: asdf" + ); + await endEventLoop(); + expect(std.err).toMatchInlineSnapshot(` + "X [ERROR] Unknown argument: asdf + + " + `); + expect(std.out).toMatchInlineSnapshot(` + " + wrangler r2 + + 📦 Manage R2 buckets & objects + + COMMANDS + wrangler r2 object Manage R2 objects + wrangler r2 bucket Manage R2 buckets + + GLOBAL FLAGS + -j, --experimental-json-config Experimental: support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean]" + `); + }); + describe("bucket", () => { mockAccountId(); 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 + +"`); + expect(std.out).toMatchInlineSnapshot(` + " + wrangler r2 bucket + + Manage R2 buckets + + COMMANDS + wrangler r2 bucket create Create a new R2 bucket + wrangler r2 bucket update Update bucket state + wrangler r2 bucket list List R2 buckets + wrangler r2 bucket delete Delete an R2 bucket + wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket + wrangler r2 bucket notification Manage event notifications for an R2 bucket + + GLOBAL FLAGS + -j, --experimental-json-config Experimental: support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean]" + `); + }); + it("should show the correct help when an invalid command is passed", async () => { await expect(() => runWrangler("r2 bucket foo") @@ -975,6 +1057,33 @@ describe("r2", () => { }); 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 + +"`); + expect(std.out).toMatchInlineSnapshot(` + " + wrangler r2 object + + Manage R2 objects + + COMMANDS + wrangler r2 object get Fetch an object from an R2 bucket + wrangler r2 object put Create an object in an R2 bucket + wrangler r2 object delete Delete an object in an R2 bucket + + GLOBAL FLAGS + -j, --experimental-json-config Experimental: support wrangler.json [boolean] + -c, --config Path to .toml configuration file [string] + -e, --env Environment to use for operations and .env files [string] + -h, --help Show help [boolean] + -v, --version Show version number [boolean]" + `); + }); describe("remote", () => { // Only login for remote tests, local tests shouldn't require auth mockAccountId(); diff --git a/packages/wrangler/src/index.ts b/packages/wrangler/src/index.ts index b184f476b836..c34c0279fea0 100644 --- a/packages/wrangler/src/index.ts +++ b/packages/wrangler/src/index.ts @@ -532,7 +532,7 @@ export function createCLIParser(argv: string[]) { // r2 wrangler.command("r2", "📦 Manage R2 buckets & objects", (r2Yargs) => { - return r2(r2Yargs.command(subHelp)); + return r2(r2Yargs, subHelp); }); // d1 diff --git a/packages/wrangler/src/r2/index.ts b/packages/wrangler/src/r2/index.ts index 76f6410ec279..67387698ce42 100644 --- a/packages/wrangler/src/r2/index.ts +++ b/packages/wrangler/src/r2/index.ts @@ -24,7 +24,7 @@ import { } from "./helpers"; import * as Notification from "./notification"; import * as Sippy from "./sippy"; -import type { CommonYargsArgv } from "../yargs-types"; +import type { CommonYargsArgv, SubHelp } from "../yargs-types"; import type { R2PutOptions } from "@cloudflare/workers-types/experimental"; const CHUNK_SIZE = 1024; @@ -50,10 +50,12 @@ async function createFileReadableStream(filePath: string) { }); } -export function r2(r2Yargs: CommonYargsArgv) { +export function r2(r2Yargs: CommonYargsArgv, subHelp: SubHelp) { return r2Yargs + .command(subHelp) .command("object", "Manage R2 objects", (r2ObjectYargs) => { return r2ObjectYargs + .demandCommand() .command( "get ", "Fetch an object from an R2 bucket", @@ -427,6 +429,7 @@ export function r2(r2Yargs: CommonYargsArgv) { }) .command("bucket", "Manage R2 buckets", (r2BucketYargs) => { + r2BucketYargs.demandCommand(); r2BucketYargs.command( "create ", "Create a new R2 bucket",