Skip to content

Commit f480ec7

Browse files
WC-3776 Require confirm for delete when Pages project binds to worker (#10187)
EWC changes add the new "pages_function" boolean field to the service reference API when a pages function has a service binding to this worker, and requires "force=true" when deleting Co-authored-by: Matthew Rodgers <[email protected]>
1 parent 1f686ef commit f480ec7

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

.changeset/hip-sites-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Deleting when Pages project binds to worker requires confirmation

packages/wrangler/src/__tests__/delete.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,99 @@ Are you sure you want to continue?`,
384384
}
385385
`);
386386
});
387+
388+
it("should prompt for extra confirmation when worker is used by a Pages function", async () => {
389+
mockConfirm({
390+
text: `Are you sure you want to delete test-name? This action cannot be undone.`,
391+
result: true,
392+
});
393+
mockConfirm({
394+
text: `test-name is currently in use by other Workers:
395+
396+
- A Pages project has a Service Binding to this Worker
397+
398+
You can still delete this Worker, but doing so WILL BREAK the Workers that depend on it. This will cause unexpected failures, and cannot be undone.
399+
Are you sure you want to continue?`,
400+
result: true,
401+
});
402+
writeWranglerConfig();
403+
mockListKVNamespacesRequest();
404+
mockListReferencesRequest("test-name", {
405+
services: {
406+
incoming: [],
407+
outgoing: [],
408+
pages_function: true,
409+
},
410+
});
411+
mockListTailsByConsumerRequest("test-name");
412+
mockDeleteWorkerRequest({ force: true });
413+
await runWrangler("delete");
414+
415+
expect(std).toMatchInlineSnapshot(`
416+
Object {
417+
"debug": "",
418+
"err": "",
419+
"info": "",
420+
"out": "Successfully deleted test-name",
421+
"warn": "",
422+
}
423+
`);
424+
});
425+
426+
it("should include Pages function in confirmation when combined with other dependencies", async () => {
427+
mockConfirm({
428+
text: `Are you sure you want to delete test-name? This action cannot be undone.`,
429+
result: true,
430+
});
431+
mockConfirm({
432+
text: `test-name is currently in use by other Workers:
433+
434+
- Worker existing-worker (production) uses this Worker as a Service Binding
435+
- A Pages project has a Service Binding to this Worker
436+
- Worker do-binder (production) has a binding to the Durable Object Namespace "actor_ns" implemented by this Worker
437+
438+
You can still delete this Worker, but doing so WILL BREAK the Workers that depend on it. This will cause unexpected failures, and cannot be undone.
439+
Are you sure you want to continue?`,
440+
result: true,
441+
});
442+
writeWranglerConfig();
443+
mockListKVNamespacesRequest();
444+
mockListReferencesRequest("test-name", {
445+
services: {
446+
incoming: [
447+
{
448+
service: "existing-worker",
449+
environment: "production",
450+
name: "BINDING",
451+
},
452+
],
453+
outgoing: [],
454+
pages_function: true,
455+
},
456+
durable_objects: [
457+
{
458+
service: "do-binder",
459+
environment: "production",
460+
name: "ACTOR",
461+
durable_object_namespace_id: "123",
462+
durable_object_namespace_name: "actor_ns",
463+
},
464+
],
465+
});
466+
mockListTailsByConsumerRequest("test-name");
467+
mockDeleteWorkerRequest({ force: true });
468+
await runWrangler("delete");
469+
470+
expect(std).toMatchInlineSnapshot(`
471+
Object {
472+
"debug": "",
473+
"err": "",
474+
"info": "",
475+
"out": "Successfully deleted test-name",
476+
"warn": "",
477+
}
478+
`);
479+
});
387480
});
388481
});
389482

packages/wrangler/src/delete.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type ServiceReferenceResponse = {
3535
services?: {
3636
incoming: ServiceReference[];
3737
outgoing: ServiceReference[];
38+
pages_function?: boolean;
3839
};
3940
durable_objects?: DurableObjectServiceReference[];
4041
dispatch_outbounds?: DispatchOutboundsServiceReference[];
@@ -196,6 +197,10 @@ function isUsedAsServiceBinding(references: ServiceReferenceResponse) {
196197
return (references.services?.incoming.length || 0) > 0;
197198
}
198199

200+
function isUsedByPagesFunction(references: ServiceReferenceResponse) {
201+
return references.services?.pages_function === true;
202+
}
203+
199204
function isUsedAsDurableObjectNamespace(
200205
references: ServiceReferenceResponse,
201206
scriptName: string
@@ -226,6 +231,7 @@ async function checkAndConfirmForceDeleteIfNecessary(
226231
);
227232
const isDependentService =
228233
isUsedAsServiceBinding(references) ||
234+
isUsedByPagesFunction(references) ||
229235
isUsedAsDurableObjectNamespace(references, scriptName) ||
230236
isUsedAsDispatchOutbound(references) ||
231237
isUsedAsTailConsumer(tailProducers);
@@ -240,6 +246,11 @@ async function checkAndConfirmForceDeleteIfNecessary(
240246
`- Worker ${dependentScript} uses this Worker as a Service Binding`
241247
);
242248
}
249+
if (isUsedByPagesFunction(references)) {
250+
dependentMessages.push(
251+
`- A Pages project has a Service Binding to this Worker`
252+
);
253+
}
243254
for (const implementedDOBindingReference of references.durable_objects ||
244255
[]) {
245256
if (implementedDOBindingReference.service === scriptName) {

0 commit comments

Comments
 (0)