@firebase-function-kits/bigquery-firestore-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.
- package/CHANGELOG.md +1 -0
- package/README.md +308 -0
- package/lib/config.d.ts +5 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +181 -0
- package/lib/config.js.map +1 -0
- package/lib/dts.d.ts +21 -0
- package/lib/dts.d.ts.map +1 -0
- package/lib/dts.js +193 -0
- package/lib/dts.js.map +1 -0
- package/lib/export-config.d.ts +55 -0
- package/lib/export-config.d.ts.map +1 -0
- package/lib/export-config.js +55 -0
- package/lib/export-config.js.map +1 -0
- package/lib/handlers.d.ts +22 -0
- package/lib/handlers.d.ts.map +1 -0
- package/lib/handlers.js +125 -0
- package/lib/handlers.js.map +1 -0
- package/lib/helper.d.ts +37 -0
- package/lib/helper.d.ts.map +1 -0
- package/lib/helper.js +219 -0
- package/lib/helper.js.map +1 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +137 -0
- package/lib/index.js.map +1 -0
- package/lib/lib.d.ts +11 -0
- package/lib/lib.d.ts.map +1 -0
- package/lib/lib.js +47 -0
- package/lib/lib.js.map +1 -0
- package/lib/logs.d.ts +21 -0
- package/lib/logs.d.ts.map +1 -0
- package/lib/logs.js +122 -0
- package/lib/logs.js.map +1 -0
- package/lib/metadata.d.ts +6 -0
- package/lib/metadata.d.ts.map +1 -0
- package/lib/metadata.js +34 -0
- package/lib/metadata.js.map +1 -0
- package/lib/types.d.ts +44 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +18 -0
- package/lib/types.js.map +1 -0
- package/npm-shrinkwrap.json +5049 -0
- package/package.json +40 -4
- package/src/config.ts +205 -0
- package/src/dts.ts +217 -0
- package/src/export-config.ts +119 -0
- package/src/handlers.ts +141 -0
- package/src/helper.ts +320 -0
- package/src/index.ts +126 -0
- package/src/lib.ts +69 -0
- package/src/logs.ts +145 -0
- package/src/metadata.ts +31 -0
- package/src/types.ts +100 -0
- package/tests/config.test.ts +61 -0
- package/tests/dts.test.ts +120 -0
- package/tests/export-config.test.ts +74 -0
- package/tests/handlers.test.ts +170 -0
- package/tests/helper.test.ts +74 -0
- package/tests/lib.test.ts +43 -0
- package/tsconfig.json +18 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/index.js +0 -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,308 @@
|
|
|
1
|
+
# @firebase/bigquery-firestore-export
|
|
2
|
+
|
|
3
|
+
Schedule a BigQuery query and write each run's rows and metadata to Firestore.
|
|
4
|
+
This is the Export BigQuery to Firestore Firebase Extension as an npm package
|
|
5
|
+
you add to your own Firebase Functions codebase and deploy.
|
|
6
|
+
|
|
7
|
+
The kit creates or reconciles a BigQuery Data Transfer scheduled query, listens
|
|
8
|
+
for its Pub/Sub completion notifications, reads the destination table, and
|
|
9
|
+
writes the rows to Firestore. The functions run in your own Firebase project;
|
|
10
|
+
there is no hosted version, so you deploy them yourself.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @firebase/bigquery-firestore-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/datastore.user` | store transfer configs, run metadata, and query output in Firestore |
|
|
29
|
+
| `roles/bigquery.admin` | create scheduled queries and read their destination tables |
|
|
30
|
+
| `roles/pubsub.admin` | create the transfer-notification topic |
|
|
31
|
+
| `roles/eventarc.eventReceiver` | receive Gen2 Pub/Sub trigger events |
|
|
32
|
+
| `roles/run.invoker` | allow Eventarc/Tasks to invoke the Gen2 Cloud Run services |
|
|
33
|
+
| `bigquery.googleapis.com` | run queries and read destination tables |
|
|
34
|
+
| `bigquerydatatransfer.googleapis.com` | create and reconcile scheduled-query transfer configs |
|
|
35
|
+
| `pubsub.googleapis.com` | deliver transfer-completion notifications |
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Export both functions from your functions codebase entry:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// functions/src/index.ts
|
|
43
|
+
export {
|
|
44
|
+
processMessages,
|
|
45
|
+
upsertTransferConfig,
|
|
46
|
+
} from "@firebase/bigquery-firestore-export";
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
and configure them with a `.env` (or `.env.<projectId>`):
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
INSTANCE_ID=analytics-export
|
|
53
|
+
BIGQUERY_DATASET_LOCATION=US
|
|
54
|
+
DATASET_ID=analytics
|
|
55
|
+
TABLE_NAME=users
|
|
56
|
+
QUERY_STRING=SELECT * FROM `my-project.source.users`
|
|
57
|
+
DISPLAY_NAME=Export users to Firestore
|
|
58
|
+
SCHEDULE=every 24 hours
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `processMessages` consumes BigQuery Data Transfer completion messages.
|
|
62
|
+
- `upsertTransferConfig` is the idempotent lifecycle task that creates, links,
|
|
63
|
+
or updates the scheduled query and its notification topic.
|
|
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": "bigquery-firestore-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-processMessages` and
|
|
91
|
+
`kit-default-upsertTransferConfig`.
|
|
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 required values that are missing.
|
|
104
|
+
`PROJECT_ID` is supplied by the Firebase CLI.
|
|
105
|
+
|
|
106
|
+
| Field | Env var | Required | Default | Description |
|
|
107
|
+
| ------------------------- | --------------------------- | -------- | ----------------- | ------------------------------------------------------------ |
|
|
108
|
+
| `instanceId` | `INSTANCE_ID` | yes | — | Must match this instance's key in the `instances` map |
|
|
109
|
+
| `bigqueryDatasetLocation` | `BIGQUERY_DATASET_LOCATION` | no | `US` | BigQuery destination dataset location |
|
|
110
|
+
| `transferConfigName` | `TRANSFER_CONFIG_NAME` | no | (empty) | Existing DTS config resource to link instead of creating one |
|
|
111
|
+
| `datasetId` | `DATASET_ID` | yes | — | BigQuery destination dataset id |
|
|
112
|
+
| `tableName` | `TABLE_NAME` | yes | — | Prefix for per-run destination tables |
|
|
113
|
+
| `queryString` | `QUERY_STRING` | yes | — | Scheduled Standard SQL query |
|
|
114
|
+
| `displayName` | `DISPLAY_NAME` | yes | — | Human-readable scheduled-query name |
|
|
115
|
+
| `partitioningField` | `PARTITIONING_FIELD` | no | (empty) | Destination-table partitioning field |
|
|
116
|
+
| `schedule` | `SCHEDULE` | yes | — | DTS schedule, such as `every 24 hours` |
|
|
117
|
+
| `firestoreCollection` | `COLLECTION_PATH` | no | `transferConfigs` | Root Firestore collection for configs and output |
|
|
118
|
+
| `logLevel` | `LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, `error`, or `silent` |
|
|
119
|
+
|
|
120
|
+
## Multiple instances
|
|
121
|
+
|
|
122
|
+
To run several reverse-sync instances, add one entry per instance to the
|
|
123
|
+
`instances` map, each pointing at its own config directory with its own `.env`:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"functions": [
|
|
128
|
+
{
|
|
129
|
+
"source": ".",
|
|
130
|
+
"kit": "bigquery-firestore-export",
|
|
131
|
+
"instances": {
|
|
132
|
+
"users": "instances/users",
|
|
133
|
+
"orders": "instances/orders"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Instance ids must be unique across all kit stanzas in the project, and every
|
|
141
|
+
instance's function names are namespaced by its `kit-<instance id>-` prefix, so
|
|
142
|
+
the instances cannot collide. Set `INSTANCE_ID` in each config directory to the
|
|
143
|
+
same value as that directory's key in the `instances` map; it also namespaces
|
|
144
|
+
the Pub/Sub notification topic and associates the deployment with its transfer
|
|
145
|
+
config.
|
|
146
|
+
|
|
147
|
+
## Provisioning
|
|
148
|
+
|
|
149
|
+
The kit wires `upsertTransferConfig` to both `afterFirstDeploy` and
|
|
150
|
+
`afterRedeploy`. The lifecycle task creates the Pub/Sub topic and scheduled
|
|
151
|
+
query on first deploy, then reconciles supported query, table, schedule,
|
|
152
|
+
dataset, partitioning, and topic changes on later deploys. It retries transient
|
|
153
|
+
failures up to five times with at least 30 seconds of backoff.
|
|
154
|
+
|
|
155
|
+
Set `TRANSFER_CONFIG_NAME` to link an existing scheduled-query config without
|
|
156
|
+
changing it. Otherwise the kit stores `extInstanceId` on the Firestore config
|
|
157
|
+
document and uses that value to find the config on later deploys. BigQuery DTS
|
|
158
|
+
does not support clearing a partitioning field once set; create a new transfer
|
|
159
|
+
config to remove partitioning.
|
|
160
|
+
|
|
161
|
+
## Firestore layout
|
|
162
|
+
|
|
163
|
+
For a transfer config `{configId}` and run `{runId}`, the kit writes:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
transferConfigs/{configId}
|
|
167
|
+
transferConfigs/{configId}/runs/{runId}
|
|
168
|
+
transferConfigs/{configId}/runs/{runId}/output/{rowId}
|
|
169
|
+
transferConfigs/{configId}/runs/latest
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The run document stores DTS metadata and row counts. Its `output` subcollection
|
|
173
|
+
contains converted query rows. The `latest` document is updated transactionally
|
|
174
|
+
so an older completion message cannot replace a newer run.
|
|
175
|
+
|
|
176
|
+
## Differences from the Export BigQuery to Firestore extension
|
|
177
|
+
|
|
178
|
+
This kit is version 0.2.2 of the extension repackaged as an npm package. It is a
|
|
179
|
+
close port: the same two functions, the same BigQuery Data Transfer scheduled
|
|
180
|
+
query, the same `transferConfigs/{configId}/runs/{runId}` and `runs/latest`
|
|
181
|
+
documents, the same `WRITE_TRUNCATE` destination table naming and the same
|
|
182
|
+
row-by-row copy into Firestore. Every setting keeps its extension environment
|
|
183
|
+
variable name and default, so a `.env` copied from your installed instance needs
|
|
184
|
+
no value changes. What changes is the instance id, the Pub/Sub topic, the identity
|
|
185
|
+
the scheduled query runs as, and how repeated BigQuery columns land in Firestore.
|
|
186
|
+
|
|
187
|
+
### You set `INSTANCE_ID` yourself, and the Pub/Sub topic is renamed
|
|
188
|
+
|
|
189
|
+
The extension derived an instance id at install and used it to name its
|
|
190
|
+
notification topic (`ext-<instance id>-processMessages`) and to tag its transfer
|
|
191
|
+
config document with `extInstanceId`. Here `INSTANCE_ID` is a setting you
|
|
192
|
+
provide, and it must match this instance's key in the `instances` map in
|
|
193
|
+
`firebase.json`.
|
|
194
|
+
|
|
195
|
+
The topic becomes `kit-<INSTANCE_ID>-processMessages`, and the kit creates it on
|
|
196
|
+
first run if it does not already exist. Set `INSTANCE_ID` to your installed
|
|
197
|
+
instance's id if you want the kit to adopt the scheduled query that instance
|
|
198
|
+
created, because the lookup is by `extInstanceId` on the documents in
|
|
199
|
+
`COLLECTION_PATH`. With a different id the kit finds nothing, creates a second
|
|
200
|
+
scheduled query, and you end up with two writing into the same collection.
|
|
201
|
+
|
|
202
|
+
The existing transfer config still points its notifications at the old `ext-`
|
|
203
|
+
topic; the kit's update path rewrites `notification_pubsub_topic` to the new one
|
|
204
|
+
on the first deploy, so the old topic can be deleted afterwards.
|
|
205
|
+
|
|
206
|
+
### Repeated BigQuery columns are now written as arrays
|
|
207
|
+
|
|
208
|
+
A repeated (`ARRAY`) column used to arrive in Firestore as a map keyed by
|
|
209
|
+
position, `{ "0": ..., "1": ... }`, because the conversion treated every
|
|
210
|
+
non-scalar value as an object. The kit writes a real Firestore array instead.
|
|
211
|
+
Anything reading those fields by numeric string key needs updating, and rows
|
|
212
|
+
written before and after the change are not the same shape. Scalars, timestamps,
|
|
213
|
+
dates, times, datetimes, bytes and geography values convert exactly as before.
|
|
214
|
+
|
|
215
|
+
### The scheduled query runs as a different service account
|
|
216
|
+
|
|
217
|
+
The extension created the transfer config with
|
|
218
|
+
`serviceAccountName: ext-<instance id>@<project>.iam.gserviceaccount.com`, so the
|
|
219
|
+
query ran as the extension's own service account. The kit creates it without a
|
|
220
|
+
service account name, so BigQuery Data Transfer runs it as the identity that
|
|
221
|
+
created it, which is your function's runtime service account (the default compute
|
|
222
|
+
service account unless you have set one).
|
|
223
|
+
|
|
224
|
+
That account needs to be able to read whatever `QUERY_STRING` touches and write
|
|
225
|
+
to `DATASET_ID`. `roles/bigquery.admin` on the function covers this for datasets
|
|
226
|
+
in the same project; a cross-project query needs the grant made explicitly. This
|
|
227
|
+
was not exercised against a live deploy.
|
|
228
|
+
|
|
229
|
+
Note also that a service account cannot be changed on an existing transfer config,
|
|
230
|
+
so a scheduled query originally created by an installed extension instance keeps
|
|
231
|
+
running as the extension's service account even after the kit adopts it. Delete
|
|
232
|
+
and recreate the scheduled query if you want it moved.
|
|
233
|
+
|
|
234
|
+
### The setup step runs on every deploy
|
|
235
|
+
|
|
236
|
+
The install, update and configure hooks are replaced by an `upsertTransferConfig`
|
|
237
|
+
task that the CLI runs after your first deploy and after every redeploy. It does
|
|
238
|
+
the same work: create the scheduled query if this instance has none, otherwise
|
|
239
|
+
reconcile the existing one against your current `QUERY_STRING`, `DATASET_ID`,
|
|
240
|
+
`TABLE_NAME`, `PARTITIONING_FIELD` and `SCHEDULE`.
|
|
241
|
+
|
|
242
|
+
Two consequences of it now being an ordinary task rather than a lifecycle event.
|
|
243
|
+
There is no install UI to report progress into, so failures show up in the task's
|
|
244
|
+
function logs, and the task retries up to five times with a 30 second minimum
|
|
245
|
+
backoff. And `DISPLAY_NAME` is no longer immutable, but changing it does not
|
|
246
|
+
rename an existing scheduled query, because display name is not part of the
|
|
247
|
+
update; it only applies to a config the kit creates.
|
|
248
|
+
|
|
249
|
+
Removing `PARTITIONING_FIELD` once it has been set still fails, with the same
|
|
250
|
+
explanation, because the BigQuery Data Transfer API cannot clear it.
|
|
251
|
+
|
|
252
|
+
### You can link an existing scheduled query
|
|
253
|
+
|
|
254
|
+
`TRANSFER_CONFIG_NAME` is a new setting. Point it at the full resource name of a
|
|
255
|
+
scheduled query you already have
|
|
256
|
+
(`projects/<project>/locations/<location>/transferConfigs/<id>`) and the kit
|
|
257
|
+
records that config in Firestore and consumes its notifications instead of
|
|
258
|
+
creating one of its own. The extension carried the code for this but no setting to
|
|
259
|
+
reach it. Leave it empty for the create-or-reconcile behaviour described above.
|
|
260
|
+
|
|
261
|
+
### Region, and no location setting
|
|
262
|
+
|
|
263
|
+
`LOCATION` is gone. Both functions deploy to your codebase's default region
|
|
264
|
+
(`us-central1` unless you have changed it) rather than the immutable location you
|
|
265
|
+
picked at install. `BIGQUERY_DATASET_LOCATION` is unchanged and still tells the
|
|
266
|
+
result query where your dataset lives.
|
|
267
|
+
|
|
268
|
+
### Failed notifications are retried
|
|
269
|
+
|
|
270
|
+
`processMessages` is a 2nd gen Pub/Sub function with retries enabled, where the
|
|
271
|
+
extension's 1st gen trigger did not retry. A run whose results fail to copy, for
|
|
272
|
+
example because BigQuery or Firestore is briefly unavailable, is now retried
|
|
273
|
+
rather than dropped. A notification that keeps failing, such as one for a transfer
|
|
274
|
+
config not tagged with this `INSTANCE_ID`, is also retried until Pub/Sub gives up.
|
|
275
|
+
|
|
276
|
+
Both functions' service accounts need `roles/eventarc.eventReceiver` and
|
|
277
|
+
`roles/run.invoker` on top of the three roles the extension asked for, and the
|
|
278
|
+
Pub/Sub API is now requested explicitly. The Firebase CLI handles all of this.
|
|
279
|
+
|
|
280
|
+
### Unchanged
|
|
281
|
+
|
|
282
|
+
- `COLLECTION_PATH` still defaults to `transferConfigs`, and the document layout
|
|
283
|
+
under it is identical: the transfer config document keyed by config id, a `runs`
|
|
284
|
+
subcollection keyed by run id holding `runMetadata`, `totalRowCount` and
|
|
285
|
+
`failedRowCount`, a `latest` document, and an `output` collection of rows per
|
|
286
|
+
run.
|
|
287
|
+
- The destination table is still `TABLE_NAME_{run_time|"%H%M%S"}` with
|
|
288
|
+
`WRITE_TRUNCATE`, and results are still read with `SELECT *` in
|
|
289
|
+
`BIGQUERY_DATASET_LOCATION`.
|
|
290
|
+
- Runs that do not succeed still write a run document with zeroed counts and still
|
|
291
|
+
update `latest`, and `latest` is still only moved forward by a newer run.
|
|
292
|
+
- Rows are still written one document at a time in chunks of 10,000, with
|
|
293
|
+
per-row failures logged and counted rather than aborting the run.
|
|
294
|
+
- `LOG_LEVEL` still accepts `debug`, `info`, `warn`, `error` and `silent`.
|
|
295
|
+
|
|
296
|
+
## API surface
|
|
297
|
+
|
|
298
|
+
- **Main entry** (`@firebase/bigquery-firestore-export`): exports
|
|
299
|
+
`processMessages` and `upsertTransferConfig`, registers first-deploy and
|
|
300
|
+
redeploy lifecycle hooks, and resolves runtime dependencies lazily. Use this
|
|
301
|
+
entry from Firebase deploy/emulator/runtime.
|
|
302
|
+
- **Library entry** (`./lib`): side-effect-free config helpers, DTS helpers,
|
|
303
|
+
injectable handlers, BigQuery-to-Firestore conversion helpers, and public
|
|
304
|
+
message/config types for consumers that own trigger registration.
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
Apache-2.0
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BigqueryFirestoreExportConfig, DeployTimeOptions } from "./export-config";
|
|
2
|
+
export declare const CONFIG_EXPRESSIONS: DeployTimeOptions;
|
|
3
|
+
/** Reads runtime values from Firebase deploy-time parameters. */
|
|
4
|
+
export declare function configFromEnv(): BigqueryFirestoreExportConfig;
|
|
5
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,6BAA6B,EAC7B,iBAAiB,EAElB,MAAM,iBAAiB,CAAC;AA+IzB,eAAO,MAAM,kBAAkB,EAAE,iBAEhC,CAAC;AAcF,iEAAiE;AACjE,wBAAgB,aAAa,IAAI,6BAA6B,CAkB7D"}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
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.CONFIG_EXPRESSIONS = void 0;
|
|
19
|
+
exports.configFromEnv = configFromEnv;
|
|
20
|
+
const params_1 = require("firebase-functions/params");
|
|
21
|
+
const LOG_LEVEL_OPTIONS = ["debug", "info", "warn", "error", "silent"];
|
|
22
|
+
const instanceId = (0, params_1.defineString)("INSTANCE_ID");
|
|
23
|
+
const params = {
|
|
24
|
+
instanceId,
|
|
25
|
+
bigqueryDatasetLocation: (0, params_1.defineString)("BIGQUERY_DATASET_LOCATION", {
|
|
26
|
+
label: "BigQuery Dataset Location",
|
|
27
|
+
description: "What is the location of the BigQuery dataset referenced in the query?",
|
|
28
|
+
default: "US",
|
|
29
|
+
input: (0, params_1.select)({
|
|
30
|
+
"Columbus, Ohio (us-east5)": "us-east5",
|
|
31
|
+
"Iowa (us-central1)": "us-central1",
|
|
32
|
+
"Las Vegas (us-west4)": "us-west4",
|
|
33
|
+
"Los Angeles (us-west2)": "us-west2",
|
|
34
|
+
"Montréal (northamerica-northeast1)": "northamerica-northeast1",
|
|
35
|
+
"Northern Virginia (us-east4)": "us-east4",
|
|
36
|
+
"Oregon (us-west1)": "us-west1",
|
|
37
|
+
"Salt Lake City (us-west3)": "us-west3",
|
|
38
|
+
"São Paulo (southamerica-east1)": "southamerica-east1",
|
|
39
|
+
"Santiago (southamerica-west1)": "southamerica-west1",
|
|
40
|
+
"South Carolina (us-east1)": "us-east1",
|
|
41
|
+
"Toronto (northamerica-northeast2)": "northamerica-northeast2",
|
|
42
|
+
"Delhi (asia-south2)": "asia-south2",
|
|
43
|
+
"Hong Kong (asia-east2)": "asia-east2",
|
|
44
|
+
"Jakarta (asia-southeast2)": "asia-southeast2",
|
|
45
|
+
"Melbourne (australia-southeast2)": "australia-southeast2",
|
|
46
|
+
"Mumbai (asia-south1)": "asia-south1",
|
|
47
|
+
"Osaka (asia-northeast2)": "asia-northeast2",
|
|
48
|
+
"Seoul (asia-northeast3)": "asia-northeast3",
|
|
49
|
+
"Singapore (asia-southeast1)": "asia-southeast1",
|
|
50
|
+
"Sydney (australia-southeast1)": "australia-southeast1",
|
|
51
|
+
"Taiwan (asia-east1)": "asia-east1",
|
|
52
|
+
"Tokyo (asia-northeast1)": "asia-northeast1",
|
|
53
|
+
"Belgium (europe-west1)": "europe-west1",
|
|
54
|
+
"Finland (europe-north1)": "europe-north1",
|
|
55
|
+
"Frankfurt (europe-west3)": "europe-west3",
|
|
56
|
+
"London (europe-west2)": "europe-west2",
|
|
57
|
+
"Madrid (europe-southwest1)": "europe-southwest1",
|
|
58
|
+
"Milan (europe-west8)": "europe-west8",
|
|
59
|
+
"Netherlands (europe-west4)": "europe-west4",
|
|
60
|
+
"Paris (europe-west9)": "europe-west9",
|
|
61
|
+
"Warsaw (europe-central2)": "europe-central2",
|
|
62
|
+
"Zürich (europe-west6)": "europe-west6",
|
|
63
|
+
"US Multi-Region (US)": "US",
|
|
64
|
+
"EU Mutli-Region (EU)": "EU",
|
|
65
|
+
}),
|
|
66
|
+
}),
|
|
67
|
+
transferConfigName: (0, params_1.defineString)("TRANSFER_CONFIG_NAME", { default: "" }),
|
|
68
|
+
datasetId: (0, params_1.defineString)("DATASET_ID", {
|
|
69
|
+
label: "Dataset ID",
|
|
70
|
+
description: "What's the BigQuery destination dataset you'd like to use? Each transfer run will write to a table in this destination dataset.",
|
|
71
|
+
input: {
|
|
72
|
+
text: {
|
|
73
|
+
nonEmpty: true,
|
|
74
|
+
example: "customer_data",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
tableName: (0, params_1.defineString)("TABLE_NAME", {
|
|
79
|
+
label: "Destination Table Name",
|
|
80
|
+
description: "What's the destination table name prefix you'd like to use? Each transfer run will write to the table with this name, postfixed with the runtime.",
|
|
81
|
+
input: {
|
|
82
|
+
text: {
|
|
83
|
+
nonEmpty: true,
|
|
84
|
+
example: "transactions",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
queryString: (0, params_1.defineString)("QUERY_STRING", {
|
|
89
|
+
label: "Query String",
|
|
90
|
+
description: "What's the BQ query you'd like to execute?",
|
|
91
|
+
input: {
|
|
92
|
+
text: {
|
|
93
|
+
nonEmpty: true,
|
|
94
|
+
example: "SELECT * from <PROJECT_ID>.customer_data.transactions",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
displayName: (0, params_1.defineString)("DISPLAY_NAME", {
|
|
99
|
+
label: "Display Name",
|
|
100
|
+
description: "What display name would you like to use?",
|
|
101
|
+
input: {
|
|
102
|
+
text: {
|
|
103
|
+
nonEmpty: true,
|
|
104
|
+
example: "Daily Rollup - Customer Transactions",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
partitioningField: (0, params_1.defineString)("PARTITIONING_FIELD", {
|
|
109
|
+
label: "Partitioning Field",
|
|
110
|
+
description: "What's the partitioning field on the destination table ID? Leave empty if not using a partitioning field.",
|
|
111
|
+
default: "",
|
|
112
|
+
input: { text: { example: "timestamp" } },
|
|
113
|
+
}),
|
|
114
|
+
schedule: (0, params_1.defineString)("SCHEDULE", {
|
|
115
|
+
label: "Schedule",
|
|
116
|
+
description: "What's the execution schedule you'd like to use for this query?",
|
|
117
|
+
input: {
|
|
118
|
+
text: {
|
|
119
|
+
nonEmpty: true,
|
|
120
|
+
example: "every 15 minutes",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
firestoreCollection: (0, params_1.defineString)("COLLECTION_PATH", {
|
|
125
|
+
label: "Firestore Collection",
|
|
126
|
+
description: "What's the top-level Firestore Collection to store transfer configs, run metadata, and query output?",
|
|
127
|
+
default: "transferConfigs",
|
|
128
|
+
input: {
|
|
129
|
+
text: {
|
|
130
|
+
example: "transferConfigs",
|
|
131
|
+
validationRegex: /^[^\/]+(\/[^\/]+\/[^\/]+)*$/,
|
|
132
|
+
validationErrorMessage: "Must be a valid Cloud Firestore Collection",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
logLevel: (0, params_1.defineString)("LOG_LEVEL", {
|
|
137
|
+
label: "Log Level",
|
|
138
|
+
description: "What's the log level you'd like to use for this extension?",
|
|
139
|
+
default: "info",
|
|
140
|
+
input: (0, params_1.select)({
|
|
141
|
+
DEBUG: "debug",
|
|
142
|
+
INFO: "info",
|
|
143
|
+
WARN: "warn",
|
|
144
|
+
ERROR: "error",
|
|
145
|
+
SILENT: "silent",
|
|
146
|
+
}),
|
|
147
|
+
}),
|
|
148
|
+
};
|
|
149
|
+
exports.CONFIG_EXPRESSIONS = {
|
|
150
|
+
pubSubTopic: (0, params_1.expr) `kit-${instanceId}-processMessages`,
|
|
151
|
+
};
|
|
152
|
+
function optional(value) {
|
|
153
|
+
const normalized = value.trim();
|
|
154
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
155
|
+
}
|
|
156
|
+
function normalizeLogLevel(value) {
|
|
157
|
+
const normalized = value.toLowerCase();
|
|
158
|
+
return LOG_LEVEL_OPTIONS.includes(normalized)
|
|
159
|
+
? normalized
|
|
160
|
+
: "info";
|
|
161
|
+
}
|
|
162
|
+
/** Reads runtime values from Firebase deploy-time parameters. */
|
|
163
|
+
function configFromEnv() {
|
|
164
|
+
const resolvedInstanceId = params.instanceId.value();
|
|
165
|
+
return {
|
|
166
|
+
bigqueryDatasetLocation: params.bigqueryDatasetLocation.value(),
|
|
167
|
+
projectId: params_1.projectID.value(),
|
|
168
|
+
instanceId: resolvedInstanceId,
|
|
169
|
+
transferConfigName: optional(params.transferConfigName.value()),
|
|
170
|
+
datasetId: params.datasetId.value(),
|
|
171
|
+
tableName: params.tableName.value(),
|
|
172
|
+
queryString: params.queryString.value(),
|
|
173
|
+
displayName: params.displayName.value(),
|
|
174
|
+
partitioningField: optional(params.partitioningField.value()),
|
|
175
|
+
schedule: params.schedule.value(),
|
|
176
|
+
pubSubTopic: `kit-${resolvedInstanceId}-processMessages`,
|
|
177
|
+
firestoreCollection: params.firestoreCollection.value(),
|
|
178
|
+
logLevel: normalizeLogLevel(params.logLevel.value()),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/lib/dts.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as bigqueryDataTransfer from "@google-cloud/bigquery-data-transfer";
|
|
2
|
+
import type { ResolvedBigqueryFirestoreExportConfig } from "./export-config";
|
|
3
|
+
export type DataTransferClient = bigqueryDataTransfer.v1.DataTransferServiceClient;
|
|
4
|
+
export type TransferConfig = bigqueryDataTransfer.protos.google.cloud.bigquery.datatransfer.v1.ITransferConfig;
|
|
5
|
+
export declare const PARTITIONING_FIELD_REMOVAL_ERROR_PREFIX = "Cannot remove partitioning_field from an existing transfer config";
|
|
6
|
+
export declare const PARTITIONING_FIELD_REMOVAL_ERROR = "Cannot remove partitioning_field from an existing transfer config. The BigQuery Data Transfer API does not support clearing this parameter once it has been set. To change partitioning, create a new transfer config with the desired setting.";
|
|
7
|
+
/**
|
|
8
|
+
* Creates the protobuf-shaped request used for a scheduled query.
|
|
9
|
+
*
|
|
10
|
+
* The query runs as whichever identity creates it, which is this function. To
|
|
11
|
+
* name a different account, `serviceAccountName` goes on the request rather
|
|
12
|
+
* than on the transfer config, and the caller needs `actAs` on the account it
|
|
13
|
+
* names, including its own.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createTransferConfigRequest(config: ResolvedBigqueryFirestoreExportConfig): bigqueryDataTransfer.protos.google.cloud.bigquery.datatransfer.v1.ICreateTransferConfigRequest;
|
|
16
|
+
export declare function getTransferConfig(client: DataTransferClient, transferConfigName: string): Promise<TransferConfig | null>;
|
|
17
|
+
export declare function createTransferConfig(client: DataTransferClient, config: ResolvedBigqueryFirestoreExportConfig): Promise<TransferConfig>;
|
|
18
|
+
/** Builds a minimal update mask while retaining unsupported immutable fields. */
|
|
19
|
+
export declare function constructUpdateTransferConfigRequest(client: DataTransferClient, transferConfigName: string, config: ResolvedBigqueryFirestoreExportConfig): Promise<bigqueryDataTransfer.protos.google.cloud.bigquery.datatransfer.v1.IUpdateTransferConfigRequest>;
|
|
20
|
+
export declare function updateTransferConfig(client: DataTransferClient, transferConfigName: string, config: ResolvedBigqueryFirestoreExportConfig): Promise<TransferConfig>;
|
|
21
|
+
//# sourceMappingURL=dts.d.ts.map
|
package/lib/dts.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dts.d.ts","sourceRoot":"","sources":["../src/dts.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,oBAAoB,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,iBAAiB,CAAC;AAG7E,MAAM,MAAM,kBAAkB,GAC5B,oBAAoB,CAAC,EAAE,CAAC,yBAAyB,CAAC;AACpD,MAAM,MAAM,cAAc,GACxB,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,eAAe,CAAC;AAMpF,eAAO,MAAM,uCAAuC,sEACxB,CAAC;AAC7B,eAAO,MAAM,gCAAgC,oPAAgN,CAAC;AAoC9P;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,qCAAqC,GAC5C,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,4BAA4B,CAqBhG;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAchC;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,qCAAqC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED,iFAAiF;AACjF,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,MAAM,EAAE,qCAAqC,GAC5C,OAAO,CAAC,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,4BAA4B,CAAC,CAyDzG;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,MAAM,EAAE,qCAAqC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAczB"}
|