triangle-utils 1.2.12 → 1.3.1
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/dist/src/Utils.js +24 -0
- package/dist/src/Utils_Bedrock.js +89 -0
- package/dist/src/Utils_Bee.js +101 -0
- package/dist/src/Utils_DynamoDB.js +381 -0
- package/dist/src/Utils_Misc.js +627 -0
- package/dist/src/Utils_S3.js +96 -0
- package/dist/src/Utils_S3Vectors.js +131 -0
- package/dist/src/Utils_Youtube.js +35 -0
- package/{utils → dist/src}/election_ids.js +472 -474
- package/{states.js → dist/src/states.js} +253 -256
- package/dist/src/types/Config.js +1 -0
- package/dist/types/Config.js +1 -0
- package/eslint.config.ts +13 -0
- package/jest.config.js +19 -0
- package/package.json +23 -3
- package/src/Utils.ts +28 -0
- package/{utils/Utils_Bedrock.js → src/Utils_Bedrock.ts} +23 -12
- package/{utils/Utils_Bee.js → src/Utils_Bee.ts} +39 -19
- package/src/Utils_DynamoDB.ts +448 -0
- package/src/Utils_Misc.ts +655 -0
- package/{utils/Utils_S3.js → src/Utils_S3.ts} +34 -14
- package/{utils/Utils_S3Vectors.js → src/Utils_S3Vectors.ts} +43 -29
- package/{utils/Utils_Youtube.js → src/Utils_Youtube.ts} +13 -6
- package/src/types/Config.ts +9 -0
- package/test/Utils.test.ts +85 -0
- package/tsconfig.json +31 -0
- package/Utils.js +0 -33
- package/test.js +0 -62
- package/utils/Utils_CitizenPortal.js +0 -26
- package/utils/Utils_DynamoDB.js +0 -316
- package/utils/Utils_Misc.js +0 -161
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { AttributeValue, DynamoDB, QueryCommandInput, QueryCommandOutput, ScanCommandInput, ScanCommandOutput, UpdateItemCommandInput } from "@aws-sdk/client-dynamodb"
|
|
2
|
+
import { Config } from "./types/Config"
|
|
3
|
+
|
|
4
|
+
function convert_output(dynamoobject : AttributeValue) : string | boolean | number | any[] | Set<any> | Record<string, any> | undefined {
|
|
5
|
+
if (dynamoobject.S !== undefined) {
|
|
6
|
+
return dynamoobject.S
|
|
7
|
+
} else if (dynamoobject.N !== undefined) {
|
|
8
|
+
return Number(dynamoobject.N)
|
|
9
|
+
} else if (dynamoobject.L !== undefined) {
|
|
10
|
+
return dynamoobject.L.map((a : AttributeValue) => convert_output(a))
|
|
11
|
+
} else if (dynamoobject.SS !== undefined) {
|
|
12
|
+
return new Set(dynamoobject.SS)
|
|
13
|
+
} else if (dynamoobject.M !== undefined) {
|
|
14
|
+
return Object.fromEntries(Object.entries(dynamoobject.M).map(([key, value]) => [key, convert_output(value)]))
|
|
15
|
+
} else if (dynamoobject.BOOL !== undefined) {
|
|
16
|
+
return dynamoobject.BOOL
|
|
17
|
+
}
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function not_undefined<T>(x : T | undefined): x is T {
|
|
22
|
+
return x !== undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function is_item(x : any): x is Record<string, any> {
|
|
26
|
+
return typeof x === "object" && Object.keys(x).filter(key => typeof key !== "string").length === 0 && !Array.isArray(x)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function convert_input(input : string | boolean | number | any[] | Set<any> | Record<string, any>) : AttributeValue | undefined {
|
|
30
|
+
if (typeof input === "string") {
|
|
31
|
+
return { S : input }
|
|
32
|
+
} else if (typeof input === "boolean") {
|
|
33
|
+
return { BOOL : input }
|
|
34
|
+
} else if (typeof input === "number") {
|
|
35
|
+
return { N : input.toString() }
|
|
36
|
+
} else if (Array.isArray(input)) {
|
|
37
|
+
const converted_list : AttributeValue[] = input.map((a : any) => convert_input(a))
|
|
38
|
+
.filter(converted_input => converted_input !== undefined)
|
|
39
|
+
if (converted_list.length !== input.length) {
|
|
40
|
+
return undefined
|
|
41
|
+
}
|
|
42
|
+
return { L : converted_list }
|
|
43
|
+
} else if (input instanceof Set) {
|
|
44
|
+
const converted_list : string[] = Array.from(input)
|
|
45
|
+
.filter(converted_input => typeof converted_input === "string")
|
|
46
|
+
if (converted_list.length !== input.size) {
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
49
|
+
return { SS : converted_list }
|
|
50
|
+
} else {
|
|
51
|
+
const converted_inputs : Record<string, AttributeValue> = Object.fromEntries(Object.entries(input)
|
|
52
|
+
.filter(([key, value]) => value !== undefined && value !== null && key !== "")
|
|
53
|
+
.map(([key, value]) => [key, convert_input(value)])
|
|
54
|
+
.filter(([key, value]) => not_undefined(value))
|
|
55
|
+
)
|
|
56
|
+
return {
|
|
57
|
+
M : converted_inputs
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function compile_pages(
|
|
63
|
+
request : ScanCommandInput|QueryCommandInput,
|
|
64
|
+
f : (request : ScanCommandInput|QueryCommandInput) => Promise<ScanCommandOutput|QueryCommandOutput>,
|
|
65
|
+
compile : boolean = true
|
|
66
|
+
) : Promise<any[]> {
|
|
67
|
+
const items = []
|
|
68
|
+
let last_eval_key : Record<string, AttributeValue> | undefined = undefined
|
|
69
|
+
while (true) {
|
|
70
|
+
const request_page : ScanCommandInput|QueryCommandInput = {
|
|
71
|
+
...request,
|
|
72
|
+
ExclusiveStartKey : last_eval_key
|
|
73
|
+
}
|
|
74
|
+
const response = await f(request_page)
|
|
75
|
+
if (response.Items === undefined) {
|
|
76
|
+
return []
|
|
77
|
+
}
|
|
78
|
+
const new_items = response.Items.map(item => convert_output({ M : item }))
|
|
79
|
+
items.push(...new_items)
|
|
80
|
+
if (response.LastEvaluatedKey === undefined || !compile) {
|
|
81
|
+
return items
|
|
82
|
+
}
|
|
83
|
+
last_eval_key = response.LastEvaluatedKey
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export class Utils_DynamoDB {
|
|
88
|
+
|
|
89
|
+
readonly dynamodb : DynamoDB
|
|
90
|
+
|
|
91
|
+
constructor(config : Config) {
|
|
92
|
+
this.dynamodb = new DynamoDB({ region : config.region })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async scan(
|
|
96
|
+
table : string,
|
|
97
|
+
options : {
|
|
98
|
+
filters? : Record<string, any>,
|
|
99
|
+
undefined_attribute_names? : string[],
|
|
100
|
+
defined_attribute_names? : string[],
|
|
101
|
+
attribute_names? : string[],
|
|
102
|
+
concurrency? : number
|
|
103
|
+
} = {}
|
|
104
|
+
) : Promise<Record<string, any>[]> {
|
|
105
|
+
const filters = options.filters !== undefined ? options.filters : {}
|
|
106
|
+
const undefined_attribute_names : string[] = options.undefined_attribute_names !== undefined ? options.undefined_attribute_names : []
|
|
107
|
+
const defined_attribute_names : string[] = options.defined_attribute_names !== undefined ? options.defined_attribute_names : []
|
|
108
|
+
const concurrency : number = options.concurrency !== undefined ? options.concurrency : 1
|
|
109
|
+
const attribute_names : string[] = options.attribute_names !== undefined ? options.attribute_names : []
|
|
110
|
+
const iterators = []
|
|
111
|
+
for (let i = 0; i < concurrency; i++) {
|
|
112
|
+
const expression_attribute_names = Object.fromEntries(
|
|
113
|
+
[...Object.keys(filters), ...attribute_names, ...undefined_attribute_names, ...defined_attribute_names]
|
|
114
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name])
|
|
115
|
+
)
|
|
116
|
+
const expression_attribute_values = Object.fromEntries(
|
|
117
|
+
Object.entries(filters)
|
|
118
|
+
.map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)])
|
|
119
|
+
.filter(([attribute_name, attribute_value]) => not_undefined(attribute_value))
|
|
120
|
+
)
|
|
121
|
+
const filter_expression = [
|
|
122
|
+
...Object.keys(filters).map(attribute_name => "#" + attribute_name + " = :" + attribute_name),
|
|
123
|
+
...undefined_attribute_names.map(attribute_name => "attribute_not_exists(#" + attribute_name + ")"),
|
|
124
|
+
...defined_attribute_names.map(attribute_name => "attribute_exists(#" + attribute_name + ")")
|
|
125
|
+
].join(" AND ")
|
|
126
|
+
const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ")
|
|
127
|
+
const request : ScanCommandInput = {
|
|
128
|
+
TableName : table,
|
|
129
|
+
ExpressionAttributeNames : Object.keys(expression_attribute_names).length > 0 ? expression_attribute_names : undefined,
|
|
130
|
+
ExpressionAttributeValues : Object.keys(expression_attribute_values).length > 0 ? expression_attribute_values : undefined,
|
|
131
|
+
FilterExpression : filter_expression.length > 0 ? filter_expression : undefined,
|
|
132
|
+
ProjectionExpression : projection_expression.length > 0 ? projection_expression : undefined,
|
|
133
|
+
Segment : i,
|
|
134
|
+
TotalSegments : concurrency
|
|
135
|
+
}
|
|
136
|
+
iterators.push(compile_pages(request, (request : ScanCommandInput) => this.dynamodb.scan(request)))
|
|
137
|
+
}
|
|
138
|
+
const segments = await Promise.all(iterators)
|
|
139
|
+
const items = segments.flat().filter(is_item)
|
|
140
|
+
return items
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async get(
|
|
144
|
+
table : string,
|
|
145
|
+
key : Record<string, any>,
|
|
146
|
+
consistent : boolean = false
|
|
147
|
+
) : Promise<Record<string, any> | undefined> {
|
|
148
|
+
const converted_key = convert_input(key)?.M
|
|
149
|
+
if (converted_key === undefined) {
|
|
150
|
+
return undefined
|
|
151
|
+
}
|
|
152
|
+
const item = await this.dynamodb.getItem({
|
|
153
|
+
ConsistentRead : consistent,
|
|
154
|
+
TableName : table,
|
|
155
|
+
Key : converted_key
|
|
156
|
+
})
|
|
157
|
+
.then(response => response.Item)
|
|
158
|
+
if (item === undefined) {
|
|
159
|
+
return undefined
|
|
160
|
+
}
|
|
161
|
+
const converted_output = convert_output({ M : item })
|
|
162
|
+
if (!is_item(converted_output)) {
|
|
163
|
+
return undefined
|
|
164
|
+
}
|
|
165
|
+
return converted_output
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async get_max(table : string, primary_key : Record<string, any>) : Promise<Record<string, any> | undefined> {
|
|
169
|
+
if (Object.keys(primary_key).length !== 1) {
|
|
170
|
+
return undefined
|
|
171
|
+
}
|
|
172
|
+
const key = Object.keys(primary_key)[0]
|
|
173
|
+
const value = convert_input(Object.values(primary_key)[0])
|
|
174
|
+
if (value === undefined) {
|
|
175
|
+
return undefined
|
|
176
|
+
}
|
|
177
|
+
const request : QueryCommandInput = {
|
|
178
|
+
TableName : table,
|
|
179
|
+
ExpressionAttributeNames: {
|
|
180
|
+
"#a": key
|
|
181
|
+
},
|
|
182
|
+
ExpressionAttributeValues: {
|
|
183
|
+
":a": value
|
|
184
|
+
},
|
|
185
|
+
KeyConditionExpression: "#a = :a",
|
|
186
|
+
Limit : 1,
|
|
187
|
+
ScanIndexForward : false
|
|
188
|
+
}
|
|
189
|
+
const items = await this.dynamodb.query(request)
|
|
190
|
+
.then(response => response.Items)
|
|
191
|
+
if (items === undefined || items[0] === undefined) {
|
|
192
|
+
return undefined
|
|
193
|
+
}
|
|
194
|
+
const converted_output = convert_output({ M : items[0] })
|
|
195
|
+
if (!is_item(converted_output)) {
|
|
196
|
+
return undefined
|
|
197
|
+
}
|
|
198
|
+
return converted_output
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async query(
|
|
202
|
+
table : string,
|
|
203
|
+
primary_key : Record<string, any>,
|
|
204
|
+
options : {
|
|
205
|
+
reverse? : boolean,
|
|
206
|
+
compile? : boolean
|
|
207
|
+
} = {}
|
|
208
|
+
) : Promise<Record<string, any>[] | undefined>{
|
|
209
|
+
const reverse = options.reverse !== undefined ? options.reverse : false
|
|
210
|
+
const compile = options.compile !== undefined ? options.compile : true
|
|
211
|
+
if (Object.keys(primary_key).length !== 1) {
|
|
212
|
+
return undefined
|
|
213
|
+
}
|
|
214
|
+
const key = Object.keys(primary_key)[0]
|
|
215
|
+
const value = convert_input(Object.values(primary_key)[0])
|
|
216
|
+
if (value === undefined) {
|
|
217
|
+
return undefined
|
|
218
|
+
}
|
|
219
|
+
const request : QueryCommandInput = {
|
|
220
|
+
TableName : table,
|
|
221
|
+
ExpressionAttributeNames: {
|
|
222
|
+
"#a": key
|
|
223
|
+
},
|
|
224
|
+
ExpressionAttributeValues: {
|
|
225
|
+
":a": value
|
|
226
|
+
},
|
|
227
|
+
KeyConditionExpression: "#a = :a",
|
|
228
|
+
ScanIndexForward : !reverse
|
|
229
|
+
}
|
|
230
|
+
return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async query_prefix(
|
|
234
|
+
table : string,
|
|
235
|
+
primary_key : Record<string, any>,
|
|
236
|
+
secondary_key_prefix : Record<string, string>,
|
|
237
|
+
options : {
|
|
238
|
+
reverse? : boolean,
|
|
239
|
+
compile? : boolean
|
|
240
|
+
} = {}
|
|
241
|
+
) : Promise<Record<string, any>[] | undefined>{
|
|
242
|
+
const reverse = options.reverse !== undefined ? options.reverse : false
|
|
243
|
+
const compile = options.compile !== undefined ? options.compile : true
|
|
244
|
+
if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
|
|
245
|
+
return undefined
|
|
246
|
+
}
|
|
247
|
+
const converted_primary_value = convert_input(Object.values(primary_key)[0])
|
|
248
|
+
const converted_secondary_prefix_value = convert_input(Object.values(secondary_key_prefix)[0])
|
|
249
|
+
if (converted_primary_value === undefined || converted_secondary_prefix_value === undefined) {
|
|
250
|
+
return undefined
|
|
251
|
+
}
|
|
252
|
+
const request : QueryCommandInput = {
|
|
253
|
+
TableName : table,
|
|
254
|
+
ExpressionAttributeNames: {
|
|
255
|
+
"#a": Object.keys(primary_key)[0],
|
|
256
|
+
"#b": Object.keys(secondary_key_prefix)[0]
|
|
257
|
+
},
|
|
258
|
+
ExpressionAttributeValues: {
|
|
259
|
+
":a": converted_primary_value,
|
|
260
|
+
":b": converted_secondary_prefix_value
|
|
261
|
+
},
|
|
262
|
+
KeyConditionExpression: "#a = :a AND begins_with(#b, :b)",
|
|
263
|
+
ScanIndexForward : !reverse
|
|
264
|
+
}
|
|
265
|
+
return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async query_range(
|
|
269
|
+
table : string,
|
|
270
|
+
primary_key : Record<string, any>,
|
|
271
|
+
secondary_key_range : Record<string, string|number[]>,
|
|
272
|
+
options : {
|
|
273
|
+
reverse? : boolean,
|
|
274
|
+
compile? : boolean
|
|
275
|
+
} = {}
|
|
276
|
+
) : Promise<Record<string, any>[] | undefined>{
|
|
277
|
+
const reverse = options.reverse !== undefined ? options.reverse : false
|
|
278
|
+
const compile = options.compile !== undefined ? options.compile : true
|
|
279
|
+
if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
|
|
280
|
+
return undefined
|
|
281
|
+
}
|
|
282
|
+
const converted_primary_value = convert_input(Object.values(primary_key)[0])
|
|
283
|
+
const converted_secondary_range_start_value = convert_input(Object.values(secondary_key_range)[0][0])
|
|
284
|
+
const converted_secondary_range_end_value = convert_input(Object.values(secondary_key_range)[0][1])
|
|
285
|
+
if (converted_primary_value === undefined || converted_secondary_range_start_value === undefined || converted_secondary_range_end_value === undefined) {
|
|
286
|
+
return undefined
|
|
287
|
+
}
|
|
288
|
+
const request : QueryCommandInput = {
|
|
289
|
+
TableName : table,
|
|
290
|
+
ExpressionAttributeNames: {
|
|
291
|
+
"#a": Object.keys(primary_key)[0],
|
|
292
|
+
"#b": Object.keys(secondary_key_range)[0]
|
|
293
|
+
},
|
|
294
|
+
ExpressionAttributeValues: {
|
|
295
|
+
":a": converted_primary_value,
|
|
296
|
+
":b1": converted_secondary_range_start_value,
|
|
297
|
+
":b2": converted_secondary_range_end_value
|
|
298
|
+
},
|
|
299
|
+
KeyConditionExpression: "#a = :a AND (#b BETWEEN :b1 AND :b2)",
|
|
300
|
+
ScanIndexForward : !reverse
|
|
301
|
+
}
|
|
302
|
+
return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async set(table : string, key : Record<string, any>, attributes : Record<string, any>) : Promise<void> {
|
|
306
|
+
const converted_key = convert_input(key)?.M
|
|
307
|
+
|
|
308
|
+
const request : UpdateItemCommandInput = {
|
|
309
|
+
TableName : table,
|
|
310
|
+
Key : converted_key,
|
|
311
|
+
UpdateExpression: "set " + Object.keys(attributes)
|
|
312
|
+
.filter(attribute_name => !Object.keys(key).includes(attribute_name))
|
|
313
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
314
|
+
.map(attribute_name => "#" + attribute_name + " = :" + attribute_name
|
|
315
|
+
).join(", "),
|
|
316
|
+
ExpressionAttributeNames: Object.fromEntries(Object.keys(attributes)
|
|
317
|
+
.filter(attribute_name => !Object.keys(key).includes(attribute_name))
|
|
318
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
319
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
320
|
+
)),
|
|
321
|
+
ExpressionAttributeValues: Object.fromEntries(Object.entries(attributes)
|
|
322
|
+
.filter(([attribute_name, attribute_value]) => !Object.keys(key).includes(attribute_name) && not_undefined(attribute_value))
|
|
323
|
+
.map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)])
|
|
324
|
+
.filter(([attribute_name, attribute_value]) => not_undefined(attribute_value))
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
await this.dynamodb.updateItem(request)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async append(table : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void>{
|
|
331
|
+
const converted_key = convert_input(key)?.M
|
|
332
|
+
|
|
333
|
+
const request : UpdateItemCommandInput = {
|
|
334
|
+
TableName : table,
|
|
335
|
+
Key : converted_key,
|
|
336
|
+
UpdateExpression: "set " + Object.keys(attributes)
|
|
337
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
338
|
+
.map(attribute_name => "#" + attribute_name + " = list_append(#" + attribute_name + ", :" + attribute_name + ")").join(", "),
|
|
339
|
+
ExpressionAttributeNames: Object.fromEntries(Object.keys(attributes)
|
|
340
|
+
.filter(attribute_name => attributes[attribute_name] !== undefined)
|
|
341
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
342
|
+
)),
|
|
343
|
+
ExpressionAttributeValues: Object.fromEntries(Object.entries(attributes)
|
|
344
|
+
.filter(([attribute_name, attribute_value]) => not_undefined(attribute_value))
|
|
345
|
+
.map(([attribute_name, attribute_value]) => [":" + attribute_name, convert_input(attribute_value)])
|
|
346
|
+
.filter(([attribute_name, attribute_value]) => not_undefined(attribute_value))
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
this.dynamodb.updateItem(request)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async add(table : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void> {
|
|
353
|
+
const item = await this.get(table, key, true)
|
|
354
|
+
if (item === undefined) {
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
const new_attributes : Record<string, any[]> = {}
|
|
358
|
+
for (const [attribute, values] of Object.entries(attributes)) {
|
|
359
|
+
if (item[attribute] === undefined) {
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
const new_values = values.filter(value => !item[attribute].includes(value))
|
|
363
|
+
if (new_values.length > 0) {
|
|
364
|
+
new_attributes[attribute] = new_values
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (Object.values(new_attributes).flat().length === 0) {
|
|
368
|
+
return undefined
|
|
369
|
+
}
|
|
370
|
+
return await this.append(table, key, attributes)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async remove(table : string, key : Record<string, any>, attributes : string[]) : Promise<void> {
|
|
374
|
+
const converted_key = convert_input(key)?.M
|
|
375
|
+
|
|
376
|
+
const request : UpdateItemCommandInput = {
|
|
377
|
+
TableName : table,
|
|
378
|
+
Key : converted_key,
|
|
379
|
+
UpdateExpression: "remove " + attributes
|
|
380
|
+
.map(attribute_name => "#" + attribute_name).join(", "),
|
|
381
|
+
ExpressionAttributeNames: Object.fromEntries(attributes
|
|
382
|
+
.map(attribute_name => ["#" + attribute_name, attribute_name]
|
|
383
|
+
))
|
|
384
|
+
}
|
|
385
|
+
await this.dynamodb.updateItem(request)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async create(table : string, key : Record<string, any>, attributes : Record<string, any[]> = {}) : Promise<void> {
|
|
389
|
+
const item = await this.get(table, key, true)
|
|
390
|
+
if (item !== undefined) {
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
const converted_key = convert_input(key)?.M
|
|
394
|
+
const converted_attributes = convert_input(attributes)?.M
|
|
395
|
+
await this.dynamodb.putItem({
|
|
396
|
+
TableName : table,
|
|
397
|
+
Item : { ...converted_key, ...converted_attributes }
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async delete(table : string, key : Record<string, any>) : Promise<void> {
|
|
402
|
+
const converted_key = convert_input(key)?.M
|
|
403
|
+
await this.dynamodb.deleteItem({
|
|
404
|
+
TableName: table,
|
|
405
|
+
Key: converted_key
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async duplicate_attribute(table : string, attribute_name : string, new_attribute_name : string) : Promise<void>{
|
|
410
|
+
const table_metadata = await this.dynamodb.describeTable({
|
|
411
|
+
TableName : table
|
|
412
|
+
})
|
|
413
|
+
if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
|
|
414
|
+
return
|
|
415
|
+
}
|
|
416
|
+
const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
|
|
417
|
+
.filter(table_key_name => table_key_name !== undefined)
|
|
418
|
+
const items = await this.scan(table, { attribute_names : table_key_names.concat([attribute_name, new_attribute_name]) })
|
|
419
|
+
if (items.filter(item => item[new_attribute_name] !== undefined).length > 0) {
|
|
420
|
+
console.log("Cannot rename.", new_attribute_name, "is an existing item.")
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
for (const item of items) {
|
|
424
|
+
if (item[attribute_name] === undefined) {
|
|
425
|
+
continue
|
|
426
|
+
}
|
|
427
|
+
const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
|
|
428
|
+
await this.set(table, key, { [new_attribute_name] : item[attribute_name] })
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async remove_attribute(table : string, attribute_name : string) {
|
|
433
|
+
const table_metadata = await this.dynamodb.describeTable({
|
|
434
|
+
TableName : table
|
|
435
|
+
})
|
|
436
|
+
if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
|
|
440
|
+
.filter(table_key_name => table_key_name !== undefined)
|
|
441
|
+
const items = await this.scan(table)
|
|
442
|
+
.then(items => items.filter(item => item[attribute_name] !== undefined))
|
|
443
|
+
for (const item of items) {
|
|
444
|
+
const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
|
|
445
|
+
await this.remove(table, key, [attribute_name])
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|