diff --git a/CHANGELOG.md b/CHANGELOG.md index b660022db..46be9d20d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ## Next Release - Adds `WebhookCustomHeader` model, allowing `custom_headers` to be passed when creating/updating a webhook +- Fixes error parsing + - Allows for alternative format of `errors` field (previously we deserialized the `errors` field into a list of `Error` objects; however, sometimes the errors are simply a list of strings. This change make the `errors` field a list of `Object` allowing for either the new `FieldError` object or a list of strings. Users will need to check for the type of error returned and handle appropriately) + - Removed the unused `Error` model + - Added an explicit `AddressVerificationFieldError` model + - The `BetaPaymentRefund` now uses a list of `FieldError` instead of `Error` for the `errors` field - Corrects payload wrapping for updating a webhook - Bumps dependencies diff --git a/src/main/java/com/easypost/Constants.java b/src/main/java/com/easypost/Constants.java index c470d63b7..c36f743b5 100644 --- a/src/main/java/com/easypost/Constants.java +++ b/src/main/java/com/easypost/Constants.java @@ -1,9 +1,9 @@ package com.easypost; +import com.easypost.exception.APIException; import com.easypost.http.HashMapSerializer; import com.easypost.model.AddressVerification; import com.easypost.model.AddressVerificationDeserializer; -import com.easypost.model.Error; import com.easypost.model.ErrorDeserializer; import com.easypost.model.SmartrateCollection; import com.easypost.model.SmartrateCollectionDeserializer; @@ -80,7 +80,7 @@ public abstract static class Http { .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(HashMap.class, new HashMapSerializer()) .registerTypeAdapter(SmartrateCollection.class, new SmartrateCollectionDeserializer()) - .registerTypeAdapter(Error.class, new ErrorDeserializer()) + .registerTypeAdapter(APIException.class, new ErrorDeserializer()) .registerTypeAdapter(AddressVerification.class, new AddressVerificationDeserializer()) .registerTypeAdapter(StatelessRate[].class, new StatelessRateDeserializer()) .registerTypeAdapter(Webhook[].class, new WebhookDeserializer()).create(); diff --git a/src/main/java/com/easypost/exception/API/BadRequestError.java b/src/main/java/com/easypost/exception/API/BadRequestError.java index 4bb1b31da..becea3047 100644 --- a/src/main/java/com/easypost/exception/API/BadRequestError.java +++ b/src/main/java/com/easypost/exception/API/BadRequestError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class BadRequestError extends APIException { /** * BadRequestError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public BadRequestError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public BadRequestError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/ForbiddenError.java b/src/main/java/com/easypost/exception/API/ForbiddenError.java index 4f5d9070e..bb6165365 100644 --- a/src/main/java/com/easypost/exception/API/ForbiddenError.java +++ b/src/main/java/com/easypost/exception/API/ForbiddenError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class ForbiddenError extends APIException { /** * ForbiddenError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public ForbiddenError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public ForbiddenError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/GatewayTimeoutError.java b/src/main/java/com/easypost/exception/API/GatewayTimeoutError.java index ab42e2015..3592e218e 100644 --- a/src/main/java/com/easypost/exception/API/GatewayTimeoutError.java +++ b/src/main/java/com/easypost/exception/API/GatewayTimeoutError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class GatewayTimeoutError extends APIException { /** * GatewayTimeoutError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public GatewayTimeoutError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public GatewayTimeoutError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/InternalServerError.java b/src/main/java/com/easypost/exception/API/InternalServerError.java index fff3980bd..a8b6e0769 100644 --- a/src/main/java/com/easypost/exception/API/InternalServerError.java +++ b/src/main/java/com/easypost/exception/API/InternalServerError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class InternalServerError extends APIException { /** * InternalServerError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public InternalServerError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public InternalServerError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/InvalidRequestError.java b/src/main/java/com/easypost/exception/API/InvalidRequestError.java index 26d32a77f..69b770831 100644 --- a/src/main/java/com/easypost/exception/API/InvalidRequestError.java +++ b/src/main/java/com/easypost/exception/API/InvalidRequestError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class InvalidRequestError extends APIException { /** * InvalidRequestError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public InvalidRequestError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public InvalidRequestError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/MethodNotAllowedError.java b/src/main/java/com/easypost/exception/API/MethodNotAllowedError.java index 719b40169..a72b643eb 100644 --- a/src/main/java/com/easypost/exception/API/MethodNotAllowedError.java +++ b/src/main/java/com/easypost/exception/API/MethodNotAllowedError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class MethodNotAllowedError extends APIException { /** * MethodNotAllowedError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public MethodNotAllowedError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public MethodNotAllowedError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/NotFoundError.java b/src/main/java/com/easypost/exception/API/NotFoundError.java index 688aa142c..7722d2693 100644 --- a/src/main/java/com/easypost/exception/API/NotFoundError.java +++ b/src/main/java/com/easypost/exception/API/NotFoundError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class NotFoundError extends APIException { /** * NotFoundError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public NotFoundError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public NotFoundError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/PaymentError.java b/src/main/java/com/easypost/exception/API/PaymentError.java index c1dddde13..0ee85bfb0 100644 --- a/src/main/java/com/easypost/exception/API/PaymentError.java +++ b/src/main/java/com/easypost/exception/API/PaymentError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class PaymentError extends APIException { /** * PaymentError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public PaymentError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public PaymentError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/RateLimitError.java b/src/main/java/com/easypost/exception/API/RateLimitError.java index 896a417a8..4e3d67c6a 100644 --- a/src/main/java/com/easypost/exception/API/RateLimitError.java +++ b/src/main/java/com/easypost/exception/API/RateLimitError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class RateLimitError extends APIException { /** * RateLimitError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public RateLimitError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public RateLimitError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/RedirectError.java b/src/main/java/com/easypost/exception/API/RedirectError.java index c00ad1ef1..7de23bf51 100644 --- a/src/main/java/com/easypost/exception/API/RedirectError.java +++ b/src/main/java/com/easypost/exception/API/RedirectError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class RedirectError extends APIException { /** * RedirectError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public RedirectError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public RedirectError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/ServiceUnavailableError.java b/src/main/java/com/easypost/exception/API/ServiceUnavailableError.java index 0756428b0..fe13a6e23 100644 --- a/src/main/java/com/easypost/exception/API/ServiceUnavailableError.java +++ b/src/main/java/com/easypost/exception/API/ServiceUnavailableError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class ServiceUnavailableError extends APIException { /** * ServiceUnavailablError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public ServiceUnavailableError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public ServiceUnavailableError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/TimeoutError.java b/src/main/java/com/easypost/exception/API/TimeoutError.java index 28993acfe..239966ad5 100644 --- a/src/main/java/com/easypost/exception/API/TimeoutError.java +++ b/src/main/java/com/easypost/exception/API/TimeoutError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class TimeoutError extends APIException { /** * TimeoutError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public TimeoutError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public TimeoutError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/UnauthorizedError.java b/src/main/java/com/easypost/exception/API/UnauthorizedError.java index 1434cc183..9fcee9262 100644 --- a/src/main/java/com/easypost/exception/API/UnauthorizedError.java +++ b/src/main/java/com/easypost/exception/API/UnauthorizedError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class UnauthorizedError extends APIException { /** * UnauthorizedError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public UnauthorizedError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public UnauthorizedError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/API/UnknownApiError.java b/src/main/java/com/easypost/exception/API/UnknownApiError.java index 423464ac1..6a1796b12 100644 --- a/src/main/java/com/easypost/exception/API/UnknownApiError.java +++ b/src/main/java/com/easypost/exception/API/UnknownApiError.java @@ -1,20 +1,19 @@ package com.easypost.exception.API; -import java.util.List; - -import com.easypost.model.Error; import com.easypost.exception.APIException; +import java.util.List; + public class UnknownApiError extends APIException { /** * UnknownApiError constructor. * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array + * @param statusCode the exception status code */ - public UnknownApiError(final String message, final String code, final int statusCode, List errors) { - super(message, code, statusCode, errors); + public UnknownApiError(final String message, final String code, List errors, final int statusCode) { + super(message, code, errors, statusCode); } } diff --git a/src/main/java/com/easypost/exception/APIException.java b/src/main/java/com/easypost/exception/APIException.java index 5bed1bed8..991a783c1 100644 --- a/src/main/java/com/easypost/exception/APIException.java +++ b/src/main/java/com/easypost/exception/APIException.java @@ -1,15 +1,5 @@ -/** - * APIException.java - * This file is a part of EasyPost API SDK. - * (c) 2022 EasyPost - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - package com.easypost.exception; -import com.easypost.model.Error; - import java.util.List; public class APIException extends EasyPostException { @@ -17,7 +7,7 @@ public class APIException extends EasyPostException { private final String code; private final Integer statusCode; private final String message; - private final List errors; + private final List errors; /** * APIException constructor. @@ -25,7 +15,7 @@ public class APIException extends EasyPostException { * @param message the exception message */ public APIException(final String message) { - this(message, null); + this(message, null, null, null, null); } /** @@ -43,11 +33,10 @@ public APIException(final String message, final Throwable ex) { * * @param message the exception message * @param code the exception code - * @param statusCode the exception status code * @param errors the errors array */ - public APIException(final String message, final String code, final int statusCode, final List errors) { - this(message, code, statusCode, errors, null); + public APIException(final String message, final String code, final List errors) { + this(message, code, errors, null, null); } /** @@ -55,22 +44,34 @@ public APIException(final String message, final String code, final int statusCod * * @param message the exception message * @param code the exception code + * @param errors the errors array * @param statusCode the exception status code + */ + public APIException(final String message, final String code, final List errors, final Integer statusCode) { + this(message, code, errors, statusCode, null); + } + + /** + * APIException constructor. + * + * @param message the exception message + * @param code the exception code * @param errors the errors array + * @param statusCode the exception status code * @param ex the exception cause */ - public APIException(final String message, final String code, final Integer statusCode, - final List errors, final Throwable ex) { + public APIException(final String message, final String code, final List errors, + final Integer statusCode, final Throwable ex) { super(message); - this.code = code; - this.statusCode = statusCode; this.message = message; + this.code = code; this.errors = errors; + this.statusCode = statusCode; } /** * Get status code of the error object. - * + * * @return statusCode the status code of the error object */ public Integer getStatusCode() { @@ -101,7 +102,7 @@ public String getMessage() { * * @return errors of the exception */ - public List getErrors() { + public List getErrors() { return errors; } } diff --git a/src/main/java/com/easypost/exception/EasyPostException.java b/src/main/java/com/easypost/exception/EasyPostException.java index 47c2ff59f..2e396ad41 100644 --- a/src/main/java/com/easypost/exception/EasyPostException.java +++ b/src/main/java/com/easypost/exception/EasyPostException.java @@ -1,11 +1,3 @@ -/** - * EasyPostException.java - * This file is a part of EasyPost API SDK. - * (c) 2022 EasyPost - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - package com.easypost.exception; public class EasyPostException extends Exception { diff --git a/src/main/java/com/easypost/http/EasyPostResponse.java b/src/main/java/com/easypost/http/EasyPostResponse.java index 65a84cbf4..b1864924f 100644 --- a/src/main/java/com/easypost/http/EasyPostResponse.java +++ b/src/main/java/com/easypost/http/EasyPostResponse.java @@ -1,11 +1,3 @@ -/** - * EasyPostResponse.java - * This file is a part of EasyPost API SDK. - * (c) 2022 EasyPost - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - package com.easypost.http; import lombok.Getter; diff --git a/src/main/java/com/easypost/http/Requestor.java b/src/main/java/com/easypost/http/Requestor.java index 447b1e484..5c3592e7f 100644 --- a/src/main/java/com/easypost/http/Requestor.java +++ b/src/main/java/com/easypost/http/Requestor.java @@ -1,15 +1,8 @@ -/** - * EasyPostResource.java - * This file is a part of EasyPost API SDK. - * (c) 2022 EasyPost - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - package com.easypost.http; import com.easypost.Constants; import com.easypost.EasyPost; +import com.easypost.exception.APIException; import com.easypost.exception.API.BadRequestError; import com.easypost.exception.API.EncodingError; import com.easypost.exception.API.ForbiddenError; @@ -31,7 +24,6 @@ import com.easypost.hooks.RequestHookResponses; import com.easypost.hooks.ResponseHookResponses; import com.easypost.model.EasyPostResource; -import com.easypost.model.Error; import com.easypost.service.EasyPostClient; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -650,42 +642,42 @@ protected static void handleAPIError(String rBody, final int rCode) if (rBody == null || rBody.length() == 0) { rBody = "{}"; } - Error error = Constants.Http.GSON.fromJson(rBody, Error.class); + APIException error = Constants.Http.GSON.fromJson(rBody, APIException.class); String errorMessage = error.getMessage(); String errorCode = error.getCode(); - List errors = error.getErrors(); + List errors = error.getErrors(); if (rCode >= Constants.ErrorCodes.REDIRECT_CODE_BEGIN && rCode <= Constants.ErrorCodes.REDIRECT_CODE_END) { - throw new RedirectError(errorMessage, errorCode, rCode, errors); + throw new RedirectError(errorMessage, errorCode, errors, rCode); } switch (rCode) { case Constants.ErrorCodes.UNAUTHORIZED_ERROR: - throw new UnauthorizedError(errorMessage, errorCode, rCode, errors); + throw new UnauthorizedError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.FORBIDDEN_ERROR: - throw new ForbiddenError(errorMessage, errorCode, rCode, errors); + throw new ForbiddenError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.PAYMENT_ERROR: - throw new PaymentError(errorMessage, errorCode, rCode, errors); + throw new PaymentError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.NOT_FOUND_ERROR: - throw new NotFoundError(errorMessage, errorCode, rCode, errors); + throw new NotFoundError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.METHOD_NOT_ALLOWED_ERROR: - throw new MethodNotAllowedError(errorMessage, errorCode, rCode, errors); + throw new MethodNotAllowedError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.TIMEOUT_ERROR: - throw new TimeoutError(errorMessage, errorCode, rCode, errors); + throw new TimeoutError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.BAD_REQUEST_ERROR: - throw new BadRequestError(errorMessage, errorCode, rCode, errors); + throw new BadRequestError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.INVALID_REQUEST_ERROR: - throw new InvalidRequestError(errorMessage, errorCode, rCode, errors); + throw new InvalidRequestError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.RATE_LIMIT_ERROR: - throw new RateLimitError(errorMessage, errorCode, rCode, errors); + throw new RateLimitError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.INTERNAL_SERVER_ERROR: - throw new InternalServerError(errorMessage, errorCode, rCode, errors); + throw new InternalServerError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.SERVICE_UNAVAILABLE_ERROR: - throw new ServiceUnavailableError(errorMessage, errorCode, rCode, errors); + throw new ServiceUnavailableError(errorMessage, errorCode, errors, rCode); case Constants.ErrorCodes.GATEWAY_TIMEOUT_ERROR: - throw new GatewayTimeoutError(errorMessage, errorCode, rCode, errors); + throw new GatewayTimeoutError(errorMessage, errorCode, errors, rCode); default: - throw new UnknownApiError(errorMessage, errorCode, rCode, errors); + throw new UnknownApiError(errorMessage, errorCode, errors, rCode); } } diff --git a/src/main/java/com/easypost/model/AddressVerification.java b/src/main/java/com/easypost/model/AddressVerification.java index 5e8dc7e3b..4853b3d82 100644 --- a/src/main/java/com/easypost/model/AddressVerification.java +++ b/src/main/java/com/easypost/model/AddressVerification.java @@ -6,7 +6,7 @@ @Getter public final class AddressVerification { private Boolean success; - private List errors; + private List errors; private AddressDetail details; /** @@ -23,7 +23,7 @@ void setSuccess(final boolean success) { * * @param errors The errors. */ - void setErrors(final List errors) { + void setErrors(final List errors) { this.errors = errors; } diff --git a/src/main/java/com/easypost/model/AddressVerificationDeserializer.java b/src/main/java/com/easypost/model/AddressVerificationDeserializer.java index 719918297..1523fe4c8 100644 --- a/src/main/java/com/easypost/model/AddressVerificationDeserializer.java +++ b/src/main/java/com/easypost/model/AddressVerificationDeserializer.java @@ -37,11 +37,11 @@ public AddressVerification deserialize(final JsonElement json, final Type typeOf if (errorsAsJson != null) { JsonArray errorsAsArray = errorsAsJson.getAsJsonArray(); - ArrayList errors = new ArrayList<>(); + ArrayList errors = new ArrayList<>(); for (JsonElement errorAsJson : errorsAsArray) { JsonObject errorAsJsonObject = errorAsJson.getAsJsonObject(); - Error error = new Error(); + AddressVerificationFieldError error = new AddressVerificationFieldError(); JsonElement code = errorAsJsonObject.get("code"); if (code != null) { diff --git a/src/main/java/com/easypost/model/Error.java b/src/main/java/com/easypost/model/AddressVerificationFieldError.java similarity index 72% rename from src/main/java/com/easypost/model/Error.java rename to src/main/java/com/easypost/model/AddressVerificationFieldError.java index e3b2dfc6f..64b9e86dc 100644 --- a/src/main/java/com/easypost/model/Error.java +++ b/src/main/java/com/easypost/model/AddressVerificationFieldError.java @@ -1,16 +1,13 @@ package com.easypost.model; -import java.util.List; import lombok.Getter; @Getter -@SuppressWarnings("JavaLangClash") -public final class Error { +public final class AddressVerificationFieldError { private String message; private String code; - private List errors; - private String suggestion; private String field; + private String suggestion; /** * Set the message of this error object. @@ -24,19 +21,19 @@ void setMessage(final String message) { /** * Set the code of this error object. * - * @param code The error code. + * @param code The code. */ void setCode(final String code) { this.code = code; } /** - * Set the errors of this error object. + * Set the field of this error object. * - * @param errors The errors. + * @param field The field. */ - void setErrors(final List errors) { - this.errors = errors; + void setField(final String field) { + this.field = field; } /** @@ -47,13 +44,4 @@ void setErrors(final List errors) { void setSuggestion(final String suggestion) { this.suggestion = suggestion; } - - /** - * Set the field of this error object. - * - * @param field The field. - */ - void setField(final String field) { - this.field = field; - } } diff --git a/src/main/java/com/easypost/model/BetaPaymentRefund.java b/src/main/java/com/easypost/model/BetaPaymentRefund.java index 6970b5529..3dc22709f 100644 --- a/src/main/java/com/easypost/model/BetaPaymentRefund.java +++ b/src/main/java/com/easypost/model/BetaPaymentRefund.java @@ -6,7 +6,7 @@ @Getter public class BetaPaymentRefund extends EasyPostResource{ private int refundedAmount; - private List errors; + private List errors; private List refundedPaymentLogs; private String paymentLogId; private String refundedAmountCurrencys; diff --git a/src/main/java/com/easypost/model/EasyPostResource.java b/src/main/java/com/easypost/model/EasyPostResource.java index 4fe9938f6..c8873a936 100644 --- a/src/main/java/com/easypost/model/EasyPostResource.java +++ b/src/main/java/com/easypost/model/EasyPostResource.java @@ -1,11 +1,3 @@ -/** - * EasyPostResource.java - * This file is a part of EasyPost API SDK. - * (c) 2022 EasyPost - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - package com.easypost.model; import java.util.Date; diff --git a/src/main/java/com/easypost/model/ErrorDeserializer.java b/src/main/java/com/easypost/model/ErrorDeserializer.java index 67f112b1b..a8322d1db 100644 --- a/src/main/java/com/easypost/model/ErrorDeserializer.java +++ b/src/main/java/com/easypost/model/ErrorDeserializer.java @@ -1,20 +1,20 @@ package com.easypost.model; import com.easypost.Constants; -import com.google.gson.Gson; +import com.easypost.exception.APIException; import com.google.gson.JsonArray; 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 com.google.gson.JsonPrimitive; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.List; import java.util.Map.Entry; -public final class ErrorDeserializer implements JsonDeserializer { +public final class ErrorDeserializer implements JsonDeserializer { /** * Recursively traverse an error JSON element and its sub-element(s), and extracts all * error string values found into the specified string list. @@ -39,41 +39,80 @@ private void traverseJsonElement(JsonElement element, ArrayList messages } /** - * Deserialize an Error from a JSON object. + * Deserialize an APIException from a JSON object. * * @param json JSON object to deserialize. * @param typeOfT Type of the object to deserialize. * @param context Deserialization context. - * @return Deserialized Error object. + * @return Deserialized APIException object. * @throws JsonParseException if the JSON object is not a valid SmartrateCollection. */ @Override - public Error deserialize(final JsonElement json, final Type typeOfT, - final JsonDeserializationContext context) throws JsonParseException { + public APIException deserialize(final JsonElement json, final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { JsonObject jo = json.getAsJsonObject(); - JsonElement results = jo.get("error"); - Gson gson = new Gson(); - if (results == null) { - Error error = new Error(); - error.setMessage(Constants.ErrorMessages.API_DID_NOT_RETURN_ERROR_DETAILS); - error.setCode("NO RESPONSE CODE"); - return error; + String message = null; + String code = null; + List errors = new ArrayList<>(); + + JsonElement errorResponse = jo.get("error"); + if (errorResponse == null) { + message = Constants.ErrorMessages.API_DID_NOT_RETURN_ERROR_DETAILS; + code = "NO RESPONSE CODE"; + return new APIException(message, code, null); + } + JsonObject errorData = errorResponse.getAsJsonObject(); + + JsonElement codeElement = errorData.get("code"); + if (codeElement != null) { + code = codeElement.getAsString(); + } + + JsonElement messageElement = errorData.get("message"); + if (messageElement != null) { + if (messageElement.isJsonPrimitive()) { + message = messageElement.getAsString(); + } else if (messageElement.isJsonObject() || messageElement.isJsonArray()) { + ArrayList messagesList = new ArrayList<>(); + traverseJsonElement(messageElement, messagesList); + message = String.join(", ", messagesList); + } else { + throw new JsonParseException("Invalid message format"); + } } - try { - ArrayList messages = new ArrayList<>(); - JsonElement errorMessageJson = results.getAsJsonObject().get("message"); - traverseJsonElement(errorMessageJson, messages); - JsonPrimitive value = new JsonPrimitive(String.join(", ", messages)); - results.getAsJsonObject().add("message", value); - } catch (Exception e) { - Error error = new Error(); - error.setMessage("Error deserializing JSON response"); - error.setCode("ERROR_DESERIALIZATION_ERROR"); - return error; + JsonElement errorsAsJson = errorData.get("errors"); + List errorList = new ArrayList<>(); + if (errorsAsJson != null) { + JsonArray errorsAsArray = errorsAsJson.getAsJsonArray(); + for (JsonElement errorAsJson : errorsAsArray) { + if (errorAsJson.isJsonObject()) { + JsonObject errorAsJsonObject = errorAsJson.getAsJsonObject(); + FieldError fieldError = new FieldError(); + + JsonElement field = errorAsJsonObject.get("field"); + if (field != null) { + fieldError.setField(field.getAsString()); + } + + JsonElement fieldMessage = errorAsJsonObject.get("message"); + if (fieldMessage != null) { + fieldError.setMessage(fieldMessage.getAsString()); + } + + JsonElement suggestion = errorAsJsonObject.get("suggestion"); + if (suggestion != null && !suggestion.isJsonNull()) { + fieldError.setSuggestion(suggestion.getAsString()); + } + + errorList.add(fieldError); + } else { + errorList.add(errorAsJson.getAsString()); + } + } } - return gson.fromJson(results, Error.class); + return new APIException(message, code, errorList); } } diff --git a/src/main/java/com/easypost/model/FieldError.java b/src/main/java/com/easypost/model/FieldError.java new file mode 100644 index 000000000..134f273a7 --- /dev/null +++ b/src/main/java/com/easypost/model/FieldError.java @@ -0,0 +1,37 @@ +package com.easypost.model; + +import lombok.Getter; + +@Getter +public final class FieldError { + private String field; + private String message; + private String suggestion; + + /** + * Set the field of this error object. + * + * @param field The field. + */ + void setField(final String field) { + this.field = field; + } + + /** + * Set the message of this error object. + * + * @param message The error message. + */ + void setMessage(final String message) { + this.message = message; + } + + /** + * Set the suggestion of this error object. + * + * @param suggestion The suggestion. + */ + void setSuggestion(final String suggestion) { + this.suggestion = suggestion; + } +} diff --git a/src/test/cassettes/error/error_alternative_format.json b/src/test/cassettes/error/error_alternative_format.json new file mode 100644 index 000000000..6faed463c --- /dev/null +++ b/src/test/cassettes/error/error_alternative_format.json @@ -0,0 +1,91 @@ +[ + { + "recordedAt": 1740505524, + "request": { + "body": "{\n \"email_evidence_attachments\": [\n \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAeUlEQVR42mP8//8/AwAI/AL+4Q7AIAAAAABJRU5ErkJggg\\u003d\\u003d\"\n ],\n \"supporting_documentation_attachments\": [\n \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAeUlEQVR42mP8//8/AwAI/AL+4Q7AIAAAAABJRU5ErkJggg\\u003d\\u003d\"\n ],\n \"description\": \"Test description\",\n \"type\": \"damage\",\n \"invoice_attachments\": [\n \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAeUlEQVR42mP8//8/AwAI/AL+4Q7AIAAAAABJRU5ErkJggg\\u003d\\u003d\"\n ],\n \"tracking_code\": \"123\",\n \"contact_email\": \"test@example.com\"\n}", + "method": "POST", + "headers": { + "Accept-Charset": [ + "UTF-8" + ], + "User-Agent": [ + "REDACTED" + ], + "Content-Type": [ + "application/json" + ] + }, + "uri": "https://api.easypost.com/v2/claims" + }, + "response": { + "body": "{\n \"error\": {\n \"code\": \"NOT_FOUND\",\n \"message\": \"The requested resource could not be found.\",\n \"errors\": [\n \"No eligible insurance found with provided tracking code.\"\n ]\n }\n}", + "httpVersion": null, + "headers": { + "null": [ + "HTTP/1.1 404 Not Found" + ], + "content-length": [ + "156" + ], + "expires": [ + "0" + ], + "x-node": [ + "bigweb39nuq" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-download-options": [ + "noopen" + ], + "x-permitted-cross-domain-policies": [ + "none" + ], + "x-backend": [ + "easypost" + ], + "pragma": [ + "no-cache" + ], + "strict-transport-security": [ + "max-age\u003d31536000; includeSubDomains; preload" + ], + "x-xss-protection": [ + "1; mode\u003dblock" + ], + "x-content-type-options": [ + "nosniff" + ], + "x-ep-request-uuid": [ + "c1c6fbd367be01b4e2b8558f001d4c8a" + ], + "x-proxied": [ + "intlb4nuq 51d74985a2", + "extlb1nuq 99aac35317" + ], + "referrer-policy": [ + "strict-origin-when-cross-origin" + ], + "x-runtime": [ + "0.028904" + ], + "content-type": [ + "application/json; charset\u003dutf-8" + ], + "x-version-label": [ + "easypost-202502251451-d4f6425809-master" + ], + "cache-control": [ + "private, no-cache, no-store" + ] + }, + "status": { + "code": 404, + "message": "Not Found" + }, + "uri": "https://api.easypost.com/v2/claims" + }, + "duration": 130 + } +] \ No newline at end of file diff --git a/src/test/java/com/easypost/AddressTest.java b/src/test/java/com/easypost/AddressTest.java index ac7b483bd..87b3dae58 100644 --- a/src/test/java/com/easypost/AddressTest.java +++ b/src/test/java/com/easypost/AddressTest.java @@ -5,6 +5,8 @@ import com.easypost.exception.General.EndOfPaginationError; import com.easypost.model.Address; import com.easypost.model.AddressCollection; +import com.easypost.model.AddressDetail; +import com.easypost.model.AddressVerificationFieldError; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -88,8 +90,28 @@ public void testCreateVerify() throws EasyPostException { address = vcr.client.address.create(addressData); assertInstanceOf(Address.class, address); - assertNotNull(address.getVerifications().getDelivery()); - assertNotNull(address.getVerifications().getZip4()); + + assertEquals(false, address.getVerifications().getDelivery().getSuccess()); + assertInstanceOf(AddressDetail.class, address.getVerifications().getDelivery().getDetails()); + AddressVerificationFieldError addressVerificationFieldErrorGetDelivery = address.getVerifications() + .getDelivery() + .getErrors() + .get(0); + assertEquals("E.ADDRESS.NOT_FOUND", addressVerificationFieldErrorGetDelivery.getCode()); + assertEquals("address", addressVerificationFieldErrorGetDelivery.getField()); + assertNull(addressVerificationFieldErrorGetDelivery.getSuggestion()); + assertEquals("Address not found", addressVerificationFieldErrorGetDelivery.getMessage()); + + assertEquals(false, address.getVerifications().getZip4().getSuccess()); + assertNull(address.getVerifications().getZip4().getDetails()); + AddressVerificationFieldError addressVerificationFieldErrorZip4 = address.getVerifications() + .getZip4() + .getErrors() + .get(0); + assertEquals("E.ADDRESS.NOT_FOUND", addressVerificationFieldErrorZip4.getCode()); + assertEquals("address", addressVerificationFieldErrorZip4.getField()); + assertNull(addressVerificationFieldErrorZip4.getSuggestion()); + assertEquals("Address not found", addressVerificationFieldErrorZip4.getMessage()); } /** diff --git a/src/test/java/com/easypost/CarrierAccountTest.java b/src/test/java/com/easypost/CarrierAccountTest.java index c4002a416..24f3119eb 100644 --- a/src/test/java/com/easypost/CarrierAccountTest.java +++ b/src/test/java/com/easypost/CarrierAccountTest.java @@ -5,6 +5,7 @@ import com.easypost.http.Requestor; import com.easypost.model.CarrierAccount; import com.easypost.model.CarrierType; +import com.easypost.model.FieldError; import com.easypost.model.Pickup; import com.easypost.model.Shipment; import com.google.common.collect.ImmutableMap; @@ -109,8 +110,9 @@ public void testCreateWithCustomWorkflow() throws EasyPostException { // We're sending bad data to the API, so we expect an error assertEquals(422, e.getStatusCode()); // We expect one of the sub-errors to be regarding a missing field - assertTrue(e.getErrors().stream().anyMatch(error -> error.getField().equals("account_number") && - error.getMessage().equals("must be present and a string"))); + FieldError fieldError = (FieldError) e.getErrors().get(0); + assertEquals("shipping_streets", fieldError.getField()); + assertEquals("must be present and a string", fieldError.getMessage()); } } diff --git a/src/test/java/com/easypost/ErrorTest.java b/src/test/java/com/easypost/ErrorTest.java index 8cc9b5636..49e142a5f 100644 --- a/src/test/java/com/easypost/ErrorTest.java +++ b/src/test/java/com/easypost/ErrorTest.java @@ -17,6 +17,7 @@ import com.easypost.exception.APIException; import com.easypost.exception.EasyPostException; import com.easypost.http.Requestor; +import com.easypost.model.FieldError; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -41,7 +42,7 @@ public static void setup() throws EasyPostException { } /** - * Test creating a bad shipment and retrieving errors. + * Tests that we assign properties of an error correctly. * * @throws EasyPostException when the request fails. */ @@ -54,8 +55,30 @@ public void testError() throws EasyPostException { assertEquals(422, exception.getStatusCode()); assertEquals("PARAMETER.REQUIRED", exception.getCode()); assertEquals("Missing required parameter.", exception.getMessage()); - assertEquals("cannot be blank", exception.getErrors().get(0).getMessage()); - assertEquals("shipment", exception.getErrors().get(0).getField()); + FieldError fieldError = (FieldError) exception.getErrors().get(0); + assertEquals("cannot be blank", fieldError.getMessage()); + assertEquals("shipment", fieldError.getField()); + } + + /** + * Tests that we assign properties of an error correctly when returned via the alternative format. + * NOTE: Claims (among other things) uses the alternative errors format. + * + * @throws EasyPostException when the request fails. + */ + @Test + public void testErrorAlternativeFormat() throws EasyPostException { + vcr.setUpTest("error_alternative_format"); + + HashMap claimData = Fixtures.basicClaim(); + claimData.put("tracking_code", "123"); // Intentionally pass a bad tracking code + + APIException exception = assertThrows(NotFoundError.class, () -> vcr.client.claim.create(claimData)); + + assertEquals(404, exception.getStatusCode()); + assertEquals("NOT_FOUND", exception.getCode()); + assertEquals("The requested resource could not be found.", exception.getMessage()); + assertEquals("No eligible insurance found with provided tracking code.", exception.getErrors().get(0)); } /**