Skip to content

Commit d9154e2

Browse files
committed
make alternative implementation
...of #211 where `ApiError` has all the API response properties directly on it instaed of inside a `response` object - `ApiError.response.error` -> `ApiError.code` - `ApiError.response.message` -> `ApiError.rawMessage` - `ApiError.response.assembly_id` -> `ApiError.assemblyId` - `ApiError.response.assembly_ssl_url` -> `ApiError.assemblySslUrl`
1 parent c6e68f9 commit d9154e2

File tree

5 files changed

+39
-50
lines changed

5 files changed

+39
-50
lines changed

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ try {
8787
}
8888
} catch (err) {
8989
console.error('❌ Unable to process Assembly.', err)
90-
if (err instanceof ApiError && err.response.assembly_id) {
91-
console.error(`💡 More info: https://transloadit.com/assemblies/${err.response.assembly_id}`)
90+
if (err instanceof ApiError && err.assemblyId) {
91+
console.error(`💡 More info: https://transloadit.com/assemblies/${err.assemblyId}`)
9292
}
9393
}
9494
```
@@ -416,12 +416,10 @@ const url = client.getSignedSmartCDNUrl({
416416
### Errors
417417

418418
Any errors originating from Node.js will be passed on and we use [GOT](https://github.com/sindresorhus/got) v11 for HTTP requests. [Errors from `got`](https://github.com/sindresorhus/got/tree/v11.8.6?tab=readme-ov-file#errors) will also be passed on, _except_ the `got.HTTPError` which will be replaced with a `transloadit.ApiError`, which will have its `cause` property set to the instance of the original `got.HTTPError`. `transloadit.ApiError` has these properties:
419-
420-
- `HTTPError.response` the JSON object returned by the server. It has these properties
421-
- `error` (`string`) - [The Transloadit API error code](https://transloadit.com/docs/api/response-codes/#error-codes).
422-
- `message` (`string`) - A textual representation of the Transloadit API error.
423-
- `assembly_id`: (`string`) - If the request is related to an assembly, this will be the ID of the assembly.
424-
- `assembly_ssl_url` (`string`) - If the request is related to an assembly, this will be the SSL URL to the assembly .
419+
- `code` (`string`) - [The Transloadit API error code](https://transloadit.com/docs/api/response-codes/#error-codes).
420+
- `rawMessage` (`string`) - A textual representation of the Transloadit API error.
421+
- `assemblyId`: (`string`) - If the request is related to an assembly, this will be the ID of the assembly.
422+
- `assemblySslUrl` (`string`) - If the request is related to an assembly, this will be the SSL URL to the assembly .
425423

426424
To identify errors you can either check its props or use `instanceof`, e.g.:
427425

@@ -435,15 +433,15 @@ try {
435433
if (err.code === 'ENOENT') {
436434
return console.error('Cannot open file', err)
437435
}
438-
if (err instanceof transloadit.ApiError && err.response.error === 'ASSEMBLY_INVALID_STEPS') {
436+
if (err instanceof transloadit.ApiError && err.code === 'ASSEMBLY_INVALID_STEPS') {
439437
return console.error('Invalid Assembly Steps', err)
440438
}
441439
}
442440
```
443441

444-
**Note:** Assemblies that have an error status (`assembly.error`) will only result in an error being thrown from `createAssembly` and `replayAssembly`. For other Assembly methods, no errors will be thrown, but any error can be found in the response's `error` property
442+
**Note:** Assemblies that have an error status (`assembly.error`) will only result in an error being thrown from `createAssembly` and `replayAssembly`. For other Assembly methods, no errors will be thrown, but any error can be found in the response's `error` property (also `ApiError.code`).
445443

446-
- [More information on Transloadit errors (`ApiError.response.error`)](https://transloadit.com/docs/api/response-codes/#error-codes)
444+
- [More information on Transloadit errors (`ApiError.code`)](https://transloadit.com/docs/api/response-codes/#error-codes)
447445
- [More information on request errors](https://github.com/sindresorhus/got#errors)
448446

449447
### Rate limiting & auto retry

examples/retry.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async function run() {
2222
const { items } = await transloadit.listTemplates({ sort: 'created', order: 'asc' })
2323
return items
2424
} catch (err) {
25-
if (err instanceof ApiError && err.response.error === 'INVALID_SIGNATURE') {
25+
if (err instanceof ApiError && err.code === 'INVALID_SIGNATURE') {
2626
// This is an unrecoverable error, abort retry
2727
throw new pRetry.AbortError('INVALID_SIGNATURE')
2828
}

src/ApiError.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ import { HTTPError } from 'got'
33
export interface TransloaditErrorResponseBody {
44
error?: string
55
message?: string
6-
http_code?: string
76
assembly_ssl_url?: string
87
assembly_id?: string
98
}
109

1110
export class ApiError extends Error {
1211
override name = 'ApiError'
1312

14-
response: TransloaditErrorResponseBody
13+
// there might not be an error code (or message) if the server didn't respond with any JSON response at all
14+
// e.g. if there was a 500 in the HTTP reverse proxy
15+
code?: string
16+
rawMessage?: string
17+
assemblySslUrl?: string
18+
assemblyId?: string
1519

1620
override cause?: HTTPError | undefined
1721

@@ -20,13 +24,13 @@ export class ApiError extends Error {
2024
appendStack?: string
2125
body: TransloaditErrorResponseBody | undefined
2226
}) {
23-
const { cause, body, appendStack } = params
27+
const { cause, body = {}, appendStack } = params
2428

2529
const parts = ['API error']
2630
if (cause?.response.statusCode) parts.push(`(HTTP ${cause.response.statusCode})`)
27-
if (body?.error) parts.push(`${body.error}:`)
28-
if (body?.message) parts.push(body.message)
29-
if (body?.assembly_ssl_url) parts.push(body.assembly_ssl_url)
31+
if (body.error) parts.push(`${body.error}:`)
32+
if (body.message) parts.push(body.message)
33+
if (body.assembly_ssl_url) parts.push(body.assembly_ssl_url)
3034

3135
const message = parts.join(' ')
3236

@@ -44,7 +48,10 @@ export class ApiError extends Error {
4448
this.stack += `\n${appendStack.replace(/^([^\n]+\n)/, '')}`
4549
}
4650

47-
this.response = body ?? {}
51+
this.rawMessage = body.message
52+
this.assemblyId = body.assembly_id
53+
this.assemblySslUrl = body.assembly_ssl_url
54+
this.code = body.error
4855
this.cause = cause
4956
}
5057
}

test/integration/live-api.test.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,8 @@ describe('API integration', { timeout: 60000 }, () => {
398398
const promise = createAssembly(client, opts)
399399
await promise.catch((err) => {
400400
expect(err).toMatchObject({
401-
response: expect.objectContaining({
402-
error: 'INVALID_INPUT_ERROR',
403-
assembly_id: expect.any(String),
404-
}),
401+
code: 'INVALID_INPUT_ERROR',
402+
assemblyId: expect.any(String),
405403
})
406404
})
407405
await expect(promise).rejects.toThrow(Error)
@@ -729,9 +727,7 @@ describe('API integration', { timeout: 60000 }, () => {
729727
expect(ok).toBe('TEMPLATE_DELETED')
730728
await expect(client.getTemplate(templId!)).rejects.toThrow(
731729
expect.objectContaining({
732-
response: expect.objectContaining({
733-
error: 'TEMPLATE_NOT_FOUND',
734-
}),
730+
code: 'TEMPLATE_NOT_FOUND',
735731
})
736732
)
737733
})
@@ -802,9 +798,7 @@ describe('API integration', { timeout: 60000 }, () => {
802798
expect(ok).toBe('TEMPLATE_CREDENTIALS_DELETED')
803799
await expect(client.getTemplateCredential(credId!)).rejects.toThrow(
804800
expect.objectContaining({
805-
response: expect.objectContaining({
806-
error: 'TEMPLATE_CREDENTIALS_NOT_READ',
807-
}),
801+
code: 'TEMPLATE_CREDENTIALS_NOT_READ',
808802
})
809803
)
810804
})

test/unit/mock-http.test.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,8 @@ describe('Mocked API tests', () => {
9898

9999
await expect(client.createAssembly()).rejects.toThrow(
100100
expect.objectContaining({
101-
response: {
102-
error: 'INVALID_FILE_META_DATA',
103-
message: 'Invalid file metadata',
104-
},
101+
code: 'INVALID_FILE_META_DATA',
102+
rawMessage: 'Invalid file metadata',
105103
message: 'API error (HTTP 400) INVALID_FILE_META_DATA: Invalid file metadata',
106104
})
107105
)
@@ -122,7 +120,7 @@ describe('Mocked API tests', () => {
122120
expect.objectContaining({
123121
message:
124122
'API error (HTTP 400) INVALID_FILE_META_DATA: Invalid file metadata https://api2-oltu.transloadit.com/assemblies/foo',
125-
response: expect.objectContaining({ assembly_id: '123' }),
123+
assemblyId: '123',
126124
})
127125
)
128126

@@ -150,14 +148,12 @@ describe('Mocked API tests', () => {
150148
expect.stringMatching(` at .+`),
151149
expect.stringMatching(` at .+`),
152150
expect.stringMatching(` name: 'ApiError',`),
153-
expect.stringMatching(` response: \\{`),
154-
expect.stringMatching(` error: 'INVALID_FILE_META_DATA',`),
155-
expect.stringMatching(` message: 'Invalid file metadata',`),
156-
expect.stringMatching(` assembly_id: '123',`),
151+
expect.stringMatching(` rawMessage: 'Invalid file metadata',`),
152+
expect.stringMatching(` assemblyId: '123',`),
157153
expect.stringMatching(
158-
` assembly_ssl_url: 'https:\\/\\/api2-oltu\\.transloadit\\.com\\/assemblies\\/foo'`
154+
` assemblySslUrl: 'https:\\/\\/api2-oltu\\.transloadit\\.com\\/assemblies\\/foo'`
159155
),
160-
expect.stringMatching(` \\},`),
156+
expect.stringMatching(` code: 'INVALID_FILE_META_DATA',`),
161157
expect.stringMatching(` cause: HTTPError: Response code 400 \\(Bad Request\\)`),
162158
expect.stringMatching(` at .+`),
163159
expect.stringMatching(` at .+`),
@@ -211,9 +207,7 @@ describe('Mocked API tests', () => {
211207
await expect(client.createAssembly()).rejects.toThrow(
212208
expect.objectContaining({
213209
message: 'API error (HTTP 413) RATE_LIMIT_REACHED: Request limit reached',
214-
response: expect.objectContaining({
215-
error: 'RATE_LIMIT_REACHED',
216-
}),
210+
code: 'RATE_LIMIT_REACHED',
217211
})
218212
)
219213
scope.done()
@@ -292,10 +286,8 @@ describe('Mocked API tests', () => {
292286

293287
await expect(client.createAssembly()).rejects.toThrow(
294288
expect.objectContaining({
295-
response: expect.objectContaining({
296-
error: 'IMPORT_FILE_ERROR',
297-
assembly_id: '1',
298-
}),
289+
code: 'IMPORT_FILE_ERROR',
290+
assemblyId: '1',
299291
})
300292
)
301293
scope.done()
@@ -310,9 +302,7 @@ describe('Mocked API tests', () => {
310302

311303
await expect(client.replayAssembly('1')).rejects.toThrow(
312304
expect.objectContaining({
313-
response: expect.objectContaining({
314-
error: 'IMPORT_FILE_ERROR',
315-
}),
305+
code: 'IMPORT_FILE_ERROR',
316306
})
317307
)
318308
scope.done()

0 commit comments

Comments
 (0)