diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..abfb9b4f12e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds 2nd gen Firebase Data Connect triggers to firebase deploy (#9394). diff --git a/src/deploy/functions/services/dataconnect.spec.ts b/src/deploy/functions/services/dataconnect.spec.ts new file mode 100644 index 00000000000..359efe80cba --- /dev/null +++ b/src/deploy/functions/services/dataconnect.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; +import { Endpoint } from "../backend"; +import * as dataconnect from "./dataconnect"; + +const projectNumber = "123456789"; + +const endpoint: Endpoint = { + id: "endpoint", + region: "us-central1", + project: projectNumber, + eventTrigger: { + retry: false, + eventType: "google.firebase.dataconnect.connector.v1.mutationExecuted", + eventFilters: {}, + eventFilterPathPatterns: {}, + }, + entryPoint: "endpoint", + platform: "gcfv2", + runtime: "nodejs16", +}; + +describe("ensureDatabaseTriggerRegion", () => { + it("should set the trigger location to the function region", async () => { + const ep = { ...endpoint }; + + await dataconnect.ensureDataConnectTriggerRegion(ep); + + expect(ep.eventTrigger.region).to.eq("us-central1"); + }); + + it("should not error if the trigger location is already set correctly", async () => { + const ep = { ...endpoint }; + ep.eventTrigger.region = "us-central1"; + + await dataconnect.ensureDataConnectTriggerRegion(ep); + + expect(ep.eventTrigger.region).to.eq("us-central1"); + }); + + it("should error if the trigger location is set incorrectly", () => { + const ep = { ...endpoint }; + ep.eventTrigger.region = "us-west1"; + + expect(() => dataconnect.ensureDataConnectTriggerRegion(ep)).to.throw( + "The Firebase Data Connect trigger location must match the function region.", + ); + }); +}); diff --git a/src/deploy/functions/services/dataconnect.ts b/src/deploy/functions/services/dataconnect.ts new file mode 100644 index 00000000000..9ef1042798e --- /dev/null +++ b/src/deploy/functions/services/dataconnect.ts @@ -0,0 +1,20 @@ +import * as backend from "../backend"; +import { FirebaseError } from "../../../error"; + +/** + * Sets a Firebase Data Connect event trigger's region to the function region. + * @param endpoint the database endpoint + */ +export function ensureDataConnectTriggerRegion( + endpoint: backend.Endpoint & backend.EventTriggered, +): Promise { + if (!endpoint.eventTrigger.region) { + endpoint.eventTrigger.region = endpoint.region; + } + if (endpoint.eventTrigger.region !== endpoint.region) { + throw new FirebaseError( + "The Firebase Data Connect trigger location must match the function region.", + ); + } + return Promise.resolve(); +} diff --git a/src/deploy/functions/services/index.ts b/src/deploy/functions/services/index.ts index b0f60e3092f..9b2dbbdc4e7 100644 --- a/src/deploy/functions/services/index.ts +++ b/src/deploy/functions/services/index.ts @@ -8,6 +8,7 @@ import { ensureDatabaseTriggerRegion } from "./database"; import { ensureRemoteConfigTriggerRegion } from "./remoteConfig"; import { ensureTestLabTriggerRegion } from "./testLab"; import { ensureFirestoreTriggerRegion } from "./firestore"; +import { ensureDataConnectTriggerRegion } from "./dataconnect"; /** A standard void No Op */ export const noop = (): Promise => Promise.resolve(); @@ -25,7 +26,8 @@ export type Name = | "database" | "remoteconfig" | "testlab" - | "firestore"; + | "firestore" + | "dataconnect"; /** A service interface for the underlying GCP event services */ export interface Service { @@ -130,6 +132,17 @@ const firestoreService: Service = { unregisterTrigger: noop, }; +/** A Firebase Data Connect service object */ +const dataconnectService: Service = { + name: "dataconnect", + api: "firebasedataconnect.googleapis.com", + requiredProjectBindings: noopProjectBindings, + ensureTriggerRegion: ensureDataConnectTriggerRegion, + validateTrigger: noop, + registerTrigger: noop, + unregisterTrigger: noop, +}; + /** Mapping from event type string to service object */ const EVENT_SERVICE_MAPPING: Record = { "google.cloud.pubsub.topic.v1.messagePublished": pubSubService, @@ -156,6 +169,7 @@ const EVENT_SERVICE_MAPPING: Record = { "google.cloud.firestore.document.v1.created.withAuthContext": firestoreService, "google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService, "google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService, + "google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService, }; /** diff --git a/src/functions/events/v2.ts b/src/functions/events/v2.ts index cd897d9a5ee..032d3ad0a17 100644 --- a/src/functions/events/v2.ts +++ b/src/functions/events/v2.ts @@ -33,6 +33,8 @@ export const FIRESTORE_EVENTS = [ export const FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.published"; +export const DATACONNECT_EVENT = "google.firebase.dataconnect.connector.v1.mutationExecuted"; + export type Event = | typeof PUBSUB_PUBLISH_EVENT | (typeof STORAGE_EVENTS)[number] @@ -41,7 +43,8 @@ export type Event = | typeof REMOTE_CONFIG_EVENT | typeof TEST_LAB_EVENT | (typeof FIRESTORE_EVENTS)[number] - | typeof FIREALERTS_EVENT; + | typeof FIREALERTS_EVENT + | typeof DATACONNECT_EVENT; // Why can't auth context be removed? This is map was added to correct a bug where a regex // allowed any non-auth type to be converted to any auth type, but we should follow up for why