-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvalidation.ts
117 lines (110 loc) · 3.72 KB
/
validation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { LwM2MObjectIDs, type LwM2MObjectID } from './LwM2MObjectID.js'
import type { LwM2MObjectInstance } from './LwM2MObjectInstance.js'
export const isLwM2MObject = (
object: unknown,
): { object: LwM2MObjectInstance } | { error: Error } => {
const error = (message: string) => ({ error: new Error(message) })
// Must be an object
if (typeof object !== 'object' || object === null)
return error(`Not an object`)
// Must have valid ObjectID
if (
!('ObjectID' in object) ||
typeof object.ObjectID !== 'number' ||
object.ObjectID < 14200 ||
object.ObjectID > 15000 ||
LwM2MObjectIDs.includes(object.ObjectID) === false
)
return error(
`Not an valid Object ID: ${JSON.stringify((object as any).ObjectID)}`,
)
// ObjectVersion must be valid
if ('ObjectVersion' in object) {
if (
typeof object.ObjectVersion !== 'string' ||
!/^\d+\.\d+$/.test(object.ObjectVersion)
)
return error(`Invalid ObjectVersion`)
}
// ObjectInstanceID must be valid
if ('ObjectInstanceID' in object) {
if (
typeof object.ObjectInstanceID !== 'number' ||
object.ObjectInstanceID < 0
)
return error(`Invalid ObjectInstanceID`)
}
// Must have valid resources
if (
!('Resources' in object) ||
typeof object.Resources !== 'object' ||
object.Resources === null
)
return error(`Resources must be an object`)
// All keys must be numbers
if (
(Object.keys(object.Resources).find((k) => /[^\d]/.test(k))?.length ?? 0) >
0
)
return error(`All resource IDs must be a number`)
// All values must be number, string, boolean
for (const v of Object.values(object.Resources)) {
if (isSimpleResource(v)) continue
if (Array.isArray(v) && v.every((v) => isSimpleResource(v))) continue
return error(`Invalid value type ${typeof v}`)
}
return { object: object as LwM2MObjectInstance }
}
const isSimpleResource = (
v?: unknown,
): v is number | string | boolean | undefined => {
if (v === undefined) return true
if (typeof v === 'string') return true
if (typeof v === 'boolean') return true
if (typeof v === 'number') return true
return false
}
export const validateInstance =
<O extends LwM2MObjectInstance>(
ObjectID: LwM2MObjectID,
ObjectVersion: string,
Resources: Record<number, (r: unknown) => boolean>,
) =>
(o: unknown): { object: LwM2MObjectInstance<O> } | { error: Error } => {
const error = (message: string) => ({ error: new Error(message) })
const maybeValidLwM2MObject = isLwM2MObject(o)
if ('error' in maybeValidLwM2MObject) return maybeValidLwM2MObject
const i = maybeValidLwM2MObject.object
if (i.ObjectID !== ObjectID) {
return error(
`Given Object ID ${i.ObjectID} does not match expected ${ObjectID}`,
)
}
if ((i.ObjectVersion ?? '1.0') !== ObjectVersion) {
return error(
`Given Object version ${i.ObjectVersion} does not match expected ${ObjectVersion}`,
)
}
for (const [ResourceID, validator] of Object.entries(Resources)) {
if (validator(i.Resources[parseInt(ResourceID, 10)]) === false) {
return error(
`Resource ${ResourceID} for Object ${ObjectID} is invalid.`,
)
}
}
return { object: o as LwM2MObjectInstance<O> }
}
export const NumberResource = (r: unknown): r is number => typeof r === 'number'
export const TimeResource = (r: unknown): r is number =>
NumberResource(r) && r > 1700000000 && r < 9999999999
export const StringResource = (r: unknown): r is string => typeof r === 'string'
export const BooleanResource = (r: unknown): r is boolean =>
typeof r === 'boolean'
export const OptionalResource =
(validator: (r: unknown) => boolean) =>
(r: unknown): boolean =>
r === undefined ? true : validator(r)
export const MultipleInstanceResource =
(validator: (r: unknown) => boolean) =>
(r: unknown): boolean =>
Array.isArray(r) && r.every(validator)