Skip to content

Commit

Permalink
feat: implement a basic wrangler delete
Browse files Browse the repository at this point in the history
This PR adds a simple (but useful!) implementation for `wrangler delete`. Of note, it'll delete a given service, including all it's bindings. It uses the same api as the dashboard.
  • Loading branch information
threepointone committed Oct 18, 2022
1 parent ccfdd0b commit fc77c35
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changeset/rare-eels-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

feat: implement a basic `wrangler delete`

This PR adds a simple (but useful!) implementation for `wrangler delete`. Of note, it'll delete a given service, including all it's bindings. It uses the same api as the dashboard.
89 changes: 89 additions & 0 deletions packages/wrangler/src/__tests__/delete.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
import { mockConsoleMethods } from "./helpers/mock-console";
import { runInTempDir } from "./helpers/run-in-tmp";
import { runWrangler } from "./helpers/run-wrangler";
import writeWranglerToml from "./helpers/write-wrangler-toml";

describe("delete", () => {
mockAccountId();
mockApiToken();
runInTempDir();

afterEach(() => {
unsetAllMocks();
});

const std = mockConsoleMethods();

it("should delete an entire service by name", async () => {
mockDeleteWorkerRequest({ name: "my-script" });
await runWrangler("delete --name my-script");

expect(std).toMatchInlineSnapshot(`
Object {
"debug": "",
"err": "",
"out": "Successfully deleted my-script",
"warn": "",
}
`);
});

it("should delete a script by configuration", async () => {
writeWranglerToml();
mockDeleteWorkerRequest();
await runWrangler("delete");

expect(std).toMatchInlineSnapshot(`
Object {
"debug": "",
"err": "",
"out": "Successfully deleted test-name",
"warn": "",
}
`);
});

it("shouldn't delete a service when doing a --dry-run", async () => {
await runWrangler("delete --name xyz --dry-run");

expect(std).toMatchInlineSnapshot(`
Object {
"debug": "",
"err": "",
"out": "--dry-run: exiting now.",
"warn": "",
}
`);
});
});

/** Create a mock handler for the request to upload a worker script. */
function mockDeleteWorkerRequest(
options: {
name?: string;
env?: string;
legacyEnv?: boolean;
} = {}
) {
const { env, legacyEnv, name } = options;
setMockResponse(
// there's no special handling for environments yet
"/accounts/:accountId/workers/services/:scriptName",
"DELETE",
async ([_url, accountId, scriptName], { method }, queryParams) => {
expect(accountId).toEqual("some-account-id");
expect(method).toEqual("DELETE");
expect(scriptName).toEqual(
legacyEnv && env
? `${name || "test-name"}-${env}`
: `${name || "test-name"}`
);

expect(queryParams.get("force")).toEqual("true");

return null;
}
);
}
2 changes: 2 additions & 0 deletions packages/wrangler/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe("wrangler", () => {
wrangler init [name] 📥 Create a wrangler.toml configuration file
wrangler dev [script] 👂 Start a local server for developing your worker
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
wrangler tail [worker] 🦚 Starts a log tailing session for a published Worker.
wrangler secret 🤫 Generate a secret that can be referenced in a Worker
wrangler secret:bulk <json> 🗄️ Bulk upload secrets for a Worker
Expand Down Expand Up @@ -75,6 +76,7 @@ describe("wrangler", () => {
wrangler init [name] 📥 Create a wrangler.toml configuration file
wrangler dev [script] 👂 Start a local server for developing your worker
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
wrangler tail [worker] 🦚 Starts a log tailing session for a published Worker.
wrangler secret 🤫 Generate a secret that can be referenced in a Worker
wrangler secret:bulk <json> 🗄️ Bulk upload secrets for a Worker
Expand Down
84 changes: 84 additions & 0 deletions packages/wrangler/src/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import path from "path";
import { fetchResult } from "./cfetch";
import { findWranglerToml, readConfig } from "./config";
import { logger } from "./logger";
import * as metrics from "./metrics";
import { requireAuth } from "./user";
import { getScriptName, printWranglerBanner } from "./index";
import type { ConfigPath } from "./index";
import type { YargsOptionsToInterface } from "./yargs-types";
import type { Argv, ArgumentsCamelCase } from "yargs";

export function deleteOptions(yargs: Argv) {
return yargs
.option("env", {
type: "string",
requiresArg: true,
describe: "Perform on a specific environment",
alias: "e",
})
.positional("script", {
describe: "The path to an entry point for your worker",
type: "string",
requiresArg: true,
})
.option("name", {
describe: "Name of the worker",
type: "string",
requiresArg: true,
})
.option("assets", {
describe: "Static assets to be served",
type: "string",
requiresArg: true,
})
.option("site", {
describe: "Root folder of static assets for Workers Sites",
type: "string",
requiresArg: true,
})
.option("dry-run", {
describe: "Don't actually delete",
type: "boolean",
})
.option("legacy-env", {
type: "boolean",
describe: "Use legacy environments",
hidden: true,
});
}

type DeleteArgs = YargsOptionsToInterface<typeof deleteOptions>;

export async function deleteHandler(args: ArgumentsCamelCase<DeleteArgs>) {
await printWranglerBanner();

const configPath =
(args.config as ConfigPath) ||
(args.script && findWranglerToml(path.dirname(args.script)));
const config = readConfig(configPath, args);
await metrics.sendMetricsEvent(
"delete worker script",
{},
{ sendMetrics: config.send_metrics }
);

const accountId = args.dryRun ? undefined : await requireAuth(config);

const scriptName = getScriptName(args, config);

if (args.dryRun) {
logger.log(`--dry-run: exiting now.`);
return;
}

await fetchResult(
`/accounts/${accountId}/workers/services/${scriptName}`,
{ method: "DELETE" },
new URLSearchParams({ force: "true" })
);

logger.log("Successfully deleted", scriptName);

// TODO: maybe delete sites/assets kv namespace as well?
}
9 changes: 9 additions & 0 deletions packages/wrangler/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import makeCLI from "yargs";
import { version as wranglerVersion } from "../package.json";
import { readConfig } from "./config";
import { d1 } from "./d1";
import { deleteHandler, deleteOptions } from "./delete";
import {
buildHandler,
buildOptions,
Expand Down Expand Up @@ -258,6 +259,14 @@ export function createCLIParser(argv: string[]) {
publishHandler
);

// delete
wrangler.command(
"delete [script]",
"🗑 Delete your Worker from Cloudflare.",
deleteOptions,
deleteHandler
);

// tail
wrangler.command(
"tail [worker]",
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/metrics/send-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Properties } from "./metrics-dispatcher";
export type EventNames =
| "view accounts"
| "deploy worker script"
| "delete worker script"
| "begin log stream"
| "end log stream"
| "create encrypted variable"
Expand Down

0 comments on commit fc77c35

Please sign in to comment.