-
Notifications
You must be signed in to change notification settings - Fork 615
Introducing UserDataReader/UserDataWriter #1177
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
Changes from 4 commits
9873f53
1909fb1
df7ae60
5730c1a
6d01e84
eb478e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright 2020 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package com.google.firebase.firestore; | ||
|
||
import org.mockito.Mockito; | ||
|
||
public class TestAccessHelper { | ||
/** | ||
* Install mocks for `FirebaseFirestore.getFirestoreSettings()` and | ||
* `FirebaseFirestore.getUserDataWriter()`, which are used in DocumentSnapshot tests. | ||
*/ | ||
public static void installDocumentSnapshotMocks(FirebaseFirestore firestore) { | ||
Mockito.when(firestore.getFirestoreSettings()) | ||
.thenReturn(new FirebaseFirestoreSettings.Builder().build()); | ||
Mockito.when(firestore.getUserDataWriter()).thenReturn(new UserDataWriter(firestore)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,25 +15,18 @@ | |
package com.google.firebase.firestore; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
import static com.google.firebase.firestore.util.Assert.hardAssert; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import com.google.firebase.Timestamp; | ||
import com.google.firebase.firestore.model.DatabaseId; | ||
import com.google.firebase.firestore.UserDataWriter.UserDataWriterOptions; | ||
import com.google.firebase.firestore.model.Document; | ||
import com.google.firebase.firestore.model.DocumentKey; | ||
import com.google.firebase.firestore.model.value.ArrayValue; | ||
import com.google.firebase.firestore.model.value.FieldValue; | ||
import com.google.firebase.firestore.model.value.ObjectValue; | ||
import com.google.firebase.firestore.model.value.ReferenceValue; | ||
import com.google.firebase.firestore.model.value.ServerTimestampValue; | ||
import com.google.firebase.firestore.model.value.TimestampValue; | ||
import com.google.firebase.firestore.util.CustomClassMapper; | ||
import com.google.firebase.firestore.util.Logger; | ||
import java.util.ArrayList; | ||
import java.util.Date; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
|
@@ -78,20 +71,10 @@ public enum ServerTimestampBehavior { | |
static final ServerTimestampBehavior DEFAULT = ServerTimestampBehavior.NONE; | ||
} | ||
|
||
/** Holds settings that define field value deserialization options. */ | ||
static class FieldValueOptions { | ||
final ServerTimestampBehavior serverTimestampBehavior; | ||
final boolean timestampsInSnapshotsEnabled; | ||
|
||
private FieldValueOptions( | ||
ServerTimestampBehavior serverTimestampBehavior, boolean timestampsInSnapshotsEnabled) { | ||
this.serverTimestampBehavior = serverTimestampBehavior; | ||
this.timestampsInSnapshotsEnabled = timestampsInSnapshotsEnabled; | ||
} | ||
} | ||
|
||
private final FirebaseFirestore firestore; | ||
|
||
private final UserDataWriter userDataWriter; | ||
|
||
private final DocumentKey key; | ||
|
||
/** Is {@code null} if the document doesn't exist */ | ||
|
@@ -109,6 +92,7 @@ private FieldValueOptions( | |
this.key = checkNotNull(key); | ||
this.doc = doc; | ||
this.metadata = new SnapshotMetadata(hasPendingWrites, isFromCache); | ||
this.userDataWriter = firestore.getUserDataWriter(); | ||
} | ||
|
||
static DocumentSnapshot fromDocument( | ||
|
@@ -170,7 +154,7 @@ public Map<String, Object> getData(@NonNull ServerTimestampBehavior serverTimest | |
? null | ||
: convertObject( | ||
doc.getData(), | ||
new FieldValueOptions( | ||
new UserDataWriterOptions( | ||
serverTimestampBehavior, | ||
firestore.getFirestoreSettings().areTimestampsInSnapshotsEnabled())); | ||
} | ||
|
@@ -285,7 +269,7 @@ public Object get( | |
serverTimestampBehavior, "Provided serverTimestampBehavior value must not be null."); | ||
return getInternal( | ||
fieldPath.getInternalPath(), | ||
new FieldValueOptions( | ||
new UserDataWriterOptions( | ||
serverTimestampBehavior, | ||
firestore.getFirestoreSettings().areTimestampsInSnapshotsEnabled())); | ||
} | ||
|
@@ -438,7 +422,7 @@ public Date getDate( | |
Object maybeDate = | ||
getInternal( | ||
FieldPath.fromDotSeparatedPath(field).getInternalPath(), | ||
new FieldValueOptions( | ||
new UserDataWriterOptions( | ||
serverTimestampBehavior, /*timestampsInSnapshotsEnabled=*/ false)); | ||
return castTypedValue(maybeDate, field, Date.class); | ||
} | ||
|
@@ -479,7 +463,8 @@ public Timestamp getTimestamp( | |
Object maybeTimestamp = | ||
getInternal( | ||
FieldPath.fromDotSeparatedPath(field).getInternalPath(), | ||
new FieldValueOptions(serverTimestampBehavior, /*timestampsInSnapshotsEnabled=*/ true)); | ||
new UserDataWriterOptions( | ||
serverTimestampBehavior, /*timestampsInSnapshotsEnabled=*/ true)); | ||
return castTypedValue(maybeTimestamp, field, Timestamp.class); | ||
} | ||
|
||
|
@@ -546,87 +531,21 @@ private <T> T castTypedValue(Object value, String field, Class<T> clazz) { | |
return clazz.cast(value); | ||
} | ||
|
||
@Nullable | ||
private Object convertValue(FieldValue value, FieldValueOptions options) { | ||
if (value instanceof ObjectValue) { | ||
return convertObject((ObjectValue) value, options); | ||
} else if (value instanceof ArrayValue) { | ||
return convertArray((ArrayValue) value, options); | ||
} else if (value instanceof ReferenceValue) { | ||
return convertReference((ReferenceValue) value); | ||
} else if (value instanceof TimestampValue) { | ||
return convertTimestamp((TimestampValue) value, options); | ||
} else if (value instanceof ServerTimestampValue) { | ||
return convertServerTimestamp((ServerTimestampValue) value, options); | ||
} else { | ||
return value.value(); | ||
} | ||
} | ||
|
||
private Object convertServerTimestamp(ServerTimestampValue value, FieldValueOptions options) { | ||
switch (options.serverTimestampBehavior) { | ||
case PREVIOUS: | ||
return value.getPreviousValue(); | ||
case ESTIMATE: | ||
return value.getLocalWriteTime(); | ||
default: | ||
return value.value(); | ||
} | ||
} | ||
|
||
private Object convertTimestamp(TimestampValue value, FieldValueOptions options) { | ||
Timestamp timestamp = value.value(); | ||
if (options.timestampsInSnapshotsEnabled) { | ||
return timestamp; | ||
} else { | ||
return timestamp.toDate(); | ||
} | ||
} | ||
|
||
private Object convertReference(ReferenceValue value) { | ||
DocumentKey key = value.value(); | ||
DatabaseId refDatabase = value.getDatabaseId(); | ||
DatabaseId database = this.firestore.getDatabaseId(); | ||
if (!refDatabase.equals(database)) { | ||
// TODO: Somehow support foreign references. | ||
Logger.warn( | ||
"DocumentSnapshot", | ||
"Document %s contains a document reference within a different database " | ||
+ "(%s/%s) which is not supported. It will be treated as a reference in " | ||
+ "the current database (%s/%s) instead.", | ||
key.getPath(), | ||
refDatabase.getProjectId(), | ||
refDatabase.getDatabaseId(), | ||
database.getProjectId(), | ||
database.getDatabaseId()); | ||
} | ||
return new DocumentReference(key, firestore); | ||
} | ||
|
||
private Map<String, Object> convertObject(ObjectValue objectValue, FieldValueOptions options) { | ||
Map<String, Object> result = new HashMap<>(); | ||
for (Map.Entry<String, FieldValue> entry : objectValue.getInternalValue()) { | ||
result.put(entry.getKey(), convertValue(entry.getValue(), options)); | ||
} | ||
return result; | ||
} | ||
|
||
private List<Object> convertArray(ArrayValue arrayValue, FieldValueOptions options) { | ||
ArrayList<Object> result = new ArrayList<>(arrayValue.getInternalValue().size()); | ||
for (FieldValue v : arrayValue.getInternalValue()) { | ||
result.add(convertValue(v, options)); | ||
} | ||
return result; | ||
private Map<String, Object> convertObject( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like it could reasonably just be a part of the Indeed there's already a convertObject method there; why not just use that and avoid asserting/casting? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea behind this was to only expose a single method in UserDataWriter. I don't feel strongly and made |
||
ObjectValue objectValue, UserDataWriterOptions options) { | ||
Object result = userDataWriter.convertValue(objectValue, options); | ||
hardAssert(result instanceof Map, "Documens should be decoded into Maps"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/Documens/Documents/ |
||
return (Map<String, Object>) result; | ||
} | ||
|
||
@Nullable | ||
private Object getInternal( | ||
@NonNull com.google.firebase.firestore.model.FieldPath fieldPath, | ||
@NonNull FieldValueOptions options) { | ||
@NonNull UserDataWriterOptions options) { | ||
if (doc != null) { | ||
FieldValue val = doc.getField(fieldPath); | ||
if (val != null) { | ||
return convertValue(val, options); | ||
return userDataWriter.convertValue(val, options); | ||
} | ||
} | ||
return null; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename the local to
dataReader
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I had caught all of these, but this PR went through a lot of iterations. Fixed now.