-
Notifications
You must be signed in to change notification settings - Fork 66
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
Feat: Record serialization/deserialization #1706
Conversation
…cloud-firestore module by adding a test-jdk17 directory and a java17 profile, and eliminate google-cloud-firestore-jdk17 module
Warning: This pull request is touching the following templated files:
|
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.
Looks good.
My comments are mostly about internal location of code. I feel code is being split across many files, and this has caused some duplication, decrease in encapsulation and code that jumps between files more. I think this could be simplified.
If this can be cleaned up a little now, that would be good. But I don't consider this a blocker to merging. We can followup on this in future PRs if you like.
// Helper class to convert from maps to custom objects (Beans), and vice versa. | ||
private static class BeanMapper<T> { | ||
private final Class<T> clazz; | ||
private static class PojoBeanMapper<T> extends BeanMapper<T> { |
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.
Since BeanMapper
and RecordMapper
exist in separate files, I think we should also move the PojoBeanMapper
from being an inner class to also being a top level class in it's own file as well.
I think previously, the serialization/deserialization logic was contained in CustomClassMapper
, but now is spread across many files. This is the Java way of doing things, with separate files for separate classes. But we should maybe consider having a folder to contain all these classes, maybe named serialization
.
I would like to see some refactoring to:
- Better encapsulate logic. Many methods have had their
private
attribute removed, which I take as indication to logic being placed in the wrong place. The BeanMapper class could be a better place for common logic, since methods can beprotected
here. - Remove duplication. For example:
if (serverTimestamps.contains(property) && propertyValue == null) {
// Replace null ServerTimestamp-annotated fields with the sentinel.
serializedValue = FieldValue.serverTimestamp();
} else {
serializedValue = CustomClassMapper.serialize(propertyValue, path.child(property));
}
Could be pushed up into parent BeanMapper somehow.
- Some more logic from
CustomClassMapper
should likely be moved over toDeserializeContext
. For example, sinceErrorPath
lives inDeserializeContext
, theserializeError
anddeserializeError
should maybe move over there as well now, perhaps even as methods on theErrorPath
class.
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.
- move PojoBeanMapper to separate class
- move serialization related files into
encoding
folder (keeping consistent with database)
-- Note: this will makeCustomClassMapper
public to be accessible by other files in firestore. Marked "internalApi" to avoid public use. - "Many methods have had their private attribute removed" -> so far, custom class mapper only have
serialize
anddeserializeToType
methods changed fromprivate
to default, this is acceptable. - move duplictated code to
BeanMapper
- move
serializeError
anddeserializeError
toDeserializeContext
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.
The BeanMapper class could be a better place for common logic, since methods can be
protected
here.
I agree. Having mapper subclasses call an external utility class is less modular.
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.
make sense
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.
Can we remove the record
folder, and have this in the parent folder instead?
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.
done. Also removed duplicated code from the file
...ud-firestore/src/test-jdk17/java/com/google/cloud/firestore/RecordDocumentReferenceTest.java
Show resolved
Hide resolved
@tom-andersen, @milaGGL, let me know if you'd like me to take a stab at these comments, either pre- or post-merge. |
hi @eranl, I agree with Tom's comments and prefer to address them pre-merge. I will work on the comments tomorrow, but you are very much welcome to help out if you would like to. |
Sure. Let me know how I can help. |
@@ -38,7 +43,7 @@ | |||
import java.util.concurrent.ConcurrentMap; | |||
|
|||
/** Helper class to convert to/from custom POJO classes and plain Java types. */ | |||
class CustomClassMapper { | |||
public class CustomClassMapper { |
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.
Is it a good idea to make this class and the methods below public, exposing them to library users?
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 am also hesitating over this.
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.
added "@internalapi" to avoid public use.
@@ -93,18 +90,6 @@ abstract T deserialize( | |||
Map<TypeVariable<Class<T>>, Type> types, | |||
DeserializeContext context); | |||
|
|||
void ensureValidDocumentIdType(String fieldDescription, String operation, Type type) { |
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.
Why move these methods from base class to a utility class?
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 am moving out all util methods that are not specific to the base class or used in both child classes.
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.
and this is renamed to validateDocumentIdType
to match validateServerTimestampType
method.
Have done some code re-format. Please review @tom-andersen, @eranl. |
if (constructor == null) { | ||
throw context.errorPath.deserializeError( | ||
"Class " | ||
+ getClazz().getName() | ||
+ " does not define a no-argument constructor. If you are using ProGuard, make " | ||
+ "sure these constructors are not stripped"); | ||
} |
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.
This check is redundant, since this class can't be instantiated with a null constructor
. Also, it's not a no-argument constructor, but the record's canonical one.
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.
That's correct, the constructor
should not be null.
|
||
documentReference.set(ALL_SUPPORTED_TYPES_OBJECT).get(); | ||
|
||
CommitRequest expectedCommit = commit(set(ALL_SUPPORTED_TYPES_PROTO)); |
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.
Since these files are only compile-able with JDK16+ anyway, why not use var
?
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.
Explicitly writing out the types is more readable and maintainable. Clarity is preferred over simplicity.
Looks good. Thanks for pushing this through. |
Fixes #1152
Contribution by @eranl: #1223
Internal link for details: b/363272745