forked from jeremydaly/dynamodb-toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
validateTypes.ts
114 lines (100 loc) · 3.63 KB
/
validateTypes.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
/**
* DynamoDB Toolbox: A simple set of tools for working with Amazon DynamoDB
* @author Jeremy Daly <jeremy@jeremydaly.com>
* @license MIT
*/
import { toBool, hasValue, error, toDynamoBigInt, typeOf, isArrayOfSameType } from './utils.js'
// Performs type validation/coercion
export default () => (mapping: any, field: any, value: any) => {
// Evaluate function expressions
// TODO: should this happen here?
// let value = typeof input === 'function' ? input(data) : input
// return if undefined or null
if (!hasValue(value)) return value
switch (mapping.type) {
case 'string':
return typeof value === 'string' || mapping.coerce
? String(value)
: error(`'${field}' must be of type string`)
case 'boolean':
return typeof value === 'boolean' || mapping.coerce
? toBool(value)
: error(`'${field}' must be of type boolean`)
case 'number': {
if (typeof value === 'number') {
return Number.isFinite(value) ? value : error(`'${field}' must be a finite number`)
}
if (!mapping.coerce) {
return error(`'${field}' must be of type number`)
}
const coercedValue = Number(value)
return typeof value === 'string' && Number.isFinite(coercedValue) && value.length > 0
? coercedValue
: error(
`Could not convert '${
Array.isArray(value) ? `[${value}]` : value
}' to a number for '${field}'`
)
}
case 'bigint':
return toDynamoBigInt(typeof value === 'bigint' || mapping.coerce
? BigInt(value)
: error(`'${field}' must be of type bigint`))
case 'list':
return Array.isArray(value)
? value
: mapping.coerce
? String(value)
.split(',')
.map(x => x.trim())
: error(`'${field}' must be a list (array)`)
case 'map':
return value?.constructor === Object ? value : error(`'${field}' must be a map (object)`)
case 'set':
if (value instanceof Set) {
if (mapping.setType === 'bigint') {
value = Array.from(value).map((n) => toDynamoBigInt(n))
}
return (
!mapping.setType ||
value.size === 0
|| isArrayOfSameType([...value])
? value
: error(
`'${field}' must be a valid set containing only ${mapping.setType} types`
))
} else if (Array.isArray(value)) {
const expectedSetType = mapping.setType?.toLowerCase?.()
const actualSetType = typeOf(value[0])?.toLowerCase?.()
if (mapping.setType === 'bigint') {
value = value.map((n) => toDynamoBigInt(n))
}
return (!mapping.setType ||
value.length === 0 ||
(expectedSetType === actualSetType && isArrayOfSameType(value))
? new Set(value)
: error(
`'${field}' must be a valid set (array) containing only ${expectedSetType ?? actualSetType} types`
)
)
} else if (mapping.coerce) {
if(value instanceof Set) return value
const arrayVal = String(value)
.split(',')
.map((x) => x.trim())
const expectedSetType = mapping.setType
const actualSetType = typeOf(arrayVal[0])
return (!mapping.setType ||
arrayVal.length === 0 ||
(expectedSetType === actualSetType && isArrayOfSameType(arrayVal))
? new Set(arrayVal)
: error(`'${field}' must be a valid set (array) of type ${mapping.setType}`)
)
} else {
return error(`'${field}' must be a valid set (array)`)
}
default:
// TODO: Binary validation
return value
}
}