Skip to content

Commit 7cb8e5e

Browse files
authored
feat(events): group CW Event Targets in module (#2576)
Move new event targets into `@aws-cdk/aws-events-targets` - CodePipeline - EC2 task - StepFunctions StateMachine Invocation payloads are now under the control of the target classes, so that targets for which the payload maps onto API calls (such as ECS, CodeBuild, etc) can specify the API parameters directly as props. `EventTargetInput` is a union class with three variants, allowing serialization of strings, multiline strings and objects. Target inputs support a special type of Token (`EventField`) which can be used to refer to fields from the event, instead of value literals. A number of predefined events and the fields that they have available have been defined, so that accessing them becomes even more convenient (`StageChangeEvent`, `PhaseChangeEvent`, `ReferenceEvent`). To be able to use Tokens to implement EventInput substitution, add a refactoring and more principaled separation of concerns in the Token code. Resolution can now be more easily hooked, CloudFormation-aware string concatenation is implemented using a plugin, and Token callbacks receive an `IResolveContext` which they can use to resolve deeper (and will reuse the same settings that the resolver was started with). We should as good as be able to get rid of `stack.node.resolve()` in the near future. ALSO: - Simplified `LatestDeploymentResource` to use existing logical ID overriding features. - Add an `onEvent()` method to `CloudTrail`. - Fix API misuse in the rendering of a CodePipeline, where Token rendering would lead to stateful side effects. Make Actions render out their region directly, if set, without requiring overrides. - In ECS task `assignPublicIp` now defaults to `undefined`, instead of `DISABLED`, to align with service API. - Fix a bug in Token resolution where Tokens returning fresh `CfnReference` objects upon resolution would fail to be detected during cross-stack reference analysis; `CfnReference` now has static methods that return singleton objects. - Get rid of an extra "resolve" call in `CfnResource.toCloudFormation()`. We can now use `PostProcessToken` to apply the property renames and output validation we were originally doing the resolve for. Fixes #2403, fixes #2404, fixes #2581. BREAKING CHANGES * `@aws-cdk/aws-codepipeline.Pipeline` is no longer an event target itself, use `@aws-cdk/aws-events-targets.CodePipeline` instead. * `@aws-cdk/aws-stepfunctions.StateMachine` is no longer an event target itself, use `@aws-cdk/aws-events-targets.SfnStateMachine` instead. * `@aws-cdk/aws-ecs.Ec2RunTask` has been renamed to `@aws-cdk/aws-events-targets.EcsEc2Task`. * `CloudFormationJSON.stringify()` is now renamed to `CloudFormationLang.toJSON()`.
1 parent 1f004f6 commit 7cb8e5e

File tree

98 files changed

+2308
-1394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+2308
-1394
lines changed

Diff for: packages/@aws-cdk/aws-apigateway/lib/deployment.ts

+4-44
Original file line numberDiff line numberDiff line change
@@ -91,51 +91,13 @@ export class Deployment extends Resource {
9191
}
9292

9393
class LatestDeploymentResource extends CfnDeployment {
94-
private originalLogicalId?: string;
95-
private lazyLogicalIdRequired: boolean;
96-
private lazyLogicalId?: string;
97-
private logicalIdToken: Token;
9894
private hashComponents = new Array<any>();
95+
private originalLogicalId: string;
9996

10097
constructor(scope: Construct, id: string, props: CfnDeploymentProps) {
10198
super(scope, id, props);
10299

103-
// from this point, don't allow accessing logical ID before synthesis
104-
this.lazyLogicalIdRequired = true;
105-
106-
this.logicalIdToken = new Token(() => this.lazyLogicalId);
107-
}
108-
109-
/**
110-
* Returns either the original or the custom logical ID of this resource.
111-
*/
112-
public get logicalId() {
113-
if (!this.lazyLogicalIdRequired) {
114-
return this.originalLogicalId!;
115-
}
116-
117-
return this.logicalIdToken.toString();
118-
}
119-
120-
/**
121-
* Sets the logical ID of this resource.
122-
*/
123-
public set logicalId(v: string) {
124-
this.originalLogicalId = v;
125-
}
126-
127-
/**
128-
* Returns a lazy reference to this resource (evaluated only upon synthesis).
129-
*/
130-
public get ref() {
131-
return new Token(() => ({ Ref: this.lazyLogicalId })).toString();
132-
}
133-
134-
/**
135-
* Does nothing.
136-
*/
137-
public set ref(_v: string) {
138-
return;
100+
this.originalLogicalId = this.node.stack.logicalIds.getLogicalId(this);
139101
}
140102

141103
/**
@@ -159,15 +121,13 @@ class LatestDeploymentResource extends CfnDeployment {
159121
protected prepare() {
160122
// if hash components were added to the deployment, we use them to calculate
161123
// a logical ID for the deployment resource.
162-
if (this.hashComponents.length === 0) {
163-
this.lazyLogicalId = this.originalLogicalId;
164-
} else {
124+
if (this.hashComponents.length > 0) {
165125
const md5 = crypto.createHash('md5');
166126
this.hashComponents
167127
.map(c => this.node.resolve(c))
168128
.forEach(c => md5.update(JSON.stringify(c)));
169129

170-
this.lazyLogicalId = this.originalLogicalId + md5.digest("hex");
130+
this.overrideLogicalId(this.originalLogicalId + md5.digest("hex"));
171131
}
172132

173133
super.prepare();

Diff for: packages/@aws-cdk/aws-cloudtrail/lib/index.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import events = require('@aws-cdk/aws-events');
12
import iam = require('@aws-cdk/aws-iam');
23
import kms = require('@aws-cdk/aws-kms');
34
import logs = require('@aws-cdk/aws-logs');
@@ -209,6 +210,21 @@ export class Trail extends Resource {
209210
}]
210211
});
211212
}
213+
214+
/**
215+
* Create an event rule for when an event is recorded by any trail.
216+
*
217+
* Note that the event doesn't necessarily have to come from this
218+
* trail. Be sure to filter the event properly using an event pattern.
219+
*/
220+
public onEvent(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
221+
const rule = new events.Rule(this, name, options);
222+
rule.addTarget(target);
223+
rule.addEventPattern({
224+
detailType: ['AWS API Call via CloudTrail']
225+
});
226+
return rule;
227+
}
212228
}
213229

214230
/**

Diff for: packages/@aws-cdk/aws-cloudtrail/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"pkglint": "^0.31.0"
6868
},
6969
"dependencies": {
70+
"@aws-cdk/aws-events": "^0.31.0",
7071
"@aws-cdk/aws-iam": "^0.31.0",
7172
"@aws-cdk/aws-kms": "^0.31.0",
7273
"@aws-cdk/aws-logs": "^0.31.0",
@@ -75,6 +76,7 @@
7576
},
7677
"homepage": "https://github.com/awslabs/aws-cdk",
7778
"peerDependencies": {
79+
"@aws-cdk/aws-events": "^0.31.0",
7880
"@aws-cdk/aws-iam": "^0.31.0",
7981
"@aws-cdk/aws-kms": "^0.31.0",
8082
"@aws-cdk/aws-logs": "^0.31.0",

Diff for: packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,39 @@ export = {
189189
test.done();
190190
},
191191
}
192-
}
192+
},
193+
194+
'add an event rule'(test: Test) {
195+
// GIVEN
196+
const stack = getTestStack();
197+
const trail = new Trail(stack, 'MyAmazingCloudTrail', { managementEvents: ReadWriteType.WriteOnly });
198+
199+
// WHEN
200+
trail.onEvent('DoEvents', {
201+
bind: () => ({
202+
arn: 'arn',
203+
id: 'myid'
204+
})
205+
});
206+
207+
// THEN
208+
expect(stack).to(haveResource('AWS::Events::Rule', {
209+
EventPattern: {
210+
"detail-type": [
211+
"AWS API Call via CloudTrail"
212+
]
213+
},
214+
State: "ENABLED",
215+
Targets: [
216+
{
217+
Arn: "arn",
218+
Id: "myid"
219+
}
220+
]
221+
}));
222+
223+
test.done();
224+
},
193225
};
194226

195227
function getTestStack(): Stack {

Diff for: packages/@aws-cdk/aws-codebuild/lib/events.ts

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import events = require('@aws-cdk/aws-events');
2+
3+
/**
4+
* Event fields for the CodeBuild "state change" event
5+
*
6+
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html#sample-build-notifications-ref
7+
*/
8+
export class StateChangeEvent {
9+
/**
10+
* The triggering build's status
11+
*/
12+
public static get buildStatus() {
13+
return events.EventField.fromPath('$.detail.build-status');
14+
}
15+
16+
/**
17+
* The triggering build's project name
18+
*/
19+
public static get projectName() {
20+
return events.EventField.fromPath('$.detail.project-name');
21+
}
22+
23+
/**
24+
* Return the build id
25+
*/
26+
public static get buildId() {
27+
return events.EventField.fromPath('$.detail.build-id');
28+
}
29+
30+
public static get currentPhase() {
31+
return events.EventField.fromPath('$.detail.current-phase');
32+
}
33+
34+
private constructor() {
35+
}
36+
}
37+
38+
/**
39+
* Event fields for the CodeBuild "phase change" event
40+
*
41+
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html#sample-build-notifications-ref
42+
*/
43+
export class PhaseChangeEvent {
44+
/**
45+
* The triggering build's project name
46+
*/
47+
public static get projectName() {
48+
return events.EventField.fromPath('$.detail.project-name');
49+
}
50+
51+
/**
52+
* The triggering build's id
53+
*/
54+
public static get buildId() {
55+
return events.EventField.fromPath('$.detail.build-id');
56+
}
57+
58+
/**
59+
* The phase that was just completed
60+
*/
61+
public static get completedPhase() {
62+
return events.EventField.fromPath('$.detail.completed-phase');
63+
}
64+
65+
/**
66+
* The status of the completed phase
67+
*/
68+
public static get completedPhaseStatus() {
69+
return events.EventField.fromPath('$.detail.completed-phase-status');
70+
}
71+
72+
/**
73+
* The duration of the completed phase
74+
*/
75+
public static get completedPhaseDurationSeconds() {
76+
return events.EventField.fromPath('$.detail.completed-phase-duration-seconds');
77+
}
78+
79+
/**
80+
* Whether the build is complete
81+
*/
82+
public static get buildComplete() {
83+
return events.EventField.fromPath('$.detail.build-complete');
84+
}
85+
86+
private constructor() {
87+
}
88+
}

Diff for: packages/@aws-cdk/aws-codebuild/lib/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './events';
12
export * from './pipeline-project';
23
export * from './project';
34
export * from './source';

Diff for: packages/@aws-cdk/aws-codebuild/lib/project.ts

+27-12
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,35 @@ export interface IProject extends IResource, iam.IGrantable {
5252
* You can also use the methods `onBuildFailed` and `onBuildSucceeded` to define rules for
5353
* these specific state changes.
5454
*
55+
* To access fields from the event in the event target input,
56+
* use the static fields on the `StateChangeEvent` class.
57+
*
5558
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html
5659
*/
57-
onStateChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps): events.EventRule;
60+
onStateChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule;
5861

5962
/**
6063
* Defines a CloudWatch event rule that triggers upon phase change of this
6164
* build project.
6265
*
6366
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html
6467
*/
65-
onPhaseChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps): events.EventRule;
68+
onPhaseChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule;
6669

6770
/**
6871
* Defines an event rule which triggers when a build starts.
6972
*/
70-
onBuildStarted(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps): events.EventRule;
73+
onBuildStarted(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule;
7174

7275
/**
7376
* Defines an event rule which triggers when a build fails.
7477
*/
75-
onBuildFailed(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps): events.EventRule;
78+
onBuildFailed(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule;
7679

7780
/**
7881
* Defines an event rule which triggers when a build completes successfully.
7982
*/
80-
onBuildSucceeded(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps): events.EventRule;
83+
onBuildSucceeded(name: string, target?: events.IRuleTarget, options?: events.RuleProps): events.Rule;
8184

8285
/**
8386
* @returns a CloudWatch metric associated with this build project.
@@ -174,10 +177,13 @@ abstract class ProjectBase extends Resource implements IProject {
174177
* You can also use the methods `onBuildFailed` and `onBuildSucceeded` to define rules for
175178
* these specific state changes.
176179
*
180+
* To access fields from the event in the event target input,
181+
* use the static fields on the `StateChangeEvent` class.
182+
*
177183
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html
178184
*/
179-
public onStateChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) {
180-
const rule = new events.EventRule(this, name, options);
185+
public onStateChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
186+
const rule = new events.Rule(this, name, options);
181187
rule.addTarget(target);
182188
rule.addEventPattern({
183189
source: ['aws.codebuild'],
@@ -197,8 +203,8 @@ abstract class ProjectBase extends Resource implements IProject {
197203
*
198204
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-build-notifications.html
199205
*/
200-
public onPhaseChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) {
201-
const rule = new events.EventRule(this, name, options);
206+
public onPhaseChange(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
207+
const rule = new events.Rule(this, name, options);
202208
rule.addTarget(target);
203209
rule.addEventPattern({
204210
source: ['aws.codebuild'],
@@ -214,8 +220,11 @@ abstract class ProjectBase extends Resource implements IProject {
214220

215221
/**
216222
* Defines an event rule which triggers when a build starts.
223+
*
224+
* To access fields from the event in the event target input,
225+
* use the static fields on the `StateChangeEvent` class.
217226
*/
218-
public onBuildStarted(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) {
227+
public onBuildStarted(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
219228
const rule = this.onStateChange(name, target, options);
220229
rule.addEventPattern({
221230
detail: {
@@ -227,8 +236,11 @@ abstract class ProjectBase extends Resource implements IProject {
227236

228237
/**
229238
* Defines an event rule which triggers when a build fails.
239+
*
240+
* To access fields from the event in the event target input,
241+
* use the static fields on the `StateChangeEvent` class.
230242
*/
231-
public onBuildFailed(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) {
243+
public onBuildFailed(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
232244
const rule = this.onStateChange(name, target, options);
233245
rule.addEventPattern({
234246
detail: {
@@ -240,8 +252,11 @@ abstract class ProjectBase extends Resource implements IProject {
240252

241253
/**
242254
* Defines an event rule which triggers when a build completes successfully.
255+
*
256+
* To access fields from the event in the event target input,
257+
* use the static fields on the `StateChangeEvent` class.
243258
*/
244-
public onBuildSucceeded(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) {
259+
public onBuildSucceeded(name: string, target?: events.IRuleTarget, options?: events.RuleProps) {
245260
const rule = this.onStateChange(name, target, options);
246261
rule.addEventPattern({
247262
detail: {

0 commit comments

Comments
 (0)