diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/gcpProject.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/gcpProject.test.ts new file mode 100644 index 000000000..01c153378 --- /dev/null +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/gcpProject.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2019 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 { resolveGcpProjectIdForBigQuery } from "../../bigquery/gcpProject"; + +const ENV_VARS = ["PROJECT_ID", "GOOGLE_CLOUD_PROJECT"]; + +function clearEnv() { + for (const v of ENV_VARS) delete process.env[v]; +} + +beforeEach(clearEnv); +afterEach(clearEnv); + +describe("resolveGcpProjectIdForBigQuery", () => { + it("returns the explicit preferred value first", () => { + process.env.GOOGLE_CLOUD_PROJECT = "env-project"; + expect(resolveGcpProjectIdForBigQuery("explicit-project")).toBe( + "explicit-project" + ); + }); + + it("prefers GOOGLE_CLOUD_PROJECT over PROJECT_ID (Gen2 / Cloud Run)", () => { + process.env.GOOGLE_CLOUD_PROJECT = "gen2-project"; + process.env.PROJECT_ID = "gen1-project"; + expect(resolveGcpProjectIdForBigQuery(undefined)).toBe("gen2-project"); + }); + + it("falls back to PROJECT_ID when GOOGLE_CLOUD_PROJECT is absent", () => { + process.env.PROJECT_ID = "gen1-project"; + expect(resolveGcpProjectIdForBigQuery(undefined)).toBe("gen1-project"); + }); + + it("returns undefined when no source is available", () => { + expect(resolveGcpProjectIdForBigQuery(undefined)).toBeUndefined(); + }); +}); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/gcpProject.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/gcpProject.ts new file mode 100644 index 000000000..7c23866ef --- /dev/null +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/gcpProject.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2019 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. + */ + +export function resolveGcpProjectIdForBigQuery( + preferred?: string +): string | undefined { + return ( + preferred || + process.env.GOOGLE_CLOUD_PROJECT || + process.env.PROJECT_ID + ); +} diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/index.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/index.ts index 7b6f8a15e..ef9627f54 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/index.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/index.ts @@ -42,6 +42,7 @@ import { tableRequiresUpdate } from "./checkUpdates"; import { parseErrorMessage, waitForInitialization } from "./utils"; import { initializeLatestView } from "./initializeLatestView"; import { logger, LogLevel } from "../logger"; +import { resolveGcpProjectIdForBigQuery } from "./gcpProject"; export { RawChangelogSchema, RawChangelogViewSchema } from "./schema"; import type { ChangeTrackerConfig } from "./types"; @@ -68,7 +69,7 @@ export class FirestoreBigQueryEventHistoryTracker constructor(public config: ChangeTrackerConfig) { this.bq = new bigquery.BigQuery(); - this.bq.projectId = config.bqProjectId || process.env.PROJECT_ID; + this.bq.projectId = resolveGcpProjectIdForBigQuery(config.bqProjectId); this.partitioningConfig = new PartitioningConfig(this.config.partitioning); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/snapshot.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/snapshot.ts index 01d2bee62..7e5fd69e0 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/snapshot.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/snapshot.ts @@ -16,6 +16,7 @@ import * as sqlFormatter from "sql-formatter"; import { timestampField } from "./schema"; +import { resolveGcpProjectIdForBigQuery } from "./gcpProject"; const excludeFields: string[] = ["document_name", "document_id"]; const nonGroupFields = ["event_id", "data", "old_data"]; @@ -65,7 +66,12 @@ export function buildLatestSnapshotViewQuery({ }: BuildLatestSnapshotViewQueryOptions): string { validateInputs({ datasetId, tableName, timestampColumnName, groupByColumns }); - const projectId = bqProjectId || process.env.PROJECT_ID; + const projectId = resolveGcpProjectIdForBigQuery(bqProjectId); + if (!projectId) { + throw new Error( + "Could not determine BigQuery project ID. Provide it via the bqProjectId config option or set the GOOGLE_CLOUD_PROJECT / PROJECT_ID environment variable." + ); + } return useLegacyQuery ? buildLegacyQuery(