Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Add this to your project's POM:
<dependency>
<groupId>com.easypost</groupId>
<artifactId>easypost-api-client</artifactId>
<version>8.5.0</version>
<version>8.5.1</version>
</dependency>
```

Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.5.0
8.5.1
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>com.easypost</groupId>
<artifactId>easypost-api-client</artifactId>

<version>8.5.0</version>
<version>8.5.1</version>
<packaging>jar</packaging>

<name>com.easypost:easypost-api-client</name>
Expand Down
22 changes: 13 additions & 9 deletions src/main/java/com/easypost/Constants.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 = "[email protected]";
Expand Down Expand Up @@ -62,12 +62,11 @@ public abstract static class ErrorCodes {

public abstract static class CarrierAccountTypes {
public static final List<String> CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_WORKFLOW = ImmutableList.of(
"FedexAccount", "FedexSmartpostAccount"
);
"FedexAccount", "FedexSmartpostAccount");

public static final List<String> CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH = ImmutableList.of(
"AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount", "UspsShipAccount"
);
"AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount",
"UspsShipAccount");
}

public abstract static class Http {
Expand All @@ -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();
}
Expand Down
23 changes: 17 additions & 6 deletions src/main/java/com/easypost/model/StatelessRateDeserializer.java
Original file line number Diff line number Diff line change
@@ -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<StatelessRate[]> {
/**
* Deserialize a StatelessRate from a JSON object.
Expand All @@ -21,11 +23,20 @@ public final class StatelessRateDeserializer implements JsonDeserializer<Statele
*/
@Override
public StatelessRate[] 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("rates");
Gson gson = new Gson();

return gson.fromJson(results, StatelessRate[].class);
if (results == null || !results.isJsonArray()) {
return new StatelessRate[0];
}

List<StatelessRate> 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]);
}
}
23 changes: 17 additions & 6 deletions src/main/java/com/easypost/model/WebhookDeserializer.java
Original file line number Diff line number Diff line change
@@ -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<Webhook[]> {
/**
* Deserialize a list of Webhook from a JSON object.
Expand All @@ -21,11 +23,20 @@ public final class WebhookDeserializer implements JsonDeserializer<Webhook[]> {
*/
@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<Webhook> 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]);
}
}
26 changes: 15 additions & 11 deletions src/test/java/com/easypost/BetaRateTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -40,7 +41,10 @@ public void testRetrieveStatelessRates() throws EasyPostException {

List<StatelessRate> 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);
}
}

/**
Expand All @@ -60,8 +64,8 @@ public void testRetrieveLowestStatelessRate() throws EasyPostException {
assertEquals("GroundAdvantage", lowestRate.getService());

List<String> 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());
}
Expand Down
35 changes: 20 additions & 15 deletions src/test/java/com/easypost/WebhookTest.java
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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);
}
}

/**
Expand Down
Loading