diff --git a/packages/@aws-cdk/assert/lib/assertions/have-resource.ts b/packages/@aws-cdk/assert/lib/assertions/have-resource.ts index 48f510016686b..a1564d1c926e7 100644 --- a/packages/@aws-cdk/assert/lib/assertions/have-resource.ts +++ b/packages/@aws-cdk/assert/lib/assertions/have-resource.ts @@ -7,7 +7,7 @@ import { StackInspector } from "../inspector"; * Properties can be: * * - An object, in which case its properties will be compared to those of the actual resource found - * - A callablage, in which case it will be treated as a predicate that is applied to the Properties of the found resources. + * - A callable, in which case it will be treated as a predicate that is applied to the Properties of the found resources. */ export function haveResource(resourceType: string, properties?: any): Assertion { return new HaveResourceAssertion(resourceType, properties); diff --git a/packages/@aws-cdk/cloudwatch/lib/alarm.ts b/packages/@aws-cdk/cloudwatch/lib/alarm.ts index 9c086a9732cec..14af5b9a91dd0 100644 --- a/packages/@aws-cdk/cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/cloudwatch/lib/alarm.ts @@ -1,7 +1,7 @@ import { Arn, Construct, Token } from '@aws-cdk/core'; import { cloudwatch } from '@aws-cdk/resources'; import { HorizontalAnnotation } from './graph'; -import { Metric } from './metric'; +import { Dimension, Metric, parseStatistic, Statistic, Unit } from './metric'; /** * Properties for Alarms @@ -158,7 +158,7 @@ export class Alarm extends Construct { okActions: new Token(() => this.okActions), // Metric - ...props.metric.toAlarmJson() + ...metricJson(props.metric) }); this.alarmArn = alarm.alarmArn; @@ -175,12 +175,12 @@ export class Alarm extends Construct { * * Typically the ARN of an SNS topic or ARN of an AutoScaling policy. */ - public onAlarm(action: IAlarmAction) { + public onAlarm(...actions: IAlarmAction[]) { if (this.alarmActions === undefined) { this.alarmActions = []; } - this.alarmActions.push(action.alarmActionArn); + this.alarmActions.push(...actions.map(a => a.alarmActionArn)); } /** @@ -188,12 +188,12 @@ export class Alarm extends Construct { * * Typically the ARN of an SNS topic or ARN of an AutoScaling policy. */ - public onInsufficientData(action: IAlarmAction) { + public onInsufficientData(...actions: IAlarmAction[]) { if (this.insufficientDataActions === undefined) { this.insufficientDataActions = []; } - this.insufficientDataActions.push(action.alarmActionArn); + this.insufficientDataActions.push(...actions.map(a => a.alarmActionArn)); } /** @@ -201,12 +201,12 @@ export class Alarm extends Construct { * * Typically the ARN of an SNS topic or ARN of an AutoScaling policy. */ - public onOk(action: IAlarmAction) { + public onOk(...actions: IAlarmAction[]) { if (this.okActions === undefined) { this.okActions = []; } - this.okActions.push(action.alarmActionArn); + this.okActions.push(...actions.map(a => a.alarmActionArn)); } /** @@ -256,4 +256,63 @@ export interface IAlarmAction { * Return the ARN that should be used for a CloudWatch Alarm action */ readonly alarmActionArn: Arn; -} \ No newline at end of file +} + +/** + * Return the JSON structure which represents the given metric in an alarm. + */ +function metricJson(metric: Metric): AlarmMetricJson { + const stat = parseStatistic(metric.statistic); + + const dims = metric.dimensionsAsList(); + + return { + dimensions: dims.length > 0 ? dims : undefined, + namespace: metric.namespace, + metricName: metric.metricName, + period: metric.periodSec, + statistic: stat.type === 'simple' ? stat.statistic : undefined, + extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, + unit: metric.unit + }; +} + +/** + * Properties used to construct the Metric identifying part of an Alarm + */ +export interface AlarmMetricJson { + /** + * The dimensions to apply to the alarm + */ + dimensions?: Dimension[]; + + /** + * Namespace of the metric + */ + namespace: string; + + /** + * Name of the metric + */ + metricName: string; + + /** + * How many seconds to aggregate over + */ + period: number; + + /** + * Simple aggregation function to use + */ + statistic?: Statistic; + + /** + * Percentile aggregation function to use + */ + extendedStatistic?: string; + + /** + * The unit of the alarm + */ + unit?: Unit; +} diff --git a/packages/@aws-cdk/cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/cloudwatch/lib/dashboard.ts index 1699fb69de5ad..a2e792e46726e 100644 --- a/packages/@aws-cdk/cloudwatch/lib/dashboard.ts +++ b/packages/@aws-cdk/cloudwatch/lib/dashboard.ts @@ -1,4 +1,4 @@ -import { Construct, Token, tokenAwareJsonify } from "@aws-cdk/core"; +import { Construct, Stack, Token, tokenAwareJsonify } from "@aws-cdk/core"; import { cloudwatch } from "@aws-cdk/resources"; import { Column, Row } from "./layout"; import { IWidget } from "./widget"; @@ -17,12 +17,19 @@ export interface DashboardProps { */ export class Dashboard extends Construct { private readonly rows: IWidget[] = []; + private readonly dashboard: cloudwatch.DashboardResource; constructor(parent: Construct, name: string, props?: DashboardProps) { super(parent, name); - new cloudwatch.DashboardResource(this, 'Resource', { - dashboardName: props && props.dashboardName, + // WORKAROUND -- Dashboard cannot be updated if the DashboardName is missing. + // This is a bug in CloudFormation, but we don't want CDK users to have a bad + // experience. We'll generate a name here if you did not supply one. + // See: https://github.com/awslabs/aws-cdk/issues/213 + const dashboardName = (props && props.dashboardName) || new Token(() => this.generateDashboardName()); + + this.dashboard = new cloudwatch.DashboardResource(this, 'Resource', { + dashboardName, dashboardBody: new Token(() => { const column = new Column(...this.rows); column.position(0, 0); @@ -48,4 +55,13 @@ export class Dashboard extends Construct { const w = widgets.length > 1 ? new Row(...widgets) : widgets[0]; this.rows.push(w); } + + /** + * Generate a unique dashboard name in case the user didn't supply one + */ + private generateDashboardName(): string { + // Combination of stack name and LogicalID, which are guaranteed to be unique. + const stack = Stack.find(this); + return stack.name + '-' + this.dashboard.logicalId; + } } diff --git a/packages/@aws-cdk/cloudwatch/lib/graph.ts b/packages/@aws-cdk/cloudwatch/lib/graph.ts index 9d9425643c2a1..c71c18e589108 100644 --- a/packages/@aws-cdk/cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/cloudwatch/lib/graph.ts @@ -1,6 +1,6 @@ import { AwsRegion, Token } from "@aws-cdk/core"; import { Alarm } from "./alarm"; -import { Metric } from "./metric"; +import { Metric, parseStatistic } from "./metric"; import { ConcreteWidget } from "./widget"; /** @@ -156,8 +156,8 @@ export class GraphWidget extends ConcreteWidget { view: 'timeSeries', title: this.props.title, region: this.props.region || new AwsRegion(), - metrics: (this.props.left || []).map(m => m.toGraphJson('left')).concat( - (this.props.right || []).map(m => m.toGraphJson('right'))), + metrics: (this.props.left || []).map(m => metricJson(m, 'left')).concat( + (this.props.right || []).map(m => metricJson(m, 'right'))), annotations: { horizontal: (this.props.leftAnnotations || []).map(mapAnnotation('left')).concat( (this.props.rightAnnotations || []).map(mapAnnotation('right'))) @@ -203,7 +203,7 @@ export class SingleValueWidget extends ConcreteWidget { view: 'singleValue', title: this.props.title, region: this.props.region || new AwsRegion(), - metrics: this.props.metrics.map(m => m.toGraphJson('left')) + metrics: this.props.metrics.map(m => metricJson(m, 'left')) } }]; } @@ -287,4 +287,34 @@ function mapAnnotation(yAxis: string): ((x: HorizontalAnnotation) => any) { return (a: HorizontalAnnotation) => { return { ...a, yAxis }; }; +} + +/** + * Return the JSON structure which represents this metric in a graph + * + * This will be called by GraphWidget, no need for clients to call this. + */ +function metricJson(metric: Metric, yAxis: string): any[] { + // Namespace and metric Name + const ret: any[] = [ + metric.namespace, + metric.metricName, + ]; + + // Dimensions + for (const dim of metric.dimensionsAsList()) { + ret.push(dim.name, dim.value); + } + + // Options + const stat = parseStatistic(metric.statistic); + ret.push({ + yAxis, + label: metric.label, + color: metric.color, + period: metric.periodSec, + stat: stat.type === 'simple' ? stat.statistic : 'p' + stat.percentile.toString(), + }); + + return ret; } \ No newline at end of file diff --git a/packages/@aws-cdk/cloudwatch/lib/metric.ts b/packages/@aws-cdk/cloudwatch/lib/metric.ts index 8a4adf74dd443..ceb2cfbd73d8c 100644 --- a/packages/@aws-cdk/cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/cloudwatch/lib/metric.ts @@ -101,6 +101,8 @@ export class Metric { this.metricName = props.metricName; this.periodSec = props.periodSec !== undefined ? props.periodSec : 300; this.statistic = props.statistic || "Average"; + this.label = props.label; + this.color = props.color; this.unit = props.unit; // Try parsing, this will throw if it's not a valid stat @@ -108,10 +110,13 @@ export class Metric { } /** - * Return a copy of Metric with properties changed - * @param props Re + * Return a copy of Metric with properties changed. + * + * All properties except namespace and metricName can be changed. + * + * @param props The set of properties to change. */ - public with(props: ChangeMetricProps): Metric { + public with(props: MetricCustomization): Metric { return new Metric({ dimensions: ifUndefined(props.dimensions, this.dimensions), namespace: this.namespace, @@ -148,91 +153,21 @@ export class Metric { } /** - * Return the JSON structure which represents this metric in an alarm + * Return the dimensions of this Metric as a list of Dimension. */ - public toAlarmJson(): MetricAlarmJson { - const stat = parseStatistic(this.statistic); + public dimensionsAsList(): Dimension[] { + const dims = this.dimensions; - return { - dimensions: hashToDimensions(this.dimensions), - namespace: this.namespace, - metricName: this.metricName, - period: this.periodSec, - statistic: stat.type === 'simple' ? stat.statistic : undefined, - extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, - unit: this.unit - }; - } - - /** - * Return the JSON structure which represents this metric in a graph - */ - public toGraphJson(yAxis: string): any[] { - // Namespace and metric Name - const ret: any[] = [ - this.namespace, - this.metricName, - ]; - - // Dimensions - for (const dim of hashToDimensions(this.dimensions) || []) { - ret.push(dim.name, dim.value); + if (dims === undefined) { + return []; } - // Options - const stat = parseStatistic(this.statistic); - ret.push({ - yAxis, - label: this.label, - color: this.color, - period: this.periodSec, - stat: stat.type === 'simple' ? stat.statistic : 'p' + stat.percentile.toString(), - }); + const list = Object.keys(dims).map(key => ({ name: key, value: dims[key] })); - return ret; + return list; } } -/** - * Properties used to construct the Metric identifying part of an Alarm - */ -export interface MetricAlarmJson { - /** - * The dimensions to apply to the alarm - */ - dimensions?: Dimension[]; - - /** - * Namespace of the metric - */ - namespace: string; - - /** - * Name of the metric - */ - metricName: string; - - /** - * How many seconds to aggregate over - */ - period: number; - - /** - * Simple aggregation function to use - */ - statistic?: Statistic; - - /** - * Percentile aggregation function to use - */ - extendedStatistic?: string; - - /** - * The unit of the alarm - */ - unit?: Unit; -} - /** * Metric dimension */ @@ -295,7 +230,7 @@ export enum Unit { /** * Properties of a metric that can be changed */ -export interface ChangeMetricProps { +export interface MetricCustomization { /** * Dimensions of the metric * @@ -426,19 +361,6 @@ export interface NewAlarmProps { actionsEnabled?: boolean; } -function hashToDimensions(x?: DimensionHash): Dimension[] | undefined { - if (x === undefined) { - return undefined; - } - - const list = Object.keys(x).map(key => ({ name: key, value: x[key] })); - if (list.length === 0) { - return undefined; - } - - return list; -} - function ifUndefined(x: T | undefined, def: T | undefined): T | undefined { if (x !== undefined) { return x; @@ -459,7 +381,7 @@ interface PercentileStatistic { /** * Parse a statistic, returning the type of metric that was used (simple or percentile) */ -function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic { +export function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic { const lowerStat = stat.toLowerCase(); // Simple statistics diff --git a/packages/@aws-cdk/cloudwatch/test/integ.alarm-and-dashboard.expected.json b/packages/@aws-cdk/cloudwatch/test/integ.alarm-and-dashboard.expected.json index 3ac45ae5ec290..64feeab61fc77 100644 --- a/packages/@aws-cdk/cloudwatch/test/integ.alarm-and-dashboard.expected.json +++ b/packages/@aws-cdk/cloudwatch/test/integ.alarm-and-dashboard.expected.json @@ -29,6 +29,7 @@ "DashCCD7F836": { "Type": "AWS::CloudWatch::Dashboard", "Properties": { + "DashboardName": "aws-cdk-cloudwatch-DashCCD7F836", "DashboardBody": { "Fn::Sub": [ "{\"widgets\":[{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":0,\"y\":0,\"properties\":{\"markdown\":\"# This is my dashboard\"}},{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":6,\"y\":0,\"properties\":{\"markdown\":\"you like?\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":2,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Messages in queue\",\"region\":\"${ref0}\",\"annotations\":{\"alarms\":[\"${ref1}\"]},\"yAxis\":{\"left\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":8,\"properties\":{\"view\":\"timeSeries\",\"title\":\"More messages in queue with alarm annotation\",\"region\":\"${ref0}\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"${ref2}\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]],\"annotations\":{\"horizontal\":[{\"label\":\"ApproximateNumberOfMessagesVisible >= 100 for 3 datapoints within 15 minutes\",\"value\":100,\"yAxis\":\"left\"}]},\"yAxis\":{\"left\":{\"min\":0},\"right\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":14,\"properties\":{\"view\":\"singleValue\",\"title\":\"Current messages in queue\",\"region\":\"${ref0}\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"${ref2}\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]]}}]}", @@ -54,4 +55,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/cloudwatch/test/test.dashboard.ts b/packages/@aws-cdk/cloudwatch/test/test.dashboard.ts index cc17d9207c5f4..58e44bb0a2bd8 100644 --- a/packages/@aws-cdk/cloudwatch/test/test.dashboard.ts +++ b/packages/@aws-cdk/cloudwatch/test/test.dashboard.ts @@ -1,5 +1,5 @@ import { expect, haveResource, isSuperObject } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import { Dashboard, GraphWidget, TextWidget } from '../lib'; @@ -94,6 +94,24 @@ export = { test.done(); }, + + 'work around CloudFormation bug'(test: Test) { + // See: https://github.com/awslabs/aws-cdk/issues/213 + + // GIVEN + const app = new App(); + const stack = new Stack(app, 'MyStack'); + + // WHEN + new Dashboard(stack, 'MyDashboard'); + + // THEN + expect(stack).to(haveResource('AWS::CloudWatch::Dashboard', { + DashboardName: 'MyStack-MyDashboardCD351363' + })); + + test.done(); + } }; /** diff --git a/packages/@aws-cdk/cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/cloudwatch/test/test.graphs.ts index eaa745dca2640..7bc5ec756d9a6 100644 --- a/packages/@aws-cdk/cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/cloudwatch/test/test.graphs.ts @@ -36,6 +36,31 @@ export = { test.done(); }, + 'label and color are respected in constructor'(test: Test) { + // WHEN + const widget = new GraphWidget({ + left: [new Metric({ namespace: 'CDK', metricName: 'Test', label: 'MyMetric', color: '000000' }) ], + }); + + // THEN + test.deepEqual(resolve(widget.toJson()), [{ + type: 'metric', + width: 6, + height: 6, + properties: { + view: 'timeSeries', + region: { Ref: 'AWS::Region' }, + metrics: [ + ['CDK', 'Test', { yAxis: 'left', period: 300, stat: 'Average', label: 'MyMetric', color: '000000' }], + ], + annotations: { horizontal: [] }, + yAxis: { left: { min: 0 }, right: { min: 0 } } + } + }]); + + test.done(); + }, + 'singlevalue widget'(test: Test) { // GIVEN const metric = new Metric({ namespace: 'CDK', metricName: 'Test' }); diff --git a/packages/@aws-cdk/lambda/lib/lambda-ref.ts b/packages/@aws-cdk/lambda/lib/lambda-ref.ts index cdc1f1cd54c5c..0b6eb53b9bbfe 100644 --- a/packages/@aws-cdk/lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/lambda/lib/lambda-ref.ts @@ -1,4 +1,4 @@ -import { ChangeMetricProps, Metric } from '@aws-cdk/cloudwatch'; +import { Metric, MetricCustomization } from '@aws-cdk/cloudwatch'; import { AccountPrincipal, Arn, Construct, FnSelect, FnSplit, PolicyPrincipal, PolicyStatement, resolve, ServicePrincipal, Token } from '@aws-cdk/core'; import { EventRuleTarget, IEventRuleTarget } from '@aws-cdk/events'; @@ -42,7 +42,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { /** * Return the given named metric for this Lambda */ - public static metricAll(metricName: string, props?: ChangeMetricProps): Metric { + public static metricAll(metricName: string, props?: MetricCustomization): Metric { return new Metric({ namespace: 'AWS/Lambda', metricName, @@ -54,7 +54,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public static metricAllErrors(props?: ChangeMetricProps): Metric { + public static metricAllErrors(props?: MetricCustomization): Metric { return LambdaRef.metricAll('Errors', { statistic: 'sum', ...props }); } @@ -63,7 +63,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default average over 5 minutes */ - public static metricAllDuration(props?: ChangeMetricProps): Metric { + public static metricAllDuration(props?: MetricCustomization): Metric { return LambdaRef.metricAll('Duration', props); } @@ -72,7 +72,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public static metricAllInvocations(props?: ChangeMetricProps): Metric { + public static metricAllInvocations(props?: MetricCustomization): Metric { return LambdaRef.metricAll('Invocations', { statistic: 'sum', ...props }); } @@ -81,7 +81,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public static metricAllThrottles(props?: ChangeMetricProps): Metric { + public static metricAllThrottles(props?: MetricCustomization): Metric { return LambdaRef.metricAll('Throttles', { statistic: 'sum', ...props }); } @@ -167,7 +167,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { /** * Return the given named metric for this Lambda */ - public metric(metricName: string, props?: ChangeMetricProps): Metric { + public metric(metricName: string, props?: MetricCustomization): Metric { return new Metric({ namespace: 'AWS/Lambda', metricName, @@ -181,7 +181,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public metricErrors(props?: ChangeMetricProps): Metric { + public metricErrors(props?: MetricCustomization): Metric { return this.metric('Errors', { statistic: 'sum', ...props }); } @@ -190,7 +190,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default average over 5 minutes */ - public metricDuration(props?: ChangeMetricProps): Metric { + public metricDuration(props?: MetricCustomization): Metric { return this.metric('Duration', props); } @@ -199,7 +199,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public metricInvocations(props?: ChangeMetricProps): Metric { + public metricInvocations(props?: MetricCustomization): Metric { return this.metric('Invocations', { statistic: 'sum', ...props }); } @@ -208,7 +208,7 @@ export abstract class LambdaRef extends Construct implements IEventRuleTarget { * * @default sum over 5 minutes */ - public metricThrottles(props?: ChangeMetricProps): Metric { + public metricThrottles(props?: MetricCustomization): Metric { return this.metric('Throttles', { statistic: 'sum', ...props }); } diff --git a/packages/@aws-cdk/sns/lib/topic-ref.ts b/packages/@aws-cdk/sns/lib/topic-ref.ts index f579cdbf40c19..93655b3382579 100644 --- a/packages/@aws-cdk/sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/sns/lib/topic-ref.ts @@ -1,4 +1,4 @@ -import { ChangeMetricProps, IAlarmAction, Metric } from '@aws-cdk/cloudwatch'; +import { IAlarmAction, Metric, MetricCustomization } from '@aws-cdk/cloudwatch'; import { Arn, Construct, Output, PolicyStatement, ServicePrincipal, Token } from '@aws-cdk/core'; import { EventRuleTarget, IEventRuleTarget } from '@aws-cdk/events'; import { IIdentityResource } from '@aws-cdk/iam'; @@ -233,7 +233,7 @@ export abstract class TopicRef extends Construct implements IEventRuleTarget, IA /** * Construct a Metric object for the current topic for the given metric */ - public metric(metricName: string, props?: ChangeMetricProps): Metric { + public metric(metricName: string, props?: MetricCustomization): Metric { return new Metric({ namespace: 'AWS/SNS', dimensions: { TopicName: this.topicName }, @@ -247,7 +247,7 @@ export abstract class TopicRef extends Construct implements IEventRuleTarget, IA * * @default average over 5 minutes */ - public metricPublishSize(props?: ChangeMetricProps): Metric { + public metricPublishSize(props?: MetricCustomization): Metric { return this.metric('PublishSize', props); } @@ -256,7 +256,7 @@ export abstract class TopicRef extends Construct implements IEventRuleTarget, IA * * @default sum over 5 minutes */ - public metricNumberOfMessagesPublished(props?: ChangeMetricProps): Metric { + public metricNumberOfMessagesPublished(props?: MetricCustomization): Metric { return this.metric('NumberOfMessagesPublished', { statistic: 'sum', ...props }); } @@ -265,7 +265,7 @@ export abstract class TopicRef extends Construct implements IEventRuleTarget, IA * * @default sum over 5 minutes */ - public metricNumberOfMessagesFailed(props?: ChangeMetricProps): Metric { + public metricNumberOfMessagesFailed(props?: MetricCustomization): Metric { return this.metric('NumberOfMessagesFailed', { statistic: 'sum', ...props }); } @@ -274,7 +274,7 @@ export abstract class TopicRef extends Construct implements IEventRuleTarget, IA * * @default sum over 5 minutes */ - public metricNumberOfMessagesDelivered(props?: ChangeMetricProps): Metric { + public metricNumberOfMessagesDelivered(props?: MetricCustomization): Metric { return this.metric('NumberOfMessagesDelivered', { statistic: 'sum', ...props }); } }