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

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 (57) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +303 -0
  3. package/lib/config.d.ts +33 -0
  4. package/lib/config.d.ts.map +1 -0
  5. package/lib/config.js +569 -0
  6. package/lib/config.js.map +1 -0
  7. package/lib/events.d.ts +45 -0
  8. package/lib/events.d.ts.map +1 -0
  9. package/lib/events.js +154 -0
  10. package/lib/events.js.map +1 -0
  11. package/lib/export-config.d.ts +96 -0
  12. package/lib/export-config.d.ts.map +1 -0
  13. package/lib/export-config.js +77 -0
  14. package/lib/export-config.js.map +1 -0
  15. package/lib/handlers.d.ts +29 -0
  16. package/lib/handlers.d.ts.map +1 -0
  17. package/lib/handlers.js +165 -0
  18. package/lib/handlers.js.map +1 -0
  19. package/lib/index.d.ts +23 -0
  20. package/lib/index.d.ts.map +1 -0
  21. package/lib/index.js +179 -0
  22. package/lib/index.js.map +1 -0
  23. package/lib/init.d.ts +16 -0
  24. package/lib/init.d.ts.map +1 -0
  25. package/lib/init.js +44 -0
  26. package/lib/init.js.map +1 -0
  27. package/lib/lib.d.ts +20 -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 +39 -0
  32. package/lib/logs.d.ts.map +1 -0
  33. package/lib/logs.js +186 -0
  34. package/lib/logs.js.map +1 -0
  35. package/lib/util.d.ts +23 -0
  36. package/lib/util.d.ts.map +1 -0
  37. package/lib/util.js +66 -0
  38. package/lib/util.js.map +1 -0
  39. package/npm-shrinkwrap.json +7016 -0
  40. package/package.json +37 -3
  41. package/src/config.ts +696 -0
  42. package/src/events.ts +146 -0
  43. package/src/export-config.ts +205 -0
  44. package/src/handlers.ts +211 -0
  45. package/src/index.ts +174 -0
  46. package/src/init.ts +45 -0
  47. package/src/lib.ts +55 -0
  48. package/src/logs.ts +233 -0
  49. package/src/util.ts +64 -0
  50. package/tests/config.test.ts +182 -0
  51. package/tests/events.test.ts +84 -0
  52. package/tests/export-config.test.ts +114 -0
  53. package/tests/handlers.test.ts +220 -0
  54. package/tests/init.test.ts +76 -0
  55. package/tests/util.test.ts +102 -0
  56. package/tsconfig.json +18 -0
  57. package/tsconfig.tsbuildinfo +1 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ - Initial release of kit, see README for differences between the legacy extension and this kit
package/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # @firebase/firestore-bigquery-export
2
+
3
+ Stream a Cloud Firestore collection to BigQuery. This is the Stream Firestore to
4
+ BigQuery Firebase Extension as an npm package you add to your own Firebase
5
+ Functions codebase and deploy.
6
+
7
+ It listens for document writes on a collection, serializes each change, and
8
+ writes it to a BigQuery changelog table. Failed writes are retried through a
9
+ Firebase Functions runtime retry policy. The functions run in your own Firebase
10
+ project; there is no hosted version, so you deploy them yourself.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install @firebase/firestore-bigquery-export
16
+ ```
17
+
18
+ ## Required IAM
19
+
20
+ Deploy needs these Google Cloud roles and APIs for the function's service
21
+ account. Firebase CLI 15.23.0 or later creates that account, grants the roles
22
+ below, enables the listed APIs, and attaches the account to every function in
23
+ this kit. Do not set a custom runtime service account for this codebase — it
24
+ conflicts with that automatic setup.
25
+
26
+ | Role / API | Why |
27
+ |---|---|
28
+ | `roles/bigquery.dataEditor` | create dataset/table/views; insert rows |
29
+ | `roles/bigquery.user` | run BigQuery jobs and materialized views |
30
+ | `roles/datastore.user` | write failed-row records back to Firestore (only if you configure a backup collection) |
31
+ | `roles/eventarc.eventReceiver` | receive Gen2 Firestore trigger events |
32
+ | `roles/run.invoker` | allow Eventarc to invoke the Gen2 Cloud Run service |
33
+ | `bigquery.googleapis.com` | mirror Firestore collection changes in BigQuery |
34
+
35
+ If the dataset lives in a different project (`BIGQUERY_PROJECT_ID`), grant the
36
+ managed runtime service account the `bigquery.*` roles on that project. For a
37
+ CMEK dataset, also grant the BigQuery service account access to your KMS key.
38
+
39
+ ## Usage
40
+
41
+ Export the three functions from your functions codebase entry:
42
+
43
+ ```ts
44
+ // functions/src/index.ts
45
+ export {
46
+ fsexportbigquery,
47
+ initBigQuerySync,
48
+ setupBigQuerySync,
49
+ } from "@firebase/firestore-bigquery-export";
50
+ ```
51
+
52
+ and configure them with a `.env` (or `.env.<projectId>`):
53
+
54
+ ```sh
55
+ COLLECTION_PATH=users
56
+ DATASET_ID=analytics
57
+ TABLE_ID=users
58
+ DATABASE_REGION=europe-west2
59
+ ```
60
+
61
+ - `fsexportbigquery` is the Firestore trigger.
62
+ - `initBigQuerySync` is the first-deploy provisioning lifecycle task.
63
+ - `setupBigQuerySync` is the reconfigure provisioning lifecycle task.
64
+
65
+ Importing the package without exporting its functions deploys nothing — the CLI
66
+ only deploys what your entry file exports.
67
+
68
+ ## Deploy
69
+
70
+ The package's `firebase.json` declares a `kit` stanza (Firebase CLI 15.25.1 or
71
+ later, behind the `kits` experiment):
72
+
73
+ ```json
74
+ {
75
+ "functions": [
76
+ {
77
+ "source": ".",
78
+ "kit": "firestore-bigquery-export",
79
+ "instances": {
80
+ "default": "."
81
+ }
82
+ }
83
+ ]
84
+ }
85
+ ```
86
+
87
+ `instances` maps each instance id to the directory (relative to
88
+ `firebase.json`) holding that instance's `.env`. The CLI prefixes every
89
+ function and task queue name with `kit-<instance id>-`, so the functions above
90
+ deploy as `kit-default-fsexportbigquery`, `kit-default-initBigQuerySync`, and
91
+ `kit-default-setupBigQuerySync`.
92
+
93
+ ```sh
94
+ firebase experiments:enable kits
95
+ firebase deploy --only functions
96
+ ```
97
+
98
+ Deploy a single instance with `firebase deploy --only functions:<instance id>`.
99
+
100
+ ## Configuration
101
+
102
+ Set these values in a `.env` (or `.env.<projectId>`) file. The Firebase CLI
103
+ loads them at deploy time and prompts for any required values that are missing.
104
+ `PROJECT_ID` is supplied by the Firebase CLI.
105
+
106
+ | Field | Env var | Required | Default | Description |
107
+ |---|---|---|---|---|
108
+ | `collectionPath` | `COLLECTION_PATH` | no | `posts` | Collection or collection-group path |
109
+ | `datasetId` | `DATASET_ID` | no | `firestore_export` | BigQuery dataset |
110
+ | `tableId` | `TABLE_ID` | no | `posts` | BigQuery changelog table |
111
+ | `databaseRegion` | `DATABASE_REGION` | yes | — | Region for the trigger and queues |
112
+ | `datasetLocation` | `DATASET_LOCATION` | no | `us` | BigQuery dataset location |
113
+ | `database` | `DATABASE` | no | `(default)` | Firestore database id |
114
+ | `bigqueryProjectId` | `BIGQUERY_PROJECT_ID` | no | project id | Dataset project, if different |
115
+ | `backupCollection` | `BACKUP_COLLECTION` | no | (empty) | Firestore collection for failed rows |
116
+ | `transformFunction` | `TRANSFORM_FUNCTION` | no | (empty) | Optional transform Cloud Function |
117
+ | `tablePartitioning` | `TABLE_PARTITIONING` | no | `NONE` | Table partitioning strategy |
118
+ | `timePartitioningField` | `TIME_PARTITIONING_FIELD` | no | (empty) | Time-partitioning column name |
119
+ | `timePartitioningFieldType` | `TIME_PARTITIONING_FIELD_TYPE` | no | `omit` | Time-partitioning field type |
120
+ | `timePartitioningFirestoreField` | `TIME_PARTITIONING_FIRESTORE_FIELD` | no | (empty) | Firestore field for partitioning |
121
+ | `clustering` | `CLUSTERING` | no | (empty) | Clustering columns (max 4) |
122
+ | `wildcardIds` | `WILDCARD_IDS` | no | `false` | Store path-param values as columns |
123
+ | `useNewSnapshotQuerySyntax` | `USE_NEW_SNAPSHOT_QUERY_SYNTAX` | no | `false` | Use newer snapshot query syntax |
124
+ | `excludeOldData` | `EXCLUDE_OLD_DATA` | no | `false` | Skip previous document state on updates |
125
+ | `viewType` | `VIEW_TYPE` | no | `view` | `view`, `materialized_incremental`, `materialized_non_incremental` |
126
+ | `maxStaleness` | `MAX_STALENESS` | no | (empty) | Materialized view max staleness |
127
+ | `refreshIntervalMinutes` | `REFRESH_INTERVAL_MINUTES` | no | (empty) | Materialized view refresh interval |
128
+ | `kmsKeyName` | `KMS_KEY_NAME` | no | (empty) | CMEK key for the dataset |
129
+ | `logLevel` | `LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, `error`, `silent` |
130
+
131
+ ## Multiple instances
132
+
133
+ To export several collections, add one entry per instance to the `instances`
134
+ map, each pointing at its own config directory with its own `.env`:
135
+
136
+ ```json
137
+ {
138
+ "functions": [
139
+ {
140
+ "source": ".",
141
+ "kit": "firestore-bigquery-export",
142
+ "instances": {
143
+ "users": "instances/users",
144
+ "orders": "instances/orders"
145
+ }
146
+ }
147
+ ]
148
+ }
149
+ ```
150
+
151
+ Instance ids must be unique across all kit stanzas in the project, and every
152
+ instance's function names are namespaced by its `kit-<instance id>-` prefix, so
153
+ the instances cannot collide.
154
+
155
+ ## Events
156
+
157
+ When `EVENTARC_CHANNEL` is configured, the function publishes lifecycle events
158
+ such as `onStart`, `onError`, `onSuccess`, and `onCompletion` under
159
+ `firebase.extensions.firestore-bigquery-export.v1.*`.
160
+
161
+ ## Provisioning
162
+
163
+ The BigQuery dataset, table, and views are created by `tracker.initialize()`
164
+ through the shared provisioning path used by both task functions. Both tasks
165
+ are idempotent and retry on transient BigQuery failures (up to 15 attempts,
166
+ 60s minimum backoff).
167
+
168
+ Deploy wiring (declared in the package):
169
+
170
+ - First deploy runs `initBigQuerySync` automatically (`afterFirstDeploy`).
171
+ - Later deploys run `setupBigQuerySync` automatically (`afterRedeploy`).
172
+
173
+ `initBigQuerySync` and `setupBigQuerySync` call the same handler; they exist as
174
+ separate task functions so first-deploy and redeploy can target different
175
+ queues, matching the extension's install vs update/configure split.
176
+
177
+ If automatic post-deploy enqueue did not run, enqueue a task yourself. The
178
+ snippets below use the `default` instance; substitute your instance id in the
179
+ `kit-<instance id>-` prefix if you named yours differently. Prefer
180
+ `initBigQuerySync` after a first deploy and `setupBigQuerySync` after a
181
+ redeploy or schema-related config change (`TABLE_PARTITIONING`, `CLUSTERING`,
182
+ `WILDCARD_IDS`, `VIEW_TYPE`, and related fields).
183
+
184
+ ```sh
185
+ node -e '
186
+ const { initializeApp } = require("firebase-admin/app");
187
+ const { getFunctions } = require("firebase-admin/functions");
188
+ initializeApp();
189
+ getFunctions()
190
+ .taskQueue("locations/'"$DATABASE_REGION"'/functions/kit-default-initBigQuerySync")
191
+ .enqueue({})
192
+ .then(() => console.log("init task enqueued"));
193
+ '
194
+ ```
195
+
196
+ Run it from your functions directory (it uses the installed `firebase-admin`)
197
+ with application-default credentials and `GOOGLE_CLOUD_PROJECT` set. The caller
198
+ needs `roles/cloudtasks.enqueuer`.
199
+
200
+ Under the hood the task queue is an authenticated HTTP endpoint, so for a quick
201
+ manual run you can also POST to it directly — note this skips the queue, so a
202
+ failure is not retried:
203
+
204
+ ```sh
205
+ URL=$(gcloud functions describe kit-default-initBigQuerySync \
206
+ --region "$DATABASE_REGION" --gen2 --format='value(url)')
207
+
208
+ curl -fsS -X POST -H "Content-Type: application/json" -d '{"data":{}}' \
209
+ -H "Authorization: Bearer $(gcloud auth print-identity-token --audiences="$URL")" "$URL"
210
+ ```
211
+
212
+ The Firestore write path never provisions on the hot path. If resources are
213
+ missing when a write arrives, the inline write fails, the handler calls
214
+ `ensureInitialized()` once as a self-heal and retries the write, and a remaining
215
+ failure is surfaced to the function runtime retry policy (`retry: true` on
216
+ `fsexportbigquery`).
217
+
218
+ ## Differences from the Stream Firestore to BigQuery extension
219
+
220
+ This kit is the extension repackaged as an npm package, but a few things behave
221
+ differently. If you are moving from an installed extension instance, read this
222
+ section before you deploy.
223
+
224
+ ### Boolean settings use `true` / `false`
225
+
226
+ `WILDCARD_IDS`, `USE_NEW_SNAPSHOT_QUERY_SYNTAX` and `EXCLUDE_OLD_DATA` are
227
+ boolean params, and only the literal string `true` enables them. The extension
228
+ used `yes` / `no` for the last two, so copying an old config across leaves them
229
+ silently disabled. Change any `yes` to `true` in your `.env`.
230
+
231
+ ### Failed writes retry differently
232
+
233
+ The extension pushed a failed BigQuery write onto a Cloud Tasks queue
234
+ (`syncBigQuery`) and retried it from there. This kit has no task queue on the
235
+ write path. A failed write is retried once in place, and anything still failing
236
+ is handed to the Cloud Functions runtime retry policy, which redelivers the
237
+ Firestore event.
238
+
239
+ The practical effects: retries no longer show up as a separate function or
240
+ queue in the console, and the two knobs that tuned that queue,
241
+ `MAX_DISPATCHES_PER_SECOND` and `MAX_ENQUEUE_ATTEMPTS`, no longer exist.
242
+
243
+ ### Events
244
+
245
+ `onSuccess` is no longer published. The extension emitted it from the task
246
+ queue handler, which is gone, so the kit publishes `onStart` and `onError`
247
+ only.
248
+
249
+ Events are published under `firebase.extensions.firestore-bigquery-export.v1.*`
250
+ only. The extension also published a duplicate copy of every event under
251
+ `firebase.extensions.firestore-counter.v1.*`, a historical naming mistake kept
252
+ for backwards compatibility. If you have Eventarc triggers listening on those
253
+ `firestore-counter` types, point them at the `firestore-bigquery-export` types.
254
+
255
+ ### Wildcard columns include the document ID
256
+
257
+ With `WILDCARD_IDS=true`, the wildcard column now contains a `documentId` key
258
+ alongside the path parameters from your collection path. The extension wrote
259
+ the path parameters only.
260
+
261
+ ### Functions deploy to your Firestore region
262
+
263
+ The extension let you pick a function location separately from the Firestore
264
+ database location. Here, `DATABASE_REGION` sets both: the trigger, the
265
+ lifecycle tasks, and the database being watched.
266
+
267
+ ### Defaults
268
+
269
+ Two settings now have defaults rather than being passed through empty:
270
+ `DATASET_LOCATION` defaults to `us`, and `BIGQUERY_PROJECT_ID` defaults to the
271
+ project the functions are deployed to.
272
+
273
+ ### Tooling that is not included
274
+
275
+ The extension shipped companion scripts that this package does not:
276
+
277
+ - `fs-bq-import-collection`, for backfilling documents that already existed
278
+ before the export started.
279
+ - `gen-schema-view`, for generating strongly typed BigQuery views over the
280
+ changelog.
281
+ - The cross-project access grant scripts.
282
+
283
+ `IMPORT_COLLECTION_PATH` is not a setting here. If you rely on any of these,
284
+ keep using the versions from the extension repository. They operate on the same
285
+ BigQuery changelog table, so they still work against data this kit writes.
286
+
287
+ ## API surface
288
+
289
+ - **Main entry** (`@firebase/firestore-bigquery-export`): exports
290
+ `fsexportbigquery`, `initBigQuerySync`, and `setupBigQuerySync`, and
291
+ registers the first-deploy / redeploy provisioning hooks. Runtime config is
292
+ resolved lazily on first invocation. Use this entry from Firebase
293
+ deploy/emulator/runtime. For your own triggers, import from `./lib` instead.
294
+ - **Library entry** (`./lib`): `handleDocumentWrite`, the raw handler for owning
295
+ trigger registration yourself, plus the config types and helpers
296
+ (`ExportConfig`, `resolveExportConfig`, `toTrackerConfig`) for building its
297
+ injected `HandlerContext`. Safe to import anywhere.
298
+
299
+ The change-tracker engine is an internal dependency and is not exported.
300
+
301
+ ## License
302
+
303
+ Apache-2.0
@@ -0,0 +1,33 @@
1
+ import type { ChangeTrackerConfig, TimePartitioningGranularity } from "@firebaseextensions/firestore-bigquery-change-tracker";
2
+ import type { Expression } from "firebase-functions/params";
3
+ import type { ExportConfig } from "./export-config";
4
+ type ConfigExpression<T extends string | number | boolean> = Expression<T>;
5
+ export interface ConfigExpressions {
6
+ collectionPath: ConfigExpression<string>;
7
+ datasetId: ConfigExpression<string>;
8
+ tableId: ConfigExpression<string>;
9
+ location: ConfigExpression<string>;
10
+ database: ConfigExpression<string>;
11
+ }
12
+ export declare const CONFIG_EXPRESSIONS: ConfigExpressions;
13
+ export declare function clustering(clusters: string | undefined): string[];
14
+ export declare function buildPartitioningConfig(params: {
15
+ timePartitioning: TimePartitioningGranularity | null;
16
+ timePartitioningField: string | undefined;
17
+ timePartitioningFieldType: string | undefined;
18
+ timePartitioningFirestoreField: string | undefined;
19
+ }): ChangeTrackerConfig["partitioning"];
20
+ /**
21
+ * Resolves all deploy-time params into an {@link ExportConfig}.
22
+ *
23
+ * Param values are read when this is called. During the Firebase deploy-time
24
+ * discovery pass params return their declared defaults, so the default
25
+ * `TABLE_PARTITIONING=NONE` keeps {@link buildPartitioningConfig} from throwing.
26
+ * This is the bridge for the env-driven path: env params in, typed config out,
27
+ * which the main entry point wires into the exported functions.
28
+ *
29
+ * @returns The export configuration assembled from environment params.
30
+ */
31
+ export declare function configFromEnv(): ExportConfig;
32
+ export {};
33
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,mBAAmB,EAEnB,2BAA2B,EAC5B,MAAM,uDAAuD,CAAC;AAE/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAO5D,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,iBAAiB,CAAC;AAG9D,KAAK,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;AAkH3E,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzC,SAAS,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACpC;AAqVD,eAAO,MAAM,kBAAkB,EAAE,iBAMhC,CAAC;AAiBF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,YAEtD;AA4BD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,gBAAgB,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACrD,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,8BAA8B,EAAE,MAAM,GAAG,SAAS,CAAC;CACpD,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAuEtC;AA6BD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,YAAY,CAiC5C"}