Skip to content

Remove timestampInSnapshots Setting #2026

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 4 commits into from
Oct 9, 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
5 changes: 4 additions & 1 deletion firebase-firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Unreleased
# Unreleased (22.0.0)
- [changed] Removed the deprecated `timestampsInSnapshotsEnabled` setting.
Any timestamps in Firestore documents are now returned as `Timestamps`. To
convert `Timestamp` classed to `java.util.Date`, use `Timestamp.toDate()`.

# 21.6.1
- [changed] Added new internal HTTP headers to the gRPC connection.
Expand Down
2 changes: 0 additions & 2 deletions firebase-firestore/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ package com.google.firebase.firestore {
}

public final class FirebaseFirestoreSettings {
method public boolean areTimestampsInSnapshotsEnabled();
method public long getCacheSizeBytes();
method @NonNull public String getHost();
method public boolean isPersistenceEnabled();
Expand All @@ -204,7 +203,6 @@ package com.google.firebase.firestore {
method @NonNull public com.google.firebase.firestore.FirebaseFirestoreSettings.Builder setHost(@NonNull String);
method @NonNull public com.google.firebase.firestore.FirebaseFirestoreSettings.Builder setPersistenceEnabled(boolean);
method @NonNull public com.google.firebase.firestore.FirebaseFirestoreSettings.Builder setSslEnabled(boolean);
method @Deprecated @NonNull public com.google.firebase.firestore.FirebaseFirestoreSettings.Builder setTimestampsInSnapshotsEnabled(boolean);
}

public class GeoPoint implements java.lang.Comparable<com.google.firebase.firestore.GeoPoint> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,20 @@
package com.google.firebase.firestore;

import static com.google.common.truth.Truth.assertThat;
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.newTestSettingsWithSnapshotTimestampsEnabled;
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.querySnapshotToValues;
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollection;
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testFirestore;
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.waitFor;
import static com.google.firebase.firestore.testutil.TestUtil.map;
import static com.google.firebase.firestore.util.Util.autoId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.Timestamp;
import com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior;
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.After;
Expand Down Expand Up @@ -218,7 +213,7 @@ private static Map<String, Object> objectWithTimestamp(Timestamp timestamp) {
}

@Test
public void testTimestampsInSnapshots() {
public void testTimestampsAreTruncated() {
Timestamp originalTimestamp = new Timestamp(100, 123456789);
// Timestamps are currently truncated to microseconds after being written to the database.
Timestamp truncatedTimestamp =
Expand All @@ -240,42 +235,4 @@ public void testTimestampsInSnapshots() {
Map<String, Object> nestedObject = (Map<String, Object>) data.get("nested");
assertThat(nestedObject.get("timestamp2")).isEqualTo(readNestedTimestamp);
}

private static DocumentReference createDocWithTimestampsDisabled() {
FirebaseFirestoreSettings settings = newTestSettingsWithSnapshotTimestampsEnabled(false);
return testFirestore(settings).collection(autoId()).document();
}

@Test
public void testTimestampsInSnapshotsLegacyBehaviorForTimestamps() {
Timestamp timestamp = new Timestamp(100, 123456789);

DocumentReference docRef = createDocWithTimestampsDisabled();
waitFor(docRef.set(objectWithTimestamp(timestamp)));
DocumentSnapshot snapshot = waitFor(docRef.get());
Map<String, Object> data = snapshot.getData();

assertThat(snapshot.get("timestamp")).isInstanceOf(Date.class);
assertThat(data.get("timestamp")).isInstanceOf(Date.class);
assertThat(snapshot.get("timestamp")).isEqualTo(timestamp.toDate());
assertThat(data.get("timestamp")).isEqualTo(timestamp.toDate());

assertThat(snapshot.get("nested.timestamp2")).isInstanceOf(Date.class);
assertThat(snapshot.get("nested.timestamp2")).isEqualTo(timestamp.toDate());
@SuppressWarnings("unchecked")
Map<String, Object> nestedObject = (Map<String, Object>) data.get("nested");
assertThat(nestedObject).isNotNull();
assertThat(nestedObject.get("timestamp2")).isEqualTo(timestamp.toDate());
}

@Test
public void testTimestampsInSnapshotsLegacyBehaviorForServerTimestamps() {
DocumentReference docRef = createDocWithTimestampsDisabled();
waitFor(docRef.set(map("timestamp", FieldValue.serverTimestamp())));
DocumentSnapshot snapshot = waitFor(docRef.get());
assertThat(snapshot.get("timestamp", ServerTimestampBehavior.ESTIMATE))
.isInstanceOf(Date.class);
assertThat(snapshot.getData(ServerTimestampBehavior.ESTIMATE).get("timestamp"))
.isInstanceOf(Date.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,6 @@ public static DatabaseInfo testEnvDatabaseInfo() {
}

public static FirebaseFirestoreSettings newTestSettings() {
return newTestSettingsWithSnapshotTimestampsEnabled(true);
}

@SuppressWarnings("deprecation") // for setTimestampsInSnapshotsEnabled()
public static FirebaseFirestoreSettings newTestSettingsWithSnapshotTimestampsEnabled(
boolean enabled) {
FirebaseFirestoreSettings.Builder settings = new FirebaseFirestoreSettings.Builder();

if (CONNECT_TO_EMULATOR) {
Expand All @@ -155,7 +149,6 @@ public static FirebaseFirestoreSettings newTestSettingsWithSnapshotTimestampsEna
}

settings.setPersistenceEnabled(true);
settings.setTimestampsInSnapshotsEnabled(enabled);

return settings.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ public Map<String, Object> getData() {
public Map<String, Object> getData(@NonNull ServerTimestampBehavior serverTimestampBehavior) {
checkNotNull(
serverTimestampBehavior, "Provided serverTimestampBehavior value must not be null.");
UserDataWriter userDataWriter =
new UserDataWriter(
firestore,
firestore.getFirestoreSettings().areTimestampsInSnapshotsEnabled(),
serverTimestampBehavior);
UserDataWriter userDataWriter = new UserDataWriter(firestore, serverTimestampBehavior);
return doc == null ? null : userDataWriter.convertObject(doc.getData().getFieldsMap());
}

Expand Down Expand Up @@ -260,10 +256,7 @@ public Object get(
checkNotNull(fieldPath, "Provided field path must not be null.");
checkNotNull(
serverTimestampBehavior, "Provided serverTimestampBehavior value must not be null.");
return getInternal(
fieldPath.getInternalPath(),
serverTimestampBehavior,
firestore.getFirestoreSettings().areTimestampsInSnapshotsEnabled());
return getInternal(fieldPath.getInternalPath(), serverTimestampBehavior);
}

/**
Expand Down Expand Up @@ -396,9 +389,6 @@ public Date getDate(@NonNull String field) {
/**
* Returns the value of the field as a Date.
*
* <p>This method ignores the global setting {@link
* FirebaseFirestoreSettings#areTimestampsInSnapshotsEnabled}.
*
* @param field The path to the field.
* @param serverTimestampBehavior Configures the behavior for server timestamps that have not yet
* been set to their final value.
Expand All @@ -411,20 +401,13 @@ public Date getDate(
checkNotNull(field, "Provided field path must not be null.");
checkNotNull(
serverTimestampBehavior, "Provided serverTimestampBehavior value must not be null.");
Object maybeDate =
getInternal(
FieldPath.fromDotSeparatedPath(field).getInternalPath(),
serverTimestampBehavior,
/* timestampsInSnapshots= */ false);
return castTypedValue(maybeDate, field, Date.class);
@Nullable Timestamp timestamp = getTimestamp(field, serverTimestampBehavior);
return timestamp != null ? timestamp.toDate() : null;
}

/**
* Returns the value of the field as a {@code com.google.firebase.Timestamp}.
*
* <p>This method ignores the global setting {@link
* FirebaseFirestoreSettings#areTimestampsInSnapshotsEnabled}.
*
* @param field The path to the field.
* @throws RuntimeException if this is not a timestamp field.
* @return The value of the field
Expand All @@ -437,9 +420,6 @@ public Timestamp getTimestamp(@NonNull String field) {
/**
* Returns the value of the field as a {@code com.google.firebase.Timestamp}.
*
* <p>This method ignores the global setting {@link
* FirebaseFirestoreSettings#areTimestampsInSnapshotsEnabled}.
*
* @param field The path to the field.
* @param serverTimestampBehavior Configures the behavior for server timestamps that have not yet
* been set to their final value.
Expand All @@ -454,9 +434,7 @@ public Timestamp getTimestamp(
serverTimestampBehavior, "Provided serverTimestampBehavior value must not be null.");
Object maybeTimestamp =
getInternal(
FieldPath.fromDotSeparatedPath(field).getInternalPath(),
serverTimestampBehavior,
/* timestampsInSnapshots= */ true);
FieldPath.fromDotSeparatedPath(field).getInternalPath(), serverTimestampBehavior);
return castTypedValue(maybeTimestamp, field, Timestamp.class);
}

Expand Down Expand Up @@ -526,13 +504,11 @@ private <T> T castTypedValue(Object value, String field, Class<T> clazz) {
@Nullable
private Object getInternal(
@NonNull com.google.firebase.firestore.model.FieldPath fieldPath,
@NonNull ServerTimestampBehavior serverTimestampBehavior,
boolean timestampsInSnapshots) {
@NonNull ServerTimestampBehavior serverTimestampBehavior) {
if (doc != null) {
Value val = doc.getField(fieldPath);
if (val != null) {
UserDataWriter userDataWriter =
new UserDataWriter(firestore, timestampsInSnapshots, serverTimestampBehavior);
UserDataWriter userDataWriter = new UserDataWriter(firestore, serverTimestampBehavior);
return userDataWriter.convertValue(val);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,19 @@ public final class FirebaseFirestoreSettings {

private static final long MINIMUM_CACHE_BYTES = 1 * 1024 * 1024; // 1 MB
private static final long DEFAULT_CACHE_SIZE_BYTES = 100 * 1024 * 1024; // 100 MB
private static final boolean DEFAULT_TIMESTAMPS_IN_SNAPSHOTS_ENABLED = true;

/** A Builder for creating {@code FirebaseFirestoreSettings}. */
public static final class Builder {
private String host;
private boolean sslEnabled;
private boolean persistenceEnabled;
private boolean timestampsInSnapshotsEnabled;
private long cacheSizeBytes;

/** Constructs a new {@code FirebaseFirestoreSettings} Builder object. */
public Builder() {
host = DEFAULT_HOST;
sslEnabled = true;
persistenceEnabled = true;
timestampsInSnapshotsEnabled = DEFAULT_TIMESTAMPS_IN_SNAPSHOTS_ENABLED;
cacheSizeBytes = DEFAULT_CACHE_SIZE_BYTES;
}

Expand All @@ -60,7 +57,6 @@ public Builder(@NonNull FirebaseFirestoreSettings settings) {
host = settings.host;
sslEnabled = settings.sslEnabled;
persistenceEnabled = settings.persistenceEnabled;
timestampsInSnapshotsEnabled = settings.timestampsInSnapshotsEnabled;
}

/**
Expand Down Expand Up @@ -98,33 +94,6 @@ public Builder setPersistenceEnabled(boolean value) {
return this;
}

/**
* Specifies whether to use {@link com.google.firebase.Timestamp Timestamps} for timestamp
* fields in {@link DocumentSnapshot DocumentSnapshots}. This is now enabled by default and
* should not be disabled.
*
* <p>Previously, Cloud Firestore returned timestamp fields as {@link java.util.Date} but {@link
* java.util.Date} only supports millisecond precision, which leads to truncation and causes
* unexpected behavior when using a timestamp from a snapshot as a part of a subsequent query.
*
* <p>So now Cloud Firestore returns {@link com.google.firebase.Timestamp Timestamp} values
* instead of {@link java.util.Date}, avoiding this kind of problem.
*
* <p>To opt into the old behavior of returning {@link java.util.Date Dates}, you can
* temporarily set {@link FirebaseFirestoreSettings#areTimestampsInSnapshotsEnabled} to false.
*
* @deprecated This setting now defaults to true and will be removed in a future release. If you
* are already setting it to true, just remove the setting. If you are setting it to false,
* you should update your code to expect {@link com.google.firebase.Timestamp Timestamps}
* instead of {@link java.util.Date Dates} and then remove the setting.
*/
@NonNull
@Deprecated
public Builder setTimestampsInSnapshotsEnabled(boolean value) {
this.timestampsInSnapshotsEnabled = value;
return this;
}

/**
* Sets an approximate cache size threshold for the on-disk data. If the cache grows beyond this
* size, Cloud Firestore will start removing data that hasn't been recently used. The size is
Expand Down Expand Up @@ -180,15 +149,13 @@ public FirebaseFirestoreSettings build() {
private final String host;
private final boolean sslEnabled;
private final boolean persistenceEnabled;
private final boolean timestampsInSnapshotsEnabled;
private final long cacheSizeBytes;

/** Constructs a {@code FirebaseFirestoreSettings} object based on the values in the Builder. */
private FirebaseFirestoreSettings(Builder builder) {
host = builder.host;
sslEnabled = builder.sslEnabled;
persistenceEnabled = builder.persistenceEnabled;
timestampsInSnapshotsEnabled = builder.timestampsInSnapshotsEnabled;
cacheSizeBytes = builder.cacheSizeBytes;
}

Expand All @@ -205,7 +172,6 @@ public boolean equals(@Nullable Object o) {
return host.equals(that.host)
&& sslEnabled == that.sslEnabled
&& persistenceEnabled == that.persistenceEnabled
&& timestampsInSnapshotsEnabled == that.timestampsInSnapshotsEnabled
&& cacheSizeBytes == that.cacheSizeBytes;
}

Expand All @@ -214,7 +180,6 @@ public int hashCode() {
int result = host.hashCode();
result = 31 * result + (sslEnabled ? 1 : 0);
result = 31 * result + (persistenceEnabled ? 1 : 0);
result = 31 * result + (timestampsInSnapshotsEnabled ? 1 : 0);
result = 31 * result + (int) cacheSizeBytes;
return result;
}
Expand All @@ -229,8 +194,6 @@ public String toString() {
+ sslEnabled
+ ", persistenceEnabled="
+ persistenceEnabled
+ ", timestampsInSnapshotsEnabled="
+ timestampsInSnapshotsEnabled
+ ", cacheSizeBytes="
+ cacheSizeBytes
+ "}";
Expand All @@ -252,14 +215,6 @@ public boolean isPersistenceEnabled() {
return persistenceEnabled;
}

/**
* Returns whether or not {@link DocumentSnapshot DocumentSnapshots} return timestamp fields as
* {@link com.google.firebase.Timestamp Timestamps}.
*/
public boolean areTimestampsInSnapshotsEnabled() {
return timestampsInSnapshotsEnabled;
}

/**
* Returns the threshold for the cache size above which the SDK will attempt to collect the least
* recently used documents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,12 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class UserDataWriter {
private final FirebaseFirestore firestore;
private final boolean timestampsInSnapshots;
private final DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior;

UserDataWriter(
FirebaseFirestore firestore,
boolean timestampsInSnapshots,
DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) {
this.firestore = firestore;
this.timestampsInSnapshots = timestampsInSnapshots;
this.serverTimestampBehavior = serverTimestampBehavior;
}

Expand Down Expand Up @@ -118,12 +115,7 @@ private Object convertServerTimestamp(Value serverTimestampValue) {
}

private Object convertTimestamp(com.google.protobuf.Timestamp value) {
Timestamp timestamp = new Timestamp(value.getSeconds(), value.getNanos());
if (timestampsInSnapshots) {
return timestamp;
} else {
return timestamp.toDate();
}
return new Timestamp(value.getSeconds(), value.getNanos());
}

private List<Object> convertArray(ArrayValue arrayValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@
public class UserDataWriterTest {

private final UserDataWriter writer =
new UserDataWriter(
TestUtil.firestore(), true, DocumentSnapshot.ServerTimestampBehavior.DEFAULT);
new UserDataWriter(TestUtil.firestore(), DocumentSnapshot.ServerTimestampBehavior.DEFAULT);

@Test
public void testConvertsNullValue() {
Expand Down Expand Up @@ -156,16 +155,14 @@ public void testConvertsDoubleValue() {
@Test
public void testConvertsDateValue() {
UserDataWriter dateWriter =
new UserDataWriter(
TestUtil.firestore(),
/* timestampsInSnapshots= */ false,
DocumentSnapshot.ServerTimestampBehavior.DEFAULT);
new UserDataWriter(TestUtil.firestore(), DocumentSnapshot.ServerTimestampBehavior.DEFAULT);
List<Date> testCases = asList(new Date(0), new Date(1356048000000L));
for (Date d : testCases) {
Value value = wrap(d);
assertValueType(Value.ValueTypeCase.TIMESTAMP_VALUE, value);
Object convertedValue = dateWriter.convertValue(value);
assertEquals(d, convertedValue);
assertTrue(convertedValue instanceof Timestamp);
assertEquals(d, ((Timestamp) convertedValue).toDate());
}
}

Expand Down