Skip to content

Commit f54d96a

Browse files
authored
Add dynamic module support to Firestore (#2432)
1 parent f56a977 commit f54d96a

File tree

14 files changed

+469
-52
lines changed

14 files changed

+469
-52
lines changed

firebase-firestore/firebase-firestore.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ dependencies {
129129
implementation 'com.google.android.gms:play-services-base:17.0.0'
130130

131131
implementation 'com.squareup.okhttp:okhttp:2.7.5'
132-
implementation('com.google.firebase:firebase-auth-interop:18.0.0') {
132+
implementation('com.google.firebase:firebase-auth-interop:19.0.2') {
133133
exclude group: "com.google.firebase", module: "firebase-common"
134134
}
135135

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/remote/StreamTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
import androidx.test.core.app.ApplicationProvider;
3030
import androidx.test.ext.junit.runners.AndroidJUnit4;
3131
import com.google.android.gms.tasks.Task;
32-
import com.google.firebase.firestore.auth.EmptyCredentialsProvider;
3332
import com.google.firebase.firestore.model.SnapshotVersion;
3433
import com.google.firebase.firestore.model.mutation.Mutation;
3534
import com.google.firebase.firestore.model.mutation.MutationResult;
35+
import com.google.firebase.firestore.testutil.EmptyCredentialsProvider;
3636
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
3737
import com.google.firebase.firestore.util.AsyncQueue;
3838
import com.google.firebase.firestore.util.AsyncQueue.TimerId;

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import com.google.firebase.firestore.ListenerRegistration;
3737
import com.google.firebase.firestore.MetadataChanges;
3838
import com.google.firebase.firestore.QuerySnapshot;
39-
import com.google.firebase.firestore.auth.EmptyCredentialsProvider;
4039
import com.google.firebase.firestore.auth.User;
4140
import com.google.firebase.firestore.core.DatabaseInfo;
4241
import com.google.firebase.firestore.local.Persistence;

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import com.google.firebase.emulators.EmulatedServiceSettings;
3232
import com.google.firebase.firestore.FirebaseFirestoreException.Code;
3333
import com.google.firebase.firestore.auth.CredentialsProvider;
34-
import com.google.firebase.firestore.auth.EmptyCredentialsProvider;
3534
import com.google.firebase.firestore.auth.FirebaseAuthCredentialsProvider;
3635
import com.google.firebase.firestore.core.ActivityScope;
3736
import com.google.firebase.firestore.core.AsyncEventListener;
@@ -48,6 +47,7 @@
4847
import com.google.firebase.firestore.util.Function;
4948
import com.google.firebase.firestore.util.Logger;
5049
import com.google.firebase.firestore.util.Logger.Level;
50+
import com.google.firebase.inject.Deferred;
5151
import java.io.ByteArrayInputStream;
5252
import java.io.InputStream;
5353
import java.nio.ByteBuffer;
@@ -117,7 +117,7 @@ private static FirebaseFirestore getInstance(@NonNull FirebaseApp app, @NonNull
117117
static FirebaseFirestore newInstance(
118118
@NonNull Context context,
119119
@NonNull FirebaseApp app,
120-
@Nullable InternalAuthProvider authProvider,
120+
@NonNull Deferred<InternalAuthProvider> authProvider,
121121
@NonNull String database,
122122
@NonNull InstanceRegistry instanceRegistry,
123123
@Nullable GrpcMetadataProvider metadataProvider) {
@@ -129,13 +129,7 @@ static FirebaseFirestore newInstance(
129129

130130
AsyncQueue queue = new AsyncQueue();
131131

132-
CredentialsProvider provider;
133-
if (authProvider == null) {
134-
Logger.debug(TAG, "Firebase Auth not available, falling back to unauthenticated usage.");
135-
provider = new EmptyCredentialsProvider();
136-
} else {
137-
provider = new FirebaseAuthCredentialsProvider(authProvider);
138-
}
132+
CredentialsProvider provider = new FirebaseAuthCredentialsProvider(authProvider);
139133

140134
// Firestore uses a different database for each app name. Note that we don't use
141135
// app.getPersistenceKey() here because it includes the application ID which is related

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.firebase.FirebaseOptions;
2525
import com.google.firebase.auth.internal.InternalAuthProvider;
2626
import com.google.firebase.firestore.remote.GrpcMetadataProvider;
27+
import com.google.firebase.inject.Deferred;
2728
import java.util.ArrayList;
2829
import java.util.HashMap;
2930
import java.util.Map;
@@ -40,13 +41,13 @@ class FirestoreMultiDbComponent
4041

4142
private final FirebaseApp app;
4243
private final Context context;
43-
private final InternalAuthProvider authProvider;
44+
private final Deferred<InternalAuthProvider> authProvider;
4445
private final GrpcMetadataProvider metadataProvider;
4546

4647
FirestoreMultiDbComponent(
4748
@NonNull Context context,
4849
@NonNull FirebaseApp app,
49-
@Nullable InternalAuthProvider authProvider,
50+
@NonNull Deferred<InternalAuthProvider> authProvider,
5051
@Nullable GrpcMetadataProvider metadataProvider) {
5152
this.context = context;
5253
this.app = app;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ public List<Component<?>> getComponents() {
4747
.add(Dependency.required(Context.class))
4848
.add(Dependency.optionalProvider(HeartBeatInfo.class))
4949
.add(Dependency.optionalProvider(UserAgentPublisher.class))
50-
.add(Dependency.optional(InternalAuthProvider.class))
50+
.add(Dependency.deferred(InternalAuthProvider.class))
5151
.add(Dependency.optional(FirebaseOptions.class))
5252
.factory(
5353
c ->
5454
new FirestoreMultiDbComponent(
5555
c.get(Context.class),
5656
c.get(FirebaseApp.class),
57-
c.get(InternalAuthProvider.class),
57+
c.getDeferred(InternalAuthProvider.class),
5858
new FirebaseClientGrpcMetadataProvider(
5959
c.getProvider(UserAgentPublisher.class),
6060
c.getProvider(HeartBeatInfo.class),

firebase-firestore/src/main/java/com/google/firebase/firestore/auth/FirebaseAuthCredentialsProvider.java

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@
1414

1515
package com.google.firebase.firestore.auth;
1616

17+
import android.annotation.SuppressLint;
18+
import androidx.annotation.GuardedBy;
1719
import androidx.annotation.NonNull;
1820
import androidx.annotation.Nullable;
1921
import com.google.android.gms.tasks.Task;
2022
import com.google.android.gms.tasks.Tasks;
23+
import com.google.firebase.FirebaseApiNotAvailableException;
2124
import com.google.firebase.FirebaseApp;
2225
import com.google.firebase.auth.GetTokenResult;
2326
import com.google.firebase.auth.internal.IdTokenListener;
2427
import com.google.firebase.auth.internal.InternalAuthProvider;
2528
import com.google.firebase.firestore.util.Executors;
2629
import com.google.firebase.firestore.util.Listener;
2730
import com.google.firebase.firestore.util.Logger;
31+
import com.google.firebase.inject.Deferred;
32+
import com.google.firebase.inject.Provider;
2833

2934
/**
3035
* FirebaseAuthCredentialsProvider uses Firebase Auth via {@link FirebaseApp} to get an auth token.
@@ -40,50 +45,54 @@ public final class FirebaseAuthCredentialsProvider extends CredentialsProvider {
4045

4146
private static final String LOG_TAG = "FirebaseAuthCredentialsProvider";
4247

43-
private final InternalAuthProvider authProvider;
44-
4548
/**
4649
* The listener registered with FirebaseApp; used to stop receiving auth changes once
4750
* changeListener is removed.
4851
*/
49-
private final IdTokenListener idTokenListener;
52+
private final IdTokenListener idTokenListener = result -> onIdTokenChanged();
5053

51-
/** The listener to be notified of credential changes (sign-in / sign-out, token changes). */
52-
@Nullable private Listener<User> changeListener;
54+
/**
55+
* The {@link Provider} that gives access to the {@link InternalAuthProvider} instance; initially,
56+
* its {@link Provider#get} method returns {@code null}, but will be changed to a new {@link
57+
* Provider} once the "auth" module becomes available.
58+
*/
59+
@Nullable
60+
@GuardedBy("this")
61+
private InternalAuthProvider internalAuthProvider;
5362

54-
/** The current user as reported to us via our IdTokenListener. */
55-
private User currentUser;
63+
/** The listener to be notified of credential changes (sign-in / sign-out, token changes). */
64+
@Nullable
65+
@GuardedBy("this")
66+
private Listener<User> changeListener;
5667

5768
/** Counter used to detect if the token changed while a getToken request was outstanding. */
69+
@GuardedBy("this")
5870
private int tokenCounter;
5971

72+
@GuardedBy("this")
6073
private boolean forceRefresh;
6174

6275
/** Creates a new FirebaseAuthCredentialsProvider. */
63-
public FirebaseAuthCredentialsProvider(InternalAuthProvider authProvider) {
64-
this.authProvider = authProvider;
65-
this.idTokenListener =
66-
token -> {
76+
@SuppressLint("ProviderAssignment") // TODO: Remove this @SuppressLint once b/181014061 is fixed.
77+
public FirebaseAuthCredentialsProvider(Deferred<InternalAuthProvider> deferredAuthProvider) {
78+
deferredAuthProvider.whenAvailable(
79+
provider -> {
6780
synchronized (this) {
68-
currentUser = getUser();
69-
tokenCounter++;
70-
71-
if (changeListener != null) {
72-
changeListener.onValue(currentUser);
73-
}
81+
internalAuthProvider = provider.get();
82+
onIdTokenChanged();
83+
internalAuthProvider.addIdTokenListener(idTokenListener);
7484
}
75-
};
76-
currentUser = getUser();
77-
tokenCounter = 0;
78-
79-
authProvider.addIdTokenListener(idTokenListener);
85+
});
8086
}
8187

8288
@Override
8389
public synchronized Task<String> getToken() {
84-
boolean doForceRefresh = forceRefresh;
90+
if (internalAuthProvider == null) {
91+
return Tasks.forException(new FirebaseApiNotAvailableException("auth is not available"));
92+
}
93+
94+
Task<GetTokenResult> res = internalAuthProvider.getAccessToken(forceRefresh);
8595
forceRefresh = false;
86-
Task<GetTokenResult> res = authProvider.getAccessToken(doForceRefresh);
8796

8897
// Take note of the current value of the tokenCounter so that this method can fail (with a
8998
// FirebaseFirestoreException) if there is a token change while the request is outstanding.
@@ -118,18 +127,29 @@ public synchronized void setChangeListener(@NonNull Listener<User> changeListene
118127
this.changeListener = changeListener;
119128

120129
// Fire the initial event.
121-
changeListener.onValue(currentUser);
130+
changeListener.onValue(getUser());
122131
}
123132

124133
@Override
125134
public synchronized void removeChangeListener() {
126135
changeListener = null;
127-
authProvider.removeIdTokenListener(idTokenListener);
136+
137+
if (internalAuthProvider != null) {
138+
internalAuthProvider.removeIdTokenListener(idTokenListener);
139+
}
140+
}
141+
142+
/** Invoked when the auth token changes. */
143+
private synchronized void onIdTokenChanged() {
144+
tokenCounter++;
145+
if (changeListener != null) {
146+
changeListener.onValue(getUser());
147+
}
128148
}
129149

130-
/** Returns the current {@link User} as obtained from the given FirebaseApp instance. */
131-
private User getUser() {
132-
@Nullable String uid = authProvider.getUid();
150+
/** Returns the current {@link User} as obtained from the given InternalAuthProvider. */
151+
private synchronized User getUser() {
152+
@Nullable String uid = (internalAuthProvider == null) ? null : internalAuthProvider.getUid();
133153
return uid != null ? new User(uid) : User.UNAUTHENTICATED;
134154
}
135155
}

firebase-firestore/src/test/java/com/google/firebase/firestore/FirestoreMultiDbComponentTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.google.firebase.FirebaseOptions;
2525
import com.google.firebase.auth.internal.InternalAuthProvider;
2626
import com.google.firebase.firestore.remote.GrpcMetadataProvider;
27+
import com.google.firebase.firestore.testutil.ImmediateDeferred;
28+
import com.google.firebase.inject.Deferred;
2729
import org.junit.Test;
2830
import org.junit.runner.RunWith;
2931
import org.robolectric.RobolectricTestRunner;
@@ -58,7 +60,9 @@ private static FirebaseApp createApp(String appName) {
5860
private static FirestoreMultiDbComponent createComponent(FirebaseApp firebaseApp) {
5961
Context context = InstrumentationRegistry.getInstrumentation().getContext();
6062
InternalAuthProvider authProvider = mock(InternalAuthProvider.class);
63+
Deferred<InternalAuthProvider> deferredAuthProvider = new ImmediateDeferred<>(authProvider);
6164
GrpcMetadataProvider metadataProvider = mock(GrpcMetadataProvider.class);
62-
return new FirestoreMultiDbComponent(context, firebaseApp, authProvider, metadataProvider);
65+
return new FirestoreMultiDbComponent(
66+
context, firebaseApp, deferredAuthProvider, metadataProvider);
6367
}
6468
}

0 commit comments

Comments
 (0)