Skip to content

Commit 4745889

Browse files
committed
feat: return FetchError if request fails
1 parent f83f820 commit 4745889

15 files changed

+95
-35
lines changed

src/api/FetchError.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export class FetchError extends Error {
2+
public readonly statusCode: number
3+
constructor(statusCode: number, message: string) {
4+
super(message)
5+
this.statusCode = statusCode
6+
this.name = 'FetchError'
7+
}
8+
}
9+
10+
export const toFetchError = async (response: Response): Promise<FetchError> =>
11+
new FetchError(
12+
response.status,
13+
parseInt(response.headers.get('content-length') ?? '0', 10) > 0
14+
? await response.text()
15+
: 'No content returned from server',
16+
)

src/api/bulkOps.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Type, type Static } from '@sinclair/typebox'
22
import { type ValidationError, validatedFetch } from './validatedFetch.js'
3+
import type { FetchError } from './FetchError.js'
34

45
/**
56
* @link https://api.nrfcloud.com/v1/#tag/Bulk-Ops-Requests/operation/FetchBulkOpsRequest
@@ -28,7 +29,7 @@ export const bulkOpsRequests =
2829
async (
2930
bulkOpsId: string,
3031
): Promise<
31-
| { error: Error | ValidationError }
32+
| { error: FetchError | ValidationError }
3233
| { result: Static<typeof BulkOpsRequestType> }
3334
> => {
3435
const vf = validatedFetch({ endpoint, apiKey }, fetchImplementation)

src/api/createFOTAJob.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Type, type Static } from '@sinclair/typebox'
2+
import type { ValidationError } from './validatedFetch.js'
23
import { JSONPayload, validatedFetch } from './validatedFetch.js'
3-
import type { ValidationError } from 'ajv'
4+
import type { FetchError } from './FetchError.js'
45

56
export const CreatedFOTAJobType = Type.Object(
67
{
@@ -35,7 +36,7 @@ export const createFOTAJob =
3536
deviceId: string
3637
bundleId: string
3738
}): Promise<
38-
| { error: Error | ValidationError }
39+
| { error: FetchError | ValidationError }
3940
| { result: Static<typeof CreatedFOTAJobType> }
4041
> => {
4142
const maybeJob = await validatedFetch(

src/api/devices.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Type, type TSchema, type Static } from '@sinclair/typebox'
22
import { slashless } from './slashless.js'
33
import { type ValidationError, validatedFetch } from './validatedFetch.js'
44
import { DeviceShadow } from './DeviceShadow.js'
5+
import type { FetchError } from './FetchError.js'
6+
import { toFetchError } from './FetchError.js'
57

68
const Page = <T extends TSchema>(Item: T) =>
79
Type.Object({
@@ -40,20 +42,21 @@ export const devices = (
4042
fetchImplementation?: typeof fetch,
4143
): {
4244
list: () => Promise<
43-
{ error: Error | ValidationError } | { result: Static<typeof Devices> }
45+
{ error: FetchError | ValidationError } | { result: Static<typeof Devices> }
4446
>
4547
get: (
4648
id: string,
4749
) => Promise<
48-
{ error: Error | ValidationError } | { result: Static<typeof DeviceShadow> }
50+
| { error: FetchError | ValidationError }
51+
| { result: Static<typeof DeviceShadow> }
4952
>
5053
updateState: (
5154
id: string,
5255
state: {
5356
desired?: Record<string, any>
5457
reported?: Record<string, any>
5558
},
56-
) => Promise<{ error: Error } | { success: boolean }>
59+
) => Promise<{ error: FetchError | ValidationError } | { success: boolean }>
5760
register: (
5861
devices: {
5962
// A globally unique device id (UUIDs are highly recommended) /^[a-z0-9:_-]{1,128}$/i
@@ -73,7 +76,9 @@ export const devices = (
7376
*/
7477
fwTypes?: FwType[]
7578
}[],
76-
) => Promise<{ error: Error } | { bulkOpsRequestId: string }>
79+
) => Promise<
80+
{ error: FetchError | ValidationError } | { bulkOpsRequestId: string }
81+
>
7782
} => {
7883
const headers = {
7984
Authorization: `Bearer ${apiKey}`,
@@ -105,9 +110,8 @@ export const devices = (
105110
method: 'PATCH',
106111
body: JSON.stringify(state),
107112
},
108-
).then((res) => {
109-
if (res.status >= 400)
110-
return { error: new Error(`Update failed: ${res.status}`) }
113+
).then(async (res) => {
114+
if (res.status >= 400) return { error: await toFetchError(res) }
111115
return { success: true }
112116
}),
113117
register: async (devices) => {
@@ -138,11 +142,7 @@ export const devices = (
138142
ProvisionDevice,
139143
)
140144

141-
if ('error' in maybeResult) {
142-
return {
143-
error: maybeResult.error,
144-
}
145-
}
145+
if ('error' in maybeResult) return maybeResult
146146

147147
return { bulkOpsRequestId: maybeResult.result.bulkOpsRequestId }
148148
},

src/api/getAccountInfo.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Type, type Static } from '@sinclair/typebox'
2+
import type { ValidationError } from './validatedFetch.js'
23
import { validatedFetch } from './validatedFetch.js'
3-
import type { ValidationError } from 'ajv'
4+
import type { FetchError } from './FetchError.js'
45

56
const AccountInfoType = Type.Object({
67
mqttEndpoint: Type.String(), // e.g. 'mqtt.nrfcloud.com'
@@ -21,7 +22,9 @@ export const getAccountInfo = async (
2122
endpoint: URL
2223
},
2324
fetchImplementation?: typeof fetch,
24-
): Promise<{ error: Error | ValidationError } | { result: AccountInfo }> => {
25+
): Promise<
26+
{ error: FetchError | ValidationError } | { result: AccountInfo }
27+
> => {
2528
const maybeAccount = await validatedFetch(
2629
{
2730
endpoint,

src/api/getCurrentMonthlyCosts.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import type { ValidationError } from 'ajv'
1+
import type { ValidationError } from './validatedFetch.js'
22
import { validatedFetch } from './validatedFetch.js'
33
import { Type } from '@sinclair/typebox'
4+
import type { FetchError } from './FetchError.js'
45

56
export const getCurrentMonthlyCosts =
67
(
@@ -15,7 +16,7 @@ export const getCurrentMonthlyCosts =
1516
) =>
1617
async (): Promise<
1718
| {
18-
error: Error | ValidationError
19+
error: FetchError | ValidationError
1920
}
2021
| { currentMonthTotalCost: number }
2122
> => {

src/api/getDeviceShadow.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Type, type Static } from '@sinclair/typebox'
2+
import type { ValidationError } from './validatedFetch.js'
23
import { validatedFetch } from './validatedFetch.js'
34
import { DeviceShadow } from './DeviceShadow.js'
4-
import type { ValidationError } from 'ajv'
5+
import type { FetchError } from './FetchError.js'
56

67
const DeviceShadows = Type.Array(DeviceShadow)
78

@@ -26,7 +27,8 @@ export const getDeviceShadow = (
2627
): ((
2728
devices: string[],
2829
) => Promise<
29-
{ shadows: Static<typeof DeviceShadows> } | { error: Error | ValidationError }
30+
| { shadows: Static<typeof DeviceShadows> }
31+
| { error: FetchError | ValidationError }
3032
>) => {
3133
const vf = validatedFetch({ endpoint, apiKey }, fetchImplementation)
3234

src/api/getFOTABundles.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Type, type Static } from '@sinclair/typebox'
2-
import type { ValidationError } from 'ajv'
2+
import type { ValidationError } from './validatedFetch.js'
33
import { validatedFetch } from './validatedFetch.js'
44
import { FwType } from './devices.js'
5+
import type { FetchError } from './FetchError.js'
56

67
export const FOTABundle = Type.Object({
78
bundleId: Type.String({
@@ -83,7 +84,7 @@ export const getFOTABundles =
8384
fetchImplementation?: typeof fetch,
8485
) =>
8586
async (): Promise<
86-
| { error: Error | ValidationError }
87+
| { error: FetchError | ValidationError }
8788
| { bundles: Array<Static<typeof FOTABundle>> }
8889
> => {
8990
const vf = validatedFetch(
@@ -101,7 +102,7 @@ const paginateFirmwares = async (
101102
bundles: Array<Static<typeof FOTABundle>> = [],
102103
pageNextToken: string | undefined = undefined,
103104
): Promise<
104-
| { error: Error | ValidationError }
105+
| { error: FetchError | ValidationError }
105106
| { bundles: Array<Static<typeof FOTABundle>> }
106107
> => {
107108
const query = new URLSearchParams({ pageLimit: '100' })

src/api/getFOTAJob.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Type, type Static } from '@sinclair/typebox'
2-
import type { ValidationError } from 'ajv'
2+
3+
import type { ValidationError } from './validatedFetch.js'
34
import { validatedFetch } from './validatedFetch.js'
45
import { FwType } from './devices.js'
6+
import type { FetchError } from './FetchError.js'
57

68
export enum FOTAJobStatus {
79
CREATED = 'CREATED',
@@ -101,7 +103,8 @@ export const getFOTAJob =
101103
}: {
102104
jobId: string
103105
}): Promise<
104-
{ error: Error | ValidationError } | { result: Static<typeof FOTAJobType> }
106+
| { error: FetchError | ValidationError }
107+
| { result: Static<typeof FOTAJobType> }
105108
> => {
106109
const maybeJob = await validatedFetch(
107110
{

src/api/getLocationHistory.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Type, type Static } from '@sinclair/typebox'
2+
import type { ValidationError } from './validatedFetch.js'
23
import { validatedFetch } from './validatedFetch.js'
3-
import type { ValidationError } from 'ajv'
4+
import type { FetchError } from './FetchError.js'
45

56
export enum LocationHistoryServiceType {
67
ANCHOR = 'ANCHOR',
@@ -118,7 +119,7 @@ export const getLocationHistory =
118119
start?: Date
119120
end?: Date
120121
}): Promise<
121-
| { error: Error | ValidationError }
122+
| { error: FetchError | ValidationError }
122123
| { result: Static<typeof LocationHistoryType> }
123124
> => {
124125
const query = new URLSearchParams({

0 commit comments

Comments
 (0)