Skip to content

Commit 9ebc35f

Browse files
authored
Query get API for RTDB (#2087)
1 parent c7f1620 commit 9ebc35f

File tree

6 files changed

+415
-15
lines changed

6 files changed

+415
-15
lines changed

firebase-database/src/androidTest/java/com/google/firebase/database/QueryTest.java

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
import static org.junit.Assert.assertTrue;
2222
import static org.junit.Assert.fail;
2323

24+
import androidx.annotation.NonNull;
2425
import androidx.test.ext.junit.runners.AndroidJUnit4;
26+
import androidx.test.platform.app.InstrumentationRegistry;
27+
import com.google.android.gms.tasks.Tasks;
28+
import com.google.firebase.FirebaseApp;
29+
import com.google.firebase.FirebaseOptions;
2530
import com.google.firebase.database.core.DatabaseConfig;
2631
import com.google.firebase.database.core.Path;
2732
import com.google.firebase.database.core.RepoManager;
@@ -31,6 +36,7 @@
3136
import java.util.Arrays;
3237
import java.util.List;
3338
import java.util.Map;
39+
import java.util.UUID;
3440
import java.util.concurrent.ExecutionException;
3541
import java.util.concurrent.Semaphore;
3642
import java.util.concurrent.TimeoutException;
@@ -3401,6 +3407,144 @@ public void onComplete(DatabaseError error, DatabaseReference ref) {
34013407
IntegrationTestHelpers.waitFor(semaphore);
34023408
}
34033409

3410+
private static FirebaseApp appForDatabaseUrl(String url, String name) {
3411+
return FirebaseApp.initializeApp(
3412+
InstrumentationRegistry.getInstrumentation().getTargetContext(),
3413+
new FirebaseOptions.Builder()
3414+
.setApplicationId("appid")
3415+
.setApiKey("apikey")
3416+
.setDatabaseUrl(url)
3417+
.build(),
3418+
name);
3419+
}
3420+
3421+
@Test
3422+
public void emptyQueryGet() throws DatabaseException, InterruptedException, ExecutionException {
3423+
FirebaseApp app =
3424+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3425+
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
3426+
assertNull(Tasks.await(db.getReference(UUID.randomUUID().toString()).get()).getValue());
3427+
}
3428+
3429+
@Test
3430+
public void offlineQueryGet() throws DatabaseException, InterruptedException {
3431+
FirebaseApp app =
3432+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3433+
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
3434+
DatabaseReference node = db.getReference();
3435+
db.goOffline();
3436+
try {
3437+
Tasks.await(node.get());
3438+
} catch (ExecutionException e) {
3439+
assertEquals(e.getCause().getMessage(), "Client is offline");
3440+
return;
3441+
}
3442+
fail("Client get succeeded even though offline.");
3443+
}
3444+
3445+
@Test
3446+
public void getQueryBasic() throws DatabaseException, InterruptedException, ExecutionException {
3447+
FirebaseApp app =
3448+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3449+
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
3450+
DatabaseReference node = db.getReference();
3451+
Tasks.await(node.setValue(42));
3452+
assertEquals(42L, Tasks.await(node.get()).getValue());
3453+
}
3454+
3455+
@Test
3456+
public void getQueryCached()
3457+
throws DatabaseException, InterruptedException, TimeoutException, TestFailure,
3458+
ExecutionException {
3459+
FirebaseApp app =
3460+
appForDatabaseUrl(IntegrationTestValues.getAltNamespace(), UUID.randomUUID().toString());
3461+
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
3462+
DatabaseReference ref = db.getReference();
3463+
final Semaphore semaphore = new Semaphore(0);
3464+
ValueEventListener listener =
3465+
new ValueEventListener() {
3466+
@Override
3467+
public void onDataChange(@NonNull DataSnapshot snapshot) {
3468+
if (snapshot.getValue() != null && snapshot.getValue().equals(42L)) {
3469+
semaphore.release();
3470+
}
3471+
}
3472+
3473+
@Override
3474+
public void onCancelled(@NonNull DatabaseError error) {}
3475+
};
3476+
ref.addValueEventListener(listener);
3477+
ref.setValue(42L);
3478+
IntegrationTestHelpers.waitFor(semaphore);
3479+
db.goOffline();
3480+
try {
3481+
// Since we still have a listener on `ref`, the 42L should be cached here.
3482+
assertEquals(42L, Tasks.await(ref.get()).getValue());
3483+
} finally {
3484+
ref.removeEventListener(listener);
3485+
}
3486+
}
3487+
3488+
@Test
3489+
public void getRetrievesLatestServerValue()
3490+
throws DatabaseException, InterruptedException, ExecutionException, TestFailure,
3491+
TimeoutException {
3492+
FirebaseApp readerApp =
3493+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3494+
FirebaseApp writerApp =
3495+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3496+
FirebaseDatabase readerDb = FirebaseDatabase.getInstance(readerApp);
3497+
FirebaseDatabase writerDb = FirebaseDatabase.getInstance(writerApp);
3498+
DatabaseReference reader = readerDb.getReference();
3499+
DatabaseReference writer = writerDb.getReference();
3500+
3501+
final Semaphore readerSemaphore = new Semaphore(0);
3502+
reader.addValueEventListener(
3503+
new ValueEventListener() {
3504+
@Override
3505+
public void onDataChange(@NonNull DataSnapshot snapshot) {
3506+
if (snapshot.getValue() != null && snapshot.getValue().equals(42L)) {
3507+
readerSemaphore.release();
3508+
}
3509+
}
3510+
3511+
@Override
3512+
public void onCancelled(@NonNull DatabaseError error) {}
3513+
});
3514+
3515+
WriteFuture write = new WriteFuture(writer, 42L);
3516+
assertNull(write.timedGet());
3517+
IntegrationTestHelpers.waitFor(readerSemaphore);
3518+
3519+
write = new WriteFuture(writer, 43L);
3520+
assertNull(write.timedGet());
3521+
3522+
assertEquals(43L, Tasks.await(reader.get()).getValue());
3523+
}
3524+
3525+
@Test
3526+
public void getUpdatesPersistenceCacheWhenEnabled()
3527+
throws DatabaseException, InterruptedException, ExecutionException, TestFailure,
3528+
TimeoutException {
3529+
FirebaseApp readerApp =
3530+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3531+
FirebaseApp writerApp =
3532+
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
3533+
FirebaseDatabase readerDb = FirebaseDatabase.getInstance(readerApp);
3534+
readerDb.setPersistenceEnabled(true);
3535+
FirebaseDatabase writerDb = FirebaseDatabase.getInstance(writerApp);
3536+
DatabaseReference reader = readerDb.getReference();
3537+
DatabaseReference writer = writerDb.getReference();
3538+
3539+
assertNull(new WriteFuture(writer, 42L).timedGet());
3540+
assertEquals(42L, Tasks.await(reader.get()).getValue());
3541+
3542+
readerDb.goOffline();
3543+
3544+
Semaphore semaphore = new Semaphore(0);
3545+
assertNotNull(ReadFuture.untilEquals(reader, 42L).timedGet());
3546+
}
3547+
34043548
@Test
34053549
public void querySnapshotChildrenRespectDefaultOrdering()
34063550
throws DatabaseException, ExecutionException, TimeoutException, TestFailure,

firebase-database/src/main/java/com/google/firebase/database/Query.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import androidx.annotation.Nullable;
2121
import androidx.annotation.RestrictTo;
2222
import com.google.android.gms.common.internal.Objects;
23+
import com.google.android.gms.tasks.Task;
2324
import com.google.firebase.database.core.ChildEventRegistration;
2425
import com.google.firebase.database.core.EventRegistration;
2526
import com.google.firebase.database.core.Path;
@@ -161,6 +162,15 @@ public ChildEventListener addChildEventListener(@NonNull ChildEventListener list
161162
return listener;
162163
}
163164

165+
/**
166+
* Get the server value for this query, updating cache and raising events if successful. If not
167+
* connected, fall back to a locally-cached value.
168+
*/
169+
@NonNull
170+
Task<DataSnapshot> get() {
171+
return repo.getValue(this);
172+
}
173+
164174
/**
165175
* Add a listener for a single change in the data at this location. This listener will be
166176
* triggered once with the value of the data at the location.

firebase-database/src/main/java/com/google/firebase/database/connection/PersistentConnection.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.firebase.database.connection;
1616

17+
import com.google.android.gms.tasks.Task;
1718
import java.util.List;
1819
import java.util.Map;
1920

@@ -56,6 +57,10 @@ void listen(
5657

5758
void unlisten(List<String> path, Map<String, Object> queryParams);
5859

60+
// Get
61+
62+
Task<Object> get(List<String> path, Map<String, Object> queryParams);
63+
5964
// Writes
6065

6166
void purgeOutstandingWrites();

0 commit comments

Comments
 (0)