diff --git a/CHANGELOG.md b/CHANGELOG.md index a59dd9cf..09824dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v8.5.1 (2026-01-08) + +- Corrects `StatelessRateDeserializer` and `WebhookDeserializer` to treat all camelCase fields like all other models to properly deserialize JSON fields containing underscors + ## v8.5.0 (2025-12-09) - Adds the following functions: @@ -12,6 +16,7 @@ ## v8.4.1 (2025-12-01) - Adds missing `apiKeys` field to `BaseUser` +- Removes unecessary `SerializedName` entries in `StatelessRate` ## v8.4.0 (2025-11-24) diff --git a/README.md b/README.md index d8802f66..52000eba 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Add this to your project's POM: com.easypost easypost-api-client - 8.5.0 + 8.5.1 ``` @@ -25,7 +25,7 @@ Add this to your project's POM: Add this to your project's build file: ```groovy -implementation "com.easypost:easypost-api-client:8.5.0" +implementation "com.easypost:easypost-api-client:8.5.1" ``` **NOTE:** [Google Gson](http://code.google.com/p/google-gson/) is required. diff --git a/VERSION b/VERSION index 6d289079..f9c71a52 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.5.0 +8.5.1 diff --git a/pom.xml b/pom.xml index 39cdfa20..710fe76a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.easypost easypost-api-client - 8.5.0 + 8.5.1 jar com.easypost:easypost-api-client diff --git a/src/main/java/com/easypost/Constants.java b/src/main/java/com/easypost/Constants.java index 68478a87..cbd00f20 100644 --- a/src/main/java/com/easypost/Constants.java +++ b/src/main/java/com/easypost/Constants.java @@ -1,5 +1,8 @@ package com.easypost; +import java.util.HashMap; +import java.util.List; + import com.easypost.exception.APIException; import com.easypost.http.HashMapSerializer; import com.easypost.model.AddressVerification; @@ -16,9 +19,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.util.HashMap; -import java.util.List; - public abstract class Constants { public static final String EASYPOST_SUPPORT_EMAIL = "support@easypost.com"; @@ -62,12 +62,11 @@ public abstract static class ErrorCodes { public abstract static class CarrierAccountTypes { public static final List CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_WORKFLOW = ImmutableList.of( - "FedexAccount", "FedexSmartpostAccount" - ); + "FedexAccount", "FedexSmartpostAccount"); public static final List CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH = ImmutableList.of( - "AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount", "UspsShipAccount" - ); + "AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount", + "UspsShipAccount"); } public abstract static class Http { @@ -77,13 +76,18 @@ public abstract static class Http { public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000; public static final Gson GSON = new GsonBuilder() + // Standard model deserializer .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + // Core (de)serializers .registerTypeAdapter(HashMap.class, new HashMapSerializer()) - .registerTypeAdapter(SmartRateCollection.class, new SmartRateCollectionDeserializer()) .registerTypeAdapter(APIException.class, new ErrorDeserializer()) + // Model custom deserializers .registerTypeAdapter(AddressVerification.class, new AddressVerificationDeserializer()) + .registerTypeAdapter(SmartRateCollection.class, new SmartRateCollectionDeserializer()) .registerTypeAdapter(StatelessRate[].class, new StatelessRateDeserializer()) - .registerTypeAdapter(Webhook[].class, new WebhookDeserializer()).create(); + .registerTypeAdapter(Webhook[].class, new WebhookDeserializer()) + .create(); + public static final Gson PRETTY_PRINT_GSON = new GsonBuilder().setPrettyPrinting().serializeNulls() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); } diff --git a/src/main/java/com/easypost/model/StatelessRateDeserializer.java b/src/main/java/com/easypost/model/StatelessRateDeserializer.java index ccf1ae42..02902015 100644 --- a/src/main/java/com/easypost/model/StatelessRateDeserializer.java +++ b/src/main/java/com/easypost/model/StatelessRateDeserializer.java @@ -1,14 +1,16 @@ package com.easypost.model; -import com.google.gson.Gson; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import com.easypost.Constants; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import java.lang.reflect.Type; - public final class StatelessRateDeserializer implements JsonDeserializer { /** * Deserialize a StatelessRate from a JSON object. @@ -21,11 +23,20 @@ public final class StatelessRateDeserializer implements JsonDeserializer ratesList = new ArrayList<>(); + for (JsonElement element : results.getAsJsonArray()) { + StatelessRate rate = Constants.Http.GSON.fromJson(element, StatelessRate.class); + ratesList.add(rate); + } + + return ratesList.toArray(new StatelessRate[0]); } } diff --git a/src/main/java/com/easypost/model/WebhookDeserializer.java b/src/main/java/com/easypost/model/WebhookDeserializer.java index e32224c0..9e1a02d1 100644 --- a/src/main/java/com/easypost/model/WebhookDeserializer.java +++ b/src/main/java/com/easypost/model/WebhookDeserializer.java @@ -1,14 +1,16 @@ package com.easypost.model; -import com.google.gson.Gson; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import com.easypost.Constants; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import java.lang.reflect.Type; - public final class WebhookDeserializer implements JsonDeserializer { /** * Deserialize a list of Webhook from a JSON object. @@ -21,11 +23,20 @@ public final class WebhookDeserializer implements JsonDeserializer { */ @Override public Webhook[] deserialize(final JsonElement json, final Type typeOfT, - final JsonDeserializationContext context) throws JsonParseException{ + final JsonDeserializationContext context) throws JsonParseException { JsonObject jo = json.getAsJsonObject(); JsonElement results = jo.get("webhooks"); - Gson gson = new Gson(); - return gson.fromJson(results, Webhook[].class); + if (results == null || !results.isJsonArray()) { + return new Webhook[0]; + } + + List webhooksList = new ArrayList<>(); + for (JsonElement element : results.getAsJsonArray()) { + Webhook webhook = Constants.Http.GSON.fromJson(element, Webhook.class); + webhooksList.add(webhook); + } + + return webhooksList.toArray(new Webhook[0]); } } diff --git a/src/test/java/com/easypost/BetaRateTest.java b/src/test/java/com/easypost/BetaRateTest.java index c9c4ed8a..ddaf651e 100644 --- a/src/test/java/com/easypost/BetaRateTest.java +++ b/src/test/java/com/easypost/BetaRateTest.java @@ -1,18 +1,19 @@ package com.easypost; -import com.easypost.exception.EasyPostException; -import com.easypost.model.StatelessRate; -import com.easypost.utils.Utilities; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.easypost.exception.EasyPostException; +import com.easypost.model.StatelessRate; +import com.easypost.utils.Utilities; public class BetaRateTest { private static TestUtils.VCR vcr; @@ -40,7 +41,10 @@ public void testRetrieveStatelessRates() throws EasyPostException { List rates = vcr.client.betaRate.retrieveStatelessRates(shipment); - assertTrue(rates.stream().allMatch(rate -> rate != null)); + // Test that deserialization worked by accessing a field with an underscore + for (StatelessRate rate : rates) { + assertTrue(rate.getListRate() != null); + } } /** @@ -60,8 +64,8 @@ public void testRetrieveLowestStatelessRate() throws EasyPostException { assertEquals("GroundAdvantage", lowestRate.getService()); List carriers = Arrays.asList("invalidCarrierName"); - EasyPostException exception = - assertThrows(EasyPostException.class, () -> Utilities.getLowestStatelessRate(rates, carriers, null)); + EasyPostException exception = assertThrows(EasyPostException.class, + () -> Utilities.getLowestStatelessRate(rates, carriers, null)); assertEquals("No rates found.", exception.getMessage()); } diff --git a/src/test/java/com/easypost/WebhookTest.java b/src/test/java/com/easypost/WebhookTest.java index 11a57ee4..c35f2400 100644 --- a/src/test/java/com/easypost/WebhookTest.java +++ b/src/test/java/com/easypost/WebhookTest.java @@ -1,26 +1,27 @@ package com.easypost; -import com.easypost.exception.EasyPostException; -import com.easypost.model.Event; -import com.easypost.model.Webhook; -import com.easypost.model.WebhookCustomHeader; -import com.easypost.utils.Utilities; -import com.google.common.collect.ImmutableMap; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import com.easypost.exception.EasyPostException; +import com.easypost.model.Event; +import com.easypost.model.Webhook; +import com.easypost.model.WebhookCustomHeader; +import com.easypost.utils.Utilities; +import com.google.common.collect.ImmutableMap; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public final class WebhookTest { @@ -119,6 +120,10 @@ public void testAll() throws EasyPostException { assertTrue(webhooks.size() > 0); assertTrue(webhooks.stream().allMatch(webhook -> webhook != null)); + // Test that deserialization worked by accessing a field with an underscore + for (Webhook webhook : webhooks) { + assertTrue(webhook.getCreatedAt() != null); + } } /**