Skip to content

Commit aaed7e4

Browse files
committed
Remove --location from apphosting:backends:delete
1 parent b21ff0b commit aaed7e4

File tree

3 files changed

+164
-29
lines changed

3 files changed

+164
-29
lines changed

src/apphosting/backend.spec.ts

+81
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
promptLocation,
1313
setDefaultTrafficPolicy,
1414
ensureAppHostingComputeServiceAccount,
15+
chooseBackends,
1516
getBackendForAmbiguousLocation,
1617
} from "./backend";
1718
import * as deploymentTool from "../deploymentTool";
@@ -266,6 +267,86 @@ describe("apphosting setup functions", () => {
266267
});
267268
});
268269

270+
describe("chooseBackends", () => {
271+
const backendChickenAsia = {
272+
name: `projects/${projectId}/locations/asia-east1/backends/chicken`,
273+
labels: {},
274+
createTime: "0",
275+
updateTime: "1",
276+
uri: "https://placeholder.com",
277+
};
278+
279+
const backendChickenEurope = {
280+
name: `projects/${projectId}/locations/europe-west4/backends/chicken`,
281+
labels: {},
282+
createTime: "0",
283+
updateTime: "1",
284+
uri: "https://placeholder.com",
285+
};
286+
287+
const backendChickenUS = {
288+
name: `projects/${projectId}/locations/us-central1/backends/chicken`,
289+
labels: {},
290+
createTime: "0",
291+
updateTime: "1",
292+
uri: "https://placeholder.com",
293+
};
294+
295+
const backendCow = {
296+
name: `projects/${projectId}/locations/asia-east1/backends/cow`,
297+
labels: {},
298+
createTime: "0",
299+
updateTime: "1",
300+
uri: "https://placeholder.com",
301+
};
302+
303+
const allBackends = [backendChickenAsia, backendChickenEurope, backendChickenUS, backendCow];
304+
305+
it("returns backend if only one is found", async () => {
306+
listBackendsStub.resolves({
307+
backends: allBackends,
308+
});
309+
310+
await expect(chooseBackends(projectId, "cow", /* prompt= */ "")).to.eventually.equal(
311+
[backendCow],
312+
);
313+
});
314+
315+
it("throws if --force is used when multiple backends are found", async () => {
316+
listBackendsStub.resolves({
317+
backends: allBackends,
318+
});
319+
320+
await expect(
321+
chooseBackends(projectId, "chicken", /* prompt= */ "", /* force= */ true),
322+
).to.be.rejectedWith(
323+
"Force cannot be used because multiple backends were found with ID chicken.",
324+
);
325+
});
326+
327+
it("throws if no backend is found", async () => {
328+
listBackendsStub.resolves({
329+
backends: allBackends,
330+
});
331+
332+
await expect(chooseBackends(projectId, "farmer", /* prompt= */ "")).to.be.rejectedWith(
333+
'No backend named "farmer" found.',
334+
);
335+
});
336+
337+
it("lets user choose backends when more than one share a name", async () => {
338+
listBackendsStub.resolves({
339+
backends: allBackends,
340+
});
341+
promptOnceStub.resolves(["chicken(asia-east1)", "chicken(europe-west4)"]);
342+
343+
await expect(chooseBackends(projectId, "chicken", /* prompt= */ "")).to.eventually.equal([
344+
backendChickenAsia,
345+
backendChickenEurope,
346+
]);
347+
});
348+
});
349+
269350
describe("getBackendForAmbiguousLocation", () => {
270351
const backendFoo = {
271352
name: `projects/${projectId}/locations/${location}/backends/foo`,

src/apphosting/backend.ts

+58
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,64 @@ export async function getBackendForLocation(
429429
}
430430
}
431431

432+
/**
433+
* Fetches backends of the given backendId and lets the user choose if more than one is found.
434+
*/
435+
export async function chooseBackends(
436+
projectId: string,
437+
backendId: string,
438+
chooseBackendPrompt: string,
439+
force?: boolean,
440+
): Promise<apphosting.Backend[]> {
441+
let { unreachable, backends } = await apphosting.listBackends(projectId, "-");
442+
if (unreachable && unreachable.length !== 0) {
443+
logWarning(
444+
`The following locations are currently unreachable: ${unreachable.join(",")}.\n` +
445+
"If your backend is in one of these regions, please try again later.",
446+
);
447+
}
448+
backends = backends.filter(
449+
(backend) => apphosting.parseBackendName(backend.name).id === backendId,
450+
);
451+
if (backends.length === 0) {
452+
throw new FirebaseError(`No backend named "${backendId}" found.`);
453+
}
454+
if (backends.length === 1) {
455+
return backends;
456+
}
457+
458+
if (force) {
459+
throw new FirebaseError(
460+
`Force cannot be used because multiple backends were found with ID ${backendId}.`,
461+
);
462+
}
463+
const backendsByDisplay = new Map<string, apphosting.Backend>();
464+
backends.forEach((backend) => {
465+
const { location, id } = apphosting.parseBackendName(backend.name);
466+
backendsByDisplay.set(`${id}(${location})`, backend);
467+
});
468+
const chosenBackendDisplays = await promptOnce({
469+
name: "backend",
470+
type: "checkbox",
471+
message: chooseBackendPrompt,
472+
choices: Array.from(backendsByDisplay.keys(), (name) => {
473+
return {
474+
checked: false,
475+
name: name,
476+
value: name,
477+
};
478+
}),
479+
});
480+
const chosenBackends: apphosting.Backend[] = [];
481+
chosenBackendDisplays.forEach((backendDisplay) => {
482+
const backend = backendsByDisplay.get(backendDisplay);
483+
if (backend !== undefined) {
484+
chosenBackends.push(backend);
485+
}
486+
});
487+
return chosenBackends;
488+
}
489+
432490
/**
433491
* Fetches a backend from the server. If there are multiple backends with that name (ie multi-regional backends),
434492
* prompts the user to disambiguate. If the force option is specified and multiple backends have the same name,

src/commands/apphosting-backends-delete.ts

+25-29
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,25 @@ import { promptOnce } from "../prompt";
66
import * as utils from "../utils";
77
import * as apphosting from "../gcp/apphosting";
88
import { printBackendsTable } from "./apphosting-backends-list";
9-
import {
10-
deleteBackendAndPoll,
11-
getBackendForAmbiguousLocation,
12-
getBackendForLocation,
13-
} from "../apphosting/backend";
9+
import { deleteBackendAndPoll, chooseBackends } from "../apphosting/backend";
1410
import * as ora from "ora";
1511

1612
export const command = new Command("apphosting:backends:delete <backend>")
1713
.description("delete a Firebase App Hosting backend")
18-
.option("-l, --location <location>", "specify the location of the backend", "-")
1914
.withForce()
2015
.before(apphosting.ensureApiEnabled)
2116
.action(async (backendId: string, options: Options) => {
2217
const projectId = needProjectId(options);
23-
let location = options.location as string;
24-
let backend: apphosting.Backend;
25-
if (location === "-" || location === "") {
26-
backend = await getBackendForAmbiguousLocation(
27-
projectId,
28-
backendId,
29-
"Please select the location of the backend you'd like to delete:",
30-
);
31-
location = apphosting.parseBackendName(backend.name).location;
32-
} else {
33-
backend = await getBackendForLocation(projectId, location, backendId);
34-
}
3518

36-
utils.logWarning("You are about to permanently delete this backend:");
37-
printBackendsTable([backend]);
19+
const backends = await chooseBackends(
20+
projectId,
21+
backendId,
22+
"Please select the backends you'd like to delete:",
23+
options.force,
24+
);
25+
26+
utils.logWarning("You are about to permanently delete these backend(s):");
27+
printBackendsTable(backends);
3828

3929
const confirmDeletion = await promptOnce(
4030
{
@@ -49,14 +39,20 @@ export const command = new Command("apphosting:backends:delete <backend>")
4939
return;
5040
}
5141

52-
const spinner = ora("Deleting backend...").start();
53-
try {
54-
await deleteBackendAndPoll(projectId, location, backendId);
55-
spinner.succeed(`Successfully deleted the backend: ${backendId}`);
56-
} catch (err: unknown) {
57-
spinner.stop();
58-
throw new FirebaseError(`Failed to delete backend: ${backendId}.`, {
59-
original: getError(err),
60-
});
42+
for (const b of backends) {
43+
const { location, id } = apphosting.parseBackendName(b.name);
44+
const spinner = ora(`Deleting backend ${id}(${location})...`).start();
45+
try {
46+
await deleteBackendAndPoll(projectId, location, id);
47+
spinner.succeed(`Successfully deleted the backend: ${id}(${location})`);
48+
} catch (err: unknown) {
49+
spinner.stop();
50+
throw new FirebaseError(
51+
`Failed to delete backend: ${id}(${location}). Please retry to delete remaining backends.`,
52+
{
53+
original: getError(err),
54+
},
55+
);
56+
}
6157
}
6258
});

0 commit comments

Comments
 (0)