idea-aws 3.14.1 → 3.15.1

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.
@@ -0,0 +1,31 @@
1
+ import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
2
+ /**
3
+ * A wrapper for simple uses of CloudWatch Metrics.
4
+ */
5
+ export declare class CloudWatchMetrics {
6
+ private metrics;
7
+ constructor(options?: {
8
+ project?: string;
9
+ });
10
+ /**
11
+ * Get the raw Metrics object. To use for custom purposes.
12
+ */
13
+ __raw(): Metrics;
14
+ /**
15
+ * Add an entry for the metrics.
16
+ */
17
+ addMetric(metricName: string, value?: number, unit?: MetricUnits): void;
18
+ /**
19
+ * Add a metadata useful when you want to search highly contextual information along with your metrics in your logs.
20
+ */
21
+ addMetadata(key: string, value: string): void;
22
+ /**
23
+ * Add an additional metrics dimension.
24
+ */
25
+ addDimension(name: string, value: string, defaultValue?: string): void;
26
+ /**
27
+ * Synchronous function to actually publish your metrics.
28
+ * It will create a new EMF blob and log it to be then ingested by Cloudwatch logs and processed for metrics creation.
29
+ */
30
+ publishStoredMetrics(): void;
31
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CloudWatchMetrics = void 0;
4
+ const metrics_1 = require("@aws-lambda-powertools/metrics");
5
+ /**
6
+ * A wrapper for simple uses of CloudWatch Metrics.
7
+ */
8
+ class CloudWatchMetrics {
9
+ constructor(options) {
10
+ const project = options?.project ?? 'unknownProject';
11
+ this.metrics = new metrics_1.Metrics({ namespace: project });
12
+ }
13
+ /**
14
+ * Get the raw Metrics object. To use for custom purposes.
15
+ */
16
+ __raw() {
17
+ return this.metrics;
18
+ }
19
+ /**
20
+ * Add an entry for the metrics.
21
+ */
22
+ addMetric(metricName, value = 1, unit = metrics_1.MetricUnits.Count) {
23
+ this.metrics.addMetric(metricName, unit, value);
24
+ }
25
+ /**
26
+ * Add a metadata useful when you want to search highly contextual information along with your metrics in your logs.
27
+ */
28
+ addMetadata(key, value) {
29
+ this.metrics.addMetadata(key, value);
30
+ }
31
+ /**
32
+ * Add an additional metrics dimension.
33
+ */
34
+ addDimension(name, value, defaultValue = '-') {
35
+ this.metrics.addDimension(name, value ?? defaultValue);
36
+ }
37
+ /**
38
+ * Synchronous function to actually publish your metrics.
39
+ * It will create a new EMF blob and log it to be then ingested by Cloudwatch logs and processed for metrics creation.
40
+ */
41
+ publishStoredMetrics() {
42
+ this.metrics.publishStoredMetrics();
43
+ }
44
+ }
45
+ exports.CloudWatchMetrics = CloudWatchMetrics;
@@ -2,6 +2,7 @@ import 'source-map-support/register';
2
2
  import { APIGatewayProxyEventV2, APIGatewayProxyEvent, Callback } from 'aws-lambda';
3
3
  import { CognitoUser, Auth0User } from 'idea-toolbox';
4
4
  import { Logger } from './logger';
5
+ import { CloudWatchMetrics } from './metrics';
5
6
  import { GenericController, GenericControllerOptions } from './genericController';
6
7
  /**
7
8
  * An abstract class to inherit to manage API requests (AWS API Gateway) in an AWS Lambda function.
@@ -15,17 +16,22 @@ export declare abstract class ResourceController extends GenericController {
15
16
  protected principalId: string;
16
17
  protected cognitoUser: CognitoUser;
17
18
  protected auth0User: Auth0User;
19
+ protected project: string;
18
20
  protected stage: string;
19
21
  protected httpMethod: string;
20
22
  protected body: any;
21
23
  protected queryParams: any;
22
- protected resource: string;
24
+ protected resourcePath: string;
23
25
  protected path: string;
24
26
  protected pathParameters: any;
27
+ protected resource: string;
25
28
  protected resourceId: string;
29
+ protected clientVersion: string;
30
+ protected clientPlatform: string;
26
31
  protected returnStatusCode?: number;
27
32
  protected logger: Logger;
28
33
  protected logRequestsWithKey: string;
34
+ protected metrics: CloudWatchMetrics;
29
35
  protected currentLang: string;
30
36
  protected defaultLang: string;
31
37
  protected translations: any;
@@ -100,12 +106,20 @@ export declare abstract class ResourceController extends GenericController {
100
106
  * Check whether shared resource exists in the back-end (translation, template, etc.).
101
107
  * Search for the specified file path in both the Lambda function's main folder and the layers folder.
102
108
  */
103
- sharedResourceExists(filePath: string): boolean;
109
+ protected sharedResourceExists(filePath: string): boolean;
104
110
  /**
105
111
  * Load a shared resource in the back-end (translation, template, etc.).
106
112
  * Search for the specified file path in both the Lambda function's main folder and the layers folder.
107
113
  */
108
- loadSharedResource(filePath: string): string;
114
+ protected loadSharedResource(filePath: string): string;
115
+ /**
116
+ * Prepare the CloudWatch metrics at the beginning of a request.
117
+ */
118
+ protected prepareMetrics(): void;
119
+ /**
120
+ * Publish the CloudWatch metrics (default and custom-defined) at the end of a reqeust.
121
+ */
122
+ protected publishMetrics(statusCode: number, error?: any): void;
109
123
  /**
110
124
  * Simulate an internal API request, invoking directly the lambda and therefore saving resources.
111
125
  * @return the body of the response
@@ -6,6 +6,7 @@ const fs_1 = require("fs");
6
6
  const aws_sdk_1 = require("aws-sdk");
7
7
  const idea_toolbox_1 = require("idea-toolbox");
8
8
  const logger_1 = require("./logger");
9
+ const metrics_1 = require("./metrics");
9
10
  const genericController_1 = require("./genericController");
10
11
  /**
11
12
  * An abstract class to inherit to manage API requests (AWS API Gateway) in an AWS Lambda function.
@@ -14,7 +15,13 @@ class ResourceController extends genericController_1.GenericController {
14
15
  constructor(event, callback, options = {}) {
15
16
  super(event, callback, options);
16
17
  this.initError = false;
18
+ this.project = process?.env?.PROJECT;
19
+ this.stage = process?.env?.STAGE;
20
+ this.resource = process?.env?.RESOURCE;
21
+ this.clientVersion = '?';
22
+ this.clientPlatform = '?';
17
23
  this.logger = new logger_1.Logger();
24
+ this.metrics = new metrics_1.CloudWatchMetrics({ project: this.project });
18
25
  this.templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
19
26
  ///
20
27
  /// REQUEST HANDLERS
@@ -95,17 +102,23 @@ class ResourceController extends genericController_1.GenericController {
95
102
  this.initFromEventV1(event, options);
96
103
  this.logRequestsWithKey = options.logRequestsWithKey;
97
104
  // acquire some info about the client, if available
98
- let version = '?', platform = '?';
99
105
  if (this.queryParams['_v']) {
100
- version = this.queryParams['_v'];
106
+ this.clientVersion = this.queryParams['_v'];
101
107
  delete this.queryParams['_v'];
102
108
  }
103
109
  if (this.queryParams['_p']) {
104
- platform = this.queryParams['_p'];
110
+ this.clientPlatform = this.queryParams['_p'];
105
111
  delete this.queryParams['_p'];
106
112
  }
113
+ this.prepareMetrics();
107
114
  // print the initial log
108
- const info = { principalId: this.principalId, queryParams: this.queryParams, body: this.body, version, platform };
115
+ const info = {
116
+ principalId: this.principalId,
117
+ queryParams: this.queryParams,
118
+ body: this.body,
119
+ version: this.clientVersion,
120
+ platform: this.clientPlatform
121
+ };
109
122
  this.logger.info(`START: ${this.httpMethod} ${this.path}`, info);
110
123
  }
111
124
  catch (err) {
@@ -120,9 +133,9 @@ class ResourceController extends genericController_1.GenericController {
120
133
  this.principalId = contextFromAuthorizer.principalId ?? contextFromAuthorizer.sub ?? null;
121
134
  this.cognitoUser = authorizer.jwt?.claims ? new idea_toolbox_1.CognitoUser(authorizer.jwt?.claims) : null;
122
135
  this.auth0User = contextFromAuthorizer.auth0User ? new idea_toolbox_1.Auth0User(contextFromAuthorizer.auth0User) : null;
123
- this.stage = event.requestContext.stage;
136
+ this.stage = this.stage ?? event.requestContext.stage;
124
137
  this.httpMethod = event.requestContext.http.method;
125
- this.resource = event.routeKey.replace('+', ''); // {proxy+} -> {proxy}
138
+ this.resourcePath = event.routeKey.replace('+', ''); // {proxy+} -> {proxy}
126
139
  this.path = event.rawPath;
127
140
  this.pathParameters = {};
128
141
  for (const param in event.pathParameters)
@@ -142,9 +155,9 @@ class ResourceController extends genericController_1.GenericController {
142
155
  this.principalId = this.claims.sub;
143
156
  this.cognitoUser = this.principalId ? new idea_toolbox_1.CognitoUser(this.claims) : null;
144
157
  this.auth0User = null;
145
- this.stage = event.requestContext.stage;
158
+ this.stage = this.stage ?? event.requestContext.stage;
146
159
  this.httpMethod = event.httpMethod;
147
- this.resource = event.resource.replace('+', ''); // {proxy+} -> {proxy}
160
+ this.resourcePath = event.resource.replace('+', ''); // {proxy+} -> {proxy}
148
161
  this.path = event.path;
149
162
  this.pathParameters = {};
150
163
  for (const param in event.pathParameters)
@@ -180,9 +193,9 @@ class ResourceController extends genericController_1.GenericController {
180
193
  this.logger.info('END-FAILED', { statusCode, error: err.message || err.errorMessage });
181
194
  else
182
195
  this.logger.info('END-SUCCESS', { statusCode });
183
- // if configured, store the log of the request
184
196
  if (this.logRequestsWithKey)
185
197
  this.storeLog(!err);
198
+ this.publishMetrics(statusCode, err);
186
199
  this.callback(null, {
187
200
  statusCode: String(statusCode),
188
201
  body: err ? JSON.stringify({ message: err.message }) : JSON.stringify(res || {}),
@@ -277,7 +290,7 @@ class ResourceController extends genericController_1.GenericController {
277
290
  const log = new idea_toolbox_1.APIRequestLog({
278
291
  logId: this.logRequestsWithKey,
279
292
  userId: this.principalId,
280
- resource: this.resource,
293
+ resource: this.resourcePath,
281
294
  path: this.path,
282
295
  resourceId: this.resourceId,
283
296
  method: this.httpMethod,
@@ -309,6 +322,34 @@ class ResourceController extends genericController_1.GenericController {
309
322
  path = `/opt/nodejs/assets/${filePath}`;
310
323
  return path ? (0, fs_1.readFileSync)(path, { encoding: 'utf-8' }) : null;
311
324
  }
325
+ /**
326
+ * Prepare the CloudWatch metrics at the beginning of a request.
327
+ */
328
+ prepareMetrics() {
329
+ this.metrics.addDimension('stage', this.stage);
330
+ this.metrics.addDimension('resource', this.resource);
331
+ this.metrics.addDimension('method', this.httpMethod);
332
+ this.metrics.addDimension('target', this.resourceId ? 'id' : 'list');
333
+ this.metrics.addDimension('action', this.body?.action);
334
+ this.metrics.addDimension('userId', this.principalId);
335
+ this.metrics.addDimension('clientVersion', this.clientVersion);
336
+ this.metrics.addDimension('clientPlatform', this.clientPlatform);
337
+ this.metrics.addMetadata('resourceId', this.resourceId);
338
+ }
339
+ /**
340
+ * Publish the CloudWatch metrics (default and custom-defined) at the end of a reqeust.
341
+ */
342
+ publishMetrics(statusCode, error) {
343
+ this.metrics.addMetric('request');
344
+ this.metrics.addMetric('statusCode', statusCode);
345
+ if (error) {
346
+ this.metrics.addMetric('failed');
347
+ this.metrics.addMetadata('errorMessage', error.name);
348
+ }
349
+ else
350
+ this.metrics.addMetric('success');
351
+ this.metrics.publishStoredMetrics();
352
+ }
312
353
  ///
313
354
  /// MANAGE INTERNAL API REQUESTS (lambda invokes masked as API requests)
314
355
  ///
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "idea-aws",
3
- "version": "3.14.1",
3
+ "version": "3.15.1",
4
4
  "description": "AWS wrappers to use in IDEA's back-ends",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,7 +26,8 @@
26
26
  "translate",
27
27
  "cognito",
28
28
  "comprehend",
29
- "secretsManager"
29
+ "secretsManager",
30
+ "cloudWatch"
30
31
  ],
31
32
  "author": "ITER IDEA <info@iter-idea.com> (https://iter-idea.com)",
32
33
  "license": "ISC",
@@ -35,7 +36,8 @@
35
36
  },
36
37
  "homepage": "https://iter-idea.github.io/IDEA-AWS",
37
38
  "dependencies": {
38
- "idea-toolbox": "^6.6.2",
39
+ "@aws-lambda-powertools/metrics": "^1.4.1",
40
+ "idea-toolbox": "^6.6.3",
39
41
  "nanoid": "^3.3.4",
40
42
  "nodemailer": "^6.7.7",
41
43
  "shortid": "^2.2.16",