idea-aws 4.4.3 → 4.4.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.
Files changed (100) hide show
  1. package/package.json +1 -1
  2. package/.prettierrc +0 -9
  3. package/.vscode/settings.json +0 -26
  4. package/HOW-TO-RELEASE.md +0 -8
  5. package/build.sh +0 -21
  6. package/dist/src/attachments.d.ts +0 -27
  7. package/dist/src/attachments.js +0 -39
  8. package/dist/src/cognito.d.ts +0 -177
  9. package/dist/src/cognito.js +0 -412
  10. package/dist/src/comprehend.d.ts +0 -34
  11. package/dist/src/comprehend.js +0 -58
  12. package/dist/src/dynamoDB.d.ts +0 -108
  13. package/dist/src/dynamoDB.js +0 -296
  14. package/dist/src/genericController.d.ts +0 -60
  15. package/dist/src/genericController.js +0 -89
  16. package/dist/src/lambdaLogger.d.ts +0 -13
  17. package/dist/src/lambdaLogger.js +0 -43
  18. package/dist/src/metrics.d.ts +0 -31
  19. package/dist/src/metrics.js +0 -45
  20. package/dist/src/resourceController.d.ts +0 -232
  21. package/dist/src/resourceController.js +0 -561
  22. package/dist/src/s3.d.ts +0 -225
  23. package/dist/src/s3.js +0 -180
  24. package/dist/src/secretsManager.d.ts +0 -15
  25. package/dist/src/secretsManager.js +0 -48
  26. package/dist/src/ses.d.ts +0 -161
  27. package/dist/src/ses.js +0 -196
  28. package/dist/src/sns.d.ts +0 -60
  29. package/dist/src/sns.js +0 -94
  30. package/dist/src/ssm.d.ts +0 -22
  31. package/dist/src/ssm.js +0 -54
  32. package/dist/src/streamController.d.ts +0 -11
  33. package/dist/src/streamController.js +0 -20
  34. package/dist/src/translate.d.ts +0 -61
  35. package/dist/src/translate.js +0 -155
  36. package/docs/.nojekyll +0 -1
  37. package/docs/assets/custom.css +0 -4
  38. package/docs/assets/highlight.css +0 -50
  39. package/docs/assets/main.js +0 -58
  40. package/docs/assets/search.js +0 -1
  41. package/docs/assets/style.css +0 -1367
  42. package/docs/classes/Attachments.html +0 -201
  43. package/docs/classes/Cognito.html +0 -713
  44. package/docs/classes/Comprehend.html +0 -176
  45. package/docs/classes/DynamoDB.html +0 -584
  46. package/docs/classes/GenericController.html +0 -262
  47. package/docs/classes/HandledError.html +0 -219
  48. package/docs/classes/LambdaLogger.html +0 -220
  49. package/docs/classes/ResourceController.html +0 -957
  50. package/docs/classes/S3.html +0 -391
  51. package/docs/classes/SES.html +0 -335
  52. package/docs/classes/SNS.html +0 -185
  53. package/docs/classes/SecretsManager.html +0 -159
  54. package/docs/classes/StreamController.html +0 -284
  55. package/docs/classes/SystemsManager.html +0 -184
  56. package/docs/classes/Translate.html +0 -239
  57. package/docs/classes/UnhandledError.html +0 -252
  58. package/docs/functions/cleanFilename.html +0 -93
  59. package/docs/index.html +0 -95
  60. package/docs/interfaces/BasicEmailData.html +0 -145
  61. package/docs/interfaces/CognitoGroup.html +0 -122
  62. package/docs/interfaces/CognitoUserGeneric.html +0 -125
  63. package/docs/interfaces/CopyObjectOptions.html +0 -132
  64. package/docs/interfaces/CreateDownloadURLFromDataOptions.html +0 -163
  65. package/docs/interfaces/CreateUserOptions.html +0 -122
  66. package/docs/interfaces/DeleteObjectOptions.html +0 -122
  67. package/docs/interfaces/DetectSentimentParameters.html +0 -121
  68. package/docs/interfaces/EmailAttachment.html +0 -176
  69. package/docs/interfaces/EmailData.html +0 -188
  70. package/docs/interfaces/GetObjectOptions.html +0 -133
  71. package/docs/interfaces/HeadObjectOptions.html +0 -132
  72. package/docs/interfaces/InternalAPIRequestParams.html +0 -207
  73. package/docs/interfaces/ListObjectsOptions.html +0 -122
  74. package/docs/interfaces/PutObjectOptions.html +0 -173
  75. package/docs/interfaces/ResourceControllerOptions.html +0 -142
  76. package/docs/interfaces/SESParams.html +0 -152
  77. package/docs/interfaces/SNSCreateEndpointParams.html +0 -132
  78. package/docs/interfaces/SNSPublishParams.html +0 -142
  79. package/docs/interfaces/SignedURLOptions.html +0 -123
  80. package/docs/interfaces/TemplatedEmailData.html +0 -186
  81. package/docs/interfaces/TranslateParameters.html +0 -140
  82. package/docs/modules.html +0 -130
  83. package/docs/variables/LOG_LEVELS_PRIORITY.html +0 -81
  84. package/docs.style.css +0 -4
  85. package/src/attachments.ts +0 -41
  86. package/src/cognito.ts +0 -511
  87. package/src/comprehend.ts +0 -52
  88. package/src/dynamoDB.ts +0 -311
  89. package/src/genericController.ts +0 -103
  90. package/src/lambdaLogger.ts +0 -39
  91. package/src/metrics.ts +0 -45
  92. package/src/resourceController.ts +0 -645
  93. package/src/s3.ts +0 -334
  94. package/src/secretsManager.ts +0 -24
  95. package/src/ses.ts +0 -313
  96. package/src/sns.ts +0 -118
  97. package/src/ssm.ts +0 -33
  98. package/src/streamController.ts +0 -25
  99. package/src/translate.ts +0 -174
  100. package/tsconfig.json +0 -10
@@ -1,561 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.ResourceController = void 0;
27
- const fs_1 = require("fs");
28
- const Lambda = __importStar(require("@aws-sdk/client-lambda"));
29
- const EventBridge = __importStar(require("@aws-sdk/client-eventbridge"));
30
- const idea_toolbox_1 = require("idea-toolbox");
31
- const metrics_1 = require("./metrics");
32
- const genericController_1 = require("./genericController");
33
- const dynamoDB_1 = require("./dynamoDB");
34
- const ENV = process?.env ?? {};
35
- const { PROJECT, STAGE, RESOURCE } = ENV;
36
- ENV.POWERTOOLS_SERVICE_NAME = [PROJECT, STAGE, RESOURCE].filter(x => x).join('_');
37
- /**
38
- * An abstract class to inherit to manage API requests (AWS API Gateway) in an AWS Lambda function.
39
- */
40
- class ResourceController extends genericController_1.GenericController {
41
- constructor(event, callback, options = {}) {
42
- super(event, callback);
43
- this.initError = false;
44
- this.project = PROJECT;
45
- this.stage = STAGE;
46
- this.resource = RESOURCE;
47
- this.clientVersion = '?';
48
- this.clientPlatform = '?';
49
- this.clientBundle = null;
50
- this.templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
51
- ///
52
- /// REQUEST HANDLERS
53
- ///
54
- this.handleRequest = async () => {
55
- if (this.initError)
56
- return;
57
- this.logger.info('START', { event: this.getEventSummary() });
58
- let lambdaSegment, rcSegment;
59
- if (this.tracer) {
60
- lambdaSegment = this.tracer.getSegment();
61
- if (lambdaSegment) {
62
- rcSegment = lambdaSegment.addNewSubsegment('RC');
63
- this.tracer.setSegment(rcSegment);
64
- }
65
- this.tracer.annotateColdStart();
66
- this.tracer.addServiceNameAnnotation();
67
- this.tracer.putMetadata('START', { event: this.getEventSummary() });
68
- }
69
- try {
70
- await this.checkAuthBeforeRequest();
71
- try {
72
- let response;
73
- if (this.resourceId) {
74
- switch (this.httpMethod) {
75
- // resource/{resourceId}
76
- case 'GET':
77
- response = await this.getResource();
78
- break;
79
- case 'POST':
80
- response = await this.postResource();
81
- break;
82
- case 'PUT':
83
- response = await this.putResource();
84
- break;
85
- case 'DELETE':
86
- response = await this.deleteResource();
87
- break;
88
- case 'PATCH':
89
- response = await this.patchResource();
90
- break;
91
- case 'HEAD':
92
- response = await this.headResource();
93
- break;
94
- default:
95
- this.done(new genericController_1.HandledError('Unsupported method'));
96
- }
97
- }
98
- else {
99
- switch (this.httpMethod) {
100
- // resource
101
- case 'GET':
102
- response = await this.getResources();
103
- break;
104
- case 'POST':
105
- response = await this.postResources();
106
- break;
107
- case 'PUT':
108
- response = await this.putResources();
109
- break;
110
- case 'DELETE':
111
- response = await this.deleteResources();
112
- break;
113
- case 'PATCH':
114
- response = await this.patchResources();
115
- break;
116
- case 'HEAD':
117
- response = await this.headResources();
118
- break;
119
- default:
120
- this.done(new genericController_1.HandledError('Unsupported method'));
121
- }
122
- }
123
- this.done(null, response);
124
- }
125
- catch (err) {
126
- this.done(this.handleControllerError(err, 'HANDLER-ERROR', 'Operation failed'));
127
- }
128
- }
129
- catch (err) {
130
- this.done(this.handleControllerError(err, 'AUTH-CHECK-ERROR', 'Forbidden'));
131
- }
132
- finally {
133
- if (this.tracer && lambdaSegment && rcSegment) {
134
- rcSegment.close();
135
- this.tracer.setSegment(lambdaSegment);
136
- }
137
- }
138
- };
139
- this.event = event;
140
- this.callback = callback;
141
- try {
142
- if (event.version === '2.0')
143
- this.initFromEventV2(event, options);
144
- else
145
- this.initFromEventV1(event, options);
146
- this.logRequestsWithKey = options.logRequestsWithKey;
147
- this.tracer = options.tracer;
148
- // acquire some info about the client, if available
149
- if (this.queryParams['_v']) {
150
- this.clientVersion = this.queryParams['_v'];
151
- delete this.queryParams['_v'];
152
- }
153
- if (this.queryParams['_p']) {
154
- this.clientPlatform = this.queryParams['_p'];
155
- delete this.queryParams['_p'];
156
- }
157
- if (this.queryParams['_b']) {
158
- this.clientBundle = this.queryParams['_b'];
159
- delete this.queryParams['_b'];
160
- }
161
- if (options.useMetrics)
162
- this.prepareMetrics();
163
- }
164
- catch (err) {
165
- this.initError = true;
166
- this.done(this.handleControllerError(err, 'INIT-ERROR', 'Malformed request'));
167
- }
168
- }
169
- initFromEventV2(event, options) {
170
- this.authorization = event.headers.authorization;
171
- const authorizer = event.requestContext?.authorizer ?? {};
172
- const contextFromAuthorizer = authorizer.lambda ?? authorizer.jwt?.claims ?? {};
173
- this.principalId = contextFromAuthorizer.principalId ?? contextFromAuthorizer.sub ?? null;
174
- this.cognitoUser = authorizer.jwt?.claims ? new idea_toolbox_1.CognitoUser(authorizer.jwt?.claims) : null;
175
- this.auth0User = contextFromAuthorizer.auth0User ? new idea_toolbox_1.Auth0User(contextFromAuthorizer.auth0User) : null;
176
- this.stage = this.stage ?? event.requestContext.stage;
177
- this.httpMethod = event.requestContext.http.method;
178
- this.resourcePath = event.routeKey.replace('+', ''); // {proxy+} -> {proxy}
179
- this.path = event.rawPath;
180
- this.pathParameters = {};
181
- for (const param in event.pathParameters)
182
- this.pathParameters[param] = event.pathParameters[param] ? decodeURIComponent(event.pathParameters[param]) : null;
183
- this.resourceId = this.pathParameters[options.resourceId || 'proxy'];
184
- this.queryParams = event.queryStringParameters || {};
185
- try {
186
- this.body = (event.body ? JSON.parse(event.body) : {}) || {};
187
- }
188
- catch (error) {
189
- throw new genericController_1.HandledError('Malformed body');
190
- }
191
- }
192
- initFromEventV1(event, options) {
193
- this.authorization = event.headers.Authorization;
194
- this.claims = event.requestContext.authorizer?.claims || {};
195
- this.principalId = this.claims.sub;
196
- this.cognitoUser = this.principalId ? new idea_toolbox_1.CognitoUser(this.claims) : null;
197
- this.auth0User = null;
198
- this.stage = this.stage ?? event.requestContext.stage;
199
- this.httpMethod = event.httpMethod;
200
- this.resourcePath = event.resource.replace('+', ''); // {proxy+} -> {proxy}
201
- this.path = event.path;
202
- this.pathParameters = {};
203
- for (const param in event.pathParameters)
204
- this.pathParameters[param] = event.pathParameters[param] ? decodeURIComponent(event.pathParameters[param]) : null;
205
- this.resourceId = this.pathParameters[options.resourceId || 'proxy'];
206
- this.queryParams = event.queryStringParameters || {};
207
- try {
208
- this.body = (event.body ? JSON.parse(event.body) : {}) || {};
209
- }
210
- catch (error) {
211
- throw new genericController_1.HandledError('Malformed body');
212
- }
213
- }
214
- getEventSummary() {
215
- return {
216
- httpMethod: this.httpMethod,
217
- path: this.path,
218
- principalId: this.principalId,
219
- queryParams: this.queryParams,
220
- body: this.body,
221
- version: this.clientVersion,
222
- platform: this.clientPlatform,
223
- bundle: this.clientBundle
224
- };
225
- }
226
- /**
227
- * Force the parsing of a query parameter as an array of strings.
228
- */
229
- getQueryParamAsArray(paramName) {
230
- if (!this.queryParams[paramName])
231
- return [];
232
- else if (Array.isArray(this.queryParams[paramName]))
233
- return this.queryParams[paramName];
234
- else
235
- return String(this.queryParams[paramName]).split(',');
236
- }
237
- done(error, rawResult, statusCode = this.returnStatusCode ?? (error ? 400 : 200)) {
238
- const result = error ? { message: error.message } : rawResult ?? {};
239
- const responseTrace = { result: Array.isArray(result) ? { array: result.length } : result };
240
- this.logger.debug('END-DETAIL', responseTrace);
241
- if (this.tracer)
242
- this.tracer.addResponseAsMetadata(responseTrace, 'END-DETAIL');
243
- const finalLogContent = { statusCode, event: this.getEventSummary() };
244
- if (error) {
245
- if (error.unhandled)
246
- this.logger.error('END-FAILED', error, finalLogContent);
247
- else
248
- this.logger.warn('END-FAILED', error, finalLogContent);
249
- if (this.tracer)
250
- this.tracer.addErrorAsMetadata(error);
251
- }
252
- else
253
- this.logger.info('END-SUCCESS', finalLogContent);
254
- if (this.logRequestsWithKey)
255
- this.storeLog(!error);
256
- if (this.metrics)
257
- this.publishMetrics(statusCode, error);
258
- this.callback(null, {
259
- statusCode: String(statusCode),
260
- body: JSON.stringify(result),
261
- headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }
262
- });
263
- }
264
- /**
265
- * To @override
266
- */
267
- async checkAuthBeforeRequest() {
268
- return;
269
- }
270
- /**
271
- * To @override
272
- */
273
- async getResource() {
274
- throw new genericController_1.HandledError('Unsupported method');
275
- }
276
- /**
277
- * To @override
278
- */
279
- async postResource() {
280
- throw new genericController_1.HandledError('Unsupported method');
281
- }
282
- /**
283
- * To @override
284
- */
285
- async putResource() {
286
- throw new genericController_1.HandledError('Unsupported method');
287
- }
288
- /**
289
- * To @override
290
- */
291
- async deleteResource() {
292
- throw new genericController_1.HandledError('Unsupported method');
293
- }
294
- /**
295
- * To @override
296
- */
297
- async headResource() {
298
- throw new genericController_1.HandledError('Unsupported method');
299
- }
300
- /**
301
- * To @override
302
- */
303
- async getResources() {
304
- throw new genericController_1.HandledError('Unsupported method');
305
- }
306
- /**
307
- * To @override
308
- */
309
- async postResources() {
310
- throw new genericController_1.HandledError('Unsupported method');
311
- }
312
- /**
313
- * To @override
314
- */
315
- async putResources() {
316
- throw new genericController_1.HandledError('Unsupported method');
317
- }
318
- /**
319
- * To @override
320
- */
321
- async patchResource() {
322
- throw new genericController_1.HandledError('Unsupported method');
323
- }
324
- /**
325
- * To @override
326
- */
327
- async patchResources() {
328
- throw new genericController_1.HandledError('Unsupported method');
329
- }
330
- /**
331
- * To @override
332
- */
333
- async deleteResources() {
334
- throw new genericController_1.HandledError('Unsupported method');
335
- }
336
- /**
337
- * To @override
338
- */
339
- async headResources() {
340
- throw new genericController_1.HandledError('Unsupported method');
341
- }
342
- ///
343
- /// HELPERS
344
- ///
345
- /**
346
- * Store the log associated to the request (no response/error handling).
347
- */
348
- async storeLog(succeeded) {
349
- const log = new idea_toolbox_1.APIRequestLog({
350
- logId: this.logRequestsWithKey,
351
- userId: this.principalId,
352
- resource: this.resourcePath,
353
- path: this.path,
354
- resourceId: this.resourceId,
355
- method: this.httpMethod,
356
- succeeded
357
- });
358
- // optionally add a track of the action
359
- if (this.httpMethod === 'PATCH' && this.body && this.body.action)
360
- log.action = this.body.action;
361
- try {
362
- await new dynamoDB_1.DynamoDB().put({ TableName: 'idea_logs', Item: log });
363
- }
364
- catch (error) {
365
- // ignore
366
- }
367
- }
368
- /**
369
- * Check whether shared resource exists in the back-end (translation, template, etc.).
370
- * Search for the specified file path in both the Lambda function's main folder and the layers folder.
371
- */
372
- sharedResourceExists(filePath) {
373
- return (0, fs_1.existsSync)(`assets/${filePath}`) || (0, fs_1.existsSync)(`/opt/nodejs/assets/${filePath}`);
374
- }
375
- /**
376
- * Load a shared resource in the back-end (translation, template, etc.).
377
- * Search for the specified file path in both the Lambda function's main folder and the layers folder.
378
- */
379
- loadSharedResource(filePath) {
380
- let path = null;
381
- if ((0, fs_1.existsSync)(`assets/${filePath}`))
382
- path = `assets/${filePath}`;
383
- else if ((0, fs_1.existsSync)(`/opt/nodejs/assets/${filePath}`))
384
- path = `/opt/nodejs/assets/${filePath}`;
385
- return path ? (0, fs_1.readFileSync)(path, { encoding: 'utf-8' }) : null;
386
- }
387
- /**
388
- * Prepare the CloudWatch metrics at the beginning of a request.
389
- */
390
- prepareMetrics() {
391
- this.metrics = new metrics_1.CloudWatchMetrics({ project: this.project });
392
- this.metrics.addDimension('stage', this.stage);
393
- this.metrics.addDimension('resource', this.resource);
394
- this.metrics.addDimension('method', this.httpMethod);
395
- this.metrics.addDimension('target', this.resourceId ? 'id' : 'list');
396
- this.metrics.addDimension('action', this.body?.action);
397
- this.metrics.addDimension('userId', this.principalId);
398
- this.metrics.addDimension('clientVersion', this.clientVersion);
399
- this.metrics.addDimension('clientPlatform', this.clientPlatform);
400
- this.metrics.addDimension('clientBundle', this.clientBundle ?? '-');
401
- this.metrics.addMetadata('resourceId', this.resourceId);
402
- }
403
- /**
404
- * Publish the CloudWatch metrics (default and custom-defined) at the end of a reqeust.
405
- */
406
- publishMetrics(statusCode, error) {
407
- if (!this.metrics)
408
- return;
409
- this.metrics.addMetric('request');
410
- this.metrics.addMetric('statusCode', statusCode);
411
- if (error) {
412
- this.metrics.addMetric('failed');
413
- this.metrics.addMetadata('error', error.name);
414
- }
415
- else
416
- this.metrics.addMetric('success');
417
- this.metrics.publishStoredMetrics();
418
- }
419
- ///
420
- /// MANAGE INTERNAL API REQUESTS (lambda invokes masked as API requests)
421
- ///
422
- /**
423
- * Simulate an internal API request, invoking directly the lambda and therefore saving resources.
424
- * @return the body of the response
425
- * @deprecated don't run a Lambda from another Lambda (bad practice)
426
- */
427
- async invokeInternalAPIRequest(params) {
428
- if (params.lambda)
429
- return await this.invokeInternalAPIRequestWithLambda(params);
430
- if (params.eventBridge)
431
- return await this.invokeInternalAPIRequestWithEventBridge(params);
432
- throw new Error('Either "lambda" or "eventBus" parameters must be set.');
433
- }
434
- async invokeInternalAPIRequestWithLambda(params) {
435
- const command = new Lambda.InvokeCommand({
436
- FunctionName: params.lambda,
437
- InvocationType: 'RequestResponse',
438
- Payload: this.mapEventForInternalApiRequest(params),
439
- Qualifier: params.stage ?? this.stage
440
- });
441
- const client = new Lambda.LambdaClient();
442
- const { Payload } = await client.send(command);
443
- const payload = JSON.parse(Buffer.from(Payload).toString());
444
- const body = JSON.parse(payload.body);
445
- if (Number(payload.statusCode) !== 200)
446
- throw new Error(body.message);
447
- return body;
448
- }
449
- async invokeInternalAPIRequestWithEventBridge(params) {
450
- const request = {
451
- EventBusName: params.eventBridge.bus,
452
- Source: this.constructor.name,
453
- DetailType: params.eventBridge.target,
454
- Detail: this.mapEventForInternalApiRequest(params)
455
- };
456
- const client = new EventBridge.EventBridgeClient();
457
- const command = new EventBridge.PutEventsCommand({ Entries: [request] });
458
- return await client.send(command);
459
- }
460
- mapEventForInternalApiRequest(params) {
461
- const event = JSON.parse(JSON.stringify(this.event));
462
- // change only the event attributes we need; e.g. the authorization is unchanged
463
- if (!event.requestContext)
464
- event.requestContext = {};
465
- event.requestContext.stage = params.stage ?? this.stage;
466
- if (!event.requestContext.http)
467
- event.requestContext.http = {};
468
- event.requestContext.http.method = event.httpMethod = params.httpMethod;
469
- event.routeKey = event.resource = params.resource;
470
- event.pathParameters = params.pathParams ?? {};
471
- event.queryStringParameters = params.queryParams ?? {};
472
- event.body = JSON.stringify(params.body ?? {});
473
- event.rawPath = event.path = params.resource;
474
- for (const p in event.pathParameters)
475
- if (event.pathParameters[p])
476
- event.rawPath = event.path = event.path.replace(`{${p}}`, event.pathParameters[p]);
477
- // set a flag to make the invoked to recognise that is an internal request
478
- event.internalAPIRequest = true;
479
- return JSON.stringify(event);
480
- }
481
- /**
482
- * Whether the current request comes from an internal API request, i.e. it was invoked by another controller.
483
- * @deprecated don't run a Lambda from another Lambda (bad practice)
484
- */
485
- comesFromInternalRequest() {
486
- return Boolean(this.event.internalAPIRequest);
487
- }
488
- //
489
- // TRANSLATIONS
490
- //
491
- /**
492
- * Load the translations from the shared resources and set them with a fallback language.
493
- */
494
- loadTranslations(lang, defLang) {
495
- // check for the existance of the mandatory source file
496
- if (!this.sharedResourceExists(`i18n/${lang}.json`))
497
- return;
498
- // set the languages
499
- this.currentLang = lang;
500
- this.defaultLang = defLang ?? lang;
501
- this.translations = {};
502
- // load the translations in the chosen language
503
- this.translations[this.currentLang] = JSON.parse(this.loadSharedResource(`i18n/${this.currentLang}.json`).toString());
504
- // load the translations in the default language, if set and differ from the current
505
- if (this.defaultLang !== this.currentLang && this.sharedResourceExists(`i18n/${this.defaultLang}.json`))
506
- this.translations[this.defaultLang] = JSON.parse(this.loadSharedResource(`i18n/${this.defaultLang}.json`).toString());
507
- }
508
- /**
509
- * Get a translated term by key, optionally interpolating variables (e.g. `{{user}}`).
510
- * If the term doesn't exist in the current language, it is searched in the default language.
511
- */
512
- t(key, interpolateParams) {
513
- if (!this.translations || !this.currentLang)
514
- return;
515
- if (!this.isDefined(key) || !key.length)
516
- return;
517
- let res = this.interpolate(this.getValue(this.translations[this.currentLang], key), interpolateParams);
518
- if (res === undefined && this.defaultLang !== null && this.defaultLang !== this.currentLang)
519
- res = this.interpolate(this.getValue(this.translations[this.defaultLang], key), interpolateParams);
520
- return res;
521
- }
522
- /**
523
- * Interpolates a string to replace parameters.
524
- * `"This is a {{ key }}"` ==> `"This is a value", with params = { key: "value" }`.
525
- */
526
- interpolate(expr, params) {
527
- if (!params || !expr)
528
- return expr;
529
- return expr.replace(this.templateMatcher, (substring, b) => {
530
- const r = this.getValue(params, b);
531
- return this.isDefined(r) ? r : substring;
532
- });
533
- }
534
- /**
535
- * Gets a value from an object by composed key.
536
- * `getValue({ key1: { keyA: 'valueI' }}, 'key1.keyA')` ==> `'valueI'`.
537
- */
538
- getValue(target, key) {
539
- const keys = typeof key === 'string' ? key.split('.') : [key];
540
- key = '';
541
- do {
542
- key += keys.shift();
543
- if (this.isDefined(target) && this.isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) {
544
- target = target[key];
545
- key = '';
546
- }
547
- else if (!keys.length)
548
- target = undefined;
549
- else
550
- key += '.';
551
- } while (keys.length);
552
- return target;
553
- }
554
- /**
555
- * Helper to quicly check if the value is defined.
556
- */
557
- isDefined(value) {
558
- return value !== undefined && value !== null;
559
- }
560
- }
561
- exports.ResourceController = ResourceController;