diff --git a/CHANGELOG.md b/CHANGELOG.md index 010a9d8..6fb5640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## Next Release + +- Fix inheritance tree for error types, allowing end-user to properly cast and check for specific error types and sub-types + ## v4.5.1 (2024-08-09) - Update HTTP logic to use query for GET/DELETE requests and body for POST/PUT/PATCH requests diff --git a/error.go b/error.go index 15cacb0..a679950 100644 --- a/error.go +++ b/error.go @@ -91,15 +91,42 @@ func (e *LibraryError) Error() string { // LocalError represents an error caused by the EasyPost library itself, such as validation or JSON serialization issues. type LocalError struct { - LibraryError + LibraryError // subtype of LibraryError } -// EndOfPaginationError is raised when there are no more pages to retrieve. -var EndOfPaginationError = &LocalError{LibraryError{Message: NoPagesLeftToRetrieve}} +// Unwrap returns the underlying LibraryError error. +func (e *LocalError) Unwrap() error { + return &e.LibraryError +} + +// EndOfPaginationErrorType is raised when there are no more pages to retrieve. +// TODO: This type will be renamed to EndOfPaginationError in a future release to match the other error types once the EndOfPaginationError helper is removed. +type EndOfPaginationErrorType struct { + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *EndOfPaginationErrorType) Unwrap() error { + return &e.LocalError +} + +// newEndOfPaginationError returns a new EndOfPaginationErrorType object. +func newEndOfPaginationError() *EndOfPaginationErrorType { + return &EndOfPaginationErrorType{LocalError{LibraryError{Message: NoPagesLeftToRetrieve}}} +} + +// EndOfPaginationError is a singleton instance of EndOfPaginationErrorType. +// Deprecated: This helper will be removed in a future release. For access to the underlying message, use easypost.NoPagesLeftToRetrieve instead. +var EndOfPaginationError = newEndOfPaginationError() // FilteringError is raised when there is an issue while running a filtering operation. type FilteringError struct { - LocalError + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *FilteringError) Unwrap() error { + return &e.LocalError } // newFilteringError returns a new FilteringError object with the given message. @@ -109,7 +136,12 @@ func newFilteringError(message string) *FilteringError { // InvalidObjectError is raised when an object is invalid. type InvalidObjectError struct { - LocalError + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *InvalidObjectError) Unwrap() error { + return &e.LocalError } // newInvalidObjectError returns a new InvalidObjectError object with the given message. @@ -119,7 +151,12 @@ func newInvalidObjectError(message string) *InvalidObjectError { // MissingPropertyError is raised when a required property is missing. type MissingPropertyError struct { - LocalError + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *MissingPropertyError) Unwrap() error { + return &e.LocalError } // newMissingPropertyError returns a new MissingPropertyError object with the given property. @@ -128,15 +165,54 @@ func newMissingPropertyError(property string) *MissingPropertyError { return &MissingPropertyError{LocalError{LibraryError{Message: message}}} } +// MissingWebhookSignatureErrorType is raised when a webhook does not contain a valid HMAC signature. +// TODO: This type will be renamed to MissingWebhookSignatureError in a future release to match the other error types once the MissingWebhookSignatureError helper is removed. +type MissingWebhookSignatureErrorType struct { + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *MissingWebhookSignatureErrorType) Unwrap() error { + return &e.LocalError +} + +// newMissingWebhookSignatureError returns a new MissingWebhookSignatureErrorType object. +func newMissingWebhookSignatureError() *MissingWebhookSignatureErrorType { + return &MissingWebhookSignatureErrorType{LocalError{LibraryError{Message: MissingWebhookSignature}}} +} + // MissingWebhookSignatureError is raised when a webhook does not contain a valid HMAC signature. -var MissingWebhookSignatureError = &LocalError{LibraryError{Message: MissingWebhookSignature}} +// Deprecated: This helper will be removed in a future release. For access to the underlying message, use easypost.MissingWebhookSignature instead. +var MissingWebhookSignatureError = newMissingWebhookSignatureError() + +// MismatchWebhookSignatureErrorType is raised when a webhook received did not originate from EasyPost or had a webhook secret mismatch. +// TODO: This type will be renamed to MismatchWebhookSignatureError in a future release to match the other error types once the MismatchWebhookSignatureError helper is removed. +type MismatchWebhookSignatureErrorType struct { + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *MismatchWebhookSignatureErrorType) Unwrap() error { + return &e.LocalError +} + +// newMismatchWebhookSignatureError returns a new MismatchWebhookSignatureErrorType object. +func newMismatchWebhookSignatureError() *MismatchWebhookSignatureErrorType { + return &MismatchWebhookSignatureErrorType{LocalError{LibraryError{Message: MismatchWebhookSignature}}} +} // MismatchWebhookSignatureError is raised when a webhook received did not originate from EasyPost or had a webhook secret mismatch. -var MismatchWebhookSignatureError = &LocalError{LibraryError{Message: MismatchWebhookSignature}} +// Deprecated: This helper will be removed in a future release. For access to the underlying message, use easypost.MismatchWebhookSignature instead. +var MismatchWebhookSignatureError = newMismatchWebhookSignatureError() // ExternalApiError represents an error caused by an external API, such as a 3rd party HTTP API (not EasyPost). type ExternalApiError struct { - LibraryError + LibraryError // subtype of LibraryError +} + +// Unwrap returns the underlying LibraryError object. +func (e *ExternalApiError) Unwrap() error { + return &e.LibraryError } // newExternalApiError returns a new ExternalApiError object with the given message. @@ -146,7 +222,12 @@ func newExternalApiError(message string) *ExternalApiError { // InvalidFunctionError is raised when a function call is invalid or not allowed. type InvalidFunctionError struct { - LocalError + LocalError // subtype of LocalError +} + +// Unwrap returns the underlying LocalError error. +func (e *InvalidFunctionError) Unwrap() error { + return &e.LocalError } // newInvalidFunctionError returns a new InvalidFunctionError object with the given message. @@ -164,7 +245,7 @@ func newInvalidFunctionError(message string) *InvalidFunctionError { // // The information from the top-level Error class is used to generate this error, and any sub-errors are stored in the Errors field. type APIError struct { - LibraryError + LibraryError // subtype of LibraryError // Code is a machine-readable status of the problem encountered. Code string // StatusCode is the HTTP numerical status code of the response. @@ -187,94 +268,188 @@ func (e *APIError) Error() string { return fmt.Sprintf("%d %s", e.StatusCode, e.Code) } +func (e *APIError) Unwrap() error { + return &e.LibraryError +} + // BadRequestError is raised when the API returns a 400 status code. type BadRequestError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *BadRequestError) Unwrap() error { + return &e.APIError } // ConnectionError is raised when the API returns a 0 status code. type ConnectionError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *ConnectionError) Unwrap() error { + return &e.APIError } // GatewayTimeoutError is raised when the API returns a 504 status code. type GatewayTimeoutError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *GatewayTimeoutError) Unwrap() error { + return &e.APIError } // InternalServerError is raised when the API returns a 500 status code. type InternalServerError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *InternalServerError) Unwrap() error { + return &e.APIError } // InvalidRequestError is raised when the API returns a 422 status code. type InvalidRequestError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *InvalidRequestError) Unwrap() error { + return &e.APIError } // MethodNotAllowedError is raised when the API returns a 405 status code. type MethodNotAllowedError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *MethodNotAllowedError) Unwrap() error { + return &e.APIError } // NotFoundError is raised when the API returns a 404 status code. type NotFoundError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *NotFoundError) Unwrap() error { + return &e.APIError } // PaymentError is raised when the API returns a 402 status code. type PaymentError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *PaymentError) Unwrap() error { + return &e.APIError } // ProxyError is raised when the API returns a 407 status code. type ProxyError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *ProxyError) Unwrap() error { + return &e.APIError } // RateLimitError is raised when the API returns a 429 status code. type RateLimitError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *RateLimitError) Unwrap() error { + return &e.APIError } // RedirectError is raised when the API returns a 3xx status code. type RedirectError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *RedirectError) Unwrap() error { + return &e.APIError } // RetryError is raised when the API returns a 1xx status code. type RetryError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *RetryError) Unwrap() error { + return &e.APIError } // ServiceUnavailableError is raised when the API returns a 503 status code. type ServiceUnavailableError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *ServiceUnavailableError) Unwrap() error { + return &e.APIError } // SSLError is raised when there is an issue with the SSL certificate. type SSLError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *SSLError) Unwrap() error { + return &e.APIError } // TimeoutError is raised when the API returns a 408 status code. type TimeoutError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *TimeoutError) Unwrap() error { + return &e.APIError } // UnauthorizedError is raised when the API returns a 401 status code. type UnauthorizedError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *UnauthorizedError) Unwrap() error { + return &e.APIError } // ForbiddenError is raised when the API returns a 403 status code. type ForbiddenError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *ForbiddenError) Unwrap() error { + return &e.APIError } // UnknownHttpError is raised when the API returns an unrecognized status code. type UnknownHttpError struct { - APIError + APIError // subtype of APIError +} + +// Unwrap returns the underlying APIError error. +func (e *UnknownHttpError) Unwrap() error { + return &e.APIError } // BuildErrorFromResponse returns an APIError-based object based on the HTTP response. diff --git a/tests/cassettes/TestBetaReferralAddPaymentMethod.yaml b/tests/cassettes/TestBetaReferralAddPaymentMethod.yaml index 2e3004d..9aaef6c 100644 --- a/tests/cassettes/TestBetaReferralAddPaymentMethod.yaml +++ b/tests/cassettes/TestBetaReferralAddPaymentMethod.yaml @@ -1,53 +1,59 @@ --- version: 1 interactions: -- request: - body: '{"payment_method":{"payment_method_reference":"ba_123","priority":"primary","stripe_customer_id":"cus_123"}}' - form: {} - headers: - Authorization: - - REDACTED - Content-Type: - - application/json - User-Agent: - - REDACTED - url: https://api.easypost.com/beta/referral_customers/payment_method - method: POST - response: - body: '{"error":{"code":"APIKEY.INACTIVE","errors":[],"message":"This api key - is no longer active. Please use a different api key or reactivate this key."}}' - headers: - Content-Type: - - application/json; charset=utf-8 - Referrer-Policy: - - strict-origin-when-cross-origin - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Vary: - - Origin - X-Backend: - - easypost - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Ep-Request-Uuid: - - 4d57346d66ad6851f440b8c700374021 - X-Frame-Options: - - SAMEORIGIN - X-Node: - - bigweb39nuq - X-Permitted-Cross-Domain-Policies: - - none - X-Proxied: - - intlb3nuq c0f5e722d1 - - extlb2nuq fa152d4755 - X-Runtime: - - "0.011152" - X-Version-Label: - - easypost-202408021936-66687f8918-master - X-Xss-Protection: - - 1; mode=block - status: 403 Forbidden - code: 403 - duration: "" + - request: + body: '{"payment_method":{"payment_method_reference":"ba_123","priority":"primary","stripe_customer_id":"cus_123"}}' + form: {} + headers: + Authorization: + - REDACTED + Content-Type: + - application/json + User-Agent: + - REDACTED + url: https://api.easypost.com/beta/referral_customers/payment_method + method: POST + response: + body: '{"error":{"code":"BILLING.INVALID_PAYMENT_GATEWAY_REFERENCE","errors":[],"message":"Invalid + Payment Gateway Reference."}}' + headers: + Cache-Control: + - private, no-cache, no-store + Content-Type: + - application/json; charset=utf-8 + Expires: + - "0" + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Vary: + - Origin + X-Backend: + - easypost + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Ep-Request-Uuid: + - 32e015fa6568ec3ae7896122001b457a + X-Frame-Options: + - SAMEORIGIN + X-Node: + - bigweb34nuq + X-Permitted-Cross-Domain-Policies: + - none + X-Proxied: + - intlb2nuq b3de2c47ef + - extlb1nuq 003ad9bca0 + X-Runtime: + - "0.146064" + X-Version-Label: + - easypost-202311301748-2efb918c5f-master + X-Xss-Protection: + - 1; mode=block + status: 422 Unprocessable Entity + code: 422 + duration: "" diff --git a/tests/cassettes/TestBetaReferralRefundByAmount.yaml b/tests/cassettes/TestBetaReferralRefundByAmount.yaml index ac05580..c400df9 100644 --- a/tests/cassettes/TestBetaReferralRefundByAmount.yaml +++ b/tests/cassettes/TestBetaReferralRefundByAmount.yaml @@ -1,53 +1,61 @@ --- version: 1 interactions: -- request: - body: '{"refund_amount":2000}' - form: {} - headers: - Authorization: - - REDACTED - Content-Type: - - application/json - User-Agent: - - REDACTED - url: https://api.easypost.com/beta/referral_customers/refunds - method: POST - response: - body: '{"error":{"code":"APIKEY.INACTIVE","errors":[],"message":"This api key - is no longer active. Please use a different api key or reactivate this key."}}' - headers: - Content-Type: - - application/json; charset=utf-8 - Referrer-Policy: - - strict-origin-when-cross-origin - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Vary: - - Origin - X-Backend: - - easypost - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Ep-Request-Uuid: - - 4d57346d66ad6851f440b8c70037402a - X-Frame-Options: - - SAMEORIGIN - X-Node: - - bigweb41nuq - X-Permitted-Cross-Domain-Policies: - - none - X-Proxied: - - intlb4nuq c0f5e722d1 - - extlb2nuq fa152d4755 - X-Runtime: - - "0.011269" - X-Version-Label: - - easypost-202408021936-66687f8918-master - X-Xss-Protection: - - 1; mode=block - status: 403 Forbidden - code: 403 - duration: "" + - request: + body: '{"refund_amount":2000}' + form: {} + headers: + Authorization: + - REDACTED + Content-Type: + - application/json + User-Agent: + - REDACTED + url: https://api.easypost.com/beta/referral_customers/refunds + method: POST + response: + body: '{"error":{"code":"TRANSACTION.AMOUNT_INVALID","errors":[],"message":"Refund + amount is invalid. Please use a valid amount or escalate to finance."}}' + headers: + Cache-Control: + - private, no-cache, no-store + Content-Type: + - application/json; charset=utf-8 + Expires: + - "0" + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Vary: + - Origin + X-Backend: + - easypost + X-Canary: + - direct + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Ep-Request-Uuid: + - 32e015fa6568ec3be7896122001b45dc + X-Frame-Options: + - SAMEORIGIN + X-Node: + - bigweb32nuq + X-Permitted-Cross-Domain-Policies: + - none + X-Proxied: + - intlb2nuq b3de2c47ef + - extlb1nuq 003ad9bca0 + X-Runtime: + - "0.430568" + X-Version-Label: + - easypost-202311301748-2efb918c5f-master + X-Xss-Protection: + - 1; mode=block + status: 422 Unprocessable Entity + code: 422 + duration: "" diff --git a/tests/cassettes/TestBetaReferralRefundByPaymentLogId.yaml b/tests/cassettes/TestBetaReferralRefundByPaymentLogId.yaml index 2e46ac5..732cc6d 100644 --- a/tests/cassettes/TestBetaReferralRefundByPaymentLogId.yaml +++ b/tests/cassettes/TestBetaReferralRefundByPaymentLogId.yaml @@ -1,53 +1,59 @@ --- version: 1 interactions: -- request: - body: '{"payment_log_id":"paylog_..."}' - form: {} - headers: - Authorization: - - REDACTED - Content-Type: - - application/json - User-Agent: - - REDACTED - url: https://api.easypost.com/beta/referral_customers/refunds - method: POST - response: - body: '{"error":{"code":"APIKEY.INACTIVE","errors":[],"message":"This api key - is no longer active. Please use a different api key or reactivate this key."}}' - headers: - Content-Type: - - application/json; charset=utf-8 - Referrer-Policy: - - strict-origin-when-cross-origin - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Vary: - - Origin - X-Backend: - - easypost - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Ep-Request-Uuid: - - 4d57346d66ad6851f440b8c700374035 - X-Frame-Options: - - SAMEORIGIN - X-Node: - - bigweb36nuq - X-Permitted-Cross-Domain-Policies: - - none - X-Proxied: - - intlb3nuq c0f5e722d1 - - extlb2nuq fa152d4755 - X-Runtime: - - "0.017758" - X-Version-Label: - - easypost-202408021936-66687f8918-master - X-Xss-Protection: - - 1; mode=block - status: 403 Forbidden - code: 403 - duration: "" + - request: + body: '{"payment_log_id":"paylog_..."}' + form: {} + headers: + Authorization: + - REDACTED + Content-Type: + - application/json + User-Agent: + - REDACTED + url: https://api.easypost.com/beta/referral_customers/refunds + method: POST + response: + body: '{"error":{"code":"TRANSACTION.DOES_NOT_EXIST","errors":[],"message":"We + could not find a transaction with that id."}}' + headers: + Cache-Control: + - private, no-cache, no-store + Content-Type: + - application/json; charset=utf-8 + Expires: + - "0" + Pragma: + - no-cache + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Vary: + - Origin + X-Backend: + - easypost + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Ep-Request-Uuid: + - 32e015fa6568ec3be7896122001b463b + X-Frame-Options: + - SAMEORIGIN + X-Node: + - bigweb40nuq + X-Permitted-Cross-Domain-Policies: + - none + X-Proxied: + - intlb1nuq b3de2c47ef + - extlb1nuq 003ad9bca0 + X-Runtime: + - "0.044583" + X-Version-Label: + - easypost-202311301748-2efb918c5f-master + X-Xss-Protection: + - 1; mode=block + status: 422 Unprocessable Entity + code: 422 + duration: "" diff --git a/tests/cassettes/TestErrorInheritance.yaml b/tests/cassettes/TestErrorInheritance.yaml new file mode 100644 index 0000000..88c2874 --- /dev/null +++ b/tests/cassettes/TestErrorInheritance.yaml @@ -0,0 +1,51 @@ +--- +version: 1 +interactions: +- request: + body: '{"address":{}}' + form: {} + headers: + Authorization: + - REDACTED + Content-Type: + - application/json + User-Agent: + - REDACTED + url: https://api.easypost.com/v2/addresses + method: POST + response: + body: '{"error":{"code":"APIKEY.INACTIVE","errors":[],"message":"This api key + is no longer active. Please use a different api key or reactivate this key."}}' + headers: + Content-Type: + - application/json; charset=utf-8 + Referrer-Policy: + - strict-origin-when-cross-origin + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Backend: + - easypost + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Ep-Request-Uuid: + - 733d01c366ba5a46f4095f2a0009a299 + X-Frame-Options: + - SAMEORIGIN + X-Node: + - bigweb35nuq + X-Permitted-Cross-Domain-Policies: + - none + X-Proxied: + - intlb4nuq c0f5e722d1 + - extlb2nuq b6e1b5034c + X-Runtime: + - "0.018713" + X-Version-Label: + - easypost-202408121729-4bd23c0206-master + X-Xss-Protection: + - 1; mode=block + status: 403 Forbidden + code: 403 + duration: "" diff --git a/tests/error_test.go b/tests/error_test.go index 102d469..2a81f76 100644 --- a/tests/error_test.go +++ b/tests/error_test.go @@ -9,6 +9,55 @@ import ( "github.com/EasyPost/easypost-go/v4" ) +func (c *ClientTests) TestErrorInheritance() { + assert, require := c.Assert(), c.Require() + + client := c.TestClient() + client.APIKey = "not-a-real-key" // Invalid API key will result in 403 Forbidden error + + _, err := client.CreateAddress( + &easypost.Address{ + Street1: "", + }, + &easypost.CreateAddressOptions{}, + ) + + require.Error(err) + + // LibraryError is the base of all errors thrown in this library, error should be able to be cast to this + var libraryError *easypost.LibraryError + if !errors.As(err, &libraryError) { + assert.Fail("Error should be a LibraryError") + } + + // APIError is the base of all errors thrown due to the API, error should be able to be cast to this + var apiError *easypost.APIError + if !errors.As(err, &apiError) { + assert.Fail("Error should be an APIError") + } + // Should be able to access APIError-specific fields now that error has been cast + assert.Equal(403, apiError.StatusCode) + + // LocalError is the base of all errors thrown due to the library itself, and a subtype of LibraryError, but separate from APIError, error should NOT be able to be cast to this + var localError *easypost.LocalError + if errors.As(err, &localError) { + assert.Fail("Error should not be a LocalError") + } + + // ForbiddenError is an error due to a 403 status code, and a subtype of APIError, error should be able to be cast to this + var forbiddenError *easypost.ForbiddenError + if !errors.As(err, &forbiddenError) { + assert.Fail("Error should be a ForbiddenError") + } + + // As a generic error, we can't access specific fields + // ex. Can't access err.StatusCode + // Once casted to a specific error type, we can access specific fields of that error type + // ex. Can access forbiddenError.StatusCode or apiError.StatusCode + assert.Equal(403, forbiddenError.StatusCode) + assert.Equal(403, apiError.StatusCode) +} + // TestApiError tests that a bad API request returns an InvalidRequestError (a subclass of APIError), and that the // error is parsed and pretty-printed correctly. func (c *ClientTests) TestApiError() { @@ -20,14 +69,14 @@ func (c *ClientTests) TestApiError() { require.Error(err) - var eperr *easypost.InvalidRequestError - if errors.As(err, &eperr) { - assert.Equal(422, eperr.StatusCode) - assert.Equal("PARAMETER.REQUIRED", eperr.Code) - assert.Equal("Missing required parameter.", eperr.Message) - assert.Equal(1, len(eperr.Errors)) + var invalidRequestError *easypost.InvalidRequestError + if errors.As(err, &invalidRequestError) { + assert.Equal(422, invalidRequestError.StatusCode) + assert.Equal("PARAMETER.REQUIRED", invalidRequestError.Code) + assert.Equal("Missing required parameter.", invalidRequestError.Message) + assert.Equal(1, len(invalidRequestError.Errors)) - subError := eperr.Errors[0] + subError := invalidRequestError.Errors[0] assert.Equal("shipment", subError.Field) assert.Equal("cannot be blank", subError.Message) } @@ -48,142 +97,142 @@ func (c *ClientTests) TestApiErrorStatusCodes() { res.StatusCode = 0 err := easypost.BuildErrorFromResponse(res) - _, ok := err.(*easypost.ConnectionError) + ok := errors.As(err, new(*easypost.ConnectionError)) assert.True(ok) res.StatusCode = 100 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RetryError) + ok = errors.As(err, new(*easypost.RetryError)) assert.True(ok) res.StatusCode = 101 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RetryError) + ok = errors.As(err, new(*easypost.RetryError)) assert.True(ok) res.StatusCode = 102 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RetryError) + ok = errors.As(err, new(*easypost.RetryError)) assert.True(ok) res.StatusCode = 103 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RetryError) + ok = errors.As(err, new(*easypost.RetryError)) assert.True(ok) res.StatusCode = 300 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 301 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 302 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 303 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 304 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 305 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 306 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 307 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 308 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RedirectError) + ok = errors.As(err, new(*easypost.RedirectError)) assert.True(ok) res.StatusCode = 400 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.BadRequestError) + ok = errors.As(err, new(*easypost.BadRequestError)) assert.True(ok) res.StatusCode = 401 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.UnauthorizedError) + ok = errors.As(err, new(*easypost.UnauthorizedError)) assert.True(ok) res.StatusCode = 402 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.PaymentError) + ok = errors.As(err, new(*easypost.PaymentError)) assert.True(ok) res.StatusCode = 403 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.ForbiddenError) + ok = errors.As(err, new(*easypost.ForbiddenError)) assert.True(ok) res.StatusCode = 404 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.NotFoundError) + ok = errors.As(err, new(*easypost.NotFoundError)) assert.True(ok) res.StatusCode = 405 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.MethodNotAllowedError) + ok = errors.As(err, new(*easypost.MethodNotAllowedError)) assert.True(ok) res.StatusCode = 407 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.ProxyError) + ok = errors.As(err, new(*easypost.ProxyError)) assert.True(ok) res.StatusCode = 408 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.TimeoutError) + ok = errors.As(err, new(*easypost.TimeoutError)) assert.True(ok) res.StatusCode = 422 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.InvalidRequestError) + ok = errors.As(err, new(*easypost.InvalidRequestError)) assert.True(ok) res.StatusCode = 429 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.RateLimitError) + ok = errors.As(err, new(*easypost.RateLimitError)) assert.True(ok) res.StatusCode = 500 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.InternalServerError) + ok = errors.As(err, new(*easypost.InternalServerError)) assert.True(ok) res.StatusCode = 503 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.ServiceUnavailableError) + ok = errors.As(err, new(*easypost.ServiceUnavailableError)) assert.True(ok) res.StatusCode = 504 err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.GatewayTimeoutError) + ok = errors.As(err, new(*easypost.GatewayTimeoutError)) assert.True(ok) res.StatusCode = 7000 // unaccounted for status code err = easypost.BuildErrorFromResponse(res) - _, ok = err.(*easypost.UnknownHttpError) + ok = errors.As(err, new(*easypost.UnknownHttpError)) assert.True(ok) } diff --git a/tests/paginated_collection_test.go b/tests/paginated_collection_test.go index 4cd1e4e..87bb9fd 100644 --- a/tests/paginated_collection_test.go +++ b/tests/paginated_collection_test.go @@ -87,7 +87,7 @@ func (c *ClientTests) TestGetNextPageReachEnd() { for { addresses, err = client.GetNextAddressPage(addresses) if err != nil { - if err.Error() == easypost.EndOfPaginationError.Error() { + if err.Error() == easypost.NoPagesLeftToRetrieve { hitEnd = true } else { require.NoError(err)