Skip to content

Commit cbe2593

Browse files
feat(firestore): variable() and arrayFilter() API features for pipelines (#9017)
Also: * test(firestore): add docs and index / deploy tools for pipeline e2e db * fix(firestore, ios): normalize findNearest distanceMeasure for native bridge * fix(firestore): export nor and fix iOS boolean pipeline coercion * test(firestore): expand pipeline e2e and native parity coverage * fix(pipelines): distanceMeasure lowercase public API / casing handled internally * test(firestore): split emulator and deploy Firebase configs The Firestore emulator ignores security rules when firebase.json uses the multi-database array format, defaulting to allow-all and breaking no_rules e2e tests. Keep single-database firebase.json for emulator/CI and move multi-database deploy config to firebase.deploy.json. --------- Co-authored-by: Mike Hardy <github@mikehardy.net>
1 parent d3c4110 commit cbe2593

34 files changed

Lines changed: 2896 additions & 172 deletions

.github/scripts/compare-types/configs/firestore-pipelines.ts

Lines changed: 34 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,117 +19,57 @@
1919

2020
import type { PackageConfig } from '../src/types';
2121

22-
2322
const config: PackageConfig = {
2423
nameMapping: {},
2524
missingInRN: [
26-
{
27-
name: 'arrayFilter',
28-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
29-
},
30-
{
31-
name: 'arrayFirst',
32-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
33-
},
34-
{
35-
name: 'arrayFirstN',
36-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
37-
},
38-
{
39-
name: 'arrayIndexOf',
40-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
41-
},
42-
{
43-
name: 'arrayIndexOfAll',
44-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
45-
},
46-
{
47-
name: 'arrayLast',
48-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
49-
},
50-
{
51-
name: 'arrayLastIndexOf',
52-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
53-
},
54-
{
55-
name: 'arrayLastN',
56-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
57-
},
58-
{
59-
name: 'arrayMaximum',
60-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
61-
},
62-
{
63-
name: 'arrayMaximumN',
64-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
65-
},
66-
{
67-
name: 'arrayMinimum',
68-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
69-
},
70-
{
71-
name: 'arrayMinimumN',
72-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
73-
},
74-
{
75-
name: 'arraySlice',
76-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
77-
},
78-
{
79-
name: 'arrayTransform',
80-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
81-
},
82-
{
83-
name: 'arrayTransformWithIndex',
84-
reason: 'Newer firebase-js-sdk array expression helper not yet exposed by RN Firebase pipelines.',
85-
},
8625
{
8726
name: 'coalesce',
8827
reason: 'Newer firebase-js-sdk expression helper not yet exposed by RN Firebase pipelines.',
8928
},
9029
{
9130
name: 'currentDocument',
92-
reason: 'Newer firebase-js-sdk document expression helper not yet exposed by RN Firebase pipelines.',
31+
reason:
32+
'Newer firebase-js-sdk document expression helper not yet exposed by RN Firebase pipelines.',
9333
},
9434
{
9535
name: 'documentMatches',
96-
reason: 'Newer firebase-js-sdk document expression helper not yet exposed by RN Firebase pipelines.',
36+
reason:
37+
'Newer firebase-js-sdk document expression helper not yet exposed by RN Firebase pipelines.',
9738
},
9839
{
9940
name: 'geoDistance',
100-
reason: 'Newer firebase-js-sdk geospatial expression helper not yet exposed by RN Firebase pipelines.',
41+
reason:
42+
'Newer firebase-js-sdk geospatial expression helper not yet exposed by RN Firebase pipelines.',
10143
},
10244
{
10345
name: 'ifNull',
104-
reason: 'Newer firebase-js-sdk null-handling expression helper not yet exposed by RN Firebase pipelines.',
105-
},
106-
{
107-
name: 'nor',
108-
reason: 'Newer firebase-js-sdk boolean expression helper not yet exposed by RN Firebase pipelines.',
46+
reason:
47+
'Newer firebase-js-sdk null-handling expression helper not yet exposed by RN Firebase pipelines.',
10948
},
11049
{
11150
name: 'score',
112-
reason: 'Newer firebase-js-sdk search score expression helper not yet exposed by RN Firebase pipelines.',
51+
reason:
52+
'Newer firebase-js-sdk search score expression helper not yet exposed by RN Firebase pipelines.',
11353
},
11454
{
11555
name: 'subcollection',
116-
reason: 'Newer firebase-js-sdk subcollection stage helper not yet exposed by RN Firebase pipelines.',
56+
reason:
57+
'Newer firebase-js-sdk subcollection stage helper not yet exposed by RN Firebase pipelines.',
11758
},
11859
{
11960
name: 'switchOn',
120-
reason: 'Newer firebase-js-sdk conditional expression helper not yet exposed by RN Firebase pipelines.',
61+
reason:
62+
'Newer firebase-js-sdk conditional expression helper not yet exposed by RN Firebase pipelines.',
12163
},
12264
{
12365
name: 'timestampDiff',
124-
reason: 'Newer firebase-js-sdk timestamp expression helper not yet exposed by RN Firebase pipelines.',
66+
reason:
67+
'Newer firebase-js-sdk timestamp expression helper not yet exposed by RN Firebase pipelines.',
12568
},
12669
{
12770
name: 'timestampExtract',
128-
reason: 'Newer firebase-js-sdk timestamp expression helper not yet exposed by RN Firebase pipelines.',
129-
},
130-
{
131-
name: 'variable',
132-
reason: 'Newer firebase-js-sdk variable expression helper not yet exposed by RN Firebase pipelines.',
71+
reason:
72+
'Newer firebase-js-sdk timestamp expression helper not yet exposed by RN Firebase pipelines.',
13373
},
13474
{
13575
name: 'DefineStageOptions',
@@ -141,15 +81,18 @@ const config: PackageConfig = {
14181
},
14282
{
14383
name: 'SearchStageOptions',
144-
reason: 'Newer firebase-js-sdk search stage options type not yet exposed by RN Firebase pipelines.',
84+
reason:
85+
'Newer firebase-js-sdk search stage options type not yet exposed by RN Firebase pipelines.',
14586
},
14687
{
14788
name: 'SubcollectionStageOptions',
148-
reason: 'Newer firebase-js-sdk subcollection stage options type not yet exposed by RN Firebase pipelines.',
89+
reason:
90+
'Newer firebase-js-sdk subcollection stage options type not yet exposed by RN Firebase pipelines.',
14991
},
15092
{
15193
name: 'TimePart',
152-
reason: 'Newer firebase-js-sdk timestamp extraction type not yet exposed by RN Firebase pipelines.',
94+
reason:
95+
'Newer firebase-js-sdk timestamp extraction type not yet exposed by RN Firebase pipelines.',
15396
},
15497
{
15598
name: 'TimeUnit',
@@ -159,7 +102,8 @@ const config: PackageConfig = {
159102
extraInRN: [
160103
{
161104
name: 'Type',
162-
reason: 'RN Firebase exposes a local type discriminator alias for pipeline expression helpers.',
105+
reason:
106+
'RN Firebase exposes a local type discriminator alias for pipeline expression helpers.',
163107
},
164108
],
165109
differentShape: [
@@ -170,19 +114,23 @@ const config: PackageConfig = {
170114
},
171115
{
172116
name: 'isType',
173-
reason: 'RN Firebase accepts its local `Type` alias where the firebase-js-sdk declaration accepts a string.',
117+
reason:
118+
'RN Firebase accepts its local `Type` alias where the firebase-js-sdk declaration accepts a string.',
174119
},
175120
{
176121
name: 'ExpressionType',
177-
reason: 'RN Firebase has not yet exposed the newer firebase-js-sdk `Variable` and `PipelineValue` expression kinds.',
122+
reason:
123+
'RN Firebase has not yet exposed the newer firebase-js-sdk `PipelineValue` expression kind.',
178124
},
179125
{
180126
name: 'StageOptions',
181-
reason: 'Declaration formatting differs for the raw options object, but the public shape is equivalent.',
127+
reason:
128+
'Declaration formatting differs for the raw options object, but the public shape is equivalent.',
182129
},
183130
{
184131
name: 'TimeGranularity',
185-
reason: 'RN Firebase uses the existing `isoWeek` and `isoYear` casing while the firebase-js-sdk declaration includes lowercase variants.',
132+
reason:
133+
'RN Firebase uses the existing `isoWeek` and `isoYear` casing while the firebase-js-sdk declaration includes lowercase variants.',
186134
},
187135
],
188136
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Firestore rules and indexes (cloud + emulator)
2+
3+
Configuration lives in this directory. **Emulator and cloud deploy use different Firebase config files** because the local Firestore emulator does not support the multi-database `firebase.json` array format ([firebase-tools#9742](https://github.com/firebase/firebase-tools/issues/9742)): it ignores rules and defaults to allowing all reads/writes.
4+
5+
| File | Purpose |
6+
|------|---------|
7+
| `firebase.json` | **Emulator + CI** — single-database `(default)` Firestore config |
8+
| `firebase.deploy.json` | **Cloud deploy only** — multi-database `(default)` + `pipelines-e2e` |
9+
10+
## Databases
11+
12+
| Database | Rules file | Indexes file | Used by |
13+
|----------|------------|--------------|---------|
14+
| `(default)` | `firestore.rules` | `firestore.indexes.json` | Standard Firestore e2e (emulator + cloud) |
15+
| `pipelines-e2e` | `firestore.pipelines-e2e.rules` | `firestore.pipelines-e2e.indexes.json` | `Pipeline.e2e.js` (Enterprise cloud only) |
16+
17+
`firebase.deploy.json` uses the **multi-database array** format:
18+
19+
```json
20+
"firestore": [
21+
{ "database": "(default)", "rules": "firestore.rules", "indexes": "firestore.indexes.json" },
22+
{ "database": "pipelines-e2e", "rules": "firestore.pipelines-e2e.rules", "indexes": "firestore.pipelines-e2e.indexes.json" }
23+
]
24+
```
25+
26+
`firebase.json` keeps the **legacy single-object** format required by the emulator:
27+
28+
```json
29+
"firestore": {
30+
"rules": "firestore.rules",
31+
"indexes": "firestore.indexes.json"
32+
}
33+
```
34+
35+
## Pull current cloud state
36+
37+
```bash
38+
cd .github/workflows/scripts
39+
./sync-firestore-indexes.sh
40+
```
41+
42+
Or manually:
43+
44+
```bash
45+
firebase firestore:indexes --project react-native-firebase-testing --database "(default)" > firestore.indexes.json
46+
firebase firestore:indexes --project react-native-firebase-testing --database pipelines-e2e > firestore.pipelines-e2e.indexes.json
47+
```
48+
49+
There is no `firebase firestore:rules` pull command; edit the `.rules` files in-repo and deploy.
50+
51+
## Deploy to cloud
52+
53+
```bash
54+
cd .github/workflows/scripts
55+
./deploy-firestore.sh
56+
```
57+
58+
Uses `firebase deploy --only firestore --config firebase.deploy.json` (not `firestore:indexes` / `firestore:rules` sub-targets — those can silently no-op with multi-database config).
59+
60+
**Vector indexes** belong in `firestore.pipelines-e2e.indexes.json` under `vectorConfig`, not in security rules.
61+
62+
## pipelines-e2e notes
63+
64+
- Enterprise database on project `react-native-firebase-testing` (location `eur3`).
65+
- Pipeline Detox tests do **not** call `connectFirestoreEmulator` for this database — they hit cloud.
66+
- `firestore.pipelines-e2e.rules` is intentionally permissive for shared CI/local testing.
67+
68+
Full context: [okf-bundle Firebase testing project doc](../../../okf-bundle/testing/firebase-testing-project.md).
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
cd "$SCRIPT_DIR"
6+
7+
# shellcheck source=firebase-cli.sh
8+
source "$SCRIPT_DIR/firebase-cli.sh"
9+
10+
PROJECT="${FIREBASE_PROJECT:-react-native-firebase-testing}"
11+
12+
echo "Deploying Firestore rules and indexes to project: ${PROJECT}"
13+
echo " (default) -> firestore.rules + firestore.indexes.json"
14+
echo " pipelines-e2e -> firestore.pipelines-e2e.rules + firestore.pipelines-e2e.indexes.json"
15+
echo ""
16+
echo "Tip: run ./sync-firestore-indexes.sh first if cloud may have indexes not in repo."
17+
echo ""
18+
19+
"${FIREBASE_CMD[@]}" use "$PROJECT"
20+
# Multi-database Firestore config lives in firebase.deploy.json (not firebase.json).
21+
# firebase.json must stay single-database so the local emulator loads security rules.
22+
# Use full firestore target (not firestore:rules / firestore:indexes) — sub-targets can
23+
# silently no-op with multi-database config.
24+
"${FIREBASE_CMD[@]}" deploy --only firestore --config firebase.deploy.json
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
# Resolve firebase-tools for scripts in this directory.
3+
# Source from sibling scripts: source "$(dirname "${BASH_SOURCE[0]}")/firebase-cli.sh"
4+
#
5+
# Preference order:
6+
# 1. repo root node_modules/.bin/firebase (yarn install at repo root)
7+
# 2. functions/node_modules/.bin/firebase (yarn in functions/)
8+
# 3. yarn firebase (same as start-firebase-emulator.bat)
9+
# 4. global firebase on PATH
10+
# 5. npx firebase-tools
11+
12+
_firebase_scripts_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
_firebase_repo_root="$(cd "$_firebase_scripts_dir/../../.." && pwd)"
14+
15+
if [ -x "$_firebase_repo_root/node_modules/.bin/firebase" ]; then
16+
FIREBASE_CMD=("$_firebase_repo_root/node_modules/.bin/firebase")
17+
elif [ -x "$_firebase_scripts_dir/functions/node_modules/.bin/firebase" ]; then
18+
FIREBASE_CMD=("$_firebase_scripts_dir/functions/node_modules/.bin/firebase")
19+
elif command -v yarn >/dev/null 2>&1; then
20+
FIREBASE_CMD=(yarn firebase)
21+
elif command -v firebase >/dev/null 2>&1; then
22+
FIREBASE_CMD=(firebase)
23+
elif command -v npx >/dev/null 2>&1; then
24+
FIREBASE_CMD=(npx --yes firebase-tools)
25+
else
26+
echo "❌ Firebase-tools CLI is missing. Run 'yarn' at the repo root, or install firebase-tools globally." >&2
27+
exit 1
28+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"firestore": [
3+
{
4+
"database": "(default)",
5+
"rules": "firestore.rules",
6+
"indexes": "firestore.indexes.json"
7+
},
8+
{
9+
"database": "pipelines-e2e",
10+
"rules": "firestore.pipelines-e2e.rules",
11+
"indexes": "firestore.pipelines-e2e.indexes.json"
12+
}
13+
]
14+
}

0 commit comments

Comments
 (0)