From d319d339ba696067c1bc4844bb9db2446d44c49d Mon Sep 17 00:00:00 2001 From: Corie Watson Date: Thu, 3 Sep 2026 12:55:33 +0100 Subject: [PATCH] fix(firestore-bigquery-export): republish legacy event types The extension published every lifecycle event twice, once under firebase.extensions.firestore-counter.v1.* for backwards compatibility. The kit published only the firestore-bigquery-export types, so Eventarc triggers filtering on the legacy types stopped firing on migration. Fixes #3108 --- kits/firestore-bigquery-export/README.md | 30 +++++++++---------- kits/firestore-bigquery-export/src/events.ts | 10 +++++-- .../tests/events.test.ts | 28 ++++++++++++----- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/kits/firestore-bigquery-export/README.md b/kits/firestore-bigquery-export/README.md index 318e996b4e..cc6aeba4b3 100644 --- a/kits/firestore-bigquery-export/README.md +++ b/kits/firestore-bigquery-export/README.md @@ -170,19 +170,27 @@ the instances cannot collide. ## Events -When `EVENTARC_CHANNEL` is configured, the functions publish lifecycle events -under `firebase.extensions.firestore-bigquery-export.v1.*`: `onStart` and -`onError` from the write path, and `onSuccess` from the `syncBigQuery` task -when a buffered write lands (matching the extension, which only emitted -`onSuccess` from its queue handler). +When `EVENTARC_CHANNEL` is configured, the functions publish lifecycle events: +`onStart` and `onError` from the write path, and `onSuccess` from the +`syncBigQuery` task when a buffered write lands (matching the extension, which +only emitted `onSuccess` from its queue handler). + +Each event is published twice, exactly as the extension published it: once +under `firebase.extensions.firestore-bigquery-export.v1.*` and once under +`firebase.extensions.firestore-counter.v1.*`. The `firestore-counter` type is a +historical naming mistake the extension kept for backwards compatibility, and +the kit keeps it for the same reason: triggers listening on it survive the +migration. The two copies carry the same `data` and `subject`, and only differ +by `type`. Write new triggers against the `firestore-bigquery-export` types. Publishing is filtered by `EXT_SELECTED_EVENTS`: the value is split on commas and only exactly matching event types are published, silently. An empty value suppresses every event, and a value carrying only another product's types (the extension offered more than one namespace to tick) publishes nothing. A config exported from the extension brings its `EXT_SELECTED_EVENTS` along, so -check it lists the `firebase.extensions.firestore-bigquery-export.v1.*` types -you expect, `onSuccess` included. +check it lists the types you expect, `onSuccess` included. It gates the legacy +`firestore-counter` copies too, so a trigger on a legacy type only fires when +that legacy type is listed. ## Provisioning @@ -367,14 +375,6 @@ inside that window. That property is gone by design - a row that exhausts the queue without a configured `BACKUP_COLLECTION` is dropped, exactly as in the extension. Set `BACKUP_COLLECTION`. -### Events - -Events are published under `firebase.extensions.firestore-bigquery-export.v1.*` -only. The extension also published a duplicate copy of every event under -`firebase.extensions.firestore-counter.v1.*`, a historical naming mistake kept -for backwards compatibility. If you have Eventarc triggers listening on those -`firestore-counter` types, point them at the `firestore-bigquery-export` types. - ### Wildcard columns include the document ID With `WILDCARD_IDS=true`, the wildcard column now contains a `documentId` key diff --git a/kits/firestore-bigquery-export/src/events.ts b/kits/firestore-bigquery-export/src/events.ts index baa9fc03cb..f03b2a8ebf 100644 --- a/kits/firestore-bigquery-export/src/events.ts +++ b/kits/firestore-bigquery-export/src/events.ts @@ -18,13 +18,17 @@ import * as eventArc from "firebase-admin/eventarc"; const { getEventarc } = eventArc; /** - * Builds the Eventarc event type for this extension. + * Generates both the OLD and NEW event types to maintain backward compatibility. + * + * Old Event Type: firebase.extensions.firestore-counter.v1.{eventName} + * New Event Type: firebase.extensions.firestore-bigquery-export.v1.{eventName} * * @param eventName The name of the event (e.g., "onStart", "onError", etc.) - * @returns The event type string. + * @returns An array containing both the old and new event types */ const getEventTypes = (eventName: string) => [ - `firebase.extensions.firestore-bigquery-export.v1.${eventName}`, + `firebase.extensions.firestore-counter.v1.${eventName}`, // OLD Event Type for backward compatibility + `firebase.extensions.firestore-bigquery-export.v1.${eventName}`, // NEW Event Type following the updated convention ]; let eventChannel: eventArc.Channel | undefined; diff --git a/kits/firestore-bigquery-export/tests/events.test.ts b/kits/firestore-bigquery-export/tests/events.test.ts index 35057e4639..18c77cde88 100644 --- a/kits/firestore-bigquery-export/tests/events.test.ts +++ b/kits/firestore-bigquery-export/tests/events.test.ts @@ -59,14 +59,13 @@ describe("channel configured", () => { setupEventChannel(); }); - test("publishes the firestore-bigquery-export event type only", async () => { + test("publishes both the legacy and the current event type", async () => { await recordStartEvent({ a: 1 }); - expect(publish).toHaveBeenCalledTimes(1); - expect(publish).toHaveBeenCalledWith( - expect.objectContaining({ - type: "firebase.extensions.firestore-bigquery-export.v1.onStart", - }) - ); + expect(publish).toHaveBeenCalledTimes(2); + expect(publish.mock.calls.map((c) => c[0].type)).toEqual([ + "firebase.extensions.firestore-counter.v1.onStart", + "firebase.extensions.firestore-bigquery-export.v1.onStart", + ]); }); test("error / success / completion map to their event types", async () => { @@ -76,9 +75,24 @@ describe("channel configured", () => { const types = publish.mock.calls.map((c) => c[0].type); expect(types).toEqual([ + "firebase.extensions.firestore-counter.v1.onError", "firebase.extensions.firestore-bigquery-export.v1.onError", + "firebase.extensions.firestore-counter.v1.onSuccess", "firebase.extensions.firestore-bigquery-export.v1.onSuccess", + "firebase.extensions.firestore-counter.v1.onCompletion", "firebase.extensions.firestore-bigquery-export.v1.onCompletion", ]); }); + + test("the legacy copy carries the same payload as the current one", async () => { + await recordErrorEvent(new Error("boom"), "doc1"); + + const [legacy, current] = publish.mock.calls.map((c) => c[0]); + expect(legacy.data).toEqual({ message: "boom" }); + expect(legacy.subject).toBe("doc1"); + expect({ ...legacy, type: undefined }).toEqual({ + ...current, + type: undefined, + }); + }); });