Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
feat: error handling for other sections
Browse files Browse the repository at this point in the history
  • Loading branch information
gigobyte committed May 13, 2020
1 parent a2c0a6d commit 7c0bc21
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 1 deletion.
67 changes: 67 additions & 0 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class HttpError extends MWSError {
public requestId!: string
}

// General errors
export class InputStreamDisconnected extends HttpError {}
export class InvalidParameterValue extends HttpError {}
export class AccessDenied extends HttpError {}
Expand All @@ -25,6 +26,42 @@ export class InvalidAddress extends HttpError {}
export class InternalError extends HttpError {}
export class QuotaExceeded extends HttpError {}
export class RequestThrottled extends HttpError {}
// Easy Ship errors
export class ResourceNotFound extends HttpError {}
export class ScheduledPackageAlreadyExists extends HttpError {}
export class RegionNotSupported extends HttpError {}
export class ScheduleWindowExpired extends HttpError {}
export class InvalidOrderState extends HttpError {}
export class PickupSlotNotAvailable extends HttpError {}
// Feeds errors
export class AccessToFeedProcessingResultDenied extends HttpError {}
export class ContentMD5Missing extends HttpError {}
export class ContentMD5DoesNotMatch extends HttpError {}
export class FeedCanceled extends HttpError {}
export class FeedProcessingResultNoLongerAvailable extends HttpError {}
export class FeedProcessingResultNotReady extends HttpError {}
export class InputDataError extends HttpError {}
export class InvalidFeedSubmissionId extends HttpError {}
export class InvalidFeedType extends HttpError {}
export class InvalidRequest extends HttpError {}
// Finances errors
export class NonRetriableInternalError extends HttpError {}
export class RetriableInternalError extends HttpError {}
// Products errors
export class InvalidUPCIdentifier extends HttpError {}
// Reports errors
export class AccessToReportDenied extends HttpError {}
export class InvalidReportId extends HttpError {}
export class InvalidReportType extends HttpError {}
export class InvalidScheduleFrequency extends HttpError {}
export class ReportNoLongerAvailable extends HttpError {}
export class ReportNotReady extends HttpError {}
// Subscriptions errors
export class DependencyFatalException extends HttpError {}
export class DependencyRetriableException extends HttpError {}
export class DependencyUnauthorizedException extends HttpError {}
export class InternalErrorFatalException extends HttpError {}
export class InvalidInputFatalException extends HttpError {}

export class ParsingError extends MWSError {}
/* eslint-enable max-classes-per-file */
Expand All @@ -42,8 +79,38 @@ export const MWSApiError = Codec.interface({
'SignatureDoesNotMatch',
'InvalidAddress',
'InternalError',
'Internal Error',
'QuotaExceeded',
'RequestThrottled',
'ResourceNotFound',
'ScheduledPackageAlreadyExists',
'RegionNotSupported',
'ScheduleWindowExpired',
'InvalidOrderState',
'PickupSlotNotAvailable',
'AccessToFeedProcessingResultDenied',
'ContentMD5Missing',
'ContentMD5DoesNotMatch',
'FeedCanceled',
'FeedProcessingResultNoLongerAvailable',
'FeedProcessingResultNotReady',
'InputDataError',
'InvalidFeedSubmissionId',
'InvalidFeedType',
'InvalidRequest',
'NonRetriableInternalError',
'RetriableInternalError',
'AccessToReportDenied',
'InvalidReportId',
'InvalidReportType',
'InvalidScheduleFrequency',
'ReportNoLongerAvailable',
'ReportNotReady',
'DependencyFatalException',
'DependencyRetriableException',
'DependencyUnauthorizedException',
'InternalErrorFatalException',
'InvalidInputFatalException',
] as const).map((element) => exactly(element)),
),
Message: string,
Expand Down
90 changes: 89 additions & 1 deletion src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,45 @@ import { URLSearchParams } from 'url'

import {
AccessDenied,
AccessToFeedProcessingResultDenied,
AccessToReportDenied,
ContentMD5DoesNotMatch,
ContentMD5Missing,
DependencyFatalException,
DependencyRetriableException,
DependencyUnauthorizedException,
enhanceError,
FeedCanceled,
FeedProcessingResultNoLongerAvailable,
FeedProcessingResultNotReady,
InputDataError,
InputStreamDisconnected,
InternalError,
InternalErrorFatalException,
InvalidAccessKeyId,
InvalidAddress,
InvalidFeedSubmissionId,
InvalidFeedType,
InvalidInputFatalException,
InvalidOrderState,
InvalidParameterValue,
InvalidReportId,
InvalidReportType,
InvalidRequest,
InvalidScheduleFrequency,
InvalidUPCIdentifier,
MWSApiError,
NonRetriableInternalError,
PickupSlotNotAvailable,
QuotaExceeded,
RegionNotSupported,
ReportNoLongerAvailable,
ReportNotReady,
RequestThrottled,
ResourceNotFound,
RetriableInternalError,
ScheduledPackageAlreadyExists,
ScheduleWindowExpired,
SignatureDoesNotMatch,
} from './error'
import { sign } from './sign'
Expand All @@ -33,6 +63,7 @@ type CleanParameters = Record<string, string>
export enum Resource {
Sellers = 'Sellers',
Orders = 'Orders',
Products = 'Products',
}

interface ResourceActions {
Expand All @@ -47,6 +78,22 @@ interface ResourceActions {
| 'ListOrderItems'
| 'ListOrderItemsByNextToken'
| 'GetServiceStatus'
[Resource.Products]:
| 'ListMatchingProducts'
| 'GetMatchingProduct'
| 'GetMatchingProductForId'
| 'GetCompetitivePricingForSKU'
| 'GetCompetitivePricingForASIN'
| 'GetLowestOfferListingsForSKU'
| 'GetLowestOfferListingsForASIN'
| 'GetLowestPricedOffersForSKU'
| 'GetLowestPricedOffersForASIN'
| 'GetMyFeesEstimate'
| 'GetMyPriceForSKU'
| 'GetMyPriceForASIN'
| 'GetProductCategoriesForSKU'
| 'GetProductCategoriesForASIN'
| 'GetServiceStatus'
}

interface Request {
Expand Down Expand Up @@ -176,7 +223,17 @@ export class HttpClient {
}

try {
return await this.fetch(config).then((x) => parseResponse(x))
const response = await this.fetch(config)

// GetMatchingProductForId can return an Invalid UPC identifier error message to an otherwise successfully processed request (i.e. 200 status code)
if (
info.action === 'GetMatchingProductForId' &&
response.data.includes('Invalid UPC identifier')
) {
throw new InvalidUPCIdentifier(`${info.action} request failed`)
}

return parseResponse(response)
} catch (error) {
if (parser.validate(error) !== true) {
throw error
Expand All @@ -196,8 +253,39 @@ export class HttpClient {
SignatureDoesNotMatch,
InvalidAddress,
InternalError,
// Subscriptions-specific
'Internal Error': InternalError,
QuotaExceeded,
RequestThrottled,
ResourceNotFound,
ScheduledPackageAlreadyExists,
RegionNotSupported,
ScheduleWindowExpired,
InvalidOrderState,
PickupSlotNotAvailable,
AccessToFeedProcessingResultDenied,
ContentMD5Missing,
ContentMD5DoesNotMatch,
FeedCanceled,
FeedProcessingResultNoLongerAvailable,
FeedProcessingResultNotReady,
InputDataError,
InvalidFeedSubmissionId,
InvalidFeedType,
InvalidRequest,
NonRetriableInternalError,
RetriableInternalError,
AccessToReportDenied,
InvalidReportId,
InvalidReportType,
InvalidScheduleFrequency,
ReportNoLongerAvailable,
ReportNotReady,
DependencyFatalException,
DependencyRetriableException,
DependencyUnauthorizedException,
InternalErrorFatalException,
InvalidInputFatalException,
}

const ErrorToThrow = errorMap[errorCode]
Expand Down
27 changes: 27 additions & 0 deletions test/unit/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
HttpError,
InvalidAddress,
InvalidParameterValue,
InvalidUPCIdentifier,
MWSError,
} from '../../src'
import { Resource } from '../../src/http'
Expand Down Expand Up @@ -84,6 +85,32 @@ describe('httpClient', () => {
await expect(() => httpClient.request('POST', mockRequest)).rejects.toStrictEqual(fixture)
})

it('should handle Invalid UPC identifier error', async () => {
expect.assertions(1)

const httpClient = new HttpClient(
{
awsAccessKeyId: '',
marketplace: amazonMarketplaces.CA,
mwsAuthToken: '',
secretKey: '',
sellerId: '',
},
() => Promise.resolve({ data: 'Invalid UPC identifier', headers: {} }),
)

const request = {
resource: Resource.Products,
version: '',
action: 'GetMatchingProductForId',
parameters: {},
} as const

await expect(() => httpClient.request('POST', request)).rejects.toStrictEqual(
new InvalidUPCIdentifier('GetMatchingProductForId request failed'),
)
})

describe('default fetch', () => {
it('returns only the XML response on failure', async () => {
expect.assertions(1)
Expand Down

0 comments on commit 7c0bc21

Please sign in to comment.