@aligent/aws-wrappers 0.1.0 → 0.1.2
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/cjs/index.cjs +1911 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/package.json +51 -0
- package/cjs/src/index.d.ts +7 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.mjs +1903 -0
- package/esm/package.json +51 -0
- package/esm/src/dynamodb/dynamodb.d.ts +127 -0
- package/esm/src/index.d.ts +7 -0
- package/esm/src/s3/s3.d.ts +131 -0
- package/esm/src/secrets-manager/secrets-manager.d.ts +78 -0
- package/esm/src/sfn/sfn.d.ts +38 -0
- package/esm/src/sns/sns.d.ts +48 -0
- package/esm/src/sqs/sqs.d.ts +60 -0
- package/esm/src/ssm/ssm.d.ts +84 -0
- package/{src/util/redact.js → esm/src/util/redact.d.ts} +2 -13
- package/esm/src/util/truncate.d.ts +15 -0
- package/package.json +35 -17
- package/CLAUDE.md +0 -173
- package/src/dynamodb/dynamodb.js +0 -308
- package/src/index.d.ts +0 -7
- package/src/index.js +0 -17
- package/src/s3/s3.js +0 -244
- package/src/secrets-manager/secrets-manager.js +0 -152
- package/src/sfn/sfn.js +0 -74
- package/src/sns/sns.js +0 -110
- package/src/sqs/sqs.js +0 -134
- package/src/ssm/ssm.js +0 -144
- package/src/util/truncate.js +0 -36
- package/tsconfig.lib.tsbuildinfo +0 -1
- /package/{src → cjs/src}/dynamodb/dynamodb.d.ts +0 -0
- /package/{src → cjs/src}/s3/s3.d.ts +0 -0
- /package/{src → cjs/src}/secrets-manager/secrets-manager.d.ts +0 -0
- /package/{src → cjs/src}/sfn/sfn.d.ts +0 -0
- /package/{src → cjs/src}/sns/sns.d.ts +0 -0
- /package/{src → cjs/src}/sqs/sqs.d.ts +0 -0
- /package/{src → cjs/src}/ssm/ssm.d.ts +0 -0
- /package/{src → cjs/src}/util/redact.d.ts +0 -0
- /package/{src → cjs/src}/util/truncate.d.ts +0 -0
package/esm/index.mjs
ADDED
|
@@ -0,0 +1,1903 @@
|
|
|
1
|
+
import { Logger } from '@aws-lambda-powertools/logger';
|
|
2
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
3
|
+
import { DynamoDBDocumentClient, paginateQuery, paginateScan, GetCommand, PutCommand, UpdateCommand, DeleteCommand, QueryCommand, ScanCommand, BatchGetCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
|
|
4
|
+
import xray from 'aws-xray-sdk-core';
|
|
5
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, CopyObjectCommand, paginateListObjectsV2, DeleteObjectCommand, DeleteObjectsCommand } from '@aws-sdk/client-s3';
|
|
6
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
7
|
+
import { SecretsManagerClient, CreateSecretCommand, UpdateSecretCommand, PutSecretValueCommand, DeleteSecretCommand, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
|
|
8
|
+
import { SFNClient, paginateListExecutions, StartExecutionCommand, DescribeExecutionCommand, StopExecutionCommand } from '@aws-sdk/client-sfn';
|
|
9
|
+
import { SNSClient, PublishCommand, PublishBatchCommand } from '@aws-sdk/client-sns';
|
|
10
|
+
import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand, SendMessageBatchCommand, DeleteMessageBatchCommand } from '@aws-sdk/client-sqs';
|
|
11
|
+
import { SSMClient, GetParameterCommand, GetParametersCommand, PutParameterCommand, DeleteParameterCommand, paginateGetParametersByPath } from '@aws-sdk/client-ssm';
|
|
12
|
+
|
|
13
|
+
/******************************************************************************
|
|
14
|
+
Copyright (c) Microsoft Corporation.
|
|
15
|
+
|
|
16
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
17
|
+
purpose with or without fee is hereby granted.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
20
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
21
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
22
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
23
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
24
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
25
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
26
|
+
***************************************************************************** */
|
|
27
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
var __assign = function() {
|
|
31
|
+
__assign = Object.assign || function __assign(t) {
|
|
32
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
33
|
+
s = arguments[i];
|
|
34
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
35
|
+
}
|
|
36
|
+
return t;
|
|
37
|
+
};
|
|
38
|
+
return __assign.apply(this, arguments);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
42
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
43
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
44
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
45
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
46
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
47
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function __generator(thisArg, body) {
|
|
52
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
53
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
54
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
55
|
+
function step(op) {
|
|
56
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
57
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
58
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
59
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
60
|
+
switch (op[0]) {
|
|
61
|
+
case 0: case 1: t = op; break;
|
|
62
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
63
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
64
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
65
|
+
default:
|
|
66
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
67
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
68
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
69
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
70
|
+
if (t[2]) _.ops.pop();
|
|
71
|
+
_.trys.pop(); continue;
|
|
72
|
+
}
|
|
73
|
+
op = body.call(thisArg, _);
|
|
74
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
75
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function __values(o) {
|
|
80
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
81
|
+
if (m) return m.call(o);
|
|
82
|
+
if (o && typeof o.length === "number") return {
|
|
83
|
+
next: function () {
|
|
84
|
+
if (o && i >= o.length) o = void 0;
|
|
85
|
+
return { value: o && o[i++], done: !o };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function __await(v) {
|
|
92
|
+
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
96
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
97
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
98
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
99
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
100
|
+
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]); } }
|
|
101
|
+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
102
|
+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
103
|
+
function fulfill(value) { resume("next", value); }
|
|
104
|
+
function reject(value) { resume("throw", value); }
|
|
105
|
+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function __asyncValues(o) {
|
|
109
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
110
|
+
var m = o[Symbol.asyncIterator], i;
|
|
111
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
112
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
113
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
117
|
+
var e = new Error(message);
|
|
118
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Return a log-safe projection of `input` based on the logger's configured level.
|
|
123
|
+
*
|
|
124
|
+
* At `DEBUG`, the full input is returned unchanged — operators who set
|
|
125
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` (or call `logger.setLogLevel('DEBUG')`) have
|
|
126
|
+
* explicitly opted into seeing everything, including payloads, secret material
|
|
127
|
+
* and PII.
|
|
128
|
+
*
|
|
129
|
+
* At any other level, only the fields listed in `safeFields` are included.
|
|
130
|
+
* Missing fields are silently skipped — the result type narrows to
|
|
131
|
+
* `Pick<T, K>` accordingly.
|
|
132
|
+
*
|
|
133
|
+
* Used across the package so that the "what's safe to log at INFO" decision
|
|
134
|
+
* lives in one place. See `packages/aws-wrappers/CLAUDE.md` ("Logging") for
|
|
135
|
+
* the design rationale and conventions on building the safe-field lists.
|
|
136
|
+
*/
|
|
137
|
+
function filterFieldsForLogLevel(logger, input, safeFields) {
|
|
138
|
+
if (logger.getLevelName() === 'DEBUG')
|
|
139
|
+
return input;
|
|
140
|
+
var out = {};
|
|
141
|
+
for (var _i = 0, safeFields_1 = safeFields; _i < safeFields_1.length; _i++) {
|
|
142
|
+
var key = safeFields_1[_i];
|
|
143
|
+
if (key in input)
|
|
144
|
+
out[key] = input[key];
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var BATCH_WRITE_MAX_ATTEMPTS = 5;
|
|
150
|
+
var BATCH_WRITE_BASE_DELAY_MS = 200;
|
|
151
|
+
/**
|
|
152
|
+
* Fields safe to log at INFO. Omits `Key` (may carry customer IDs / tenant IDs).
|
|
153
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
154
|
+
*/
|
|
155
|
+
var GET_ITEM_SAFE_FIELDS = [
|
|
156
|
+
'TableName',
|
|
157
|
+
'ConsistentRead',
|
|
158
|
+
'ProjectionExpression',
|
|
159
|
+
'ReturnConsumedCapacity',
|
|
160
|
+
'ExpressionAttributeNames',
|
|
161
|
+
];
|
|
162
|
+
/**
|
|
163
|
+
* Fields safe to log at INFO. Omits `Item` (the payload itself) and
|
|
164
|
+
* `ExpressionAttributeValues` (values bound to ConditionExpression, often PII).
|
|
165
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
166
|
+
*/
|
|
167
|
+
var PUT_ITEM_SAFE_FIELDS = [
|
|
168
|
+
'TableName',
|
|
169
|
+
'ConditionExpression',
|
|
170
|
+
'ExpressionAttributeNames',
|
|
171
|
+
'ReturnValues',
|
|
172
|
+
'ReturnConsumedCapacity',
|
|
173
|
+
'ReturnItemCollectionMetrics',
|
|
174
|
+
'ReturnValuesOnConditionCheckFailure',
|
|
175
|
+
];
|
|
176
|
+
/**
|
|
177
|
+
* Fields safe to log at INFO. Omits `Key` and `ExpressionAttributeValues`.
|
|
178
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
179
|
+
*/
|
|
180
|
+
var UPDATE_ITEM_SAFE_FIELDS = [
|
|
181
|
+
'TableName',
|
|
182
|
+
'UpdateExpression',
|
|
183
|
+
'ConditionExpression',
|
|
184
|
+
'ExpressionAttributeNames',
|
|
185
|
+
'ReturnValues',
|
|
186
|
+
'ReturnConsumedCapacity',
|
|
187
|
+
'ReturnItemCollectionMetrics',
|
|
188
|
+
'ReturnValuesOnConditionCheckFailure',
|
|
189
|
+
];
|
|
190
|
+
/**
|
|
191
|
+
* Fields safe to log at INFO. Omits `Key` and `ExpressionAttributeValues`
|
|
192
|
+
* (the latter binds to ConditionExpression and may carry PII).
|
|
193
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
194
|
+
*/
|
|
195
|
+
var DELETE_ITEM_SAFE_FIELDS = [
|
|
196
|
+
'TableName',
|
|
197
|
+
'ConditionExpression',
|
|
198
|
+
'ExpressionAttributeNames',
|
|
199
|
+
'ReturnValues',
|
|
200
|
+
'ReturnConsumedCapacity',
|
|
201
|
+
'ReturnItemCollectionMetrics',
|
|
202
|
+
'ReturnValuesOnConditionCheckFailure',
|
|
203
|
+
];
|
|
204
|
+
/**
|
|
205
|
+
* Fields safe to log at INFO for `query` and `paginateItems`. Omits
|
|
206
|
+
* `ExpressionAttributeValues` (values often carry PII) and `ExclusiveStartKey`
|
|
207
|
+
* (pagination cursor includes Key shape). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks
|
|
208
|
+
* the full input.
|
|
209
|
+
*/
|
|
210
|
+
var QUERY_SAFE_FIELDS = [
|
|
211
|
+
'TableName',
|
|
212
|
+
'IndexName',
|
|
213
|
+
'KeyConditionExpression',
|
|
214
|
+
'FilterExpression',
|
|
215
|
+
'ProjectionExpression',
|
|
216
|
+
'ExpressionAttributeNames',
|
|
217
|
+
'ConsistentRead',
|
|
218
|
+
'ScanIndexForward',
|
|
219
|
+
'Select',
|
|
220
|
+
'Limit',
|
|
221
|
+
'ReturnConsumedCapacity',
|
|
222
|
+
];
|
|
223
|
+
/**
|
|
224
|
+
* Fields safe to log at INFO for `scan` and `paginateScan`. Omits
|
|
225
|
+
* `ExpressionAttributeValues` and `ExclusiveStartKey`.
|
|
226
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
227
|
+
*/
|
|
228
|
+
var SCAN_SAFE_FIELDS = [
|
|
229
|
+
'TableName',
|
|
230
|
+
'IndexName',
|
|
231
|
+
'FilterExpression',
|
|
232
|
+
'ProjectionExpression',
|
|
233
|
+
'ExpressionAttributeNames',
|
|
234
|
+
'ConsistentRead',
|
|
235
|
+
'Select',
|
|
236
|
+
'Limit',
|
|
237
|
+
'Segment',
|
|
238
|
+
'TotalSegments',
|
|
239
|
+
'ReturnConsumedCapacity',
|
|
240
|
+
];
|
|
241
|
+
var sleep = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
|
|
242
|
+
var backoffDelay = function (attempt) {
|
|
243
|
+
var exp = BATCH_WRITE_BASE_DELAY_MS * Math.pow(2, attempt);
|
|
244
|
+
return exp + Math.random() * exp;
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* Wrapper around the AWS DynamoDB Document client providing structured
|
|
248
|
+
* Powertools logging and X-Ray tracing by default.
|
|
249
|
+
*
|
|
250
|
+
* Items are automatically marshalled / unmarshalled via the document client —
|
|
251
|
+
* callers work with plain TypeScript objects in both directions.
|
|
252
|
+
*/
|
|
253
|
+
var DynamoDBService = /** @class */ (function () {
|
|
254
|
+
/**
|
|
255
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
256
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
257
|
+
* @param opts.client - Optional pre-configured `DynamoDBDocumentClient`.
|
|
258
|
+
* When supplied, the wrapper does not apply X-Ray instrumentation. When
|
|
259
|
+
* omitted, a default `DynamoDBClient` is wrapped with `captureAWSv3Client`
|
|
260
|
+
* *before* being passed to `DynamoDBDocumentClient.from`, so X-Ray
|
|
261
|
+
* tracing captures every DynamoDB call.
|
|
262
|
+
*/
|
|
263
|
+
function DynamoDBService(opts) {
|
|
264
|
+
var _a, _b;
|
|
265
|
+
this.client =
|
|
266
|
+
(_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : DynamoDBDocumentClient.from(xray.captureAWSv3Client(new DynamoDBClient({})), {
|
|
267
|
+
marshallOptions: { removeUndefinedValues: true },
|
|
268
|
+
});
|
|
269
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get an item from DynamoDB.
|
|
273
|
+
* @template K - Shape of the partition / sort key.
|
|
274
|
+
* @template R - Expected unmarshalled item shape.
|
|
275
|
+
* @returns The item, or `undefined` if not found.
|
|
276
|
+
*/
|
|
277
|
+
DynamoDBService.prototype.getItem = function (input) {
|
|
278
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
279
|
+
var response;
|
|
280
|
+
return __generator(this, function (_a) {
|
|
281
|
+
switch (_a.label) {
|
|
282
|
+
case 0:
|
|
283
|
+
this.logger.info('Getting DynamoDB item', {
|
|
284
|
+
input: filterFieldsForLogLevel(this.logger, input, GET_ITEM_SAFE_FIELDS),
|
|
285
|
+
});
|
|
286
|
+
return [4 /*yield*/, this.client.send(new GetCommand(input))];
|
|
287
|
+
case 1:
|
|
288
|
+
response = _a.sent();
|
|
289
|
+
return [2 /*return*/, response.Item];
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Put an item into DynamoDB. The caller's `Item` is typed as `T`, which
|
|
296
|
+
* the document client marshalls automatically.
|
|
297
|
+
* @template T - Type of the item being stored.
|
|
298
|
+
*/
|
|
299
|
+
DynamoDBService.prototype.putItem = function (input) {
|
|
300
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
301
|
+
return __generator(this, function (_a) {
|
|
302
|
+
this.logger.info('Putting DynamoDB item', {
|
|
303
|
+
input: filterFieldsForLogLevel(this.logger, input, PUT_ITEM_SAFE_FIELDS),
|
|
304
|
+
});
|
|
305
|
+
return [2 /*return*/, this.client.send(new PutCommand(input))];
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* Update an item in DynamoDB. The `Attributes` field on the response is
|
|
311
|
+
* typed as `R` — the caller should choose `R` to match their
|
|
312
|
+
* `ReturnValues` setting:
|
|
313
|
+
* - `NONE` (default): no `Attributes` returned.
|
|
314
|
+
* - `ALL_OLD` / `ALL_NEW`: full item.
|
|
315
|
+
* - `UPDATED_OLD` / `UPDATED_NEW`: only updated attributes (partial).
|
|
316
|
+
* @template K - Shape of the partition / sort key.
|
|
317
|
+
* @template R - Expected shape of the returned `Attributes`.
|
|
318
|
+
*/
|
|
319
|
+
DynamoDBService.prototype.updateItem = function (input) {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
321
|
+
var response;
|
|
322
|
+
return __generator(this, function (_a) {
|
|
323
|
+
switch (_a.label) {
|
|
324
|
+
case 0:
|
|
325
|
+
this.logger.info('Updating DynamoDB item', {
|
|
326
|
+
input: filterFieldsForLogLevel(this.logger, input, UPDATE_ITEM_SAFE_FIELDS),
|
|
327
|
+
});
|
|
328
|
+
return [4 /*yield*/, this.client.send(new UpdateCommand(input))];
|
|
329
|
+
case 1:
|
|
330
|
+
response = _a.sent();
|
|
331
|
+
return [2 /*return*/, response];
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Delete an item from DynamoDB. The `Attributes` field on the response is
|
|
338
|
+
* typed as `R` — relevant when `ReturnValues: 'ALL_OLD'` is set.
|
|
339
|
+
* @template K - Shape of the partition / sort key.
|
|
340
|
+
* @template R - Expected shape of the returned `Attributes`.
|
|
341
|
+
*/
|
|
342
|
+
DynamoDBService.prototype.deleteItem = function (input) {
|
|
343
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
344
|
+
var response;
|
|
345
|
+
return __generator(this, function (_a) {
|
|
346
|
+
switch (_a.label) {
|
|
347
|
+
case 0:
|
|
348
|
+
this.logger.info('Deleting DynamoDB item', {
|
|
349
|
+
input: filterFieldsForLogLevel(this.logger, input, DELETE_ITEM_SAFE_FIELDS),
|
|
350
|
+
});
|
|
351
|
+
return [4 /*yield*/, this.client.send(new DeleteCommand(input))];
|
|
352
|
+
case 1:
|
|
353
|
+
response = _a.sent();
|
|
354
|
+
return [2 /*return*/, response];
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* Execute a DynamoDB Query. The full `QueryCommandOutput` is returned with
|
|
361
|
+
* `Items` typed as `T[]` so callers retain pagination metadata
|
|
362
|
+
* (`LastEvaluatedKey`, `Count`, etc.).
|
|
363
|
+
* @template T - Expected shape of each unmarshalled item.
|
|
364
|
+
*/
|
|
365
|
+
DynamoDBService.prototype.query = function (input) {
|
|
366
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
367
|
+
var response;
|
|
368
|
+
return __generator(this, function (_a) {
|
|
369
|
+
switch (_a.label) {
|
|
370
|
+
case 0:
|
|
371
|
+
this.logger.info('Querying DynamoDB', {
|
|
372
|
+
input: filterFieldsForLogLevel(this.logger, input, QUERY_SAFE_FIELDS),
|
|
373
|
+
});
|
|
374
|
+
return [4 /*yield*/, this.client.send(new QueryCommand(input))];
|
|
375
|
+
case 1:
|
|
376
|
+
response = _a.sent();
|
|
377
|
+
return [2 /*return*/, response];
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Scan a DynamoDB table. The full `ScanCommandOutput` is returned with
|
|
384
|
+
* `Items` typed as `T[]` so callers retain pagination metadata.
|
|
385
|
+
*
|
|
386
|
+
* Scan reads every item in the table, so cost and latency grow linearly
|
|
387
|
+
* with table size; it is rarely the right tool in a runtime service.
|
|
388
|
+
* Prefer, in order:
|
|
389
|
+
*
|
|
390
|
+
* 1. `query` with the table's partition key.
|
|
391
|
+
* 2. `query` against a GSI or LSI whose key matches your access pattern.
|
|
392
|
+
* 3. A sparse GSI populated only for the items you need to enumerate.
|
|
393
|
+
* 4. A denormalised lookup item or table maintained on write.
|
|
394
|
+
*
|
|
395
|
+
* Legitimate scan use cases are mostly one-off admin work (export,
|
|
396
|
+
* migration, audit). For those, prefer the AWS CLI or Console rather than
|
|
397
|
+
* embedding a scan in a Lambda.
|
|
398
|
+
*
|
|
399
|
+
* @template T - Expected shape of each unmarshalled item.
|
|
400
|
+
*/
|
|
401
|
+
DynamoDBService.prototype.scan = function (input) {
|
|
402
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
403
|
+
var response;
|
|
404
|
+
return __generator(this, function (_a) {
|
|
405
|
+
switch (_a.label) {
|
|
406
|
+
case 0:
|
|
407
|
+
this.logger.info('Scanning DynamoDB', {
|
|
408
|
+
input: filterFieldsForLogLevel(this.logger, input, SCAN_SAFE_FIELDS),
|
|
409
|
+
});
|
|
410
|
+
return [4 /*yield*/, this.client.send(new ScanCommand(input))];
|
|
411
|
+
case 1:
|
|
412
|
+
response = _a.sent();
|
|
413
|
+
return [2 /*return*/, response];
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Batch-get items from one or more DynamoDB tables.
|
|
420
|
+
*
|
|
421
|
+
* Note: this method is intentionally **not** generic. `BatchGet`'s
|
|
422
|
+
* `Responses` field is a multi-table `Record<string, item[]>` whose item
|
|
423
|
+
* shapes can differ per table — no single `T` can soundly describe it.
|
|
424
|
+
* Callers should narrow the result type at the call site.
|
|
425
|
+
*/
|
|
426
|
+
DynamoDBService.prototype.batchGet = function (input) {
|
|
427
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
428
|
+
var isDebug;
|
|
429
|
+
var _a;
|
|
430
|
+
return __generator(this, function (_b) {
|
|
431
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
432
|
+
this.logger.info('Batch getting DynamoDB items', {
|
|
433
|
+
input: isDebug ? input : { tables: Object.keys((_a = input.RequestItems) !== null && _a !== void 0 ? _a : {}) },
|
|
434
|
+
});
|
|
435
|
+
return [2 /*return*/, this.client.send(new BatchGetCommand(input))];
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* Batch-write items to DynamoDB, retrying `UnprocessedItems` with jittered
|
|
441
|
+
* exponential backoff. Up to 5 attempts (200ms base delay). Throws when
|
|
442
|
+
* items remain unprocessed after the final attempt.
|
|
443
|
+
*/
|
|
444
|
+
DynamoDBService.prototype.batchWrite = function (input) {
|
|
445
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
446
|
+
var isDebug, current, attempt, response, unprocessed;
|
|
447
|
+
var _a;
|
|
448
|
+
return __generator(this, function (_b) {
|
|
449
|
+
switch (_b.label) {
|
|
450
|
+
case 0:
|
|
451
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
452
|
+
this.logger.info('Batch writing DynamoDB items', {
|
|
453
|
+
input: isDebug ? input : { tables: Object.keys((_a = input.RequestItems) !== null && _a !== void 0 ? _a : {}) },
|
|
454
|
+
});
|
|
455
|
+
current = input;
|
|
456
|
+
attempt = 0;
|
|
457
|
+
_b.label = 1;
|
|
458
|
+
case 1:
|
|
459
|
+
if (!(attempt < BATCH_WRITE_MAX_ATTEMPTS)) return [3 /*break*/, 6];
|
|
460
|
+
return [4 /*yield*/, this.client.send(new BatchWriteCommand(current))];
|
|
461
|
+
case 2:
|
|
462
|
+
response = _b.sent();
|
|
463
|
+
unprocessed = response.UnprocessedItems;
|
|
464
|
+
if (!unprocessed || Object.keys(unprocessed).length === 0)
|
|
465
|
+
return [2 /*return*/, response];
|
|
466
|
+
this.logger.warn('Retrying unprocessed DynamoDB items', {
|
|
467
|
+
attempt: attempt + 1,
|
|
468
|
+
tables: Object.keys(unprocessed),
|
|
469
|
+
});
|
|
470
|
+
if (!(attempt < BATCH_WRITE_MAX_ATTEMPTS - 1)) return [3 /*break*/, 4];
|
|
471
|
+
return [4 /*yield*/, sleep(backoffDelay(attempt))];
|
|
472
|
+
case 3:
|
|
473
|
+
_b.sent();
|
|
474
|
+
_b.label = 4;
|
|
475
|
+
case 4:
|
|
476
|
+
current = __assign(__assign({}, input), { RequestItems: unprocessed });
|
|
477
|
+
_b.label = 5;
|
|
478
|
+
case 5:
|
|
479
|
+
attempt++;
|
|
480
|
+
return [3 /*break*/, 1];
|
|
481
|
+
case 6: throw new Error("batchWrite failed after ".concat(BATCH_WRITE_MAX_ATTEMPTS, " attempts"));
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Paginate over Query results, yielding one unmarshalled item at a time.
|
|
488
|
+
* @template T - Expected shape of each yielded item.
|
|
489
|
+
*/
|
|
490
|
+
DynamoDBService.prototype.paginateItems = function (input) {
|
|
491
|
+
return __asyncGenerator(this, arguments, function paginateItems_1() {
|
|
492
|
+
var paginator, _a, paginator_1, paginator_1_1, page, _i, _b, item, e_1_1;
|
|
493
|
+
var _c, e_1, _d, _e;
|
|
494
|
+
return __generator(this, function (_f) {
|
|
495
|
+
switch (_f.label) {
|
|
496
|
+
case 0:
|
|
497
|
+
this.logger.info('Paginating DynamoDB query', {
|
|
498
|
+
input: filterFieldsForLogLevel(this.logger, input, QUERY_SAFE_FIELDS),
|
|
499
|
+
});
|
|
500
|
+
paginator = paginateQuery({ client: this.client }, input);
|
|
501
|
+
_f.label = 1;
|
|
502
|
+
case 1:
|
|
503
|
+
_f.trys.push([1, 10, 11, 16]);
|
|
504
|
+
_a = true, paginator_1 = __asyncValues(paginator);
|
|
505
|
+
_f.label = 2;
|
|
506
|
+
case 2: return [4 /*yield*/, __await(paginator_1.next())];
|
|
507
|
+
case 3:
|
|
508
|
+
if (!(paginator_1_1 = _f.sent(), _c = paginator_1_1.done, !_c)) return [3 /*break*/, 9];
|
|
509
|
+
_e = paginator_1_1.value;
|
|
510
|
+
_a = false;
|
|
511
|
+
page = _e;
|
|
512
|
+
if (!page.Items)
|
|
513
|
+
return [3 /*break*/, 8];
|
|
514
|
+
_i = 0, _b = page.Items;
|
|
515
|
+
_f.label = 4;
|
|
516
|
+
case 4:
|
|
517
|
+
if (!(_i < _b.length)) return [3 /*break*/, 8];
|
|
518
|
+
item = _b[_i];
|
|
519
|
+
return [4 /*yield*/, __await(item)];
|
|
520
|
+
case 5: return [4 /*yield*/, _f.sent()];
|
|
521
|
+
case 6:
|
|
522
|
+
_f.sent();
|
|
523
|
+
_f.label = 7;
|
|
524
|
+
case 7:
|
|
525
|
+
_i++;
|
|
526
|
+
return [3 /*break*/, 4];
|
|
527
|
+
case 8:
|
|
528
|
+
_a = true;
|
|
529
|
+
return [3 /*break*/, 2];
|
|
530
|
+
case 9: return [3 /*break*/, 16];
|
|
531
|
+
case 10:
|
|
532
|
+
e_1_1 = _f.sent();
|
|
533
|
+
e_1 = { error: e_1_1 };
|
|
534
|
+
return [3 /*break*/, 16];
|
|
535
|
+
case 11:
|
|
536
|
+
_f.trys.push([11, , 14, 15]);
|
|
537
|
+
if (!(!_a && !_c && (_d = paginator_1.return))) return [3 /*break*/, 13];
|
|
538
|
+
return [4 /*yield*/, __await(_d.call(paginator_1))];
|
|
539
|
+
case 12:
|
|
540
|
+
_f.sent();
|
|
541
|
+
_f.label = 13;
|
|
542
|
+
case 13: return [3 /*break*/, 15];
|
|
543
|
+
case 14:
|
|
544
|
+
if (e_1) throw e_1.error;
|
|
545
|
+
return [7 /*endfinally*/];
|
|
546
|
+
case 15: return [7 /*endfinally*/];
|
|
547
|
+
case 16: return [2 /*return*/];
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Paginate over Scan results, yielding one unmarshalled item at a time.
|
|
554
|
+
* @template T - Expected shape of each yielded item.
|
|
555
|
+
*/
|
|
556
|
+
DynamoDBService.prototype.paginateScan = function (input) {
|
|
557
|
+
return __asyncGenerator(this, arguments, function paginateScan_1() {
|
|
558
|
+
var paginator, _a, paginator_2, paginator_2_1, page, _i, _b, item, e_2_1;
|
|
559
|
+
var _c, e_2, _d, _e;
|
|
560
|
+
return __generator(this, function (_f) {
|
|
561
|
+
switch (_f.label) {
|
|
562
|
+
case 0:
|
|
563
|
+
this.logger.info('Paginating DynamoDB scan', {
|
|
564
|
+
input: filterFieldsForLogLevel(this.logger, input, SCAN_SAFE_FIELDS),
|
|
565
|
+
});
|
|
566
|
+
paginator = paginateScan({ client: this.client }, input);
|
|
567
|
+
_f.label = 1;
|
|
568
|
+
case 1:
|
|
569
|
+
_f.trys.push([1, 10, 11, 16]);
|
|
570
|
+
_a = true, paginator_2 = __asyncValues(paginator);
|
|
571
|
+
_f.label = 2;
|
|
572
|
+
case 2: return [4 /*yield*/, __await(paginator_2.next())];
|
|
573
|
+
case 3:
|
|
574
|
+
if (!(paginator_2_1 = _f.sent(), _c = paginator_2_1.done, !_c)) return [3 /*break*/, 9];
|
|
575
|
+
_e = paginator_2_1.value;
|
|
576
|
+
_a = false;
|
|
577
|
+
page = _e;
|
|
578
|
+
if (!page.Items)
|
|
579
|
+
return [3 /*break*/, 8];
|
|
580
|
+
_i = 0, _b = page.Items;
|
|
581
|
+
_f.label = 4;
|
|
582
|
+
case 4:
|
|
583
|
+
if (!(_i < _b.length)) return [3 /*break*/, 8];
|
|
584
|
+
item = _b[_i];
|
|
585
|
+
return [4 /*yield*/, __await(item)];
|
|
586
|
+
case 5: return [4 /*yield*/, _f.sent()];
|
|
587
|
+
case 6:
|
|
588
|
+
_f.sent();
|
|
589
|
+
_f.label = 7;
|
|
590
|
+
case 7:
|
|
591
|
+
_i++;
|
|
592
|
+
return [3 /*break*/, 4];
|
|
593
|
+
case 8:
|
|
594
|
+
_a = true;
|
|
595
|
+
return [3 /*break*/, 2];
|
|
596
|
+
case 9: return [3 /*break*/, 16];
|
|
597
|
+
case 10:
|
|
598
|
+
e_2_1 = _f.sent();
|
|
599
|
+
e_2 = { error: e_2_1 };
|
|
600
|
+
return [3 /*break*/, 16];
|
|
601
|
+
case 11:
|
|
602
|
+
_f.trys.push([11, , 14, 15]);
|
|
603
|
+
if (!(!_a && !_c && (_d = paginator_2.return))) return [3 /*break*/, 13];
|
|
604
|
+
return [4 /*yield*/, __await(_d.call(paginator_2))];
|
|
605
|
+
case 12:
|
|
606
|
+
_f.sent();
|
|
607
|
+
_f.label = 13;
|
|
608
|
+
case 13: return [3 /*break*/, 15];
|
|
609
|
+
case 14:
|
|
610
|
+
if (e_2) throw e_2.error;
|
|
611
|
+
return [7 /*endfinally*/];
|
|
612
|
+
case 15: return [7 /*endfinally*/];
|
|
613
|
+
case 16: return [2 /*return*/];
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
};
|
|
618
|
+
return DynamoDBService;
|
|
619
|
+
}());
|
|
620
|
+
|
|
621
|
+
var DEFAULT_PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
622
|
+
var DELETE_OBJECTS_BATCH_LIMIT = 1000;
|
|
623
|
+
/**
|
|
624
|
+
* Fields safe to log at INFO for `putObject`. Omits `Body` (object payload).
|
|
625
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
626
|
+
*/
|
|
627
|
+
var PUT_OBJECT_SAFE_FIELDS = ['Bucket', 'Key'];
|
|
628
|
+
/**
|
|
629
|
+
* Fields safe to log at INFO for `putJsonObject`. Omits `Body` (the
|
|
630
|
+
* unserialised JSON payload). `Metadata` is included (operational labels).
|
|
631
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
632
|
+
*/
|
|
633
|
+
var PUT_JSON_OBJECT_SAFE_FIELDS = [
|
|
634
|
+
'Bucket',
|
|
635
|
+
'Key',
|
|
636
|
+
'Metadata',
|
|
637
|
+
];
|
|
638
|
+
/**
|
|
639
|
+
* Wrapper around the AWS S3 client providing structured Powertools logging
|
|
640
|
+
* and X-Ray tracing by default.
|
|
641
|
+
*
|
|
642
|
+
* Input shapes are intentionally tight (Bucket/Key/Body only). Callers
|
|
643
|
+
* needing SDK-level options not exposed here (server-side encryption,
|
|
644
|
+
* tagging, version IDs) should use `S3Client` directly.
|
|
645
|
+
*/
|
|
646
|
+
var S3Service = /** @class */ (function () {
|
|
647
|
+
/**
|
|
648
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
649
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
650
|
+
* @param opts.client - Optional pre-configured `S3Client`. When supplied,
|
|
651
|
+
* the wrapper does not apply X-Ray instrumentation.
|
|
652
|
+
*/
|
|
653
|
+
function S3Service(opts) {
|
|
654
|
+
var _a, _b;
|
|
655
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new S3Client());
|
|
656
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Put an object into S3.
|
|
660
|
+
*
|
|
661
|
+
* Note: the structured log line only includes `Bucket` and `Key` —
|
|
662
|
+
* `Body` is omitted to avoid spilling large payloads or sensitive
|
|
663
|
+
* content into CloudWatch.
|
|
664
|
+
*
|
|
665
|
+
* @param input - Bucket, Key, and Body of the object to store.
|
|
666
|
+
*/
|
|
667
|
+
S3Service.prototype.putObject = function (input) {
|
|
668
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
669
|
+
return __generator(this, function (_a) {
|
|
670
|
+
this.logger.info('Putting S3 object', {
|
|
671
|
+
input: filterFieldsForLogLevel(this.logger, input, PUT_OBJECT_SAFE_FIELDS),
|
|
672
|
+
});
|
|
673
|
+
return [2 /*return*/, this.client.send(new PutObjectCommand(input))];
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
};
|
|
677
|
+
/**
|
|
678
|
+
* Serialise a value to JSON and store it as an S3 object.
|
|
679
|
+
*
|
|
680
|
+
* Note: the structured log line only includes `Bucket` and `Key` —
|
|
681
|
+
* the JSON-encoded body is omitted to avoid spilling potentially
|
|
682
|
+
* large or sensitive content into CloudWatch.
|
|
683
|
+
*
|
|
684
|
+
* @template T - Type of the value being stored.
|
|
685
|
+
*/
|
|
686
|
+
S3Service.prototype.putJsonObject = function (input) {
|
|
687
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
688
|
+
return __generator(this, function (_a) {
|
|
689
|
+
this.logger.info('Putting S3 JSON object', {
|
|
690
|
+
input: filterFieldsForLogLevel(this.logger, input, PUT_JSON_OBJECT_SAFE_FIELDS),
|
|
691
|
+
});
|
|
692
|
+
return [2 /*return*/, this.client.send(new PutObjectCommand({
|
|
693
|
+
Bucket: input.Bucket,
|
|
694
|
+
Key: input.Key,
|
|
695
|
+
Body: JSON.stringify(input.Body),
|
|
696
|
+
Metadata: input.Metadata,
|
|
697
|
+
}))];
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
702
|
+
* Get an object from S3.
|
|
703
|
+
*/
|
|
704
|
+
S3Service.prototype.getObject = function (input) {
|
|
705
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
706
|
+
return __generator(this, function (_a) {
|
|
707
|
+
this.logger.info('Getting S3 object', { input: input });
|
|
708
|
+
return [2 /*return*/, this.client.send(new GetObjectCommand(input))];
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
};
|
|
712
|
+
/**
|
|
713
|
+
* Get an object from S3 and return its body as a string.
|
|
714
|
+
* @returns The object body as a string, or `undefined` if the response
|
|
715
|
+
* has no body.
|
|
716
|
+
*/
|
|
717
|
+
S3Service.prototype.getObjectBody = function (input) {
|
|
718
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
719
|
+
var response;
|
|
720
|
+
var _a;
|
|
721
|
+
return __generator(this, function (_b) {
|
|
722
|
+
switch (_b.label) {
|
|
723
|
+
case 0:
|
|
724
|
+
this.logger.info('Getting S3 object body', { input: input });
|
|
725
|
+
return [4 /*yield*/, this.client.send(new GetObjectCommand(input))];
|
|
726
|
+
case 1:
|
|
727
|
+
response = _b.sent();
|
|
728
|
+
return [2 /*return*/, (_a = response.Body) === null || _a === void 0 ? void 0 : _a.transformToString()];
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
/**
|
|
734
|
+
* Get an object from S3 and parse it as JSON.
|
|
735
|
+
* @template T - Expected type of the parsed value.
|
|
736
|
+
* @returns The parsed value, or `undefined` if the response has no body.
|
|
737
|
+
* @throws If the body is non-empty and not valid JSON.
|
|
738
|
+
*/
|
|
739
|
+
S3Service.prototype.getJsonObject = function (input) {
|
|
740
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
741
|
+
var response, body;
|
|
742
|
+
var _a;
|
|
743
|
+
return __generator(this, function (_b) {
|
|
744
|
+
switch (_b.label) {
|
|
745
|
+
case 0:
|
|
746
|
+
this.logger.info('Getting S3 JSON object', { input: input });
|
|
747
|
+
return [4 /*yield*/, this.client.send(new GetObjectCommand(input))];
|
|
748
|
+
case 1:
|
|
749
|
+
response = _b.sent();
|
|
750
|
+
return [4 /*yield*/, ((_a = response.Body) === null || _a === void 0 ? void 0 : _a.transformToString())];
|
|
751
|
+
case 2:
|
|
752
|
+
body = _b.sent();
|
|
753
|
+
return [2 /*return*/, body ? JSON.parse(body) : undefined];
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
};
|
|
758
|
+
/**
|
|
759
|
+
* Fetch the metadata for an S3 object without downloading its body.
|
|
760
|
+
*/
|
|
761
|
+
S3Service.prototype.headObject = function (input) {
|
|
762
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
763
|
+
return __generator(this, function (_a) {
|
|
764
|
+
this.logger.info('Fetching S3 object metadata', { input: input });
|
|
765
|
+
return [2 /*return*/, this.client.send(new HeadObjectCommand(input))];
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* Copy an object within S3.
|
|
771
|
+
*/
|
|
772
|
+
S3Service.prototype.copyObject = function (input) {
|
|
773
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
774
|
+
return __generator(this, function (_a) {
|
|
775
|
+
this.logger.info('Copying S3 object', { input: input });
|
|
776
|
+
return [2 /*return*/, this.client.send(new CopyObjectCommand(input))];
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* List object keys under a bucket and optional prefix, auto-paginating
|
|
782
|
+
* across all pages.
|
|
783
|
+
*/
|
|
784
|
+
S3Service.prototype.listObjects = function (bucket, prefix) {
|
|
785
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
786
|
+
var paginator, keys, _a, paginator_1, paginator_1_1, page, _i, _b, object, e_1_1;
|
|
787
|
+
var _c, e_1, _d, _e;
|
|
788
|
+
var _f;
|
|
789
|
+
return __generator(this, function (_g) {
|
|
790
|
+
switch (_g.label) {
|
|
791
|
+
case 0:
|
|
792
|
+
this.logger.info('Listing S3 objects', { input: { bucket: bucket, prefix: prefix } });
|
|
793
|
+
paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket, Prefix: prefix });
|
|
794
|
+
keys = [];
|
|
795
|
+
_g.label = 1;
|
|
796
|
+
case 1:
|
|
797
|
+
_g.trys.push([1, 6, 7, 12]);
|
|
798
|
+
_a = true, paginator_1 = __asyncValues(paginator);
|
|
799
|
+
_g.label = 2;
|
|
800
|
+
case 2: return [4 /*yield*/, paginator_1.next()];
|
|
801
|
+
case 3:
|
|
802
|
+
if (!(paginator_1_1 = _g.sent(), _c = paginator_1_1.done, !_c)) return [3 /*break*/, 5];
|
|
803
|
+
_e = paginator_1_1.value;
|
|
804
|
+
_a = false;
|
|
805
|
+
page = _e;
|
|
806
|
+
for (_i = 0, _b = (_f = page.Contents) !== null && _f !== void 0 ? _f : []; _i < _b.length; _i++) {
|
|
807
|
+
object = _b[_i];
|
|
808
|
+
if (object.Key)
|
|
809
|
+
keys.push(object.Key);
|
|
810
|
+
}
|
|
811
|
+
_g.label = 4;
|
|
812
|
+
case 4:
|
|
813
|
+
_a = true;
|
|
814
|
+
return [3 /*break*/, 2];
|
|
815
|
+
case 5: return [3 /*break*/, 12];
|
|
816
|
+
case 6:
|
|
817
|
+
e_1_1 = _g.sent();
|
|
818
|
+
e_1 = { error: e_1_1 };
|
|
819
|
+
return [3 /*break*/, 12];
|
|
820
|
+
case 7:
|
|
821
|
+
_g.trys.push([7, , 10, 11]);
|
|
822
|
+
if (!(!_a && !_c && (_d = paginator_1.return))) return [3 /*break*/, 9];
|
|
823
|
+
return [4 /*yield*/, _d.call(paginator_1)];
|
|
824
|
+
case 8:
|
|
825
|
+
_g.sent();
|
|
826
|
+
_g.label = 9;
|
|
827
|
+
case 9: return [3 /*break*/, 11];
|
|
828
|
+
case 10:
|
|
829
|
+
if (e_1) throw e_1.error;
|
|
830
|
+
return [7 /*endfinally*/];
|
|
831
|
+
case 11: return [7 /*endfinally*/];
|
|
832
|
+
case 12: return [2 /*return*/, keys];
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* List and JSON-parse every object under a bucket and optional prefix.
|
|
839
|
+
* Auto-paginated. Objects without a body are skipped.
|
|
840
|
+
* @template T - Expected type of each parsed object.
|
|
841
|
+
*/
|
|
842
|
+
S3Service.prototype.getAllObjects = function (bucket, prefix) {
|
|
843
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
844
|
+
var paginator, bodies, _a, paginator_2, paginator_2_1, page, _i, _b, object, response, body, e_2_1;
|
|
845
|
+
var _c, e_2, _d, _e;
|
|
846
|
+
var _f, _g;
|
|
847
|
+
return __generator(this, function (_h) {
|
|
848
|
+
switch (_h.label) {
|
|
849
|
+
case 0:
|
|
850
|
+
this.logger.info('Getting all S3 objects', { input: { bucket: bucket, prefix: prefix } });
|
|
851
|
+
paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket, Prefix: prefix });
|
|
852
|
+
bodies = [];
|
|
853
|
+
_h.label = 1;
|
|
854
|
+
case 1:
|
|
855
|
+
_h.trys.push([1, 10, 11, 16]);
|
|
856
|
+
_a = true, paginator_2 = __asyncValues(paginator);
|
|
857
|
+
_h.label = 2;
|
|
858
|
+
case 2: return [4 /*yield*/, paginator_2.next()];
|
|
859
|
+
case 3:
|
|
860
|
+
if (!(paginator_2_1 = _h.sent(), _c = paginator_2_1.done, !_c)) return [3 /*break*/, 9];
|
|
861
|
+
_e = paginator_2_1.value;
|
|
862
|
+
_a = false;
|
|
863
|
+
page = _e;
|
|
864
|
+
_i = 0, _b = (_f = page.Contents) !== null && _f !== void 0 ? _f : [];
|
|
865
|
+
_h.label = 4;
|
|
866
|
+
case 4:
|
|
867
|
+
if (!(_i < _b.length)) return [3 /*break*/, 8];
|
|
868
|
+
object = _b[_i];
|
|
869
|
+
if (!object.Key)
|
|
870
|
+
return [3 /*break*/, 7];
|
|
871
|
+
return [4 /*yield*/, this.client.send(new GetObjectCommand({ Bucket: bucket, Key: object.Key }))];
|
|
872
|
+
case 5:
|
|
873
|
+
response = _h.sent();
|
|
874
|
+
return [4 /*yield*/, ((_g = response.Body) === null || _g === void 0 ? void 0 : _g.transformToString())];
|
|
875
|
+
case 6:
|
|
876
|
+
body = _h.sent();
|
|
877
|
+
if (body)
|
|
878
|
+
bodies.push(JSON.parse(body));
|
|
879
|
+
_h.label = 7;
|
|
880
|
+
case 7:
|
|
881
|
+
_i++;
|
|
882
|
+
return [3 /*break*/, 4];
|
|
883
|
+
case 8:
|
|
884
|
+
_a = true;
|
|
885
|
+
return [3 /*break*/, 2];
|
|
886
|
+
case 9: return [3 /*break*/, 16];
|
|
887
|
+
case 10:
|
|
888
|
+
e_2_1 = _h.sent();
|
|
889
|
+
e_2 = { error: e_2_1 };
|
|
890
|
+
return [3 /*break*/, 16];
|
|
891
|
+
case 11:
|
|
892
|
+
_h.trys.push([11, , 14, 15]);
|
|
893
|
+
if (!(!_a && !_c && (_d = paginator_2.return))) return [3 /*break*/, 13];
|
|
894
|
+
return [4 /*yield*/, _d.call(paginator_2)];
|
|
895
|
+
case 12:
|
|
896
|
+
_h.sent();
|
|
897
|
+
_h.label = 13;
|
|
898
|
+
case 13: return [3 /*break*/, 15];
|
|
899
|
+
case 14:
|
|
900
|
+
if (e_2) throw e_2.error;
|
|
901
|
+
return [7 /*endfinally*/];
|
|
902
|
+
case 15: return [7 /*endfinally*/];
|
|
903
|
+
case 16: return [2 /*return*/, bodies];
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
};
|
|
908
|
+
/**
|
|
909
|
+
* Generate a presigned URL that callers can use to GET or PUT an S3 object
|
|
910
|
+
* directly, without going through the wrapper. The signing happens against
|
|
911
|
+
* the wrapper's `S3Client`, so callers do not need their own client.
|
|
912
|
+
*
|
|
913
|
+
* GET URLs are signed with `ResponseContentDisposition: 'attachment'` so
|
|
914
|
+
* browsers download the object rather than rendering it in-place.
|
|
915
|
+
*
|
|
916
|
+
* The input shape is an inline object (rather than the `Required<Pick<...>>`
|
|
917
|
+
* pattern used by other S3 methods) because this method wraps two SDK
|
|
918
|
+
* commands rather than one, and `action` / `expiresIn` are wrapper-level
|
|
919
|
+
* concerns with no SDK-input equivalent.
|
|
920
|
+
*
|
|
921
|
+
* @param input.Bucket - The S3 bucket name.
|
|
922
|
+
* @param input.Key - The S3 object key.
|
|
923
|
+
* @param input.action - `'get'` to download, `'put'` to upload.
|
|
924
|
+
* @param input.expiresIn - URL lifetime in seconds. Defaults to 3600 (1 hour).
|
|
925
|
+
*/
|
|
926
|
+
S3Service.prototype.getPresignedUrl = function (input) {
|
|
927
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
928
|
+
var expiresIn, command;
|
|
929
|
+
var _a;
|
|
930
|
+
return __generator(this, function (_b) {
|
|
931
|
+
expiresIn = (_a = input.expiresIn) !== null && _a !== void 0 ? _a : DEFAULT_PRESIGNED_URL_EXPIRES_IN_SECONDS;
|
|
932
|
+
this.logger.info('Generating S3 presigned URL', {
|
|
933
|
+
input: { Bucket: input.Bucket, Key: input.Key, action: input.action, expiresIn: expiresIn },
|
|
934
|
+
});
|
|
935
|
+
command = input.action === 'get'
|
|
936
|
+
? new GetObjectCommand({
|
|
937
|
+
Bucket: input.Bucket,
|
|
938
|
+
Key: input.Key,
|
|
939
|
+
ResponseContentDisposition: 'attachment',
|
|
940
|
+
})
|
|
941
|
+
: new PutObjectCommand({ Bucket: input.Bucket, Key: input.Key });
|
|
942
|
+
return [2 /*return*/, getSignedUrl(this.client, command, { expiresIn: expiresIn })];
|
|
943
|
+
});
|
|
944
|
+
});
|
|
945
|
+
};
|
|
946
|
+
/**
|
|
947
|
+
* Delete a single object from S3.
|
|
948
|
+
*/
|
|
949
|
+
S3Service.prototype.deleteObject = function (input) {
|
|
950
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
951
|
+
return __generator(this, function (_a) {
|
|
952
|
+
this.logger.info('Deleting S3 object', { input: input });
|
|
953
|
+
return [2 /*return*/, this.client.send(new DeleteObjectCommand(input))];
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
};
|
|
957
|
+
/**
|
|
958
|
+
* Delete multiple objects from S3, auto-chunking the request into batches
|
|
959
|
+
* of 1000 keys (the S3-enforced DeleteObjects limit). Returns one output
|
|
960
|
+
* per chunk.
|
|
961
|
+
*/
|
|
962
|
+
S3Service.prototype.deleteObjects = function (bucket, keys) {
|
|
963
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
964
|
+
var isDebug, results, i, batch, _a, _b;
|
|
965
|
+
return __generator(this, function (_c) {
|
|
966
|
+
switch (_c.label) {
|
|
967
|
+
case 0:
|
|
968
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
969
|
+
this.logger.info('Deleting S3 objects', {
|
|
970
|
+
input: isDebug ? { bucket: bucket, keys: keys } : { bucket: bucket, keyCount: keys.length },
|
|
971
|
+
});
|
|
972
|
+
results = [];
|
|
973
|
+
i = 0;
|
|
974
|
+
_c.label = 1;
|
|
975
|
+
case 1:
|
|
976
|
+
if (!(i < keys.length)) return [3 /*break*/, 4];
|
|
977
|
+
batch = keys.slice(i, i + DELETE_OBJECTS_BATCH_LIMIT);
|
|
978
|
+
_b = (_a = results).push;
|
|
979
|
+
return [4 /*yield*/, this.client.send(new DeleteObjectsCommand({
|
|
980
|
+
Bucket: bucket,
|
|
981
|
+
Delete: { Objects: batch.map(function (Key) { return ({ Key: Key }); }) },
|
|
982
|
+
}))];
|
|
983
|
+
case 2:
|
|
984
|
+
_b.apply(_a, [_c.sent()]);
|
|
985
|
+
_c.label = 3;
|
|
986
|
+
case 3:
|
|
987
|
+
i += DELETE_OBJECTS_BATCH_LIMIT;
|
|
988
|
+
return [3 /*break*/, 1];
|
|
989
|
+
case 4: return [2 /*return*/, results];
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
};
|
|
994
|
+
/**
|
|
995
|
+
* Delete every object in a bucket. Streams the listing page-by-page and
|
|
996
|
+
* delegates each page's deletion to `deleteObjects`, so peak memory stays
|
|
997
|
+
* bounded by one page (~1000 keys) regardless of bucket size.
|
|
998
|
+
* @returns The keys of every deleted object.
|
|
999
|
+
*/
|
|
1000
|
+
S3Service.prototype.emptyBucket = function (bucket) {
|
|
1001
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1002
|
+
var paginator, deletedKeys, _a, paginator_3, paginator_3_1, page, keys, e_3_1;
|
|
1003
|
+
var _b, e_3, _c, _d;
|
|
1004
|
+
var _e;
|
|
1005
|
+
return __generator(this, function (_f) {
|
|
1006
|
+
switch (_f.label) {
|
|
1007
|
+
case 0:
|
|
1008
|
+
this.logger.info('Emptying S3 bucket', { input: { bucket: bucket } });
|
|
1009
|
+
paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket });
|
|
1010
|
+
deletedKeys = [];
|
|
1011
|
+
_f.label = 1;
|
|
1012
|
+
case 1:
|
|
1013
|
+
_f.trys.push([1, 7, 8, 13]);
|
|
1014
|
+
_a = true, paginator_3 = __asyncValues(paginator);
|
|
1015
|
+
_f.label = 2;
|
|
1016
|
+
case 2: return [4 /*yield*/, paginator_3.next()];
|
|
1017
|
+
case 3:
|
|
1018
|
+
if (!(paginator_3_1 = _f.sent(), _b = paginator_3_1.done, !_b)) return [3 /*break*/, 6];
|
|
1019
|
+
_d = paginator_3_1.value;
|
|
1020
|
+
_a = false;
|
|
1021
|
+
page = _d;
|
|
1022
|
+
keys = ((_e = page.Contents) !== null && _e !== void 0 ? _e : []).flatMap(function (o) { return (o.Key ? [o.Key] : []); });
|
|
1023
|
+
if (keys.length === 0)
|
|
1024
|
+
return [3 /*break*/, 5];
|
|
1025
|
+
return [4 /*yield*/, this.deleteObjects(bucket, keys)];
|
|
1026
|
+
case 4:
|
|
1027
|
+
_f.sent();
|
|
1028
|
+
deletedKeys.push.apply(deletedKeys, keys);
|
|
1029
|
+
_f.label = 5;
|
|
1030
|
+
case 5:
|
|
1031
|
+
_a = true;
|
|
1032
|
+
return [3 /*break*/, 2];
|
|
1033
|
+
case 6: return [3 /*break*/, 13];
|
|
1034
|
+
case 7:
|
|
1035
|
+
e_3_1 = _f.sent();
|
|
1036
|
+
e_3 = { error: e_3_1 };
|
|
1037
|
+
return [3 /*break*/, 13];
|
|
1038
|
+
case 8:
|
|
1039
|
+
_f.trys.push([8, , 11, 12]);
|
|
1040
|
+
if (!(!_a && !_b && (_c = paginator_3.return))) return [3 /*break*/, 10];
|
|
1041
|
+
return [4 /*yield*/, _c.call(paginator_3)];
|
|
1042
|
+
case 9:
|
|
1043
|
+
_f.sent();
|
|
1044
|
+
_f.label = 10;
|
|
1045
|
+
case 10: return [3 /*break*/, 12];
|
|
1046
|
+
case 11:
|
|
1047
|
+
if (e_3) throw e_3.error;
|
|
1048
|
+
return [7 /*endfinally*/];
|
|
1049
|
+
case 12: return [7 /*endfinally*/];
|
|
1050
|
+
case 13: return [2 /*return*/, deletedKeys];
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
};
|
|
1055
|
+
return S3Service;
|
|
1056
|
+
}());
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* Fields safe to log at INFO level. Omits `SecretString` and `SecretBinary` —
|
|
1060
|
+
* the secret material itself. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
|
|
1061
|
+
* input.
|
|
1062
|
+
*/
|
|
1063
|
+
var CREATE_SECRET_SAFE_FIELDS = [
|
|
1064
|
+
'Name',
|
|
1065
|
+
'Description',
|
|
1066
|
+
'KmsKeyId',
|
|
1067
|
+
'Tags',
|
|
1068
|
+
'ClientRequestToken',
|
|
1069
|
+
'AddReplicaRegions',
|
|
1070
|
+
'ForceOverwriteReplicaSecret',
|
|
1071
|
+
];
|
|
1072
|
+
var UPDATE_SECRET_SAFE_FIELDS = [
|
|
1073
|
+
'SecretId',
|
|
1074
|
+
'Description',
|
|
1075
|
+
'KmsKeyId',
|
|
1076
|
+
'ClientRequestToken',
|
|
1077
|
+
];
|
|
1078
|
+
var PUT_SECRET_VALUE_SAFE_FIELDS = [
|
|
1079
|
+
'SecretId',
|
|
1080
|
+
'VersionStages',
|
|
1081
|
+
'ClientRequestToken',
|
|
1082
|
+
];
|
|
1083
|
+
/**
|
|
1084
|
+
* `DeleteSecretCommandInput` carries no secret material today, but the
|
|
1085
|
+
* explicit allowlist keeps the log shape consistent with the other write
|
|
1086
|
+
* methods and protects against future field additions.
|
|
1087
|
+
*/
|
|
1088
|
+
var DELETE_SECRET_SAFE_FIELDS = [
|
|
1089
|
+
'SecretId',
|
|
1090
|
+
'RecoveryWindowInDays',
|
|
1091
|
+
'ForceDeleteWithoutRecovery',
|
|
1092
|
+
];
|
|
1093
|
+
/**
|
|
1094
|
+
* Wrapper around the AWS Secrets Manager client providing structured
|
|
1095
|
+
* Powertools logging and X-Ray tracing by default.
|
|
1096
|
+
*
|
|
1097
|
+
* Write operations (`createSecret`, `updateSecret`, `putSecretValue`,
|
|
1098
|
+
* `deleteSecret`) are exposed for convenience but should be used with care:
|
|
1099
|
+
* secret lifecycle is usually managed by IaC (CDK / Terraform). Prefer IaC
|
|
1100
|
+
* for anything that exists at deploy time; reserve runtime writes for
|
|
1101
|
+
* dynamically-issued credentials, rotation flows, or other genuinely
|
|
1102
|
+
* mutable values.
|
|
1103
|
+
*/
|
|
1104
|
+
var SecretsManagerService = /** @class */ (function () {
|
|
1105
|
+
/**
|
|
1106
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
1107
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
1108
|
+
* @param opts.client - Optional pre-configured `SecretsManagerClient`. When
|
|
1109
|
+
* supplied, the wrapper does not apply X-Ray instrumentation — the caller
|
|
1110
|
+
* owns that decision.
|
|
1111
|
+
*/
|
|
1112
|
+
function SecretsManagerService(opts) {
|
|
1113
|
+
var _a, _b;
|
|
1114
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SecretsManagerClient());
|
|
1115
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Fetch a secret's string value from Secrets Manager.
|
|
1119
|
+
* @param secretId - The ARN or friendly name of the secret.
|
|
1120
|
+
* @returns The secret's `SecretString` value.
|
|
1121
|
+
* @throws If the response does not contain a `SecretString` (e.g. the secret
|
|
1122
|
+
* stores binary data).
|
|
1123
|
+
*/
|
|
1124
|
+
SecretsManagerService.prototype.getSecret = function (secretId) {
|
|
1125
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1126
|
+
return __generator(this, function (_a) {
|
|
1127
|
+
this.logger.info('Fetching secret', { input: { secretId: secretId } });
|
|
1128
|
+
return [2 /*return*/, this.fetchSecretString(secretId)];
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
};
|
|
1132
|
+
/**
|
|
1133
|
+
* Fetch a secret and parse it as JSON.
|
|
1134
|
+
* @param secretId - The ARN or friendly name of the secret.
|
|
1135
|
+
* @template T - Expected shape of the parsed secret.
|
|
1136
|
+
* @returns The parsed secret value.
|
|
1137
|
+
* @throws If the secret has no `SecretString` or the value is not valid JSON.
|
|
1138
|
+
*/
|
|
1139
|
+
SecretsManagerService.prototype.getJsonSecret = function (secretId) {
|
|
1140
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1141
|
+
var secretString;
|
|
1142
|
+
return __generator(this, function (_a) {
|
|
1143
|
+
switch (_a.label) {
|
|
1144
|
+
case 0:
|
|
1145
|
+
this.logger.info('Fetching JSON secret', { input: { secretId: secretId } });
|
|
1146
|
+
return [4 /*yield*/, this.fetchSecretString(secretId)];
|
|
1147
|
+
case 1:
|
|
1148
|
+
secretString = _a.sent();
|
|
1149
|
+
return [2 /*return*/, JSON.parse(secretString)];
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
};
|
|
1154
|
+
/**
|
|
1155
|
+
* Create a new secret. At INFO level the log line includes only identity
|
|
1156
|
+
* and non-secret metadata; `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
|
|
1157
|
+
* input (including `SecretString` / `SecretBinary`).
|
|
1158
|
+
*
|
|
1159
|
+
* Prefer IaC (CDK / Terraform) for secret lifecycle — use this for
|
|
1160
|
+
* dynamically-issued credentials only.
|
|
1161
|
+
*/
|
|
1162
|
+
SecretsManagerService.prototype.createSecret = function (input) {
|
|
1163
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1164
|
+
return __generator(this, function (_a) {
|
|
1165
|
+
this.logger.info('Creating secret', {
|
|
1166
|
+
input: filterFieldsForLogLevel(this.logger, input, CREATE_SECRET_SAFE_FIELDS),
|
|
1167
|
+
});
|
|
1168
|
+
return [2 /*return*/, this.client.send(new CreateSecretCommand(input))];
|
|
1169
|
+
});
|
|
1170
|
+
});
|
|
1171
|
+
};
|
|
1172
|
+
/**
|
|
1173
|
+
* Update an existing secret's metadata or value. At INFO level the log
|
|
1174
|
+
* line omits `SecretString` / `SecretBinary`; `POWERTOOLS_LOG_LEVEL=DEBUG`
|
|
1175
|
+
* unlocks the full input.
|
|
1176
|
+
*
|
|
1177
|
+
* Prefer IaC (CDK / Terraform) for secret lifecycle — use this for
|
|
1178
|
+
* runtime metadata updates only.
|
|
1179
|
+
*/
|
|
1180
|
+
SecretsManagerService.prototype.updateSecret = function (input) {
|
|
1181
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1182
|
+
return __generator(this, function (_a) {
|
|
1183
|
+
this.logger.info('Updating secret', {
|
|
1184
|
+
input: filterFieldsForLogLevel(this.logger, input, UPDATE_SECRET_SAFE_FIELDS),
|
|
1185
|
+
});
|
|
1186
|
+
return [2 /*return*/, this.client.send(new UpdateSecretCommand(input))];
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
};
|
|
1190
|
+
/**
|
|
1191
|
+
* Store a new version of a secret's value. At INFO level the log line
|
|
1192
|
+
* omits `SecretString` / `SecretBinary`; `POWERTOOLS_LOG_LEVEL=DEBUG`
|
|
1193
|
+
* unlocks the full input.
|
|
1194
|
+
*
|
|
1195
|
+
* Typically used by rotation flows.
|
|
1196
|
+
*/
|
|
1197
|
+
SecretsManagerService.prototype.putSecretValue = function (input) {
|
|
1198
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1199
|
+
return __generator(this, function (_a) {
|
|
1200
|
+
this.logger.info('Putting secret value', {
|
|
1201
|
+
input: filterFieldsForLogLevel(this.logger, input, PUT_SECRET_VALUE_SAFE_FIELDS),
|
|
1202
|
+
});
|
|
1203
|
+
return [2 /*return*/, this.client.send(new PutSecretValueCommand(input))];
|
|
1204
|
+
});
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
/**
|
|
1208
|
+
* Delete a secret. Pass `ForceDeleteWithoutRecovery: true` to bypass the
|
|
1209
|
+
* default 7-30 day recovery window (irreversible).
|
|
1210
|
+
*
|
|
1211
|
+
* Prefer IaC (CDK / Terraform) for secret lifecycle.
|
|
1212
|
+
*/
|
|
1213
|
+
SecretsManagerService.prototype.deleteSecret = function (input) {
|
|
1214
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1215
|
+
return __generator(this, function (_a) {
|
|
1216
|
+
this.logger.info('Deleting secret', {
|
|
1217
|
+
input: filterFieldsForLogLevel(this.logger, input, DELETE_SECRET_SAFE_FIELDS),
|
|
1218
|
+
});
|
|
1219
|
+
return [2 /*return*/, this.client.send(new DeleteSecretCommand(input))];
|
|
1220
|
+
});
|
|
1221
|
+
});
|
|
1222
|
+
};
|
|
1223
|
+
SecretsManagerService.prototype.fetchSecretString = function (secretId) {
|
|
1224
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1225
|
+
var response;
|
|
1226
|
+
return __generator(this, function (_a) {
|
|
1227
|
+
switch (_a.label) {
|
|
1228
|
+
case 0: return [4 /*yield*/, this.client.send(new GetSecretValueCommand({ SecretId: secretId }))];
|
|
1229
|
+
case 1:
|
|
1230
|
+
response = _a.sent();
|
|
1231
|
+
if (response.SecretString === undefined) {
|
|
1232
|
+
throw new Error("Secret '".concat(secretId, "' does not contain a string value"));
|
|
1233
|
+
}
|
|
1234
|
+
return [2 /*return*/, response.SecretString];
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
};
|
|
1239
|
+
return SecretsManagerService;
|
|
1240
|
+
}());
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Fields safe to log at INFO for `startExecution`. Omits `input` — the SFN
|
|
1244
|
+
* execution payload, which routinely carries PII (customer IDs, addresses,
|
|
1245
|
+
* order contents, etc.). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
1246
|
+
*/
|
|
1247
|
+
var START_EXECUTION_SAFE_FIELDS = [
|
|
1248
|
+
'stateMachineArn',
|
|
1249
|
+
'name',
|
|
1250
|
+
'traceHeader',
|
|
1251
|
+
];
|
|
1252
|
+
/**
|
|
1253
|
+
* Wrapper around the AWS Step Functions client providing structured
|
|
1254
|
+
* Powertools logging and X-Ray tracing by default.
|
|
1255
|
+
*/
|
|
1256
|
+
var StepFunctionsService = /** @class */ (function () {
|
|
1257
|
+
/**
|
|
1258
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
1259
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
1260
|
+
* @param opts.client - Optional pre-configured `SFNClient`. When supplied,
|
|
1261
|
+
* the wrapper does not apply X-Ray instrumentation.
|
|
1262
|
+
*/
|
|
1263
|
+
function StepFunctionsService(opts) {
|
|
1264
|
+
var _a, _b;
|
|
1265
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SFNClient());
|
|
1266
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* List all executions for a state machine, auto-paginating across all
|
|
1270
|
+
* pages. Typically bounded by `statusFilter` and state-machine retention,
|
|
1271
|
+
* so the flat-array shape is safe in practice.
|
|
1272
|
+
*/
|
|
1273
|
+
StepFunctionsService.prototype.listExecutions = function (input) {
|
|
1274
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1275
|
+
var paginator, executions, _a, paginator_1, paginator_1_1, page, e_1_1;
|
|
1276
|
+
var _b, e_1, _c, _d;
|
|
1277
|
+
return __generator(this, function (_e) {
|
|
1278
|
+
switch (_e.label) {
|
|
1279
|
+
case 0:
|
|
1280
|
+
this.logger.info('Listing Step Functions executions', { input: input });
|
|
1281
|
+
paginator = paginateListExecutions({ client: this.client }, input);
|
|
1282
|
+
executions = [];
|
|
1283
|
+
_e.label = 1;
|
|
1284
|
+
case 1:
|
|
1285
|
+
_e.trys.push([1, 6, 7, 12]);
|
|
1286
|
+
_a = true, paginator_1 = __asyncValues(paginator);
|
|
1287
|
+
_e.label = 2;
|
|
1288
|
+
case 2: return [4 /*yield*/, paginator_1.next()];
|
|
1289
|
+
case 3:
|
|
1290
|
+
if (!(paginator_1_1 = _e.sent(), _b = paginator_1_1.done, !_b)) return [3 /*break*/, 5];
|
|
1291
|
+
_d = paginator_1_1.value;
|
|
1292
|
+
_a = false;
|
|
1293
|
+
page = _d;
|
|
1294
|
+
if (page.executions)
|
|
1295
|
+
executions.push.apply(executions, page.executions);
|
|
1296
|
+
_e.label = 4;
|
|
1297
|
+
case 4:
|
|
1298
|
+
_a = true;
|
|
1299
|
+
return [3 /*break*/, 2];
|
|
1300
|
+
case 5: return [3 /*break*/, 12];
|
|
1301
|
+
case 6:
|
|
1302
|
+
e_1_1 = _e.sent();
|
|
1303
|
+
e_1 = { error: e_1_1 };
|
|
1304
|
+
return [3 /*break*/, 12];
|
|
1305
|
+
case 7:
|
|
1306
|
+
_e.trys.push([7, , 10, 11]);
|
|
1307
|
+
if (!(!_a && !_b && (_c = paginator_1.return))) return [3 /*break*/, 9];
|
|
1308
|
+
return [4 /*yield*/, _c.call(paginator_1)];
|
|
1309
|
+
case 8:
|
|
1310
|
+
_e.sent();
|
|
1311
|
+
_e.label = 9;
|
|
1312
|
+
case 9: return [3 /*break*/, 11];
|
|
1313
|
+
case 10:
|
|
1314
|
+
if (e_1) throw e_1.error;
|
|
1315
|
+
return [7 /*endfinally*/];
|
|
1316
|
+
case 11: return [7 /*endfinally*/];
|
|
1317
|
+
case 12: return [2 /*return*/, executions];
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
};
|
|
1322
|
+
/**
|
|
1323
|
+
* Start a new Step Functions execution.
|
|
1324
|
+
*/
|
|
1325
|
+
StepFunctionsService.prototype.startExecution = function (input) {
|
|
1326
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1327
|
+
return __generator(this, function (_a) {
|
|
1328
|
+
this.logger.info('Starting Step Functions execution', {
|
|
1329
|
+
input: filterFieldsForLogLevel(this.logger, input, START_EXECUTION_SAFE_FIELDS),
|
|
1330
|
+
});
|
|
1331
|
+
return [2 /*return*/, this.client.send(new StartExecutionCommand(input))];
|
|
1332
|
+
});
|
|
1333
|
+
});
|
|
1334
|
+
};
|
|
1335
|
+
/**
|
|
1336
|
+
* Describe an existing Step Functions execution.
|
|
1337
|
+
*/
|
|
1338
|
+
StepFunctionsService.prototype.describeExecution = function (input) {
|
|
1339
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1340
|
+
return __generator(this, function (_a) {
|
|
1341
|
+
this.logger.info('Describing Step Functions execution', { input: input });
|
|
1342
|
+
return [2 /*return*/, this.client.send(new DescribeExecutionCommand(input))];
|
|
1343
|
+
});
|
|
1344
|
+
});
|
|
1345
|
+
};
|
|
1346
|
+
/**
|
|
1347
|
+
* Stop an in-progress Step Functions execution.
|
|
1348
|
+
*/
|
|
1349
|
+
StepFunctionsService.prototype.stopExecution = function (input) {
|
|
1350
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1351
|
+
return __generator(this, function (_a) {
|
|
1352
|
+
this.logger.info('Stopping Step Functions execution', { input: input });
|
|
1353
|
+
return [2 /*return*/, this.client.send(new StopExecutionCommand(input))];
|
|
1354
|
+
});
|
|
1355
|
+
});
|
|
1356
|
+
};
|
|
1357
|
+
return StepFunctionsService;
|
|
1358
|
+
}());
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* Truncate a UTF-8 string to fit within `maxBytes` when encoded as UTF-8.
|
|
1362
|
+
*
|
|
1363
|
+
* Walks back from the cut point to the start of the previous codepoint, so the
|
|
1364
|
+
* result is never a malformed UTF-8 sequence. Returns the input unchanged when
|
|
1365
|
+
* it already fits.
|
|
1366
|
+
*/
|
|
1367
|
+
function truncateUtf8(str, maxBytes) {
|
|
1368
|
+
var buf = Buffer.from(str, 'utf8');
|
|
1369
|
+
if (buf.length <= maxBytes)
|
|
1370
|
+
return str;
|
|
1371
|
+
var end = maxBytes;
|
|
1372
|
+
while (end > 0) {
|
|
1373
|
+
var byte = buf[end];
|
|
1374
|
+
if (byte === undefined || (byte & 0xc0) !== 0x80)
|
|
1375
|
+
break;
|
|
1376
|
+
end--;
|
|
1377
|
+
}
|
|
1378
|
+
return buf.toString('utf8', 0, end);
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Truncate a string to at most `maxChars` Unicode codepoints. Splits the
|
|
1382
|
+
* string via `Array.from` so surrogate pairs (emoji, supplementary-plane
|
|
1383
|
+
* characters) are not split in the middle. Returns the input unchanged when
|
|
1384
|
+
* it already fits.
|
|
1385
|
+
*/
|
|
1386
|
+
function truncateCodepoints(str, maxChars) {
|
|
1387
|
+
var codepoints = Array.from(str);
|
|
1388
|
+
if (codepoints.length <= maxChars)
|
|
1389
|
+
return str;
|
|
1390
|
+
return codepoints.slice(0, maxChars).join('');
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
var PUBLISH_BATCH_LIMIT = 10;
|
|
1394
|
+
var SNS_MESSAGE_MAX_BYTES = 256 * 1024;
|
|
1395
|
+
var SNS_SUBJECT_MAX_CHARS = 100;
|
|
1396
|
+
/**
|
|
1397
|
+
* Fields safe to log at INFO level for `publish`. Omits `Message`, `Subject`,
|
|
1398
|
+
* `MessageAttributes`, and `PhoneNumber` — payloads, user-visible content
|
|
1399
|
+
* (subjects often carry order numbers / customer names), and PII recipient
|
|
1400
|
+
* identifiers. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
1401
|
+
*/
|
|
1402
|
+
var PUBLISH_SAFE_FIELDS = [
|
|
1403
|
+
'TopicArn',
|
|
1404
|
+
'TargetArn',
|
|
1405
|
+
'MessageStructure',
|
|
1406
|
+
'MessageGroupId',
|
|
1407
|
+
'MessageDeduplicationId',
|
|
1408
|
+
];
|
|
1409
|
+
/**
|
|
1410
|
+
* Wrapper around the AWS SNS client providing structured Powertools logging
|
|
1411
|
+
* and X-Ray tracing by default.
|
|
1412
|
+
*/
|
|
1413
|
+
var SNSService = /** @class */ (function () {
|
|
1414
|
+
/**
|
|
1415
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
1416
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
1417
|
+
* @param opts.client - Optional pre-configured `SNSClient`. When supplied,
|
|
1418
|
+
* the wrapper does not apply X-Ray instrumentation.
|
|
1419
|
+
* @param opts.truncate - When `true`, oversized `Message` / `Subject` are
|
|
1420
|
+
* truncated (byte-safe / codepoint-safe) before sending instead of failing
|
|
1421
|
+
* at the SDK. Defaults to `false` — the SDK throws on oversize, which is
|
|
1422
|
+
* usually the right failure mode. Each `publish` call can override via
|
|
1423
|
+
* its own `truncate` option.
|
|
1424
|
+
*/
|
|
1425
|
+
function SNSService(opts) {
|
|
1426
|
+
var _a, _b, _c;
|
|
1427
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SNSClient());
|
|
1428
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
1429
|
+
this.truncate = (_c = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _c !== void 0 ? _c : false;
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Publish a single message to an SNS topic.
|
|
1433
|
+
*
|
|
1434
|
+
* At INFO level the log line includes only routing / dedup metadata; see
|
|
1435
|
+
* `PUBLISH_SAFE_FIELDS` for the list. Setting `POWERTOOLS_LOG_LEVEL=DEBUG`
|
|
1436
|
+
* unlocks the full input.
|
|
1437
|
+
*
|
|
1438
|
+
* @param input - PublishCommandInput including TopicArn and Message.
|
|
1439
|
+
*/
|
|
1440
|
+
SNSService.prototype.publish = function (input, opts) {
|
|
1441
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1442
|
+
var shouldTruncate, effective;
|
|
1443
|
+
var _a;
|
|
1444
|
+
return __generator(this, function (_b) {
|
|
1445
|
+
shouldTruncate = (_a = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _a !== void 0 ? _a : this.truncate;
|
|
1446
|
+
effective = shouldTruncate ? this.applyTruncation(input) : input;
|
|
1447
|
+
this.logger.info('Publishing SNS message', {
|
|
1448
|
+
input: filterFieldsForLogLevel(this.logger, effective, PUBLISH_SAFE_FIELDS),
|
|
1449
|
+
});
|
|
1450
|
+
return [2 /*return*/, this.client.send(new PublishCommand(effective))];
|
|
1451
|
+
});
|
|
1452
|
+
});
|
|
1453
|
+
};
|
|
1454
|
+
SNSService.prototype.applyTruncation = function (input) {
|
|
1455
|
+
var truncated = [];
|
|
1456
|
+
var Message = input.Message;
|
|
1457
|
+
if (Message !== undefined && Buffer.byteLength(Message, 'utf8') > SNS_MESSAGE_MAX_BYTES) {
|
|
1458
|
+
Message = truncateUtf8(Message, SNS_MESSAGE_MAX_BYTES);
|
|
1459
|
+
truncated.push('Message');
|
|
1460
|
+
}
|
|
1461
|
+
var Subject = input.Subject;
|
|
1462
|
+
if (Subject !== undefined && Array.from(Subject).length > SNS_SUBJECT_MAX_CHARS) {
|
|
1463
|
+
Subject = truncateCodepoints(Subject, SNS_SUBJECT_MAX_CHARS);
|
|
1464
|
+
truncated.push('Subject');
|
|
1465
|
+
}
|
|
1466
|
+
if (truncated.length > 0) {
|
|
1467
|
+
this.logger.warn('Truncated SNS publish input', { fields: truncated });
|
|
1468
|
+
}
|
|
1469
|
+
return __assign(__assign({}, input), { Message: Message, Subject: Subject });
|
|
1470
|
+
};
|
|
1471
|
+
/**
|
|
1472
|
+
* Publish a batch of messages to an SNS topic. The SNS API caps
|
|
1473
|
+
* PublishBatch at 10 entries per request, so this method auto-chunks
|
|
1474
|
+
* the caller's `PublishBatchRequestEntries` and sends one request per
|
|
1475
|
+
* chunk, returning the array of outputs.
|
|
1476
|
+
* @param input - PublishBatchCommandInput including TopicArn and entries.
|
|
1477
|
+
*/
|
|
1478
|
+
SNSService.prototype.publishBatch = function (input) {
|
|
1479
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1480
|
+
var entries, isDebug, results, i, chunk, _a, _b;
|
|
1481
|
+
var _c;
|
|
1482
|
+
return __generator(this, function (_d) {
|
|
1483
|
+
switch (_d.label) {
|
|
1484
|
+
case 0:
|
|
1485
|
+
entries = (_c = input.PublishBatchRequestEntries) !== null && _c !== void 0 ? _c : [];
|
|
1486
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
1487
|
+
this.logger.info('Publishing SNS message batch', {
|
|
1488
|
+
input: isDebug ? input : { TopicArn: input.TopicArn, entryCount: entries.length },
|
|
1489
|
+
});
|
|
1490
|
+
results = [];
|
|
1491
|
+
i = 0;
|
|
1492
|
+
_d.label = 1;
|
|
1493
|
+
case 1:
|
|
1494
|
+
if (!(i < entries.length)) return [3 /*break*/, 4];
|
|
1495
|
+
chunk = entries.slice(i, i + PUBLISH_BATCH_LIMIT);
|
|
1496
|
+
_b = (_a = results).push;
|
|
1497
|
+
return [4 /*yield*/, this.client.send(new PublishBatchCommand(__assign(__assign({}, input), { PublishBatchRequestEntries: chunk })))];
|
|
1498
|
+
case 2:
|
|
1499
|
+
_b.apply(_a, [_d.sent()]);
|
|
1500
|
+
_d.label = 3;
|
|
1501
|
+
case 3:
|
|
1502
|
+
i += PUBLISH_BATCH_LIMIT;
|
|
1503
|
+
return [3 /*break*/, 1];
|
|
1504
|
+
case 4: return [2 /*return*/, results];
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
});
|
|
1508
|
+
};
|
|
1509
|
+
return SNSService;
|
|
1510
|
+
}());
|
|
1511
|
+
|
|
1512
|
+
var SQS_BATCH_LIMIT = 10;
|
|
1513
|
+
var SQS_MESSAGE_BODY_MAX_BYTES = 256 * 1024;
|
|
1514
|
+
/**
|
|
1515
|
+
* Fields safe to log at INFO level for `sendMessage`. Omits `MessageBody` and
|
|
1516
|
+
* `MessageAttributes` — both carry payload content.
|
|
1517
|
+
* `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
|
|
1518
|
+
*/
|
|
1519
|
+
var SEND_MESSAGE_SAFE_FIELDS = [
|
|
1520
|
+
'QueueUrl',
|
|
1521
|
+
'DelaySeconds',
|
|
1522
|
+
'MessageGroupId',
|
|
1523
|
+
'MessageDeduplicationId',
|
|
1524
|
+
];
|
|
1525
|
+
/**
|
|
1526
|
+
* Wrapper around the AWS SQS client providing structured Powertools logging
|
|
1527
|
+
* and X-Ray tracing by default.
|
|
1528
|
+
*/
|
|
1529
|
+
var SQSService = /** @class */ (function () {
|
|
1530
|
+
/**
|
|
1531
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
1532
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
1533
|
+
* @param opts.client - Optional pre-configured `SQSClient`. When supplied,
|
|
1534
|
+
* the wrapper does not apply X-Ray instrumentation.
|
|
1535
|
+
* @param opts.truncate - When `true`, oversized `MessageBody` is truncated
|
|
1536
|
+
* (byte-safe) before sending instead of failing at the SDK. Defaults to
|
|
1537
|
+
* `false`. Each `sendMessage` call can override via its own `truncate`
|
|
1538
|
+
* option.
|
|
1539
|
+
*/
|
|
1540
|
+
function SQSService(opts) {
|
|
1541
|
+
var _a, _b, _c;
|
|
1542
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SQSClient());
|
|
1543
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
1544
|
+
this.truncate = (_c = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _c !== void 0 ? _c : false;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Send a single message to an SQS queue.
|
|
1548
|
+
*
|
|
1549
|
+
* At INFO level the log line includes only queue routing / FIFO metadata;
|
|
1550
|
+
* see `SEND_MESSAGE_SAFE_FIELDS`. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the
|
|
1551
|
+
* full input.
|
|
1552
|
+
*/
|
|
1553
|
+
SQSService.prototype.sendMessage = function (input, opts) {
|
|
1554
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1555
|
+
var shouldTruncate, effective;
|
|
1556
|
+
var _a;
|
|
1557
|
+
return __generator(this, function (_b) {
|
|
1558
|
+
shouldTruncate = (_a = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _a !== void 0 ? _a : this.truncate;
|
|
1559
|
+
effective = shouldTruncate ? this.applyTruncation(input) : input;
|
|
1560
|
+
this.logger.info('Sending SQS message', {
|
|
1561
|
+
input: filterFieldsForLogLevel(this.logger, effective, SEND_MESSAGE_SAFE_FIELDS),
|
|
1562
|
+
});
|
|
1563
|
+
return [2 /*return*/, this.client.send(new SendMessageCommand(effective))];
|
|
1564
|
+
});
|
|
1565
|
+
});
|
|
1566
|
+
};
|
|
1567
|
+
SQSService.prototype.applyTruncation = function (input) {
|
|
1568
|
+
var truncated = [];
|
|
1569
|
+
var MessageBody = input.MessageBody;
|
|
1570
|
+
if (MessageBody !== undefined &&
|
|
1571
|
+
Buffer.byteLength(MessageBody, 'utf8') > SQS_MESSAGE_BODY_MAX_BYTES) {
|
|
1572
|
+
MessageBody = truncateUtf8(MessageBody, SQS_MESSAGE_BODY_MAX_BYTES);
|
|
1573
|
+
truncated.push('MessageBody');
|
|
1574
|
+
}
|
|
1575
|
+
if (truncated.length > 0) {
|
|
1576
|
+
this.logger.warn('Truncated SQS sendMessage input', { fields: truncated });
|
|
1577
|
+
}
|
|
1578
|
+
return __assign(__assign({}, input), { MessageBody: MessageBody });
|
|
1579
|
+
};
|
|
1580
|
+
/**
|
|
1581
|
+
* Receive messages from an SQS queue. Returns an empty array when no
|
|
1582
|
+
* messages are available. No automatic deletion is performed — visibility
|
|
1583
|
+
* timeout semantics are the caller's responsibility.
|
|
1584
|
+
* @returns The `Messages` array from the response, or `[]` if absent.
|
|
1585
|
+
*/
|
|
1586
|
+
SQSService.prototype.receiveMessages = function (input) {
|
|
1587
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1588
|
+
var response;
|
|
1589
|
+
var _a;
|
|
1590
|
+
return __generator(this, function (_b) {
|
|
1591
|
+
switch (_b.label) {
|
|
1592
|
+
case 0:
|
|
1593
|
+
this.logger.info('Receiving SQS messages', { input: input });
|
|
1594
|
+
return [4 /*yield*/, this.client.send(new ReceiveMessageCommand(input))];
|
|
1595
|
+
case 1:
|
|
1596
|
+
response = _b.sent();
|
|
1597
|
+
return [2 /*return*/, (_a = response.Messages) !== null && _a !== void 0 ? _a : []];
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
});
|
|
1601
|
+
};
|
|
1602
|
+
/**
|
|
1603
|
+
* Delete a single message from an SQS queue.
|
|
1604
|
+
*/
|
|
1605
|
+
SQSService.prototype.deleteMessage = function (input) {
|
|
1606
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1607
|
+
return __generator(this, function (_a) {
|
|
1608
|
+
this.logger.info('Deleting SQS message', { input: input });
|
|
1609
|
+
return [2 /*return*/, this.client.send(new DeleteMessageCommand(input))];
|
|
1610
|
+
});
|
|
1611
|
+
});
|
|
1612
|
+
};
|
|
1613
|
+
/**
|
|
1614
|
+
* Send a batch of messages to an SQS queue. The SQS API caps
|
|
1615
|
+
* SendMessageBatch at 10 entries per request, so this method auto-chunks
|
|
1616
|
+
* the caller's entries and sends one request per chunk.
|
|
1617
|
+
*/
|
|
1618
|
+
SQSService.prototype.sendMessageBatch = function (input) {
|
|
1619
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1620
|
+
var entries, isDebug, results, i, chunk, _a, _b;
|
|
1621
|
+
var _c;
|
|
1622
|
+
return __generator(this, function (_d) {
|
|
1623
|
+
switch (_d.label) {
|
|
1624
|
+
case 0:
|
|
1625
|
+
entries = (_c = input.Entries) !== null && _c !== void 0 ? _c : [];
|
|
1626
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
1627
|
+
this.logger.info('Sending SQS message batch', {
|
|
1628
|
+
input: isDebug ? input : { QueueUrl: input.QueueUrl, entryCount: entries.length },
|
|
1629
|
+
});
|
|
1630
|
+
results = [];
|
|
1631
|
+
i = 0;
|
|
1632
|
+
_d.label = 1;
|
|
1633
|
+
case 1:
|
|
1634
|
+
if (!(i < entries.length)) return [3 /*break*/, 4];
|
|
1635
|
+
chunk = entries.slice(i, i + SQS_BATCH_LIMIT);
|
|
1636
|
+
_b = (_a = results).push;
|
|
1637
|
+
return [4 /*yield*/, this.client.send(new SendMessageBatchCommand(__assign(__assign({}, input), { Entries: chunk })))];
|
|
1638
|
+
case 2:
|
|
1639
|
+
_b.apply(_a, [_d.sent()]);
|
|
1640
|
+
_d.label = 3;
|
|
1641
|
+
case 3:
|
|
1642
|
+
i += SQS_BATCH_LIMIT;
|
|
1643
|
+
return [3 /*break*/, 1];
|
|
1644
|
+
case 4: return [2 /*return*/, results];
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
});
|
|
1648
|
+
};
|
|
1649
|
+
/**
|
|
1650
|
+
* Delete a batch of messages from an SQS queue. The SQS API caps
|
|
1651
|
+
* DeleteMessageBatch at 10 entries per request, so this method auto-chunks
|
|
1652
|
+
* the caller's entries and sends one request per chunk.
|
|
1653
|
+
*/
|
|
1654
|
+
SQSService.prototype.deleteMessageBatch = function (input) {
|
|
1655
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1656
|
+
var entries, isDebug, results, i, chunk, _a, _b;
|
|
1657
|
+
var _c;
|
|
1658
|
+
return __generator(this, function (_d) {
|
|
1659
|
+
switch (_d.label) {
|
|
1660
|
+
case 0:
|
|
1661
|
+
entries = (_c = input.Entries) !== null && _c !== void 0 ? _c : [];
|
|
1662
|
+
isDebug = this.logger.getLevelName() === 'DEBUG';
|
|
1663
|
+
this.logger.info('Deleting SQS message batch', {
|
|
1664
|
+
input: isDebug ? input : { QueueUrl: input.QueueUrl, entryCount: entries.length },
|
|
1665
|
+
});
|
|
1666
|
+
results = [];
|
|
1667
|
+
i = 0;
|
|
1668
|
+
_d.label = 1;
|
|
1669
|
+
case 1:
|
|
1670
|
+
if (!(i < entries.length)) return [3 /*break*/, 4];
|
|
1671
|
+
chunk = entries.slice(i, i + SQS_BATCH_LIMIT);
|
|
1672
|
+
_b = (_a = results).push;
|
|
1673
|
+
return [4 /*yield*/, this.client.send(new DeleteMessageBatchCommand(__assign(__assign({}, input), { Entries: chunk })))];
|
|
1674
|
+
case 2:
|
|
1675
|
+
_b.apply(_a, [_d.sent()]);
|
|
1676
|
+
_d.label = 3;
|
|
1677
|
+
case 3:
|
|
1678
|
+
i += SQS_BATCH_LIMIT;
|
|
1679
|
+
return [3 /*break*/, 1];
|
|
1680
|
+
case 4: return [2 /*return*/, results];
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1683
|
+
});
|
|
1684
|
+
};
|
|
1685
|
+
return SQSService;
|
|
1686
|
+
}());
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* Fields safe to log at INFO level for `putParameter`. Omits `Value` (the
|
|
1690
|
+
* parameter content itself). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
|
|
1691
|
+
* input.
|
|
1692
|
+
*/
|
|
1693
|
+
var PUT_PARAMETER_SAFE_FIELDS = [
|
|
1694
|
+
'Name',
|
|
1695
|
+
'Type',
|
|
1696
|
+
'Description',
|
|
1697
|
+
'KeyId',
|
|
1698
|
+
'Overwrite',
|
|
1699
|
+
'AllowedPattern',
|
|
1700
|
+
'Tags',
|
|
1701
|
+
'Tier',
|
|
1702
|
+
'Policies',
|
|
1703
|
+
'DataType',
|
|
1704
|
+
];
|
|
1705
|
+
/**
|
|
1706
|
+
* Wrapper around the AWS SSM Parameter Store client providing structured
|
|
1707
|
+
* Powertools logging and X-Ray tracing by default. All read operations enable
|
|
1708
|
+
* `WithDecryption` — callers needing plaintext should use `SSMClient`
|
|
1709
|
+
* directly.
|
|
1710
|
+
*
|
|
1711
|
+
* Write operations (`putParameter`, `deleteParameter`) are exposed for
|
|
1712
|
+
* convenience but should be used with care: parameter lifecycle is usually
|
|
1713
|
+
* managed by IaC (CDK / Terraform). Prefer IaC for anything that exists at
|
|
1714
|
+
* deploy time; reserve runtime writes for values that genuinely need to
|
|
1715
|
+
* mutate within the application.
|
|
1716
|
+
*/
|
|
1717
|
+
var SSMService = /** @class */ (function () {
|
|
1718
|
+
/**
|
|
1719
|
+
* @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
|
|
1720
|
+
* which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
|
|
1721
|
+
* @param opts.client - Optional pre-configured `SSMClient`. When supplied,
|
|
1722
|
+
* the wrapper does not apply X-Ray instrumentation.
|
|
1723
|
+
*/
|
|
1724
|
+
function SSMService(opts) {
|
|
1725
|
+
var _a, _b;
|
|
1726
|
+
this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SSMClient());
|
|
1727
|
+
this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Fetch a single SSM parameter's value.
|
|
1731
|
+
* @param name - The parameter name (or ARN).
|
|
1732
|
+
* @returns The parameter value, or `undefined` if the parameter has no
|
|
1733
|
+
* value set.
|
|
1734
|
+
*/
|
|
1735
|
+
SSMService.prototype.getParameter = function (name) {
|
|
1736
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1737
|
+
var response;
|
|
1738
|
+
var _a;
|
|
1739
|
+
return __generator(this, function (_b) {
|
|
1740
|
+
switch (_b.label) {
|
|
1741
|
+
case 0:
|
|
1742
|
+
this.logger.info('Fetching SSM parameter', { input: { name: name } });
|
|
1743
|
+
return [4 /*yield*/, this.client.send(new GetParameterCommand({ Name: name, WithDecryption: true }))];
|
|
1744
|
+
case 1:
|
|
1745
|
+
response = _b.sent();
|
|
1746
|
+
return [2 /*return*/, (_a = response.Parameter) === null || _a === void 0 ? void 0 : _a.Value];
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
});
|
|
1750
|
+
};
|
|
1751
|
+
/**
|
|
1752
|
+
* Fetch multiple SSM parameters in a single request. Callers supply an
|
|
1753
|
+
* alias-to-path record, and the returned record is keyed by the same
|
|
1754
|
+
* aliases — so the SSM path is only mentioned at the call site and the
|
|
1755
|
+
* destructured names are whatever the caller wants to use locally:
|
|
1756
|
+
*
|
|
1757
|
+
* ```ts
|
|
1758
|
+
* const { username, password } = await ssm.getParameters({
|
|
1759
|
+
* username: '/myapp/db/username',
|
|
1760
|
+
* password: '/myapp/db/password',
|
|
1761
|
+
* });
|
|
1762
|
+
* ```
|
|
1763
|
+
*
|
|
1764
|
+
* @param aliases - Record mapping each desired local alias to its SSM
|
|
1765
|
+
* parameter name (or ARN).
|
|
1766
|
+
* @returns A record keyed by the same aliases, mapping each to the
|
|
1767
|
+
* parameter's value, or `undefined` when the parameter is missing or has
|
|
1768
|
+
* no value.
|
|
1769
|
+
*/
|
|
1770
|
+
SSMService.prototype.getParameters = function (aliases) {
|
|
1771
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1772
|
+
var response, byPath, _i, _a, parameter, result, _b, _c, alias;
|
|
1773
|
+
var _d;
|
|
1774
|
+
return __generator(this, function (_e) {
|
|
1775
|
+
switch (_e.label) {
|
|
1776
|
+
case 0:
|
|
1777
|
+
this.logger.info('Fetching SSM parameters', { input: { aliases: aliases } });
|
|
1778
|
+
return [4 /*yield*/, this.client.send(new GetParametersCommand({
|
|
1779
|
+
Names: Object.values(aliases),
|
|
1780
|
+
WithDecryption: true,
|
|
1781
|
+
}))];
|
|
1782
|
+
case 1:
|
|
1783
|
+
response = _e.sent();
|
|
1784
|
+
byPath = new Map();
|
|
1785
|
+
for (_i = 0, _a = (_d = response.Parameters) !== null && _d !== void 0 ? _d : []; _i < _a.length; _i++) {
|
|
1786
|
+
parameter = _a[_i];
|
|
1787
|
+
if (parameter.Name !== undefined)
|
|
1788
|
+
byPath.set(parameter.Name, parameter.Value);
|
|
1789
|
+
}
|
|
1790
|
+
result = {};
|
|
1791
|
+
for (_b = 0, _c = Object.keys(aliases); _b < _c.length; _b++) {
|
|
1792
|
+
alias = _c[_b];
|
|
1793
|
+
result[alias] = byPath.get(aliases[alias]);
|
|
1794
|
+
}
|
|
1795
|
+
return [2 /*return*/, result];
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
});
|
|
1799
|
+
};
|
|
1800
|
+
/**
|
|
1801
|
+
* Create or update an SSM parameter. The log line omits `Value` to avoid
|
|
1802
|
+
* leaking secret material.
|
|
1803
|
+
*
|
|
1804
|
+
* Prefer IaC (CDK / Terraform) for parameter lifecycle — use this for
|
|
1805
|
+
* runtime values only.
|
|
1806
|
+
*/
|
|
1807
|
+
SSMService.prototype.putParameter = function (input) {
|
|
1808
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1809
|
+
return __generator(this, function (_a) {
|
|
1810
|
+
this.logger.info('Putting SSM parameter', {
|
|
1811
|
+
input: filterFieldsForLogLevel(this.logger, input, PUT_PARAMETER_SAFE_FIELDS),
|
|
1812
|
+
});
|
|
1813
|
+
return [2 /*return*/, this.client.send(new PutParameterCommand(input))];
|
|
1814
|
+
});
|
|
1815
|
+
});
|
|
1816
|
+
};
|
|
1817
|
+
/**
|
|
1818
|
+
* Delete an SSM parameter by name.
|
|
1819
|
+
*
|
|
1820
|
+
* Prefer IaC (CDK / Terraform) for parameter lifecycle — use this for
|
|
1821
|
+
* runtime cleanup only.
|
|
1822
|
+
*/
|
|
1823
|
+
SSMService.prototype.deleteParameter = function (name) {
|
|
1824
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1825
|
+
return __generator(this, function (_a) {
|
|
1826
|
+
switch (_a.label) {
|
|
1827
|
+
case 0:
|
|
1828
|
+
this.logger.info('Deleting SSM parameter', { input: { name: name } });
|
|
1829
|
+
return [4 /*yield*/, this.client.send(new DeleteParameterCommand({ Name: name }))];
|
|
1830
|
+
case 1:
|
|
1831
|
+
_a.sent();
|
|
1832
|
+
return [2 /*return*/];
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
});
|
|
1836
|
+
};
|
|
1837
|
+
/**
|
|
1838
|
+
* Fetch all parameters under an SSM hierarchy path, auto-paginating across
|
|
1839
|
+
* all pages. `Recursive` defaults to `true` (overriding the AWS SDK
|
|
1840
|
+
* default of `false`) to match the typical "load all config under
|
|
1841
|
+
* `/myapp/`" use case.
|
|
1842
|
+
* @param path - The parameter path prefix (e.g. `/myapp/`).
|
|
1843
|
+
* @param opts.recursive - Whether to recurse into nested paths. Defaults
|
|
1844
|
+
* to `true`.
|
|
1845
|
+
* @returns The full `Parameter` objects (including `Version`,
|
|
1846
|
+
* `LastModifiedDate`, etc.).
|
|
1847
|
+
*/
|
|
1848
|
+
SSMService.prototype.getParametersByPath = function (path, opts) {
|
|
1849
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1850
|
+
var recursive, paginator, parameters, _a, paginator_1, paginator_1_1, page, e_1_1;
|
|
1851
|
+
var _b, e_1, _c, _d;
|
|
1852
|
+
var _e, _f;
|
|
1853
|
+
return __generator(this, function (_g) {
|
|
1854
|
+
switch (_g.label) {
|
|
1855
|
+
case 0:
|
|
1856
|
+
recursive = (_e = opts === null || opts === void 0 ? void 0 : opts.recursive) !== null && _e !== void 0 ? _e : true;
|
|
1857
|
+
this.logger.info('Fetching SSM parameters by path', {
|
|
1858
|
+
input: { path: path, recursive: recursive },
|
|
1859
|
+
});
|
|
1860
|
+
paginator = paginateGetParametersByPath({ client: this.client }, { Path: path, Recursive: recursive, WithDecryption: true });
|
|
1861
|
+
parameters = [];
|
|
1862
|
+
_g.label = 1;
|
|
1863
|
+
case 1:
|
|
1864
|
+
_g.trys.push([1, 6, 7, 12]);
|
|
1865
|
+
_a = true, paginator_1 = __asyncValues(paginator);
|
|
1866
|
+
_g.label = 2;
|
|
1867
|
+
case 2: return [4 /*yield*/, paginator_1.next()];
|
|
1868
|
+
case 3:
|
|
1869
|
+
if (!(paginator_1_1 = _g.sent(), _b = paginator_1_1.done, !_b)) return [3 /*break*/, 5];
|
|
1870
|
+
_d = paginator_1_1.value;
|
|
1871
|
+
_a = false;
|
|
1872
|
+
page = _d;
|
|
1873
|
+
parameters.push.apply(parameters, ((_f = page.Parameters) !== null && _f !== void 0 ? _f : []));
|
|
1874
|
+
_g.label = 4;
|
|
1875
|
+
case 4:
|
|
1876
|
+
_a = true;
|
|
1877
|
+
return [3 /*break*/, 2];
|
|
1878
|
+
case 5: return [3 /*break*/, 12];
|
|
1879
|
+
case 6:
|
|
1880
|
+
e_1_1 = _g.sent();
|
|
1881
|
+
e_1 = { error: e_1_1 };
|
|
1882
|
+
return [3 /*break*/, 12];
|
|
1883
|
+
case 7:
|
|
1884
|
+
_g.trys.push([7, , 10, 11]);
|
|
1885
|
+
if (!(!_a && !_b && (_c = paginator_1.return))) return [3 /*break*/, 9];
|
|
1886
|
+
return [4 /*yield*/, _c.call(paginator_1)];
|
|
1887
|
+
case 8:
|
|
1888
|
+
_g.sent();
|
|
1889
|
+
_g.label = 9;
|
|
1890
|
+
case 9: return [3 /*break*/, 11];
|
|
1891
|
+
case 10:
|
|
1892
|
+
if (e_1) throw e_1.error;
|
|
1893
|
+
return [7 /*endfinally*/];
|
|
1894
|
+
case 11: return [7 /*endfinally*/];
|
|
1895
|
+
case 12: return [2 /*return*/, parameters];
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1898
|
+
});
|
|
1899
|
+
};
|
|
1900
|
+
return SSMService;
|
|
1901
|
+
}());
|
|
1902
|
+
|
|
1903
|
+
export { DynamoDBService, S3Service, SNSService, SQSService, SSMService, SecretsManagerService, StepFunctionsService };
|