Skip to content
This repository has been archived by the owner on Nov 22, 2022. It is now read-only.

Commit

Permalink
Errors refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
aplegatt committed Aug 17, 2022
1 parent 834c720 commit 439253e
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 114 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,6 @@ Available `SpreeSDKError` subtypes:
| `MisconfigurationError` | Signifies the SDK's `Client` was created with improper options. Make sure the values of `host` and other options (if any) provided to `Client` have the correct format. |
| `NoResponseError` | Spree store could not be reached. Ensure it's running and available under the `host` address provided to the `Client` instance. |
| `SpreeError` | Spree responded with an error. To debug the issue, check the error's `serverResponse` field. It contains details about the response from Spree, such as the HTTP status code and headers. |
| `BasicSpreeError` | Extends `SpreeError` with a `summary` field provided by Spree and containing a summary of the issue. |
| `ExpandedSpreeError` | Extends `BasicSpreeError` with a `errors` field. `errors` contains a detailed explanation of the issue, ex. all the validation errors when trying to add shipping details to a Spree order. The `getErrors` method can be used to retrieve a concrete value inside `errors`, ex. `expSpreeError.getErrors(['bill_address', 'firstname'])`. |

The specific type of error returned by `fail()` can be determined using [`instanceof`][3], ex. `if(response.fail() instanceof BasicSpreeError){...}`.

## Tokens

Expand Down
44 changes: 10 additions & 34 deletions src/Http.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import {
BasicSpreeError,
ExpandedSpreeError,
MisconfigurationError,
NoResponseError,
SpreeError,
SpreeSDKError
} from './errors'
import FetchError from './errors/FetchError'
Expand All @@ -14,6 +11,7 @@ import type { FetchConfig, HttpMethod, ResponseParsing } from './interfaces/Fetc
import type { JsonApiResponse } from './interfaces/JsonApi'
import type { ResultResponse } from './interfaces/ResultResponse'
import type { OptionalAnyToken } from './interfaces/Token'
import type { SpreeError } from './errors'

export type EndpointOptions = {
fetcher: Fetcher
Expand Down Expand Up @@ -52,30 +50,15 @@ export default class Http {
}
}

/**
* The HTTP error code returned by Spree is not indicative of its response shape.
* This function determines the information provided by Spree and uses everything available.
*/
protected classifySpreeError(error: FetchError): ErrorType {
const { error: errorSummary, errors } = error.data

if (typeof errorSummary === 'string') {
if (typeof errors === 'object') {
return 'full'
}
return 'basic'
}
return 'limited'
}

protected processError(error: Error): SpreeSDKError {
if (error instanceof FetchError) {
if (error.response) {
protected processError(error: Error): any {
if ((error as FetchError)?.response) {
const fetchError = (error as FetchError)
if (fetchError.response) {
// Error from Spree outside HTTP 2xx codes
return this.processSpreeError(error)
return this.processSpreeError(fetchError)
}

if (error.request) {
if (fetchError.request) {
// No response received from Spree
return new NoResponseError()
}
Expand All @@ -88,16 +71,9 @@ export default class Http {
}

protected processSpreeError(error: FetchError): SpreeError {
const { error: errorSummary, errors } = error.data
const errorType = this.classifySpreeError(error)

if (errorType === 'full') {
return new ExpandedSpreeError(error.response, errorSummary, errors)
} else if (errorType === 'basic') {
return new BasicSpreeError(error.response, errorSummary)
} else {
return new SpreeError(error.response)
}
const { response: { status: code }, data: { error: message } } = error

return { code, message }
}

protected spreeOrderHeaders(tokens: OptionalAnyToken): { [headerName: string]: string } {
Expand Down
15 changes: 0 additions & 15 deletions src/errors/BasicSpreeError.ts

This file was deleted.

33 changes: 0 additions & 33 deletions src/errors/ExpandedSpreeError.ts

This file was deleted.

15 changes: 3 additions & 12 deletions src/errors/SpreeError.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import type { RawFetchResponse } from '../interfaces/RawFetchResponse'
import SpreeSDKError from './SpreeSDKError'

export default class SpreeError extends SpreeSDKError {
public serverResponse: RawFetchResponse

constructor(serverResponse: RawFetchResponse) {
super(`Spree returned a HTTP ${serverResponse.status} error code`)
Object.setPrototypeOf(this, SpreeError.prototype)
this.name = 'SpreeError'
this.serverResponse = serverResponse
}
export type SpreeError = {
code: number
message: string
}
10 changes: 3 additions & 7 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import BasicSpreeError from './BasicSpreeError'
import ExpandedSpreeError from './ExpandedSpreeError'
import MisconfigurationError from './MisconfigurationError'
import NoResponseError from './NoResponseError'
import SpreeError from './SpreeError'
import SpreeSDKError from './SpreeSDKError'
import FetchError from './FetchError'
import DocumentRelationshipError from './DocumentRelationshipError'
import type { SpreeError } from './SpreeError'

export {
BasicSpreeError,
ExpandedSpreeError,
MisconfigurationError,
NoResponseError,
SpreeError,
SpreeSDKError,
FetchError,
DocumentRelationshipError
DocumentRelationshipError,
SpreeError
}
10 changes: 1 addition & 9 deletions src/helpers/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ const toJson = <F extends Error, S>(result: Result<F, S>): { type: string; subty
}
}

const castError = (error: { name: string; message: string; stack? }): errors.SpreeSDKError => {
if (!(error.name in errors)) {
throw new CastError('Error not recognized')
}

return Object.assign(Object.create(errors[error.name].prototype), error)
}

/**
* Converts JSON to a Result instance.
* If the JSON represents a fail, converts the error into an instance of SpreeSDKError its subtype.
Expand All @@ -64,7 +56,7 @@ const fromJson = (json: { [key: string]: any }): Result<errors.SpreeSDKError, an
if (json.subtype === 'success') {
return makeSuccess(json.value)
} else if (json.subtype === 'fail') {
return makeFail(castError(json.value))
return makeFail(json.value)
} else {
throw new DeserializeError('Expected success or fail subtype.')
}
Expand Down
57 changes: 57 additions & 0 deletions tests/e2e/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,31 @@ const createTests = function () {
expect(paymentMethodsResponse.isSuccess()).to.be.true
})
})

it('returns errors with code and message', function () {
const client: Client = this.clientRef.value

cy.wrap(null)
.then(function () {
return result.extractSuccess(client.cart.create())
})
.then(function (cartCreateResponse) {
const orderToken = cartCreateResponse.data.attributes.token

return client.wishlists.updateWishedItem({
order_token: orderToken,
wishlist_token: 'wishlist_token',
id: 'item_id'
})
})
.then(function(wishlistsResponse) {
const failedResponse = wishlistsResponse.fail()

expect(wishlistsResponse.isFail()).to.be.true
expect(failedResponse).to.have.property('code')
expect(failedResponse).to.have.property('message')
})
})
}

const createServerVersionInTheBrowserTests = ({
Expand Down Expand Up @@ -335,6 +360,38 @@ const createVendoSpecificTests = ({
.then(function (categoriesListResponse) {
expect(categoriesListResponse.isSuccess()).to.be.true
})
})

it('test', function () {
const client: Client = this.clientRef.value

cy.wrap(null)
.then(function () {
return result.extractSuccess(client.cart.create())
})
.then(function (cartCreateResponse) {
const { token: order_token, number: order_number } = cartCreateResponse.data.attributes

return cy
.wrap(null)
.then(function () {
return result.extractSuccess(client.products.list({ include: 'default_variant,brand' }))
})
.then(function (variantsResponse) {
const variantId = jsonApi.findSingleRelationshipDocument(
variantsResponse,
variantsResponse.data[0],
'default_variant'
).id

const brand = jsonApi.findSingleRelationshipDocument(
variantsResponse,
variantsResponse.data[0],
'brand'
)
})

})
})
})
}
Expand Down

0 comments on commit 439253e

Please sign in to comment.