From 637a7b8877c371b434eaa9ccff681706c3c8d1ae Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 24 Apr 2023 18:47:25 +0200 Subject: [PATCH 1/3] chore: update types & comments --- packages/metrics/src/Metrics.ts | 62 ++++++++++++++++-------- packages/metrics/src/MetricsInterface.ts | 3 +- packages/metrics/src/types/Metrics.ts | 10 ++-- packages/metrics/tests/helpers/index.ts | 1 - 4 files changed, 47 insertions(+), 29 deletions(-) delete mode 100644 packages/metrics/tests/helpers/index.ts diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index ad5e64345b..a6a1e9d609 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -1,6 +1,6 @@ import { Callback, Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; -import { MetricsInterface } from '.'; +import type { MetricsInterface } from './MetricsInterface'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; import { MAX_DIMENSION_COUNT, MAX_METRICS_SIZE, DEFAULT_NAMESPACE, COLD_START_METRIC } from './constants'; import { @@ -14,7 +14,6 @@ import { MetricUnits, MetricResolution, MetricDefinition, - StoredMetric, } from './types'; /** @@ -109,7 +108,7 @@ class Metrics extends Utility implements MetricsInterface { private envVarsService?: EnvironmentVariablesService; private functionName?: string; private isSingleMetric: boolean = false; - private metadata: { [key: string]: string } = {}; + private metadata: Record = {}; private namespace?: string; private shouldThrowOnEmptyMetrics: boolean = false; private storedMetrics: StoredMetrics = {}; @@ -189,7 +188,6 @@ class Metrics extends Utility implements MetricsInterface { * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition Amazon Cloudwatch Concepts Documentation * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html#CloudWatch_Embedded_Metric_Format_Specification_structure_metricdefinition Metric Definition of Embedded Metric Format Specification */ - public addMetric(name: string, unit: MetricUnit, value: number, resolution: MetricResolution = MetricResolution.Standard): void { this.storeMetric(name, unit, value, resolution); if (this.isSingleMetric) this.publishStoredMetrics(); @@ -341,7 +339,13 @@ class Metrics extends Utility implements MetricsInterface { } /** - * Function to create the right object compliant with Cloudwatch EMF (Embedded Metric Format). + * Function to create a new + * + * the right object compliant with Cloudwatch EMF (Embedded Metric Format). + * + * The function will create a new EMF blob and log it to standard output to be then ingested by Cloudwatch logs and processed automatically for metrics creation. + * + * The function iterates over the stored metrics, and for each metric it will create a new metric object compliant with the EMF schema which includes the metric name, unit, and storage resolution. * * * @returns metrics as JSON object compliant EMF Schema Specification @@ -353,16 +357,16 @@ class Metrics extends Utility implements MetricsInterface { // For standard resolution metrics, don't add StorageResolution property to avoid unnecessary ingestion of data into cloudwatch // Example: [ { "Name": "metric_name", "Unit": "Count"} ] - const metricDefinitions: MetricDefinition[] = Object.values(this.storedMetrics).map((metricDefinition) => - this.isHigh(metricDefinition['resolution']) - ? ({ - Name: metricDefinition.name, - Unit: metricDefinition.unit, - StorageResolution: metricDefinition.resolution - }): ({ - Name: metricDefinition.name, - Unit: metricDefinition.unit, - })); + + const metricDefinitions: MetricDefinition[] = Object.values( + this.storedMetrics + ).map((metricDefinition) => ({ + Name: metricDefinition.name, + Unit: metricDefinition.unit, + ...(metricDefinition.resolution === MetricResolution.High + ? { StorageResolution: metricDefinition.resolution } + : {}), + })); if (metricDefinitions.length === 0 && this.shouldThrowOnEmptyMetrics) { throw new RangeError('The number of metrics recorded must be higher than zero'); @@ -371,7 +375,7 @@ class Metrics extends Utility implements MetricsInterface { if (!this.namespace) console.warn('Namespace should be defined, default used'); const metricValues = Object.values(this.storedMetrics).reduce( - (result: { [key: string]: number | number[] }, { name, value }: { name: string; value: number | number[] }) => { + (result: Record, { name, value }: { name: string; value: number | number[] }) => { result[name] = value; return result; @@ -470,13 +474,21 @@ class Metrics extends Utility implements MetricsInterface { return this.envVarsService; } - private isHigh(resolution: StoredMetric['resolution']): resolution is typeof MetricResolution['High'] { - return resolution === MetricResolution.High; - } - + /** + * Checks if a metric is new or not. + * + * A metric is considered new if there is no metric with the same name already stored. + * + * When a metric is not new, we also check if the unit is consistent with the stored metric with + * the same name. If the units are inconsistent, we throw an error as this is likely a bug or typo. + * This can happen if a metric is added without using the `MetricUnit` helper in JavaScript codebases. + * + * @param name The name of the metric + * @param unit The unit of the metric + * @returns true if the metric is new, false if another metric with the same name already exists + */ private isNewMetric(name: string, unit: MetricUnit): boolean { if (this.storedMetrics[name]){ - // Inconsistent units indicates a bug or typos and we want to flag this to users early if (this.storedMetrics[name].unit !== unit) { const currentUnit = this.storedMetrics[name].unit; throw new Error(`Metric "${name}" has already been added with unit "${currentUnit}", but we received unit "${unit}". Did you mean to use metric unit "${currentUnit}"?`); @@ -524,6 +536,14 @@ class Metrics extends Utility implements MetricsInterface { } } + /** + * Stores a metric in the buffer + * + * @param name The name of the metric to store + * @param unit The unit of the metric to store + * @param value The value of the metric to store + * @param resolution The resolution of the metric to store + */ private storeMetric( name: string, unit: MetricUnit, diff --git a/packages/metrics/src/MetricsInterface.ts b/packages/metrics/src/MetricsInterface.ts index bb8dea45b7..5d294722c6 100644 --- a/packages/metrics/src/MetricsInterface.ts +++ b/packages/metrics/src/MetricsInterface.ts @@ -7,11 +7,12 @@ import { Dimensions, MetricsOptions } from './types'; + interface MetricsInterface { addDimension(name: string, value: string): void addDimensions(dimensions: {[key: string]: string}): void addMetadata(key: string, value: string): void - addMetric(name: string, unit:MetricUnit, value:number, resolution?: MetricResolution): void + addMetric(name: string, unit: MetricUnit, value: number, resolution?: MetricResolution): void clearDimensions(): void clearMetadata(): void clearMetrics(): void diff --git a/packages/metrics/src/types/Metrics.ts b/packages/metrics/src/types/Metrics.ts index 8c0c12f541..ca1da5935c 100644 --- a/packages/metrics/src/types/Metrics.ts +++ b/packages/metrics/src/types/Metrics.ts @@ -4,7 +4,7 @@ import { ConfigServiceInterface } from '../config'; import { MetricUnit } from './MetricUnit'; import { MetricResolution } from './MetricResolution'; -type Dimensions = { [key: string]: string }; +type Dimensions = Record; type MetricsOptions = { customConfigService?: ConfigServiceInterface @@ -14,7 +14,7 @@ type MetricsOptions = { defaultDimensions?: Dimensions }; -type EmfOutput = { +type EmfOutput = Readonly<{ [key: string]: string | number | object _aws: { Timestamp: number @@ -24,7 +24,7 @@ type EmfOutput = { Metrics: MetricDefinition[] }[] } -}; +}>; type HandlerMethodDecorator = ( target: LambdaInterface, @@ -64,9 +64,7 @@ type StoredMetric = { resolution: MetricResolution }; -type StoredMetrics = { - [key: string]: StoredMetric -}; +type StoredMetrics = Record; type MetricDefinition = { Name: string diff --git a/packages/metrics/tests/helpers/index.ts b/packages/metrics/tests/helpers/index.ts deleted file mode 100644 index d3e043d223..0000000000 --- a/packages/metrics/tests/helpers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './populate-environment-variables'; \ No newline at end of file From 1731c93f25f55e3d8ce76b13dfea30e8acef087a Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 24 Apr 2023 19:16:42 +0200 Subject: [PATCH 2/3] chore: add consoleSpy, remove aws-sdk --- package-lock.json | 1025 +++++++++++++++++ packages/metrics/package.json | 1 + .../e2e/basicFeatures.decorators.test.ts | 61 +- .../tests/e2e/basicFeatures.manual.test.ts | 60 +- .../metrics/tests/helpers/metricsUtils.ts | 37 +- packages/metrics/tests/unit/Metrics.test.ts | 2 + 6 files changed, 1109 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3181b931a..ff68c0d93b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -438,6 +438,1030 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-cloudwatch": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch/-/client-cloudwatch-3.316.0.tgz", + "integrity": "sha512-M/LzfCYpufZVAMjiP0Po7bPNo9o8Zivj0B6G/0pNpIcxUyeU17aqhryYWRQBB9TnGhAOpKadBTxkSgcJarwPHg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.316.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "@aws-sdk/util-waiter": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dev": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/abort-controller": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.310.0.tgz", + "integrity": "sha512-v1zrRQxDLA1MdPim159Vx/CPHqsB4uybSxRi1CnfHO5ZjHryx3a5htW2gdGAykVCul40+yJXvfpufMrELVxH+g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/client-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.316.0.tgz", + "integrity": "sha512-wGXfIhR0lJGB8QTT0fwSwwklHePHxd2GW3IQt3trXnEYe0frmJ7vYRnVL5CSRKsikLDmaU7ll3SdsshMzQzo3w==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.316.0.tgz", + "integrity": "sha512-e2fvC7o42YV+LcZYfXCcvBn4L7NM9oNccnZ7T+pS6SFpHZlaqkw4uuQMRE6iUAof+Id7Mt7xDrz1x2yGlP+8GA==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/client-sts": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.316.0.tgz", + "integrity": "sha512-5SD59+DRVy1mKckGs/5J8OwWpRS3E5v4BX19XaX/s9JJ5Rw9aZd9DP4SZVpeNXztIPjkQSEzHgrUVlZFB1QJgg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-node": "3.316.0", + "@aws-sdk/fetch-http-handler": "3.310.0", + "@aws-sdk/hash-node": "3.310.0", + "@aws-sdk/invalid-dependency": "3.310.0", + "@aws-sdk/middleware-content-length": "3.310.0", + "@aws-sdk/middleware-endpoint": "3.310.0", + "@aws-sdk/middleware-host-header": "3.310.0", + "@aws-sdk/middleware-logger": "3.310.0", + "@aws-sdk/middleware-recursion-detection": "3.310.0", + "@aws-sdk/middleware-retry": "3.310.0", + "@aws-sdk/middleware-sdk-sts": "3.310.0", + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/middleware-user-agent": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/node-http-handler": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/smithy-client": "3.316.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.316.0", + "@aws-sdk/util-defaults-mode-node": "3.316.0", + "@aws-sdk/util-endpoints": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "@aws-sdk/util-user-agent-browser": "3.310.0", + "@aws-sdk/util-user-agent-node": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/config-resolver": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.310.0.tgz", + "integrity": "sha512-8vsT+/50lOqfDxka9m/rRt6oxv1WuGZoP8oPMk0Dt+TxXMbAzf4+rejBgiB96wshI1k3gLokYRjSQZn+dDtT8g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.310.0.tgz", + "integrity": "sha512-vvIPQpI16fj95xwS7M3D48F7QhZJBnnCgB5lR+b7So+vsG9ibm1mZRVGzVpdxCvgyOhHFbvrby9aalNJmmIP1A==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.310.0.tgz", + "integrity": "sha512-baxK7Zp6dai5AGW01FIW27xS2KAaPUmKLIXv5SvFYsUgXXvNW55im4uG3b+2gA0F7V+hXvVBH08OEqmwW6we5w==", + "dev": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.316.0.tgz", + "integrity": "sha512-ZADkpdEjFCAXyzEpYbCRENlZ/AQEwevWdPd2yshjNo7xvOcepv4pPIBpYd8h9LvRafSLGA7zlWDz84hkIt+HKA==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.316.0.tgz", + "integrity": "sha512-oE1LTXP8XZp4bT8LhBeolMRiz0RwnmHDC2XpUmWO8LTmbDNrQO0mVzxEvXDLeKaN5BIFIJqNFlMgjWUMa9Kwcw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/credential-provider-ini": "3.316.0", + "@aws-sdk/credential-provider-process": "3.310.0", + "@aws-sdk/credential-provider-sso": "3.316.0", + "@aws-sdk/credential-provider-web-identity": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.310.0.tgz", + "integrity": "sha512-h73sg6GPMUWC+3zMCbA1nZ2O03nNJt7G96JdmnantiXBwHpRKWW8nBTLzx5uhXn6hTuTaoQRP/P+oxQJKYdMmA==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.316.0.tgz", + "integrity": "sha512-8/O2twlsoV1bDkZ9jd7JCMWsftfyoTyRT1UYscsKZGUDEgZRAxRkzS3GLYuLXEWNuxb1OB9rYk/cEJoxwy7T9g==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/token-providers": "3.316.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.310.0.tgz", + "integrity": "sha512-H4SzuZXILNhK6/IR1uVvsUDZvzc051hem7GLyYghBCu8mU+tq28YhKE8MfSroi6eL2e5Vujloij1OM2EQQkPkw==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.310.0.tgz", + "integrity": "sha512-Bi9vIwzdkw1zMcvi/zGzlWS9KfIEnAq4NNhsnCxbQ4OoIRU9wvU+WGZdBBhxg0ZxZmpp1j1aZhU53lLjA07MHw==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/hash-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.310.0.tgz", + "integrity": "sha512-NvE2fhRc8GRwCXBfDehxVAWCmVwVMILliAKVPAEr4yz2CkYs0tqU51S48x23dtna07H4qHtgpeNqVTthcIQOEQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/invalid-dependency": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.310.0.tgz", + "integrity": "sha512-1s5RG5rSPXoa/aZ/Kqr5U/7lqpx+Ry81GprQ2bxWqJvWQIJ0IRUwo5pk8XFxbKVr/2a+4lZT/c3OGoBOM1yRRA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-content-length": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.310.0.tgz", + "integrity": "sha512-P8tQZxgDt6CAh1wd/W6WPzjc+uWPJwQkm+F7rAwRlM+k9q17HrhnksGDKcpuuLyIhPQYdmOMIkpKVgXGa4avhQ==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.310.0.tgz", + "integrity": "sha512-Z+N2vOL8K354/lstkClxLLsr6hCpVRh+0tCMXrVj66/NtKysCEZ/0b9LmqOwD9pWHNiI2mJqXwY0gxNlKAroUg==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-serde": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/url-parser": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.310.0.tgz", + "integrity": "sha512-QWSA+46/hXorXyWa61ic2K7qZzwHTiwfk2e9mRRjeIRepUgI3qxFjsYqrWtrOGBjmFmq0pYIY8Bb/DCJuQqcoA==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-logger": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.310.0.tgz", + "integrity": "sha512-Lurm8XofrASBRnAVtiSNuDSRsRqPNg27RIFLLsLp/pqog9nFJ0vz0kgdb9S5Z+zw83Mm+UlqOe6D8NTUNp4fVg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.310.0.tgz", + "integrity": "sha512-SuB75/xk/gyue24gkriTwO2jFd7YcUGZDClQYuRejgbXSa3CO0lWyawQtfLcSSEBp9izrEVXuFH24K1eAft5nQ==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.310.0.tgz", + "integrity": "sha512-oTPsRy2W4s+dfxbJPW7Km+hHtv/OMsNsVfThAq8DDYKC13qlr1aAyOqGLD+dpBy2aKe7ss517Sy2HcHtHqm7/g==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/service-error-classification": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-retry": "3.310.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.310.0.tgz", + "integrity": "sha512-+5PFwlYNLvLLIfw0ASAoWV/iIF8Zv6R6QGtyP0CclhRSvNjgbQDVnV0g95MC5qvh+GB/Yjlkt8qAjLSPjHfsrQ==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-serde": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.310.0.tgz", + "integrity": "sha512-RNeeTVWSLTaentUeCgQKZhAl+C6hxtwD78cQWS10UymWpQFwbaxztzKUu4UQS5xA2j6PxwPRRUjqa4jcFjfLsg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-signing": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.310.0.tgz", + "integrity": "sha512-f9mKq+XMdW207Af3hKjdTnpNhdtwqWuvFs/ZyXoOkp/g1MY1O6L23Jy6i52m29LxbT4AuNRG1oKODfXM0vYVjQ==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/signature-v4": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-stack": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.310.0.tgz", + "integrity": "sha512-010O1PD+UAcZVKRvqEusE1KJqN96wwrf6QsqbRM0ywsKQ21NDweaHvEDlds2VHpgmofxkRLRu/IDrlPkKRQrRg==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz", + "integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==", + "dev": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-endpoints": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/node-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.310.0.tgz", + "integrity": "sha512-T/Pp6htc6hq/Cq+MLNDSyiwWCMVF6GqbBbXKVlO5L8rdHx4sq9xPdoPveZhGWrxvkanjA6eCwUp6E0riBOSVng==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/node-http-handler": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.310.0.tgz", + "integrity": "sha512-irv9mbcM9xC2xYjArQF5SYmHBMu4ciMWtGsoHII1nRuFOl9FoT4ffTvEPuLlfC6pznzvKt9zvnm6xXj7gDChKg==", + "dev": true, + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/protocol-http": "3.310.0", + "@aws-sdk/querystring-builder": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/property-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.310.0.tgz", + "integrity": "sha512-3lxDb0akV6BBzmFe4nLPaoliQbAifyWJhuvuDOu7e8NzouvpQXs0275w9LePhhcgjKAEVXUIse05ZW2DLbxo/g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/protocol-http": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.310.0.tgz", + "integrity": "sha512-fgZ1aw/irQtnrsR58pS8ThKOWo57Py3xX6giRvwSgZDEcxHfVzuQjy9yPuV++v04fdmdtgpbGf8WfvAAJ11yXQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/querystring-builder": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.310.0.tgz", + "integrity": "sha512-ZHH8GV/80+pWGo7DzsvwvXR5xVxUHXUvPJPFAkhr6nCf78igdoF8gR10ScFoEKbtEapoNTaZlKHPXxpD8aPG7A==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/querystring-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.310.0.tgz", + "integrity": "sha512-YkIznoP6lsiIUHinx++/lbb3tlMURGGqMpo0Pnn32zYzGrJXA6eC3D0as2EcMjo55onTfuLcIiX4qzXes2MYOA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/service-error-classification": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.310.0.tgz", + "integrity": "sha512-PuyC7k3qfIKeH2LCnDwbttMOKq3qAx4buvg0yfnJtQOz6t1AR8gsnAq0CjKXXyfkXwNKWTqCpE6lVNUIkXgsMw==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.310.0.tgz", + "integrity": "sha512-N0q9pG0xSjQwc690YQND5bofm+4nfUviQ/Ppgan2kU6aU0WUq8KwgHJBto/YEEI+VlrME30jZJnxtOvcZJc2XA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/signature-v4": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.310.0.tgz", + "integrity": "sha512-1M60P1ZBNAjCFv9sYW29OF6okktaeibWyW3lMXqzoHF70lHBZh+838iUchznXUA5FLabfn4jBFWMRxlAXJUY2Q==", + "dev": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.310.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/smithy-client": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz", + "integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-stack": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/token-providers": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.316.0.tgz", + "integrity": "sha512-foJ2YmB8A/mtp52riO2zdmBgzA3IpASNgUhY9FZg1BKpGcjqLQDGYP+BY3BA0H7CFsMa4PCf13M5wWwn1onyBA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.316.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/shared-ini-file-loader": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/types": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.310.0.tgz", + "integrity": "sha512-j8eamQJ7YcIhw7fneUfs8LYl3t01k4uHi4ZDmNRgtbmbmTTG3FZc2MotStZnp3nZB6vLiPF1o5aoJxWVvkzS6A==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/url-parser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.310.0.tgz", + "integrity": "sha512-mCLnCaSB9rQvAgx33u0DujLvr4d5yEm/W5r789GblwwQnlNXedVu50QRizMLTpltYWyAUoXjJgQnJHmJMaKXhw==", + "dev": true, + "dependencies": { + "@aws-sdk/querystring-parser": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "dev": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "dev": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz", + "integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==", + "dev": true, + "dependencies": { + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.316.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz", + "integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==", + "dev": true, + "dependencies": { + "@aws-sdk/config-resolver": "3.310.0", + "@aws-sdk/credential-provider-imds": "3.310.0", + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/property-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-endpoints": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz", + "integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-middleware": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.310.0.tgz", + "integrity": "sha512-FTSUKL/eRb9X6uEZClrTe27QFXUNNp7fxYrPndZwk1hlaOP5ix+MIHBcI7pIiiY/JPfOUmPyZOu+HetlFXjWog==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-retry": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.310.0.tgz", + "integrity": "sha512-FwWGhCBLfoivTMUHu1LIn4NjrN9JLJ/aX5aZmbcPIOhZVFJj638j0qDgZXyfvVqBuBZh7M8kGq0Oahy3dp69OA==", + "dev": true, + "dependencies": { + "@aws-sdk/service-error-classification": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.310.0.tgz", + "integrity": "sha512-yU/4QnHHuQ5z3vsUqMQVfYLbZGYwpYblPiuZx4Zo9+x0PBkNjYMqctdDcrpoH9Z2xZiDN16AmQGK1tix117ZKw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.310.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.310.0.tgz", + "integrity": "sha512-Ra3pEl+Gn2BpeE7KiDGpi4zj7WJXZA5GXnGo3mjbi9+Y3zrbuhJAbdZO3mO/o7xDgMC6ph4xCTbaSGzU6b6EDg==", + "dev": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/@aws-sdk/util-waiter": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.310.0.tgz", + "integrity": "sha512-AV5j3guH/Y4REu+Qh3eXQU9igljHuU4XjX2sADAgf54C0kkhcCCkkiuzk3IsX089nyJCqIcj5idbjdvpnH88Vw==", + "dev": true, + "dependencies": { + "@aws-sdk/abort-controller": "3.310.0", + "@aws-sdk/types": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/fast-xml-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", + "dev": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/@aws-sdk/client-cloudwatch/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-dynamodb": { "version": "3.245.0", "license": "Apache-2.0", @@ -19196,6 +20220,7 @@ "@aws-lambda-powertools/commons": "^1.8.0" }, "devDependencies": { + "@aws-sdk/client-cloudwatch": "^3.316.0", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" } diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 745858c134..dea1265b6c 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -34,6 +34,7 @@ "types": "./lib/index.d.ts", "typedocMain": "src/index.ts", "devDependencies": { + "@aws-sdk/client-cloudwatch": "^3.316.0", "@types/promise-retry": "^1.1.3", "promise-retry": "^2.0.1" }, diff --git a/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts b/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts index 0c0cfd71a5..6675ba08d8 100644 --- a/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts +++ b/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts @@ -1,16 +1,15 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test metrics standard functions * * @group e2e/metrics/decorator */ - import path from 'path'; import { Tracing } from 'aws-cdk-lib/aws-lambda'; import { App, Stack } from 'aws-cdk-lib'; -import * as AWS from 'aws-sdk'; +import { + CloudWatchClient, + GetMetricStatisticsCommand +} from '@aws-sdk/client-cloudwatch'; import { v4 } from 'uuid'; import { generateUniqueName, @@ -40,7 +39,7 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'decor const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'decorator'); const lambdaFunctionCodeFile = 'basicFeatures.decorator.test.functionCode.ts'; -const cloudwatchClient = new AWS.CloudWatch(); +const cloudwatchClient = new CloudWatchClient({}); const invocationCount = 2; const startTime = new Date(); @@ -114,20 +113,17 @@ describe(`metrics E2E tests (decorator) for runtime: ${runtime}`, () => { const adjustedStartTime = new Date(startTime.getTime() - ONE_MINUTE); const endTime = new Date(new Date().getTime() + ONE_MINUTE); console.log(`Manual command: aws cloudwatch get-metric-statistics --namespace ${expectedNamespace} --metric-name ColdStart --start-time ${Math.floor(adjustedStartTime.getTime()/1000)} --end-time ${Math.floor(endTime.getTime()/1000)} --statistics 'Sum' --period 60 --dimensions '${JSON.stringify(expectedDimensions)}'`); - const coldStartMetricStat = await cloudwatchClient - .getMetricStatistics( - { - Namespace: expectedNamespace, - StartTime: adjustedStartTime, - Dimensions: expectedDimensions, - EndTime: endTime, - Period: 60, - MetricName: 'ColdStart', - Statistics: ['Sum'], - }, - undefined - ) - .promise(); + const coldStartMetricStat = await cloudwatchClient.send( + new GetMetricStatisticsCommand({ + Namespace: expectedNamespace, + StartTime: adjustedStartTime, + Dimensions: expectedDimensions, + EndTime: endTime, + Period: 60, + MetricName: 'ColdStart', + Statistics: ['Sum'], + }) + ); // Despite lambda has been called twice, coldstart metric sum should only be 1 const singleDataPoint = coldStartMetricStat.Datapoints ? coldStartMetricStat.Datapoints[0] : {}; @@ -154,20 +150,17 @@ describe(`metrics E2E tests (decorator) for runtime: ${runtime}`, () => { const adjustedStartTime = new Date(startTime.getTime() - 3 * ONE_MINUTE); const endTime = new Date(new Date().getTime() + ONE_MINUTE); console.log(`Manual command: aws cloudwatch get-metric-statistics --namespace ${expectedNamespace} --metric-name ${expectedMetricName} --start-time ${Math.floor(adjustedStartTime.getTime()/1000)} --end-time ${Math.floor(endTime.getTime()/1000)} --statistics 'Sum' --period 60 --dimensions '${JSON.stringify(expectedDimensions)}'`); - const metricStat = await cloudwatchClient - .getMetricStatistics( - { - Namespace: expectedNamespace, - StartTime: adjustedStartTime, - Dimensions: expectedDimensions, - EndTime: endTime, - Period: 60, - MetricName: expectedMetricName, - Statistics: ['Sum'], - }, - undefined - ) - .promise(); + const metricStat = await cloudwatchClient.send( + new GetMetricStatisticsCommand({ + Namespace: expectedNamespace, + StartTime: adjustedStartTime, + Dimensions: expectedDimensions, + EndTime: endTime, + Period: 60, + MetricName: expectedMetricName, + Statistics: ['Sum'], + }) + ); // Since lambda has been called twice in this test and potentially more in others, metric sum should be at least of expectedMetricValue * invocationCount const singleDataPoint = metricStat.Datapoints ? metricStat.Datapoints[0] : {}; diff --git a/packages/metrics/tests/e2e/basicFeatures.manual.test.ts b/packages/metrics/tests/e2e/basicFeatures.manual.test.ts index 7a88c6eba0..f9fedf1bea 100644 --- a/packages/metrics/tests/e2e/basicFeatures.manual.test.ts +++ b/packages/metrics/tests/e2e/basicFeatures.manual.test.ts @@ -1,6 +1,3 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - /** * Test metrics standard functions * @@ -10,7 +7,10 @@ import path from 'path'; import { Tracing } from 'aws-cdk-lib/aws-lambda'; import { App, Stack } from 'aws-cdk-lib'; -import * as AWS from 'aws-sdk'; +import { + CloudWatchClient, + GetMetricStatisticsCommand +} from '@aws-sdk/client-cloudwatch'; import { v4 } from 'uuid'; import { generateUniqueName, @@ -40,7 +40,7 @@ const stackName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'manua const functionName = generateUniqueName(RESOURCE_NAME_PREFIX, uuid, runtime, 'manual'); const lambdaFunctionCodeFile = 'basicFeatures.manual.test.functionCode.ts'; -const cloudwatchClient = new AWS.CloudWatch(); +const cloudwatchClient = new CloudWatchClient({}); const invocationCount = 2; const startTime = new Date(); @@ -108,20 +108,17 @@ describe(`metrics E2E tests (manual) for runtime: ${runtime}`, () => { const adjustedStartTime = new Date(startTime.getTime() - 60 * 1000); const endTime = new Date(new Date().getTime() + 60 * 1000); console.log(`Manual command: aws cloudwatch get-metric-statistics --namespace ${expectedNamespace} --metric-name ColdStart --start-time ${Math.floor(adjustedStartTime.getTime()/1000)} --end-time ${Math.floor(endTime.getTime()/1000)} --statistics 'Sum' --period 60 --dimensions '${JSON.stringify([{ Name: 'service', Value: expectedServiceName }])}'`); - const coldStartMetricStat = await cloudwatchClient - .getMetricStatistics( - { - Namespace: expectedNamespace, - StartTime: adjustedStartTime, - Dimensions: [{ Name: 'service', Value: expectedServiceName }], - EndTime: endTime, - Period: 60, - MetricName: 'ColdStart', - Statistics: ['Sum'], - }, - undefined - ) - .promise(); + const coldStartMetricStat = await cloudwatchClient.send( + new GetMetricStatisticsCommand({ + Namespace: expectedNamespace, + StartTime: adjustedStartTime, + Dimensions: [{ Name: 'service', Value: expectedServiceName }], + EndTime: endTime, + Period: 60, + MetricName: 'ColdStart', + Statistics: ['Sum'], + }) + ); // Despite lambda has been called twice, coldstart metric sum should only be 1 const singleDataPoint = coldStartMetricStat.Datapoints ? coldStartMetricStat.Datapoints[0] : {}; @@ -147,20 +144,17 @@ describe(`metrics E2E tests (manual) for runtime: ${runtime}`, () => { const adjustedStartTime = new Date(startTime.getTime() - 3 * ONE_MINUTE); const endTime = new Date(new Date().getTime() + ONE_MINUTE); console.log(`Manual command: aws cloudwatch get-metric-statistics --namespace ${expectedNamespace} --metric-name ${expectedMetricName} --start-time ${Math.floor(adjustedStartTime.getTime()/1000)} --end-time ${Math.floor(endTime.getTime()/1000)} --statistics 'Sum' --period 60 --dimensions '${JSON.stringify(expectedDimensions)}'`); - const metricStat = await cloudwatchClient - .getMetricStatistics( - { - Namespace: expectedNamespace, - StartTime: adjustedStartTime, - Dimensions: expectedDimensions, - EndTime: endTime, - Period: 60, - MetricName: expectedMetricName, - Statistics: ['Sum'], - }, - undefined - ) - .promise(); + const metricStat = await cloudwatchClient.send( + new GetMetricStatisticsCommand({ + Namespace: expectedNamespace, + StartTime: adjustedStartTime, + Dimensions: expectedDimensions, + EndTime: endTime, + Period: 60, + MetricName: expectedMetricName, + Statistics: ['Sum'], + }) + ); // Since lambda has been called twice in this test and potentially more in others, metric sum should be at least of expectedMetricValue * invocationCount const singleDataPoint = metricStat.Datapoints ? metricStat.Datapoints[0] : {}; diff --git a/packages/metrics/tests/helpers/metricsUtils.ts b/packages/metrics/tests/helpers/metricsUtils.ts index 77ad46b345..bf29a176db 100644 --- a/packages/metrics/tests/helpers/metricsUtils.ts +++ b/packages/metrics/tests/helpers/metricsUtils.ts @@ -1,24 +1,41 @@ -import { CloudWatch } from 'aws-sdk'; import promiseRetry from 'promise-retry'; import { Metrics } from '../../src'; import { ExtraOptions, MetricUnits } from '../../src/types'; -import { Context, Handler } from 'aws-lambda'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { + CloudWatchClient, + ListMetricsCommand, +} from '@aws-sdk/client-cloudwatch'; +import type { ListMetricsCommandOutput } from '@aws-sdk/client-cloudwatch'; +import type { Context, Handler } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons'; -const getMetrics = async (cloudWatchClient: CloudWatch, namespace: string, metric: string, expectedMetrics: number): Promise => { - const retryOptions = { retries: 20, minTimeout: 5_000, maxTimeout: 10_000, factor: 1.25 }; +const getMetrics = async ( + cloudWatchClient: CloudWatchClient, + namespace: string, + metric: string, + expectedMetrics: number +): Promise => { + const retryOptions = { + retries: 20, + minTimeout: 5_000, + maxTimeout: 10_000, + factor: 1.25, + }; return promiseRetry(async (retry: (err?: Error) => never, _: number) => { - - const result = await cloudWatchClient - .listMetrics({ + const result = await cloudWatchClient.send( + new ListMetricsCommand({ Namespace: namespace, MetricName: metric, }) - .promise(); + ); if (result.Metrics?.length !== expectedMetrics) { - retry(new Error(`Expected ${expectedMetrics} metrics, got ${result.Metrics?.length} for ${namespace}.${metric}`)); + retry( + new Error( + `Expected ${expectedMetrics} metrics, got ${result.Metrics?.length} for ${namespace}.${metric}` + ) + ); } return result; diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 9fcd932efb..724d88ed99 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -17,6 +17,8 @@ import { ConfigServiceInterface, EnvironmentVariablesService } from '../../src/c const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); +jest.spyOn(console, 'log').mockImplementation(); +jest.spyOn(console, 'warn').mockImplementation(); interface LooseObject { [key: string]: string From 4c8a54f1784498b804b1959d6d7ac225648f3a60 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 24 Apr 2023 19:37:16 +0200 Subject: [PATCH 3/3] chore: added typedoc to all private method --- packages/metrics/src/Metrics.ts | 164 +++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 37 deletions(-) diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index a6a1e9d609..b4bc9bc03e 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -2,7 +2,12 @@ import { Callback, Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; import type { MetricsInterface } from './MetricsInterface'; import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; -import { MAX_DIMENSION_COUNT, MAX_METRICS_SIZE, DEFAULT_NAMESPACE, COLD_START_METRIC } from './constants'; +import { + MAX_DIMENSION_COUNT, + MAX_METRICS_SIZE, + DEFAULT_NAMESPACE, + COLD_START_METRIC, +} from './constants'; import { MetricsOptions, Dimensions, @@ -122,7 +127,9 @@ class Metrics extends Utility implements MetricsInterface { /** * Add a dimension to the metrics. + * * A dimension is a key-value pair that is used to group metrics. + * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Dimension for more details. * @param name * @param value @@ -136,7 +143,10 @@ class Metrics extends Utility implements MetricsInterface { /** * Add multiple dimensions to the metrics. - * @param dimensions + * + * A dimension is a key-value pair that is used to group metrics. + * + * @param dimensions A key-value pair of dimensions */ public addDimensions(dimensions: { [key: string]: string }): void { const newDimensions = { ...this.dimensions }; @@ -154,9 +164,12 @@ class Metrics extends Utility implements MetricsInterface { } /** - * A high-cardinality data part of your Metrics log. This is useful when you want to search highly contextual information along with your metrics in your logs. - * @param key - * @param value + * A high-cardinality data part of your Metrics log. + * + * This is useful when you want to search highly contextual information along with your metrics in your logs. + * + * @param key The key of the metadata + * @param value The value of the metadata */ public addMetadata(key: string, value: string): void { this.metadata[key] = value; @@ -165,28 +178,38 @@ class Metrics extends Utility implements MetricsInterface { /** * Add a metric to the metrics buffer. * - * @example + * By default, metrics are buffered and flushed at the end of the Lambda invocation + * or when calling {@link Metrics.publishStoredMetrics}. * - * Add Metric using MetricUnit Enum supported by Cloudwatch + * You can add a metric by specifying the metric name, unit, and value. For convenience, + * we provide a set of constants for the most common units in {@link MetricUnits}. + * + * @example + * ```typescript + * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + * + * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * - * ```ts * metrics.addMetric('successfulBooking', MetricUnits.Count, 1); * ``` + * + * Optionally, you can specify the metric resolution, which can be either `High` or `Standard`. + * By default, metrics are published with a resolution of `Standard`, click [here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition) + * to learn more about metric resolutions. + * + * @example + * ```typescript + * import { Metrics, MetricUnits, MetricResolution } from '@aws-lambda-powertools/metrics'; * - * @example - * - * Add Metric using MetricResolution type with resolutions High or Standard supported by cloudwatch + * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * - * ```ts * metrics.addMetric('successfulBooking', MetricUnits.Count, 1, MetricResolution.High); * ``` - * + * * @param name - The metric name * @param unit - The metric unit * @param value - The metric value * @param resolution - The metric resolution - * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition Amazon Cloudwatch Concepts Documentation - * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html#CloudWatch_Embedded_Metric_Format_Specification_structure_metricdefinition Metric Definition of Embedded Metric Format Specification */ public addMetric(name: string, unit: MetricUnit, value: number, resolution: MetricResolution = MetricResolution.Standard): void { this.storeMetric(name, unit, value, resolution); @@ -195,14 +218,15 @@ class Metrics extends Utility implements MetricsInterface { /** * Create a singleMetric to capture cold start. + * * If it's a cold start invocation, this feature will: - * * Create a separate EMF blob solely containing a metric named ColdStart + * * Create a separate EMF blob that contains a single metric named ColdStart * * Add function_name and service dimensions * - * This has the advantage of keeping cold start metric separate from your application metrics, where you might have unrelated dimensions. + * This has the advantage of keeping cold start metric separate from your application metrics, where you might have unrelated dimensions, + * as well as avoiding potential data loss from metrics not being published for other reasons. * * @example - * * ```typescript * import { Metrics } from '@aws-lambda-powertools/metrics'; * @@ -226,18 +250,30 @@ class Metrics extends Utility implements MetricsInterface { singleMetric.addMetric(COLD_START_METRIC, MetricUnits.Count, 1); } + /** + * Clear all default dimensions. + */ public clearDefaultDimensions(): void { this.defaultDimensions = {}; } + /** + * Clear all dimensions. + */ public clearDimensions(): void { this.dimensions = {}; } + /** + * Clear all metadata. + */ public clearMetadata(): void { this.metadata = {}; } + /** + * Clear all the metrics stored in the buffer. + */ public clearMetrics(): void { this.storedMetrics = {}; } @@ -339,25 +375,17 @@ class Metrics extends Utility implements MetricsInterface { } /** - * Function to create a new - * - * the right object compliant with Cloudwatch EMF (Embedded Metric Format). - * - * The function will create a new EMF blob and log it to standard output to be then ingested by Cloudwatch logs and processed automatically for metrics creation. - * - * The function iterates over the stored metrics, and for each metric it will create a new metric object compliant with the EMF schema which includes the metric name, unit, and storage resolution. + * Function to create a new metric object compliant with the EMF (Embedded Metric Format) schema which + * includes the metric name, unit, and optionally storage resolution. * + * The function will create a new EMF blob and log it to standard output to be then ingested by Cloudwatch + * logs and processed automatically for metrics creation. * * @returns metrics as JSON object compliant EMF Schema Specification * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html for more details */ public serializeMetrics(): EmfOutput { - // For high-resolution metrics, add StorageResolution property - // Example: [ { "Name": "metric_name", "Unit": "Count", "StorageResolution": 1 } ] - - // For standard resolution metrics, don't add StorageResolution property to avoid unnecessary ingestion of data into cloudwatch - // Example: [ { "Name": "metric_name", "Unit": "Count"} ] - + // Storage resolution is included only for High resolution metrics const metricDefinitions: MetricDefinition[] = Object.values( this.storedMetrics ).map((metricDefinition) => ({ @@ -367,23 +395,34 @@ class Metrics extends Utility implements MetricsInterface { ? { StorageResolution: metricDefinition.resolution } : {}), })); - + if (metricDefinitions.length === 0 && this.shouldThrowOnEmptyMetrics) { - throw new RangeError('The number of metrics recorded must be higher than zero'); + throw new RangeError( + 'The number of metrics recorded must be higher than zero' + ); } - if (!this.namespace) console.warn('Namespace should be defined, default used'); + if (!this.namespace) + console.warn('Namespace should be defined, default used'); + // We reduce the stored metrics to a single object with the metric + // name as the key and the value as the value. const metricValues = Object.values(this.storedMetrics).reduce( - (result: Record, { name, value }: { name: string; value: number | number[] }) => { + ( + result: Record, + { name, value }: { name: string; value: number | number[] } + ) => { result[name] = value; return result; }, - {}, + {} ); - const dimensionNames = [ ...Object.keys(this.defaultDimensions), ...Object.keys(this.dimensions) ]; + const dimensionNames = [ + ...Object.keys(this.defaultDimensions), + ...Object.keys(this.dimensions), + ]; return { _aws: { @@ -403,6 +442,11 @@ class Metrics extends Utility implements MetricsInterface { }; } + /** + * Sets default dimensions that will be added to all metrics. + * + * @param dimensions The default dimensions to be added to all metrics. + */ public setDefaultDimensions(dimensions: Dimensions | undefined): void { const targetDimensions = { ...this.defaultDimensions, @@ -414,6 +458,11 @@ class Metrics extends Utility implements MetricsInterface { this.defaultDimensions = targetDimensions; } + /** + * Sets the function name to be added to the metric. + * + * @param value The function name to be added to the metric. + */ public setFunctionName(value: string): void { this.functionName = value; } @@ -462,14 +511,29 @@ class Metrics extends Utility implements MetricsInterface { this.shouldThrowOnEmptyMetrics = true; } + /** + * Gets the current number of dimensions stored. + * + * @returns the number of dimensions currently stored + */ private getCurrentDimensionsCount(): number { return Object.keys(this.dimensions).length + Object.keys(this.defaultDimensions).length; } + /** + * Gets the custom config service if it exists. + * + * @returns the custom config service if it exists, undefined otherwise + */ private getCustomConfigService(): ConfigServiceInterface | undefined { return this.customConfigService; } + /** + * Gets the environment variables service. + * + * @returns the environment variables service + */ private getEnvVarsService(): EnvironmentVariablesService { return this.envVarsService; } @@ -500,20 +564,41 @@ class Metrics extends Utility implements MetricsInterface { } } + /** + * Sets the custom config service to be used. + * + * @param customConfigService The custom config service to be used + */ private setCustomConfigService(customConfigService?: ConfigServiceInterface): void { this.customConfigService = customConfigService ? customConfigService : undefined; } + /** + * Sets the environment variables service to be used. + */ private setEnvVarsService(): void { this.envVarsService = new EnvironmentVariablesService(); } + /** + * Sets the namespace to be used. + * + * @param namespace The namespace to be used + */ private setNamespace(namespace: string | undefined): void { this.namespace = (namespace || this.getCustomConfigService()?.getNamespace() || this.getEnvVarsService().getNamespace()) as string; } + /** + * Sets the options to be used by the Metrics instance. + * + * This method is used during the initialization of the Metrics instance. + * + * @param options The options to be used + * @returns the Metrics instance + */ private setOptions(options: MetricsOptions): Metrics { const { customConfigService, namespace, serviceName, singleMetric, defaultDimensions } = options; @@ -527,6 +612,11 @@ class Metrics extends Utility implements MetricsInterface { return this; } + /** + * Sets the service to be used. + * + * @param service The service to be used + */ private setService(service: string | undefined): void { const targetService = (service || this.getCustomConfigService()?.getServiceName() ||