@hello.nrfcloud.com/nrfcloud-api-helpers 5.0.0 → 5.0.2

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.
@@ -20,7 +20,7 @@ declare const Devices: import("@sinclair/typebox").TObject<{
20
20
  /**
21
21
  * firmware types supported by a device for FOTA
22
22
  */
23
- declare enum FwType {
23
+ export declare enum FwType {
24
24
  APP = "APP",
25
25
  MODEM = "MODEM",
26
26
  BOOT = "BOOT",
@@ -52,10 +52,17 @@ export declare const devices: ({ endpoint, apiKey, }: {
52
52
  }>;
53
53
  register: (devices: {
54
54
  deviceId: string;
55
+ certPem: string;
55
56
  subType?: string;
56
57
  tags?: string[];
58
+ /**
59
+ * A list of pipe-delimited firmware types that each device supports for FOTA (e.g., APP|MODEM)
60
+ *
61
+ * Defaults to all supported (APP|MODEM|BOOT|SOFTDEVICE|BOOTLOADER|MDM_FULL)
62
+ *
63
+ * @default 'APP|MODEM|BOOT|SOFTDEVICE|BOOTLOADER|MDM_FULL'
64
+ */
57
65
  fwTypes?: FwType[];
58
- certPem: string;
59
66
  }[]) => Promise<{
60
67
  error: Error;
61
68
  } | {
@@ -16,7 +16,7 @@ const ProvisionDevice = Type.Object({
16
16
  /**
17
17
  * firmware types supported by a device for FOTA
18
18
  */
19
- var FwType;
19
+ export var FwType;
20
20
  (function (FwType) {
21
21
  FwType["APP"] = "APP";
22
22
  FwType["MODEM"] = "MODEM";
@@ -32,6 +32,7 @@ export const devices = ({ endpoint, apiKey, }, fetchImplementation) => {
32
32
  };
33
33
  const vf = validatedFetch({ endpoint, apiKey }, fetchImplementation);
34
34
  return {
35
+ // FIXME: implement pagination
35
36
  list: async () => vf({
36
37
  resource: `devices?${new URLSearchParams({
37
38
  pageLimit: '100',
@@ -53,15 +54,18 @@ export const devices = ({ endpoint, apiKey, }, fetchImplementation) => {
53
54
  }),
54
55
  register: async (devices) => {
55
56
  const bulkRegistrationPayload = devices
56
- .map(({ deviceId, subType, tags, fwTypes, certPem }) => [
57
- [
58
- deviceId,
59
- subType ?? '',
60
- (tags ?? []).join('|'),
61
- (fwTypes ?? []).join('|'),
62
- `"${certPem}"`,
63
- ],
64
- ])
57
+ .map(({ deviceId, subType, tags, fwTypes, certPem }) => {
58
+ const deviceFwTypes = fwTypes ?? Object.values(FwType);
59
+ return [
60
+ [
61
+ deviceId,
62
+ subType ?? '',
63
+ (tags ?? []).join('|'),
64
+ deviceFwTypes.join('|'),
65
+ `"${certPem}"`,
66
+ ],
67
+ ];
68
+ })
65
69
  .map((cols) => cols.join(','))
66
70
  .join('\n');
67
71
  const maybeResult = await vf({
@@ -1,5 +1,6 @@
1
1
  import { type Static } from '@sinclair/typebox';
2
2
  import type { ValidationError } from 'ajv';
3
+ import { FwType } from './devices.js';
3
4
  export declare enum FOTAJobStatus {
4
5
  CREATED = "CREATED",
5
6
  IN_PROGRESS = "IN_PROGRESS",
@@ -14,6 +15,18 @@ export declare const FOTAJobType: import("@sinclair/typebox").TObject<{
14
15
  createdAt: import("@sinclair/typebox").TString;
15
16
  lastUpdatedAt: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
16
17
  completedAt: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
18
+ firmware: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
19
+ bundleId: import("@sinclair/typebox").TString;
20
+ fileSize: import("@sinclair/typebox").TNumber;
21
+ firmwareType: import("@sinclair/typebox").TEnum<typeof FwType>;
22
+ host: import("@sinclair/typebox").TString;
23
+ uris: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
24
+ version: import("@sinclair/typebox").TString;
25
+ }>>;
26
+ target: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
27
+ deviceIds: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
28
+ tags: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
29
+ }>>;
17
30
  }>;
18
31
  export declare const getFOTAJob: ({ apiKey, endpoint, }: {
19
32
  apiKey: string;
@@ -1,5 +1,6 @@
1
1
  import { Type } from '@sinclair/typebox';
2
2
  import { validatedFetch } from './validatedFetch.js';
3
+ import { FwType } from './devices.js';
3
4
  export var FOTAJobStatus;
4
5
  (function (FOTAJobStatus) {
5
6
  FOTAJobStatus["CREATED"] = "CREATED";
@@ -28,6 +29,33 @@ export const FOTAJobType = Type.Object({
28
29
  createdAt: ts,
29
30
  lastUpdatedAt: Type.Optional(ts),
30
31
  completedAt: Type.Optional(ts),
32
+ firmware: Type.Optional(Type.Object({
33
+ bundleId: Type.String({
34
+ minLength: 1,
35
+ examples: ['APP*439ddbc7*v2.0.0'],
36
+ }),
37
+ fileSize: Type.Number({ minimum: 1, examples: [385068] }),
38
+ firmwareType: Type.Enum(FwType, { title: 'Firmware Type' }),
39
+ host: Type.String({
40
+ minLength: 1,
41
+ examples: ['firmware.nrfcloud.com'],
42
+ }),
43
+ uris: Type.Array(Type.String({
44
+ minLength: 1,
45
+ examples: [
46
+ 'bbfe6b73-a46a-43ad-94bd-8e4b4a7847ce/APP*439ddbc7*v2.0.0/hello-nrfcloud-thingy91-v2.0.0-fwupd.bin',
47
+ ],
48
+ })),
49
+ version: Type.String({ minLength: 1, examples: ['v2.0.0'] }),
50
+ })),
51
+ target: Type.Optional(Type.Object({
52
+ deviceIds: Type.Array(Type.String({
53
+ minLength: 1,
54
+ title: 'Device ID',
55
+ examples: ['oob-358299840021360'],
56
+ })),
57
+ tags: Type.Array(Type.String({ minLength: 1, title: 'Tag', examples: ['nrf9160'] })),
58
+ })),
31
59
  }, {
32
60
  title: 'FOTA Job',
33
61
  description: 'See https://api.nrfcloud.com/#tag/FOTA-Jobs/operation/FetchFOTAJob',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hello.nrfcloud.com/nrfcloud-api-helpers",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Helper functions for integrating nRF Cloud APIs in AWS lambdas written in TypeScript.",
5
5
  "exports": {
6
6
  "./*": {
@@ -1,18 +0,0 @@
1
- import { type DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
- import { cellId } from './cellId.js';
3
- export declare const get: ({ db, TableName }: {
4
- db: DynamoDBClient;
5
- TableName: string;
6
- }) => (cell: Parameters<typeof cellId>[0]) => Promise<{
7
- lat: number;
8
- lng: number;
9
- accuracy: number;
10
- } | null>;
11
- export declare const store: ({ db, TableName }: {
12
- db: DynamoDBClient;
13
- TableName: string;
14
- }) => (cell: Parameters<typeof cellId>[0], location: {
15
- lat: number;
16
- lng: number;
17
- accuracy: number;
18
- }) => Promise<void>;
@@ -1,30 +0,0 @@
1
- import { GetItemCommand, PutItemCommand, } from '@aws-sdk/client-dynamodb';
2
- import { cellId } from './cellId.js';
3
- import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
4
- export const get = ({ db, TableName }) => async (cell) => {
5
- try {
6
- const { Item } = await db.send(new GetItemCommand({
7
- TableName,
8
- Key: {
9
- cellId: {
10
- S: cellId(cell),
11
- },
12
- },
13
- }));
14
- const { lat, lng, accuracy } = unmarshall(Item);
15
- return { lat, lng, accuracy };
16
- }
17
- catch {
18
- return null;
19
- }
20
- };
21
- export const store = ({ db, TableName }) => async (cell, location) => {
22
- await db.send(new PutItemCommand({
23
- TableName,
24
- Item: marshall({
25
- cellId: cellId(cell),
26
- ...location,
27
- ttl: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,
28
- }),
29
- }));
30
- };
@@ -1,3 +0,0 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import { mock } from 'node:test';
3
- export declare const assertCall: (mockFn: ReturnType<(typeof mock)['fn']>, args: Record<string, unknown>, callNumber?: number) => void;
@@ -1,99 +0,0 @@
1
- import { describe, it, mock } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { ResourceNotFoundException } from '@aws-sdk/client-dynamodb';
4
- import { get, store } from './cache.js';
5
- import { arrayContaining, check, objectMatching } from 'tsmatchers';
6
- export const assertCall = (mockFn, args, callNumber = 0) => {
7
- check(mockFn.mock.calls[callNumber]?.arguments).is(arrayContaining(objectMatching(args)));
8
- };
9
- void describe('cellGeoLocation/cache', () => {
10
- void describe('get()', () => {
11
- void it('should return null if cell is not cached', async () => {
12
- const send = mock.fn(async () => Promise.reject(new ResourceNotFoundException({
13
- message: ` Requested resource not found`,
14
- $metadata: {},
15
- })));
16
- assert.equal(await get({
17
- db: { send },
18
- TableName: 'cacheTable',
19
- })({
20
- area: 42,
21
- mccmnc: 53005,
22
- cell: 666,
23
- }), null);
24
- assertCall(send, {
25
- input: {
26
- Key: {
27
- cellId: {
28
- S: '53005-42-666',
29
- },
30
- },
31
- TableName: 'cacheTable',
32
- },
33
- });
34
- });
35
- void it('should return the location if the cell is cached', async () => {
36
- const send = mock.fn(async () => Promise.resolve({
37
- $metadata: {
38
- httpStatusCode: 200,
39
- requestId: 'SHTTK1LO0ELJ5LI2U0LUJOUBOJVV4KQNSO5AEMVJF66Q9ASUAAJG',
40
- extendedRequestId: undefined,
41
- cfId: undefined,
42
- attempts: 1,
43
- totalRetryDelay: 0,
44
- },
45
- Item: {
46
- cellId: { S: '53005-42-666' },
47
- lat: { N: '-36.87313199' },
48
- lng: { N: '174.7577405' },
49
- accuracy: { N: '510' },
50
- },
51
- }));
52
- assert.deepEqual(await get({
53
- db: { send },
54
- TableName: 'cacheTable',
55
- })({
56
- area: 42,
57
- mccmnc: 53005,
58
- cell: 666,
59
- }), { accuracy: 510, lat: -36.87313199, lng: 174.7577405 });
60
- assertCall(send, {
61
- input: {
62
- Key: {
63
- cellId: {
64
- S: '53005-42-666',
65
- },
66
- },
67
- TableName: 'cacheTable',
68
- },
69
- });
70
- });
71
- });
72
- void describe('store()', () => {
73
- void it('should store the cell', async () => {
74
- const send = mock.fn(async () => Promise.resolve({}));
75
- await store({
76
- db: { send },
77
- TableName: 'cacheTable',
78
- })({
79
- area: 42,
80
- mccmnc: 53005,
81
- cell: 666,
82
- }, { accuracy: 510, lat: -36.87313199, lng: 174.7577405 });
83
- assertCall(send, {
84
- input: {
85
- Item: {
86
- accuracy: { N: '510' },
87
- cellId: { S: '53005-42-666' },
88
- lat: { N: '-36.87313199' },
89
- lng: { N: '174.7577405' },
90
- ttl: {
91
- N: `${Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60}`,
92
- },
93
- },
94
- TableName: 'cacheTable',
95
- },
96
- });
97
- });
98
- });
99
- });
@@ -1,5 +0,0 @@
1
- export declare const cellId: ({ area, mccmnc, cell, }: {
2
- area: number;
3
- mccmnc: number;
4
- cell: number;
5
- }) => string;
@@ -1 +0,0 @@
1
- export const cellId = ({ area, mccmnc, cell, }) => `${mccmnc}-${area}-${cell}`;
@@ -1 +0,0 @@
1
- export {};
@@ -1,10 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { cellId } from './cellId.js';
4
- void describe('cellId', () => {
5
- void it('should generate a cellId', () => assert.equal(cellId({
6
- area: 42,
7
- mccmnc: 53005,
8
- cell: 666,
9
- }), '53005-42-666'));
10
- });
@@ -1,2 +0,0 @@
1
- export * from './cache.js';
2
- export * from './cellId.js';
@@ -1,2 +0,0 @@
1
- export * from './cache.js';
2
- export * from './cellId.js';