Skip to content
Open
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
51 changes: 51 additions & 0 deletions src/Client.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import nock from 'nock'
import { Client } from './Client'

const client = new Client({ token: 'mockToken' })

describe('Client', () => {
afterEach(() => {
nock.cleanAll()
})

test('should keep the status of a JSON error response', async () => {
nock(/(.*)/)
.get('/air/offers/off_123')
.reply(404, {
meta: { request_id: 'req_123', status: 404 },
errors: [
{
code: 'not_found',
title: 'Resource not found',
message: 'The resource you requested could not be found.',
type: 'invalid_request_error',
documentation_url: '',
},
],
})

await expect(
client.request({ method: 'GET', path: '/air/offers/off_123' }),
).rejects.toMatchObject({
status: 404,
meta: { request_id: 'req_123', status: 404 },
})
})

test('should keep the status when the error response is not JSON', async () => {
nock(/(.*)/)
.get('/air/offer_requests')
.reply(503, '<html>503 Service Unavailable</html>', {
'content-type': 'text/html',
})

// A gateway responding on Duffel's behalf produces no `meta`, so `status` is the only
// indication of what went wrong.
await expect(
client.request({ method: 'GET', path: '/air/offer_requests' }),
).rejects.toMatchObject({
status: 503,
meta: undefined,
})
})
})
17 changes: 17 additions & 0 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,36 @@ export class DuffelError extends Error {
public errors: ApiResponseError[]
public headers: Headers

/**
* The [HTTP status](https://httpstatuses.com/) the SDK received.
*
* `meta` is only populated when the API returned a JSON body, so this is the
* only status available when a proxy or gateway responds with something else.
*
* It is the status of the response that arrived, not necessarily the outcome
* of the request at Duffel: a gateway can time out or fail while Duffel still
* processes the request successfully. Treat it as diagnostic information, and
* do not use it on its own to decide whether the request took effect or
* whether it is safe to retry.
*/
public status: number | undefined

constructor({
meta,
errors,
headers,
status,
}: {
meta: ApiResponseMeta
errors: ApiResponseError[]
headers: Headers
status?: number
}) {
super()
this.meta = meta
this.errors = errors
this.headers = headers
this.status = status ?? meta?.status
}
}

Expand Down
Loading