-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
112 lines (98 loc) · 3.73 KB
/
index.js
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
const { is, capitalize } = require("@architect/inventory/src/lib")
const unique = objArray => [...new Set(objArray.map(i => Object.keys(i)[0]))]
module.exports = {
deploy: {
start: async ({ arc, cloudformation }) => {
let cfn = cloudformation
const localIndexes = arc["tables-local-indexes"]
if (!Array.isArray(localIndexes) || !localIndexes.length) return cloudformation
if (!Array.isArray(arc.tables) || !unique(localIndexes).every(i => unique(arc.tables).includes(i))) {
throw ReferenceError(`Specifying @tables-local-indexes requires specifying corresponding @tables`)
}
// Loop thru manifest tables
arc.tables.forEach(table => {
const name = Object.keys(table).pop()
const tableName = name
?.split(/[-._]/)
.map(p => capitalize(p))
.join("")
.concat("Table")
const tableProperties = cfn.Resources[tableName].Properties
// Reduce Local Indexes from manifest into CloudFormation DynamoDB properties
cfn.Resources[tableName].Properties = localIndexes
.filter(localIndex => Object.keys(localIndex).pop() === name)
.reduce((lsiProps, localIndex, i) => {
const pk = tableProperties.KeySchema.filter(k => k.KeyType === "HASH")[0].AttributeName
const lsiTemplate = {
IndexName: undefined,
KeySchema: [
{
AttributeName: pk,
KeyType: "HASH",
},
{
AttributeName: undefined,
KeyType: "RANGE",
},
],
Projection: {
ProjectionType: "ALL",
},
}
// Check if table has a Local Index
const hasLocalIndex = unique(localIndexes).includes(Object.keys(table)[0])
// Reduce Local Indexes' attributes into CloudFormation LSI properties
const attribs = Object.entries(localIndex[name])
return !hasLocalIndex
? lsiProps
: attribs.reduce(
(cfnProps, attr, seq) => {
let cfnLSI = seq === 0 ? lsiTemplate : cfnProps.LocalSecondaryIndexes[i]
switch (attr[0]) {
case "name":
cfnLSI.IndexName = attr[1]
break
case "projection":
if (attr[1] === "keys") {
cfnLSI.Projection.ProjectionType = "KEYS_ONLY"
} else if (attr[1] === "all") {
cfnLSI.Projection.ProjectionType = "ALL"
} else {
cfnLSI.Projection.ProjectionType = "INCLUDE"
cfnLSI.Projection.NonKeyAttributes = Array.isArray(attr[1]) ? attr[1] : attr[1].split(" ")
}
break
default:
if (is.sortKey(attr[1])) {
cfnLSI.KeySchema.map(key => (key.KeyType === "RANGE" ? (key.AttributeName = attr[0]) : null))
const sortKeyType = attr[1] ? attr[1].replace("**", "").slice(0, 1).toUpperCase() : "S"
cfnProps.AttributeDefinitions.push({
AttributeName: attr[0],
AttributeType: sortKeyType,
})
} else if (is.primaryKey(attr[1]) && attr[1] !== pk) {
throw ReferenceError(
`The partition key of a Local Secondary Index must be the same as the base table's (${pk}). It cannot be ${attr[0]}.`
)
}
}
// Build IndexName if one is not provided
const sk = cfnLSI.KeySchema.filter(k => k.KeyType === "RANGE")[0].AttributeName
cfnLSI.IndexName ??= `${pk}-${sk}-index`
// Update Cloudformation properties
if (seq === 0) {
cfnProps.LocalSecondaryIndexes ??= []
cfnProps.LocalSecondaryIndexes.push(cfnLSI)
} else {
cfnProps.LocalSecondaryIndexes[i] = cfnLSI
}
return cfnProps
},
{ ...lsiProps }
)
}, tableProperties)
})
return cfn
},
},
}