diff --git a/firebase-firestore/CHANGELOG.md b/firebase-firestore/CHANGELOG.md index b6a6d31679a..f8222997291 100644 --- a/firebase-firestore/CHANGELOG.md +++ b/firebase-firestore/CHANGELOG.md @@ -2,6 +2,9 @@ - [feature] Custom objects (POJOs) can now be passed as a field value in update(), within `Map<>` objects passed to set(), in array transform operations, and in query filters. +- [feature] DocumentSnapshot.get() now supports retrieving fields as + custom objects (POJOs) by passing a Class instance, e.g. + `snapshot.get("field", CustomType.class)`. # 17.1.2 - [changed] Changed the internal handling for locally updated documents that diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/POJOTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/POJOTest.java index 46bca54e0b8..bb60af5f7c3 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/POJOTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/POJOTest.java @@ -222,6 +222,28 @@ public void testAPIsAcceptPOJOsForFields() { waitFor(Tasks.whenAll(tasks)); } + @Test + public void testDocumentSnapshotGetWithPOJOs() { + DocumentReference ref = testDocument(); + + // Go offline so that we can verify server timestamp behavior overload. + ref.getFirestore().disableNetwork(); + + POJO pojo = new POJO(1.0, "a", ref); + ref.set(map("field", pojo)); + + DocumentSnapshot snap = waitFor(ref.get()); + + assertEquals(pojo, snap.get("field", POJO.class)); + assertEquals(pojo, snap.get(FieldPath.of("field"), POJO.class)); + assertEquals( + pojo, snap.get("field", POJO.class, DocumentSnapshot.ServerTimestampBehavior.DEFAULT)); + assertEquals( + pojo, + snap.get( + FieldPath.of("field"), POJO.class, DocumentSnapshot.ServerTimestampBehavior.DEFAULT)); + } + @Test public void setFieldMaskMustHaveCorrespondingValue() { CollectionReference collection = testCollection(); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java index 9b71486e3bb..0f9e0652969 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/ServerTimestampTest.java @@ -248,6 +248,28 @@ public void testServerTimestampsUsesPreviousValueFromLocalMutation() { assertThat(remoteSnapshot.get("a")).isInstanceOf(Timestamp.class); } + @Test + public void testServerTimestampBehaviorOverloadsOfDocumentSnapshotGet() { + writeInitialData(); + waitFor(docRef.update(updateData)); + DocumentSnapshot snap = accumulator.awaitLocalEvent(); + + // Default behavior should return null timestamp (via any overload). + assertNull(snap.get("when")); + assertNull(snap.get(FieldPath.of("when"))); + assertNull(snap.get("when", Timestamp.class)); + assertNull(snap.get(FieldPath.of("when"), Timestamp.class)); + + // Estimate should return a Timestamp object (via any overload). + assertThat(snap.get("when", ServerTimestampBehavior.ESTIMATE)).isInstanceOf(Timestamp.class); + assertThat(snap.get(FieldPath.of("when"), ServerTimestampBehavior.ESTIMATE)) + .isInstanceOf(Timestamp.class); + assertThat(snap.get("when", Timestamp.class, ServerTimestampBehavior.ESTIMATE)) + .isInstanceOf(Timestamp.class); + assertThat(snap.get(FieldPath.of("when"), Timestamp.class, ServerTimestampBehavior.ESTIMATE)) + .isInstanceOf(Timestamp.class); + } + @Test public void testServerTimestampsWorkViaTransactionSet() { waitFor( diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentSnapshot.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentSnapshot.java index e3444ab2e5e..4a13eefd90a 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentSnapshot.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentSnapshot.java @@ -289,6 +289,69 @@ public Object get( firestore.getFirestoreSettings().areTimestampsInSnapshotsEnabled())); } + /** + * Returns the value at the field, converted to a POJO, or null if the field or document doesn't + * exist. + * + * @param field The path to the field + * @param valueType The Java class to convert the field value to. + * @return The value at the given field or null. + */ + @Nullable + public T get(@NonNull String field, @NonNull Class valueType) { + return get(FieldPath.fromDotSeparatedPath(field), valueType, ServerTimestampBehavior.DEFAULT); + } + + /** + * Returns the value at the field, converted to a POJO, or null if the field or document doesn't + * exist. + * + * @param field The path to the field + * @param valueType The Java class to convert the field value to. + * @param serverTimestampBehavior Configures the behavior for server timestamps that have not yet + * been set to their final value. + * @return The value at the given field or null. + */ + @Nullable + public T get( + @NonNull String field, + @NonNull Class valueType, + @NonNull ServerTimestampBehavior serverTimestampBehavior) { + return get(FieldPath.fromDotSeparatedPath(field), valueType, serverTimestampBehavior); + } + + /** + * Returns the value at the field, converted to a POJO, or null if the field or document doesn't + * exist. + * + * @param fieldPath The path to the field + * @param valueType The Java class to convert the field value to. + * @return The value at the given field or null. + */ + @Nullable + public T get(@NonNull FieldPath fieldPath, @NonNull Class valueType) { + return get(fieldPath, valueType, ServerTimestampBehavior.DEFAULT); + } + + /** + * Returns the value at the field, converted to a POJO, or null if the field or document doesn't + * exist. + * + * @param fieldPath The path to the field + * @param valueType The Java class to convert the field value to. + * @param serverTimestampBehavior Configures the behavior for server timestamps that have not yet + * been set to their final value. + * @return The value at the given field or null. + */ + @Nullable + public T get( + @NonNull FieldPath fieldPath, + @NonNull Class valueType, + @NonNull ServerTimestampBehavior serverTimestampBehavior) { + Object data = get(fieldPath, serverTimestampBehavior); + return data == null ? null : CustomClassMapper.convertToCustomClass(data, valueType); + } + /** * Returns the value of the field as a boolean. If the value is not a boolean this will throw a * runtime exception.