From 289dbfa9b5a328b57f2a899e40ceace141ff7a56 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Sat, 23 Sep 2023 17:20:36 +0300 Subject: [PATCH 1/2] feat: Instant support feat: Instant support --- .../cloud/firestore/CustomClassMapper.java | 29 ++++++++++++++++--- .../firestore/DocumentReferenceTest.java | 1 + .../cloud/firestore/LocalFirestoreHelper.java | 17 +++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java index a17c3a08e2..6d4f143fe6 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java @@ -36,6 +36,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.math.BigDecimal; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -175,6 +176,9 @@ private static Object serialize(T o, ErrorPath path) { || o instanceof FieldValue || o instanceof Value) { return o; + } else if (o instanceof Instant) { + Instant instant = (Instant) o; + return Timestamp.ofTimeSecondsAndNanos(instant.getEpochSecond(), instant.getNano()); } else { Class clazz = (Class) o.getClass(); BeanMapper mapper = loadOrCreateBeanMapperForClass(clazz); @@ -233,6 +237,8 @@ private static T deserializeToClass(Object o, Class clazz, DeserializeCon return (T) convertDate(o, context); } else if (Timestamp.class.isAssignableFrom(clazz)) { return (T) convertTimestamp(o, context); + } else if (Instant.class.isAssignableFrom(clazz)) { + return (T) convertInstant(o, context); } else if (Blob.class.isAssignableFrom(clazz)) { return (T) convertBlob(o, context); } else if (GeoPoint.class.isAssignableFrom(clazz)) { @@ -557,6 +563,19 @@ private static Timestamp convertTimestamp(Object o, DeserializeContext context) } } + private static Instant convertInstant(Object o, DeserializeContext context) { + if (o instanceof Timestamp) { + Timestamp timestamp = (Timestamp) o; + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); + } else if (o instanceof Date) { + return Instant.ofEpochMilli(((Date) o).getTime()); + } else { + throw deserializeError( + context.errorPath, + "Failed to convert value of type " + o.getClass().getName() + " to Instant"); + } + } + private static Blob convertBlob(Object o, DeserializeContext context) { if (o instanceof Blob) { return (Blob) o; @@ -976,13 +995,13 @@ Map serialize(T object, ErrorPath path) { private void applyFieldAnnotations(Field field) { if (field.isAnnotationPresent(ServerTimestamp.class)) { Class fieldType = field.getType(); - if (fieldType != Date.class && fieldType != Timestamp.class) { + if (fieldType != Date.class && fieldType != Timestamp.class && fieldType != Instant.class) { throw new IllegalArgumentException( "Field " + field.getName() + " is annotated with @ServerTimestamp but is " + fieldType - + " instead of Date or Timestamp."); + + " instead of Date, Timestamp, or Instant."); } serverTimestamps.add(propertyName(field)); } @@ -997,13 +1016,15 @@ private void applyFieldAnnotations(Field field) { private void applyGetterAnnotations(Method method) { if (method.isAnnotationPresent(ServerTimestamp.class)) { Class returnType = method.getReturnType(); - if (returnType != Date.class && returnType != Timestamp.class) { + if (returnType != Date.class + && returnType != Timestamp.class + && returnType != Instant.class) { throw new IllegalArgumentException( "Method " + method.getName() + " is annotated with @ServerTimestamp but returns " + returnType - + " instead of Date or Timestamp."); + + " instead of Date, Timestamp, or Instant."); } serverTimestamps.add(propertyName(method)); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java index 7d982b86c6..6858e1a14d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java @@ -872,6 +872,7 @@ public void extractFieldMaskFromMerge() throws Exception { "second.foo", "second.geoPointValue", "second.infValue", + "second.instantValue", "second.longValue", "second.nanValue", "second.negInfValue", diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java index 706412dae9..d38779fca5 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java @@ -75,6 +75,7 @@ import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -148,6 +149,7 @@ public final class LocalFirestoreHelper { public static final Date DATE; public static final Timestamp TIMESTAMP; + public static final Instant INSTANT; public static final GeoPoint GEO_POINT; public static final Blob BLOB; public static final FooList FOO_LIST = new FooList<>(); @@ -942,6 +944,7 @@ public static class AllSupportedTypes { public SingleField objectValue = new SingleField(); public Date dateValue = DATE; public Timestamp timestampValue = TIMESTAMP; + public Instant instantValue = INSTANT; public List arrayValue = ImmutableList.of("foo"); public String nullValue = null; public Blob bytesValue = BLOB; @@ -968,6 +971,7 @@ public boolean equals(Object o) { && Objects.equals(objectValue, that.objectValue) && Objects.equals(dateValue, that.dateValue) && Objects.equals(timestampValue, that.timestampValue) + && Objects.equals(instantValue, that.instantValue) && Objects.equals(arrayValue, that.arrayValue) && Objects.equals(nullValue, that.nullValue) && Objects.equals(bytesValue, that.bytesValue) @@ -989,6 +993,10 @@ public boolean equals(Object o) { Timestamp.ofTimeSecondsAndNanos( TimeUnit.MILLISECONDS.toSeconds(DATE.getTime()), 123000); // Firestore truncates to microsecond precision. + INSTANT = + Instant.ofEpochSecond( + TimeUnit.MILLISECONDS.toSeconds(DATE.getTime()), + 123000); // Firestore truncates to microsecond precision. GEO_POINT = new GeoPoint(50.1430847, -122.9477780); BLOB = Blob.fromBytes(new byte[] {1, 2, 3}); SINGLE_FLOAT_MAP = map("float", 0.1F); @@ -1083,6 +1091,7 @@ public boolean equals(Object o) { ALL_SUPPORTED_TYPES_MAP.put("objectValue", map("foo", (Object) "bar")); ALL_SUPPORTED_TYPES_MAP.put("dateValue", Timestamp.of(DATE)); ALL_SUPPORTED_TYPES_MAP.put("timestampValue", TIMESTAMP); + ALL_SUPPORTED_TYPES_MAP.put("instantValue", TIMESTAMP); ALL_SUPPORTED_TYPES_MAP.put("arrayValue", ImmutableList.of("foo")); ALL_SUPPORTED_TYPES_MAP.put("nullValue", null); ALL_SUPPORTED_TYPES_MAP.put("bytesValue", BLOB); @@ -1119,6 +1128,14 @@ public boolean equals(Object o) { .setSeconds(479978400) .setNanos(123000)) // Timestamps supports microsecond precision. .build()) + .put( + "instantValue", + Value.newBuilder() + .setTimestampValue( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(479978400) + .setNanos(123000)) // Instants supports microsecond precision. + .build()) .put( "arrayValue", Value.newBuilder() From 370f4c32020856b2102f5e94e1615010868be936 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 26 Feb 2024 19:16:43 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b8a8fffdc8..83d4551446 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ implementation 'com.google.cloud:google-cloud-firestore' If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-firestore:3.17.0' +implementation 'com.google.cloud:google-cloud-firestore:3.17.1' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.17.0" +libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.17.1" ``` @@ -222,7 +222,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-firestore/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-firestore.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-firestore/3.17.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-firestore/3.17.1 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles