@hello.nrfcloud.com/nrfcloud-api-helpers 5.0.10 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/FetchError.d.ts +5 -0
- package/dist/api/FetchError.js +11 -0
- package/dist/api/bulkOps.d.ts +2 -1
- package/dist/api/createFOTAJob.d.ts +3 -2
- package/dist/api/devices.d.ts +5 -4
- package/dist/api/devices.js +5 -7
- package/dist/api/getAccountInfo.d.ts +3 -2
- package/dist/api/getCurrentMonthlyCosts.d.ts +3 -2
- package/dist/api/getDeviceShadow.d.ts +3 -2
- package/dist/api/getFOTABundle.spec.d.ts +1 -0
- package/dist/api/getFOTABundle.spec.js +92 -0
- package/dist/api/getFOTABundles.d.ts +22 -0
- package/dist/api/getFOTABundles.js +73 -0
- package/dist/api/getFOTAJob.d.ts +3 -2
- package/dist/api/getLocationHistory.d.ts +3 -2
- package/dist/api/groundFix.d.ts +2 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/serviceToken.d.ts +2 -1
- package/dist/api/validatedFetch.d.ts +2 -1
- package/dist/api/validatedFetch.js +2 -1
- package/dist/api/validatedFetch.spec.js +17 -0
- package/package.json +4 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class FetchError extends Error {
|
|
2
|
+
statusCode;
|
|
3
|
+
constructor(statusCode, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
this.name = 'FetchError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const toFetchError = async (response) => new FetchError(response.status, parseInt(response.headers.get('content-length') ?? '0', 10) > 0
|
|
10
|
+
? await response.text()
|
|
11
|
+
: 'No content returned from server');
|
package/dist/api/bulkOps.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
2
|
import { type ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
/**
|
|
4
5
|
* @link https://api.nrfcloud.com/v1/#tag/Bulk-Ops-Requests/operation/FetchBulkOpsRequest
|
|
5
6
|
*/
|
|
@@ -11,7 +12,7 @@ export declare const bulkOpsRequests: ({ apiKey, endpoint, }: {
|
|
|
11
12
|
apiKey: string;
|
|
12
13
|
endpoint: URL;
|
|
13
14
|
}, fetchImplementation?: typeof fetch) => (bulkOpsId: string) => Promise<{
|
|
14
|
-
error:
|
|
15
|
+
error: FetchError | ValidationError;
|
|
15
16
|
} | {
|
|
16
17
|
result: Static<typeof BulkOpsRequestType>;
|
|
17
18
|
}>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
|
-
import type { ValidationError } from '
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
export declare const CreatedFOTAJobType: import("@sinclair/typebox").TObject<{
|
|
4
5
|
jobId: import("@sinclair/typebox").TString;
|
|
5
6
|
}>;
|
|
@@ -10,7 +11,7 @@ export declare const createFOTAJob: ({ apiKey, endpoint, }: {
|
|
|
10
11
|
deviceId: string;
|
|
11
12
|
bundleId: string;
|
|
12
13
|
}) => Promise<{
|
|
13
|
-
error:
|
|
14
|
+
error: FetchError | ValidationError;
|
|
14
15
|
} | {
|
|
15
16
|
result: Static<typeof CreatedFOTAJobType>;
|
|
16
17
|
}>;
|
package/dist/api/devices.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
2
|
import { type ValidationError } from './validatedFetch.js';
|
|
3
3
|
import { DeviceShadow } from './DeviceShadow.js';
|
|
4
|
+
import type { FetchError } from './FetchError.js';
|
|
4
5
|
declare const Devices: import("@sinclair/typebox").TObject<{
|
|
5
6
|
total: import("@sinclair/typebox").TInteger;
|
|
6
7
|
items: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
@@ -33,12 +34,12 @@ export declare const devices: ({ endpoint, apiKey, }: {
|
|
|
33
34
|
apiKey: string;
|
|
34
35
|
}, fetchImplementation?: typeof fetch) => {
|
|
35
36
|
list: () => Promise<{
|
|
36
|
-
error:
|
|
37
|
+
error: FetchError | ValidationError;
|
|
37
38
|
} | {
|
|
38
39
|
result: Static<typeof Devices>;
|
|
39
40
|
}>;
|
|
40
41
|
get: (id: string) => Promise<{
|
|
41
|
-
error:
|
|
42
|
+
error: FetchError | ValidationError;
|
|
42
43
|
} | {
|
|
43
44
|
result: Static<typeof DeviceShadow>;
|
|
44
45
|
}>;
|
|
@@ -46,7 +47,7 @@ export declare const devices: ({ endpoint, apiKey, }: {
|
|
|
46
47
|
desired?: Record<string, any>;
|
|
47
48
|
reported?: Record<string, any>;
|
|
48
49
|
}) => Promise<{
|
|
49
|
-
error:
|
|
50
|
+
error: FetchError | ValidationError;
|
|
50
51
|
} | {
|
|
51
52
|
success: boolean;
|
|
52
53
|
}>;
|
|
@@ -64,7 +65,7 @@ export declare const devices: ({ endpoint, apiKey, }: {
|
|
|
64
65
|
*/
|
|
65
66
|
fwTypes?: FwType[];
|
|
66
67
|
}[]) => Promise<{
|
|
67
|
-
error:
|
|
68
|
+
error: FetchError | ValidationError;
|
|
68
69
|
} | {
|
|
69
70
|
bulkOpsRequestId: string;
|
|
70
71
|
}>;
|
package/dist/api/devices.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Type } from '@sinclair/typebox';
|
|
|
2
2
|
import { slashless } from './slashless.js';
|
|
3
3
|
import { validatedFetch } from './validatedFetch.js';
|
|
4
4
|
import { DeviceShadow } from './DeviceShadow.js';
|
|
5
|
+
import { toFetchError } from './FetchError.js';
|
|
5
6
|
const Page = (Item) => Type.Object({
|
|
6
7
|
total: Type.Integer(),
|
|
7
8
|
items: Type.Array(Item),
|
|
@@ -47,9 +48,9 @@ export const devices = ({ endpoint, apiKey, }, fetchImplementation) => {
|
|
|
47
48
|
},
|
|
48
49
|
method: 'PATCH',
|
|
49
50
|
body: JSON.stringify(state),
|
|
50
|
-
}).then((res) => {
|
|
51
|
+
}).then(async (res) => {
|
|
51
52
|
if (res.status >= 400)
|
|
52
|
-
return { error:
|
|
53
|
+
return { error: await toFetchError(res) };
|
|
53
54
|
return { success: true };
|
|
54
55
|
}),
|
|
55
56
|
register: async (devices) => {
|
|
@@ -75,11 +76,8 @@ export const devices = ({ endpoint, apiKey, }, fetchImplementation) => {
|
|
|
75
76
|
type: 'application/octet-stream',
|
|
76
77
|
},
|
|
77
78
|
}, ProvisionDevice);
|
|
78
|
-
if ('error' in maybeResult)
|
|
79
|
-
return
|
|
80
|
-
error: maybeResult.error,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
79
|
+
if ('error' in maybeResult)
|
|
80
|
+
return maybeResult;
|
|
83
81
|
return { bulkOpsRequestId: maybeResult.result.bulkOpsRequestId };
|
|
84
82
|
},
|
|
85
83
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
|
-
import type { ValidationError } from '
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
declare const AccountInfoType: import("@sinclair/typebox").TObject<{
|
|
4
5
|
mqttEndpoint: import("@sinclair/typebox").TString;
|
|
5
6
|
mqttTopicPrefix: import("@sinclair/typebox").TString;
|
|
@@ -13,7 +14,7 @@ export declare const getAccountInfo: ({ apiKey, endpoint, }: {
|
|
|
13
14
|
apiKey: string;
|
|
14
15
|
endpoint: URL;
|
|
15
16
|
}, fetchImplementation?: typeof fetch) => Promise<{
|
|
16
|
-
error:
|
|
17
|
+
error: FetchError | ValidationError;
|
|
17
18
|
} | {
|
|
18
19
|
result: AccountInfo;
|
|
19
20
|
}>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ValidationError } from '
|
|
1
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
2
|
+
import type { FetchError } from './FetchError.js';
|
|
2
3
|
export declare const getCurrentMonthlyCosts: ({ apiKey, endpoint, }: {
|
|
3
4
|
apiKey: string;
|
|
4
5
|
endpoint: URL;
|
|
5
6
|
}, fetchImplementation?: typeof fetch) => () => Promise<{
|
|
6
|
-
error:
|
|
7
|
+
error: FetchError | ValidationError;
|
|
7
8
|
} | {
|
|
8
9
|
currentMonthTotalCost: number;
|
|
9
10
|
}>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
|
-
import type { ValidationError } from '
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
declare const DeviceShadows: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
4
5
|
id: import("@sinclair/typebox").TString;
|
|
5
6
|
$meta: import("@sinclair/typebox").TObject<{
|
|
@@ -19,6 +20,6 @@ export declare const getDeviceShadow: ({ endpoint, apiKey, }: {
|
|
|
19
20
|
}, fetchImplementation?: typeof fetch) => ((devices: string[]) => Promise<{
|
|
20
21
|
shadows: Static<typeof DeviceShadows>;
|
|
21
22
|
} | {
|
|
22
|
-
error:
|
|
23
|
+
error: FetchError | ValidationError;
|
|
23
24
|
}>);
|
|
24
25
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, mock } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { getFOTABundles } from './getFOTABundles.js';
|
|
4
|
+
void describe('getFOTABundles()', () => {
|
|
5
|
+
void it('should fetch all FOTA bundles', async () => {
|
|
6
|
+
const mockFetch = mock.fn();
|
|
7
|
+
mockFetch.mock.mockImplementationOnce(() => ({
|
|
8
|
+
ok: true,
|
|
9
|
+
json: async () => Promise.resolve({
|
|
10
|
+
items: [
|
|
11
|
+
{
|
|
12
|
+
bundleId: 'APP*0038b655*v1.1.1-debug',
|
|
13
|
+
lastModified: '2023-06-28T09:50:02.000Z',
|
|
14
|
+
size: 418565,
|
|
15
|
+
version: 'v1.1.1-debug',
|
|
16
|
+
type: 'APP',
|
|
17
|
+
filenames: ['hello-nrfcloud-thingy91-debug-v1.1.1-fwupd.bin'],
|
|
18
|
+
name: 'hello.nrfcloud.com v1.1.1-debug',
|
|
19
|
+
description: 'Firmware Update Image BIN file (thingy91, debug)',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
total: 1,
|
|
23
|
+
pageNextToken: '102/B5fGZNAs7vcw8E2i611ID4apx/Du/2/H6nr2UDWk5eoihEeAgh6qoaGcDzAI4M8JCoO4iAAK96TWfuB19ru9c1PrnwiTUdw/sZzwrYSrS433vPjDJNvJUIEmqm9+V3ElM5M1bLmt6GrGa57SymHHK4nN0W+zHhmE97cCCfzJMBXhVTl3TzvBx5rE1KJYf',
|
|
24
|
+
}),
|
|
25
|
+
}), 0);
|
|
26
|
+
mockFetch.mock.mockImplementationOnce(() => ({
|
|
27
|
+
ok: true,
|
|
28
|
+
json: async () => Promise.resolve({
|
|
29
|
+
items: [
|
|
30
|
+
{
|
|
31
|
+
bundleId: 'APP*0103b0f9*v1.1.2-sol-dbg',
|
|
32
|
+
lastModified: '2023-06-29T14:18:19.000Z',
|
|
33
|
+
size: 426280,
|
|
34
|
+
version: 'v1.1.2-sol-dbg',
|
|
35
|
+
type: 'APP',
|
|
36
|
+
filenames: ['hello-nrfcloud-thingy91-sol-dbg-v1.1.2-fwupd.bin'],
|
|
37
|
+
name: 'hello.nrfcloud.com v1.1.2-sol-dbg',
|
|
38
|
+
description: 'Firmware Update Image BIN file (thingy91, solar, debug)',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
total: 1,
|
|
42
|
+
}),
|
|
43
|
+
}), 1);
|
|
44
|
+
const fetcher = getFOTABundles({
|
|
45
|
+
endpoint: new URL('https://example.com/'),
|
|
46
|
+
apiKey: 'some-key',
|
|
47
|
+
}, mockFetch);
|
|
48
|
+
const res = await fetcher();
|
|
49
|
+
assert.deepEqual(mockFetch.mock.calls[0]?.arguments, [
|
|
50
|
+
`https://example.com/v1/firmwares?pageLimit=100`,
|
|
51
|
+
{
|
|
52
|
+
headers: {
|
|
53
|
+
Accept: 'application/json; charset=utf-8',
|
|
54
|
+
Authorization: 'Bearer some-key',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
assert.deepEqual(mockFetch.mock.calls[1]?.arguments, [
|
|
59
|
+
`https://example.com/v1/firmwares?pageLimit=100&pageNextToken=${encodeURIComponent(`102/B5fGZNAs7vcw8E2i611ID4apx/Du/2/H6nr2UDWk5eoihEeAgh6qoaGcDzAI4M8JCoO4iAAK96TWfuB19ru9c1PrnwiTUdw/sZzwrYSrS433vPjDJNvJUIEmqm9+V3ElM5M1bLmt6GrGa57SymHHK4nN0W+zHhmE97cCCfzJMBXhVTl3TzvBx5rE1KJYf`)}`,
|
|
60
|
+
{
|
|
61
|
+
headers: {
|
|
62
|
+
Accept: 'application/json; charset=utf-8',
|
|
63
|
+
Authorization: 'Bearer some-key',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
assert.deepEqual(res, {
|
|
68
|
+
bundles: [
|
|
69
|
+
{
|
|
70
|
+
bundleId: 'APP*0038b655*v1.1.1-debug',
|
|
71
|
+
lastModified: '2023-06-28T09:50:02.000Z',
|
|
72
|
+
size: 418565,
|
|
73
|
+
version: 'v1.1.1-debug',
|
|
74
|
+
type: 'APP',
|
|
75
|
+
filenames: ['hello-nrfcloud-thingy91-debug-v1.1.1-fwupd.bin'],
|
|
76
|
+
name: 'hello.nrfcloud.com v1.1.1-debug',
|
|
77
|
+
description: 'Firmware Update Image BIN file (thingy91, debug)',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
bundleId: 'APP*0103b0f9*v1.1.2-sol-dbg',
|
|
81
|
+
lastModified: '2023-06-29T14:18:19.000Z',
|
|
82
|
+
size: 426280,
|
|
83
|
+
version: 'v1.1.2-sol-dbg',
|
|
84
|
+
type: 'APP',
|
|
85
|
+
filenames: ['hello-nrfcloud-thingy91-sol-dbg-v1.1.2-fwupd.bin'],
|
|
86
|
+
name: 'hello.nrfcloud.com v1.1.2-sol-dbg',
|
|
87
|
+
description: 'Firmware Update Image BIN file (thingy91, solar, debug)',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Static } from '@sinclair/typebox';
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
|
+
import { FwType } from './devices.js';
|
|
4
|
+
import type { FetchError } from './FetchError.js';
|
|
5
|
+
export declare const FOTABundle: import("@sinclair/typebox").TObject<{
|
|
6
|
+
bundleId: import("@sinclair/typebox").TString;
|
|
7
|
+
lastModified: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
8
|
+
size: import("@sinclair/typebox").TNumber;
|
|
9
|
+
version: import("@sinclair/typebox").TString;
|
|
10
|
+
type: import("@sinclair/typebox").TEnum<typeof FwType>;
|
|
11
|
+
filenames: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
12
|
+
name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
13
|
+
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const getFOTABundles: ({ apiKey, endpoint, }: {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
endpoint: URL;
|
|
18
|
+
}, fetchImplementation?: typeof fetch) => () => Promise<{
|
|
19
|
+
error: FetchError | ValidationError;
|
|
20
|
+
} | {
|
|
21
|
+
bundles: Array<Static<typeof FOTABundle>>;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
import { validatedFetch } from './validatedFetch.js';
|
|
3
|
+
import { FwType } from './devices.js';
|
|
4
|
+
export const FOTABundle = Type.Object({
|
|
5
|
+
bundleId: Type.String({
|
|
6
|
+
minLength: 1,
|
|
7
|
+
description: 'The ID of the bundle',
|
|
8
|
+
examples: ['APP*20a4b75a*v1.1.1-debug'],
|
|
9
|
+
}),
|
|
10
|
+
lastModified: Type.Optional(Type.String({
|
|
11
|
+
title: 'Timestamp',
|
|
12
|
+
description: 'ISO-8601 date-time string',
|
|
13
|
+
examples: ['2019-08-24T14:15:22Z'],
|
|
14
|
+
})),
|
|
15
|
+
size: Type.Number({
|
|
16
|
+
minimum: 0,
|
|
17
|
+
description: 'Size of the bundle in bytes',
|
|
18
|
+
examples: [418565],
|
|
19
|
+
}),
|
|
20
|
+
version: Type.String({ minLength: 1, examples: ['v1.1.1-debug'] }),
|
|
21
|
+
type: Type.Enum(FwType, { title: 'Firmware Type' }),
|
|
22
|
+
filenames: Type.Array(Type.String({
|
|
23
|
+
minLength: 1,
|
|
24
|
+
examples: ['hello-nrfcloud-thingy91-debug-v1.1.1-fwupd.bin'],
|
|
25
|
+
}), { description: 'The files in the bundle.' }),
|
|
26
|
+
name: Type.Optional(Type.String({
|
|
27
|
+
minLength: 1,
|
|
28
|
+
title: 'Name',
|
|
29
|
+
description: 'The name of the bundle',
|
|
30
|
+
examples: ['hello.nrfcloud.com v1.1.1-debug'],
|
|
31
|
+
})),
|
|
32
|
+
description: Type.Optional(Type.String({
|
|
33
|
+
minLength: 1,
|
|
34
|
+
title: 'Description',
|
|
35
|
+
description: 'The description of the bundle',
|
|
36
|
+
examples: ['Firmware Update Image BIN file (thingy91, debug)'],
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
const FirmwaresType = Type.Object({
|
|
40
|
+
items: Type.Array(FOTABundle),
|
|
41
|
+
total: Type.Integer({
|
|
42
|
+
minimum: 0,
|
|
43
|
+
description: 'Reflects the total results returned by the query, which may be less than the total number of items available. If the response contains a pageNextToken value, you can supply the pageNextToken in the next request to get more results. The maximum value of total is the page limit of the request, or ten pages if no page limit is provided.',
|
|
44
|
+
}),
|
|
45
|
+
pageNextToken: Type.Optional(Type.String({
|
|
46
|
+
minLength: 1,
|
|
47
|
+
description: 'Token used to retrieve the next page of items in the list. Present in a response only if the total available results exceeds the specified limit on a page. This token does not change between requests. When supplying as a request parameter, use URL-encoding.',
|
|
48
|
+
})),
|
|
49
|
+
}, {
|
|
50
|
+
title: 'Firmware bundles',
|
|
51
|
+
description: 'Returns the list of firmware bundles. See https://api.nrfcloud.com/v1#tag/Firmware-Bundles/operation/ListFirmware',
|
|
52
|
+
});
|
|
53
|
+
export const getFOTABundles = ({ apiKey, endpoint, }, fetchImplementation) => async () => {
|
|
54
|
+
const vf = validatedFetch({
|
|
55
|
+
endpoint,
|
|
56
|
+
apiKey,
|
|
57
|
+
}, fetchImplementation);
|
|
58
|
+
return paginateFirmwares(vf);
|
|
59
|
+
};
|
|
60
|
+
const paginateFirmwares = async (vf, bundles = [], pageNextToken = undefined) => {
|
|
61
|
+
const query = new URLSearchParams({ pageLimit: '100' });
|
|
62
|
+
if (pageNextToken !== undefined)
|
|
63
|
+
query.set('pageNextToken', pageNextToken);
|
|
64
|
+
const maybeBundles = await vf({
|
|
65
|
+
resource: `firmwares`,
|
|
66
|
+
query,
|
|
67
|
+
}, FirmwaresType);
|
|
68
|
+
if ('error' in maybeBundles)
|
|
69
|
+
return maybeBundles;
|
|
70
|
+
if (maybeBundles.result.pageNextToken !== undefined)
|
|
71
|
+
return paginateFirmwares(vf, [...bundles, ...maybeBundles.result.items], maybeBundles.result.pageNextToken);
|
|
72
|
+
return { bundles: [...bundles, ...maybeBundles.result.items] };
|
|
73
|
+
};
|
package/dist/api/getFOTAJob.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
|
-
import type { ValidationError } from '
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
3
|
import { FwType } from './devices.js';
|
|
4
|
+
import type { FetchError } from './FetchError.js';
|
|
4
5
|
export declare enum FOTAJobStatus {
|
|
5
6
|
CREATED = "CREATED",
|
|
6
7
|
IN_PROGRESS = "IN_PROGRESS",
|
|
@@ -42,7 +43,7 @@ export declare const getFOTAJob: ({ apiKey, endpoint, }: {
|
|
|
42
43
|
}, fetchImplementation?: typeof fetch) => ({ jobId, }: {
|
|
43
44
|
jobId: string;
|
|
44
45
|
}) => Promise<{
|
|
45
|
-
error:
|
|
46
|
+
error: FetchError | ValidationError;
|
|
46
47
|
} | {
|
|
47
48
|
result: Static<typeof FOTAJobType>;
|
|
48
49
|
}>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
|
-
import type { ValidationError } from '
|
|
2
|
+
import type { ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
export declare enum LocationHistoryServiceType {
|
|
4
5
|
ANCHOR = "ANCHOR",
|
|
5
6
|
GNSS = "GNSS",
|
|
@@ -39,7 +40,7 @@ export declare const getLocationHistory: ({ apiKey, endpoint, }: {
|
|
|
39
40
|
start?: Date;
|
|
40
41
|
end?: Date;
|
|
41
42
|
}) => Promise<{
|
|
42
|
-
error:
|
|
43
|
+
error: FetchError | ValidationError;
|
|
43
44
|
} | {
|
|
44
45
|
result: Static<typeof LocationHistoryType>;
|
|
45
46
|
}>;
|
package/dist/api/groundFix.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static } from '@sinclair/typebox';
|
|
2
2
|
import { type ValidationError } from './validatedFetch.js';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
export declare const lat: import("@sinclair/typebox").TNumber;
|
|
4
5
|
export declare const lng: import("@sinclair/typebox").TNumber;
|
|
5
6
|
export declare const accuracy: import("@sinclair/typebox").TNumber;
|
|
@@ -22,7 +23,7 @@ export declare const groundFix: ({ apiKey, endpoint, }: {
|
|
|
22
23
|
tac: number;
|
|
23
24
|
rsrp?: number;
|
|
24
25
|
}) => Promise<{
|
|
25
|
-
error:
|
|
26
|
+
error: FetchError | ValidationError;
|
|
26
27
|
} | {
|
|
27
28
|
result: Static<typeof GroundFixType>;
|
|
28
29
|
}>;
|
package/dist/api/index.d.ts
CHANGED
package/dist/api/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ValidationError } from './validatedFetch.js';
|
|
2
|
+
import type { FetchError } from './FetchError.js';
|
|
2
3
|
/**
|
|
3
4
|
* @link https://api.nrfcloud.com/v1/#tag/Account/operation/GetServiceToken
|
|
4
5
|
*/
|
|
@@ -9,7 +10,7 @@ export declare const serviceToken: ({ apiKey, endpoint, }: {
|
|
|
9
10
|
apiKey: string;
|
|
10
11
|
endpoint: URL;
|
|
11
12
|
}, fetchImplementation?: typeof fetch) => (() => Promise<{
|
|
12
|
-
error:
|
|
13
|
+
error: FetchError | ValidationError;
|
|
13
14
|
} | {
|
|
14
15
|
token: string;
|
|
15
16
|
}>);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Static, type TObject } from '@sinclair/typebox';
|
|
2
2
|
import type { ValueError } from '@sinclair/typebox/compiler';
|
|
3
|
+
import type { FetchError } from './FetchError.js';
|
|
3
4
|
export declare class ValidationError extends Error {
|
|
4
5
|
errors: ValueError[];
|
|
5
6
|
readonly isValidationError = true;
|
|
@@ -19,7 +20,7 @@ export declare const validatedFetch: ({ endpoint, apiKey }: {
|
|
|
19
20
|
}) & {
|
|
20
21
|
query?: URLSearchParams;
|
|
21
22
|
}, schema: Schema) => Promise<{
|
|
22
|
-
error:
|
|
23
|
+
error: FetchError | ValidationError;
|
|
23
24
|
} | {
|
|
24
25
|
result: Static<Schema>;
|
|
25
26
|
}>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {} from '@sinclair/typebox';
|
|
2
2
|
import { slashless } from './slashless.js';
|
|
3
3
|
import { validateWithTypeBox } from '@hello.nrfcloud.com/proto';
|
|
4
|
+
import { toFetchError } from './FetchError.js';
|
|
4
5
|
export class ValidationError extends Error {
|
|
5
6
|
errors;
|
|
6
7
|
isValidationError = true;
|
|
@@ -21,7 +22,7 @@ const validate = (SchemaObject, data) => {
|
|
|
21
22
|
const fetchData = (fetchImplementation) => async (...args) => {
|
|
22
23
|
const response = await (fetchImplementation ?? fetch)(...args);
|
|
23
24
|
if (!response.ok)
|
|
24
|
-
throw
|
|
25
|
+
throw await toFetchError(response);
|
|
25
26
|
return response.json();
|
|
26
27
|
};
|
|
27
28
|
export const validatedFetch = ({ endpoint, apiKey }, fetchImplementation) => async (params, schema) => {
|
|
@@ -2,6 +2,7 @@ import { describe, it, mock } from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { Type } from '@sinclair/typebox';
|
|
4
4
|
import { JSONPayload, validatedFetch } from './validatedFetch.js';
|
|
5
|
+
import { FetchError } from './FetchError.js';
|
|
5
6
|
void describe('validatedFetch()', () => {
|
|
6
7
|
void it('should call an nRF Cloud API endpoint and validate the response', async () => {
|
|
7
8
|
const mockFetch = mock.fn(() => ({
|
|
@@ -92,6 +93,22 @@ void describe('validatedFetch()', () => {
|
|
|
92
93
|
},
|
|
93
94
|
]);
|
|
94
95
|
});
|
|
96
|
+
void it('should return an error if the response is not OK', async () => {
|
|
97
|
+
const mockFetch = mock.fn(() => ({
|
|
98
|
+
ok: false,
|
|
99
|
+
status: 400,
|
|
100
|
+
text: async () => Promise.resolve('Bad Request'),
|
|
101
|
+
headers: new Map([[`content-length`, 'Bad Request'.length.toString()]]),
|
|
102
|
+
}));
|
|
103
|
+
const vf = validatedFetch({
|
|
104
|
+
endpoint: new URL('https://example.com/'),
|
|
105
|
+
apiKey: 'some-key',
|
|
106
|
+
}, mockFetch);
|
|
107
|
+
const res = await vf({ resource: 'foo' }, Type.Object({}));
|
|
108
|
+
assert.equal('error' in res, true);
|
|
109
|
+
assert.equal('result' in res, false);
|
|
110
|
+
assert.deepEqual('error' in res && res.error, new FetchError(400, 'Bad Request'));
|
|
111
|
+
});
|
|
95
112
|
});
|
|
96
113
|
void describe('JSONPayload()', () => {
|
|
97
114
|
void it('should convert a an object to a payload definition to be used in validatedFetch', () => assert.deepEqual(JSONPayload({ foo: 'bar' }), {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hello.nrfcloud.com/nrfcloud-api-helpers",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Helper functions for integrating nRF Cloud APIs in AWS lambdas written in TypeScript.",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./*": {
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"author": "Nordic Semiconductor ASA | nordicsemi.no",
|
|
33
33
|
"license": "BSD-3-Clause",
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@bifravst/eslint-config-typescript": "6.1.
|
|
36
|
-
"@bifravst/prettier-config": "1.0.
|
|
35
|
+
"@bifravst/eslint-config-typescript": "6.1.3",
|
|
36
|
+
"@bifravst/prettier-config": "1.0.3",
|
|
37
37
|
"@commitlint/config-conventional": "19.2.2",
|
|
38
38
|
"@types/aws-lambda": "8.10.138",
|
|
39
39
|
"@types/node": "20.14.2",
|
|
40
40
|
"husky": "9.0.11",
|
|
41
41
|
"tsmatchers": "5.0.2",
|
|
42
|
-
"tsx": "4.15.
|
|
42
|
+
"tsx": "4.15.2"
|
|
43
43
|
},
|
|
44
44
|
"lint-staged": {
|
|
45
45
|
"*.ts": [
|