forked from dynamodb-toolbox/dynamodb-toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparseMapping.ts
149 lines (131 loc) · 5.62 KB
/
parseMapping.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
* DynamoDB Toolbox: A simple set of tools for working with Amazon DynamoDB
* @author Jeremy Daly <[email protected]>
* @license MIT
*/
import { EntityAttributeConfig } from '../classes/Entity'
import { TrackingInfo } from './parseEntity'
import { error } from './utils'
// Parse and validate mapping config
export default (field: string, config: EntityAttributeConfig, track: TrackingInfo) => {
// Validate props
Object.keys(config).forEach(prop => {
switch(prop) {
case 'type':
case 'default':
break
case 'dependsOn':
if (typeof config[prop] !== 'string' && !Array.isArray(config[prop]))
error(`'dependsOn' must be the string name of an attribute or alias`)
break
case 'transform':
if (typeof config[prop] !== 'function') error(`'${prop}' must be a function`)
break
case "inverseTransform":
if (typeof config[prop] !== "function")
error(`'${prop}' must be a function`);
break;
case 'coerce':
case 'onUpdate':
case 'hidden':
case 'save':
if (typeof config[prop] !== 'boolean') error(`'${prop}' must be a boolean`)
break
case 'required':
if (typeof config[prop] !== 'boolean' && config[prop] !== 'always')
error(`'required' must be a boolean or set to 'always'`)
break
case 'alias':
case 'map':
if (typeof config[prop] !== 'string'
|| track.fields.includes((config[prop] || '').trim())
|| (config[prop] || '').trim().length === 0) error(`'${prop}' must be a unique string`)
break
case 'setType':
if (config.type !== 'set') error(`'setType' is only valid for type 'set'`)
if (!['string','number','binary'].includes((config[prop] || '')))
error(`Invalid 'setType', must be 'string', 'number', or 'binary'`)
break
case 'delimiter':
if (typeof config[prop] !== 'string' || (config[prop] || '').trim().length === 0)
error(`'delimiter' must be a 'string'`)
config[prop] = (config[prop] || '').trim()
break
case 'prefix':
case 'suffix':
if (config.type && config.type !== 'string')
error(`'${prop}' can only be used on 'string' types`)
if (typeof config[prop] !== 'string' || (config[prop] || '').trim().length === 0)
error(`'${prop}' must be a 'string'`)
break
case 'partitionKey':
case 'sortKey':
if (config.map || config.alias) error(`Attributes with a ${prop} cannot have a 'map' or 'alias' associated`)
if (typeof config[prop] === 'boolean' || typeof config[prop] === 'string' || Array.isArray(config[prop])) {
// Coerce/cast to an array of strings/booleans
const indexes = (Array.isArray(config[prop]) ? config[prop] : [config[prop]]) as (string|boolean)[]
// Loop through values and track keys
for (let i in indexes) {
// If a boolean, set primary pk/sk
if (typeof indexes[i] === 'boolean') {
// Check that another prop isn't already a key
if (track.keys[prop])
error(`'${track.keys[prop]}' has already been declared as the ${prop}`)
// If true, add the field as the key
if (indexes[i])
track.keys[prop] = field
// If the partionKey is the same as the sortKey, throw an error
if (track.keys.partitionKey && track.keys.partitionKey === track.keys.sortKey)
error(`'${field}' attribute cannot be both the partitionKey and sortKey`)
// If string, set index pk/sk
} else if (typeof indexes[i] === 'string') {
const index = indexes[i] as string
// If the index isn't being tracked yet, add it
if (!track.keys[index])
track.keys[index] = {}
// If the index's pk/sk already exist
if (track.keys[index][prop]) {
error(`'${track.keys[index][prop]}' has already been declared as the ${prop} for the ${index} index`)
}
track.keys[index][prop] = field
if (track.keys[index].partitionKey === track.keys[index].sortKey) error(`'${field}' attribute cannot be both the partitionKey and sortKey for the ${index} index`)
} else {
error(`Index assignments for '${field}' must be string or boolean values`)
}
} // end for
} else {
error(`'${prop}' must be a boolean, string, or array`)
}
break
default:
error(`'${prop}' is not a valid property type`)
}
})
// Error on alias and map
if (config.alias && config.map)
error(`'${field}' cannot contain both an alias and a map`)
// Default the type
if (!config.type) config.type = 'string'
// Default coerce based on type
if (['string','boolean','number'].includes(config.type)
&& typeof config.coerce === 'undefined') config.coerce = true
// Set defaults
if (config.default !== undefined) track.defaults[field] = config.default
// Track required settings
if (config.required === true) track.required[config.map || field] = false
if (config.required === 'always') track.required[config.map || field] = true
// Destructure the config to pull out map and alias
const { map, alias, ..._config } = config
// Return the original config
return Object.assign(
{
[field]: config
},
alias ? {
[alias]: Object.assign({},_config, { map: field })
} : {},
map ? {
[map]: Object.assign({},_config, { alias: field })
} : {}
) // end assign
}