-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
rule.ts
237 lines (207 loc) · 6.6 KB
/
rule.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import { Construct, Lazy, Resource } from '@aws-cdk/cdk';
import { EventPattern } from './event-pattern';
import { CfnRule } from './events.generated';
import { IRule } from './rule-ref';
import { Schedule } from './schedule';
import { IRuleTarget } from './target';
import { mergeEventPattern } from './util';
export interface RuleProps {
/**
* A description of the rule's purpose.
*
* @default - No description.
*/
readonly description?: string;
/**
* A name for the rule.
*
* @default - AWS CloudFormation generates a unique physical ID and uses that ID
* for the rule name. For more information, see Name Type.
*/
readonly ruleName?: string;
/**
* Indicates whether the rule is enabled.
*
* @default true
*/
readonly enabled?: boolean;
/**
* The schedule or rate (frequency) that determines when CloudWatch Events
* runs the rule. For more information, see Schedule Expression Syntax for
* Rules in the Amazon CloudWatch User Guide.
*
* @see http://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
*
* You must specify this property, the `eventPattern` property, or both.
*
* @default - None.
*/
readonly schedule?: Schedule;
/**
* Describes which events CloudWatch Events routes to the specified target.
* These routed events are matched events. For more information, see Events
* and Event Patterns in the Amazon CloudWatch User Guide.
*
* @see
* http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CloudWatchEventsandEventPatterns.html
*
* You must specify this property (either via props or via
* `addEventPattern`), the `scheduleExpression` property, or both. The
* method `addEventPattern` can be used to add filter values to the event
* pattern.
*
* @default - None.
*/
readonly eventPattern?: EventPattern;
/**
* Targets to invoke when this rule matches an event.
*
* Input will be the full matched event. If you wish to specify custom
* target input, use `addTarget(target[, inputOptions])`.
*
* @default - No targets.
*/
readonly targets?: IRuleTarget[];
}
/**
* Defines a CloudWatch Event Rule in this stack.
*
* @resource AWS::Events::Rule
*/
export class Rule extends Resource implements IRule {
public static fromEventRuleArn(scope: Construct, id: string, eventRuleArn: string): IRule {
class Import extends Resource implements IRule {
public ruleArn = eventRuleArn;
}
return new Import(scope, id);
}
public readonly ruleArn: string;
private readonly targets = new Array<CfnRule.TargetProperty>();
private readonly eventPattern: EventPattern = { };
private scheduleExpression?: string;
constructor(scope: Construct, id: string, props: RuleProps = { }) {
super(scope, id);
const resource = new CfnRule(this, 'Resource', {
name: props.ruleName,
description: props.description,
state: props.enabled == null ? 'ENABLED' : (props.enabled ? 'ENABLED' : 'DISABLED'),
scheduleExpression: Lazy.stringValue({ produce: () => this.scheduleExpression }),
eventPattern: Lazy.anyValue({ produce: () => this.renderEventPattern() }),
targets: Lazy.anyValue({ produce: () => this.renderTargets() }),
});
this.ruleArn = resource.attrArn;
this.addEventPattern(props.eventPattern);
this.scheduleExpression = props.schedule && props.schedule.expressionString;
for (const target of props.targets || []) {
this.addTarget(target);
}
}
/**
* Adds a target to the rule. The abstract class RuleTarget can be extended to define new
* targets.
*
* No-op if target is undefined.
*/
public addTarget(target?: IRuleTarget) {
if (!target) { return; }
const targetProps = target.bind(this);
const id = sanitizeId(targetProps.id);
const inputProps = targetProps.input && targetProps.input.bind(this);
// check if a target with this ID already exists
if (this.targets.find(t => t.id === id)) {
throw new Error('Duplicate event rule target with ID: ' + id);
}
const roleArn = targetProps.role ? targetProps.role.roleArn : undefined;
this.targets.push({
id,
arn: targetProps.arn,
roleArn,
ecsParameters: targetProps.ecsParameters,
kinesisParameters: targetProps.kinesisParameters,
runCommandParameters: targetProps.runCommandParameters,
input: inputProps && inputProps.input,
inputPath: inputProps && inputProps.inputPath,
inputTransformer: inputProps && inputProps.inputTemplate !== undefined ? {
inputTemplate: inputProps.inputTemplate,
inputPathsMap: inputProps.inputPathsMap,
} : undefined,
});
}
/**
* Adds an event pattern filter to this rule. If a pattern was already specified,
* these values are merged into the existing pattern.
*
* For example, if the rule already contains the pattern:
*
* {
* "resources": [ "r1" ],
* "detail": {
* "hello": [ 1 ]
* }
* }
*
* And `addEventPattern` is called with the pattern:
*
* {
* "resources": [ "r2" ],
* "detail": {
* "foo": [ "bar" ]
* }
* }
*
* The resulting event pattern will be:
*
* {
* "resources": [ "r1", "r2" ],
* "detail": {
* "hello": [ 1 ],
* "foo": [ "bar" ]
* }
* }
*
*/
public addEventPattern(eventPattern?: EventPattern) {
if (!eventPattern) {
return;
}
mergeEventPattern(this.eventPattern, eventPattern);
}
protected validate() {
if (Object.keys(this.eventPattern).length === 0 && !this.scheduleExpression) {
return [ `Either 'eventPattern' or 'schedule' must be defined` ];
}
return [ ];
}
private renderTargets() {
if (this.targets.length === 0) {
return undefined;
}
return this.targets;
}
private renderEventPattern() {
const eventPattern = this.eventPattern;
if (Object.keys(eventPattern).length === 0) {
return undefined;
}
// rename 'detailType' to 'detail-type'
const out: any = {};
for (let key of Object.keys(eventPattern)) {
const value = (eventPattern as any)[key];
if (key === 'detailType') {
key = 'detail-type';
}
out[key] = value;
}
return out;
}
}
/**
* Sanitize whatever is returned to make a valid ID
*
* Result must match regex [\.\-_A-Za-z0-9]+
*/
function sanitizeId(id: string) {
const _id = id.replace(/[^\.\-_A-Za-z0-9]/g, '-');
// cut to 64 chars to respect AWS::Events::Rule Target Id field specification
return _id.substring(Math.max(_id.length - 64, 0), _id.length);
}