triangle-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dynamodb.js +287 -0
- package/index.js +5 -0
- package/package.json +11 -0
package/dynamodb.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import config from "../config.js"
|
|
2
|
+
import { DynamoDB } from "@aws-sdk/client-dynamodb"
|
|
3
|
+
|
|
4
|
+
var dynamodb = new DynamoDB({ region: config.region })
|
|
5
|
+
|
|
6
|
+
function convert_output(dynamoobject) {
|
|
7
|
+
if (dynamoobject.S !== undefined) {
|
|
8
|
+
return dynamoobject.S
|
|
9
|
+
} else if (dynamoobject.BOOL !== undefined) {
|
|
10
|
+
return dynamoobject.BOOL
|
|
11
|
+
} else if (dynamoobject.N !== undefined) {
|
|
12
|
+
return Number(dynamoobject.N)
|
|
13
|
+
} else if (dynamoobject.L !== undefined) {
|
|
14
|
+
return dynamoobject.L.map(a => convert_output(a))
|
|
15
|
+
} else if (dynamoobject.SS !== undefined) {
|
|
16
|
+
return new Set(dynamoobject.SS)
|
|
17
|
+
} else if (dynamoobject.M !== undefined) {
|
|
18
|
+
return Object.fromEntries(Object.entries(dynamoobject.M).map(([key, value]) => [key, convert_output(value)]))
|
|
19
|
+
}
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function convert_input(input) {
|
|
24
|
+
if (typeof input === typeof "") {
|
|
25
|
+
return { S : input }
|
|
26
|
+
} else if (typeof input === typeof true) {
|
|
27
|
+
return { BOOL : input }
|
|
28
|
+
} else if (typeof input === typeof 3.2) {
|
|
29
|
+
return { N : input.toString() }
|
|
30
|
+
} else if (Array.isArray(input)) {
|
|
31
|
+
return { L : input.map(a => convert_input(a)) }
|
|
32
|
+
} else if (input instanceof Set) {
|
|
33
|
+
return { SS : Array.from(input) }
|
|
34
|
+
} else if (typeof input === typeof {}) {
|
|
35
|
+
return {
|
|
36
|
+
M : Object.fromEntries(Object.entries(input)
|
|
37
|
+
.filter(([key, value]) => value !== undefined && value !== null && key !== "")
|
|
38
|
+
.map(([key, value]) => [key, convert_input(value)]))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function paginate(request, f) {
|
|
45
|
+
const items = []
|
|
46
|
+
let last_eval_key = undefined
|
|
47
|
+
while (true) {
|
|
48
|
+
const response = last_eval_key !== undefined ? (
|
|
49
|
+
await f({...request, ExclusiveStartKey : last_eval_key })
|
|
50
|
+
) : (
|
|
51
|
+
await f(request)
|
|
52
|
+
)
|
|
53
|
+
const new_items = response.Items.map(item => convert_output({ M : item }))
|
|
54
|
+
items.push(...new_items)
|
|
55
|
+
if (response.LastEvaluatedKey === undefined) {
|
|
56
|
+
return items
|
|
57
|
+
}
|
|
58
|
+
last_eval_key = response.LastEvaluatedKey
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function scan(table, attribute_names = [], filters = {}) {
|
|
63
|
+
const request = Object.fromEntries(Object.entries({
|
|
64
|
+
TableName : table,
|
|
65
|
+
ExpressionAttributeNames : Object.fromEntries((Object.keys(filters).concat(attribute_names)).map(attribute_name => ["#" + attribute_name, attribute_name])),
|
|
66
|
+
ExpressionAttributeValues : Object.fromEntries(Object.entries(filters).map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)])),
|
|
67
|
+
FilterExpression : Object.keys(filters).map(attribute_name => "#" + attribute_name + " = :" + attribute_name).join(","),
|
|
68
|
+
ProjectionExpression : attribute_names.map(attribute_name => "#" + attribute_name).join(", ")
|
|
69
|
+
}).filter(([field, value]) => value !== undefined && Object.keys(value).length !== 0))
|
|
70
|
+
return await paginate(request, (request) => dynamodb.scan(request))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function get(table, key, consistent=false) {
|
|
74
|
+
const item = await dynamodb.getItem({
|
|
75
|
+
ConsistentRead : consistent,
|
|
76
|
+
TableName : table,
|
|
77
|
+
Key : convert_input(key).M
|
|
78
|
+
})
|
|
79
|
+
.then(response => response.Item)
|
|
80
|
+
if (item === undefined) {
|
|
81
|
+
return undefined
|
|
82
|
+
}
|
|
83
|
+
return convert_output({ M : item })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function get_max(table, primary_key) {
|
|
87
|
+
if (Object.keys(primary_key).length !== 1) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
const request = {
|
|
91
|
+
TableName : table,
|
|
92
|
+
ExpressionAttributeNames: {
|
|
93
|
+
"#a": Object.keys(primary_key)[0]
|
|
94
|
+
},
|
|
95
|
+
ExpressionAttributeValues: {
|
|
96
|
+
":a": convert_input(Object.values(primary_key)[0])
|
|
97
|
+
},
|
|
98
|
+
KeyConditionExpression: "#a = :a",
|
|
99
|
+
Limit : 1,
|
|
100
|
+
ScanIndexForward : false
|
|
101
|
+
}
|
|
102
|
+
const items = await dynamodb.query(request)
|
|
103
|
+
.then(response => response.Items)
|
|
104
|
+
if (items[0] === undefined) {
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
return convert_output({ M : items[0] })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
export async function query(table, primary_key, reverse=false) {
|
|
113
|
+
if (Object.keys(primary_key).length !== 1) {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
const request = {
|
|
117
|
+
TableName : table,
|
|
118
|
+
ExpressionAttributeNames: {
|
|
119
|
+
"#a": Object.keys(primary_key)[0]
|
|
120
|
+
},
|
|
121
|
+
ExpressionAttributeValues: {
|
|
122
|
+
":a": convert_input(Object.values(primary_key)[0])
|
|
123
|
+
},
|
|
124
|
+
KeyConditionExpression: "#a = :a",
|
|
125
|
+
ScanIndexForward : !reverse
|
|
126
|
+
}
|
|
127
|
+
return await paginate(request, (request) => dynamodb.query(request))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function query_prefix(table, primary_key, secondary_key_prefix, reverse=false) {
|
|
131
|
+
if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
|
|
132
|
+
return undefined
|
|
133
|
+
}
|
|
134
|
+
const request = {
|
|
135
|
+
TableName : table,
|
|
136
|
+
ExpressionAttributeNames: {
|
|
137
|
+
"#a": Object.keys(primary_key)[0],
|
|
138
|
+
"#b": Object.keys(secondary_key_prefix)[0]
|
|
139
|
+
},
|
|
140
|
+
ExpressionAttributeValues: {
|
|
141
|
+
":a": convert_input(Object.values(primary_key)[0]),
|
|
142
|
+
":b": convert_input(Object.values(secondary_key_prefix)[0])
|
|
143
|
+
},
|
|
144
|
+
KeyConditionExpression: "#a = :a AND begins_with(#b, :b)",
|
|
145
|
+
ScanIndexForward : !reverse
|
|
146
|
+
}
|
|
147
|
+
return await paginate(request, (request) => dynamodb.query(request))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function query_range(table, primary_key, secondary_key_range, reverse=false) {
|
|
151
|
+
if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
|
|
152
|
+
return undefined
|
|
153
|
+
}
|
|
154
|
+
const request = {
|
|
155
|
+
TableName : table,
|
|
156
|
+
ExpressionAttributeNames: {
|
|
157
|
+
"#a": Object.keys(primary_key)[0],
|
|
158
|
+
"#b": Object.keys(secondary_key_range)[0]
|
|
159
|
+
},
|
|
160
|
+
ExpressionAttributeValues: {
|
|
161
|
+
":a": convert_input(Object.values(primary_key)[0]),
|
|
162
|
+
":b1": convert_input(Object.values(secondary_key_range)[0][0]),
|
|
163
|
+
":b2": convert_input(Object.values(secondary_key_range)[0][1])
|
|
164
|
+
},
|
|
165
|
+
KeyConditionExpression: "#a = :a AND (#b BETWEEN :b1 AND :b2)",
|
|
166
|
+
ScanIndexForward : !reverse
|
|
167
|
+
}
|
|
168
|
+
return await paginate(request, (request) => dynamodb.query(request))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function set(table, key, attributes) {
|
|
172
|
+
return await dynamodb.updateItem({
|
|
173
|
+
TableName : table,
|
|
174
|
+
Key : convert_input(key).M,
|
|
175
|
+
UpdateExpression: "set " + Object.keys(attributes)
|
|
176
|
+
.filter(attribute_name => !Object.keys(key).includes(attribute_name))
|
|
177
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
178
|
+
.map(attribute_name => "#" + attribute_name + " = :" + attribute_name
|
|
179
|
+
).join(", "),
|
|
180
|
+
ExpressionAttributeNames: Object.fromEntries(Object.keys(attributes)
|
|
181
|
+
.filter(attribute_name => !Object.keys(key).includes(attribute_name))
|
|
182
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
183
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
184
|
+
)),
|
|
185
|
+
ExpressionAttributeValues: Object.fromEntries(Object.entries(attributes)
|
|
186
|
+
.filter(([attribute_name, attribute_value]) => !Object.keys(key).includes(attribute_name))
|
|
187
|
+
.filter(([attribute_name, attribute_value]) => attribute_value !== undefined)
|
|
188
|
+
.map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)]
|
|
189
|
+
))
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function append(table, key, attributes) {
|
|
194
|
+
return await dynamodb.updateItem({
|
|
195
|
+
TableName : table,
|
|
196
|
+
Key : convert_input(key).M,
|
|
197
|
+
UpdateExpression: "set " + Object.keys(attributes)
|
|
198
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
199
|
+
.map(attribute_name => "#" + attribute_name + " = list_append(#" + attribute_name + ", :" + attribute_name + ")").join(", "),
|
|
200
|
+
ExpressionAttributeNames: Object.fromEntries(Object.keys(attributes)
|
|
201
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
202
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
203
|
+
)),
|
|
204
|
+
ExpressionAttributeValues: Object.fromEntries(Object.entries(attributes)
|
|
205
|
+
.filter(([attribute_name, attribute_value]) => attribute_value !== undefined)
|
|
206
|
+
.map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)]
|
|
207
|
+
))
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function add(table, key, attributes) {
|
|
212
|
+
const item = await get(table, key, true)
|
|
213
|
+
const new_attributes = {}
|
|
214
|
+
for (const [attribute, values] of Object.entries(attributes)) {
|
|
215
|
+
if (item[attribute] === undefined) {
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
const new_values = values.filter(value => !item[attribute].includes(value))
|
|
219
|
+
if (new_values.length > 0) {
|
|
220
|
+
new_attributes[attribute] = new_values
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (Object.values(new_attributes).flat().length === 0) {
|
|
224
|
+
return undefined
|
|
225
|
+
}
|
|
226
|
+
return await append(table, key, attributes)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export async function remove(table, key, attributes) {
|
|
230
|
+
return await dynamodb.updateItem({
|
|
231
|
+
TableName : table,
|
|
232
|
+
Key : convert_input(key).M,
|
|
233
|
+
UpdateExpression: "remove " + attributes
|
|
234
|
+
.map(attribute_name => "#" + attribute_name).join(", "),
|
|
235
|
+
ExpressionAttributeNames: Object.fromEntries(attributes
|
|
236
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
237
|
+
))
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export async function create(table, key, attributes = {}) {
|
|
242
|
+
const item = await get(table, key)
|
|
243
|
+
if (item !== undefined) {
|
|
244
|
+
return undefined
|
|
245
|
+
}
|
|
246
|
+
return await dynamodb.putItem({
|
|
247
|
+
TableName : table,
|
|
248
|
+
Item : { ...convert_input(key).M, ...convert_input(attributes).M }
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export async function del(table, key) {
|
|
253
|
+
return await dynamodb.deleteItem({
|
|
254
|
+
TableName: table,
|
|
255
|
+
Key: Object.fromEntries(Object.keys(key).map(key_name => [key_name, convert_input(key[key_name])]))
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export async function duplicate_attribute(table, attribute_name, new_attribute_name) {
|
|
260
|
+
const table_key_names = await dynamodb.describeTable({
|
|
261
|
+
TableName : table
|
|
262
|
+
}).then(metadata => metadata.Table.KeySchema.map(key => key.AttributeName))
|
|
263
|
+
const items = await scan(table, table_key_names.concat([attribute_name, new_attribute_name]))
|
|
264
|
+
if (items.filter(item => item[new_attribute_name] !== undefined).length > 0) {
|
|
265
|
+
console.log("Cannot rename.", new_attribute_name, "is an existing item.")
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
for (const item of items) {
|
|
269
|
+
if (item[attribute_name] === undefined) {
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
|
|
273
|
+
await set(table, key, { [new_attribute_name] : item[attribute_name] })
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export async function remove_attribute(table, attribute_name) {
|
|
278
|
+
const table_key_names = await dynamodb.describeTable({
|
|
279
|
+
TableName : table
|
|
280
|
+
}).then(metadata => metadata.Table.KeySchema.map(key => key.AttributeName))
|
|
281
|
+
const items = await scan(table)
|
|
282
|
+
.then(items => items.filter(item => item[attribute_name] !== undefined))
|
|
283
|
+
for (const item of items) {
|
|
284
|
+
const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
|
|
285
|
+
await remove(table, key, [attribute_name])
|
|
286
|
+
}
|
|
287
|
+
}
|
package/index.js
ADDED