diff --git a/.projen/deps.json b/.projen/deps.json index 2403de47b..bca6b6fa7 100644 --- a/.projen/deps.json +++ b/.projen/deps.json @@ -15,6 +15,10 @@ "version": "2.120.0-alpha.0", "type": "build" }, + { + "name": "@aws-sdk/client-cloudwatch", + "type": "build" + }, { "name": "@aws-sdk/client-codeartifact", "type": "build" diff --git a/.projen/tasks.json b/.projen/tasks.json index b318ffc5a..ef855a0ea 100644 --- a/.projen/tasks.json +++ b/.projen/tasks.json @@ -1201,13 +1201,13 @@ }, "steps": [ { - "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --dep=dev --filter=@aws-sdk/client-codeartifact,@aws-sdk/client-ecs,@aws-sdk/client-lambda,@aws-sdk/client-s3,@aws-sdk/client-sfn,@aws-sdk/client-sqs,@jsii/spec,@octokit/rest,@smithy/types,@smithy/util-retry,@smithy/util-stream,@types/aws-lambda,@types/fs-extra,@types/jest,@types/markdown-it,@types/markdown-it-emoji,@types/semver,@types/streamx,@types/tar-stream,@types/tough-cookie,@types/uuid,async-sema,aws-embedded-metrics,aws-sdk,aws-sdk-client-mock,aws-sdk-client-mock-jest,aws-sdk-mock,aws-xray-sdk-core,case,cdk-dia,cdk-watchful,cdklabs-projen-project-types,chalk,construct-hub-webapp,dotenv,esbuild,eslint-config-prettier,eslint-import-resolver-typescript,eslint-plugin-import,eslint-plugin-prettier,feed,fs-extra,glob,got,jest,jsii-diff,jsii-pacmak,JSONStream,markdown-it,markdown-it-emoji,nock,normalize-registry-metadata,prettier,projen,semver,spdx-license-list,streamcount,streamx,tar-stream,ts-node,uuid,yaml" + "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --dep=dev --filter=@aws-sdk/client-cloudwatch,@aws-sdk/client-codeartifact,@aws-sdk/client-ecs,@aws-sdk/client-lambda,@aws-sdk/client-s3,@aws-sdk/client-sfn,@aws-sdk/client-sqs,@jsii/spec,@octokit/rest,@smithy/types,@smithy/util-retry,@smithy/util-stream,@types/aws-lambda,@types/fs-extra,@types/jest,@types/markdown-it,@types/markdown-it-emoji,@types/semver,@types/streamx,@types/tar-stream,@types/tough-cookie,@types/uuid,async-sema,aws-embedded-metrics,aws-sdk,aws-sdk-client-mock,aws-sdk-client-mock-jest,aws-sdk-mock,aws-xray-sdk-core,case,cdk-dia,cdk-watchful,cdklabs-projen-project-types,chalk,construct-hub-webapp,dotenv,esbuild,eslint-config-prettier,eslint-import-resolver-typescript,eslint-plugin-import,eslint-plugin-prettier,feed,fs-extra,glob,got,jest,jsii-diff,jsii-pacmak,JSONStream,markdown-it,markdown-it-emoji,nock,normalize-registry-metadata,prettier,projen,semver,spdx-license-list,streamcount,streamx,tar-stream,ts-node,uuid,yaml" }, { "exec": "yarn install --check-files" }, { - "exec": "yarn upgrade @aws-cdk/aws-servicecatalogappregistry-alpha @aws-cdk/integ-runner @aws-cdk/integ-tests-alpha @aws-sdk/client-codeartifact @aws-sdk/client-ecs @aws-sdk/client-lambda @aws-sdk/client-s3 @aws-sdk/client-sfn @aws-sdk/client-sqs @jsii/spec @octokit/rest @smithy/types @smithy/util-retry @smithy/util-stream @types/aws-lambda @types/fs-extra @types/jest @types/markdown-it @types/markdown-it-emoji @types/node @types/semver @types/streamx @types/tar-stream @types/tough-cookie @types/uuid @typescript-eslint/eslint-plugin @typescript-eslint/parser async-sema aws-cdk-lib aws-cdk aws-embedded-metrics aws-sdk aws-sdk-client-mock aws-sdk-client-mock-jest aws-sdk-mock aws-xray-sdk-core case cdk-dia cdk-watchful cdklabs-projen-project-types chalk commit-and-tag-version construct-hub-webapp dotenv esbuild eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-prettier eslint feed fs-extra glob got jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii JSONStream markdown-it markdown-it-emoji nock normalize-registry-metadata prettier projen semver spdx-license-list streamcount streamx tar-stream ts-node typescript uuid yaml" + "exec": "yarn upgrade @aws-cdk/aws-servicecatalogappregistry-alpha @aws-cdk/integ-runner @aws-cdk/integ-tests-alpha @aws-sdk/client-cloudwatch @aws-sdk/client-codeartifact @aws-sdk/client-ecs @aws-sdk/client-lambda @aws-sdk/client-s3 @aws-sdk/client-sfn @aws-sdk/client-sqs @jsii/spec @octokit/rest @smithy/types @smithy/util-retry @smithy/util-stream @types/aws-lambda @types/fs-extra @types/jest @types/markdown-it @types/markdown-it-emoji @types/node @types/semver @types/streamx @types/tar-stream @types/tough-cookie @types/uuid @typescript-eslint/eslint-plugin @typescript-eslint/parser async-sema aws-cdk-lib aws-cdk aws-embedded-metrics aws-sdk aws-sdk-client-mock aws-sdk-client-mock-jest aws-sdk-mock aws-xray-sdk-core case cdk-dia cdk-watchful cdklabs-projen-project-types chalk commit-and-tag-version construct-hub-webapp dotenv esbuild eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-prettier eslint feed fs-extra glob got jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii JSONStream markdown-it markdown-it-emoji nock normalize-registry-metadata prettier projen semver spdx-license-list streamcount streamx tar-stream ts-node typescript uuid yaml" }, { "exec": "npx projen" diff --git a/.projenrc.ts b/.projenrc.ts index 9ea5ee6ee..1237c0a5b 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -47,6 +47,7 @@ const project = new CdklabsConstructLibrary({ '@types/tough-cookie', '@types/uuid', cdkCli, + '@aws-sdk/client-cloudwatch', '@aws-sdk/client-codeartifact', '@aws-sdk/client-lambda', '@aws-sdk/client-s3', diff --git a/package.json b/package.json index 38f241b45..cc1941b66 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "@aws-cdk/aws-servicecatalogappregistry-alpha": "2.120.0-alpha.0", "@aws-cdk/integ-runner": "2.120.0-alpha.0", "@aws-cdk/integ-tests-alpha": "2.120.0-alpha.0", + "@aws-sdk/client-cloudwatch": "^3.650.0", "@aws-sdk/client-codeartifact": "^3.650.0", "@aws-sdk/client-ecs": "^3.650.0", "@aws-sdk/client-lambda": "^3.650.0", diff --git a/src/__tests__/__snapshots__/construct-hub.test.ts.snap b/src/__tests__/__snapshots__/construct-hub.test.ts.snap index d54eee582..d0ed10181 100644 --- a/src/__tests__/__snapshots__/construct-hub.test.ts.snap +++ b/src/__tests__/__snapshots__/construct-hub.test.ts.snap @@ -12462,7 +12462,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", @@ -25461,7 +25461,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", @@ -38032,7 +38032,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", @@ -50989,7 +50989,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", @@ -66846,7 +66846,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", diff --git a/src/__tests__/backend/overview-dashboard/sqs-dlq-stats-widget-function.lambda.test.ts b/src/__tests__/backend/overview-dashboard/sqs-dlq-stats-widget-function.lambda.test.ts new file mode 100644 index 000000000..ef442a881 --- /dev/null +++ b/src/__tests__/backend/overview-dashboard/sqs-dlq-stats-widget-function.lambda.test.ts @@ -0,0 +1,78 @@ +import { + CloudWatchClient, + GetMetricDataCommand, +} from '@aws-sdk/client-cloudwatch'; +import { mockClient } from 'aws-sdk-client-mock'; + +import { + Event, + handler, +} from '../../../overview-dashboard/sqs-dlq-stats-widget-function.lambda'; + +test('build stats page', async () => { + const cloudwatchMock = mockClient(CloudWatchClient); + + const event: Event = { + description: 'foo', + key: 'bar', + widgetContext: { + params: { + queues: { + MyQueue: { + queueName: 'MyQueue', + name: 'bar', + reDriveFunctionArn: + 'arn:aws:lambda:us-east-2:123456789012:function:my-function', + }, + }, + nonEmptyQueueMessage: 'MyQueue', + }, + accountId: 'XXXXXXXXXXXX', + dashboardName: 'MyDashboard', + domain: 'us-east-1', + height: 6, + period: 300, + title: 'SomeTitle', + width: 6, + widgetId: 'abc', + linkCharts: false, + locale: 'en-US', + timezone: { + label: 'UTC', + offsetISO: '+0', + offsetInMinutes: 0, + }, + isAutoPeriod: true, + theme: 'dark', + timeRange: { + mode: 'relative', + start: 0, + end: 0, + relativeStart: 0, + zoom: { + start: 0, + end: 0, + }, + }, + forms: { all: {} }, + }, + }; + + cloudwatchMock.on(GetMetricDataCommand).resolves({ + MetricDataResults: [ + { + Id: 'm0', + Values: [1, 2, 3], + }, + ], + }); + + const response = await handler(event); + + expect(response).toEqual(`

SomeTitle

+ +

MyQueue

+ + +
Queue NameVisible MessagesAction
bar1Goto Queue ReDrive
`); +}); diff --git a/src/__tests__/devapp/__snapshots__/snapshot.test.ts.snap b/src/__tests__/devapp/__snapshots__/snapshot.test.ts.snap index 15dc96a84..41d8346bc 100644 --- a/src/__tests__/devapp/__snapshots__/snapshot.test.ts.snap +++ b/src/__tests__/devapp/__snapshots__/snapshot.test.ts.snap @@ -16729,7 +16729,7 @@ function handler(event) { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "0a3aa2907d0245d3c25a4929bfd034a9fa84b893ca2209de39684e757f4cc170.zip", + "S3Key": "47bb9ddd0b4fa8279b3fcf81236a3bccc1ca3cabdf7b63ab0ddbaa6b13d793a1.zip", }, "Description": "[ConstructHub/SQSDLQWidget] Is a custom CloudWatch widget handler", "Handler": "index.handler", diff --git a/src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts b/src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts index b2fd60470..9037c6285 100644 --- a/src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts +++ b/src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts @@ -35,10 +35,14 @@ Param | Description \`\`\` `; -import { CloudWatch } from 'aws-sdk'; +import { + CloudWatchClient, + GetMetricDataCommand, +} from '@aws-sdk/client-cloudwatch'; + const DURATION = 5; // minutes -interface Event { +export interface Event { readonly key: string; readonly description: string; readonly widgetContext: WidgetContext; @@ -152,38 +156,41 @@ const getVisibleMessageCount = async ( ): Promise> => { // Keeping the time period to 5 minutes to show current state of the queue when re-drive happens const queues = Object.values(queueConfigs); - const params: CloudWatch.GetMetricDataInput = { - StartTime: new Date(new Date().getTime() - DURATION * 60 * 1000), // 5 minutes ago - EndTime: new Date(), // now - ScanBy: 'TimestampDescending', - MetricDataQueries: queues.map((queue, index) => ({ - Id: `m${index}`, - MetricStat: { - Period: 60, - Stat: 'Maximum', - Metric: { - Namespace: 'AWS/SQS', - MetricName: 'ApproximateNumberOfMessagesVisible', - Dimensions: [ - { - Name: 'QueueName', - Value: queue.queueName, - }, - ], + + const cloudwatchClient = new CloudWatchClient({}); + + const response = await cloudwatchClient.send( + new GetMetricDataCommand({ + StartTime: new Date(new Date().getTime() - DURATION * 60 * 1000), // 5 minutes ago + EndTime: new Date(), // now + ScanBy: 'TimestampDescending', + MetricDataQueries: queues.map((queue, index) => ({ + Id: `m${index}`, + MetricStat: { + Period: 60, + Stat: 'Maximum', + Metric: { + Namespace: 'AWS/SQS', + MetricName: 'ApproximateNumberOfMessagesVisible', + Dimensions: [ + { + Name: 'QueueName', + Value: queue.queueName, + }, + ], + }, }, - }, - })), - }; - const cloudwatch = new CloudWatch(); - const response = await cloudwatch.getMetricData(params).promise(); - const data = (response.MetricDataResults ?? []).reduce((acc, result) => { + })), + }) + ); + + return (response.MetricDataResults ?? []).reduce((acc, result) => { if (result.Id) { const id = Number.parseInt(result.Id.replace('m', ''), 10); return { ...acc, [queues[id].queueName]: result.Values?.[0] ?? 0 }; } return acc; }, {} as Record); - return data; }; function generateTable( diff --git a/yarn.lock b/yarn.lock index ca67ecc51..163ac2e5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -163,6 +163,55 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" +"@aws-sdk/client-cloudwatch@^3.650.0": + version "3.650.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-cloudwatch/-/client-cloudwatch-3.650.0.tgz#3136b6f4e9b81d311852d426b1c00825daa007cc" + integrity sha512-a8zmxWF6pfXi8icBraYimuHAiOKJ8fO+//XWzVPyc7X/DIFkIUkvlmk1B5WskJY80u3B21QFdPDUv6AlmyARMQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.650.0" + "@aws-sdk/client-sts" "3.650.0" + "@aws-sdk/core" "3.649.0" + "@aws-sdk/credential-provider-node" "3.650.0" + "@aws-sdk/middleware-host-header" "3.649.0" + "@aws-sdk/middleware-logger" "3.649.0" + "@aws-sdk/middleware-recursion-detection" "3.649.0" + "@aws-sdk/middleware-user-agent" "3.649.0" + "@aws-sdk/region-config-resolver" "3.649.0" + "@aws-sdk/types" "3.649.0" + "@aws-sdk/util-endpoints" "3.649.0" + "@aws-sdk/util-user-agent-browser" "3.649.0" + "@aws-sdk/util-user-agent-node" "3.649.0" + "@smithy/config-resolver" "^3.0.6" + "@smithy/core" "^2.4.1" + "@smithy/fetch-http-handler" "^3.2.5" + "@smithy/hash-node" "^3.0.4" + "@smithy/invalid-dependency" "^3.0.4" + "@smithy/middleware-compression" "^3.0.8" + "@smithy/middleware-content-length" "^3.0.6" + "@smithy/middleware-endpoint" "^3.1.1" + "@smithy/middleware-retry" "^3.0.16" + "@smithy/middleware-serde" "^3.0.4" + "@smithy/middleware-stack" "^3.0.4" + "@smithy/node-config-provider" "^3.1.5" + "@smithy/node-http-handler" "^3.2.0" + "@smithy/protocol-http" "^4.1.1" + "@smithy/smithy-client" "^3.3.0" + "@smithy/types" "^3.4.0" + "@smithy/url-parser" "^3.0.4" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.16" + "@smithy/util-defaults-mode-node" "^3.0.16" + "@smithy/util-endpoints" "^2.1.0" + "@smithy/util-middleware" "^3.0.4" + "@smithy/util-retry" "^3.0.4" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.1.3" + tslib "^2.6.2" + "@aws-sdk/client-codeartifact@^3.650.0": version "3.650.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-codeartifact/-/client-codeartifact-3.650.0.tgz#98bf9c351578eba8076b09ed36be6750232d0a65" @@ -2827,6 +2876,21 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@smithy/middleware-compression@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@smithy/middleware-compression/-/middleware-compression-3.0.8.tgz#f6ef00167d545175a57978dfd234ef377ba497e2" + integrity sha512-qY3ALlU0YT+ewBniwjQHwYApIK3fQWiNrLtcnw5Wk4spYZJQlDl0Z9SOvL+GKX4mKOlbR/pWG/gKfpGqwHW65Q== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/node-config-provider" "^3.1.5" + "@smithy/protocol-http" "^4.1.1" + "@smithy/types" "^3.4.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.4" + "@smithy/util-utf8" "^3.0.0" + fflate "0.8.1" + tslib "^2.6.2" + "@smithy/middleware-content-length@^3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.6.tgz#4837dafcfc085f1b9523d0784d05b87b569ad4ce" @@ -5478,6 +5542,11 @@ feed@^4.2.2: dependencies: xml-js "^1.6.11" +fflate@0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.1.tgz#1ed92270674d2ad3c73f077cd0acf26486dae6c9" + integrity sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ== + figures@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"