From 15e7cf0a53c8c01ad9a057a9ec75084aedf3688f Mon Sep 17 00:00:00 2001 From: timtay-microsoft Date: Thu, 29 Oct 2020 16:43:00 -0700 Subject: [PATCH] Fix digital twins client not deserializing date times correctly (#16975) Also fix BasicDigitalTwinMetadata to use the DigitalTwinPropertyMetadata class --- .../digitaltwins/core/BasicDigitalTwinMetadata.java | 6 +++--- .../core/DigitalTwinPropertyMetadata.java | 4 +++- .../digitaltwins/core/DigitalTwinsAsyncClient.java | 6 ++++-- .../digitaltwins/core/ComponentSyncSamples.java | 12 +++++++++++- .../digitaltwins/core/DigitalTwinsTestBase.java | 3 ++- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/BasicDigitalTwinMetadata.java b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/BasicDigitalTwinMetadata.java index 4eeb513d2a6a4..963dd912e9d71 100644 --- a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/BasicDigitalTwinMetadata.java +++ b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/BasicDigitalTwinMetadata.java @@ -25,7 +25,7 @@ public final class BasicDigitalTwinMetadata { private String modelId; @JsonIgnore - private final Map propertyMetadata = new HashMap<>(); + private final Map propertyMetadata = new HashMap<>(); /** * Creates an instance of digital twin metadata. @@ -56,7 +56,7 @@ public BasicDigitalTwinMetadata setModelId(String modelId) { * @return The metadata about changes on properties on a component. */ @JsonAnyGetter - public Map getPropertyMetadata() { + public Map getPropertyMetadata() { return propertyMetadata; } @@ -68,7 +68,7 @@ public Map getPropertyMetadata() { * @return The BasicDigitalTwin object itself. */ @JsonAnySetter - public BasicDigitalTwinMetadata addPropertyMetadata(String key, Object value) { + public BasicDigitalTwinMetadata addPropertyMetadata(String key, DigitalTwinPropertyMetadata value) { this.propertyMetadata.put(key, value); return this; } diff --git a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinPropertyMetadata.java b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinPropertyMetadata.java index fca1389b51f66..891169ecddfde 100644 --- a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinPropertyMetadata.java +++ b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinPropertyMetadata.java @@ -3,6 +3,7 @@ package com.azure.digitaltwins.core; +import com.azure.core.annotation.Fluent; import com.azure.digitaltwins.core.models.DigitalTwinsJsonPropertyNames; import com.fasterxml.jackson.annotation.JsonProperty; @@ -11,7 +12,8 @@ /** * Contains metadata about changes on properties on a digital twin or component. */ -public class DigitalTwinPropertyMetadata { +@Fluent +public final class DigitalTwinPropertyMetadata { @JsonProperty(value = DigitalTwinsJsonPropertyNames.METADATA_PROPERTY_LAST_UPDATE_TIME, required = true) private OffsetDateTime lastUpdatedOn; diff --git a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinsAsyncClient.java b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinsAsyncClient.java index 95c0b77ce0b0b..e445ab52a9438 100644 --- a/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinsAsyncClient.java +++ b/sdk/digitaltwins/azure-digitaltwins-core/src/main/java/com/azure/digitaltwins/core/DigitalTwinsAsyncClient.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import reactor.core.publisher.Mono; import java.util.ArrayList; @@ -53,7 +54,7 @@ @ServiceClient(builder = DigitalTwinsClientBuilder.class, isAsync = true) public final class DigitalTwinsAsyncClient { private static final ClientLogger logger = new ClientLogger(DigitalTwinsAsyncClient.class); - private static final ObjectMapper mapper = new ObjectMapper(); + private ObjectMapper mapper; private final DigitalTwinsServiceVersion serviceVersion; private final AzureDigitalTwinsAPIImpl protocolLayer; private static final Boolean includeModelDefinitionOnGet = true; @@ -61,9 +62,10 @@ public final class DigitalTwinsAsyncClient { DigitalTwinsAsyncClient(String serviceEndpoint, HttpPipeline pipeline, DigitalTwinsServiceVersion serviceVersion, JsonSerializer jsonSerializer) { final SimpleModule stringModule = new SimpleModule("String Serializer"); - stringModule.addSerializer(new DigitalTwinsStringSerializer(String.class, mapper)); JacksonAdapter jacksonAdapter = new JacksonAdapter(); + mapper = jacksonAdapter.serializer(); // Use the same mapper in this layer that the generated layer will use + stringModule.addSerializer(new DigitalTwinsStringSerializer(String.class, mapper)); jacksonAdapter.serializer().registerModule(stringModule); this.serviceVersion = serviceVersion; diff --git a/sdk/digitaltwins/azure-digitaltwins-core/src/samples/java/com/azure/digitaltwins/core/ComponentSyncSamples.java b/sdk/digitaltwins/azure-digitaltwins-core/src/samples/java/com/azure/digitaltwins/core/ComponentSyncSamples.java index b60698067d071..324dd08805d73 100644 --- a/sdk/digitaltwins/azure-digitaltwins-core/src/samples/java/com/azure/digitaltwins/core/ComponentSyncSamples.java +++ b/sdk/digitaltwins/azure-digitaltwins-core/src/samples/java/com/azure/digitaltwins/core/ComponentSyncSamples.java @@ -13,10 +13,15 @@ import com.azure.identity.ClientSecretCredentialBuilder; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import javax.net.ssl.HttpsURLConnection; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Random; import java.util.function.Function; public class ComponentSyncSamples { @@ -46,6 +51,10 @@ public static void main(String[] args) throws IOException { .setLogLevel(parsedArguments.getHttpLogDetailLevel())) .buildClient(); + // This mapper gets used to deserialize a digital twin that has a date time within a property metadata, so it + // needs to have this module in order to correctly deserialize that date time + mapper.registerModule(new JavaTimeModule()); + runComponentSample(); } @@ -145,6 +154,7 @@ public static void runComponentSample() throws JsonProcessingException { ConsoleLogger.print("Retrieved component for digital twin " + basicDigitalTwinId + " :"); for (String key : getComponentResponse.getContents().keySet()) { ConsoleLogger.print("\t" + key + " : " + getComponentResponse.getContents().get(key)); + ConsoleLogger.print("\t\tLast updated on: " + getComponentResponse.getMetadata().get(key).getLastUpdatedOn()); } // Clean up diff --git a/sdk/digitaltwins/azure-digitaltwins-core/src/test/java/com/azure/digitaltwins/core/DigitalTwinsTestBase.java b/sdk/digitaltwins/azure-digitaltwins-core/src/test/java/com/azure/digitaltwins/core/DigitalTwinsTestBase.java index 2a91f03d7d770..08a7fdc0441be 100644 --- a/sdk/digitaltwins/azure-digitaltwins-core/src/test/java/com/azure/digitaltwins/core/DigitalTwinsTestBase.java +++ b/sdk/digitaltwins/azure-digitaltwins-core/src/test/java/com/azure/digitaltwins/core/DigitalTwinsTestBase.java @@ -12,6 +12,7 @@ import com.azure.identity.ClientSecretCredentialBuilder; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import reactor.core.publisher.Mono; import java.time.OffsetDateTime; @@ -125,6 +126,6 @@ public Mono getToken(TokenRequestContext tokenRequestContext) { // Used for converting json strings into BasicDigitalTwins, BasicRelationships, etc. static T deserializeJsonString(String rawJsonString, Class clazz) throws JsonProcessingException { - return new ObjectMapper().readValue(rawJsonString, clazz); + return new ObjectMapper().registerModule(new JavaTimeModule()).readValue(rawJsonString, clazz); } }