Skip to content

Query get API for RTDB #2087

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.database.core.DatabaseConfig;
import com.google.firebase.database.core.Path;
import com.google.firebase.database.core.RepoManager;
Expand All @@ -31,6 +36,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeoutException;
Expand Down Expand Up @@ -3401,6 +3407,144 @@ public void onComplete(DatabaseError error, DatabaseReference ref) {
IntegrationTestHelpers.waitFor(semaphore);
}

private static FirebaseApp appForDatabaseUrl(String url, String name) {
return FirebaseApp.initializeApp(
InstrumentationRegistry.getInstrumentation().getTargetContext(),
new FirebaseOptions.Builder()
.setApplicationId("appid")
.setApiKey("apikey")
.setDatabaseUrl(url)
.build(),
name);
}

@Test
public void emptyQueryGet() throws DatabaseException, InterruptedException, ExecutionException {
FirebaseApp app =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
assertNull(Tasks.await(db.getReference(UUID.randomUUID().toString()).get()).getValue());
}

@Test
public void offlineQueryGet() throws DatabaseException, InterruptedException {
FirebaseApp app =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
DatabaseReference node = db.getReference();
db.goOffline();
try {
Tasks.await(node.get());
} catch (ExecutionException e) {
assertEquals(e.getCause().getMessage(), "Client is offline");
return;
}
fail("Client get succeeded even though offline.");
}

@Test
public void getQueryBasic() throws DatabaseException, InterruptedException, ExecutionException {
FirebaseApp app =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
DatabaseReference node = db.getReference();
Tasks.await(node.setValue(42));
assertEquals(42L, Tasks.await(node.get()).getValue());
}

@Test
public void getQueryCached()
throws DatabaseException, InterruptedException, TimeoutException, TestFailure,
ExecutionException {
FirebaseApp app =
appForDatabaseUrl(IntegrationTestValues.getAltNamespace(), UUID.randomUUID().toString());
FirebaseDatabase db = FirebaseDatabase.getInstance(app);
DatabaseReference ref = db.getReference();
final Semaphore semaphore = new Semaphore(0);
ValueEventListener listener =
new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.getValue() != null && snapshot.getValue().equals(42L)) {
semaphore.release();
}
}

@Override
public void onCancelled(@NonNull DatabaseError error) {}
};
ref.addValueEventListener(listener);
ref.setValue(42L);
IntegrationTestHelpers.waitFor(semaphore);
db.goOffline();
try {
// Since we still have a listener on `ref`, the 42L should be cached here.
assertEquals(42L, Tasks.await(ref.get()).getValue());
} finally {
ref.removeEventListener(listener);
}
}

@Test
public void getRetrievesLatestServerValue()
throws DatabaseException, InterruptedException, ExecutionException, TestFailure,
TimeoutException {
FirebaseApp readerApp =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseApp writerApp =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseDatabase readerDb = FirebaseDatabase.getInstance(readerApp);
FirebaseDatabase writerDb = FirebaseDatabase.getInstance(writerApp);
DatabaseReference reader = readerDb.getReference();
DatabaseReference writer = writerDb.getReference();

final Semaphore readerSemaphore = new Semaphore(0);
reader.addValueEventListener(
new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.getValue() != null && snapshot.getValue().equals(42L)) {
readerSemaphore.release();
}
}

@Override
public void onCancelled(@NonNull DatabaseError error) {}
});

WriteFuture write = new WriteFuture(writer, 42L);
assertNull(write.timedGet());
IntegrationTestHelpers.waitFor(readerSemaphore);

write = new WriteFuture(writer, 43L);
assertNull(write.timedGet());

assertEquals(43L, Tasks.await(reader.get()).getValue());
}

@Test
public void getUpdatesPersistenceCacheWhenEnabled()
throws DatabaseException, InterruptedException, ExecutionException, TestFailure,
TimeoutException {
FirebaseApp readerApp =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseApp writerApp =
appForDatabaseUrl(IntegrationTestValues.getNamespace(), UUID.randomUUID().toString());
FirebaseDatabase readerDb = FirebaseDatabase.getInstance(readerApp);
readerDb.setPersistenceEnabled(true);
FirebaseDatabase writerDb = FirebaseDatabase.getInstance(writerApp);
DatabaseReference reader = readerDb.getReference();
DatabaseReference writer = writerDb.getReference();

assertNull(new WriteFuture(writer, 42L).timedGet());
assertEquals(42L, Tasks.await(reader.get()).getValue());

readerDb.goOffline();

Semaphore semaphore = new Semaphore(0);
assertNotNull(ReadFuture.untilEquals(reader, 42L).timedGet());
}

@Test
public void querySnapshotChildrenRespectDefaultOrdering()
throws DatabaseException, ExecutionException, TimeoutException, TestFailure,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import com.google.android.gms.common.internal.Objects;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.core.ChildEventRegistration;
import com.google.firebase.database.core.EventRegistration;
import com.google.firebase.database.core.Path;
Expand Down Expand Up @@ -161,6 +162,15 @@ public ChildEventListener addChildEventListener(@NonNull ChildEventListener list
return listener;
}

/**
* Get the server value for this query, updating cache and raising events if successful. If not
* connected, fall back to a locally-cached value.
*/
@NonNull
Task<DataSnapshot> get() {
return repo.getValue(this);
}

/**
* Add a listener for a single change in the data at this location. This listener will be
* triggered once with the value of the data at the location.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package com.google.firebase.database.connection;

import com.google.android.gms.tasks.Task;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -56,6 +57,10 @@ void listen(

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

// Get

Task<Object> get(List<String> path, Map<String, Object> queryParams);

// Writes

void purgeOutstandingWrites();
Expand Down
Loading