Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions kits/delete-user-data/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- fix: the functions deploy to `us-central1` by default again, as the extension did. The kit set no region, so the Firebase CLI deployed them to `us-east1`. The region is now read from `FUNCTION_DEFAULT_REGION` (default `us-central1`), the name `firebase ext:migrate` writes to `.env`, so each instance can deploy to its own region. `setGlobalOptions({ region })` no longer moves the functions; set `FUNCTION_DEFAULT_REGION` instead.

- fix: an Eventarc publish failure no longer fails the function. A channel that was deleted or is unreachable answers `PERMISSION_DENIED`, and the publish was awaited after the work, so the deletion completed but the invocation still failed. The publish failure is now logged as a warning and the invocation continues. Deploys also declare `eventarcpublishing.googleapis.com`, so the API is enabled on your project whether or not you use events. The failed invocation used to be redelivered, re-running a deletion that had already succeeded; it no longer is.

- fix: `ENABLE_AUTO_DISCOVERY` accepts the extension's `yes` / `no` values again. The kit had declared it as a boolean param, which only counts the literal `true` as enabled, so a `.env` copied from an installed extension instance carried `yes` and silently left auto-discovery switched off. The param is now the extension's labeled `Yes` / `No` select and is read the same way the extension read it.
Expand Down
23 changes: 19 additions & 4 deletions kits/delete-user-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ overridden there.
| `searchDepth` | `AUTO_DISCOVERY_SEARCH_DEPTH` | no | `3` | Discovery depth |
| `searchFields` | `AUTO_DISCOVERY_SEARCH_FIELDS` | no | `id,uid,userId` | Fields treated as user ids |
| `searchFunction` | `SEARCH_FUNCTION` | no | (empty) | URL of a custom search function that returns paths to delete |
| — | `FUNCTION_DEFAULT_REGION` | no | `us-central1` | Region the functions deploy to |
| `discoveryTopicName` | `DISCOVERY_TOPIC_NAME` | no | `kit-<instance id>-discovery` | Pub/Sub discovery topic |
| `deletionTopicName` | `DELETION_TOPIC_NAME` | no | `kit-<instance id>-deletion` | Pub/Sub deletion topic |

Expand Down Expand Up @@ -187,11 +188,25 @@ You can also override both names with `DISCOVERY_TOPIC_NAME` and
`DELETION_TOPIC_NAME`, which the extension did not allow. Change them together,
since one function publishes to a topic the other is triggered by.

### Functions deploy to your default region
### Functions deploy to `FUNCTION_DEFAULT_REGION`

The extension deployed to the location you picked at install time. This kit
sets no region, so its functions deploy to your codebase's default
(`us-central1` unless you have changed it).
The extension deployed to the location you picked at install time
(`us-central1` by default). The kit reads the region from
`FUNCTION_DEFAULT_REGION` in each instance's `.env`, also defaulting to
`us-central1`, so instances can deploy to different regions.
`firebase ext:migrate` writes it with the region the extension ran in.

```sh
# .env (or .env.<projectId>)
FUNCTION_DEFAULT_REGION=us-central1
```

Keep the functions in the same region as your Firestore database (for a `nam5`
database, `us-central1`), so user data is not read and deleted from another
region.

The kit sets the region on each function, so `setGlobalOptions({ region })`
does not move them. Set `FUNCTION_DEFAULT_REGION` instead.

### All functions are 2nd gen

Expand Down
10 changes: 10 additions & 0 deletions kits/delete-user-data/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ const params = {
},
},
}),
// The extension's install-time location, under the name the CLI's generated
// entry file and `ext:migrate` use. Defaulting here keeps the extension's
// `us-central1`; with no region set the CLI would deploy to `us-east1`.
functionRegion: defineString("FUNCTION_DEFAULT_REGION", {
label: "Cloud Functions location",
description:
"Where do you want to deploy the functions created for this kit? You usually want a location close to your database.",
default: "us-central1",
}),
// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
Expand All @@ -174,6 +183,7 @@ const params = {
};

export const CONFIG_EXPRESSIONS = {
functionRegion: params.functionRegion,
discoveryTopicName: params.discoveryTopicName,
deletionTopicName: params.deletionTopicName,
} as const;
Expand Down
24 changes: 15 additions & 9 deletions kits/delete-user-data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,26 +109,32 @@ function getContext(): HandlerContext {
return ctx;
}

export const clearData = onUserDeleted((event) => {
// The Auth event delivers no user record when the payload envelope is empty,
// so bail before getContext() rather than initialising the SDKs for nothing.
const uid = event.data?.uid;
if (!uid) {
logs.deletionEventMissingUid(event.id);
return;
export const clearData = onUserDeleted(
{ region: CONFIG_EXPRESSIONS.functionRegion },
(event) => {
// The Auth event delivers no user record when the payload envelope is
// empty, so bail before getContext() rather than initialising the SDKs for
// nothing.
const uid = event.data?.uid;
if (!uid) {
logs.deletionEventMissingUid(event.id);
return;
}
return handleClear(uid, getContext());
}
return handleClear(uid, getContext());
});
);

export const handleSearch = onMessagePublished(
{
region: CONFIG_EXPRESSIONS.functionRegion,
topic: CONFIG_EXPRESSIONS.discoveryTopicName,
},
(event) => runSearch(event.data.message.json, getContext())
);

export const handleDeletion = onMessagePublished(
{
region: CONFIG_EXPRESSIONS.functionRegion,
topic: CONFIG_EXPRESSIONS.deletionTopicName,
},
(event) => runDeletion(event.data.message.json, getContext())
Expand Down
37 changes: 37 additions & 0 deletions kits/delete-user-data/tests/region.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, test, vi } from "vitest";
Comment thread
CorieW marked this conversation as resolved.
Resolved

const FUNCTIONS = ["clearData", "handleSearch", "handleDeletion"] as const;

async function loadKit() {
vi.resetModules();
return (await import("../src/index")) as Record<string, any>;
}

// The extension deployed to its install-time location, `us-central1` by
// default. With no region set the CLI would pick `us-east1` instead.
describe("function region", () => {
test("is read from FUNCTION_DEFAULT_REGION, defaulting to us-central1", async () => {
const kit = await loadKit();
for (const name of FUNCTIONS) {
const region = kit[name].__endpoint.region;
expect(region.name).toBe("FUNCTION_DEFAULT_REGION");
expect(region.options.default).toBe("us-central1");
}
});
});
Loading