@firebase-function-kits/bigquery-firestore-export 0.0.1 → 0.0.2-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +188 -0
  3. package/lib/config.d.ts +5 -0
  4. package/lib/config.d.ts.map +1 -0
  5. package/lib/config.js +75 -0
  6. package/lib/config.js.map +1 -0
  7. package/lib/dts.d.ts +14 -0
  8. package/lib/dts.d.ts.map +1 -0
  9. package/lib/dts.js +189 -0
  10. package/lib/dts.js.map +1 -0
  11. package/lib/export-config.d.ts +58 -0
  12. package/lib/export-config.d.ts.map +1 -0
  13. package/lib/export-config.js +56 -0
  14. package/lib/export-config.js.map +1 -0
  15. package/lib/handlers.d.ts +22 -0
  16. package/lib/handlers.d.ts.map +1 -0
  17. package/lib/handlers.js +125 -0
  18. package/lib/handlers.js.map +1 -0
  19. package/lib/helper.d.ts +37 -0
  20. package/lib/helper.d.ts.map +1 -0
  21. package/lib/helper.js +219 -0
  22. package/lib/helper.js.map +1 -0
  23. package/lib/index.d.ts +7 -0
  24. package/lib/index.d.ts.map +1 -0
  25. package/lib/index.js +137 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/lib.d.ts +11 -0
  28. package/lib/lib.d.ts.map +1 -0
  29. package/lib/lib.js +47 -0
  30. package/lib/lib.js.map +1 -0
  31. package/lib/logs.d.ts +21 -0
  32. package/lib/logs.d.ts.map +1 -0
  33. package/lib/logs.js +122 -0
  34. package/lib/logs.js.map +1 -0
  35. package/lib/metadata.d.ts +6 -0
  36. package/lib/metadata.d.ts.map +1 -0
  37. package/lib/metadata.js +34 -0
  38. package/lib/metadata.js.map +1 -0
  39. package/lib/types.d.ts +44 -0
  40. package/lib/types.d.ts.map +1 -0
  41. package/lib/types.js +18 -0
  42. package/lib/types.js.map +1 -0
  43. package/package.json +40 -4
  44. package/src/config.ts +88 -0
  45. package/src/dts.ts +214 -0
  46. package/src/export-config.ts +123 -0
  47. package/src/handlers.ts +141 -0
  48. package/src/helper.ts +320 -0
  49. package/src/index.ts +126 -0
  50. package/src/lib.ts +69 -0
  51. package/src/logs.ts +145 -0
  52. package/src/metadata.ts +31 -0
  53. package/src/types.ts +100 -0
  54. package/tests/config.test.ts +61 -0
  55. package/tests/dts.test.ts +124 -0
  56. package/tests/export-config.test.ts +78 -0
  57. package/tests/handlers.test.ts +170 -0
  58. package/tests/helper.test.ts +74 -0
  59. package/tests/lib.test.ts +43 -0
  60. package/tsconfig.json +18 -0
  61. package/tsconfig.tsbuildinfo +1 -0
  62. package/index.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,iBAAiB,CAAC;AAG7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,MAAM,gBAAgB,GAAG,UAAU,CACvC,oBAAoB,CAAC,kBAAkB,CAAC,CACzC,CAAC;AAEF,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,kBAAkB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,qCAAqC,CAAC;CAC/C;AAqCD,sEAAsE;AACtE,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,gBAAgB,EACvB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.handleMessagePublished = handleMessagePublished;
52
+ exports.handleUpsertTransferConfig = handleUpsertTransferConfig;
53
+ const dts_1 = require("./dts");
54
+ const helper_1 = require("./helper");
55
+ const logs = __importStar(require("./logs"));
56
+ async function ensureNotificationTopic(ctx) {
57
+ const topic = ctx.pubsub.topic(ctx.config.pubSubTopic);
58
+ const [exists] = await topic.exists();
59
+ if (exists)
60
+ return;
61
+ try {
62
+ await ctx.pubsub.createTopic(ctx.config.pubSubTopic);
63
+ logs.topicCreated(ctx.config.pubSubTopic);
64
+ }
65
+ catch (err) {
66
+ // A concurrent init can create the same topic between exists() and create().
67
+ if (typeof err !== "object" ||
68
+ err === null ||
69
+ !("code" in err) ||
70
+ err.code !== 6) {
71
+ throw err;
72
+ }
73
+ }
74
+ }
75
+ async function storeTransferConfig(ctx, transferConfig) {
76
+ if (!transferConfig?.name) {
77
+ throw new Error("BigQuery transfer config is missing its resource name");
78
+ }
79
+ const { transferConfigId } = (0, helper_1.parseTransferConfigName)(transferConfig.name);
80
+ await ctx.db
81
+ .collection(ctx.config.firestoreCollection)
82
+ .doc(transferConfigId)
83
+ .set({ extInstanceId: ctx.config.instanceId, ...transferConfig });
84
+ }
85
+ /** Handles a v2 Pub/Sub completion notification from BigQuery DTS. */
86
+ async function handleMessagePublished(event, ctx) {
87
+ logs.start();
88
+ try {
89
+ await (0, helper_1.handleTransferRunMessage)(ctx, { json: event.data.message.json });
90
+ logs.complete();
91
+ }
92
+ catch (err) {
93
+ logs.error(err);
94
+ throw err;
95
+ }
96
+ }
97
+ /** Idempotently creates, links, or updates this deployment's DTS config. */
98
+ async function handleUpsertTransferConfig(ctx) {
99
+ await ensureNotificationTopic(ctx);
100
+ if (ctx.config.transferConfigName) {
101
+ const linked = await (0, dts_1.getTransferConfig)(ctx.dataTransfer, ctx.config.transferConfigName);
102
+ if (!linked) {
103
+ throw new Error(`Transfer config not found: ${ctx.config.transferConfigName}`);
104
+ }
105
+ await storeTransferConfig(ctx, linked);
106
+ return;
107
+ }
108
+ const existing = await ctx.db
109
+ .collection(ctx.config.firestoreCollection)
110
+ .where("extInstanceId", "==", ctx.config.instanceId)
111
+ .limit(1)
112
+ .get();
113
+ if (existing.empty) {
114
+ const created = await (0, dts_1.createTransferConfig)(ctx.dataTransfer, ctx.config);
115
+ await storeTransferConfig(ctx, created);
116
+ return;
117
+ }
118
+ const transferConfigName = existing.docs[0].data().name;
119
+ if (typeof transferConfigName !== "string" || !transferConfigName) {
120
+ throw new Error(`Existing transfer config document in ${ctx.config.firestoreCollection} is missing required 'name' field.`);
121
+ }
122
+ const updated = await (0, dts_1.updateTransferConfig)(ctx.dataTransfer, transferConfigName, ctx.config);
123
+ await storeTransferConfig(ctx, updated);
124
+ }
125
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type BigQuery } from "@google-cloud/bigquery";
2
+ import { type Firestore } from "firebase-admin/firestore";
3
+ import type { ResolvedBigqueryFirestoreExportConfig } from "./export-config";
4
+ import type { BigQueryRow, BigQueryRowValue, FirestoreRow, FirestoreRowValue, TransferRunMessage } from "./types";
5
+ export interface ParsedTransferRunName {
6
+ projectId: string;
7
+ location: string;
8
+ transferConfigId: string;
9
+ runId: string;
10
+ }
11
+ export interface ParsedTransferConfigName {
12
+ projectId: string;
13
+ location: string;
14
+ transferConfigId: string;
15
+ }
16
+ export interface ResultHandlerContext {
17
+ db: Firestore;
18
+ bigquery: BigQuery;
19
+ config: ResolvedBigqueryFirestoreExportConfig;
20
+ }
21
+ export declare function parseTransferRunName(name: string): ParsedTransferRunName;
22
+ export declare function parseTransferConfigName(name: string): ParsedTransferConfigName;
23
+ export declare function updateLatestRunDocument(db: Firestore, config: ResolvedBigqueryFirestoreExportConfig, transferConfigId: string, runId: string, message: TransferRunMessage, rowCounts: {
24
+ failedRowCount: number;
25
+ totalRowCount: number;
26
+ }): Promise<void>;
27
+ export declare function getBigqueryResults(bigquery: BigQuery, config: ResolvedBigqueryFirestoreExportConfig, transferConfigId: string, runId: string, datasetId: string, tableName: string): Promise<BigQueryRow[]>;
28
+ export declare function convertUnsupportedDataTypes(row: null): null;
29
+ export declare function convertUnsupportedDataTypes(row: string): string;
30
+ export declare function convertUnsupportedDataTypes(row: number): number;
31
+ export declare function convertUnsupportedDataTypes(row: boolean): boolean;
32
+ export declare function convertUnsupportedDataTypes(row: BigQueryRow): FirestoreRow;
33
+ export declare function convertUnsupportedDataTypes(row: BigQueryRowValue): FirestoreRowValue;
34
+ export declare function writeRunResultsToFirestore(ctx: ResultHandlerContext, message: TransferRunMessage): Promise<void>;
35
+ export declare function transferConfigAssociatedWithInstance(db: Firestore, config: ResolvedBigqueryFirestoreExportConfig, transferConfigId: string): Promise<boolean>;
36
+ export declare function handleTransferRunMessage(ctx: ResultHandlerContext, message: TransferRunMessage): Promise<void>;
37
+ //# sourceMappingURL=helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,KAAK,QAAQ,EAMd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,qCAAqC,CAAC;CAC/C;AAQD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAcxE;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,GACX,wBAAwB,CAa1B;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,SAAS,EACb,MAAM,EAAE,qCAAqC,EAC7C,gBAAgB,EAAE,MAAM,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC3D,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,qCAAqC,EAC7C,gBAAgB,EAAE,MAAM,EACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgBxB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;AAC7D,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;AACjE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;AACjE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;AACnE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,CAAC;AAC5E,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,gBAAgB,GACpB,iBAAiB,CAAC;AA+BrB,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,oBAAoB,EACzB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA+Df;AAED,wBAAsB,oCAAoC,CACxD,EAAE,EAAE,SAAS,EACb,MAAM,EAAE,qCAAqC,EAC7C,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,oBAAoB,EACzB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiCf"}
package/lib/helper.js ADDED
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2025 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.parseTransferRunName = parseTransferRunName;
52
+ exports.parseTransferConfigName = parseTransferConfigName;
53
+ exports.updateLatestRunDocument = updateLatestRunDocument;
54
+ exports.getBigqueryResults = getBigqueryResults;
55
+ exports.convertUnsupportedDataTypes = convertUnsupportedDataTypes;
56
+ exports.writeRunResultsToFirestore = writeRunResultsToFirestore;
57
+ exports.transferConfigAssociatedWithInstance = transferConfigAssociatedWithInstance;
58
+ exports.handleTransferRunMessage = handleTransferRunMessage;
59
+ const bigquery_1 = require("@google-cloud/bigquery");
60
+ const firestore_1 = require("firebase-admin/firestore");
61
+ const logs = __importStar(require("./logs"));
62
+ const TRANSFER_RUN_NAME_REGEX = /^projects\/([^/]+)\/locations\/([^/]+)\/transferConfigs\/([^/]+)\/runs\/([^/]+)$/;
63
+ const TRANSFER_CONFIG_NAME_REGEX = /^projects\/([^/]+)\/locations\/([^/]+)\/transferConfigs\/([^/]+)$/;
64
+ const FIRESTORE_WRITE_CHUNK_SIZE = 10_000;
65
+ function parseTransferRunName(name) {
66
+ const match = name.match(TRANSFER_RUN_NAME_REGEX);
67
+ if (!match) {
68
+ throw new Error(`Invalid transfer run name format: "${name}". Expected format: projects/{projectId}/locations/{location}/transferConfigs/{configId}/runs/{runId}`);
69
+ }
70
+ return {
71
+ projectId: match[1],
72
+ location: match[2],
73
+ transferConfigId: match[3],
74
+ runId: match[4],
75
+ };
76
+ }
77
+ function parseTransferConfigName(name) {
78
+ const match = name.match(TRANSFER_CONFIG_NAME_REGEX);
79
+ if (!match) {
80
+ throw new Error(`Invalid transfer config name format: "${name}". Expected format: projects/{projectId}/locations/{location}/transferConfigs/{configId}`);
81
+ }
82
+ return {
83
+ projectId: match[1],
84
+ location: match[2],
85
+ transferConfigId: match[3],
86
+ };
87
+ }
88
+ async function updateLatestRunDocument(db, config, transferConfigId, runId, message, rowCounts) {
89
+ const latestRef = db
90
+ .collection(`${config.firestoreCollection}/${transferConfigId}/runs`)
91
+ .doc("latest");
92
+ const runTime = new Date(message.json.runTime);
93
+ const docUpdate = {
94
+ runMetadata: message.json,
95
+ latestRunId: runId,
96
+ ...rowCounts,
97
+ };
98
+ await db.runTransaction(async (transaction) => {
99
+ const latest = await transaction.get(latestRef);
100
+ const latestData = latest.data();
101
+ const existingRunTime = latestData?.runMetadata?.runTime;
102
+ const existingRunId = latestData?.latestRunId;
103
+ const shouldUpdate = !latestData ||
104
+ !existingRunTime ||
105
+ new Date(existingRunTime) < runTime ||
106
+ existingRunId === runId;
107
+ if (shouldUpdate) {
108
+ transaction.set(latestRef, docUpdate);
109
+ return;
110
+ }
111
+ logs.latestDocUpdateSkipped(transferConfigId, runId, `existing run is newer (${existingRunTime} >= ${message.json.runTime})`);
112
+ });
113
+ }
114
+ async function getBigqueryResults(bigquery, config, transferConfigId, runId, datasetId, tableName) {
115
+ const query = `SELECT * FROM \`${config.projectId}.${datasetId}.${tableName}\``;
116
+ try {
117
+ const [job] = await bigquery.createQueryJob({
118
+ query,
119
+ location: config.bigqueryDatasetLocation,
120
+ });
121
+ logs.bigqueryJobStarted(job.id);
122
+ const [rows] = await job.getQueryResults();
123
+ logs.bigqueryResultsRowCount(transferConfigId, runId, rows.length);
124
+ return rows;
125
+ }
126
+ catch (err) {
127
+ logs.bigqueryQueryFailed(transferConfigId, runId, tableName, err);
128
+ throw err;
129
+ }
130
+ }
131
+ function convertUnsupportedDataTypes(row) {
132
+ if (row === null || typeof row !== "object") {
133
+ return row;
134
+ }
135
+ if (row instanceof bigquery_1.BigQueryTimestamp ||
136
+ row instanceof bigquery_1.BigQueryDate ||
137
+ row instanceof bigquery_1.BigQueryTime ||
138
+ row instanceof bigquery_1.BigQueryDatetime) {
139
+ return firestore_1.Timestamp.fromDate(new Date(row.value));
140
+ }
141
+ if (row instanceof Date)
142
+ return firestore_1.Timestamp.fromDate(row);
143
+ if (row instanceof Buffer)
144
+ return new Uint8Array(row);
145
+ if (row instanceof bigquery_1.Geography)
146
+ return row.value;
147
+ if (Array.isArray(row)) {
148
+ return row.map((value) => convertUnsupportedDataTypes(value));
149
+ }
150
+ return Object.fromEntries(Object.entries(row).map(([key, value]) => [
151
+ key,
152
+ convertUnsupportedDataTypes(value),
153
+ ]));
154
+ }
155
+ async function writeRunResultsToFirestore(ctx, message) {
156
+ const { db, bigquery, config } = ctx;
157
+ const { transferConfigId, runId } = parseTransferRunName(message.json.name);
158
+ const runTime = new Date(message.json.runTime);
159
+ const runTimeSuffix = [
160
+ runTime.getUTCHours(),
161
+ runTime.getUTCMinutes(),
162
+ runTime.getUTCSeconds(),
163
+ ]
164
+ .map((part) => String(part).padStart(2, "0"))
165
+ .join("");
166
+ const tableName = message.json.params.destination_table_name_template.replace('{run_time|"%H%M%S"}', runTimeSuffix);
167
+ const rows = await getBigqueryResults(bigquery, config, transferConfigId, runId, message.json.destinationDatasetId, tableName);
168
+ const collection = db.collection(`${config.firestoreCollection}/${transferConfigId}/runs/${runId}/output`);
169
+ let succeededRowCount = 0;
170
+ for (let i = 0; i < rows.length; i += FIRESTORE_WRITE_CHUNK_SIZE) {
171
+ const writes = [];
172
+ for (let j = i; j < i + FIRESTORE_WRITE_CHUNK_SIZE && j < rows.length; j++) {
173
+ writes.push(collection.add(convertUnsupportedDataTypes(rows[j])));
174
+ }
175
+ const results = await Promise.allSettled(writes);
176
+ for (const result of results) {
177
+ if (result.status === "fulfilled")
178
+ succeededRowCount++;
179
+ else
180
+ logs.errorWritingToFirestore(result.reason);
181
+ }
182
+ }
183
+ const rowCounts = {
184
+ failedRowCount: rows.length - succeededRowCount,
185
+ totalRowCount: rows.length,
186
+ };
187
+ logs.runResultsWrittenToFirestore(runId, succeededRowCount, rows.length);
188
+ await db
189
+ .collection(`${config.firestoreCollection}/${transferConfigId}/runs`)
190
+ .doc(runId)
191
+ .set({ runMetadata: message.json, ...rowCounts });
192
+ await updateLatestRunDocument(db, config, transferConfigId, runId, message, rowCounts);
193
+ }
194
+ async function transferConfigAssociatedWithInstance(db, config, transferConfigId) {
195
+ const results = await db
196
+ .collection(config.firestoreCollection)
197
+ .where("extInstanceId", "==", config.instanceId)
198
+ .get();
199
+ return results.docs.some((doc) => doc.id === transferConfigId);
200
+ }
201
+ async function handleTransferRunMessage(ctx, message) {
202
+ const { transferConfigId, runId } = parseTransferRunName(message.json.name);
203
+ const associated = await transferConfigAssociatedWithInstance(ctx.db, ctx.config, transferConfigId);
204
+ if (!associated) {
205
+ throw new Error(`Skipping handling pubsub message because transferConfig '${transferConfigId}' is not associated with extension instance '${ctx.config.instanceId}'.`);
206
+ }
207
+ if (message.json.state === "SUCCEEDED") {
208
+ await writeRunResultsToFirestore(ctx, message);
209
+ return;
210
+ }
211
+ logs.handlingNonSuccessRun(transferConfigId, runId, message.json.state);
212
+ const rowCounts = { failedRowCount: 0, totalRowCount: 0 };
213
+ await ctx.db
214
+ .collection(`${ctx.config.firestoreCollection}/${transferConfigId}/runs`)
215
+ .doc(runId)
216
+ .set({ runMetadata: message.json, ...rowCounts });
217
+ await updateLatestRunDocument(ctx.db, ctx.config, transferConfigId, runId, message, rowCounts);
218
+ }
219
+ //# sourceMappingURL=helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { TransferRunPayload } from "./types";
2
+ export * from "./lib";
3
+ /** Consumes BigQuery Data Transfer completion notifications. */
4
+ export declare const processMessages: import("firebase-functions/core").CloudFunction<import("firebase-functions/core").CloudEvent<import("firebase-functions/v2/pubsub").MessagePublishedData<TransferRunPayload>>>;
5
+ /** Creates, links, or reconciles this deployment's scheduled query. */
6
+ export declare const upsertTransferConfig: import("firebase-functions/v2/tasks").TaskQueueFunction<any>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,cAAc,OAAO,CAAC;AA+DtB,gEAAgE;AAChE,eAAO,MAAM,eAAe,gLAM3B,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,8DAMhC,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
51
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.upsertTransferConfig = exports.processMessages = void 0;
55
+ /**
56
+ * Main entry point. Exports the wired Pub/Sub and lifecycle task functions,
57
+ * while resolving concrete config and clients lazily at runtime. Import from
58
+ * `./lib` for the side-effect-free handlers, helpers, and config types.
59
+ */
60
+ const bigquery_1 = require("@google-cloud/bigquery");
61
+ const bigquery_data_transfer_1 = require("@google-cloud/bigquery-data-transfer");
62
+ const pubsub_1 = require("@google-cloud/pubsub");
63
+ const app_1 = require("firebase-admin/app");
64
+ const firestore_1 = require("firebase-admin/firestore");
65
+ const pubsub_2 = require("firebase-functions/v2/pubsub");
66
+ const tasks_1 = require("firebase-functions/v2/tasks");
67
+ const v2_1 = require("firebase-functions/v2");
68
+ const lifecycle_1 = require("firebase-functions/v2/lifecycle");
69
+ const config_1 = require("./config");
70
+ const export_config_1 = require("./export-config");
71
+ const handlers_1 = require("./handlers");
72
+ const logs = __importStar(require("./logs"));
73
+ __exportStar(require("./lib"), exports);
74
+ const UPSERT_FUNCTION = "upsertTransferConfig";
75
+ const REQUIRED_ROLES = [
76
+ "roles/datastore.user",
77
+ "roles/bigquery.admin",
78
+ "roles/pubsub.admin",
79
+ "roles/eventarc.eventReceiver",
80
+ "roles/run.invoker",
81
+ ];
82
+ const REQUIRED_APIS = [
83
+ {
84
+ api: "bigquery.googleapis.com",
85
+ reason: "Runs scheduled queries and reads their destination tables.",
86
+ },
87
+ {
88
+ api: "bigquerydatatransfer.googleapis.com",
89
+ reason: "Creates and reconciles the scheduled-query transfer config.",
90
+ },
91
+ {
92
+ api: "pubsub.googleapis.com",
93
+ reason: "Delivers BigQuery transfer-completion notifications.",
94
+ },
95
+ ];
96
+ for (const role of REQUIRED_ROLES) {
97
+ (0, v2_1.requiresRole)(role);
98
+ }
99
+ for (const { api, reason } of REQUIRED_APIS) {
100
+ (0, v2_1.requiresAPI)(api, reason);
101
+ }
102
+ (0, lifecycle_1.afterFirstDeploy)({
103
+ task: { function: UPSERT_FUNCTION, body: { data: {} } },
104
+ });
105
+ (0, lifecycle_1.afterRedeploy)({
106
+ task: { function: UPSERT_FUNCTION, body: { data: {} } },
107
+ });
108
+ let ctx;
109
+ function getContext() {
110
+ if (ctx)
111
+ return ctx;
112
+ const config = (0, export_config_1.resolveConfig)((0, config_1.configFromEnv)());
113
+ if ((0, app_1.getApps)().length === 0)
114
+ (0, app_1.initializeApp)({ projectId: config.projectId });
115
+ logs.init(config);
116
+ ctx = {
117
+ db: (0, firestore_1.getFirestore)(),
118
+ bigquery: new bigquery_1.BigQuery({ projectId: config.projectId }),
119
+ dataTransfer: new bigquery_data_transfer_1.v1.DataTransferServiceClient({
120
+ projectId: config.projectId,
121
+ }),
122
+ pubsub: new pubsub_1.PubSub({ projectId: config.projectId }),
123
+ config,
124
+ };
125
+ return ctx;
126
+ }
127
+ /** Consumes BigQuery Data Transfer completion notifications. */
128
+ exports.processMessages = (0, pubsub_2.onMessagePublished)({
129
+ topic: config_1.CONFIG_EXPRESSIONS.pubSubTopic,
130
+ retry: true,
131
+ }, (event) => (0, handlers_1.handleMessagePublished)(event, getContext()));
132
+ /** Creates, links, or reconciles this deployment's scheduled query. */
133
+ exports.upsertTransferConfig = (0, tasks_1.onTaskDispatched)({
134
+ memory: "1GiB",
135
+ retryConfig: { maxAttempts: 5, minBackoffSeconds: 30 },
136
+ }, () => (0, handlers_1.handleUpsertTransferConfig)(getContext()));
137
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/lib.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Side-effect-free library surface. Importing this module registers no
3
+ * functions, lifecycle hooks, roles, or APIs and reads no environment params.
4
+ */
5
+ export { constructUpdateTransferConfigRequest, createTransferConfig, createTransferConfigRequest, type DataTransferClient, getTransferConfig, PARTITIONING_FIELD_REMOVAL_ERROR, PARTITIONING_FIELD_REMOVAL_ERROR_PREFIX, type TransferConfig, updateTransferConfig, } from "./dts";
6
+ export { type BigqueryFirestoreExportConfig, type DeployTimeOptions, type LogLevel, type ResolvedBigqueryFirestoreExportConfig, resolveConfig, } from "./export-config";
7
+ export { type HandlerContext, handleMessagePublished, handleUpsertTransferConfig, type TransferRunEvent, } from "./handlers";
8
+ export { convertUnsupportedDataTypes, getBigqueryResults, handleTransferRunMessage, type ParsedTransferConfigName, type ParsedTransferRunName, parseTransferConfigName, parseTransferRunName, type ResultHandlerContext, transferConfigAssociatedWithInstance, updateLatestRunDocument, writeRunResultsToFirestore, } from "./helper";
9
+ export { metadata } from "./metadata";
10
+ export type { BigQueryRow, BigQueryRowValue, FirestoreRow, FirestoreRowValue, TransferRunMessage, TransferRunParams, TransferRunPayload, TransferRunState, } from "./types";
11
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,EACL,oCAAoC,EACpC,oBAAoB,EACpB,2BAA2B,EAC3B,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,gCAAgC,EAChC,uCAAuC,EACvC,KAAK,cAAc,EACnB,oBAAoB,GACrB,MAAM,OAAO,CAAC;AACf,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,qCAAqC,EAC1C,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,cAAc,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,oCAAoC,EACpC,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
package/lib/lib.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.metadata = exports.writeRunResultsToFirestore = exports.updateLatestRunDocument = exports.transferConfigAssociatedWithInstance = exports.parseTransferRunName = exports.parseTransferConfigName = exports.handleTransferRunMessage = exports.getBigqueryResults = exports.convertUnsupportedDataTypes = exports.handleUpsertTransferConfig = exports.handleMessagePublished = exports.resolveConfig = exports.updateTransferConfig = exports.PARTITIONING_FIELD_REMOVAL_ERROR_PREFIX = exports.PARTITIONING_FIELD_REMOVAL_ERROR = exports.getTransferConfig = exports.createTransferConfigRequest = exports.createTransferConfig = exports.constructUpdateTransferConfigRequest = void 0;
19
+ /**
20
+ * Side-effect-free library surface. Importing this module registers no
21
+ * functions, lifecycle hooks, roles, or APIs and reads no environment params.
22
+ */
23
+ var dts_1 = require("./dts");
24
+ Object.defineProperty(exports, "constructUpdateTransferConfigRequest", { enumerable: true, get: function () { return dts_1.constructUpdateTransferConfigRequest; } });
25
+ Object.defineProperty(exports, "createTransferConfig", { enumerable: true, get: function () { return dts_1.createTransferConfig; } });
26
+ Object.defineProperty(exports, "createTransferConfigRequest", { enumerable: true, get: function () { return dts_1.createTransferConfigRequest; } });
27
+ Object.defineProperty(exports, "getTransferConfig", { enumerable: true, get: function () { return dts_1.getTransferConfig; } });
28
+ Object.defineProperty(exports, "PARTITIONING_FIELD_REMOVAL_ERROR", { enumerable: true, get: function () { return dts_1.PARTITIONING_FIELD_REMOVAL_ERROR; } });
29
+ Object.defineProperty(exports, "PARTITIONING_FIELD_REMOVAL_ERROR_PREFIX", { enumerable: true, get: function () { return dts_1.PARTITIONING_FIELD_REMOVAL_ERROR_PREFIX; } });
30
+ Object.defineProperty(exports, "updateTransferConfig", { enumerable: true, get: function () { return dts_1.updateTransferConfig; } });
31
+ var export_config_1 = require("./export-config");
32
+ Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return export_config_1.resolveConfig; } });
33
+ var handlers_1 = require("./handlers");
34
+ Object.defineProperty(exports, "handleMessagePublished", { enumerable: true, get: function () { return handlers_1.handleMessagePublished; } });
35
+ Object.defineProperty(exports, "handleUpsertTransferConfig", { enumerable: true, get: function () { return handlers_1.handleUpsertTransferConfig; } });
36
+ var helper_1 = require("./helper");
37
+ Object.defineProperty(exports, "convertUnsupportedDataTypes", { enumerable: true, get: function () { return helper_1.convertUnsupportedDataTypes; } });
38
+ Object.defineProperty(exports, "getBigqueryResults", { enumerable: true, get: function () { return helper_1.getBigqueryResults; } });
39
+ Object.defineProperty(exports, "handleTransferRunMessage", { enumerable: true, get: function () { return helper_1.handleTransferRunMessage; } });
40
+ Object.defineProperty(exports, "parseTransferConfigName", { enumerable: true, get: function () { return helper_1.parseTransferConfigName; } });
41
+ Object.defineProperty(exports, "parseTransferRunName", { enumerable: true, get: function () { return helper_1.parseTransferRunName; } });
42
+ Object.defineProperty(exports, "transferConfigAssociatedWithInstance", { enumerable: true, get: function () { return helper_1.transferConfigAssociatedWithInstance; } });
43
+ Object.defineProperty(exports, "updateLatestRunDocument", { enumerable: true, get: function () { return helper_1.updateLatestRunDocument; } });
44
+ Object.defineProperty(exports, "writeRunResultsToFirestore", { enumerable: true, get: function () { return helper_1.writeRunResultsToFirestore; } });
45
+ var metadata_1 = require("./metadata");
46
+ Object.defineProperty(exports, "metadata", { enumerable: true, get: function () { return metadata_1.metadata; } });
47
+ //# sourceMappingURL=lib.js.map
package/lib/lib.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH;;;GAGG;AAEH,6BAUe;AATb,2HAAA,oCAAoC,OAAA;AACpC,2GAAA,oBAAoB,OAAA;AACpB,kHAAA,2BAA2B,OAAA;AAE3B,wGAAA,iBAAiB,OAAA;AACjB,uHAAA,gCAAgC,OAAA;AAChC,8HAAA,uCAAuC,OAAA;AAEvC,2GAAA,oBAAoB,OAAA;AAEtB,iDAMyB;AADvB,8GAAA,aAAa,OAAA;AAEf,uCAKoB;AAHlB,kHAAA,sBAAsB,OAAA;AACtB,sHAAA,0BAA0B,OAAA;AAG5B,mCAYkB;AAXhB,qHAAA,2BAA2B,OAAA;AAC3B,4GAAA,kBAAkB,OAAA;AAClB,kHAAA,wBAAwB,OAAA;AAGxB,iHAAA,uBAAuB,OAAA;AACvB,8GAAA,oBAAoB,OAAA;AAEpB,8HAAA,oCAAoC,OAAA;AACpC,iHAAA,uBAAuB,OAAA;AACvB,oHAAA,0BAA0B,OAAA;AAE5B,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA"}