idea-aws 4.3.2 → 4.3.4
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/dynamoDB.d.ts +2 -0
- package/dist/src/dynamoDB.js +23 -21
- package/dist/src/genericController.d.ts +41 -5
- package/dist/src/genericController.js +64 -6
- package/dist/src/lambdaLogger.d.ts +13 -0
- package/dist/src/lambdaLogger.js +43 -0
- package/dist/src/resourceController.d.ts +0 -7
- package/dist/src/resourceController.js +30 -49
- package/dist/src/s3.d.ts +2 -0
- package/dist/src/s3.js +7 -5
- package/dist/src/ses.d.ts +2 -0
- package/dist/src/ses.js +5 -3
- package/dist/src/sns.d.ts +2 -0
- package/dist/src/sns.js +4 -2
- package/dist/src/streamController.d.ts +3 -0
- package/dist/src/streamController.js +6 -1
- package/index.ts +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -27,4 +27,4 @@ __exportStar(require("./src/sns"), exports);
|
|
|
27
27
|
__exportStar(require("./src/ssm"), exports);
|
|
28
28
|
__exportStar(require("./src/translate"), exports);
|
|
29
29
|
__exportStar(require("./src/attachments"), exports);
|
|
30
|
-
__exportStar(require("./src/
|
|
30
|
+
__exportStar(require("./src/lambdaLogger"), exports);
|
package/dist/src/dynamoDB.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as DDB from '@aws-sdk/lib-dynamodb';
|
|
2
2
|
import * as DDBUtils from '@aws-sdk/util-dynamodb';
|
|
3
|
+
import { LambdaLogger } from './lambdaLogger';
|
|
3
4
|
/**
|
|
4
5
|
* A wrapper for AWS DynamoDB.
|
|
5
6
|
*/
|
|
6
7
|
export declare class DynamoDB {
|
|
7
8
|
protected dynamo: DDB.DynamoDBDocument;
|
|
9
|
+
protected logger: LambdaLogger;
|
|
8
10
|
constructor();
|
|
9
11
|
/**
|
|
10
12
|
* Convert a JSON object from DynamoDB format to simple JSON.
|
package/dist/src/dynamoDB.js
CHANGED
|
@@ -29,11 +29,13 @@ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
|
29
29
|
const DDBUtils = __importStar(require("@aws-sdk/util-dynamodb"));
|
|
30
30
|
const nanoid_1 = require("nanoid");
|
|
31
31
|
const NanoID = (0, nanoid_1.customAlphabet)('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 25);
|
|
32
|
+
const lambdaLogger_1 = require("./lambdaLogger");
|
|
32
33
|
/**
|
|
33
34
|
* A wrapper for AWS DynamoDB.
|
|
34
35
|
*/
|
|
35
36
|
class DynamoDB {
|
|
36
37
|
constructor() {
|
|
38
|
+
this.logger = new lambdaLogger_1.LambdaLogger();
|
|
37
39
|
this.dynamo = DDB.DynamoDBDocument.from(new client_dynamodb_1.DynamoDB(), {
|
|
38
40
|
marshallOptions: { convertEmptyValues: true, removeUndefinedValues: true, convertClassInstanceToMap: true }
|
|
39
41
|
});
|
|
@@ -84,7 +86,7 @@ class DynamoDB {
|
|
|
84
86
|
* @param key the key of the counter
|
|
85
87
|
*/
|
|
86
88
|
async getAtomicCounterByKey(key) {
|
|
87
|
-
|
|
89
|
+
this.logger.trace(`Get atomic counter for ${key}`);
|
|
88
90
|
const { Attributes } = await this.update({
|
|
89
91
|
TableName: 'idea_atomicCounters',
|
|
90
92
|
Key: { key },
|
|
@@ -101,7 +103,7 @@ class DynamoDB {
|
|
|
101
103
|
* @param params the params to apply to DynamoDB's function
|
|
102
104
|
*/
|
|
103
105
|
async get(params) {
|
|
104
|
-
|
|
106
|
+
this.logger.trace(`Get ${params.TableName}`);
|
|
105
107
|
const { Item } = await this.dynamo.get(params);
|
|
106
108
|
if (!Item)
|
|
107
109
|
throw new Error('Not found');
|
|
@@ -112,7 +114,7 @@ class DynamoDB {
|
|
|
112
114
|
* @param params the params to apply to DynamoDB's function
|
|
113
115
|
*/
|
|
114
116
|
async put(params) {
|
|
115
|
-
|
|
117
|
+
this.logger.trace(`Put ${params.TableName}`);
|
|
116
118
|
return await this.dynamo.put(params);
|
|
117
119
|
}
|
|
118
120
|
/**
|
|
@@ -120,7 +122,7 @@ class DynamoDB {
|
|
|
120
122
|
* @param params the params to apply to DynamoDB's function
|
|
121
123
|
*/
|
|
122
124
|
async update(params) {
|
|
123
|
-
|
|
125
|
+
this.logger.trace(`Update ${params.TableName}`);
|
|
124
126
|
return await this.dynamo.update(params);
|
|
125
127
|
}
|
|
126
128
|
/**
|
|
@@ -128,7 +130,7 @@ class DynamoDB {
|
|
|
128
130
|
* @param params the params to apply to DynamoDB's function
|
|
129
131
|
*/
|
|
130
132
|
async delete(params) {
|
|
131
|
-
|
|
133
|
+
this.logger.trace(`Delete ${params.TableName}`);
|
|
132
134
|
return await this.dynamo.delete(params);
|
|
133
135
|
}
|
|
134
136
|
/**
|
|
@@ -139,7 +141,7 @@ class DynamoDB {
|
|
|
139
141
|
*/
|
|
140
142
|
async batchGet(table, keys, ignoreErr) {
|
|
141
143
|
if (!keys.length) {
|
|
142
|
-
|
|
144
|
+
this.logger.trace(`Batch get ${table}: no elements to get`);
|
|
143
145
|
return [];
|
|
144
146
|
}
|
|
145
147
|
return await this.batchGetHelper(table, keys, [], Boolean(ignoreErr));
|
|
@@ -150,7 +152,7 @@ class DynamoDB {
|
|
|
150
152
|
[table]: { Keys: keys.slice(currentChunk, currentChunk + chunkSize) }
|
|
151
153
|
}
|
|
152
154
|
};
|
|
153
|
-
|
|
155
|
+
this.logger.trace(`Batch get ${table}: ${currentChunk} of ${keys.length}`);
|
|
154
156
|
let result;
|
|
155
157
|
try {
|
|
156
158
|
result = await this.dynamo.batchGet(batch);
|
|
@@ -177,7 +179,7 @@ class DynamoDB {
|
|
|
177
179
|
*/
|
|
178
180
|
async batchPut(table, items) {
|
|
179
181
|
if (!items.length)
|
|
180
|
-
return
|
|
182
|
+
return this.logger.trace(`Batch write (put) ${table}: no elements to write`);
|
|
181
183
|
await this.batchWriteHelper(table, items, true);
|
|
182
184
|
}
|
|
183
185
|
/**
|
|
@@ -189,11 +191,11 @@ class DynamoDB {
|
|
|
189
191
|
*/
|
|
190
192
|
async batchDelete(table, keys) {
|
|
191
193
|
if (!keys.length)
|
|
192
|
-
return
|
|
194
|
+
return this.logger.trace(`Batch write (delete) ${table}: no elements to write`);
|
|
193
195
|
await this.batchWriteHelper(table, keys, false);
|
|
194
196
|
}
|
|
195
197
|
async batchWriteHelper(table, itemsOrKeys, isPut, currentChunk = 0, chunkSize = 25) {
|
|
196
|
-
|
|
198
|
+
this.logger.trace(`Batch write (${isPut ? 'put' : 'delete'}) ${table}: ${currentChunk} of ${itemsOrKeys.length}`);
|
|
197
199
|
let requests;
|
|
198
200
|
if (isPut)
|
|
199
201
|
requests = itemsOrKeys.slice(currentChunk, currentChunk + chunkSize).map(i => ({ PutRequest: { Item: i } }));
|
|
@@ -218,7 +220,7 @@ class DynamoDB {
|
|
|
218
220
|
params.RequestItems = response.UnprocessedItems;
|
|
219
221
|
attempts++;
|
|
220
222
|
const waitSeconds = getRandomInt(attempts * 5);
|
|
221
|
-
|
|
223
|
+
this.logger.trace(`Batch write throttled: waiting ${waitSeconds} seconds to retry`);
|
|
222
224
|
await wait(waitSeconds);
|
|
223
225
|
}
|
|
224
226
|
else {
|
|
@@ -231,9 +233,9 @@ class DynamoDB {
|
|
|
231
233
|
* @param params the params to apply to DynamoDB's function
|
|
232
234
|
*/
|
|
233
235
|
async query(params) {
|
|
234
|
-
|
|
236
|
+
this.logger.trace(`Query ${params.TableName}`);
|
|
235
237
|
const result = await this.queryScanHelper(params, [], true);
|
|
236
|
-
|
|
238
|
+
this.logger.trace(`Results query ${params.TableName}: ${result.length ?? 0}`);
|
|
237
239
|
return result;
|
|
238
240
|
}
|
|
239
241
|
/**
|
|
@@ -241,9 +243,9 @@ class DynamoDB {
|
|
|
241
243
|
* @param params the params to apply to DynamoDB's function
|
|
242
244
|
*/
|
|
243
245
|
async scan(params) {
|
|
244
|
-
|
|
246
|
+
this.logger.trace(`Scan ${params.TableName}`);
|
|
245
247
|
const result = await this.queryScanHelper(params, [], false);
|
|
246
|
-
|
|
248
|
+
this.logger.trace(`Results scan ${params.TableName}: ${result.length ?? 0}`);
|
|
247
249
|
return result;
|
|
248
250
|
}
|
|
249
251
|
async queryScanHelper(params, items, isQuery) {
|
|
@@ -265,9 +267,9 @@ class DynamoDB {
|
|
|
265
267
|
* @param params the params to apply to DynamoDB's function
|
|
266
268
|
*/
|
|
267
269
|
async queryClassic(params) {
|
|
268
|
-
|
|
270
|
+
this.logger.trace(`Query classic ${params.TableName}`);
|
|
269
271
|
const result = await this.dynamo.query(params);
|
|
270
|
-
|
|
272
|
+
this.logger.trace(`Results query classic ${params.TableName}: ${result.Items.length ?? 0}`);
|
|
271
273
|
return result;
|
|
272
274
|
}
|
|
273
275
|
/**
|
|
@@ -275,9 +277,9 @@ class DynamoDB {
|
|
|
275
277
|
* @param params the params to apply to DynamoDB's function
|
|
276
278
|
*/
|
|
277
279
|
async scanClassic(params) {
|
|
278
|
-
|
|
280
|
+
this.logger.trace(`Scan classic ${params.TableName}`);
|
|
279
281
|
const result = await this.dynamo.scan(params);
|
|
280
|
-
|
|
282
|
+
this.logger.trace(`Results scan classic ${params.TableName}: ${result.Items.length ?? 0}`);
|
|
281
283
|
return result;
|
|
282
284
|
}
|
|
283
285
|
/**
|
|
@@ -286,8 +288,8 @@ class DynamoDB {
|
|
|
286
288
|
*/
|
|
287
289
|
async transactWrites(ops) {
|
|
288
290
|
if (!ops.length)
|
|
289
|
-
return
|
|
290
|
-
|
|
291
|
+
return this.logger.trace('Transaction writes: no elements to write');
|
|
292
|
+
this.logger.trace('Transaction writes');
|
|
291
293
|
await this.dynamo.transactWrite({ TransactItems: ops });
|
|
292
294
|
}
|
|
293
295
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import 'source-map-support/register';
|
|
2
|
-
import {
|
|
2
|
+
import { LambdaLogger } from './lambdaLogger';
|
|
3
3
|
/**
|
|
4
4
|
* An abstract class to inherit to manage some resources with an AWS Lambda function.
|
|
5
5
|
*/
|
|
6
6
|
export declare abstract class GenericController {
|
|
7
7
|
protected event: any;
|
|
8
8
|
protected callback: any;
|
|
9
|
-
protected logger:
|
|
9
|
+
protected logger: LambdaLogger;
|
|
10
10
|
/**
|
|
11
11
|
* Initialize a new GenericController helper object.
|
|
12
12
|
* @param event the event that invoked the AWS lambda function
|
|
@@ -14,11 +14,47 @@ export declare abstract class GenericController {
|
|
|
14
14
|
*/
|
|
15
15
|
constructor(event: any, callback: any);
|
|
16
16
|
/**
|
|
17
|
-
* The main function, that
|
|
17
|
+
* The main function (to override), that handles the request and must terminate invoking the method `done`.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
handleRequest(): Promise<void>;
|
|
20
20
|
/**
|
|
21
21
|
* Default callback for the Lambda.
|
|
22
22
|
*/
|
|
23
|
-
protected done(error
|
|
23
|
+
protected done(error?: Error | any, res?: any): void;
|
|
24
|
+
/**
|
|
25
|
+
* Remap an error to manage the logging and make sure no unhandled error is returned to the requester.
|
|
26
|
+
*/
|
|
27
|
+
protected handleControllerError(err: Error | HandledError | any, interceptedInContext: string, replaceWithMessage: string): HandledError | UnhandledError;
|
|
28
|
+
/**
|
|
29
|
+
* Get the current log level for the current Lambda function's `logger`.
|
|
30
|
+
* Note: "FATAL" means that no log will be printed.
|
|
31
|
+
*/
|
|
32
|
+
getLambdaLogLevel(): 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
|
|
33
|
+
/**
|
|
34
|
+
* Set the log level for the current Lambda function's `logger`.
|
|
35
|
+
*/
|
|
36
|
+
setLambdaLogLevel(logLevel: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL'): void;
|
|
37
|
+
/**
|
|
38
|
+
* Raise the log level of the current Lambda function's `logger` to "FATAL", hence avoiding printing any log.
|
|
39
|
+
*/
|
|
40
|
+
silentLambdaLogs(): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A specific type of error in the context of the Controller, to distinguish from "unhandled" errors.
|
|
44
|
+
*/
|
|
45
|
+
export declare class HandledError extends Error {
|
|
46
|
+
constructor(message: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* An unhandled error thrown inside the controller (i.e. `!(error instanceof HandledError)`) .
|
|
50
|
+
*/
|
|
51
|
+
export declare class UnhandledError extends Error {
|
|
52
|
+
/**
|
|
53
|
+
* The context where the unhandled error was intercepted.
|
|
54
|
+
*/
|
|
55
|
+
unhandled: string;
|
|
56
|
+
/**
|
|
57
|
+
* The original error message before it was replaced by a public-facing message.
|
|
58
|
+
*/
|
|
59
|
+
internalMessage: string;
|
|
24
60
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GenericController = void 0;
|
|
3
|
+
exports.UnhandledError = exports.HandledError = exports.GenericController = void 0;
|
|
4
4
|
require("source-map-support/register");
|
|
5
|
-
const
|
|
5
|
+
const lambdaLogger_1 = require("./lambdaLogger");
|
|
6
6
|
/**
|
|
7
7
|
* An abstract class to inherit to manage some resources with an AWS Lambda function.
|
|
8
8
|
*/
|
|
@@ -13,19 +13,77 @@ class GenericController {
|
|
|
13
13
|
* @param callback the callback to resolve or reject the execution
|
|
14
14
|
*/
|
|
15
15
|
constructor(event, callback) {
|
|
16
|
-
this.logger = new
|
|
16
|
+
this.logger = new lambdaLogger_1.LambdaLogger();
|
|
17
17
|
this.event = event;
|
|
18
18
|
this.callback = callback;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The main function (to override), that handles the request and must terminate invoking the method `done`.
|
|
22
|
+
*/
|
|
23
|
+
async handleRequest() {
|
|
24
|
+
this.logger.info('START');
|
|
25
|
+
this.done();
|
|
26
|
+
}
|
|
20
27
|
/**
|
|
21
28
|
* Default callback for the Lambda.
|
|
22
29
|
*/
|
|
23
|
-
done(error, res) {
|
|
24
|
-
if (error)
|
|
25
|
-
|
|
30
|
+
done(error = null, res) {
|
|
31
|
+
if (error) {
|
|
32
|
+
if (error.unhandled)
|
|
33
|
+
this.logger.error('END-FAILED', error);
|
|
34
|
+
else
|
|
35
|
+
this.logger.warn('END-FAILED', error);
|
|
36
|
+
}
|
|
26
37
|
else
|
|
27
38
|
this.logger.info('END-SUCCESS');
|
|
28
39
|
this.callback(error, res);
|
|
29
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Remap an error to manage the logging and make sure no unhandled error is returned to the requester.
|
|
43
|
+
*/
|
|
44
|
+
handleControllerError(err, interceptedInContext, replaceWithMessage) {
|
|
45
|
+
if (err instanceof HandledError)
|
|
46
|
+
return err;
|
|
47
|
+
const error = err;
|
|
48
|
+
error.unhandled = interceptedInContext;
|
|
49
|
+
error.internalMessage = error.message;
|
|
50
|
+
error.message = replaceWithMessage;
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the current log level for the current Lambda function's `logger`.
|
|
55
|
+
* Note: "FATAL" means that no log will be printed.
|
|
56
|
+
*/
|
|
57
|
+
getLambdaLogLevel() {
|
|
58
|
+
return process.env.AWS_LAMBDA_LOG_LEVEL;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Set the log level for the current Lambda function's `logger`.
|
|
62
|
+
*/
|
|
63
|
+
setLambdaLogLevel(logLevel) {
|
|
64
|
+
process.env.AWS_LAMBDA_LOG_LEVEL = logLevel;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Raise the log level of the current Lambda function's `logger` to "FATAL", hence avoiding printing any log.
|
|
68
|
+
*/
|
|
69
|
+
silentLambdaLogs() {
|
|
70
|
+
process.env.AWS_LAMBDA_LOG_LEVEL = 'FATAL';
|
|
71
|
+
}
|
|
30
72
|
}
|
|
31
73
|
exports.GenericController = GenericController;
|
|
74
|
+
/**
|
|
75
|
+
* A specific type of error in the context of the Controller, to distinguish from "unhandled" errors.
|
|
76
|
+
*/
|
|
77
|
+
class HandledError extends Error {
|
|
78
|
+
constructor(message) {
|
|
79
|
+
super(message);
|
|
80
|
+
Object.setPrototypeOf(this, HandledError.prototype);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.HandledError = HandledError;
|
|
84
|
+
/**
|
|
85
|
+
* An unhandled error thrown inside the controller (i.e. `!(error instanceof HandledError)`) .
|
|
86
|
+
*/
|
|
87
|
+
class UnhandledError extends Error {
|
|
88
|
+
}
|
|
89
|
+
exports.UnhandledError = UnhandledError;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage structured logging in the context of a Lambda function.
|
|
3
|
+
* Note: the log level is controlled by each Lambda function's configuration.
|
|
4
|
+
*/
|
|
5
|
+
export declare class LambdaLogger {
|
|
6
|
+
shouldLog: (logLevel: 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL') => boolean;
|
|
7
|
+
trace: (summary: string, content?: object) => void;
|
|
8
|
+
debug: (summary: string, content?: object) => void;
|
|
9
|
+
info: (summary: string, content?: object) => void;
|
|
10
|
+
warn: (summary: string, error: Error | any, content?: object) => void;
|
|
11
|
+
error: (summary: string, error: Error | any, content?: object) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const LOG_LEVELS_PRIORITY: Record<string, number>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOG_LEVELS_PRIORITY = exports.LambdaLogger = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Manage structured logging in the context of a Lambda function.
|
|
6
|
+
* Note: the log level is controlled by each Lambda function's configuration.
|
|
7
|
+
*/
|
|
8
|
+
class LambdaLogger {
|
|
9
|
+
constructor() {
|
|
10
|
+
// note: this is needed as long as the Lambda functions don't become reactive to changes to `AWS_LAMBDA_LOG_LEVEL`
|
|
11
|
+
this.shouldLog = (logLevel) => exports.LOG_LEVELS_PRIORITY[logLevel] >= exports.LOG_LEVELS_PRIORITY[process.env.AWS_LAMBDA_LOG_LEVEL];
|
|
12
|
+
this.trace = (summary, content = {}) => {
|
|
13
|
+
if (this.shouldLog('TRACE'))
|
|
14
|
+
console.trace({ summary, ...content });
|
|
15
|
+
};
|
|
16
|
+
this.debug = (summary, content = {}) => {
|
|
17
|
+
if (this.shouldLog('DEBUG'))
|
|
18
|
+
console.debug({ summary, ...content });
|
|
19
|
+
};
|
|
20
|
+
this.info = (summary, content = {}) => {
|
|
21
|
+
if (this.shouldLog('INFO'))
|
|
22
|
+
console.info({ summary, ...content });
|
|
23
|
+
};
|
|
24
|
+
this.warn = (summary, error, content = {}) => {
|
|
25
|
+
if (this.shouldLog('WARN'))
|
|
26
|
+
console.warn({ summary, ...content, error });
|
|
27
|
+
};
|
|
28
|
+
this.error = (summary, error, content = {}) => {
|
|
29
|
+
if (this.shouldLog('ERROR'))
|
|
30
|
+
console.error({ summary, ...content, error });
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.LambdaLogger = LambdaLogger;
|
|
35
|
+
// levels here are identical to bunyan practices (https://github.com/trentm/node-bunyan#levels)
|
|
36
|
+
exports.LOG_LEVELS_PRIORITY = {
|
|
37
|
+
TRACE: 10,
|
|
38
|
+
DEBUG: 20,
|
|
39
|
+
INFO: 30,
|
|
40
|
+
WARN: 40,
|
|
41
|
+
ERROR: 50,
|
|
42
|
+
FATAL: 60
|
|
43
|
+
};
|
|
@@ -43,7 +43,6 @@ export declare abstract class ResourceController extends GenericController {
|
|
|
43
43
|
*/
|
|
44
44
|
protected getQueryParamAsArray(paramName: string): string[];
|
|
45
45
|
handleRequest: () => Promise<void>;
|
|
46
|
-
private remapHandlerError;
|
|
47
46
|
protected done(error?: Error | any, rawResult?: any, statusCode?: number): void;
|
|
48
47
|
/**
|
|
49
48
|
* To @override
|
|
@@ -225,9 +224,3 @@ export interface InternalAPIRequestParams {
|
|
|
225
224
|
*/
|
|
226
225
|
body?: any;
|
|
227
226
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Explicitly define a specific type of error to use in the RC's handler, to distinguish it from the normal errors.
|
|
230
|
-
*/
|
|
231
|
-
export declare class RCError extends Error {
|
|
232
|
-
constructor(message: string);
|
|
233
|
-
}
|
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
26
|
+
exports.ResourceController = void 0;
|
|
27
27
|
const fs_1 = require("fs");
|
|
28
28
|
const Lambda = __importStar(require("@aws-sdk/client-lambda"));
|
|
29
29
|
const EventBridge = __importStar(require("@aws-sdk/client-eventbridge"));
|
|
@@ -49,6 +49,7 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
49
49
|
/// REQUEST HANDLERS
|
|
50
50
|
///
|
|
51
51
|
this.handleRequest = async () => {
|
|
52
|
+
this.logger.info('START', { event: this.getEventSummary() });
|
|
52
53
|
if (this.initError)
|
|
53
54
|
return;
|
|
54
55
|
try {
|
|
@@ -77,7 +78,7 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
77
78
|
response = await this.headResource();
|
|
78
79
|
break;
|
|
79
80
|
default:
|
|
80
|
-
this.done(new
|
|
81
|
+
this.done(new genericController_1.HandledError('Unsupported method'));
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
else {
|
|
@@ -102,17 +103,17 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
102
103
|
response = await this.headResources();
|
|
103
104
|
break;
|
|
104
105
|
default:
|
|
105
|
-
this.done(new
|
|
106
|
+
this.done(new genericController_1.HandledError('Unsupported method'));
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
this.done(null, response);
|
|
109
110
|
}
|
|
110
111
|
catch (err) {
|
|
111
|
-
this.done(this.
|
|
112
|
+
this.done(this.handleControllerError(err, 'HANDLER-ERROR', 'Operation failed'));
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
catch (err) {
|
|
115
|
-
this.done(this.
|
|
116
|
+
this.done(this.handleControllerError(err, 'AUTH-CHECK-ERROR', 'Forbidden'));
|
|
116
117
|
}
|
|
117
118
|
};
|
|
118
119
|
this.event = event;
|
|
@@ -138,11 +139,10 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
138
139
|
}
|
|
139
140
|
if (options.useMetrics)
|
|
140
141
|
this.prepareMetrics();
|
|
141
|
-
this.logger.info('START', { event: this.getEventSummary() });
|
|
142
142
|
}
|
|
143
143
|
catch (err) {
|
|
144
144
|
this.initError = true;
|
|
145
|
-
this.done(this.
|
|
145
|
+
this.done(this.handleControllerError(err, 'INIT-ERROR', 'Malformed request'));
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
initFromEventV2(event, options) {
|
|
@@ -165,7 +165,7 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
165
165
|
this.body = (event.body ? JSON.parse(event.body) : {}) || {};
|
|
166
166
|
}
|
|
167
167
|
catch (error) {
|
|
168
|
-
throw new
|
|
168
|
+
throw new genericController_1.HandledError('Malformed body');
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
initFromEventV1(event, options) {
|
|
@@ -187,7 +187,7 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
187
187
|
this.body = (event.body ? JSON.parse(event.body) : {}) || {};
|
|
188
188
|
}
|
|
189
189
|
catch (error) {
|
|
190
|
-
throw new
|
|
190
|
+
throw new genericController_1.HandledError('Malformed body');
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
getEventSummary() {
|
|
@@ -213,22 +213,18 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
213
213
|
else
|
|
214
214
|
return String(this.queryParams[paramName]).split(',');
|
|
215
215
|
}
|
|
216
|
-
remapHandlerError(err, interceptedInContext, replaceWithMessage) {
|
|
217
|
-
if (err instanceof RCError)
|
|
218
|
-
return err;
|
|
219
|
-
const error = err;
|
|
220
|
-
error.intercepted = interceptedInContext;
|
|
221
|
-
error.internalMessage = error.message;
|
|
222
|
-
error.message = replaceWithMessage;
|
|
223
|
-
return error;
|
|
224
|
-
}
|
|
225
216
|
done(error, rawResult, statusCode = this.returnStatusCode ?? (error ? 400 : 200)) {
|
|
226
217
|
const result = error ? { message: error.message } : rawResult ?? {};
|
|
227
|
-
if (error)
|
|
228
|
-
this.logger.error('END-FAILED', error, { statusCode, event: this.getEventSummary() });
|
|
229
|
-
else
|
|
230
|
-
this.logger.info('END-SUCCESS', { statusCode, event: this.getEventSummary() });
|
|
231
218
|
this.logger.debug('END-DETAIL', { result: Array.isArray(result) ? { array: result.length } : result });
|
|
219
|
+
const finalLogContent = { statusCode, event: this.getEventSummary() };
|
|
220
|
+
if (error) {
|
|
221
|
+
if (error.unhandled)
|
|
222
|
+
this.logger.error('END-FAILED', error, finalLogContent);
|
|
223
|
+
else
|
|
224
|
+
this.logger.warn('END-FAILED', error, finalLogContent);
|
|
225
|
+
}
|
|
226
|
+
else
|
|
227
|
+
this.logger.info('END-SUCCESS', finalLogContent);
|
|
232
228
|
if (this.logRequestsWithKey)
|
|
233
229
|
this.storeLog(!error);
|
|
234
230
|
if (this.metrics)
|
|
@@ -249,73 +245,73 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
249
245
|
* To @override
|
|
250
246
|
*/
|
|
251
247
|
async getResource() {
|
|
252
|
-
throw new
|
|
248
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
253
249
|
}
|
|
254
250
|
/**
|
|
255
251
|
* To @override
|
|
256
252
|
*/
|
|
257
253
|
async postResource() {
|
|
258
|
-
throw new
|
|
254
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
259
255
|
}
|
|
260
256
|
/**
|
|
261
257
|
* To @override
|
|
262
258
|
*/
|
|
263
259
|
async putResource() {
|
|
264
|
-
throw new
|
|
260
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
265
261
|
}
|
|
266
262
|
/**
|
|
267
263
|
* To @override
|
|
268
264
|
*/
|
|
269
265
|
async deleteResource() {
|
|
270
|
-
throw new
|
|
266
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
271
267
|
}
|
|
272
268
|
/**
|
|
273
269
|
* To @override
|
|
274
270
|
*/
|
|
275
271
|
async headResource() {
|
|
276
|
-
throw new
|
|
272
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
277
273
|
}
|
|
278
274
|
/**
|
|
279
275
|
* To @override
|
|
280
276
|
*/
|
|
281
277
|
async getResources() {
|
|
282
|
-
throw new
|
|
278
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
283
279
|
}
|
|
284
280
|
/**
|
|
285
281
|
* To @override
|
|
286
282
|
*/
|
|
287
283
|
async postResources() {
|
|
288
|
-
throw new
|
|
284
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
289
285
|
}
|
|
290
286
|
/**
|
|
291
287
|
* To @override
|
|
292
288
|
*/
|
|
293
289
|
async putResources() {
|
|
294
|
-
throw new
|
|
290
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
295
291
|
}
|
|
296
292
|
/**
|
|
297
293
|
* To @override
|
|
298
294
|
*/
|
|
299
295
|
async patchResource() {
|
|
300
|
-
throw new
|
|
296
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
301
297
|
}
|
|
302
298
|
/**
|
|
303
299
|
* To @override
|
|
304
300
|
*/
|
|
305
301
|
async patchResources() {
|
|
306
|
-
throw new
|
|
302
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
307
303
|
}
|
|
308
304
|
/**
|
|
309
305
|
* To @override
|
|
310
306
|
*/
|
|
311
307
|
async deleteResources() {
|
|
312
|
-
throw new
|
|
308
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
313
309
|
}
|
|
314
310
|
/**
|
|
315
311
|
* To @override
|
|
316
312
|
*/
|
|
317
313
|
async headResources() {
|
|
318
|
-
throw new
|
|
314
|
+
throw new genericController_1.HandledError('Unsupported method');
|
|
319
315
|
}
|
|
320
316
|
///
|
|
321
317
|
/// HELPERS
|
|
@@ -537,18 +533,3 @@ class ResourceController extends genericController_1.GenericController {
|
|
|
537
533
|
}
|
|
538
534
|
}
|
|
539
535
|
exports.ResourceController = ResourceController;
|
|
540
|
-
/**
|
|
541
|
-
* Explicitly define a specific type of error to use in the RC's handler, to distinguish it from the normal errors.
|
|
542
|
-
*/
|
|
543
|
-
class RCError extends Error {
|
|
544
|
-
constructor(message) {
|
|
545
|
-
super(message);
|
|
546
|
-
Object.setPrototypeOf(this, RCError.prototype);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
exports.RCError = RCError;
|
|
550
|
-
/**
|
|
551
|
-
* An unhandled error thrown inside the RC (i.e. `!(error instanceof RCError)`) .
|
|
552
|
-
*/
|
|
553
|
-
class RCUnhandledError extends Error {
|
|
554
|
-
}
|
package/dist/src/s3.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as AWSS3 from '@aws-sdk/client-s3';
|
|
2
2
|
import { BodyDataTypes } from '@aws-sdk/lib-storage';
|
|
3
3
|
import { SignedURL } from 'idea-toolbox';
|
|
4
|
+
import { LambdaLogger } from './lambdaLogger';
|
|
4
5
|
/**
|
|
5
6
|
* A wrapper for AWS Simple Storage Service.
|
|
6
7
|
*/
|
|
7
8
|
export declare class S3 {
|
|
8
9
|
protected s3: AWSS3.S3Client;
|
|
10
|
+
protected logger: LambdaLogger;
|
|
9
11
|
protected DEFAULT_DOWNLOAD_BUCKET_PREFIX: string;
|
|
10
12
|
protected DEFAULT_DOWNLOAD_BUCKET: string;
|
|
11
13
|
protected DEFAULT_DOWNLOAD_BUCKET_SEC_TO_EXP: number;
|
package/dist/src/s3.js
CHANGED
|
@@ -28,11 +28,13 @@ const AWSS3 = __importStar(require("@aws-sdk/client-s3"));
|
|
|
28
28
|
const lib_storage_1 = require("@aws-sdk/lib-storage");
|
|
29
29
|
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
30
30
|
const idea_toolbox_1 = require("idea-toolbox");
|
|
31
|
+
const lambdaLogger_1 = require("./lambdaLogger");
|
|
31
32
|
/**
|
|
32
33
|
* A wrapper for AWS Simple Storage Service.
|
|
33
34
|
*/
|
|
34
35
|
class S3 {
|
|
35
36
|
constructor() {
|
|
37
|
+
this.logger = new lambdaLogger_1.LambdaLogger();
|
|
36
38
|
this.DEFAULT_DOWNLOAD_BUCKET_PREFIX = 'common';
|
|
37
39
|
this.DEFAULT_DOWNLOAD_BUCKET = 'idea-downloads';
|
|
38
40
|
this.DEFAULT_DOWNLOAD_BUCKET_SEC_TO_EXP = 180;
|
|
@@ -81,7 +83,7 @@ class S3 {
|
|
|
81
83
|
* Make a copy of an object of the bucket.
|
|
82
84
|
*/
|
|
83
85
|
async copyObject(options) {
|
|
84
|
-
|
|
86
|
+
this.logger.trace(`S3 copy object: ${options.key}`);
|
|
85
87
|
const command = new AWSS3.CopyObjectCommand({
|
|
86
88
|
CopySource: options.copySource,
|
|
87
89
|
Bucket: options.bucket,
|
|
@@ -93,7 +95,7 @@ class S3 {
|
|
|
93
95
|
* Get an object from a S3 bucket.
|
|
94
96
|
*/
|
|
95
97
|
async getObject(options) {
|
|
96
|
-
|
|
98
|
+
this.logger.trace(`S3 get object: ${options.key}`);
|
|
97
99
|
const params = { Bucket: options.bucket, Key: options.key };
|
|
98
100
|
if (options.filename)
|
|
99
101
|
params.ResponseContentDisposition = `attachment; filename ="${(0, exports.cleanFilename)(options.filename)}"`;
|
|
@@ -127,14 +129,14 @@ class S3 {
|
|
|
127
129
|
params.Metadata = options.metadata;
|
|
128
130
|
if (options.filename)
|
|
129
131
|
params.ContentDisposition = `attachment; filename ="${(0, exports.cleanFilename)(options.filename)}"`;
|
|
130
|
-
|
|
132
|
+
this.logger.trace(`S3 put object: ${options.key}`);
|
|
131
133
|
return await this.s3.send(new AWSS3.PutObjectCommand(params));
|
|
132
134
|
}
|
|
133
135
|
/**
|
|
134
136
|
* Delete an object from an S3 bucket.
|
|
135
137
|
*/
|
|
136
138
|
async deleteObject(options) {
|
|
137
|
-
|
|
139
|
+
this.logger.trace(`S3 delete object: ${options.key}`);
|
|
138
140
|
const deleteCommand = new AWSS3.DeleteObjectCommand({ Bucket: options.bucket, Key: options.key });
|
|
139
141
|
return await this.s3.send(deleteCommand);
|
|
140
142
|
}
|
|
@@ -142,7 +144,7 @@ class S3 {
|
|
|
142
144
|
* List the objects of an S3 bucket.
|
|
143
145
|
*/
|
|
144
146
|
async listObjects(options) {
|
|
145
|
-
|
|
147
|
+
this.logger.trace(`S3 list object: ${options.prefix}`);
|
|
146
148
|
const command = new AWSS3.ListObjectsCommand({ Bucket: options.bucket, Prefix: options.prefix });
|
|
147
149
|
return await this.s3.send(command);
|
|
148
150
|
}
|
package/dist/src/ses.d.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import * as AWSSES from '@aws-sdk/client-sesv2';
|
|
3
3
|
import { SentMessageInfo as NodemailerSentMessageInfo } from 'nodemailer';
|
|
4
4
|
import { Headers } from 'nodemailer/lib/mailer';
|
|
5
|
+
import { LambdaLogger } from './lambdaLogger';
|
|
5
6
|
/**
|
|
6
7
|
* A wrapper for AWS Simple Email Service.
|
|
7
8
|
*/
|
|
8
9
|
export declare class SES {
|
|
9
10
|
protected ses: AWSSES.SESv2Client;
|
|
11
|
+
protected logger: LambdaLogger;
|
|
10
12
|
constructor(options?: {
|
|
11
13
|
region?: string;
|
|
12
14
|
});
|
package/dist/src/ses.js
CHANGED
|
@@ -28,11 +28,13 @@ const client_ses_1 = require("@aws-sdk/client-ses");
|
|
|
28
28
|
const AWSSES = __importStar(require("@aws-sdk/client-sesv2"));
|
|
29
29
|
const nodemailer_1 = require("nodemailer");
|
|
30
30
|
const dynamoDB_1 = require("./dynamoDB");
|
|
31
|
+
const lambdaLogger_1 = require("./lambdaLogger");
|
|
31
32
|
/**
|
|
32
33
|
* A wrapper for AWS Simple Email Service.
|
|
33
34
|
*/
|
|
34
35
|
class SES {
|
|
35
36
|
constructor(options = {}) {
|
|
37
|
+
this.logger = new lambdaLogger_1.LambdaLogger();
|
|
36
38
|
this.ses = new AWSSES.SESv2Client({ region: options.region });
|
|
37
39
|
}
|
|
38
40
|
//
|
|
@@ -104,7 +106,7 @@ class SES {
|
|
|
104
106
|
ses = this.ses;
|
|
105
107
|
else
|
|
106
108
|
ses = new AWSSES.SESv2Client({ region: sesParams.region });
|
|
107
|
-
|
|
109
|
+
this.logger.trace('SES send templated email');
|
|
108
110
|
return await ses.send(command);
|
|
109
111
|
}
|
|
110
112
|
/**
|
|
@@ -144,7 +146,7 @@ class SES {
|
|
|
144
146
|
ses = this.ses;
|
|
145
147
|
else
|
|
146
148
|
ses = new AWSSES.SESv2Client({ region: sesParams.region });
|
|
147
|
-
|
|
149
|
+
this.logger.trace('SES send email');
|
|
148
150
|
return await ses.send(command);
|
|
149
151
|
}
|
|
150
152
|
async sendEmailWithNodemailer(emailData, sesParams) {
|
|
@@ -165,7 +167,7 @@ class SES {
|
|
|
165
167
|
mailOptions.attachments = emailData.attachments;
|
|
166
168
|
// note: Nodemailer doesn't support SESv2 as of August 2023
|
|
167
169
|
const ses = new client_ses_1.SESClient({ region: sesParams.region });
|
|
168
|
-
|
|
170
|
+
this.logger.trace('SES send email (Nodemailer)');
|
|
169
171
|
// note: this is a workaround to make Nodemailer works with AWS SDK 3;
|
|
170
172
|
// see: https://github.com/nodemailer/nodemailer/issues/1430
|
|
171
173
|
return await (0, nodemailer_1.createTransport)({ SES: { ses, aws: { SendRawEmailCommand: client_ses_1.SendRawEmailCommand } } }).sendMail(mailOptions);
|
package/dist/src/sns.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as AWSSNS from '@aws-sdk/client-sns';
|
|
2
2
|
import { PushNotificationsPlatforms } from 'idea-toolbox';
|
|
3
|
+
import { LambdaLogger } from './lambdaLogger';
|
|
3
4
|
/**
|
|
4
5
|
* A wrapper for AWS Simple Notification Service.
|
|
5
6
|
*/
|
|
6
7
|
export declare class SNS {
|
|
7
8
|
protected client: AWSSNS.SNSClient;
|
|
9
|
+
protected logger: LambdaLogger;
|
|
8
10
|
constructor(options?: {
|
|
9
11
|
region?: string;
|
|
10
12
|
});
|
package/dist/src/sns.js
CHANGED
|
@@ -26,11 +26,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
exports.SNS = void 0;
|
|
27
27
|
const AWSSNS = __importStar(require("@aws-sdk/client-sns"));
|
|
28
28
|
const idea_toolbox_1 = require("idea-toolbox");
|
|
29
|
+
const lambdaLogger_1 = require("./lambdaLogger");
|
|
29
30
|
/**
|
|
30
31
|
* A wrapper for AWS Simple Notification Service.
|
|
31
32
|
*/
|
|
32
33
|
class SNS {
|
|
33
34
|
constructor(options = {}) {
|
|
35
|
+
this.logger = new lambdaLogger_1.LambdaLogger();
|
|
34
36
|
this.client = new AWSSNS.SNSClient({ region: options.region });
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
@@ -52,7 +54,7 @@ class SNS {
|
|
|
52
54
|
default:
|
|
53
55
|
throw new Error('Unsupported platform');
|
|
54
56
|
}
|
|
55
|
-
|
|
57
|
+
this.logger.trace('SNS add platform endpoint');
|
|
56
58
|
const command = new AWSSNS.CreatePlatformEndpointCommand({ PlatformApplicationArn: platformARN, Token: token });
|
|
57
59
|
const { EndpointArn } = await this.client.send(command);
|
|
58
60
|
return EndpointArn;
|
|
@@ -80,7 +82,7 @@ class SNS {
|
|
|
80
82
|
default:
|
|
81
83
|
throw new Error('Unsupported platform');
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
this.logger.trace('SNS publish in topic');
|
|
84
86
|
const command = new AWSSNS.PublishCommand({
|
|
85
87
|
MessageStructure: 'json',
|
|
86
88
|
Message: JSON.stringify(structuredMessage),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DynamoDBRecord } from 'aws-lambda';
|
|
1
2
|
import { GenericController } from './genericController';
|
|
2
3
|
/**
|
|
3
4
|
* An abstract class to inherit to manage AWS DDB streams in an AWS Lambda function.
|
|
@@ -5,4 +6,6 @@ import { GenericController } from './genericController';
|
|
|
5
6
|
export declare abstract class StreamController extends GenericController {
|
|
6
7
|
records: any[];
|
|
7
8
|
constructor(event: any, callback: any);
|
|
9
|
+
protected abstract handleRecord(record: DynamoDBRecord): Promise<void>;
|
|
10
|
+
handleRequest(): Promise<void>;
|
|
8
11
|
}
|
|
@@ -9,7 +9,12 @@ class StreamController extends genericController_1.GenericController {
|
|
|
9
9
|
constructor(event, callback) {
|
|
10
10
|
super(event, callback);
|
|
11
11
|
this.records = event.Records ?? [];
|
|
12
|
-
|
|
12
|
+
}
|
|
13
|
+
async handleRequest() {
|
|
14
|
+
this.logger.info('START', { streamOfRecords: this.records.length ?? 0 });
|
|
15
|
+
await Promise.all(this.records.map(record => this.handleRecord(record)))
|
|
16
|
+
.then(() => this.done())
|
|
17
|
+
.catch((err) => this.done(this.handleControllerError(err, 'STREAM-ERROR', 'Operation failed')));
|
|
13
18
|
}
|
|
14
19
|
}
|
|
15
20
|
exports.StreamController = StreamController;
|
package/index.ts
CHANGED