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.
- package/dist/src/metrics.d.ts +31 -0
- package/dist/src/metrics.js +45 -0
- package/dist/src/resourceController.d.ts +17 -3
- package/dist/src/resourceController.js +51 -10
- package/package.json +5 -3
|
@@ -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
|
|
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
|
-
|
|
106
|
+
this.clientVersion = this.queryParams['_v'];
|
|
101
107
|
delete this.queryParams['_v'];
|
|
102
108
|
}
|
|
103
109
|
if (this.queryParams['_p']) {
|
|
104
|
-
|
|
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 = {
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
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",
|