Skip to content

Commit 0eaca5e

Browse files
authored
Unified emulator settings for Firestore (#1690)
1 parent 71a4740 commit 0eaca5e

File tree

4 files changed

+221
-4
lines changed

4 files changed

+221
-4
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/AccessHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public static FirebaseFirestore newFirebaseFirestore(
4141
asyncQueue,
4242
firebaseApp,
4343
instanceRegistry,
44+
null,
4445
null);
4546
}
4647

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestore.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import com.google.android.gms.tasks.Tasks;
2828
import com.google.firebase.FirebaseApp;
2929
import com.google.firebase.auth.internal.InternalAuthProvider;
30+
import com.google.firebase.emulators.EmulatedServiceSettings;
31+
import com.google.firebase.emulators.EmulatorSettings;
32+
import com.google.firebase.emulators.FirebaseEmulator;
3033
import com.google.firebase.firestore.FirebaseFirestoreException.Code;
3134
import com.google.firebase.firestore.auth.CredentialsProvider;
3235
import com.google.firebase.firestore.auth.EmptyCredentialsProvider;
@@ -55,6 +58,15 @@
5558
*/
5659
public class FirebaseFirestore {
5760

61+
/**
62+
* Emulator identifier. See {@link FirebaseApp#enableEmulators(EmulatorSettings)}
63+
*
64+
* <p>TODO(samstern): Un-hide this once Firestore, Database, and Functions are implemented
65+
*
66+
* @hide
67+
*/
68+
public static FirebaseEmulator EMULATOR = FirebaseEmulator.forName("firestore");
69+
5870
/**
5971
* Provides a registry management interface for {@code FirebaseFirestore} instances.
6072
*
@@ -81,6 +93,7 @@ public interface InstanceRegistry {
8193
private FirebaseFirestoreSettings settings;
8294
private volatile FirestoreClient client;
8395
private final GrpcMetadataProvider metadataProvider;
96+
private final EmulatedServiceSettings emulatorSettings;
8497

8598
@NonNull
8699
public static FirebaseFirestore getInstance() {
@@ -144,7 +157,8 @@ static FirebaseFirestore newInstance(
144157
queue,
145158
app,
146159
instanceRegistry,
147-
metadataProvider);
160+
metadataProvider,
161+
app.getEmulatorSettings().getServiceSettings(EMULATOR));
148162
return firestore;
149163
}
150164

@@ -157,7 +171,8 @@ static FirebaseFirestore newInstance(
157171
AsyncQueue asyncQueue,
158172
@Nullable FirebaseApp firebaseApp,
159173
InstanceRegistry instanceRegistry,
160-
@Nullable GrpcMetadataProvider metadataProvider) {
174+
@Nullable GrpcMetadataProvider metadataProvider,
175+
@Nullable EmulatedServiceSettings emulatorSettings) {
161176
this.context = checkNotNull(context);
162177
this.databaseId = checkNotNull(checkNotNull(databaseId));
163178
this.userDataReader = new UserDataReader(databaseId);
@@ -168,8 +183,10 @@ static FirebaseFirestore newInstance(
168183
this.firebaseApp = firebaseApp;
169184
this.instanceRegistry = instanceRegistry;
170185
this.metadataProvider = metadataProvider;
186+
this.emulatorSettings = emulatorSettings;
171187

172-
settings = new FirebaseFirestoreSettings.Builder().build();
188+
this.settings = new FirebaseFirestoreSettings.Builder().build();
189+
this.settings = mergeEmulatorSettings(settings, emulatorSettings);
173190
}
174191

175192
/** Returns the settings used by this {@code FirebaseFirestore} object. */
@@ -185,6 +202,8 @@ public FirebaseFirestoreSettings getFirestoreSettings() {
185202
public void setFirestoreSettings(@NonNull FirebaseFirestoreSettings settings) {
186203
synchronized (databaseId) {
187204
checkNotNull(settings, "Provided settings must not be null.");
205+
settings = mergeEmulatorSettings(settings, emulatorSettings);
206+
188207
// As a special exception, don't throw if the same settings are passed repeatedly. This
189208
// should make it simpler to get a Firestore instance in an activity.
190209
if (client != null && !this.settings.equals(settings)) {
@@ -193,6 +212,7 @@ public void setFirestoreSettings(@NonNull FirebaseFirestoreSettings settings) {
193212
+ "You can only call setFirestoreSettings() before calling any other methods on a "
194213
+ "FirebaseFirestore object.");
195214
}
215+
196216
this.settings = settings;
197217
}
198218
}
@@ -215,6 +235,24 @@ private void ensureClientConfigured() {
215235
}
216236
}
217237

238+
private FirebaseFirestoreSettings mergeEmulatorSettings(
239+
@NonNull FirebaseFirestoreSettings settings,
240+
@Nullable EmulatedServiceSettings emulatorSettings) {
241+
if (emulatorSettings == null) {
242+
return settings;
243+
}
244+
245+
if (!FirebaseFirestoreSettings.DEFAULT_HOST.equals(settings.getHost())) {
246+
throw new IllegalStateException(
247+
"Cannot specify the host in FirebaseFirestoreSettings when EmulatedServiceSettings is provided.");
248+
}
249+
250+
return new FirebaseFirestoreSettings.Builder(settings)
251+
.setHost(emulatorSettings.getHost() + ":" + emulatorSettings.getPort())
252+
.setSslEnabled(false)
253+
.build();
254+
}
255+
218256
/** Returns the FirebaseApp instance to which this {@code FirebaseFirestore} belongs. */
219257
@NonNull
220258
public FirebaseApp getApp() {

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestoreSettings.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ public final class FirebaseFirestoreSettings {
2727
*/
2828
public static final long CACHE_SIZE_UNLIMITED = -1;
2929

30+
/** @hide */
31+
public static final String DEFAULT_HOST = "firestore.googleapis.com";
32+
3033
private static final long MINIMUM_CACHE_BYTES = 1 * 1024 * 1024; // 1 MB
3134
private static final long DEFAULT_CACHE_SIZE_BYTES = 100 * 1024 * 1024; // 100 MB
32-
private static final String DEFAULT_HOST = "firestore.googleapis.com";
3335
private static final boolean DEFAULT_TIMESTAMPS_IN_SNAPSHOTS_ENABLED = true;
3436

3537
/** A Builder for creating {@code FirebaseFirestoreSettings}. */
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore;
16+
17+
import static org.junit.Assert.assertEquals;
18+
import static org.junit.Assert.assertFalse;
19+
import static org.junit.Assert.assertTrue;
20+
import static org.junit.Assert.fail;
21+
22+
import androidx.annotation.NonNull;
23+
import androidx.test.platform.app.InstrumentationRegistry;
24+
import com.google.firebase.FirebaseApp;
25+
import com.google.firebase.FirebaseOptions;
26+
import com.google.firebase.emulators.EmulatedServiceSettings;
27+
import com.google.firebase.emulators.EmulatorSettings;
28+
import org.junit.Test;
29+
import org.junit.runner.RunWith;
30+
import org.robolectric.RobolectricTestRunner;
31+
import org.robolectric.annotation.Config;
32+
33+
@RunWith(RobolectricTestRunner.class)
34+
@Config(manifest = Config.NONE)
35+
public class FirebaseFirestoreTest {
36+
37+
@Test
38+
public void getInstance_withEmulator() {
39+
FirebaseApp app = getApp("getInstance_withEmulator");
40+
41+
app.enableEmulators(
42+
new EmulatorSettings.Builder()
43+
.addEmulatedService(
44+
FirebaseFirestore.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 8080))
45+
.build());
46+
47+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
48+
FirebaseFirestoreSettings settings = firestore.getFirestoreSettings();
49+
50+
assertEquals(settings.getHost(), "10.0.2.2:8080");
51+
assertFalse(settings.isSslEnabled());
52+
}
53+
54+
@Test
55+
public void getInstance_withEmulator_mergeSettingsSuccess() {
56+
FirebaseApp app = getApp("getInstance_withEmulator_mergeSettingsSuccess");
57+
app.enableEmulators(
58+
new EmulatorSettings.Builder()
59+
.addEmulatedService(
60+
FirebaseFirestore.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 8080))
61+
.build());
62+
63+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
64+
firestore.setFirestoreSettings(
65+
new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(false).build());
66+
67+
FirebaseFirestoreSettings settings = firestore.getFirestoreSettings();
68+
69+
assertEquals(settings.getHost(), "10.0.2.2:8080");
70+
assertFalse(settings.isSslEnabled());
71+
assertFalse(settings.isPersistenceEnabled());
72+
}
73+
74+
@Test
75+
public void getInstance_withEmulator_mergeSettingsFailure() {
76+
FirebaseApp app = getApp("getInstance_withEmulator_mergeSettingsFailure");
77+
app.enableEmulators(
78+
new EmulatorSettings.Builder()
79+
.addEmulatedService(
80+
FirebaseFirestore.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 8080))
81+
.build());
82+
83+
try {
84+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
85+
firestore.setFirestoreSettings(
86+
new FirebaseFirestoreSettings.Builder().setHost("myhost.com").build());
87+
fail("Exception should be thrown");
88+
} catch (Exception e) {
89+
assertTrue(e instanceof IllegalStateException);
90+
assertEquals(
91+
e.getMessage(),
92+
"Cannot specify the host in FirebaseFirestoreSettings when EmulatedServiceSettings is provided.");
93+
}
94+
}
95+
96+
@Test
97+
public void setSettings_repeatedSuccess() {
98+
FirebaseApp app = getApp("setSettings_repeatedSuccess");
99+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
100+
101+
FirebaseFirestoreSettings settings =
102+
new FirebaseFirestoreSettings.Builder().setHost("myhost.com").setSslEnabled(false).build();
103+
firestore.setFirestoreSettings(settings);
104+
105+
// This should 'start' Firestore
106+
DocumentReference reference = firestore.document("foo/bar");
107+
108+
// Second settings set should pass because the settings are equal
109+
firestore.setFirestoreSettings(settings);
110+
}
111+
112+
@Test
113+
public void setSettings_repeatedSuccess_withEmulator() {
114+
FirebaseApp app = getApp("setSettings_repeatedSuccess_withEmulator");
115+
app.enableEmulators(
116+
new EmulatorSettings.Builder()
117+
.addEmulatedService(
118+
FirebaseFirestore.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 8080))
119+
.build());
120+
121+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
122+
123+
FirebaseFirestoreSettings settings =
124+
new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(false).build();
125+
firestore.setFirestoreSettings(settings);
126+
127+
// This should 'start' Firestore
128+
DocumentReference reference = firestore.document("foo/bar");
129+
130+
// Second settings set should pass because the settings are equal
131+
firestore.setFirestoreSettings(settings);
132+
}
133+
134+
@Test
135+
public void setSettings_repeatedFailure() {
136+
FirebaseApp app = getApp("setSettings_repeatedFailure");
137+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
138+
139+
FirebaseFirestoreSettings settings =
140+
new FirebaseFirestoreSettings.Builder().setHost("myhost.com").setSslEnabled(false).build();
141+
142+
FirebaseFirestoreSettings otherSettings =
143+
new FirebaseFirestoreSettings.Builder()
144+
.setHost("otherhost.com")
145+
.setSslEnabled(false)
146+
.build();
147+
148+
firestore.setFirestoreSettings(settings);
149+
150+
// This should 'start' Firestore
151+
DocumentReference reference = firestore.document("foo/bar");
152+
153+
try {
154+
firestore.setFirestoreSettings(otherSettings);
155+
fail("Exception should be thrown");
156+
} catch (Exception e) {
157+
assertTrue(e instanceof IllegalStateException);
158+
assertTrue(
159+
e.getMessage()
160+
.startsWith(
161+
"FirebaseFirestore has already been started and its settings can no longer be changed."));
162+
}
163+
}
164+
165+
@NonNull
166+
private FirebaseApp getApp(@NonNull String name) {
167+
return FirebaseApp.initializeApp(
168+
InstrumentationRegistry.getInstrumentation().getContext(),
169+
new FirebaseOptions.Builder()
170+
.setApplicationId("appid")
171+
.setApiKey("apikey")
172+
.setProjectId("projectid")
173+
.build(),
174+
name);
175+
}
176+
}

0 commit comments

Comments
 (0)