Skip to content

Commit 0632ca5

Browse files
committed
feat(database): add database.useEmulator()
- detox needed another timer disable on android to succeed - offline / online is flaky - lots of the e2e databases tests themselves are flaky - did my best there - database rules are injected dynamically using @firebase/rules-unit-testing which is nice
1 parent d97587b commit 0632ca5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+781
-313
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"rules": {
3+
// Database in general is closed. Read/Write to anything but "tests/" will fail.
4+
".read": false,
5+
".write": false,
6+
7+
// ..."tests" node will succeed
8+
"tests": {
9+
".read": true,
10+
".write": true,
11+
}
12+
}
13+
}

.github/workflows/scripts/firebase.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
"rules": "firestore.rules",
44
"indexes": "firestore.indexes.json"
55
},
6+
"database": {
7+
"rules": "database.rules"
8+
},
69
"emulators": {
710
"auth": {
811
"port": "9099"
912
},
13+
"database": {
14+
"port": "9000"
15+
},
1016
"firestore": {
1117
"port": "8080"
1218
},

.github/workflows/scripts/start-firebase-emulator.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ if ! [ -x "$(command -v firebase)" ]; then
44
exit 1
55
fi
66

7-
EMU_START_COMMAND="firebase emulators:start --only firestore,auth --project react-native-firebase-testing"
7+
EMU_START_COMMAND="firebase emulators:start --only auth,database,firestore --project react-native-firebase-testing"
88

99
if [ "$1" == "--no-daemon" ]; then
1010
$EMU_START_COMMAND

docs/database/usage/index.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -307,34 +307,6 @@ database and the new [`DataSnapshot`](/reference/database/datasnapshot) containi
307307
It is important that you understand how to write rules in your Firebase console to ensure that your data is secure.
308308
Please follow the Firebase Realtime Database documentation on [security](https://firebase.google.com/docs/database/security)
309309

310-
# Using Emulator
311-
312-
The Realtime Database currently has no direct support to use Firebase emulator, meaning that there is no "useEmulator" function to be used.
313-
To use Firebase emulator with Realtime Database, please, refer to "Using a secondary database section" or use approach from the code snippet below:
314-
315-
```
316-
// databaseWrapper.js
317-
import {firebase} from '@react-native-firebase/database';
318-
319-
class DatabaseWrapper {
320-
constructor(){
321-
if(__DEV__){
322-
// setup correct address and port of the firebase emulator
323-
this.database = firebase.app().database("http://localhost:9000?ns=YOUR_EMULATOR_DATABASE_NAME");
324-
} else {
325-
this.database = firebase.app().database("http://yourProductionUrl?ns=YOUR_PRODUCTION_DATABASE_NAME");
326-
}
327-
}
328-
}
329-
export const RealtimeDatabase = new DatabaseWrapper();
330-
331-
332-
//fetchData.js
333-
import {RealtimeDatabase} from "./databaseWrapper.js"
334-
335-
const myRealtimeRef = RealtimeDatabase.database.ref("1234")...
336-
```
337-
338310
# Using a secondary database
339311

340312
If the default installed Firebase instance needs to address a different database within the same project, call the database method on the default app with passing the database URL.

jest.setup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ jest.doMock('react-native', () => {
4141
useEmulator: jest.fn(),
4242
},
4343
RNFBCrashlyticsModule: {},
44+
RNFBDatabaseModule: {
45+
on: jest.fn(),
46+
useEmulator: jest.fn(),
47+
},
4448
RNFBPerfModule: {},
4549
},
4650
},
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import database, { firebase } from '../lib';
2+
3+
describe('Database', function () {
4+
describe('namespace', function () {
5+
it('accessible from firebase.app()', function () {
6+
const app = firebase.app();
7+
expect(app.database).toBeDefined();
8+
expect(app.database().useEmulator).toBeDefined();
9+
});
10+
});
11+
12+
describe('useEmulator()', function () {
13+
it('useEmulator requires a string host', function () {
14+
// @ts-ignore because we pass an invalid argument...
15+
expect(() => database().useEmulator()).toThrow(
16+
'firebase.database().useEmulator() takes a non-empty host',
17+
);
18+
expect(() => database().useEmulator('', -1)).toThrow(
19+
'firebase.database().useEmulator() takes a non-empty host',
20+
);
21+
// @ts-ignore because we pass an invalid argument...
22+
expect(() => database().useEmulator(123)).toThrow(
23+
'firebase.database().useEmulator() takes a non-empty host',
24+
);
25+
});
26+
27+
it('useEmulator requires a host and port', function () {
28+
expect(() => database().useEmulator('', 9000)).toThrow(
29+
'firebase.database().useEmulator() takes a non-empty host and port',
30+
);
31+
// No port
32+
// @ts-ignore because we pass an invalid argument...
33+
expect(() => database().useEmulator('localhost')).toThrow(
34+
'firebase.database().useEmulator() takes a non-empty host and port',
35+
);
36+
});
37+
38+
it('useEmulator -> remaps Android loopback to host', function () {
39+
const foo = database().useEmulator('localhost', 9000);
40+
expect(foo).toEqual(['10.0.2.2', 9000]);
41+
42+
const bar = database().useEmulator('127.0.0.1', 9000);
43+
expect(bar).toEqual(['10.0.2.2', 9000]);
44+
});
45+
});
46+
});

packages/database/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseCommon.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
public class UniversalFirebaseDatabaseCommon {
2929
private static HashMap<String, Boolean> configSettingsLock = new HashMap<>();
30+
private static HashMap<String, HashMap<String, Object>> emulatorConfigs = new HashMap<>();
3031

3132
static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
3233
FirebaseDatabase firebaseDatabase;
@@ -45,6 +46,11 @@ static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
4546

4647
setDatabaseConfig(firebaseDatabase, appName, dbURL);
4748

49+
HashMap emulatorConfig = getEmulatorConfig(appName, dbURL);
50+
if (emulatorConfig != null) {
51+
firebaseDatabase.useEmulator((String)emulatorConfig.get("host"), (Integer)emulatorConfig.get("port"));
52+
}
53+
4854
return firebaseDatabase;
4955
}
5056

@@ -83,4 +89,17 @@ private static void setDatabaseConfig(FirebaseDatabase firebaseDatabase, String
8389

8490
configSettingsLock.put(lockKey, true);
8591
}
92+
93+
static void addEmulatorConfig(String appName, String dbURL, String host, int port) {
94+
System.err.println("adding emulator config");
95+
String configKey = appName + dbURL;
96+
HashMap<String, Object> emulatorConfig = new HashMap<>();
97+
emulatorConfig.put("host", host);
98+
emulatorConfig.put("port", new Integer(port));
99+
emulatorConfigs.put(configKey, emulatorConfig);
100+
}
101+
102+
private static HashMap<String, Object> getEmulatorConfig(String appName, String dbURL) {
103+
return emulatorConfigs.get(appName + dbURL);
104+
}
86105
}

packages/database/android/src/main/java/io/invertase/firebase/database/UniversalFirebaseDatabaseModule.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public class UniversalFirebaseDatabaseModule extends UniversalFirebaseModule {
3030
super(context, serviceName);
3131
}
3232

33-
3433
Task<Void> goOnline(String appName, String dbURL) {
3534
return Tasks.call(() -> {
3635
getDatabaseForApp(appName, dbURL).goOnline();

packages/database/android/src/reactnative/java/io/invertase/firebase/database/ReactNativeFirebaseDatabaseModule.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import io.invertase.firebase.common.ReactNativeFirebaseModule;
2424
import io.invertase.firebase.common.UniversalFirebasePreferences;
2525

26+
import static io.invertase.firebase.database.UniversalFirebaseDatabaseCommon.addEmulatorConfig;
27+
2628
public class ReactNativeFirebaseDatabaseModule extends ReactNativeFirebaseModule {
2729
private static final String SERVICE_NAME = "Database";
2830
private final UniversalFirebaseDatabaseModule module;
@@ -77,4 +79,9 @@ public void setPersistenceCacheSizeBytes(String app, String dbURL, double cacheS
7779
(long) cacheSizeBytes
7880
);
7981
}
82+
83+
@ReactMethod
84+
public void useEmulator(String app, String dbURL, String host, int port) {
85+
addEmulatorConfig(app, dbURL, host, port);
86+
}
8087
}

packages/database/e2e/helpers.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
const testingUtils = require('@firebase/rules-unit-testing');
2+
13
// TODO make more unique?
24
const ID = Date.now();
35

46
const PATH = `tests/${ID}`;
7+
const DB_NAME = 'react-native-firebase-testing';
8+
const DB_RULES = `{ "rules": {".read": false, ".write": false, "tests": {".read": true, ".write": true } } }`;
59

610
const CONTENT = {
711
TYPES: {
@@ -34,6 +38,8 @@ exports.seed = function seed(path) {
3438
return Promise.all([
3539
firebase.database().ref(`${path}/types`).set(CONTENT.TYPES),
3640
firebase.database().ref(`${path}/query`).set(CONTENT.QUERY),
41+
// The database emulator does not load rules correctly. We force them pre-test.
42+
testingUtils.loadDatabaseRules({ databaseName: DB_NAME, rules: DB_RULES }),
3743
]);
3844
};
3945

@@ -43,3 +49,4 @@ exports.wipe = function wipe(path) {
4349

4450
exports.PATH = PATH;
4551
exports.CONTENT = CONTENT;
52+
exports.DB_RULES = DB_RULES;

0 commit comments

Comments
 (0)