idea-aws 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,24 +27,16 @@ exports.DynamoDB = void 0;
27
27
  const DDB = __importStar(require("@aws-sdk/lib-dynamodb"));
28
28
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
29
29
  const DDBUtils = __importStar(require("@aws-sdk/util-dynamodb"));
30
- const uuid_1 = require("uuid");
31
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
32
- // @ts-ignore
33
30
  const nanoid_1 = require("nanoid");
34
31
  const NanoID = (0, nanoid_1.customAlphabet)('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 25);
35
- const shortid_1 = require("shortid");
36
- const logger_1 = require("./logger");
37
32
  /**
38
33
  * A wrapper for AWS DynamoDB.
39
34
  */
40
35
  class DynamoDB {
41
- constructor(params) {
42
- this.logger = new logger_1.Logger();
43
- const options = Object.assign({}, params, { debug: true });
36
+ constructor() {
44
37
  this.dynamo = DDB.DynamoDBDocument.from(new client_dynamodb_1.DynamoDB(), {
45
38
  marshallOptions: { convertEmptyValues: true, removeUndefinedValues: true, convertClassInstanceToMap: true }
46
39
  });
47
- this.logger.level = options.debug ? 'DEBUG' : 'INFO';
48
40
  }
49
41
  /**
50
42
  * Convert a JSON object from DynamoDB format to simple JSON.
@@ -54,19 +46,6 @@ class DynamoDB {
54
46
  unmarshall(data, options) {
55
47
  return DDBUtils.unmarshall(data, options);
56
48
  }
57
- /**
58
- * Returns an IUID: IDEA's Unique IDentifier, which is an id unique through an IDEA's AWS account and region.
59
- * Note: no need of an auth check for external uses: the permissions depend from the context in which it's executed.
60
- * @deprecated use IUNID instead (nano version)
61
- * @param project project code
62
- * @return the IUID
63
- */
64
- async IUID(project) {
65
- const MAX_ATTEMPTS = 3;
66
- if (!project)
67
- throw new Error('Missing project');
68
- return await this.identifiersGeneratorHelper(project, 'IUID', 0, MAX_ATTEMPTS);
69
- }
70
49
  /**
71
50
  * Returns an IUNID: IDEA's Unique Nano IDentifier, which is an id unique through an IDEA's AWS account and region.
72
51
  * Note: no need of an auth check for external uses: the permissions depend from the context in which it's executed.
@@ -77,43 +56,16 @@ class DynamoDB {
77
56
  const MAX_ATTEMPTS = 3;
78
57
  if (!project)
79
58
  throw new Error('Missing project');
80
- return await this.identifiersGeneratorHelper(project, 'IUNID', 0, MAX_ATTEMPTS);
81
- }
82
- /**
83
- * Returns an ISID: IDEA's Short IDentifier, which is a short, unique id intended to be used in small namespaces.
84
- * Note: no need of an auth check for external uses: the permissions depend from the context in which it's executed.
85
- * @param project project code
86
- * @return the ISID
87
- */
88
- async ISID(project) {
89
- const MAX_ATTEMPTS = 3;
90
- if (!project)
91
- throw new Error('Missing project');
92
- return await this.identifiersGeneratorHelper(project, 'ISID', 0, MAX_ATTEMPTS);
59
+ return await this.IUNIDHelper(project, 0, MAX_ATTEMPTS);
93
60
  }
94
- async identifiersGeneratorHelper(project, type, attempt, maxAttempts) {
61
+ async IUNIDHelper(project, attempt, maxAttempts) {
95
62
  if (attempt > maxAttempts)
96
63
  throw new Error('Operation failed');
97
- let id, result;
98
- switch (type) {
99
- case 'IUNID':
100
- id = NanoID();
101
- result = `${project}_${id}`;
102
- break;
103
- case 'IUID':
104
- id = (0, uuid_1.v4)();
105
- result = `${project}_${id}`;
106
- break;
107
- case 'ISID':
108
- // avoid _ characters (to avoid concatenation problems with ids) -- it must be anyway 64 chars-long
109
- (0, shortid_1.characters)('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-@');
110
- id = (0, shortid_1.generate)();
111
- result = id;
112
- break;
113
- }
64
+ const id = NanoID();
65
+ const result = `${project}_${id}`;
114
66
  try {
115
67
  await this.put({
116
- TableName: 'idea_'.concat(type),
68
+ TableName: 'idea_IUNID',
117
69
  Item: { project, id },
118
70
  ConditionExpression: 'NOT (#p = :project AND #id = :id)',
119
71
  ExpressionAttributeNames: { '#p': 'project', '#id': 'id' },
@@ -123,7 +75,7 @@ class DynamoDB {
123
75
  }
124
76
  catch (err) {
125
77
  // ID exists, try again
126
- await this.identifiersGeneratorHelper(project, type, attempt + 1, maxAttempts);
78
+ await this.IUNIDHelper(project, attempt + 1, maxAttempts);
127
79
  }
128
80
  }
129
81
  /**
@@ -132,36 +84,35 @@ class DynamoDB {
132
84
  * @param key the key of the counter
133
85
  */
134
86
  async getAtomicCounterByKey(key) {
135
- this.logger.debug(`Get atomic counter for ${key}`);
136
- const result = await this.update({
87
+ console.debug(`Get atomic counter for ${key}`);
88
+ const { Attributes } = await this.update({
137
89
  TableName: 'idea_atomicCounters',
138
90
  Key: { key },
139
91
  UpdateExpression: 'ADD atomicCounter :increment',
140
92
  ExpressionAttributeValues: { ':increment': 1 },
141
93
  ReturnValues: 'UPDATED_NEW'
142
94
  });
143
- if (!result?.Attributes?.atomicCounter)
95
+ if (!Attributes.atomicCounter)
144
96
  throw new Error('Operation failed');
145
- else
146
- return result.Attributes.atomicCounter;
97
+ return Attributes.atomicCounter;
147
98
  }
148
99
  /**
149
100
  * Get an item of a DynamoDB table.
150
101
  * @param params the params to apply to DynamoDB's function
151
102
  */
152
103
  async get(params) {
153
- this.logger.debug(`Get ${params.TableName}`);
154
- const result = await this.dynamo.get(params);
155
- if (!result?.Item)
104
+ console.debug(`Get ${params.TableName}`);
105
+ const { Item } = await this.dynamo.get(params);
106
+ if (!Item)
156
107
  throw new Error('Not found');
157
- return result.Item;
108
+ return Item;
158
109
  }
159
110
  /**
160
111
  * Put an item in a DynamoDB table.
161
112
  * @param params the params to apply to DynamoDB's function
162
113
  */
163
114
  async put(params) {
164
- this.logger.debug(`Put ${params.TableName}`);
115
+ console.debug(`Put ${params.TableName}`);
165
116
  return await this.dynamo.put(params);
166
117
  }
167
118
  /**
@@ -169,7 +120,7 @@ class DynamoDB {
169
120
  * @param params the params to apply to DynamoDB's function
170
121
  */
171
122
  async update(params) {
172
- this.logger.debug(`Update ${params.TableName}`);
123
+ console.debug(`Update ${params.TableName}`);
173
124
  return await this.dynamo.update(params);
174
125
  }
175
126
  /**
@@ -177,7 +128,7 @@ class DynamoDB {
177
128
  * @param params the params to apply to DynamoDB's function
178
129
  */
179
130
  async delete(params) {
180
- this.logger.debug(`Delete ${params.TableName}`);
131
+ console.debug(`Delete ${params.TableName}`);
181
132
  return await this.dynamo.delete(params);
182
133
  }
183
134
  /**
@@ -188,7 +139,7 @@ class DynamoDB {
188
139
  */
189
140
  async batchGet(table, keys, ignoreErr) {
190
141
  if (!keys.length) {
191
- this.logger.debug(`Batch get ${table}: no elements to get`);
142
+ console.debug(`Batch get ${table}: no elements to get`);
192
143
  return [];
193
144
  }
194
145
  return await this.batchGetHelper(table, keys, [], Boolean(ignoreErr));
@@ -199,7 +150,7 @@ class DynamoDB {
199
150
  [table]: { Keys: keys.slice(currentChunk, currentChunk + chunkSize) }
200
151
  }
201
152
  };
202
- this.logger.debug(`Batch get ${table}: ${currentChunk} of ${keys.length}`);
153
+ console.debug(`Batch get ${table}: ${currentChunk} of ${keys.length}`);
203
154
  let result;
204
155
  try {
205
156
  result = await this.dynamo.batchGet(batch);
@@ -226,7 +177,7 @@ class DynamoDB {
226
177
  */
227
178
  async batchPut(table, items) {
228
179
  if (!items.length)
229
- return this.logger.debug(`Batch write (put) ${table}: no elements to write`);
180
+ return console.debug(`Batch write (put) ${table}: no elements to write`);
230
181
  await this.batchWriteHelper(table, items, true);
231
182
  }
232
183
  /**
@@ -238,11 +189,11 @@ class DynamoDB {
238
189
  */
239
190
  async batchDelete(table, keys) {
240
191
  if (!keys.length)
241
- return this.logger.debug(`Batch write (delete) ${table}: no elements to write`);
192
+ return console.debug(`Batch write (delete) ${table}: no elements to write`);
242
193
  await this.batchWriteHelper(table, keys, false);
243
194
  }
244
195
  async batchWriteHelper(table, itemsOrKeys, isPut, currentChunk = 0, chunkSize = 25) {
245
- this.logger.debug(`Batch write (${isPut ? 'put' : 'delete'}) ${table}: ${currentChunk} of ${itemsOrKeys.length}`);
196
+ console.debug(`Batch write (${isPut ? 'put' : 'delete'}) ${table}: ${currentChunk} of ${itemsOrKeys.length}`);
246
197
  let requests;
247
198
  if (isPut)
248
199
  requests = itemsOrKeys.slice(currentChunk, currentChunk + chunkSize).map(i => ({ PutRequest: { Item: i } }));
@@ -267,7 +218,7 @@ class DynamoDB {
267
218
  params.RequestItems = response.UnprocessedItems;
268
219
  attempts++;
269
220
  const waitSeconds = getRandomInt(attempts * 5);
270
- this.logger.debug(`Batch write throttled: waiting ${waitSeconds} seconds to retry`);
221
+ console.debug(`Batch write throttled: waiting ${waitSeconds} seconds to retry`);
271
222
  await wait(waitSeconds);
272
223
  }
273
224
  else {
@@ -280,9 +231,9 @@ class DynamoDB {
280
231
  * @param params the params to apply to DynamoDB's function
281
232
  */
282
233
  async query(params) {
283
- this.logger.debug(`Query ${params.TableName}`);
234
+ console.debug(`Query ${params.TableName}`);
284
235
  const result = await this.queryScanHelper(params, [], true);
285
- this.logger.debug(`Results query ${params.TableName}: ${result?.length || 0}`);
236
+ console.debug(`Results query ${params.TableName}: ${result.length ?? 0}`);
286
237
  return result;
287
238
  }
288
239
  /**
@@ -290,9 +241,9 @@ class DynamoDB {
290
241
  * @param params the params to apply to DynamoDB's function
291
242
  */
292
243
  async scan(params) {
293
- this.logger.debug(`Scan ${params.TableName}`);
244
+ console.debug(`Scan ${params.TableName}`);
294
245
  const result = await this.queryScanHelper(params, [], false);
295
- this.logger.debug(`Results scan ${params.TableName}: ${result?.length || 0}`);
246
+ console.debug(`Results scan ${params.TableName}: ${result.length ?? 0}`);
296
247
  return result;
297
248
  }
298
249
  async queryScanHelper(params, items, isQuery) {
@@ -314,9 +265,9 @@ class DynamoDB {
314
265
  * @param params the params to apply to DynamoDB's function
315
266
  */
316
267
  async queryClassic(params) {
317
- this.logger.debug(`Query classic ${params.TableName}`);
268
+ console.debug(`Query classic ${params.TableName}`);
318
269
  const result = await this.dynamo.query(params);
319
- this.logger.debug(`Results query classic ${params.TableName}: ${result?.Items?.length || 0}`);
270
+ console.debug(`Results query classic ${params.TableName}: ${result.Items.length ?? 0}`);
320
271
  return result;
321
272
  }
322
273
  /**
@@ -324,20 +275,20 @@ class DynamoDB {
324
275
  * @param params the params to apply to DynamoDB's function
325
276
  */
326
277
  async scanClassic(params) {
327
- this.logger.debug(`Scan classic ${params.TableName}`);
278
+ console.debug(`Scan classic ${params.TableName}`);
328
279
  const result = await this.dynamo.scan(params);
329
- this.logger.debug(`Results scan classic ${params.TableName}: ${result?.Items?.length || 0}`);
280
+ console.debug(`Results scan classic ${params.TableName}: ${result.Items.length ?? 0}`);
330
281
  return result;
331
282
  }
332
283
  /**
333
- * Execute a series of max 10 write operations in a single transaction.
284
+ * Execute a series of write operations in a single transaction.
334
285
  * @param ops the operations to execute in the transaction
335
286
  */
336
287
  async transactWrites(ops) {
337
288
  if (!ops.length)
338
- return this.logger.debug('Transaction writes: no elements to write');
339
- this.logger.debug('Transaction writes');
340
- await this.dynamo.transactWrite({ TransactItems: ops.slice(0, 10) });
289
+ return console.debug('Transaction writes: no elements to write');
290
+ console.debug('Transaction writes');
291
+ await this.dynamo.transactWrite({ TransactItems: ops });
341
292
  }
342
293
  }
343
294
  exports.DynamoDB = DynamoDB;
@@ -20,5 +20,5 @@ export declare abstract class GenericController {
20
20
  /**
21
21
  * Default callback for the Lambda.
22
22
  */
23
- protected done(err: any, res?: any): void;
23
+ protected done(error: Error | any, res?: any): void;
24
24
  }
@@ -20,12 +20,12 @@ class GenericController {
20
20
  /**
21
21
  * Default callback for the Lambda.
22
22
  */
23
- done(err, res) {
24
- if (err)
25
- this.logger.info('END-FAILED', { error: err.message || err.errorMessage });
23
+ done(error, res) {
24
+ if (error)
25
+ this.logger.error('END-FAILED', error);
26
26
  else
27
27
  this.logger.info('END-SUCCESS');
28
- this.callback(err, res);
28
+ this.callback(error, res);
29
29
  }
30
30
  }
31
31
  exports.GenericController = GenericController;
@@ -1,19 +1,10 @@
1
1
  /**
2
- * Manage structured logging.
2
+ * Manage structured logging in the context of a Lambda function.
3
+ * Note: the log level is controlled by each Lambda function's configuration.
3
4
  */
4
5
  export declare class Logger {
5
- level: string;
6
- private originalLevel;
7
- constructor(params?: {
8
- level?: string;
9
- });
10
- isEnabled(level: number): boolean;
11
- appendError(params: any, err: Error): any;
12
- log(levelName: string, message: string, params: any): void;
13
- debug(msg: string, params?: any): void;
14
- info(msg: string, params?: any): void;
15
- warn(msg: string, err: Error | any, params?: any): void;
16
- error(msg: string, err: Error | any, params?: any): void;
17
- enableDebug(): () => void;
18
- resetLevel(): void;
6
+ debug: (_: string, params?: any) => void;
7
+ info: (_: string, params?: any) => void;
8
+ warn: (_: string, err: Error | any, params?: any) => void;
9
+ error: (_: string, err: Error | any, params?: any) => void;
19
10
  }
@@ -2,53 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Logger = void 0;
4
4
  /**
5
- * Manage structured logging.
5
+ * Manage structured logging in the context of a Lambda function.
6
+ * Note: the log level is controlled by each Lambda function's configuration.
6
7
  */
7
8
  class Logger {
8
- constructor(params) {
9
- const options = Object.assign({}, params, { level: process.env.LOG_LEVEL });
10
- this.level = (options.level ?? 'DEBUG').toUpperCase();
11
- this.originalLevel = this.level;
12
- }
13
- isEnabled(level) {
14
- return level >= (LogLevels[this.level] || LogLevels.DEBUG);
15
- }
16
- appendError(params, err) {
17
- if (!err)
18
- return params;
19
- return { ...(params || {}), errorName: err.name, errorMessage: err.message, stackTrace: err.stack };
20
- }
21
- log(levelName, message, params) {
22
- const level = LogLevels[levelName];
23
- if (!this.isEnabled(level))
24
- return;
25
- const logMsg = { ...params, level, sLevel: levelName, message };
26
- const consoleMethods = { DEBUG: console.debug, INFO: console.info, WARN: console.warn, ERROR: console.error };
27
- // re-order message and params to appear earlier in the log row
28
- consoleMethods[levelName](JSON.stringify({ message, ...params, ...logMsg }, (_, value) => typeof value === 'bigint' ? value.toString() : value));
29
- }
30
- debug(msg, params = {}) {
31
- this.log('DEBUG', msg, params);
32
- }
33
- info(msg, params = {}) {
34
- this.log('INFO', msg, params);
35
- }
36
- warn(msg, err, params = {}) {
37
- const parameters = this.appendError(params, err);
38
- this.log('WARN', msg, parameters);
39
- }
40
- error(msg, err, params = {}) {
41
- const parameters = this.appendError(params, err);
42
- this.log('ERROR', msg, parameters);
43
- }
44
- enableDebug() {
45
- this.level = 'DEBUG';
46
- return () => this.resetLevel();
47
- }
48
- resetLevel() {
49
- this.level = this.originalLevel;
9
+ constructor() {
10
+ this.debug = (_, params = {}) => console.debug({ _, ...params });
11
+ this.info = (_, params = {}) => console.info({ _, ...params });
12
+ this.warn = (_, err, params = {}) => console.warn({ _, ...params, errorType: err.name, errorMessage: err.message, stackTrace: err.stack });
13
+ this.error = (_, err, params = {}) => console.error({ _, ...params, errorType: err.name, errorMessage: err.message, stackTrace: err.stack });
50
14
  }
51
15
  }
52
16
  exports.Logger = Logger;
53
- // levels here are identical to bunyan practices (https://github.com/trentm/node-bunyan#levels)
54
- const LogLevels = { DEBUG: 20, INFO: 30, WARN: 40, ERROR: 50 };
@@ -4,7 +4,7 @@ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
4
4
  */
5
5
  export declare class CloudWatchMetrics {
6
6
  private metrics;
7
- constructor(params?: {
7
+ constructor(options?: {
8
8
  project?: string;
9
9
  });
10
10
  /**
@@ -6,9 +6,9 @@ const metrics_1 = require("@aws-lambda-powertools/metrics");
6
6
  * A wrapper for simple uses of CloudWatch Metrics.
7
7
  */
8
8
  class CloudWatchMetrics {
9
- constructor(params) {
10
- const options = Object.assign({}, params, { project: 'unknownProject' });
11
- this.metrics = new metrics_1.Metrics({ namespace: options.project });
9
+ constructor(options = {}) {
10
+ const namespace = options.project ?? 'unknownProject';
11
+ this.metrics = new metrics_1.Metrics({ namespace });
12
12
  }
13
13
  /**
14
14
  * Get the raw Metrics object. To use for custom purposes.
@@ -37,13 +37,14 @@ export declare abstract class ResourceController extends GenericController {
37
37
  constructor(event: APIGatewayProxyEventV2 | APIGatewayProxyEvent, callback: Callback, options?: ResourceControllerOptions);
38
38
  private initFromEventV2;
39
39
  private initFromEventV1;
40
+ protected getEventInfo(): Record<string, any>;
40
41
  /**
41
42
  * Force the parsing of a query parameter as an array of strings.
42
43
  */
43
44
  protected getQueryParamAsArray(paramName: string): string[];
44
45
  handleRequest: () => Promise<void>;
45
- private controlHandlerError;
46
- protected done(err: any, res?: any, statusCode?: number): void;
46
+ private remapHandlerError;
47
+ protected done(error?: Error | any, result?: any, statusCode?: number): void;
47
48
  /**
48
49
  * To @override
49
50
  */
@@ -108,11 +108,11 @@ class ResourceController extends genericController_1.GenericController {
108
108
  this.done(null, response);
109
109
  }
110
110
  catch (err) {
111
- this.done(this.controlHandlerError(err, 'HANDLER-ERROR', 'Operation failed'));
111
+ this.done(this.remapHandlerError(err, 'HANDLER-ERROR', 'Operation failed'));
112
112
  }
113
113
  }
114
114
  catch (err) {
115
- this.done(this.controlHandlerError(err, 'AUTH-CHECK-ERROR', 'Forbidden'));
115
+ this.done(this.remapHandlerError(err, 'AUTH-CHECK-ERROR', 'Forbidden'));
116
116
  }
117
117
  };
118
118
  this.event = event;
@@ -138,20 +138,11 @@ class ResourceController extends genericController_1.GenericController {
138
138
  }
139
139
  if (options.useMetrics)
140
140
  this.prepareMetrics();
141
- // print the initial log
142
- const info = {
143
- principalId: this.principalId,
144
- queryParams: this.queryParams,
145
- body: this.body,
146
- version: this.clientVersion,
147
- platform: this.clientPlatform,
148
- bundle: this.clientBundle
149
- };
150
- this.logger.info(`START: ${this.httpMethod} ${this.path}`, info);
141
+ this.logger.info(`START: ${this.httpMethod} ${this.path}`, this.getEventInfo());
151
142
  }
152
143
  catch (err) {
153
144
  this.initError = true;
154
- this.done(this.controlHandlerError(err, 'INIT-ERROR', 'Malformed request'));
145
+ this.done(this.remapHandlerError(err, 'INIT-ERROR', 'Malformed request'));
155
146
  }
156
147
  }
157
148
  initFromEventV2(event, options) {
@@ -199,6 +190,16 @@ class ResourceController extends genericController_1.GenericController {
199
190
  throw new RCError('Malformed body');
200
191
  }
201
192
  }
193
+ getEventInfo() {
194
+ return {
195
+ principalId: this.principalId,
196
+ queryParams: this.queryParams,
197
+ body: this.body,
198
+ version: this.clientVersion,
199
+ platform: this.clientPlatform,
200
+ bundle: this.clientBundle
201
+ };
202
+ }
202
203
  /**
203
204
  * Force the parsing of a query parameter as an array of strings.
204
205
  */
@@ -210,24 +211,30 @@ class ResourceController extends genericController_1.GenericController {
210
211
  else
211
212
  return String(this.queryParams[paramName]).split(',');
212
213
  }
213
- controlHandlerError(err = {}, context, replaceWithErrorMessage) {
214
+ remapHandlerError(err, context, replaceWithMessage) {
214
215
  if (err instanceof RCError)
215
- return new Error(err.message);
216
- this.logger.error(context, err);
217
- return new Error(replaceWithErrorMessage);
218
- }
219
- done(err, res, statusCode = this.returnStatusCode || (err ? 400 : 200)) {
220
- if (err)
221
- this.logger.info('END-FAILED', { statusCode, error: err.message || err.errorMessage });
216
+ return err;
217
+ const error = err;
218
+ error.context = context;
219
+ error.internalMessage = error.message;
220
+ error.message = replaceWithMessage;
221
+ return error;
222
+ }
223
+ done(error, result, statusCode = this.returnStatusCode ?? (error ? 400 : 200)) {
224
+ const logContent = { statusCode, event: this.getEventInfo() };
225
+ if (result)
226
+ logContent.result = Array.isArray(result) ? `Array (${result.length})` : result;
227
+ if (error)
228
+ this.logger.error(`END-FAILED: ${this.httpMethod} ${this.path}`, error, logContent);
222
229
  else
223
- this.logger.info('END-SUCCESS', { statusCode });
230
+ this.logger.info(`END-SUCCESS: ${this.httpMethod} ${this.path}`, logContent);
224
231
  if (this.logRequestsWithKey)
225
- this.storeLog(!err);
232
+ this.storeLog(!error);
226
233
  if (this.metrics)
227
- this.publishMetrics(statusCode, err);
234
+ this.publishMetrics(statusCode, error);
228
235
  this.callback(null, {
229
236
  statusCode: String(statusCode),
230
- body: err ? JSON.stringify({ message: err.message }) : JSON.stringify(res || {}),
237
+ body: error ? JSON.stringify({ message: error.message }) : JSON.stringify(result ?? {}),
231
238
  headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }
232
239
  });
233
240
  }
@@ -380,7 +387,7 @@ class ResourceController extends genericController_1.GenericController {
380
387
  this.metrics.addMetric('statusCode', statusCode);
381
388
  if (error) {
382
389
  this.metrics.addMetric('failed');
383
- this.metrics.addMetadata('errorMessage', error.name);
390
+ this.metrics.addMetadata('error', error.name);
384
391
  }
385
392
  else
386
393
  this.metrics.addMetric('success');
@@ -406,7 +413,7 @@ class ResourceController extends genericController_1.GenericController {
406
413
  FunctionName: params.lambda,
407
414
  InvocationType: 'RequestResponse',
408
415
  Payload: this.mapEventForInternalApiRequest(params),
409
- Qualifier: params.stage || this.stage
416
+ Qualifier: params.stage ?? this.stage
410
417
  });
411
418
  const client = new Lambda.LambdaClient();
412
419
  const { Payload } = await client.send(command);
@@ -432,14 +439,14 @@ class ResourceController extends genericController_1.GenericController {
432
439
  // change only the event attributes we need; e.g. the authorization is unchanged
433
440
  if (!event.requestContext)
434
441
  event.requestContext = {};
435
- event.requestContext.stage = params.stage || this.stage;
442
+ event.requestContext.stage = params.stage ?? this.stage;
436
443
  if (!event.requestContext.http)
437
444
  event.requestContext.http = {};
438
445
  event.requestContext.http.method = event.httpMethod = params.httpMethod;
439
446
  event.routeKey = event.resource = params.resource;
440
- event.pathParameters = params.pathParams || {};
441
- event.queryStringParameters = params.queryParams || {};
442
- event.body = JSON.stringify(params.body || {});
447
+ event.pathParameters = params.pathParams ?? {};
448
+ event.queryStringParameters = params.queryParams ?? {};
449
+ event.body = JSON.stringify(params.body ?? {});
443
450
  event.rawPath = event.path = params.resource;
444
451
  for (const p in event.pathParameters)
445
452
  if (event.pathParameters[p])
@@ -467,7 +474,7 @@ class ResourceController extends genericController_1.GenericController {
467
474
  return;
468
475
  // set the languages
469
476
  this.currentLang = lang;
470
- this.defaultLang = defLang || lang;
477
+ this.defaultLang = defLang ?? lang;
471
478
  this.translations = {};
472
479
  // load the translations in the chosen language
473
480
  this.translations[this.currentLang] = JSON.parse(this.loadSharedResource(`i18n/${this.currentLang}.json`).toString());
@@ -539,3 +546,8 @@ class RCError extends Error {
539
546
  }
540
547
  }
541
548
  exports.RCError = RCError;
549
+ /**
550
+ * An unhandled error thrown inside the RC (i.e. `!(error instanceof RCError)`) .
551
+ */
552
+ class RCUnhandledError extends Error {
553
+ }