diff --git a/_emulator/extensions/delete-user-data.env.local b/_emulator/extensions/delete-user-data.env.local index c56b0354bb..67801e550e 100644 --- a/_emulator/extensions/delete-user-data.env.local +++ b/_emulator/extensions/delete-user-data.env.local @@ -1,7 +1,7 @@ LOCATION=europe-west2 FIRESTORE_QUERY_COLLECTION=queries -ENABLE_AUTO_DISCOVERY=true -AUTO_DISCOVERY_SEARCH_FIELDS=field1,field2 +ENABLE_AUTO_DISCOVERY=yes +AUTO_DISCOVERY_SEARCH_FIELDS=field1,field2,scope.owner.id SEARCH_FUNCTION=http://127.0.0.1:5001/demo-test/us-central1/findDocumentReferences AUTO_DISCOVERY_TOPIC=discovery AUTO_DISCOVERY_SEARCH_DEPTH=4 diff --git a/delete-user-data/functions/__tests__/search.test.ts b/delete-user-data/functions/__tests__/search.test.ts index 161dd8167a..9142175f6b 100644 --- a/delete-user-data/functions/__tests__/search.test.ts +++ b/delete-user-data/functions/__tests__/search.test.ts @@ -67,6 +67,27 @@ describe("discovery", () => { await waitForDocumentDeletion(document, 60000); }, 60000); + test("can delete a document with a nested field value matching {uid}", async () => { + const document = await db + .collection(generateRandomId()) + .add({ scope: { owner: { id: user.uid } } }); + await search(user.uid, 1, db); + + await waitForDocumentDeletion(document, 60000); + }, 60000); + + test("cannot delete a document with a nested field value that does not match {uid}", async () => { + const document = await db + .collection(generateRandomId()) + .add({ scope: { owner: { id: "not-the-uid" } } }); + await search(user.uid, 1, db); + + await new Promise((resolve) => setTimeout(resolve, 10000)); + + const checkExists = await document.get().then((doc) => doc.exists); + expect(checkExists).toBe(true); + }, 60000); + test("can check a document without any field values", async () => { await db.collection(generateRandomId()).add({}); await search(user.uid, 1, db); diff --git a/delete-user-data/functions/src/helpers.ts b/delete-user-data/functions/src/helpers.ts index e5f64eb115..a47df725c1 100644 --- a/delete-user-data/functions/src/helpers.ts +++ b/delete-user-data/functions/src/helpers.ts @@ -14,9 +14,18 @@ * limitations under the License. */ -import { DocumentReference, FieldPath } from "firebase-admin/firestore"; +import { + DocumentReference, + DocumentSnapshot, + FieldPath, +} from "firebase-admin/firestore"; import config from "./config"; +export const getNestedFieldValue = ( + snapshot: DocumentSnapshot, + field: string +) => snapshot.get(new FieldPath(...field.trim().split("."))); + export const getDatabaseUrl = ( selectedDatabaseInstance: string | undefined, selectedDatabaseLocation: string | undefined @@ -42,7 +51,7 @@ export const hasValidUserPath = async ( if (snapshot.exists) { for (const field of config.searchFields.split(",")) { - const fieldValue = snapshot.get(new FieldPath(field)); + const fieldValue = getNestedFieldValue(snapshot, field); /** Return if a matching string includes the id */ if (typeof fieldValue === "string" && fieldValue.includes(uid)) { diff --git a/delete-user-data/functions/src/index.ts b/delete-user-data/functions/src/index.ts index 5d1f9c985d..f81f3beb45 100644 --- a/delete-user-data/functions/src/index.ts +++ b/delete-user-data/functions/src/index.ts @@ -15,13 +15,13 @@ */ import * as admin from "firebase-admin"; -import { - FieldPath, - DocumentReference, - getFirestore, -} from "firebase-admin/firestore"; +import { DocumentReference, getFirestore } from "firebase-admin/firestore"; import * as functions from "firebase-functions"; -import { getDatabaseUrl, hasValidUserPath } from "./helpers"; +import { + getDatabaseUrl, + getNestedFieldValue, + hasValidUserPath, +} from "./helpers"; import chunk from "lodash.chunk"; import { getEventarc } from "firebase-admin/eventarc"; @@ -177,7 +177,10 @@ export const handleSearch = functions.pubsub for (const snapshot of snapshots) { if (snapshot.exists) { for (const field of config.searchFields.split(",")) { - if (snapshot.get(new FieldPath(field)) === uid) { + if ( + field.trim() && + getNestedFieldValue(snapshot, field) === uid + ) { pathsToDelete.push(snapshot.ref.path); continue; }