Skip to content

Commit 79b0493

Browse files
authored
Implement useEmulator for Firestore (#3909)
1 parent 8939aec commit 79b0493

File tree

7 files changed

+84
-3
lines changed

7 files changed

+84
-3
lines changed

.changeset/short-icons-travel.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'firebase': minor
3+
'@firebase/firestore': minor
4+
'@firebase/firestore-types': minor
5+
---
6+
7+
Add a useEmulator(host, port) method to Firestore

packages/firebase/index.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -8030,6 +8030,16 @@ declare namespace firebase.firestore {
80308030
*/
80318031
settings(settings: Settings): void;
80328032

8033+
/**
8034+
* Modify this instance to communicate with the Cloud Firestore emulator.
8035+
*
8036+
* <p>Note: this must be called before this instance has been used to do any operations.
8037+
*
8038+
* @param host the emulator host (ex: localhost).
8039+
* @param port the emulator port (ex: 9000).
8040+
*/
8041+
useEmulator(host: string, port: number): void;
8042+
80338043
/**
80348044
* Attempts to enable persistent storage, if possible.
80358045
*

packages/firestore-types/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export class FirebaseFirestore {
6161

6262
settings(settings: Settings): void;
6363

64+
useEmulator(host: string, port: number): void;
65+
6466
enablePersistence(settings?: PersistenceSettings): Promise<void>;
6567

6668
collection(collectionPath: string): CollectionReference<DocumentData>;

packages/firestore/exp/test/shim.ts

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export class FirebaseFirestore
9999
initializeFirestore(this.app._delegate, settings);
100100
}
101101

102+
useEmulator(host: string, port: number): void {
103+
this.settings({ host: `${host}:${port}`, ssl: false, merge: true });
104+
}
105+
102106
enablePersistence(settings?: legacy.PersistenceSettings): Promise<void> {
103107
return settings?.synchronizeTabs
104108
? enableMultiTabIndexedDbPersistence(this._delegate)

packages/firestore/src/api/database.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ import {
111111
valueDescription,
112112
validateIsNotUsedTogether
113113
} from '../util/input_validation';
114-
import { logError, setLogLevel as setClientLogLevel } from '../util/log';
114+
import {
115+
setLogLevel as setClientLogLevel,
116+
logWarn
117+
} from '../util/log';
115118
import { AutoId } from '../util/misc';
116119
import { Deferred } from '../util/promise';
117120
import { FieldPath as ExternalFieldPath } from './field_path';
@@ -503,7 +506,7 @@ export class Firestore implements PublicFirestore, FirebaseService {
503506
throw new FirestoreError(
504507
Code.FAILED_PRECONDITION,
505508
'Firestore has already been started and its settings can no longer ' +
506-
'be changed. You can only call settings() before calling any other ' +
509+
'be changed. You can only modify settings before calling any other ' +
507510
'methods on a Firestore object.'
508511
);
509512
}
@@ -514,6 +517,24 @@ export class Firestore implements PublicFirestore, FirebaseService {
514517
}
515518
}
516519

520+
useEmulator(host: string, port: number): void {
521+
validateExactNumberOfArgs('Firestore.useEmulator', arguments, 2);
522+
validateArgType('Firestore.useEmulator', 'string', 1, host);
523+
validateArgType('Firestore.useEmulator', 'number', 2, port);
524+
525+
if (this._settings.host !== DEFAULT_HOST) {
526+
logWarn(
527+
'Host has been set in both settings() and useEmulator(), emulator host will be used'
528+
);
529+
}
530+
531+
this.settings({
532+
host: `${host}:${port}`,
533+
ssl: false,
534+
merge: true
535+
});
536+
}
537+
517538
enableNetwork(): Promise<void> {
518539
this.ensureClientConfigured();
519540
return this._firestoreClient!.enableNetwork();

packages/firestore/test/integration/api/validation.test.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ apiDescribe('Validation:', (persistence: boolean) => {
159159
'getFirestore()';
160160
} else {
161161
errorMsg +=
162-
'You can only call settings() before calling any other ' +
162+
'You can only modify settings before calling any other ' +
163163
'methods on a Firestore object.';
164164
}
165165

@@ -182,6 +182,24 @@ apiDescribe('Validation:', (persistence: boolean) => {
182182
// Verify that this doesn't throw.
183183
db.settings({ cacheSizeBytes: /* CACHE_SIZE_UNLIMITED= */ -1 });
184184
});
185+
186+
validationIt(persistence, 'useEmulator can set host and port', () => {
187+
const db = newTestFirestore('test-project');
188+
// Verify that this doesn't throw.
189+
db.useEmulator('localhost', 9000);
190+
});
191+
192+
validationIt(
193+
persistence,
194+
'disallows calling useEmulator after use',
195+
async db => {
196+
const errorMsg =
197+
'Firestore has already been started and its settings can no longer be changed.';
198+
199+
await db.doc('foo/bar').set({});
200+
expect(() => db.useEmulator('localhost', 9000)).to.throw(errorMsg);
201+
}
202+
);
185203
});
186204

187205
describe('Firestore', () => {

packages/firestore/test/unit/api/database.test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,23 @@ describe('Settings', () => {
195195
expect(firestoreClient._getSettings().ignoreUndefinedProperties).to.be.true;
196196
expect(firestoreClient._getSettings().host).to.equal('other.host');
197197
});
198+
199+
it('gets settings from useEmulator', () => {
200+
// Use a new instance of Firestore in order to configure settings.
201+
const firestoreClient = newTestFirestore();
202+
firestoreClient.useEmulator('localhost', 9000);
203+
204+
expect(firestoreClient._getSettings().host).to.equal('localhost:9000');
205+
expect(firestoreClient._getSettings().ssl).to.be.false;
206+
});
207+
208+
it('prefers host from useEmulator to host from settings', () => {
209+
// Use a new instance of Firestore in order to configure settings.
210+
const firestoreClient = newTestFirestore();
211+
firestoreClient.settings({ host: 'other.host' });
212+
firestoreClient.useEmulator('localhost', 9000);
213+
214+
expect(firestoreClient._getSettings().host).to.equal('localhost:9000');
215+
expect(firestoreClient._getSettings().ssl).to.be.false;
216+
});
198217
});

0 commit comments

Comments
 (0)