Skip to content

Commit 57dd8f0

Browse files
committed
feat(delete-user-data): add non-default firestore instance support
1 parent 2c6ca7e commit 57dd8f0

File tree

10 files changed

+53
-27
lines changed

10 files changed

+53
-27
lines changed

delete-user-data/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Version 0.2.0
2+
3+
feat - add support for multiple Firestore databases via FIRESTORE_DATABASE_ID parameter
4+
15
## Version 0.1.24
26

37
feat - move to Node.js 20 runtimes

delete-user-data/PREINSTALL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Depending on where you'd like to delete user data from, make sure that you've se
1414

1515
Also, make sure that you've set up [Firebase Authentication](https://firebase.google.com/docs/auth) to manage your users.
1616

17+
**Note about multiple Firestore databases:** This extension supports using non-default Firestore databases. During installation, you can specify which database to use via the `FIRESTORE_DATABASE_ID` parameter. Use "(default)" for the default database, or specify your named database ID.
18+
1719
#### Billing
1820
To install an extension, your project must be on the [Blaze (pay as you go) plan](https://firebase.google.com/pricing)
1921

delete-user-data/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Depending on where you'd like to delete user data from, make sure that you've se
2222

2323
Also, make sure that you've set up [Firebase Authentication](https://firebase.google.com/docs/auth) to manage your users.
2424

25+
**Note about multiple Firestore databases:** This extension supports using non-default Firestore databases. During installation, you can specify which database to use via the `FIRESTORE_DATABASE_ID` parameter. Use "(default)" for the default database, or specify your named database ID.
26+
2527
#### Billing
2628
To install an extension, your project must be on the [Blaze (pay as you go) plan](https://firebase.google.com/pricing)
2729

@@ -37,6 +39,8 @@ To install an extension, your project must be on the [Blaze (pay as you go) plan
3739

3840
**Configuration Parameters:**
3941

42+
* Firestore Database ID: The ID of the Firestore database to use. Use "(default)" for the default database. You can view your available Firestore databases at https://console.cloud.google.com/firestore/databases.
43+
4044
* Cloud Firestore paths: Which paths in your Cloud Firestore instance contain data keyed on a user ID? Leave empty if you don't use Cloud Firestore.
4145
Enter the full paths, separated by commas. Use `{UID}` as a placeholder for the user's UID.
4246
For example, if you have the collections `users` and `admins`, and each collection has documents with User ID as document IDs, then enter `users/{UID},admins/{UID}`.

delete-user-data/extension.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
name: delete-user-data
16-
version: 0.1.24
16+
version: 0.2.0
1717
specVersion: v1beta
1818

1919
displayName: Delete User Data
@@ -86,6 +86,16 @@ resources:
8686
resource: projects/${PROJECT_ID}/topics/ext-${EXT_INSTANCE_ID}-deletion
8787

8888
params:
89+
- param: FIRESTORE_DATABASE_ID
90+
label: Firestore Database ID
91+
description: >-
92+
The ID of the Firestore database to use. Use "(default)" for the default
93+
database. You can view your available Firestore databases at
94+
https://console.cloud.google.com/firestore/databases.
95+
type: string
96+
default: (default)
97+
required: true
98+
8999
- param: FIRESTORE_PATHS
90100
label: Cloud Firestore paths
91101
description: >-

delete-user-data/functions/__tests__/recursiveDelete.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("recursiveDelete", () => {
2020
foo: "bar",
2121
});
2222

23-
await recursiveDelete(ref);
23+
await recursiveDelete(ref, db);
2424

2525
const doc = db.doc(ref);
2626
await doc.get().then((doc) => {
@@ -34,7 +34,7 @@ describe("recursiveDelete", () => {
3434
foo: "bar",
3535
});
3636

37-
await recursiveDelete(ref);
37+
await recursiveDelete(ref, db);
3838

3939
const collection = db.collection(ref);
4040
await collection.get().then((collection) => {
@@ -49,7 +49,7 @@ describe("recursiveDelete", () => {
4949
foo: "bar",
5050
});
5151

52-
await recursiveDelete(parentRef);
52+
await recursiveDelete(parentRef, db);
5353

5454
const collection = db.collection(ref);
5555
await collection.get().then((collection) => {

delete-user-data/functions/__tests__/search.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe("discovery", () => {
4444

4545
describe("searches on top level collections", () => {
4646
test("can delete is single collection named {uid}", async () => {
47-
await search(user.uid, 1);
47+
await search(user.uid, 1, db);
4848

4949
await waitForCollectionDeletion(rootCollection, 20_000);
5050
}, 60000);
@@ -53,7 +53,7 @@ describe("discovery", () => {
5353
describe("searches on top level collection documents", () => {
5454
test("can delete a document named {uid}", async () => {
5555
const document = await db.collection(generateRandomId()).doc(user.uid);
56-
await search(user.uid, 1);
56+
await search(user.uid, 1, db);
5757

5858
await waitForDocumentDeletion(document);
5959
}, 60000);
@@ -62,14 +62,14 @@ describe("discovery", () => {
6262
const document = await db
6363
.collection(generateRandomId())
6464
.add({ field1: user.uid });
65-
await search(user.uid, 1);
65+
await search(user.uid, 1, db);
6666

6767
await waitForDocumentDeletion(document, 60000);
6868
}, 60000);
6969

7070
test("can check a document without any field values", async () => {
7171
await db.collection(generateRandomId()).add({});
72-
await search(user.uid, 1);
72+
await search(user.uid, 1, db);
7373

7474
expect(true).toBeTruthy();
7575
}, 60000);
@@ -93,7 +93,7 @@ describe("discovery", () => {
9393

9494
expect(checkExists).toBe(true);
9595

96-
await search(user.uid, 1);
96+
await search(user.uid, 1, db);
9797

9898
await waitForCollectionDeletion(subcollection);
9999
}, 60000);
@@ -118,7 +118,7 @@ describe("discovery", () => {
118118

119119
expect(collectionPathCount).toBeGreaterThan(config.searchDepth);
120120

121-
await search(user.uid, 1);
121+
await search(user.uid, 1, db);
122122

123123
// /** Wait 10 seconds for the discovery to complete */
124124
await new Promise((resolve) => setTimeout(resolve, 20000));
@@ -149,7 +149,7 @@ describe("discovery", () => {
149149

150150
expect(collectionPathCount).toBeGreaterThan(config.searchDepth);
151151

152-
await search(user.uid, 1);
152+
await search(user.uid, 1, db);
153153

154154
// /** Wait 10 seconds for the discovery to complete */
155155
await new Promise((resolve) => setTimeout(resolve, 20000));
@@ -169,7 +169,7 @@ describe("discovery", () => {
169169
const collection = await db.collection(generateRandomId());
170170
const document = await collection.add({ testing: "should-not-delete" });
171171

172-
await search(collection.id, -1, document);
172+
await search(collection.id, -1, db, document);
173173

174174
/** Check document still exists */
175175
const checkExists = await document.get().then((doc) => doc.exists);
@@ -180,7 +180,7 @@ describe("discovery", () => {
180180
const document = await db
181181
.collection(generateRandomId())
182182
.add({ field1: "unknown" });
183-
await search(user.uid, 1);
183+
await search(user.uid, 1, db);
184184

185185
/** Wait 10 seconds */
186186
await new Promise((resolve) => setTimeout(resolve, 10000));

delete-user-data/functions/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
export default {
1818
location: process.env.LOCATION,
19+
databaseId: process.env.FIRESTORE_DATABASE_ID || "(default)",
1920
firestorePaths: process.env.FIRESTORE_PATHS,
2021
firestoreDeleteMode: process.env.FIRESTORE_DELETE_MODE,
2122
rtdbPaths: process.env.RTDB_PATHS,

delete-user-data/functions/src/index.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
*/
1616

1717
import * as admin from "firebase-admin";
18-
import { FieldPath, DocumentReference } from "firebase-admin/firestore";
18+
import {
19+
FieldPath,
20+
DocumentReference,
21+
getFirestore,
22+
} from "firebase-admin/firestore";
1923
import * as functions from "firebase-functions";
2024
import { getDatabaseUrl, hasValidUserPath } from "./helpers";
2125
import chunk from "lodash.chunk";
@@ -40,7 +44,8 @@ admin.initializeApp({
4044
databaseURL,
4145
});
4246

43-
const db = admin.firestore();
47+
// Initialize Firestore with the configured database
48+
const db = getFirestore(config.databaseId);
4449

4550
/** Setup EventArc Channels */
4651
const eventChannel =
@@ -82,7 +87,7 @@ export const handleDeletion = functions.pubsub
8287
continue;
8388
}
8489
if (config.firestoreDeleteMode === "recursive") {
85-
await recursiveDelete(path);
90+
await recursiveDelete(path, db);
8691
} else {
8792
batch.delete(docRef);
8893
}
@@ -127,7 +132,7 @@ export const handleSearch = functions.pubsub
127132
if (depth <= config.searchDepth) {
128133
// If the collection ID is the same as the UID, delete the entire collection and sub-collections
129134
if (collection.id === uid) {
130-
await recursiveDelete(path);
135+
await recursiveDelete(path, db);
131136

132137
if (eventChannel) {
133138
/** Publish event to EventArc */
@@ -151,7 +156,7 @@ export const handleSearch = functions.pubsub
151156
documentReferences.map(async (reference) => {
152157
// Start a sub-collection search on each document.
153158
if (nextDepth <= config.searchDepth) {
154-
await search(uid, nextDepth, reference);
159+
await search(uid, nextDepth, db, reference);
155160
}
156161

157162
// If the ID of the document is the same as the UID, add it to delete list.
@@ -223,7 +228,7 @@ export const clearData = functions.auth.user().onDelete(async (user) => {
223228

224229
/** If search mode enable, run pubsub search fn */
225230
if (enableSearch) {
226-
await search(uid, 1);
231+
await search(uid, 1, db);
227232
}
228233

229234
/** If search function provided, return a list of queries */
@@ -316,19 +321,18 @@ const clearFirestoreData = async (firestorePaths: string, uid: string) => {
316321
const isRecursive = config.firestoreDeleteMode === "recursive";
317322

318323
if (!isRecursive) {
319-
const firestore = admin.firestore();
320324
logs.firestorePathDeleting(path, false);
321325

322326
// Wrapping in transaction to allow for automatic retries (#48)
323-
await firestore.runTransaction((transaction) => {
324-
transaction.delete(firestore.doc(path));
327+
await db.runTransaction((transaction) => {
328+
transaction.delete(db.doc(path));
325329
return Promise.resolve();
326330
});
327331
logs.firestorePathDeleted(path, false);
328332
} else {
329333
logs.firestorePathDeleting(path, true);
330334

331-
await recursiveDelete(path);
335+
await recursiveDelete(path, db);
332336

333337
logs.firestorePathDeleted(path, true);
334338
}

delete-user-data/functions/src/recursiveDelete.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import * as admin from "firebase-admin";
22

33
const MAX_RETRY_ATTEMPTS = 3;
44

5-
export const recursiveDelete = async (path: string) => {
6-
const db = admin.firestore();
5+
export const recursiveDelete = async (
6+
path: string,
7+
db: admin.firestore.Firestore
8+
) => {
79
// Recursively delete a reference and log the references of failures.
810
const bulkWriter = db.bulkWriter();
911

delete-user-data/functions/src/search.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ const { PubSub } = require("@google-cloud/pubsub");
55
export const search = async (
66
uid: string,
77
depth: number,
8+
db: admin.firestore.Firestore,
89
document?: admin.firestore.DocumentReference<admin.firestore.DocumentData>
910
) => {
10-
const db = admin.firestore();
11-
1211
const pubsub = new PubSub();
1312

1413
const topic = pubsub.topic(

0 commit comments

Comments
 (0)