Skip to content

Commit 31b059a

Browse files
committed
feat: add FOTA support
1 parent b30a978 commit 31b059a

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

src/api/createFOTAJob.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Type, type Static } from '@sinclair/typebox'
2+
import { JSONPayload, validatedFetch } from './validatedFetch.js'
3+
import type { ValidationError } from 'ajv'
4+
5+
const FOTAJobType = Type.Object(
6+
{
7+
jobId: Type.String({
8+
minLength: 1,
9+
title: 'ID',
10+
description: 'Universally unique identifier',
11+
examples: ['bc631093-7f7c-4c1b-aa63-a68c759bcd5c'],
12+
}),
13+
},
14+
{
15+
title: 'FOTA Job',
16+
description:
17+
'See https://api.nrfcloud.com/#tag/FOTA-Jobs/operation/CreateFOTAJob',
18+
},
19+
)
20+
export const createFOTAJob =
21+
(
22+
{
23+
apiKey,
24+
endpoint,
25+
}: {
26+
apiKey: string
27+
endpoint: URL
28+
},
29+
fetchImplementation?: typeof fetch,
30+
) =>
31+
async ({
32+
deviceId,
33+
bundleId,
34+
}: {
35+
deviceId: string
36+
bundleId: string
37+
}): Promise<
38+
{ error: Error | ValidationError } | { result: Static<typeof FOTAJobType> }
39+
> => {
40+
const maybeJob = await validatedFetch(
41+
{
42+
endpoint,
43+
apiKey,
44+
},
45+
fetchImplementation,
46+
)(
47+
{
48+
resource: 'fota-jobs',
49+
payload: JSONPayload({
50+
bundleId,
51+
autoApply: 'true',
52+
deviceIds: [deviceId],
53+
}),
54+
},
55+
FOTAJobType,
56+
)
57+
58+
if ('error' in maybeJob) return maybeJob
59+
return maybeJob
60+
}

src/api/fetchFOTAJob.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Type, type Static } from '@sinclair/typebox'
2+
import type { ValidationError } from 'ajv'
3+
import { validatedFetch } from './validatedFetch.js'
4+
5+
export enum FOTAJobStatus {
6+
CREATED = 'CREATED',
7+
IN_PROGRESS = 'IN_PROGRESS',
8+
CANCELLED = 'CANCELLED',
9+
DELETION_IN_PROGRESS = 'DELETION_IN_PROGRESS',
10+
COMPLETED = 'COMPLETED',
11+
}
12+
13+
const ts = Type.String({
14+
title: 'Timestamp',
15+
description: 'ISO-8601 date-time string',
16+
examples: ['2019-08-24T14:15:22Z'],
17+
})
18+
19+
export const FOTAJobType = Type.Object(
20+
{
21+
jobId: Type.String({
22+
minLength: 1,
23+
title: 'ID',
24+
description: 'Universally unique identifier',
25+
examples: ['bc631093-7f7c-4c1b-aa63-a68c759bcd5c'],
26+
}),
27+
status: Type.Enum(FOTAJobStatus, {
28+
title: 'Status',
29+
description: 'Current status of the job',
30+
}),
31+
statusDetail: Type.Optional(Type.String({ minLength: 1 })),
32+
createdAt: ts,
33+
lastUpdatedAt: Type.Optional(ts),
34+
completedAt: Type.Optional(ts),
35+
},
36+
{
37+
title: 'FOTA Job',
38+
description:
39+
'See https://api.nrfcloud.com/#tag/FOTA-Jobs/operation/FetchFOTAJob',
40+
},
41+
)
42+
export const fetchFOTAJob =
43+
(
44+
{
45+
apiKey,
46+
endpoint,
47+
}: {
48+
apiKey: string
49+
endpoint: URL
50+
},
51+
fetchImplementation?: typeof fetch,
52+
) =>
53+
async ({
54+
jobId,
55+
}: {
56+
jobId: string
57+
}): Promise<
58+
{ error: Error | ValidationError } | { result: Static<typeof FOTAJobType> }
59+
> => {
60+
const maybeJob = await validatedFetch(
61+
{
62+
endpoint,
63+
apiKey,
64+
},
65+
fetchImplementation,
66+
)(
67+
{
68+
resource: `fota-jobs/${jobId}`,
69+
},
70+
FOTAJobType,
71+
)
72+
73+
if ('error' in maybeJob) return maybeJob
74+
return maybeJob
75+
}

src/api/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export * from './DeviceShadow.js'
1111
export * from './serviceToken.js'
1212
export * from './groundFix.js'
1313
export * from './bulkOps.js'
14+
export * from './createFOTAJob.js'
15+
export * from './fetchFOTAJob.js'

0 commit comments

Comments
 (0)