@baadal-sdk/dapi 0.31.5 → 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/README.md +62 -2
- package/dist/index.d.ts +674 -0
- package/dist/index.js +1118 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -106
- package/LICENSE.txt +0 -21
- package/dist/cjs/index.js +0 -3
- package/dist/cjs/index.js.LICENSE.txt +0 -1
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/package.json +0 -3
- package/dist/esm/index.js +0 -3
- package/dist/esm/index.js.LICENSE.txt +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/package.json +0 -3
- package/dist/types/aws/client.d.ts +0 -13
- package/dist/types/aws/client.d.ts.map +0 -1
- package/dist/types/aws/db.d.ts +0 -291
- package/dist/types/aws/db.d.ts.map +0 -1
- package/dist/types/aws/index.d.ts +0 -12
- package/dist/types/aws/index.d.ts.map +0 -1
- package/dist/types/aws/s3.d.ts +0 -90
- package/dist/types/aws/s3.d.ts.map +0 -1
- package/dist/types/common/common.model.d.ts +0 -4
- package/dist/types/common/common.model.d.ts.map +0 -1
- package/dist/types/common/const.d.ts +0 -4
- package/dist/types/common/const.d.ts.map +0 -1
- package/dist/types/common/error.d.ts +0 -4
- package/dist/types/common/error.d.ts.map +0 -1
- package/dist/types/common/logger.d.ts +0 -29
- package/dist/types/common/logger.d.ts.map +0 -1
- package/dist/types/fs/index.d.ts +0 -102
- package/dist/types/fs/index.d.ts.map +0 -1
- package/dist/types/gh/index.d.ts +0 -22
- package/dist/types/gh/index.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts +0 -6
- package/dist/types/utils/index.d.ts.map +0 -1
- package/src/aws/client.ts +0 -18
- package/src/aws/db.ts +0 -764
- package/src/aws/index.ts +0 -33
- package/src/aws/s3.ts +0 -476
- package/src/common/common.model.ts +0 -3
- package/src/common/const.ts +0 -3
- package/src/common/error.ts +0 -12
- package/src/common/logger.ts +0 -18
- package/src/fs/index.ts +0 -316
- package/src/gh/index.ts +0 -60
- package/src/index.ts +0 -8
- package/src/typings/index.d.ts +0 -0
- package/src/utils/index.ts +0 -39
package/src/aws/db.ts
DELETED
|
@@ -1,764 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Examples:
|
|
3
|
-
* Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-examples.html
|
|
4
|
-
*
|
|
5
|
-
* Partition key vs Composite primary key:
|
|
6
|
-
* Ref: https://aws.amazon.com/premiumsupport/knowledge-center/primary-key-dynamodb-table/
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
10
|
-
import {
|
|
11
|
-
DynamoDBDocumentClient,
|
|
12
|
-
GetCommand,
|
|
13
|
-
GetCommandInput,
|
|
14
|
-
BatchGetCommand,
|
|
15
|
-
BatchGetCommandInput,
|
|
16
|
-
PutCommand,
|
|
17
|
-
PutCommandInput,
|
|
18
|
-
BatchWriteCommand,
|
|
19
|
-
BatchWriteCommandInput,
|
|
20
|
-
UpdateCommand,
|
|
21
|
-
UpdateCommandInput,
|
|
22
|
-
QueryCommand,
|
|
23
|
-
QueryCommandInput,
|
|
24
|
-
ScanCommand,
|
|
25
|
-
ScanCommandInput,
|
|
26
|
-
DeleteCommand,
|
|
27
|
-
DeleteCommandInput,
|
|
28
|
-
} from '@aws-sdk/lib-dynamodb';
|
|
29
|
-
import short from 'short-uuid';
|
|
30
|
-
import { chunkifyArray } from '@baadal-sdk/utils';
|
|
31
|
-
|
|
32
|
-
import { dbClient } from './client';
|
|
33
|
-
import { StringIndexable } from '../common/common.model';
|
|
34
|
-
import { CustomError } from '../common/error';
|
|
35
|
-
import { warn, error } from '../common/logger';
|
|
36
|
-
import { BATCH_SIZE, CHUNK_SIZE, MAX_RETRY_ATTEMPTS } from '../common/const';
|
|
37
|
-
|
|
38
|
-
const DynamoDBError = (msg: string) => new CustomError(msg, { name: 'DynamoDBError' });
|
|
39
|
-
|
|
40
|
-
/** @internal */
|
|
41
|
-
export const init = (region: string) => {
|
|
42
|
-
// Ref: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_lib_dynamodb.html#configuration
|
|
43
|
-
if (!dbClient.client) {
|
|
44
|
-
const dydbClient = new DynamoDBClient({ region }); // may also pass `credentials`
|
|
45
|
-
dbClient.client = DynamoDBDocumentClient.from(dydbClient);
|
|
46
|
-
dbClient.id = short.uuid();
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** @internal */
|
|
53
|
-
export const status = () => dbClient.id;
|
|
54
|
-
|
|
55
|
-
const tryInit = (silent = false) => {
|
|
56
|
-
if (dbClient.client) return;
|
|
57
|
-
const region = process.env.AWS_REGION || '';
|
|
58
|
-
if (region) {
|
|
59
|
-
if (init(region)) {
|
|
60
|
-
// console.log('Auto-initialization of DynamoDB successful');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (!silent) {
|
|
65
|
-
// throw DynamoDBError('DynamoDB is possibly uninitialized!');
|
|
66
|
-
throw DynamoDBError('Could not auto-initialize DynamoDB!');
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// auto-initialize on load
|
|
71
|
-
tryInit(true);
|
|
72
|
-
|
|
73
|
-
const writeItemForceHelper = async <T = any>(
|
|
74
|
-
table: string,
|
|
75
|
-
item: T,
|
|
76
|
-
key: string,
|
|
77
|
-
i: number,
|
|
78
|
-
imax?: number
|
|
79
|
-
): Promise<T | null> => {
|
|
80
|
-
if (!dbClient.client) tryInit();
|
|
81
|
-
if (!dbClient.client) return null;
|
|
82
|
-
if (!table || !item) return null;
|
|
83
|
-
|
|
84
|
-
if (!(item as any)[key]) {
|
|
85
|
-
(item as any)[key] = short.uuid();
|
|
86
|
-
}
|
|
87
|
-
const cmdParams: PutCommandInput = { TableName: table, Item: item, ConditionExpression: `attribute_not_exists(${key})` };
|
|
88
|
-
const command = new PutCommand(cmdParams);
|
|
89
|
-
const numberOfAttempts = imax ?? MAX_RETRY_ATTEMPTS;
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await dbClient.client.send(command);
|
|
93
|
-
} catch (err: any) {
|
|
94
|
-
if (err.name === 'ConditionalCheckFailedException') {
|
|
95
|
-
if (i < numberOfAttempts - 1) {
|
|
96
|
-
(item as any)[key] = short.uuid(); // new primary key
|
|
97
|
-
const ret: T | null = await writeItemForceHelper(table, item, key, i + 1, imax);
|
|
98
|
-
return ret;
|
|
99
|
-
}
|
|
100
|
-
console.error('PutCommandInput:', cmdParams);
|
|
101
|
-
if (numberOfAttempts === 1) {
|
|
102
|
-
error(`[ERROR] An item with the same key(${(item as any)[key]}) already exists!`);
|
|
103
|
-
} else {
|
|
104
|
-
error('[ERROR] Maximum attempts overflow!');
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return item;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
export interface WriteItemForceInput<T = any> {
|
|
114
|
-
table: string;
|
|
115
|
-
item: T;
|
|
116
|
-
key?: string;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export type WriteItemUniqueInput<T = any> = WriteItemForceInput<T>;
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Write an item to a DynamoDB table, retry in case of key conflict
|
|
123
|
-
* @param input input command object
|
|
124
|
-
* @returns the created item, null in case of error
|
|
125
|
-
*
|
|
126
|
-
* ```js
|
|
127
|
-
* writeItemForce({
|
|
128
|
-
* table: 'lesson_list',
|
|
129
|
-
* item: { title: 'My Lesson' },
|
|
130
|
-
* key: 'id',
|
|
131
|
-
* });
|
|
132
|
-
*
|
|
133
|
-
* interface WriteItemForceInput<T = any> {
|
|
134
|
-
* table: string;
|
|
135
|
-
* item: T;
|
|
136
|
-
* key?: string; // default: `id`
|
|
137
|
-
* }
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
export const writeItemForce = async <T = any>(input: WriteItemForceInput<T>): Promise<T | null> => {
|
|
141
|
-
const key = input.key || 'id';
|
|
142
|
-
return writeItemForceHelper<T>(input.table, input.item, key, 0);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Write an item (uniquely) to a DynamoDB table.
|
|
147
|
-
* Unlike `writeItemForce`, it does not retry in case of key conflict
|
|
148
|
-
* Unlike `writeItem`, it does not overwrite an item with the same key (if it exists)
|
|
149
|
-
* @param input input command object
|
|
150
|
-
* @returns the created item, null in case of error or key conflict (i.e., if item with same key already exists)
|
|
151
|
-
*
|
|
152
|
-
* ```js
|
|
153
|
-
* writeItemUnique({
|
|
154
|
-
* table: 'lesson_list',
|
|
155
|
-
* item: { id: 'id_001', title: 'My Lesson' },
|
|
156
|
-
* key: 'id',
|
|
157
|
-
* });
|
|
158
|
-
*
|
|
159
|
-
* interface WriteItemUniqueInput<T = any> {
|
|
160
|
-
* table: string;
|
|
161
|
-
* item: T;
|
|
162
|
-
* key?: string; // default: `id`
|
|
163
|
-
* }
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
export const writeItemUnique = async <T = any>(input: WriteItemUniqueInput<T>): Promise<T | null> => {
|
|
167
|
-
const key = input.key || 'id';
|
|
168
|
-
return writeItemForceHelper<T>(input.table, input.item, key, 0, 1);
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
export interface WriteItemInput {
|
|
172
|
-
table: string;
|
|
173
|
-
item: StringIndexable;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Write an item to a DynamoDB table
|
|
178
|
-
* @param input input command object
|
|
179
|
-
* @returns true if successful, null in case of error
|
|
180
|
-
*
|
|
181
|
-
* ```js
|
|
182
|
-
* writeItem({
|
|
183
|
-
* table: 'lesson_list',
|
|
184
|
-
* item: { id: 'id_001', title: 'My Lesson' },
|
|
185
|
-
* });
|
|
186
|
-
*
|
|
187
|
-
* interface WriteItemInput {
|
|
188
|
-
* table: string;
|
|
189
|
-
* item: StringIndexable;
|
|
190
|
-
* }
|
|
191
|
-
* ```
|
|
192
|
-
*/
|
|
193
|
-
export const writeItem = async (input: WriteItemInput) => {
|
|
194
|
-
if (!dbClient.client) tryInit();
|
|
195
|
-
if (!dbClient.client) return null;
|
|
196
|
-
if (!input.table || !input.item) return null;
|
|
197
|
-
|
|
198
|
-
const cmdParams: PutCommandInput = { TableName: input.table, Item: input.item };
|
|
199
|
-
const command = new PutCommand(cmdParams);
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
await dbClient.client.send(command);
|
|
203
|
-
} catch (err) {
|
|
204
|
-
console.error('PutCommandInput:', cmdParams);
|
|
205
|
-
console.error(err);
|
|
206
|
-
return null;
|
|
207
|
-
// throw err;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return true;
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-table-read-write-batch.html
|
|
214
|
-
const batchWriteItems = async (table: string, items: StringIndexable[]) => {
|
|
215
|
-
if (!dbClient.client) tryInit();
|
|
216
|
-
if (!dbClient.client) return null;
|
|
217
|
-
if (!table || !items || !Array.isArray(items)) return null;
|
|
218
|
-
if (!items.length) return true;
|
|
219
|
-
|
|
220
|
-
const reqList = items.map(item => ({ PutRequest: { Item: item } }));
|
|
221
|
-
const cmdParams: BatchWriteCommandInput = {
|
|
222
|
-
RequestItems: {
|
|
223
|
-
[table]: reqList,
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const command = new BatchWriteCommand(cmdParams);
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
await dbClient.client.send(command);
|
|
231
|
-
} catch (err) {
|
|
232
|
-
console.error('BatchWriteCommandInput:', cmdParams);
|
|
233
|
-
console.error(err);
|
|
234
|
-
return null;
|
|
235
|
-
// throw err;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return true;
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
export interface WriteItemsAllInput {
|
|
242
|
-
table: string;
|
|
243
|
-
items: StringIndexable[];
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Write a list of items to a DynamoDB table
|
|
248
|
-
* @param input input command object
|
|
249
|
-
* @returns true if successful, null in case of error
|
|
250
|
-
*
|
|
251
|
-
* ```js
|
|
252
|
-
* writeItemsAll({
|
|
253
|
-
* table: 'lesson_list',
|
|
254
|
-
* items: [{ id: 'id_001', title: 'My Lesson' }, { id: 'id_002', title: 'My Lesson 2' }],
|
|
255
|
-
* });
|
|
256
|
-
*
|
|
257
|
-
* interface WriteItemInput {
|
|
258
|
-
* table: string;
|
|
259
|
-
* items: StringIndexable[];
|
|
260
|
-
* }
|
|
261
|
-
* ```
|
|
262
|
-
*/
|
|
263
|
-
export const writeItemsAll = async (input: WriteItemsAllInput) => {
|
|
264
|
-
if (!dbClient.client) tryInit();
|
|
265
|
-
if (!dbClient.client) return null;
|
|
266
|
-
if (!input.table || !input.items || !Array.isArray(input.items)) return null;
|
|
267
|
-
if (!input.items.length) return true;
|
|
268
|
-
|
|
269
|
-
let errFlag = false;
|
|
270
|
-
|
|
271
|
-
const batchedItems = chunkifyArray(input.items, BATCH_SIZE);
|
|
272
|
-
const chunkedItems = chunkifyArray(batchedItems, CHUNK_SIZE);
|
|
273
|
-
|
|
274
|
-
for (let i = 0; i < chunkedItems.length; i += 1) {
|
|
275
|
-
const bchunks = chunkedItems[i];
|
|
276
|
-
|
|
277
|
-
const brlist = bchunks.map(iItems => batchWriteItems(input.table, iItems));
|
|
278
|
-
const bslist = await Promise.all(brlist); // eslint-disable-line no-await-in-loop
|
|
279
|
-
|
|
280
|
-
const isSuccess = bslist.every(e => e === true);
|
|
281
|
-
if (!isSuccess) errFlag = true;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return errFlag ? null : true;
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
export interface UpdateItemInput {
|
|
288
|
-
table: string;
|
|
289
|
-
key: StringIndexable;
|
|
290
|
-
update: string;
|
|
291
|
-
attr: StringIndexable;
|
|
292
|
-
attrNames?: StringIndexable;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Update an item in DynamoDB table
|
|
297
|
-
* @param input input command object
|
|
298
|
-
* @returns true if successful, null in case of error
|
|
299
|
-
*
|
|
300
|
-
* ```js
|
|
301
|
-
* updateItem({
|
|
302
|
-
* table: 'lesson_list',
|
|
303
|
-
* key: { id: 'id_001' },
|
|
304
|
-
* update: 'SET status = :status, #rev = 10',
|
|
305
|
-
* attr: { ':status': 'completed' },
|
|
306
|
-
* attrNames: { '#rev': 'revision' },
|
|
307
|
-
* });
|
|
308
|
-
*
|
|
309
|
-
* interface UpdateItemInput {
|
|
310
|
-
* table: string;
|
|
311
|
-
* key: StringIndexable;
|
|
312
|
-
* update: string;
|
|
313
|
-
* attr: StringIndexable;
|
|
314
|
-
* attrNames?: StringIndexable;
|
|
315
|
-
* }
|
|
316
|
-
* ```
|
|
317
|
-
*/
|
|
318
|
-
export const updateItem = async (input: UpdateItemInput) => {
|
|
319
|
-
if (!dbClient.client) tryInit();
|
|
320
|
-
if (!dbClient.client) return null;
|
|
321
|
-
if (!input.table || !input.key || !input.update || !input.attr) return null;
|
|
322
|
-
|
|
323
|
-
let cmdParams: UpdateCommandInput = {
|
|
324
|
-
TableName: input.table,
|
|
325
|
-
Key: input.key,
|
|
326
|
-
UpdateExpression: input.update,
|
|
327
|
-
ExpressionAttributeValues: input.attr,
|
|
328
|
-
};
|
|
329
|
-
if (input.attrNames) cmdParams = { ...cmdParams, ExpressionAttributeNames: input.attrNames };
|
|
330
|
-
const command = new UpdateCommand(cmdParams);
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
await dbClient.client.send(command);
|
|
334
|
-
} catch (err) {
|
|
335
|
-
console.error('UpdateCommandInput:', cmdParams);
|
|
336
|
-
console.error(err);
|
|
337
|
-
return null;
|
|
338
|
-
// throw err;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return true;
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
export interface ReadItemInput {
|
|
345
|
-
table: string;
|
|
346
|
-
key: StringIndexable;
|
|
347
|
-
projection?: string;
|
|
348
|
-
attrNames?: StringIndexable;
|
|
349
|
-
loud?: boolean;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Read an item from DynamoDB table
|
|
354
|
-
* @param input input command object
|
|
355
|
-
* @returns item contents, null in case of error
|
|
356
|
-
*
|
|
357
|
-
* ```js
|
|
358
|
-
* readItem({
|
|
359
|
-
* table: 'lesson_list',
|
|
360
|
-
* key: { id: 'id_001' },
|
|
361
|
-
* projection: 'id, lesson, status',
|
|
362
|
-
* });
|
|
363
|
-
*
|
|
364
|
-
* interface ReadItemInput {
|
|
365
|
-
* table: string;
|
|
366
|
-
* key: StringIndexable;
|
|
367
|
-
* projection?: string;
|
|
368
|
-
* attrNames?: StringIndexable;
|
|
369
|
-
* }
|
|
370
|
-
* ```
|
|
371
|
-
*/
|
|
372
|
-
export const readItem = async <T = any>(input: ReadItemInput) => {
|
|
373
|
-
if (!dbClient.client) tryInit();
|
|
374
|
-
if (!dbClient.client) return null;
|
|
375
|
-
if (!input.table || !input.key) return null;
|
|
376
|
-
|
|
377
|
-
let contents: T | null = null;
|
|
378
|
-
let cmdParams: GetCommandInput = { TableName: input.table, Key: input.key };
|
|
379
|
-
|
|
380
|
-
// Ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html
|
|
381
|
-
if (input.projection) cmdParams = { ...cmdParams, ProjectionExpression: input.projection };
|
|
382
|
-
if (input.attrNames) cmdParams = { ...cmdParams, ExpressionAttributeNames: input.attrNames };
|
|
383
|
-
|
|
384
|
-
const command = new GetCommand(cmdParams);
|
|
385
|
-
|
|
386
|
-
try {
|
|
387
|
-
const results = await dbClient.client.send(command);
|
|
388
|
-
const item = results.Item;
|
|
389
|
-
|
|
390
|
-
if (item) {
|
|
391
|
-
contents = item as T;
|
|
392
|
-
}
|
|
393
|
-
} catch (err) {
|
|
394
|
-
console.error('GetCommandInput:', cmdParams);
|
|
395
|
-
console.error(err);
|
|
396
|
-
if (input.loud) {
|
|
397
|
-
throw err;
|
|
398
|
-
} else {
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return contents;
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
// Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-table-read-write-batch.html
|
|
407
|
-
const batchReadItems = async <T = any>(
|
|
408
|
-
table: string,
|
|
409
|
-
keys: StringIndexable[],
|
|
410
|
-
projection?: string,
|
|
411
|
-
attrNames?: StringIndexable
|
|
412
|
-
) => {
|
|
413
|
-
if (!dbClient.client) tryInit();
|
|
414
|
-
if (!dbClient.client) return null;
|
|
415
|
-
if (!table || !keys || !Array.isArray(keys)) return null;
|
|
416
|
-
if (!keys.length) return [];
|
|
417
|
-
|
|
418
|
-
let contents: StringIndexable<T>[] = [];
|
|
419
|
-
|
|
420
|
-
let reqParams: any = { Keys: keys };
|
|
421
|
-
if (projection) reqParams = { ...reqParams, ProjectionExpression: projection };
|
|
422
|
-
if (attrNames) reqParams = { ...reqParams, ExpressionAttributeNames: attrNames };
|
|
423
|
-
|
|
424
|
-
const cmdParams: BatchGetCommandInput = {
|
|
425
|
-
RequestItems: {
|
|
426
|
-
[table]: reqParams,
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const command = new BatchGetCommand(cmdParams);
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
const results = await dbClient.client.send(command);
|
|
434
|
-
const items = results.Responses;
|
|
435
|
-
|
|
436
|
-
if (items && items[table]) {
|
|
437
|
-
contents = items[table];
|
|
438
|
-
}
|
|
439
|
-
} catch (err) {
|
|
440
|
-
console.error('BatchGetCommandInput:', cmdParams);
|
|
441
|
-
console.error(err);
|
|
442
|
-
return null;
|
|
443
|
-
// throw err;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return contents;
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
export interface ReadItemsAllInput {
|
|
450
|
-
table: string;
|
|
451
|
-
keys: StringIndexable[];
|
|
452
|
-
projection?: string;
|
|
453
|
-
attrNames?: StringIndexable;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Read a list of items from DynamoDB table
|
|
458
|
-
* Note: ordering of items in result may not be same as that in `keys`
|
|
459
|
-
* @param input input command object
|
|
460
|
-
* @returns list of contents for items, null in case of error
|
|
461
|
-
*
|
|
462
|
-
* ```js
|
|
463
|
-
* readItemsAll({
|
|
464
|
-
* table: 'lesson_list',
|
|
465
|
-
* keys: [{ id: 'id_001' }, { id: 'id_002' }],
|
|
466
|
-
* projection: 'id, lesson, status',
|
|
467
|
-
* });
|
|
468
|
-
*
|
|
469
|
-
* interface ReadItemsAllInput {
|
|
470
|
-
* table: string;
|
|
471
|
-
* keys: StringIndexable[];
|
|
472
|
-
* projection?: string;
|
|
473
|
-
* attrNames?: StringIndexable;
|
|
474
|
-
* }
|
|
475
|
-
* ```
|
|
476
|
-
*/
|
|
477
|
-
export const readItemsAll = async <T = any>(input: ReadItemsAllInput) => {
|
|
478
|
-
if (!dbClient.client) tryInit();
|
|
479
|
-
if (!dbClient.client) return null;
|
|
480
|
-
if (!input.table || !input.keys || !Array.isArray(input.keys)) return null;
|
|
481
|
-
if (!input.keys.length) return [];
|
|
482
|
-
|
|
483
|
-
let contents: StringIndexable<T>[] = [];
|
|
484
|
-
let errFlag = false;
|
|
485
|
-
|
|
486
|
-
const batchedKeys = chunkifyArray(input.keys, BATCH_SIZE);
|
|
487
|
-
const chunkedKeys = chunkifyArray(batchedKeys, CHUNK_SIZE);
|
|
488
|
-
|
|
489
|
-
for (let i = 0; i < chunkedKeys.length; i += 1) {
|
|
490
|
-
const bchunks = chunkedKeys[i];
|
|
491
|
-
|
|
492
|
-
const brlist = bchunks.map(ikeys => batchReadItems(input.table, ikeys, input.projection, input.attrNames));
|
|
493
|
-
const bslist = await Promise.all(brlist); // eslint-disable-line no-await-in-loop
|
|
494
|
-
|
|
495
|
-
const icontents = bslist.flat();
|
|
496
|
-
const isError = icontents.find(e => e === null) === null;
|
|
497
|
-
if (isError) {
|
|
498
|
-
errFlag = true;
|
|
499
|
-
return null;
|
|
500
|
-
}
|
|
501
|
-
if (!errFlag) {
|
|
502
|
-
contents = contents.concat(icontents as StringIndexable[]);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return contents;
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
export interface QueryItemsInput {
|
|
510
|
-
table: string;
|
|
511
|
-
indexName?: string;
|
|
512
|
-
cond: string;
|
|
513
|
-
attr: StringIndexable;
|
|
514
|
-
attrNames?: StringIndexable;
|
|
515
|
-
projection?: string;
|
|
516
|
-
desc?: boolean;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Query items from a DynamoDB table based on some condition
|
|
521
|
-
* @param input input command object
|
|
522
|
-
* @returns query results array, null in case of error
|
|
523
|
-
*
|
|
524
|
-
* ```js
|
|
525
|
-
* dbQueryItems({
|
|
526
|
-
* table: 'lesson_list',
|
|
527
|
-
* indexName: 'status-revision-index',
|
|
528
|
-
* cond: 'status = :comp AND #rev >= :rev',
|
|
529
|
-
* attr: { ':comp': 'completed', ':rev': 9 },
|
|
530
|
-
* attrNames: { '#rev': 'revision' },
|
|
531
|
-
* projection: 'id, lesson, status, revision',
|
|
532
|
-
* });
|
|
533
|
-
*
|
|
534
|
-
* interface QueryItemsInput {
|
|
535
|
-
* table: string;
|
|
536
|
-
* indexName?: string;
|
|
537
|
-
* cond: string;
|
|
538
|
-
* attr: StringIndexable;
|
|
539
|
-
* attrNames?: StringIndexable;
|
|
540
|
-
* projection?: string;
|
|
541
|
-
* desc?: boolean;
|
|
542
|
-
* }
|
|
543
|
-
* ```
|
|
544
|
-
*/
|
|
545
|
-
export const queryItems = async (input: QueryItemsInput) => {
|
|
546
|
-
if (!dbClient.client) tryInit();
|
|
547
|
-
if (!dbClient.client) return null;
|
|
548
|
-
if (!input.table || !input.cond || !input.attr) return null;
|
|
549
|
-
|
|
550
|
-
let contents: StringIndexable[] = [];
|
|
551
|
-
const desc = input.desc || false;
|
|
552
|
-
|
|
553
|
-
// Ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html
|
|
554
|
-
// Ref: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html
|
|
555
|
-
// Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-query-scan.html
|
|
556
|
-
// Ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SQLtoNoSQL.Indexes.QueryAndScan.html#SQLtoNoSQL.Indexes.QueryAndScan.DynamoDB
|
|
557
|
-
let cmdParams: QueryCommandInput = {
|
|
558
|
-
TableName: input.table,
|
|
559
|
-
KeyConditionExpression: input.cond,
|
|
560
|
-
ExpressionAttributeValues: input.attr,
|
|
561
|
-
// FilterExpression: "contains (category_id, :cid)",
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
if (input.indexName) cmdParams = { ...cmdParams, IndexName: input.indexName };
|
|
565
|
-
if (input.attrNames) cmdParams = { ...cmdParams, ExpressionAttributeNames: input.attrNames };
|
|
566
|
-
if (input.projection) cmdParams = { ...cmdParams, ProjectionExpression: input.projection };
|
|
567
|
-
if (desc) cmdParams = { ...cmdParams, ScanIndexForward: false };
|
|
568
|
-
|
|
569
|
-
const command = new QueryCommand(cmdParams);
|
|
570
|
-
|
|
571
|
-
try {
|
|
572
|
-
const results = await dbClient.client.send(command);
|
|
573
|
-
const items = results.Items;
|
|
574
|
-
|
|
575
|
-
if (items) {
|
|
576
|
-
contents = items;
|
|
577
|
-
}
|
|
578
|
-
} catch (err) {
|
|
579
|
-
console.error('QueryCommandInput:', command.input);
|
|
580
|
-
console.error(err);
|
|
581
|
-
return null;
|
|
582
|
-
// throw err;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
return contents;
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
export interface ScanItemsInput {
|
|
589
|
-
table: string;
|
|
590
|
-
projection?: string;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Scan all items in a DynamoDB table
|
|
595
|
-
* Note: avoid using this method in favour of `queryItems` method due to performance reasons
|
|
596
|
-
* @param input input command object
|
|
597
|
-
* @returns results of the scan query, null in case of error
|
|
598
|
-
*
|
|
599
|
-
* ```js
|
|
600
|
-
* scanItems({
|
|
601
|
-
* table: 'lesson_list',
|
|
602
|
-
* projection: 'id, status',
|
|
603
|
-
* });
|
|
604
|
-
*
|
|
605
|
-
* interface ScanItemsInput {
|
|
606
|
-
* table: string;
|
|
607
|
-
* projection?: string;
|
|
608
|
-
* }
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
export const scanItems = async (input: ScanItemsInput) => {
|
|
612
|
-
if (!dbClient.client) tryInit();
|
|
613
|
-
if (!dbClient.client) return null;
|
|
614
|
-
if (!input.table) return null;
|
|
615
|
-
|
|
616
|
-
let contents: StringIndexable[] = [];
|
|
617
|
-
|
|
618
|
-
// Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-query-scan.html
|
|
619
|
-
let cmdParams: ScanCommandInput = {
|
|
620
|
-
TableName: input.table,
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
if (input.projection) cmdParams = { ...cmdParams, ProjectionExpression: input.projection };
|
|
624
|
-
|
|
625
|
-
const command = new ScanCommand(cmdParams);
|
|
626
|
-
|
|
627
|
-
try {
|
|
628
|
-
const results = await dbClient.client.send(command);
|
|
629
|
-
const items = results.Items;
|
|
630
|
-
|
|
631
|
-
// Ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Pagination
|
|
632
|
-
if (results.LastEvaluatedKey) {
|
|
633
|
-
warn('[scanItems] Partial results obtained! Consider pagination.');
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
if (items) {
|
|
637
|
-
contents = items;
|
|
638
|
-
}
|
|
639
|
-
} catch (err) {
|
|
640
|
-
console.error('ScanCommandInput:', cmdParams);
|
|
641
|
-
console.error(err);
|
|
642
|
-
return null;
|
|
643
|
-
// throw err;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
return contents;
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
export interface DeleteItemInput {
|
|
650
|
-
table: string;
|
|
651
|
-
key: StringIndexable;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Delete an item in a DynamoDB table
|
|
656
|
-
* @param input input command object
|
|
657
|
-
* @returns true if successful, null in case of error
|
|
658
|
-
*
|
|
659
|
-
* ```js
|
|
660
|
-
* deleteItem({
|
|
661
|
-
* table: 'lesson_list',
|
|
662
|
-
* key: { id: 'id_001' },
|
|
663
|
-
* });
|
|
664
|
-
*
|
|
665
|
-
* interface DeleteItemInput {
|
|
666
|
-
* table: string;
|
|
667
|
-
* key: StringIndexable;
|
|
668
|
-
* }
|
|
669
|
-
* ```
|
|
670
|
-
*/
|
|
671
|
-
export const deleteItem = async (input: DeleteItemInput) => {
|
|
672
|
-
if (!dbClient.client) tryInit();
|
|
673
|
-
if (!dbClient.client) return null;
|
|
674
|
-
if (!input.table || !input.key) return null;
|
|
675
|
-
|
|
676
|
-
const cmdParams: DeleteCommandInput = { TableName: input.table, Key: input.key };
|
|
677
|
-
const command = new DeleteCommand(cmdParams);
|
|
678
|
-
|
|
679
|
-
try {
|
|
680
|
-
await dbClient.client.send(command);
|
|
681
|
-
} catch (err) {
|
|
682
|
-
console.error('DeleteCommandInput:', cmdParams);
|
|
683
|
-
console.error(err);
|
|
684
|
-
return null;
|
|
685
|
-
// throw err;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
return true;
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
// Ref: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-table-read-write-batch.html
|
|
692
|
-
// Ref: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/interfaces/batchwriteitemcommandinput.html#requestitems
|
|
693
|
-
const batchDeleteItems = async (table: string, keys: StringIndexable[]) => {
|
|
694
|
-
if (!dbClient.client) tryInit();
|
|
695
|
-
if (!dbClient.client) return null;
|
|
696
|
-
if (!table || !keys || !Array.isArray(keys)) return null;
|
|
697
|
-
if (!keys.length) return true;
|
|
698
|
-
|
|
699
|
-
const reqList = keys.map(key => ({ DeleteRequest: { Key: key } }));
|
|
700
|
-
const cmdParams: BatchWriteCommandInput = {
|
|
701
|
-
RequestItems: {
|
|
702
|
-
[table]: reqList,
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
const command = new BatchWriteCommand(cmdParams);
|
|
707
|
-
|
|
708
|
-
try {
|
|
709
|
-
await dbClient.client.send(command);
|
|
710
|
-
} catch (err) {
|
|
711
|
-
console.error('BatchWriteCommandInput:', cmdParams);
|
|
712
|
-
console.error(err);
|
|
713
|
-
return null;
|
|
714
|
-
// throw err;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
return true;
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
export interface DeleteItemsAllInput {
|
|
721
|
-
table: string;
|
|
722
|
-
keys: StringIndexable[];
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* Delete a list of items in a DynamoDB table
|
|
727
|
-
* @param input input command object
|
|
728
|
-
* @returns true if successful, null in case of error
|
|
729
|
-
*
|
|
730
|
-
* ```js
|
|
731
|
-
* deleteItemsAll({
|
|
732
|
-
* table: 'lesson_list',
|
|
733
|
-
* keys: [{ id: 'id_001' }, { id: 'id_002' }],
|
|
734
|
-
* });
|
|
735
|
-
*
|
|
736
|
-
* interface DeleteItemsAllInput {
|
|
737
|
-
* table: string;
|
|
738
|
-
* keys: StringIndexable[];
|
|
739
|
-
* }
|
|
740
|
-
* ```
|
|
741
|
-
*/
|
|
742
|
-
export const deleteItemsAll = async (input: DeleteItemsAllInput) => {
|
|
743
|
-
if (!dbClient.client) tryInit();
|
|
744
|
-
if (!dbClient.client) return null;
|
|
745
|
-
if (!input.table || !input.keys || !Array.isArray(input.keys)) return null;
|
|
746
|
-
if (!input.keys.length) return true;
|
|
747
|
-
|
|
748
|
-
let errFlag = false;
|
|
749
|
-
|
|
750
|
-
const batchedItems = chunkifyArray(input.keys, BATCH_SIZE);
|
|
751
|
-
const chunkedItems = chunkifyArray(batchedItems, CHUNK_SIZE);
|
|
752
|
-
|
|
753
|
-
for (let i = 0; i < chunkedItems.length; i += 1) {
|
|
754
|
-
const bchunks = chunkedItems[i];
|
|
755
|
-
|
|
756
|
-
const brlist = bchunks.map(ikeys => batchDeleteItems(input.table, ikeys));
|
|
757
|
-
const bslist = await Promise.all(brlist); // eslint-disable-line no-await-in-loop
|
|
758
|
-
|
|
759
|
-
const isSuccess = bslist.every(e => e === true);
|
|
760
|
-
if (!isSuccess) errFlag = true;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return errFlag ? null : true;
|
|
764
|
-
};
|