@hello.nrfcloud.com/nrfcloud-api-helpers 6.0.463 → 6.0.464
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 → npm/src}/api/bulkOps.d.ts +2 -2
- package/{dist → npm/src}/api/cancelFOTAJob.d.ts +2 -2
- package/{dist → npm/src}/api/createFOTAJob.d.ts +2 -2
- package/{dist → npm/src}/api/devices.d.ts +3 -3
- package/npm/src/api/export.d.ts +18 -0
- package/{dist → npm/src}/api/getAccountInfo.d.ts +2 -2
- package/{dist → npm/src}/api/getCurrentMonthlyCosts.d.ts +2 -2
- package/{dist → npm/src}/api/getDeviceShadow.d.ts +2 -2
- package/{dist → npm/src}/api/getFOTABundles.d.ts +3 -3
- package/{dist → npm/src}/api/getFOTAJob.d.ts +3 -3
- package/{dist → npm/src}/api/getLocationHistory.d.ts +2 -2
- package/{dist → npm/src}/api/groundFix.d.ts +2 -2
- package/{dist → npm/src}/api/serviceToken.d.ts +2 -2
- package/{dist → npm/src}/api/validatedFetch.d.ts +1 -1
- package/npm/src/settings/export.d.ts +5 -0
- package/{dist → npm/src}/settings/getAllAccountsSettings.d.ts +1 -1
- package/package.json +18 -18
- package/dist/api/DeviceShadow.js +0 -23
- package/dist/api/DeviceShadow.spec.js +0 -45
- package/dist/api/FetchError.js +0 -11
- package/dist/api/bulkOps.js +0 -20
- package/dist/api/cancelFOTAJob.js +0 -14
- package/dist/api/createAccountDevice.js +0 -20
- package/dist/api/createFOTAJob.js +0 -29
- package/dist/api/deleteAccountDevice.js +0 -9
- package/dist/api/devices.js +0 -93
- package/dist/api/getAccountInfo.js +0 -21
- package/dist/api/getAccountInfo.spec.js +0 -25
- package/dist/api/getCurrentMonthlyCosts.js +0 -15
- package/dist/api/getCurrentMonthlyCosts.spec.js +0 -17
- package/dist/api/getDeviceShadow.js +0 -33
- package/dist/api/getDeviceShadow.spec.js +0 -28
- package/dist/api/getFOTABundle.spec.js +0 -94
- package/dist/api/getFOTABundles.js +0 -73
- package/dist/api/getFOTAJob.js +0 -81
- package/dist/api/getLocationHistory.js +0 -97
- package/dist/api/getLocationHistory.spec.js +0 -35
- package/dist/api/groundFix.js +0 -34
- package/dist/api/index.d.ts +0 -18
- package/dist/api/index.js +0 -18
- package/dist/api/serviceToken.js +0 -16
- package/dist/api/slashless.js +0 -1
- package/dist/api/slashless.spec.js +0 -6
- package/dist/api/test-data/account.json +0 -63
- package/dist/api/test-data/location.json +0 -26
- package/dist/api/validatedFetch.js +0 -57
- package/dist/api/validatedFetch.spec.js +0 -132
- package/dist/settings/getAllAccounts.js +0 -9
- package/dist/settings/getAllAccountsSettings.js +0 -14
- package/dist/settings/groupByAccount.js +0 -14
- package/dist/settings/groupByAccount.spec.js +0 -20
- package/dist/settings/index.d.ts +0 -5
- package/dist/settings/index.js +0 -5
- package/dist/settings/scope.js +0 -7
- package/dist/settings/settings.js +0 -88
- /package/{dist → npm/src}/api/DeviceShadow.d.ts +0 -0
- /package/{dist → npm/src}/api/DeviceShadow.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/FetchError.d.ts +0 -0
- /package/{dist → npm/src}/api/createAccountDevice.d.ts +0 -0
- /package/{dist → npm/src}/api/deleteAccountDevice.d.ts +0 -0
- /package/{dist → npm/src}/api/getAccountInfo.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/getCurrentMonthlyCosts.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/getDeviceShadow.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/getFOTABundle.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/getLocationHistory.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/slashless.d.ts +0 -0
- /package/{dist → npm/src}/api/slashless.spec.d.ts +0 -0
- /package/{dist → npm/src}/api/validatedFetch.spec.d.ts +0 -0
- /package/{dist → npm/src}/settings/getAllAccounts.d.ts +0 -0
- /package/{dist → npm/src}/settings/groupByAccount.d.ts +0 -0
- /package/{dist → npm/src}/settings/groupByAccount.spec.d.ts +0 -0
- /package/{dist → npm/src}/settings/scope.d.ts +0 -0
- /package/{dist → npm/src}/settings/settings.d.ts +0 -0
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it, mock } from 'node:test';
|
|
3
|
-
import { getDeviceShadow } from './getDeviceShadow.js';
|
|
4
|
-
void describe('getDeviceShadow()', () => {
|
|
5
|
-
void it('should accept a response without pagination and total devices', async () => {
|
|
6
|
-
const mockFetch = mock.fn(() => ({
|
|
7
|
-
ok: true,
|
|
8
|
-
json: async () => Promise.resolve({
|
|
9
|
-
items: [],
|
|
10
|
-
}),
|
|
11
|
-
}));
|
|
12
|
-
const fetcher = getDeviceShadow({
|
|
13
|
-
endpoint: new URL('https://example.com/'),
|
|
14
|
-
apiKey: 'some-key',
|
|
15
|
-
}, mockFetch);
|
|
16
|
-
const res = await fetcher(['device-id']);
|
|
17
|
-
assert.deepEqual(mockFetch.mock.calls[0]?.arguments, [
|
|
18
|
-
`https://example.com/v1/devices?deviceIds=device-id&includeState=true&includeStateMeta=true&pageLimit=100`,
|
|
19
|
-
{
|
|
20
|
-
headers: {
|
|
21
|
-
Accept: 'application/json; charset=utf-8',
|
|
22
|
-
Authorization: 'Bearer some-key',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
]);
|
|
26
|
-
assert.deepEqual(res, { shadows: [] });
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it, mock } from 'node:test';
|
|
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(async () => Promise.resolve({
|
|
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(async () => Promise.resolve({
|
|
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: [
|
|
37
|
-
'hello-nrfcloud-thingy91-sol-dbg-v1.1.2-fwupd.bin',
|
|
38
|
-
],
|
|
39
|
-
name: 'hello.nrfcloud.com v1.1.2-sol-dbg',
|
|
40
|
-
description: 'Firmware Update Image BIN file (thingy91, solar, debug)',
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
total: 1,
|
|
44
|
-
}),
|
|
45
|
-
}), 1);
|
|
46
|
-
const fetcher = getFOTABundles({
|
|
47
|
-
endpoint: new URL('https://example.com/'),
|
|
48
|
-
apiKey: 'some-key',
|
|
49
|
-
}, mockFetch);
|
|
50
|
-
const res = await fetcher();
|
|
51
|
-
assert.deepEqual(mockFetch.mock.calls[0]?.arguments, [
|
|
52
|
-
`https://example.com/v1/firmwares?pageLimit=100`,
|
|
53
|
-
{
|
|
54
|
-
headers: {
|
|
55
|
-
Accept: 'application/json; charset=utf-8',
|
|
56
|
-
Authorization: 'Bearer some-key',
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
]);
|
|
60
|
-
assert.deepEqual(mockFetch.mock.calls[1]?.arguments, [
|
|
61
|
-
`https://example.com/v1/firmwares?pageLimit=100&pageNextToken=${encodeURIComponent(`102/B5fGZNAs7vcw8E2i611ID4apx/Du/2/H6nr2UDWk5eoihEeAgh6qoaGcDzAI4M8JCoO4iAAK96TWfuB19ru9c1PrnwiTUdw/sZzwrYSrS433vPjDJNvJUIEmqm9+V3ElM5M1bLmt6GrGa57SymHHK4nN0W+zHhmE97cCCfzJMBXhVTl3TzvBx5rE1KJYf`)}`,
|
|
62
|
-
{
|
|
63
|
-
headers: {
|
|
64
|
-
Accept: 'application/json; charset=utf-8',
|
|
65
|
-
Authorization: 'Bearer some-key',
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
]);
|
|
69
|
-
assert.deepEqual(res, {
|
|
70
|
-
bundles: [
|
|
71
|
-
{
|
|
72
|
-
bundleId: 'APP*0038b655*v1.1.1-debug',
|
|
73
|
-
lastModified: '2023-06-28T09:50:02.000Z',
|
|
74
|
-
size: 418565,
|
|
75
|
-
version: 'v1.1.1-debug',
|
|
76
|
-
type: 'APP',
|
|
77
|
-
filenames: ['hello-nrfcloud-thingy91-debug-v1.1.1-fwupd.bin'],
|
|
78
|
-
name: 'hello.nrfcloud.com v1.1.1-debug',
|
|
79
|
-
description: 'Firmware Update Image BIN file (thingy91, debug)',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
bundleId: 'APP*0103b0f9*v1.1.2-sol-dbg',
|
|
83
|
-
lastModified: '2023-06-29T14:18:19.000Z',
|
|
84
|
-
size: 426280,
|
|
85
|
-
version: 'v1.1.2-sol-dbg',
|
|
86
|
-
type: 'APP',
|
|
87
|
-
filenames: ['hello-nrfcloud-thingy91-sol-dbg-v1.1.2-fwupd.bin'],
|
|
88
|
-
name: 'hello.nrfcloud.com v1.1.2-sol-dbg',
|
|
89
|
-
description: 'Firmware Update Image BIN file (thingy91, solar, debug)',
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { FwType } from './devices.js';
|
|
3
|
-
import { validatedFetch } from './validatedFetch.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.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { FwType } from './devices.js';
|
|
3
|
-
import { validatedFetch } from './validatedFetch.js';
|
|
4
|
-
export var FOTAJobStatus;
|
|
5
|
-
(function (FOTAJobStatus) {
|
|
6
|
-
FOTAJobStatus["CREATED"] = "CREATED";
|
|
7
|
-
FOTAJobStatus["IN_PROGRESS"] = "IN_PROGRESS";
|
|
8
|
-
FOTAJobStatus["CANCELLED"] = "CANCELLED";
|
|
9
|
-
FOTAJobStatus["DELETION_IN_PROGRESS"] = "DELETION_IN_PROGRESS";
|
|
10
|
-
FOTAJobStatus["COMPLETED"] = "COMPLETED";
|
|
11
|
-
FOTAJobStatus["QUEUED"] = "QUEUED";
|
|
12
|
-
FOTAJobStatus["FAILED"] = "FAILED";
|
|
13
|
-
FOTAJobStatus["SUCCEEDED"] = "SUCCEEDED";
|
|
14
|
-
FOTAJobStatus["TIMED_OUT"] = "TIMED_OUT";
|
|
15
|
-
FOTAJobStatus["REJECTED"] = "REJECTED";
|
|
16
|
-
FOTAJobStatus["DOWNLOADING"] = "DOWNLOADING";
|
|
17
|
-
})(FOTAJobStatus || (FOTAJobStatus = {}));
|
|
18
|
-
const ts = Type.String({
|
|
19
|
-
title: 'Timestamp',
|
|
20
|
-
description: 'ISO-8601 date-time string',
|
|
21
|
-
examples: ['2019-08-24T14:15:22Z'],
|
|
22
|
-
});
|
|
23
|
-
export const FOTAJobType = Type.Object({
|
|
24
|
-
jobId: Type.String({
|
|
25
|
-
minLength: 1,
|
|
26
|
-
title: 'ID',
|
|
27
|
-
description: 'Universally unique identifier',
|
|
28
|
-
examples: ['bc631093-7f7c-4c1b-aa63-a68c759bcd5c'],
|
|
29
|
-
}),
|
|
30
|
-
status: Type.Enum(FOTAJobStatus, {
|
|
31
|
-
title: 'Status',
|
|
32
|
-
description: 'Current status of the job',
|
|
33
|
-
}),
|
|
34
|
-
statusDetail: Type.Optional(Type.String({ minLength: 0 })),
|
|
35
|
-
name: Type.Optional(Type.String({ minLength: 1 })),
|
|
36
|
-
description: Type.Optional(Type.String({ minLength: 1 })),
|
|
37
|
-
createdAt: ts,
|
|
38
|
-
lastUpdatedAt: Type.Optional(ts),
|
|
39
|
-
completedAt: Type.Optional(ts),
|
|
40
|
-
firmware: Type.Optional(Type.Object({
|
|
41
|
-
bundleId: Type.String({
|
|
42
|
-
minLength: 1,
|
|
43
|
-
examples: ['APP*439ddbc7*v2.0.0'],
|
|
44
|
-
}),
|
|
45
|
-
fileSize: Type.Number({ minimum: 1, examples: [385068] }),
|
|
46
|
-
firmwareType: Type.Enum(FwType, { title: 'Firmware Type' }),
|
|
47
|
-
host: Type.String({
|
|
48
|
-
minLength: 1,
|
|
49
|
-
examples: ['firmware.nrfcloud.com'],
|
|
50
|
-
}),
|
|
51
|
-
uris: Type.Array(Type.String({
|
|
52
|
-
minLength: 1,
|
|
53
|
-
examples: [
|
|
54
|
-
'bbfe6b73-a46a-43ad-94bd-8e4b4a7847ce/APP*439ddbc7*v2.0.0/hello-nrfcloud-thingy91-v2.0.0-fwupd.bin',
|
|
55
|
-
],
|
|
56
|
-
})),
|
|
57
|
-
version: Type.String({ minLength: 1, examples: ['v2.0.0'] }),
|
|
58
|
-
})),
|
|
59
|
-
target: Type.Optional(Type.Object({
|
|
60
|
-
deviceIds: Type.Array(Type.String({
|
|
61
|
-
minLength: 1,
|
|
62
|
-
title: 'Device ID',
|
|
63
|
-
examples: ['oob-358299840021360'],
|
|
64
|
-
})),
|
|
65
|
-
tags: Type.Array(Type.String({ minLength: 1, title: 'Tag', examples: ['nrf9160'] })),
|
|
66
|
-
})),
|
|
67
|
-
}, {
|
|
68
|
-
title: 'FOTA Job',
|
|
69
|
-
description: 'See https://api.nrfcloud.com/#tag/FOTA-Jobs/operation/FetchFOTAJob',
|
|
70
|
-
});
|
|
71
|
-
export const getFOTAJob = ({ apiKey, endpoint, }, fetchImplementation) => async ({ jobId, }) => {
|
|
72
|
-
const maybeJob = await validatedFetch({
|
|
73
|
-
endpoint,
|
|
74
|
-
apiKey,
|
|
75
|
-
}, fetchImplementation)({
|
|
76
|
-
resource: `fota-jobs/${jobId}`,
|
|
77
|
-
}, FOTAJobType);
|
|
78
|
-
if ('error' in maybeJob)
|
|
79
|
-
return maybeJob;
|
|
80
|
-
return maybeJob;
|
|
81
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { validatedFetch } from './validatedFetch.js';
|
|
3
|
-
export var LocationHistoryServiceType;
|
|
4
|
-
(function (LocationHistoryServiceType) {
|
|
5
|
-
LocationHistoryServiceType["ANCHOR"] = "ANCHOR";
|
|
6
|
-
LocationHistoryServiceType["GNSS"] = "GNSS";
|
|
7
|
-
LocationHistoryServiceType["GPS"] = "GPS";
|
|
8
|
-
LocationHistoryServiceType["MCELL"] = "MCELL";
|
|
9
|
-
LocationHistoryServiceType["MCELL_EVAL"] = "MCELL_EVAL";
|
|
10
|
-
LocationHistoryServiceType["SCELL"] = "SCELL";
|
|
11
|
-
LocationHistoryServiceType["SCELL_EVAL"] = "SCELL_EVAL";
|
|
12
|
-
LocationHistoryServiceType["WIFI"] = "WIFI";
|
|
13
|
-
LocationHistoryServiceType["WIFI_EVAL"] = "WIFI_EVAL";
|
|
14
|
-
})(LocationHistoryServiceType || (LocationHistoryServiceType = {}));
|
|
15
|
-
const LocationHistoryType = Type.Object({
|
|
16
|
-
items: Type.Array(Type.Object({
|
|
17
|
-
id: Type.String({
|
|
18
|
-
minLength: 1,
|
|
19
|
-
title: 'ID',
|
|
20
|
-
description: 'Universally unique identifier',
|
|
21
|
-
examples: ['bc631093-7f7c-4c1b-aa63-a68c759bcd5c'],
|
|
22
|
-
}),
|
|
23
|
-
deviceId: Type.String({
|
|
24
|
-
minLength: 1,
|
|
25
|
-
title: 'This is the canonical device id used in the device certificate, and as the MQTT client id.',
|
|
26
|
-
examples: ['nrf-1234567890123456789000'],
|
|
27
|
-
}),
|
|
28
|
-
serviceType: Type.Enum(LocationHistoryServiceType, {
|
|
29
|
-
title: 'Tracker Service Type',
|
|
30
|
-
description: 'This is the service used to obtain the location of a device. The "_EVAL" suffix means the location was obtained using an evaluation token. GNSS location is derived on the device and reported back to the cloud. "GPS" type has been deprecated, but will still return for older records.',
|
|
31
|
-
examples: ['location'],
|
|
32
|
-
}),
|
|
33
|
-
insertedAt: Type.String({
|
|
34
|
-
title: 'Insertion Time',
|
|
35
|
-
description: 'HTML-encoded ISO-8601 date-time string denoting the start or end of a date range. If the string includes only a date, the time is the beginning of the day (00:00:00).',
|
|
36
|
-
examples: ['2021-08-31T20:00:00Z'],
|
|
37
|
-
}),
|
|
38
|
-
lat: Type.String({
|
|
39
|
-
minLength: 1,
|
|
40
|
-
examples: ['63.41999531'],
|
|
41
|
-
description: 'Latitude in degrees',
|
|
42
|
-
}),
|
|
43
|
-
lon: Type.String({
|
|
44
|
-
minLength: 1,
|
|
45
|
-
examples: ['-122.688408'],
|
|
46
|
-
description: 'Longitude in degrees',
|
|
47
|
-
}),
|
|
48
|
-
meta: Type.Record(Type.String({ minLength: 1 }), Type.Any(), {
|
|
49
|
-
title: 'GNSS metatdata',
|
|
50
|
-
description: 'Metadata sent from device when reporting GNSS location in PVT format. Can include other non-gnss related key/value pairs for easy retrieval later. Only populated for GNSS PVT formatted fixes, empty object otherwise.',
|
|
51
|
-
}),
|
|
52
|
-
uncertainty: Type.RegExp(/^[0-9.]+$/, {
|
|
53
|
-
title: 'Uncertainty',
|
|
54
|
-
description: 'Radius of the uncertainty circle around the location in meters. Also known as Horizontal Positioning Error (HPE).',
|
|
55
|
-
examples: ['2420', '13.012'],
|
|
56
|
-
}),
|
|
57
|
-
anchors: Type.Optional(Type.Array(Type.Object({
|
|
58
|
-
macAddress: Type.RegExp(/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/i, {
|
|
59
|
-
title: 'Mac Address',
|
|
60
|
-
description: 'String comprised of 6 hexadecimal pairs, separated by colons or dashes. When used in a positioning request, it must be universally assigned. See this help page for details.',
|
|
61
|
-
examples: ['FE:1E:41:2D:9E:53'],
|
|
62
|
-
}),
|
|
63
|
-
name: Type.Optional(Type.RegExp(/^[a-z0-9_ -]{1,32}$/i, {
|
|
64
|
-
title: 'Name',
|
|
65
|
-
description: 'Limit 32 characters. Only numbers, letters, underscores, dashes, and spaces allowed. All other characters will be removed.',
|
|
66
|
-
examples: ['anchor-1'],
|
|
67
|
-
})),
|
|
68
|
-
}))),
|
|
69
|
-
})),
|
|
70
|
-
total: Type.Number({ minimum: 0 }),
|
|
71
|
-
pageNextToken: Type.Optional(Type.String({ minLength: 1 })),
|
|
72
|
-
}, {
|
|
73
|
-
title: 'Location History',
|
|
74
|
-
description: 'See https://api.nrfcloud.com/v1#tag/Location-History/operation/GetLocationHistory',
|
|
75
|
-
});
|
|
76
|
-
export const getLocationHistory = ({ apiKey, endpoint, }, fetchImplementation) => async ({ deviceId, pageNextToken, start, end, }) => {
|
|
77
|
-
const query = new URLSearchParams({
|
|
78
|
-
pageLimit: '100',
|
|
79
|
-
deviceId,
|
|
80
|
-
});
|
|
81
|
-
if (start !== undefined)
|
|
82
|
-
query.set('start', start.toISOString());
|
|
83
|
-
if (end !== undefined)
|
|
84
|
-
query.set('end', end.toISOString());
|
|
85
|
-
if (pageNextToken !== undefined)
|
|
86
|
-
query.set('pageNextToken', pageNextToken);
|
|
87
|
-
const maybeHistory = await validatedFetch({
|
|
88
|
-
endpoint,
|
|
89
|
-
apiKey,
|
|
90
|
-
}, fetchImplementation)({
|
|
91
|
-
resource: 'location/history',
|
|
92
|
-
query,
|
|
93
|
-
}, LocationHistoryType);
|
|
94
|
-
if ('error' in maybeHistory)
|
|
95
|
-
return maybeHistory;
|
|
96
|
-
return maybeHistory;
|
|
97
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { aString, arrayMatching, check, objectMatching } from 'tsmatchers';
|
|
4
|
-
import { getLocationHistory } from './getLocationHistory.js';
|
|
5
|
-
import APIresponse from './test-data/location.json' assert { type: 'json' };
|
|
6
|
-
await describe('getLocationHistory()', async () => {
|
|
7
|
-
await it('return the location history', async () => {
|
|
8
|
-
const res = await getLocationHistory({
|
|
9
|
-
endpoint: new URL('https://example.com/'),
|
|
10
|
-
apiKey: 'some-key',
|
|
11
|
-
}, () => Promise.resolve({
|
|
12
|
-
ok: true,
|
|
13
|
-
json: async () => Promise.resolve(APIresponse),
|
|
14
|
-
}))({
|
|
15
|
-
deviceId: 'oob-355025930003742',
|
|
16
|
-
});
|
|
17
|
-
assert.equal('error' in res, false);
|
|
18
|
-
check('result' in res && res.result).is(objectMatching({
|
|
19
|
-
items: arrayMatching([
|
|
20
|
-
objectMatching({
|
|
21
|
-
id: '3b45f2db-3b0c-4be8-be9a-273f12697fc4',
|
|
22
|
-
deviceId: 'oob-355025930003742',
|
|
23
|
-
serviceType: 'MCELL',
|
|
24
|
-
insertedAt: '2024-06-04T09:54:52.651Z',
|
|
25
|
-
uncertainty: '301',
|
|
26
|
-
lat: '59.92335269',
|
|
27
|
-
lon: '10.68829941',
|
|
28
|
-
meta: {},
|
|
29
|
-
}),
|
|
30
|
-
]),
|
|
31
|
-
total: 100,
|
|
32
|
-
pageNextToken: aString,
|
|
33
|
-
}));
|
|
34
|
-
});
|
|
35
|
-
});
|
package/dist/api/groundFix.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { JSONPayload, validatedFetch, } from './validatedFetch.js';
|
|
3
|
-
export const lat = Type.Number({
|
|
4
|
-
minimum: -90,
|
|
5
|
-
maximum: 90,
|
|
6
|
-
description: 'Latitude in degrees',
|
|
7
|
-
});
|
|
8
|
-
export const lng = Type.Number({
|
|
9
|
-
minimum: -180,
|
|
10
|
-
maximum: 180,
|
|
11
|
-
description: 'Longitude in degrees',
|
|
12
|
-
});
|
|
13
|
-
export const accuracy = Type.Number({
|
|
14
|
-
minimum: 0,
|
|
15
|
-
description: 'HPE (horizontal positioning error) in meters',
|
|
16
|
-
});
|
|
17
|
-
/**
|
|
18
|
-
* @link https://api.nrfcloud.com/v1/#tag/Ground-Fix
|
|
19
|
-
*/
|
|
20
|
-
export const GroundFixType = Type.Object({
|
|
21
|
-
lat, // 63.41999531
|
|
22
|
-
lon: lng, // 10.42999506
|
|
23
|
-
uncertainty: accuracy, // 2420
|
|
24
|
-
fulfilledWith: Type.Literal('SCELL'),
|
|
25
|
-
});
|
|
26
|
-
export const groundFix = ({ apiKey, endpoint, }, fetchImplementation) => async (cell) => {
|
|
27
|
-
const vf = validatedFetch({ endpoint, apiKey }, fetchImplementation);
|
|
28
|
-
return vf({
|
|
29
|
-
resource: 'location/ground-fix',
|
|
30
|
-
payload: JSONPayload({
|
|
31
|
-
lte: [cell],
|
|
32
|
-
}),
|
|
33
|
-
}, GroundFixType);
|
|
34
|
-
};
|
package/dist/api/index.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export * from './bulkOps.js';
|
|
2
|
-
export * from './cancelFOTAJob.js';
|
|
3
|
-
export * from './createAccountDevice.js';
|
|
4
|
-
export * from './createFOTAJob.js';
|
|
5
|
-
export * from './deleteAccountDevice.js';
|
|
6
|
-
export * from './devices.js';
|
|
7
|
-
export * from './DeviceShadow.js';
|
|
8
|
-
export * from './FetchError.js';
|
|
9
|
-
export * from './getAccountInfo.js';
|
|
10
|
-
export * from './getCurrentMonthlyCosts.js';
|
|
11
|
-
export * from './getDeviceShadow.js';
|
|
12
|
-
export * from './getFOTABundles.js';
|
|
13
|
-
export * from './getFOTAJob.js';
|
|
14
|
-
export * from './getLocationHistory.js';
|
|
15
|
-
export * from './groundFix.js';
|
|
16
|
-
export * from './serviceToken.js';
|
|
17
|
-
export * from './slashless.js';
|
|
18
|
-
export * from './validatedFetch.js';
|
package/dist/api/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export * from './bulkOps.js';
|
|
2
|
-
export * from './cancelFOTAJob.js';
|
|
3
|
-
export * from './createAccountDevice.js';
|
|
4
|
-
export * from './createFOTAJob.js';
|
|
5
|
-
export * from './deleteAccountDevice.js';
|
|
6
|
-
export * from './devices.js';
|
|
7
|
-
export * from './DeviceShadow.js';
|
|
8
|
-
export * from './FetchError.js';
|
|
9
|
-
export * from './getAccountInfo.js';
|
|
10
|
-
export * from './getCurrentMonthlyCosts.js';
|
|
11
|
-
export * from './getDeviceShadow.js';
|
|
12
|
-
export * from './getFOTABundles.js';
|
|
13
|
-
export * from './getFOTAJob.js';
|
|
14
|
-
export * from './getLocationHistory.js';
|
|
15
|
-
export * from './groundFix.js';
|
|
16
|
-
export * from './serviceToken.js';
|
|
17
|
-
export * from './slashless.js';
|
|
18
|
-
export * from './validatedFetch.js';
|
package/dist/api/serviceToken.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { validatedFetch } from './validatedFetch.js';
|
|
3
|
-
/**
|
|
4
|
-
* @link https://api.nrfcloud.com/v1/#tag/Account/operation/GetServiceToken
|
|
5
|
-
*/
|
|
6
|
-
export const ServiceToken = Type.Object({
|
|
7
|
-
token: Type.String(),
|
|
8
|
-
});
|
|
9
|
-
export const serviceToken = ({ apiKey, endpoint, }, fetchImplementation) => async () => {
|
|
10
|
-
const vf = validatedFetch({ endpoint, apiKey }, fetchImplementation);
|
|
11
|
-
const maybeResult = await vf({ resource: 'account/service-token' }, ServiceToken);
|
|
12
|
-
if ('error' in maybeResult) {
|
|
13
|
-
return maybeResult;
|
|
14
|
-
}
|
|
15
|
-
return maybeResult.result;
|
|
16
|
-
};
|
package/dist/api/slashless.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const slashless = (url) => url.toString().replace(/\/$/, '');
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { slashless } from './slashless.js';
|
|
4
|
-
void describe('slashless()', () => {
|
|
5
|
-
void it('should remove the slash from an URL and convert it to a string', () => assert.equal(slashless(new URL('https://api.nrfcloud.com/')), 'https://api.nrfcloud.com'));
|
|
6
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"mqttEndpoint": "mqtt.nrfcloud.com",
|
|
3
|
-
"mqttTopicPrefix": "prod/b8b26bc5-2814-4063-b4fa-83ecddb2fec7/",
|
|
4
|
-
"team": {
|
|
5
|
-
"tenantId": "b8b26bc5-2814-4063-b4fa-83ecddb2fec7",
|
|
6
|
-
"name": "XXX"
|
|
7
|
-
},
|
|
8
|
-
"role": "owner",
|
|
9
|
-
"tags": [],
|
|
10
|
-
"plan": {
|
|
11
|
-
"name": "ENTERPRISE",
|
|
12
|
-
"proxyUsageDeclarations": {
|
|
13
|
-
"AGPS": 0,
|
|
14
|
-
"PGPS": 0,
|
|
15
|
-
"GROUND_FIX": 0
|
|
16
|
-
},
|
|
17
|
-
"serviceKeys": [
|
|
18
|
-
{
|
|
19
|
-
"service": "ALL",
|
|
20
|
-
"enabled": true,
|
|
21
|
-
"createdAt": "2023-09-14T10:24:31.663Z"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"currentMonthCosts": [
|
|
25
|
-
{
|
|
26
|
-
"serviceId": "Devices",
|
|
27
|
-
"serviceDescription": "Devices in your account",
|
|
28
|
-
"quantity": 53,
|
|
29
|
-
"price": 0.1,
|
|
30
|
-
"total": 5.3
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"serviceId": "Messages",
|
|
34
|
-
"serviceDescription": "Device messages stored",
|
|
35
|
-
"quantity": 70281,
|
|
36
|
-
"price": 0.0001,
|
|
37
|
-
"total": 7.03
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"serviceId": "AGPS",
|
|
41
|
-
"serviceDescription": "Assisted GPS Service: total requests",
|
|
42
|
-
"quantity": 8,
|
|
43
|
-
"price": 0.001,
|
|
44
|
-
"total": 0.01
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"serviceId": "SCELL",
|
|
48
|
-
"serviceDescription": "Single-Cell Location Service: total requests",
|
|
49
|
-
"quantity": 2684,
|
|
50
|
-
"price": 0.001,
|
|
51
|
-
"total": 2.68
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"serviceId": "MCELL",
|
|
55
|
-
"serviceDescription": "Multi-Cell Location Service: total requests",
|
|
56
|
-
"quantity": 4505,
|
|
57
|
-
"price": 0.002,
|
|
58
|
-
"total": 9.01
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"currentMonthTotalCost": 24.03
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"items": [
|
|
3
|
-
{
|
|
4
|
-
"id": "3b45f2db-3b0c-4be8-be9a-273f12697fc4",
|
|
5
|
-
"deviceId": "oob-355025930003742",
|
|
6
|
-
"serviceType": "MCELL",
|
|
7
|
-
"insertedAt": "2024-06-04T09:54:52.651Z",
|
|
8
|
-
"uncertainty": "301",
|
|
9
|
-
"lat": "59.92335269",
|
|
10
|
-
"lon": "10.68829941",
|
|
11
|
-
"meta": {}
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"id": "1d2d7188-ef48-427f-9e7d-55caf47958ac",
|
|
15
|
-
"deviceId": "oob-355025930003742",
|
|
16
|
-
"serviceType": "WIFI",
|
|
17
|
-
"insertedAt": "2024-06-04T09:54:47.014Z",
|
|
18
|
-
"uncertainty": "15.066",
|
|
19
|
-
"lat": "59.9212502",
|
|
20
|
-
"lon": "10.6885059",
|
|
21
|
-
"meta": {}
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"total": 100,
|
|
25
|
-
"pageNextToken": "G6QAUI3UVncWwk%2Bps9f3dKLy3I6kBRhUXs5fmFKo3JJn4JADhwMGFhAH3BIINuA4xsDGDT8Ev7rn%2FV45Dd2vBBCx6wxjnHJOFJ6JQyLRS0Kmudiqz%2FMEsmojHdxaYxOCA06orvL7gm7dxH2o06ADouim%2BstZ2lqDqFDECoX6Rh%2FJGB5dsxTPDQM%3D"
|
|
26
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { validateWithTypeBox } from '@hello.nrfcloud.com/proto';
|
|
2
|
-
import {} from '@sinclair/typebox';
|
|
3
|
-
import { toFetchError } from './FetchError.js';
|
|
4
|
-
import { slashless } from './slashless.js';
|
|
5
|
-
export class ValidationError extends Error {
|
|
6
|
-
errors;
|
|
7
|
-
isValidationError = true;
|
|
8
|
-
constructor(errors) {
|
|
9
|
-
super(`Validation errors`);
|
|
10
|
-
this.name = 'ValidationError';
|
|
11
|
-
this.errors = errors;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const validate = (SchemaObject, data) => {
|
|
15
|
-
const maybeData = validateWithTypeBox(SchemaObject)(data);
|
|
16
|
-
if ('errors' in maybeData) {
|
|
17
|
-
console.error('Validation failed', { error: maybeData.errors });
|
|
18
|
-
throw new ValidationError(maybeData.errors);
|
|
19
|
-
}
|
|
20
|
-
return maybeData.value;
|
|
21
|
-
};
|
|
22
|
-
const fetchData = (fetchImplementation) => async (...args) => {
|
|
23
|
-
const response = await (fetchImplementation ?? fetch)(...args);
|
|
24
|
-
if (!response.ok)
|
|
25
|
-
throw await toFetchError(response);
|
|
26
|
-
if (response.headers?.get('content-length') === '0')
|
|
27
|
-
return undefined;
|
|
28
|
-
return response.json();
|
|
29
|
-
};
|
|
30
|
-
export const validatedFetch = ({ endpoint, apiKey }, fetchImplementation) => async (params, schema) => {
|
|
31
|
-
const { resource, query } = params;
|
|
32
|
-
const args = {
|
|
33
|
-
headers: headers(apiKey),
|
|
34
|
-
};
|
|
35
|
-
if ('payload' in params) {
|
|
36
|
-
const payload = params.payload;
|
|
37
|
-
args.method = 'POST';
|
|
38
|
-
args.body = payload.body;
|
|
39
|
-
args.headers = { ...(args.headers ?? {}), ['Content-Type']: payload.type };
|
|
40
|
-
}
|
|
41
|
-
else if ('method' in params) {
|
|
42
|
-
args.method = params.method;
|
|
43
|
-
}
|
|
44
|
-
return fetchData(fetchImplementation)(`${slashless(endpoint)}/v1/${resource}${query !== undefined ? `?${query.toString()}` : ''}`, args)
|
|
45
|
-
.then((res) => ({ result: validate(schema, res) }))
|
|
46
|
-
.catch((error) => ({
|
|
47
|
-
error,
|
|
48
|
-
}));
|
|
49
|
-
};
|
|
50
|
-
const headers = (apiKey) => ({
|
|
51
|
-
Authorization: `Bearer ${apiKey}`,
|
|
52
|
-
Accept: 'application/json; charset=utf-8',
|
|
53
|
-
});
|
|
54
|
-
export const JSONPayload = (payload) => ({
|
|
55
|
-
type: 'application/json',
|
|
56
|
-
body: JSON.stringify(payload),
|
|
57
|
-
});
|