Skip to content

Commit 489340e

Browse files
feat(cloudwatch): Add CustomWidget (#19327)
This PR introcudes the CustomWidget for Cloudwatch Dashboards fixes: #17579 based on the work of @dannyber If you wonder, why only a string typed option was used instead of an lambda.IFunction, the reason is, that this would introduce a circular dependency between lambda -> cloudwatch -> lambda and leads to build errors. Creating a new package for this tiny feature to work around the circular dependency would be too much of a good thing. Apart from that it could be unintuitive to find the custom widget in a package 'aws-cloudwatch-pattarns' if all other widgets come from 'aws-cloudwatch'. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 42a8947 commit 489340e

File tree

7 files changed

+186
-4
lines changed

7 files changed

+186
-4
lines changed

packages/@aws-cdk/aws-cloudwatch/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,28 @@ dashboard.addWidgets(new cloudwatch.LogQueryWidget({
501501
}));
502502
```
503503

504+
### Custom widget
505+
506+
A `CustomWidget` shows the result of an AWS Lambda function:
507+
508+
```ts
509+
declare const dashboard: cloudwatch.Dashboard;
510+
511+
// Import or create a lambda function
512+
const fn = lambda.Function.fromFunctionArn(
513+
dashboard,
514+
'Function',
515+
'arn:aws:lambda:us-east-1:123456789012:function:MyFn',
516+
);
517+
518+
dashboard.addWidgets(new cloudwatch.CustomWidget({
519+
functionArn: fn.functionArn,
520+
title: 'My lambda baked widget',
521+
}));
522+
```
523+
524+
You can learn more about custom widgets in the [Amazon Cloudwatch User Guide](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/add_custom_widget_dashboard.html).
525+
504526
### Dashboard Layout
505527

506528
The widgets on a dashboard are visually laid out in a grid that is 24 columns

packages/@aws-cdk/aws-cloudwatch/lib/graph.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,97 @@ export class SingleValueWidget extends ConcreteWidget {
368368
}
369369
}
370370

371+
/**
372+
* The properties for a CustomWidget
373+
*/
374+
export interface CustomWidgetProps {
375+
/**
376+
* The Arn of the AWS Lambda function that returns HTML or JSON that will be displayed in the widget
377+
*/
378+
readonly functionArn: string;
379+
380+
/**
381+
* Width of the widget, in a grid of 24 units wide
382+
*
383+
* @default 6
384+
*/
385+
readonly width?: number;
386+
387+
/**
388+
* Height of the widget
389+
*
390+
* @default - 6 for Alarm and Graph widgets.
391+
* 3 for single value widgets where most recent value of a metric is displayed.
392+
*/
393+
readonly height?: number;
394+
395+
/**
396+
* The title of the widget
397+
*/
398+
readonly title: string;
399+
400+
/**
401+
* Update the widget on refresh
402+
*
403+
* @default true
404+
*/
405+
readonly updateOnRefresh?: boolean;
406+
407+
/**
408+
* Update the widget on resize
409+
*
410+
* @default true
411+
*/
412+
readonly updateOnResize?: boolean;
413+
414+
/**
415+
* Update the widget on time range change
416+
*
417+
* @default true
418+
*/
419+
readonly updateOnTimeRangeChange?: boolean;
420+
421+
/**
422+
* Parameters passed to the lambda function
423+
*
424+
* @default - no parameters are passed to the lambda function
425+
*/
426+
readonly params?: any;
427+
}
428+
429+
/**
430+
* A CustomWidget shows the result of a AWS lambda function
431+
*/
432+
export class CustomWidget extends ConcreteWidget {
433+
434+
private readonly props: CustomWidgetProps;
435+
436+
public constructor(props: CustomWidgetProps) {
437+
super(props.width ?? 6, props.height ?? 6);
438+
this.props = props;
439+
}
440+
441+
public toJson(): any[] {
442+
return [{
443+
type: 'custom',
444+
width: this.width,
445+
height: this.height,
446+
x: this.x,
447+
y: this.y,
448+
properties: {
449+
endpoint: this.props.functionArn,
450+
params: this.props.params,
451+
title: this.props.title,
452+
updateOn: {
453+
refresh: this.props.updateOnRefresh ?? true,
454+
resize: this.props.updateOnResize ?? true,
455+
timeRange: this.props.updateOnTimeRangeChange ?? true,
456+
},
457+
},
458+
}];
459+
}
460+
}
461+
371462
/**
372463
* Horizontal annotation to be added to a graph
373464
*/

packages/@aws-cdk/aws-cloudwatch/test/alarm-and-dashboard.integ.snapshot/aws-cdk-cloudwatch-alarms.template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
"QueueName"
117117
]
118118
},
119-
"\"]],\"singleValueFullPrecision\":true}}]}"
119+
"\"]],\"singleValueFullPrecision\":true}},{\"type\":\"custom\",\"width\":6,\"height\":6,\"x\":0,\"y\":56,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-west-2:123456789012:function:my-function\",\"title\":\"My custom alarm\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true}}}]}"
120120
]
121121
]
122122
},

packages/@aws-cdk/aws-cloudwatch/test/alarm-and-dashboard.integ.snapshot/tree.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
"QueueName"
163163
]
164164
},
165-
"\"]],\"singleValueFullPrecision\":true}}]}"
165+
"\"]],\"singleValueFullPrecision\":true}},{\"type\":\"custom\",\"width\":6,\"height\":6,\"x\":0,\"y\":56,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-west-2:123456789012:function:my-function\",\"title\":\"My custom alarm\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true}}}]}"
166166
]
167167
]
168168
},

packages/@aws-cdk/aws-cloudwatch/test/graphs.test.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Duration, Stack } from '@aws-cdk/core';
2-
import { Alarm, AlarmWidget, Color, GraphWidget, GraphWidgetView, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType } from '../lib';
2+
import { Alarm, AlarmWidget, Color, GraphWidget, GraphWidgetView, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType, CustomWidget } from '../lib';
33

44
describe('Graphs', () => {
55
test('add stacked property to graphs', () => {
@@ -348,6 +348,71 @@ describe('Graphs', () => {
348348

349349
});
350350

351+
test('custom widget basic', () => {
352+
// GIVEN
353+
const stack = new Stack();
354+
355+
// WHEN
356+
const widget = new CustomWidget({
357+
functionArn: 'arn:aws:lambda:us-east-1:123456789:function:customwidgetfunction',
358+
title: 'CustomWidget',
359+
});
360+
361+
// THEN
362+
expect(stack.resolve(widget.toJson())).toEqual([{
363+
type: 'custom',
364+
width: 6,
365+
height: 6,
366+
properties: {
367+
title: 'CustomWidget',
368+
endpoint: 'arn:aws:lambda:us-east-1:123456789:function:customwidgetfunction',
369+
updateOn: {
370+
refresh: true,
371+
resize: true,
372+
timeRange: true,
373+
},
374+
},
375+
}]);
376+
});
377+
378+
test('custom widget full config', () => {
379+
// GIVEN
380+
const stack = new Stack();
381+
382+
// WHEN
383+
const widget = new CustomWidget({
384+
functionArn: 'arn:aws:lambda:us-east-1:123456789:function:customwidgetfunction',
385+
title: 'CustomWidget',
386+
height: 1,
387+
width: 1,
388+
params: {
389+
any: 'param',
390+
},
391+
updateOnRefresh: false,
392+
updateOnResize: false,
393+
updateOnTimeRangeChange: false,
394+
});
395+
396+
// THEN
397+
expect(stack.resolve(widget.toJson())).toEqual([{
398+
type: 'custom',
399+
width: 1,
400+
height: 1,
401+
properties: {
402+
title: 'CustomWidget',
403+
endpoint: 'arn:aws:lambda:us-east-1:123456789:function:customwidgetfunction',
404+
params: {
405+
any: 'param',
406+
},
407+
updateOn: {
408+
refresh: false,
409+
resize: false,
410+
timeRange: false,
411+
},
412+
},
413+
}]);
414+
});
415+
351416
test('add annotations to graph', () => {
352417
// WHEN
353418
const stack = new Stack();

packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
"QueueName"
117117
]
118118
},
119-
"\"]],\"singleValueFullPrecision\":true}}]}"
119+
"\"]],\"singleValueFullPrecision\":true}},{\"type\":\"custom\",\"width\":6,\"height\":6,\"x\":0,\"y\":56,\"properties\":{\"endpoint\":\"arn:aws:lambda:us-west-2:123456789012:function:my-function\",\"title\":\"My custom alarm\",\"updateOn\":{\"refresh\":true,\"resize\":true,\"timeRange\":true}}}]}"
120120
]
121121
]
122122
},

packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,9 @@ dashboard.addWidgets(new cloudwatch.SingleValueWidget({
102102
metrics: [sentMessageSizeMetric],
103103
fullPrecision: true,
104104
}));
105+
dashboard.addWidgets(new cloudwatch.CustomWidget({
106+
title: 'My custom alarm',
107+
functionArn: 'arn:aws:lambda:us-west-2:123456789012:function:my-function',
108+
}));
105109

106110
app.synth();

0 commit comments

Comments
 (0)