Commit 2626667
authored
fix(firestore-bigquery-export): accept ISO 8601 string partition values (#2813)
* fix(firestore-bigquery-export): accept ISO 8601 string partition values
Restores 0.2.x behavior for Firestore string partition fields. The 0.3.0
refactor of PartitionValueConverter narrowed accepted inputs to Firestore
Timestamp / timestamp-like / Date, silently coercing strings (including
ISO 8601 dates such as "2026-01-01") to NULL and landing rows in the
__NULL__ partition.
PartitionValueConverter.convert now parses strings via new Date(value).
Unparseable strings still return null and trigger the existing warning.
Adds a defensive try/catch around the BigQuery formatter switch so any
serialization failure degrades to null + warn rather than crashing the
row write.
Fixes #2803
* fix(firestore-bigquery-export): explicit default in partition value switch
Adds default: return null to PartitionValueConverter.convert. fieldType is
typed as a strict union, so exhaustiveness is already enforced at compile
time, but the value comes from external config — a runtime mismatch falls
through cleanly to null instead of returning undefined.
Per gemini-code-assist review on #2813.
* chore(firestore-bigquery-export): bump version to 0.3.2
* fix(firestore-bigquery-export): strict ISO 8601 validation for partition strings
JavaScript's Date parser is too permissive for partition value validation:
new Date("2024-02-30") -> 2024-03-01 (silent month overflow)
new Date("2024-01") -> 2024-01-01 (silent partial-date fill)
new Date("1") -> 2001-01-01 (bare numeric as year)
new Date("2023-02-29") -> 2023-03-01 (non-leap-year overflow)
new Date("2024-01-15T10:30") -> engine-dependent local-time interpretation
Replaces the loose new Date() check with a strict YYYY-MM-DD prefix regex
that requires an explicit timezone designator (Z or +/-HH:MM) when a time
component is present, plus a calendar validator built via setUTCFullYear
that rejects calendar-invalid components like Feb 30, non-leap-year Feb 29,
month 13, and day 32.
Per CorieW review on #2813.
* fix(firestore-bigquery-export): reject year 0 in partition strings
BigQuery DATE / DATETIME / TIMESTAMP all reject year 0 — the supported
range is 0001-01-01 to 9999-12-31. The previous strict validator passed
"0000-01-01" through (setUTCFullYear(0, 0, 1) yields year 0, matching
input components) but BigQuery rejects it server-side, causing the same
NULL-partition symptom this PR is fixing.
Reject yearN < 1 client-side so the user gets a clear warning log.
* test(firestore-bigquery-export): cover partition string edge cases
Adds 4 cases to PartitionValueConverter TIMESTAMP suite:
- out-of-range hour ("25:00:00Z") -> null
- out-of-range minute ("23:60:00Z") -> null
- timezone offset without colon ("+0800") -> accepted
- fractional seconds beyond millisecond precision -> accepted
The first two pin the implicit time-validation behavior (regex matches
H/M/S as 2-digit pairs, then new Date() rejects out-of-range values).
The last two document accepted RFC 3339 variants.
* test(firestore-bigquery-export): pin DATETIME canonical output form
Adds a strict-equality test that asserts the DATETIME converter outputs
"2024-01-15 10:30:00.000" (space separator, no Z) when fed an ISO 8601
string with a Z suffix. The existing tests only checked that the result
contained "2024-01-15", which left the canonical-form contract unpinned.
@google-cloud/bigquery's BigQuery.datetime() already normalises Z-suffixed
input to BigQuery's canonical DATETIME form, so the production code is
correct. This test guards against future regressions.
* test(firestore-bigquery-export): cover space-separated partition strings
The regex in PartitionValueConverter accepts both "T" and a literal space
as the date/time separator (RFC 3339 allows either), but no test covered
the space form. Pinned so the contract is explicit.
* fix(firestore-bigquery-export): reject hour 24 in partition strings
ISO 8601 allows "24:00:00" as an alias for next-day midnight, and JS Date
parses it that way. The new string parsing path in 0.3.2 would therefore
silently roll inputs like "2024-01-15T24:00:00Z" forward to the
"2024-01-16" partition. That is a regression vs 0.2.x, where the raw
string was passed straight to BigQuery and BigQuery rejected hour 24
outright, surfacing the bad row instead of misfiling it.
Reject hour 24 explicitly so the caller logs firestoreTimePartitionFieldError
and the row lands in the __NULL__ partition (loud failure, matching the
calendar-validator philosophy already applied to Feb 30 etc.). Minute
and second out-of-range values are still caught by new Date() returning
Invalid Date in the existing check below.
Adds a test pinning the hour 24 rejection.1 parent a675f5c commit 2626667
4 files changed
Lines changed: 221 additions & 10 deletions
File tree
- firestore-bigquery-export
- firestore-bigquery-change-tracker/src
- __tests__/bigquery/partitioning
- bigquery/partitioning
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
1 | 5 | | |
2 | 6 | | |
3 | 7 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
Lines changed: 155 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
46 | 51 | | |
47 | | - | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
48 | 144 | | |
49 | 145 | | |
50 | 146 | | |
| |||
104 | 200 | | |
105 | 201 | | |
106 | 202 | | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
107 | 229 | | |
108 | 230 | | |
109 | 231 | | |
| |||
134 | 256 | | |
135 | 257 | | |
136 | 258 | | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
137 | 290 | | |
138 | 291 | | |
firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/partitioning/converter.ts
Lines changed: 61 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
30 | 78 | | |
31 | 79 | | |
32 | 80 | | |
33 | 81 | | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
41 | 95 | | |
42 | 96 | | |
43 | 97 | | |
0 commit comments