ag-common 0.0.724 → 0.0.726
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/api/helpers/dynamo.d.ts +64 -51
- package/dist/api/helpers/dynamo.js +259 -248
- package/package.json +1 -1
|
@@ -1,77 +1,90 @@
|
|
|
1
1
|
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
|
|
2
2
|
import type { AwsCredentialIdentity } from '@smithy/types';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type DynamoDBError = {
|
|
4
|
+
error: string;
|
|
5
|
+
};
|
|
6
|
+
type DynamoDBSuccess<T> = {
|
|
7
|
+
data: T;
|
|
8
|
+
};
|
|
9
|
+
type DynamoDBResult<T> = DynamoDBSuccess<T> | DynamoDBError;
|
|
10
|
+
interface Key {
|
|
11
|
+
[key: string]: string | number;
|
|
12
|
+
}
|
|
13
|
+
interface DynamoFilter {
|
|
14
|
+
filterExpression: string;
|
|
15
|
+
attrNames: Record<string, string>;
|
|
16
|
+
attrValues?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
interface ScanOptions {
|
|
19
|
+
/** eg
|
|
20
|
+
* filter: {
|
|
21
|
+
filterExpression: '#feedIcon = :empty',
|
|
22
|
+
attrNames: { '#feedIcon': 'feedIcon' },
|
|
23
|
+
attrValues: {
|
|
24
|
+
':empty': '',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
*/
|
|
28
|
+
filter?: DynamoFilter;
|
|
29
|
+
requiredAttributeList?: string[];
|
|
30
|
+
limit?: number;
|
|
31
|
+
}
|
|
32
|
+
interface DynamoQueryParams {
|
|
33
|
+
tableName: string;
|
|
34
|
+
pkName: string;
|
|
35
|
+
pkValue: string | number;
|
|
36
|
+
pkOperator?: '=' | '<' | '>' | '<=' | '>=';
|
|
37
|
+
skName?: string;
|
|
38
|
+
skValue?: string | number | [string | number, string | number];
|
|
39
|
+
skOperator?: '=' | '<' | '>' | '<=' | '>=' | 'BETWEEN' | 'BEGINS_WITH';
|
|
40
|
+
indexName?: string;
|
|
41
|
+
limit?: number;
|
|
42
|
+
startKey?: Key;
|
|
43
|
+
filterName?: string;
|
|
44
|
+
filterValue?: unknown;
|
|
45
|
+
filterOperator?: string;
|
|
46
|
+
sortAscending?: boolean;
|
|
47
|
+
}
|
|
5
48
|
export declare let dynamoDb: DynamoDBDocument;
|
|
6
|
-
export declare const
|
|
7
|
-
|
|
49
|
+
export declare const setDynamo: (region: string, credentials?: AwsCredentialIdentity) => DynamoDBDocument;
|
|
50
|
+
export declare const putDynamo: <T extends Record<string, unknown>>(item: T, tableName: string, opt?: {
|
|
8
51
|
pkName?: string;
|
|
9
|
-
}) => Promise<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export declare const batchWrite: <T extends Record<string, any>>(tableName: string, itemsIn: T[]) => Promise<{
|
|
13
|
-
error?: string;
|
|
14
|
-
}>;
|
|
15
|
-
export declare const batchDelete: ({ tableName, keys, pkName, }: {
|
|
52
|
+
}) => Promise<DynamoDBResult<void>>;
|
|
53
|
+
export declare const batchWrite: <T extends Record<string, unknown>>(tableName: string, items: T[]) => Promise<DynamoDBResult<void>>;
|
|
54
|
+
export declare const batchDelete: (params: {
|
|
16
55
|
tableName: string;
|
|
17
56
|
keys: string[];
|
|
18
57
|
pkName: string;
|
|
19
|
-
}) => Promise<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export declare const
|
|
23
|
-
|
|
24
|
-
filterExpression: string;
|
|
25
|
-
attrNames: Record<string, string>;
|
|
26
|
-
attrValues?: Record<string, string>;
|
|
27
|
-
};
|
|
28
|
-
/** ProjectionExpression. will csv values */
|
|
29
|
-
requiredAttributeList?: string[];
|
|
30
|
-
/** default =ALL */
|
|
31
|
-
limit?: number;
|
|
32
|
-
}) => Promise<{
|
|
33
|
-
data: T[];
|
|
34
|
-
} | {
|
|
35
|
-
error: string;
|
|
36
|
-
}>;
|
|
37
|
-
export declare const getItemsDynamo: <T>({ tableName, items, }: {
|
|
58
|
+
}) => Promise<DynamoDBResult<void>>;
|
|
59
|
+
export declare const scan: <T>(tableName: string, options?: ScanOptions) => Promise<DynamoDBResult<T[]>>;
|
|
60
|
+
export declare function scanWithGenerator<T>(tableName: string, options?: ScanOptions): AsyncGenerator<T[], void, unknown>;
|
|
61
|
+
export declare const getItemsDynamo: <T>(params: {
|
|
62
|
+
tableName: string;
|
|
38
63
|
items: {
|
|
39
64
|
pkName: string;
|
|
40
65
|
pkValue: string;
|
|
41
66
|
}[];
|
|
67
|
+
}) => Promise<DynamoDBResult<T[]>>;
|
|
68
|
+
export declare const getItemDynamo: <T>(params: {
|
|
42
69
|
tableName: string;
|
|
43
|
-
}) => Promise<{
|
|
44
|
-
data: T[];
|
|
45
|
-
} | {
|
|
46
|
-
error: string;
|
|
47
|
-
}>;
|
|
48
|
-
export declare const getItemDynamo: <T>({ tableName, pkName, pkValue, }: {
|
|
49
70
|
pkName: string;
|
|
50
71
|
pkValue: string;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
data: T;
|
|
54
|
-
} | {
|
|
55
|
-
error: string;
|
|
56
|
-
}>;
|
|
57
|
-
export declare const queryDynamo: <T>({ tableName, pkName, pkValue, pkOperator, skName, skValue, skOperator, indexName, limit, startKey, filterName, filterValue, filterOperator, sortAscending, }: IQueryDynamo) => Promise<{
|
|
72
|
+
}) => Promise<DynamoDBResult<T>>;
|
|
73
|
+
export declare const queryDynamo: <T>(params: DynamoQueryParams) => Promise<{
|
|
58
74
|
data: T[];
|
|
59
75
|
startKey?: Key;
|
|
60
76
|
} | {
|
|
61
77
|
error: string;
|
|
62
78
|
}>;
|
|
63
79
|
export declare const getDynamoTtlDays: (days: number) => number;
|
|
64
|
-
export declare const getDynamoTtlMinutes: (
|
|
65
|
-
export declare const wipeTable: (tableName: string) => Promise<
|
|
66
|
-
|
|
67
|
-
}>;
|
|
68
|
-
/** gets all fields in dynamokeys, and moves them into update expressions. eg will turn item.yourFieldName, into a dynamo write into field "yourFieldName" */
|
|
69
|
-
export declare const getDynamoUpdates: (item: Record<string, string | number | boolean>, opt?: {
|
|
70
|
-
/** default PK. will also exclude null or undefined */
|
|
80
|
+
export declare const getDynamoTtlMinutes: (minutes: number) => number;
|
|
81
|
+
export declare const wipeTable: (tableName: string) => Promise<DynamoDBResult<void>>;
|
|
82
|
+
export declare const getDynamoUpdates: <T extends Record<string, unknown>>(item: T, options?: {
|
|
71
83
|
excludeKeys?: string[];
|
|
72
84
|
}) => {
|
|
73
85
|
UpdateExpression: string;
|
|
74
86
|
ExpressionAttributeNames: Record<string, string>;
|
|
75
|
-
ExpressionAttributeValues: Record<string,
|
|
87
|
+
ExpressionAttributeValues: Record<string, unknown>;
|
|
76
88
|
ReturnValues: 'UPDATED_NEW';
|
|
77
89
|
};
|
|
90
|
+
export {};
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable guard-for-in */
|
|
3
|
-
/* eslint-disable no-restricted-syntax */
|
|
4
|
-
/* eslint-disable no-await-in-loop */
|
|
5
|
-
/* eslint-disable prefer-const */
|
|
6
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -12,353 +8,368 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
9
|
});
|
|
14
10
|
};
|
|
11
|
+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
12
|
+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
13
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
14
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
15
|
+
return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
16
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
17
|
+
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
18
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
19
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
20
|
+
function fulfill(value) { resume("next", value); }
|
|
21
|
+
function reject(value) { resume("throw", value); }
|
|
22
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
23
|
+
};
|
|
15
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.getDynamoUpdates = exports.wipeTable = exports.getDynamoTtlMinutes = exports.getDynamoTtlDays = exports.queryDynamo = exports.getItemDynamo = exports.getItemsDynamo = exports.scan = exports.batchDelete = exports.batchWrite = exports.putDynamo = exports.
|
|
25
|
+
exports.getDynamoUpdates = exports.wipeTable = exports.getDynamoTtlMinutes = exports.getDynamoTtlDays = exports.queryDynamo = exports.getItemDynamo = exports.getItemsDynamo = exports.scanWithGenerator = exports.scan = exports.batchDelete = exports.batchWrite = exports.putDynamo = exports.setDynamo = exports.dynamoDb = void 0;
|
|
17
26
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
18
27
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
19
|
-
// ES6 import
|
|
20
28
|
const array_1 = require("../../common/helpers/array");
|
|
21
29
|
const async_1 = require("../../common/helpers/async");
|
|
22
30
|
const log_1 = require("../../common/helpers/log");
|
|
23
31
|
const sleep_1 = require("../../common/helpers/sleep");
|
|
24
|
-
const
|
|
32
|
+
const RETRY_CONFIG = {
|
|
33
|
+
maxRetries: 3,
|
|
34
|
+
baseDelay: 2000,
|
|
35
|
+
};
|
|
36
|
+
const isError = (result) => 'error' in result;
|
|
37
|
+
const withRetry = (operation, operationName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
let retryCount = 0;
|
|
39
|
+
// eslint-disable-next-line
|
|
40
|
+
while (true) {
|
|
41
|
+
try {
|
|
42
|
+
return yield operation();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
const error = e;
|
|
46
|
+
const errorString = error.toString();
|
|
47
|
+
if (errorString.includes('429') ||
|
|
48
|
+
errorString.includes('ProvisionedThroughputExceeded')) {
|
|
49
|
+
retryCount++;
|
|
50
|
+
if (retryCount >= RETRY_CONFIG.maxRetries) {
|
|
51
|
+
(0, log_1.warn)(`${operationName}: Max retries exceeded`);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
const delay = RETRY_CONFIG.baseDelay * Math.pow(2, retryCount - 1);
|
|
55
|
+
(0, log_1.warn)(`${operationName}: Throttled. Retry ${retryCount}`);
|
|
56
|
+
yield (0, sleep_1.sleep)(delay);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
25
63
|
const setDynamo = (region, credentials) => {
|
|
26
|
-
|
|
27
|
-
|
|
64
|
+
const client = new client_dynamodb_1.DynamoDBClient({ region, credentials });
|
|
65
|
+
exports.dynamoDb = lib_dynamodb_1.DynamoDBDocument.from(client, {
|
|
28
66
|
marshallOptions: { removeUndefinedValues: true },
|
|
29
67
|
});
|
|
30
|
-
exports.dynamoDb
|
|
31
|
-
return ddbDocClient;
|
|
68
|
+
return exports.dynamoDb;
|
|
32
69
|
};
|
|
33
70
|
exports.setDynamo = setDynamo;
|
|
34
71
|
exports.dynamoDb = (0, exports.setDynamo)('ap-southeast-2');
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
72
|
const putDynamo = (item, tableName, opt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
-
|
|
73
|
+
const params = new lib_dynamodb_1.PutCommand(Object.assign({ TableName: tableName, Item: item }, ((opt === null || opt === void 0 ? void 0 : opt.pkName) && {
|
|
38
74
|
ConditionExpression: `attribute_not_exists(${opt.pkName})`,
|
|
39
75
|
})));
|
|
40
|
-
(0, log_1.debug)(`running dynamo put=${JSON.stringify(params, null, 2)}`);
|
|
41
76
|
try {
|
|
42
|
-
yield exports.dynamoDb.send(params);
|
|
43
|
-
return {};
|
|
77
|
+
yield withRetry(() => exports.dynamoDb.send(params), 'putDynamo');
|
|
78
|
+
return { data: undefined };
|
|
44
79
|
}
|
|
45
80
|
catch (e) {
|
|
46
|
-
(0, log_1.warn)('putDynamo error', e);
|
|
47
81
|
return { error: e.toString() };
|
|
48
82
|
}
|
|
49
83
|
});
|
|
50
84
|
exports.putDynamo = putDynamo;
|
|
51
|
-
|
|
52
|
-
const batchWrite = (tableName, itemsIn) => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
-
//batch up to 20, so we can retry.
|
|
54
|
-
let chunked = (0, array_1.chunk)(itemsIn, 20);
|
|
85
|
+
const batchWrite = (tableName, items) => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
86
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const retryMax = 3;
|
|
87
|
+
const chunked = (0, array_1.chunk)(items, 20);
|
|
88
|
+
yield (0, async_1.asyncForEach)(chunked, (chunk) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
89
|
const params = new lib_dynamodb_1.BatchWriteCommand({
|
|
60
90
|
RequestItems: {
|
|
61
|
-
[
|
|
62
|
-
PutRequest: { Item },
|
|
63
|
-
})),
|
|
91
|
+
[tableName]: chunk.map((Item) => ({ PutRequest: { Item } })),
|
|
64
92
|
},
|
|
65
93
|
});
|
|
66
|
-
(
|
|
67
|
-
// eslint-disable-next-line
|
|
68
|
-
while (true) {
|
|
69
|
-
try {
|
|
70
|
-
yield exports.dynamoDb.send(params);
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
catch (e) {
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
-
const es = e.toString();
|
|
76
|
-
let msg = es;
|
|
77
|
-
(0, log_1.warn)('dynamo write error', msg);
|
|
78
|
-
if (es.indexOf('429') !== -1 ||
|
|
79
|
-
es.indexOf('ProvisionedThroughputExceeded') !== -1) {
|
|
80
|
-
retryCount += 1;
|
|
81
|
-
msg = `batch write throttled. retry ${retryCount}/${retryMax}`;
|
|
82
|
-
(0, log_1.warn)(msg);
|
|
83
|
-
if (retryCount >= retryMax) {
|
|
84
|
-
throw new Error(`Max retries (${retryMax}) exceeded: ${es}`);
|
|
85
|
-
}
|
|
86
|
-
yield (0, sleep_1.sleep)(2000);
|
|
87
|
-
// Continue the while loop to retry
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
// For non-throttling errors, throw immediately
|
|
91
|
-
throw e;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
+
yield withRetry(() => exports.dynamoDb.send(params), 'batchWrite');
|
|
94
95
|
}));
|
|
95
|
-
return {};
|
|
96
|
+
return { data: undefined };
|
|
96
97
|
}
|
|
97
98
|
catch (e) {
|
|
98
|
-
(0, log_1.warn)('batchWrite error', e);
|
|
99
99
|
return { error: e.toString() };
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
102
|
exports.batchWrite = batchWrite;
|
|
103
|
-
const batchDelete = (
|
|
104
|
-
// batch up to 20, so we can retry
|
|
105
|
-
let chunked = (0, array_1.chunk)(keys, 20);
|
|
103
|
+
const batchDelete = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
104
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const params = new lib_dynamodb_1.BatchWriteCommand({
|
|
105
|
+
const chunked = (0, array_1.chunk)(params.keys, 20);
|
|
106
|
+
yield (0, async_1.asyncForEach)(chunked, (chunk) => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
const command = new lib_dynamodb_1.BatchWriteCommand({
|
|
111
108
|
RequestItems: {
|
|
112
|
-
[
|
|
113
|
-
DeleteRequest: { Key: { [
|
|
109
|
+
[params.tableName]: chunk.map((key) => ({
|
|
110
|
+
DeleteRequest: { Key: { [params.pkName]: key } },
|
|
114
111
|
})),
|
|
115
112
|
},
|
|
116
113
|
});
|
|
117
|
-
(
|
|
118
|
-
while (retryCount < retryMax) {
|
|
119
|
-
try {
|
|
120
|
-
yield exports.dynamoDb.send(params);
|
|
121
|
-
return {};
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
-
const es = e.toString();
|
|
126
|
-
let shouldRetry = false;
|
|
127
|
-
if (es.indexOf('429') !== -1 ||
|
|
128
|
-
es.indexOf('ProvisionedThroughputExceeded') !== -1) {
|
|
129
|
-
shouldRetry = true;
|
|
130
|
-
retryCount += 1;
|
|
131
|
-
const msg = `batch delete write throttled. retry ${retryCount}/${retryMax}`;
|
|
132
|
-
(0, log_1.warn)('dynamo write error', msg);
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// Non-retryable error
|
|
136
|
-
throw e;
|
|
137
|
-
}
|
|
138
|
-
if (shouldRetry) {
|
|
139
|
-
if (retryCount >= retryMax) {
|
|
140
|
-
(0, log_1.warn)(`Max retries (${retryMax}) reached, giving up`);
|
|
141
|
-
throw e;
|
|
142
|
-
}
|
|
143
|
-
(0, log_1.warn)(`dynamo retry ${retryCount}/${retryMax}`);
|
|
144
|
-
yield (0, sleep_1.sleep)(2000 * Math.pow(2, retryCount - 1)); // Exponential backoff
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
114
|
+
yield withRetry(() => exports.dynamoDb.send(command), 'batchDelete');
|
|
149
115
|
}));
|
|
150
|
-
return {};
|
|
116
|
+
return { data: undefined };
|
|
151
117
|
}
|
|
152
118
|
catch (e) {
|
|
153
|
-
(0, log_1.warn)('batchDelete error', e);
|
|
154
119
|
return { error: e.toString() };
|
|
155
120
|
}
|
|
156
121
|
});
|
|
157
122
|
exports.batchDelete = batchDelete;
|
|
158
|
-
const scan = (tableName,
|
|
159
|
-
var
|
|
123
|
+
const scan = (tableName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
124
|
+
var _a, _b;
|
|
160
125
|
try {
|
|
126
|
+
const Items = [];
|
|
161
127
|
let ExclusiveStartKey;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const projectionAttrs = (_b = opt === null || opt === void 0 ? void 0 : opt.requiredAttributeList) === null || _b === void 0 ? void 0 : _b.reduce((acc, attr, index) => {
|
|
165
|
-
const escapedName = `#proj${index}`;
|
|
166
|
-
acc[escapedName] = attr;
|
|
128
|
+
const projectionAttrs = (_a = options === null || options === void 0 ? void 0 : options.requiredAttributeList) === null || _a === void 0 ? void 0 : _a.reduce((acc, attr, index) => {
|
|
129
|
+
acc[`#proj${index}`] = attr;
|
|
167
130
|
return acc;
|
|
168
131
|
}, {});
|
|
169
|
-
|
|
170
|
-
const expressionAttributeNames = Object.assign(Object.assign({}, projectionAttrs), (_c = opt === null || opt === void 0 ? void 0 : opt.filter) === null || _c === void 0 ? void 0 : _c.attrNames);
|
|
132
|
+
const expressionAttributeNames = Object.assign(Object.assign({}, projectionAttrs), (_b = options === null || options === void 0 ? void 0 : options.filter) === null || _b === void 0 ? void 0 : _b.attrNames);
|
|
171
133
|
do {
|
|
172
|
-
const params = new lib_dynamodb_1.ScanCommand(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ TableName: tableName }, ((
|
|
173
|
-
ExpressionAttributeValues:
|
|
134
|
+
const params = new lib_dynamodb_1.ScanCommand(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ TableName: tableName }, ((options === null || options === void 0 ? void 0 : options.filter) && Object.assign({ FilterExpression: options.filter.filterExpression }, (options.filter.attrValues && {
|
|
135
|
+
ExpressionAttributeValues: options.filter.attrValues,
|
|
174
136
|
})))), (Object.keys(expressionAttributeNames).length > 0 && {
|
|
175
137
|
ExpressionAttributeNames: expressionAttributeNames,
|
|
176
|
-
})), ((
|
|
177
|
-
ProjectionExpression:
|
|
138
|
+
})), ((options === null || options === void 0 ? void 0 : options.requiredAttributeList) && {
|
|
139
|
+
ProjectionExpression: options.requiredAttributeList
|
|
178
140
|
.map((_, index) => `#proj${index}`)
|
|
179
141
|
.join(', '),
|
|
180
|
-
})), { ExclusiveStartKey }), ((
|
|
181
|
-
|
|
142
|
+
})), { ExclusiveStartKey }), ((options === null || options === void 0 ? void 0 : options.limit) &&
|
|
143
|
+
Items.length < options.limit && {
|
|
144
|
+
Limit: options.limit - Items.length,
|
|
182
145
|
})));
|
|
183
|
-
(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if ((
|
|
146
|
+
const result = yield withRetry(() => exports.dynamoDb.send(params), 'scan');
|
|
147
|
+
if (result.Items) {
|
|
148
|
+
Items.push(...result.Items);
|
|
149
|
+
}
|
|
150
|
+
ExclusiveStartKey = result.LastEvaluatedKey;
|
|
151
|
+
if ((options === null || options === void 0 ? void 0 : options.limit) && Items.length >= options.limit) {
|
|
189
152
|
break;
|
|
190
153
|
}
|
|
191
154
|
} while (ExclusiveStartKey);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
(0, log_1.debug)(`dynamo scan against ${tableName} ok, count=${Items.length}`);
|
|
197
|
-
return { data: Items };
|
|
155
|
+
return {
|
|
156
|
+
data: (options === null || options === void 0 ? void 0 : options.limit) ? Items.slice(0, options.limit) : Items,
|
|
157
|
+
};
|
|
198
158
|
}
|
|
199
159
|
catch (e) {
|
|
200
|
-
(0, log_1.warn)('scan error:', e);
|
|
201
160
|
return { error: e.toString() };
|
|
202
161
|
}
|
|
203
162
|
});
|
|
204
163
|
exports.scan = scan;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
164
|
+
function scanWithGenerator(tableName, options) {
|
|
165
|
+
return __asyncGenerator(this, arguments, function* scanWithGenerator_1() {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
const BATCH_SIZE = 25;
|
|
168
|
+
let items = [];
|
|
169
|
+
let exclusiveStartKey;
|
|
170
|
+
let totalItems = 0;
|
|
171
|
+
try {
|
|
172
|
+
const projectionAttrs = (_a = options === null || options === void 0 ? void 0 : options.requiredAttributeList) === null || _a === void 0 ? void 0 : _a.reduce((acc, attr, index) => {
|
|
173
|
+
acc[`#proj${index}`] = attr;
|
|
174
|
+
return acc;
|
|
175
|
+
}, {});
|
|
176
|
+
const expressionAttributeNames = Object.assign(Object.assign({}, projectionAttrs), (_b = options === null || options === void 0 ? void 0 : options.filter) === null || _b === void 0 ? void 0 : _b.attrNames);
|
|
177
|
+
do {
|
|
178
|
+
const params = new lib_dynamodb_1.ScanCommand(Object.assign(Object.assign(Object.assign(Object.assign({ TableName: tableName, Limit: BATCH_SIZE }, ((options === null || options === void 0 ? void 0 : options.filter) && Object.assign({ FilterExpression: options.filter.filterExpression }, (options.filter.attrValues && {
|
|
179
|
+
ExpressionAttributeValues: options.filter.attrValues,
|
|
180
|
+
})))), (Object.keys(expressionAttributeNames).length > 0 && {
|
|
181
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
182
|
+
})), ((options === null || options === void 0 ? void 0 : options.requiredAttributeList) && {
|
|
183
|
+
ProjectionExpression: options.requiredAttributeList
|
|
184
|
+
.map((_, index) => `#proj${index}`)
|
|
185
|
+
.join(', '),
|
|
186
|
+
})), { ExclusiveStartKey: exclusiveStartKey }));
|
|
187
|
+
const result = yield __await(withRetry(() => exports.dynamoDb.send(params), 'scanWithGenerator'));
|
|
188
|
+
if (result.Items) {
|
|
189
|
+
items.push(...result.Items);
|
|
190
|
+
// Process items in chunks of BATCH_SIZE
|
|
191
|
+
while (items.length >= BATCH_SIZE) {
|
|
192
|
+
const batch = items.slice(0, BATCH_SIZE);
|
|
193
|
+
items = items.slice(BATCH_SIZE);
|
|
194
|
+
totalItems += batch.length;
|
|
195
|
+
// If we've reached the limit, yield the final batch and return
|
|
196
|
+
if ((options === null || options === void 0 ? void 0 : options.limit) && totalItems >= options.limit) {
|
|
197
|
+
const remainingCount = options.limit - (totalItems - batch.length);
|
|
198
|
+
yield yield __await(batch.slice(0, remainingCount));
|
|
199
|
+
return yield __await(void 0);
|
|
200
|
+
}
|
|
201
|
+
yield yield __await(batch);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exclusiveStartKey = result.LastEvaluatedKey;
|
|
205
|
+
} while (exclusiveStartKey &&
|
|
206
|
+
(!(options === null || options === void 0 ? void 0 : options.limit) || totalItems < options.limit));
|
|
207
|
+
// Yield any remaining items
|
|
208
|
+
if (items.length > 0) {
|
|
209
|
+
if (options === null || options === void 0 ? void 0 : options.limit) {
|
|
210
|
+
const remainingCount = options.limit - totalItems;
|
|
211
|
+
if (remainingCount > 0) {
|
|
212
|
+
yield yield __await(items.slice(0, remainingCount));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
yield yield __await(items);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
throw new Error(`Scan generator error: ${e.toString()}`);
|
|
222
|
+
}
|
|
215
223
|
});
|
|
224
|
+
}
|
|
225
|
+
exports.scanWithGenerator = scanWithGenerator;
|
|
226
|
+
const getItemsDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
227
|
+
var _c, _d;
|
|
216
228
|
try {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
const command = new lib_dynamodb_1.BatchGetCommand({
|
|
230
|
+
RequestItems: {
|
|
231
|
+
[params.tableName]: {
|
|
232
|
+
Keys: params.items.map(({ pkName, pkValue }) => ({
|
|
233
|
+
[pkName]: pkValue,
|
|
234
|
+
})),
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
const result = yield withRetry(() => exports.dynamoDb.send(command), 'getItemsDynamo');
|
|
239
|
+
return {
|
|
240
|
+
data: (_d = (_c = result.Responses) === null || _c === void 0 ? void 0 : _c[params.tableName]) !== null && _d !== void 0 ? _d : [],
|
|
241
|
+
};
|
|
220
242
|
}
|
|
221
243
|
catch (e) {
|
|
222
|
-
(0, log_1.warn)('getItemsDynamo error:', e);
|
|
223
244
|
return { error: e.toString() };
|
|
224
|
-
//
|
|
225
245
|
}
|
|
226
246
|
});
|
|
227
247
|
exports.getItemsDynamo = getItemsDynamo;
|
|
228
|
-
const getItemDynamo = (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
const getItemDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
249
|
+
const result = yield (0, exports.getItemsDynamo)({
|
|
250
|
+
tableName: params.tableName,
|
|
251
|
+
items: [{ pkName: params.pkName, pkValue: params.pkValue }],
|
|
252
|
+
});
|
|
253
|
+
if (isError(result)) {
|
|
254
|
+
return result;
|
|
232
255
|
}
|
|
233
|
-
return { data:
|
|
256
|
+
return { data: result.data[0] };
|
|
234
257
|
});
|
|
235
258
|
exports.getItemDynamo = getItemDynamo;
|
|
236
|
-
const queryDynamo = (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
const queryDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
260
|
+
var _e, _f, _g;
|
|
261
|
+
try {
|
|
262
|
+
let kce = `#${params.pkName.toLowerCase()} ${(_e = params.pkOperator) !== null && _e !== void 0 ? _e : '='} :${params.pkName.toLowerCase()}`;
|
|
263
|
+
const ean = {
|
|
264
|
+
[`#${params.pkName.toLowerCase()}`]: params.pkName,
|
|
265
|
+
};
|
|
266
|
+
const eav = {
|
|
267
|
+
[`:${params.pkName.toLowerCase()}`]: params.pkValue,
|
|
268
|
+
};
|
|
269
|
+
if (params.skName && params.skValue !== undefined) {
|
|
270
|
+
const { skName, skValue, skOperator = '=' } = params;
|
|
271
|
+
if (skOperator === 'BETWEEN' && Array.isArray(skValue)) {
|
|
272
|
+
const [start, end] = skValue;
|
|
273
|
+
kce += ` AND #${skName.toLowerCase()} BETWEEN :${skName}1 AND :${skName}2`;
|
|
274
|
+
ean[`#${skName.toLowerCase()}`] = skName;
|
|
275
|
+
eav[`:${skName}1`] = start;
|
|
276
|
+
eav[`:${skName}2`] = end;
|
|
277
|
+
}
|
|
278
|
+
else if (skOperator === 'BEGINS_WITH') {
|
|
279
|
+
kce += ` AND begins_with(#${skName.toLowerCase()}, :${skName.toLowerCase()})`;
|
|
280
|
+
ean[`#${skName.toLowerCase()}`] = skName;
|
|
281
|
+
eav[`:${skName.toLowerCase()}`] = skValue;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
kce += ` AND #${skName.toLowerCase()} ${skOperator} :${skName.toLowerCase()}`;
|
|
285
|
+
ean[`#${skName.toLowerCase()}`] = skName;
|
|
286
|
+
eav[`:${skName.toLowerCase()}`] = skValue;
|
|
287
|
+
}
|
|
261
288
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
ean[`#${
|
|
265
|
-
|
|
266
|
-
|
|
289
|
+
let FilterExpression;
|
|
290
|
+
if (params.filterName && params.filterValue !== undefined) {
|
|
291
|
+
ean[`#${params.filterName.toLowerCase()}`] = params.filterName;
|
|
292
|
+
eav[`:${params.filterName.toLowerCase()}`] = params.filterValue;
|
|
293
|
+
FilterExpression =
|
|
294
|
+
params.filterOperator === 'contains'
|
|
295
|
+
? `contains(#${params.filterName.toLowerCase()}, :${params.filterName.toLowerCase()})`
|
|
296
|
+
: `#${params.filterName.toLowerCase()} ${(_f = params.filterOperator) !== null && _f !== void 0 ? _f : '='} :${params.filterName.toLowerCase()}`;
|
|
267
297
|
}
|
|
298
|
+
const items = [];
|
|
299
|
+
let { startKey } = params;
|
|
300
|
+
do {
|
|
301
|
+
const queryParams = new lib_dynamodb_1.QueryCommand({
|
|
302
|
+
TableName: params.tableName,
|
|
303
|
+
KeyConditionExpression: kce,
|
|
304
|
+
ExpressionAttributeNames: ean,
|
|
305
|
+
ExpressionAttributeValues: eav,
|
|
306
|
+
ScanIndexForward: (_g = params.sortAscending) !== null && _g !== void 0 ? _g : true,
|
|
307
|
+
Limit: params.limit,
|
|
308
|
+
IndexName: params.indexName,
|
|
309
|
+
ExclusiveStartKey: startKey,
|
|
310
|
+
FilterExpression,
|
|
311
|
+
});
|
|
312
|
+
const result = yield withRetry(() => exports.dynamoDb.send(queryParams), 'queryDynamo');
|
|
313
|
+
if (result.Items) {
|
|
314
|
+
items.push(...result.Items);
|
|
315
|
+
}
|
|
316
|
+
startKey = result.LastEvaluatedKey;
|
|
317
|
+
if (params.limit && items.length >= params.limit) {
|
|
318
|
+
return {
|
|
319
|
+
data: items.slice(0, params.limit),
|
|
320
|
+
startKey,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
} while (startKey && Object.keys(startKey).length > 0);
|
|
324
|
+
return { data: items };
|
|
268
325
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
ean[`#${filterName.toLowerCase()}`] = filterName;
|
|
272
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
273
|
-
eav[`:${filterName.toLowerCase()}`] = filterValue;
|
|
274
|
-
FilterExpression = `#${filterName.toLowerCase()} ${filterOperator} :${filterName.toLowerCase()}`;
|
|
275
|
-
if (filterOperator === 'contains') {
|
|
276
|
-
FilterExpression = `contains(#${filterName.toLowerCase()}, :${filterName.toLowerCase()})`;
|
|
277
|
-
}
|
|
326
|
+
catch (e) {
|
|
327
|
+
return { error: e.toString() };
|
|
278
328
|
}
|
|
279
|
-
const Items = [];
|
|
280
|
-
do {
|
|
281
|
-
const params = new lib_dynamodb_1.QueryCommand(Object.assign(Object.assign(Object.assign(Object.assign({ TableName: tableName, KeyConditionExpression: kce, ExpressionAttributeNames: ean, ExpressionAttributeValues: eav, ScanIndexForward: sortAscending }, (limit !== null && { Limit: limit || 1000 })), (indexName && { IndexName: indexName })), (startKey && {
|
|
282
|
-
ExclusiveStartKey: startKey,
|
|
283
|
-
})), (FilterExpression && { FilterExpression })));
|
|
284
|
-
let lek;
|
|
285
|
-
let newItems;
|
|
286
|
-
try {
|
|
287
|
-
({
|
|
288
|
-
Items: newItems,
|
|
289
|
-
LastEvaluatedKey: lek,
|
|
290
|
-
// eslint-disable-next-line no-await-in-loop
|
|
291
|
-
} = yield exports.dynamoDb.send(params));
|
|
292
|
-
if (newItems) {
|
|
293
|
-
Items.push(...newItems.map((i) => i));
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
catch (e) {
|
|
297
|
-
(0, log_1.warn)('error. query params=', JSON.stringify(params), e);
|
|
298
|
-
return { error: e.toString() };
|
|
299
|
-
}
|
|
300
|
-
startKey = lek;
|
|
301
|
-
(0, log_1.debug)(`dynamo query against ${params.input.TableName} ok, count=${newItems === null || newItems === void 0 ? void 0 : newItems.length} ${JSON.stringify(params)}`, ` next startkey=${startKey}`);
|
|
302
|
-
if (!!limit && Items.length > limit) {
|
|
303
|
-
return { data: Items, startKey };
|
|
304
|
-
}
|
|
305
|
-
} while (startKey && Object.keys(startKey).length > 0);
|
|
306
|
-
return { data: Items };
|
|
307
329
|
});
|
|
308
330
|
exports.queryDynamo = queryDynamo;
|
|
309
|
-
const getDynamoTtlDays = (days) => Math.ceil(
|
|
331
|
+
const getDynamoTtlDays = (days) => Math.ceil(Date.now() / 1000) + days * 86400;
|
|
310
332
|
exports.getDynamoTtlDays = getDynamoTtlDays;
|
|
311
|
-
const getDynamoTtlMinutes = (
|
|
333
|
+
const getDynamoTtlMinutes = (minutes) => Math.ceil(Date.now() / 1000) + minutes * 60;
|
|
312
334
|
exports.getDynamoTtlMinutes = getDynamoTtlMinutes;
|
|
313
335
|
const wipeTable = (tableName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
336
|
+
var _h, _j, _k;
|
|
314
337
|
try {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// @ts-ignore
|
|
318
|
-
let keyHash = infoV.Table.KeySchema.find((k) => k.KeyType === 'HASH').AttributeName;
|
|
338
|
+
const info = yield withRetry(() => exports.dynamoDb.send(new client_dynamodb_1.DescribeTableCommand({ TableName: tableName })), 'wipeTable-describe');
|
|
339
|
+
const keyHash = (_k = (_j = (_h = info.Table) === null || _h === void 0 ? void 0 : _h.KeySchema) === null || _j === void 0 ? void 0 : _j.find((k) => k.KeyType === 'HASH')) === null || _k === void 0 ? void 0 : _k.AttributeName;
|
|
319
340
|
if (!keyHash) {
|
|
320
|
-
throw new Error('
|
|
321
|
-
}
|
|
322
|
-
let allraw = yield (0, exports.scan)(tableName);
|
|
323
|
-
if ('error' in allraw) {
|
|
324
|
-
throw allraw.error;
|
|
341
|
+
throw new Error('Could not find hash key');
|
|
325
342
|
}
|
|
326
343
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
327
|
-
|
|
328
|
-
(
|
|
344
|
+
const scanResult = yield (0, exports.scan)(tableName);
|
|
345
|
+
if (isError(scanResult)) {
|
|
346
|
+
throw new Error(scanResult.error);
|
|
347
|
+
}
|
|
329
348
|
yield (0, exports.batchDelete)({
|
|
330
349
|
tableName,
|
|
331
|
-
keys:
|
|
332
|
-
pkName:
|
|
350
|
+
keys: scanResult.data.map((item) => item[keyHash]),
|
|
351
|
+
pkName: keyHash,
|
|
333
352
|
});
|
|
334
|
-
|
|
335
|
-
return {};
|
|
353
|
+
return { data: undefined };
|
|
336
354
|
}
|
|
337
355
|
catch (e) {
|
|
338
|
-
(0, log_1.warn)('wipeTable error:', e);
|
|
339
356
|
return { error: e.toString() };
|
|
340
357
|
}
|
|
341
358
|
});
|
|
342
359
|
exports.wipeTable = wipeTable;
|
|
343
|
-
|
|
344
|
-
const getDynamoUpdates = (item, opt) => {
|
|
360
|
+
const getDynamoUpdates = (item, options) => {
|
|
345
361
|
var _a;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
let UpdateExpression = `SET `;
|
|
362
|
+
const excludeKeys = ((_a = options === null || options === void 0 ? void 0 : options.excludeKeys) !== null && _a !== void 0 ? _a : ['PK']).map((k) => k.toLowerCase());
|
|
363
|
+
const validEntries = Object.entries(item).filter(([key, value]) => !excludeKeys.includes(key.toLowerCase()) && value != null);
|
|
349
364
|
const ExpressionAttributeNames = {};
|
|
350
365
|
const ExpressionAttributeValues = {};
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
UpdateExpression += `#${k} = :${k}, `;
|
|
357
|
-
ExpressionAttributeNames[`#${k}`] = k;
|
|
358
|
-
ExpressionAttributeValues[`:${k}`] = v;
|
|
359
|
-
});
|
|
366
|
+
const UpdateExpression = validEntries.reduce((expr, [key, value], index) => {
|
|
367
|
+
ExpressionAttributeNames[`#${key}`] = key;
|
|
368
|
+
ExpressionAttributeValues[`:${key}`] = value;
|
|
369
|
+
return `${expr}${index > 0 ? ', ' : ''}#${key} = :${key}`;
|
|
370
|
+
}, 'SET ');
|
|
360
371
|
return {
|
|
361
|
-
UpdateExpression
|
|
372
|
+
UpdateExpression,
|
|
362
373
|
ExpressionAttributeNames,
|
|
363
374
|
ExpressionAttributeValues,
|
|
364
375
|
ReturnValues: 'UPDATED_NEW',
|
package/package.json
CHANGED