Skip to content

Commit

Permalink
feat(events): new filtering capabilities (#23075)
Browse files Browse the repository at this point in the history
Add suffix and equals ignore case matching.

See https://aws.amazon.com/about-aws/whats-new/2022/11/amazon-eventbridge-enhanced-filtering-capabilities/ See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html


----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jogold committed Nov 24, 2022
1 parent 52edf29 commit 680a755
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 39 deletions.
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-events/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ const rule = new events.Rule(this, 'rule', {

// 'OR' condition
'source-storage-class': events.Match.anyOf(
events.Match.prefix("GLACIER"),
events.Match.prefix("GLACIER"),
events.Match.exactString('DEEP_ARCHIVE'),
),
},
'detail-type': events.Match.equalsIgnoreCase('object created'),

// If you prefer, you can use a low level array of strings, as directly consumed by EventBridge
source: ['aws.s3'],
Expand Down
28 changes: 21 additions & 7 deletions packages/@aws-cdk/aws-events/lib/event-pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,35 @@ export class Match implements IResolvable {
/**
* Matches a string, exactly, in the JSON of the event
*/
public static exactString(value: string): string [] {
public static exactString(value: string): string[] {
return this.fromObjects([value]);
}

/**
* Matches a string, regardless of case, in the JSON of the event
*/
public static equalsIgnoreCase(value: string): string[] {
return this.fromObjects([{ 'equals-ignore-case': value }]);
}

/**
* Matches strings with the given prefix in the JSON of the event
*/
static prefix(value: string): string[] {
public static prefix(value: string): string[] {
return this.fromObjects([{ prefix: value }]);
}

/**
* Matches strings with the given suffix in the JSON of the event
*/
public static suffix(value: string): string[] {
return this.fromObjects([{ suffix: value }]);
}

/**
* Matches IPv4 and IPv6 network addresses using the Classless Inter-Domain Routing (CIDR) format
*/
static cidr(range: string): string[] {
public static cidr(range: string): string[] {
const ipv4Regex = /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/igm;
const ipv6Regex = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/igm;

Expand All @@ -69,15 +83,15 @@ export class Match implements IResolvable {
* Matches IPv4 and IPv6 network addresses using the Classless Inter-Domain Routing (CIDR) format.
* Alias of `cidr()`.
*/
static ipAddressRange(range: string): string[] {
public static ipAddressRange(range: string): string[] {
return Match.cidr(range);
}

/**
* Matches anything except what's provided in the rule. The list of provided values must contain
* only strings or only numbers.
*/
static anythingBut(...values: any[]): string[] {
public static anythingBut(...values: any[]): string[] {
if (values.length === 0) {
throw new Error('anythingBut matchers must be non-empty lists');
}
Expand All @@ -95,7 +109,7 @@ export class Match implements IResolvable {
/**
* Matches any string that doesn't start with the given prefix.
*/
static anythingButPrefix(prefix: string): string[] {
public static anythingButPrefix(prefix: string): string[] {
return this.fromObjects([{ 'anything-but': { prefix: prefix } }]);
}

Expand Down Expand Up @@ -142,7 +156,7 @@ export class Match implements IResolvable {
* @param lower Lower bound (inclusive)
* @param upper Upper bound (inclusive)
*/
static interval(lower: number, upper: number): string[] {
public static interval(lower: number, upper: number): string[] {
if (lower > upper) {
throw new Error(`Invalid interval: [${lower}, ${upper}]`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "20.0.0",
"version": "21.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "20.0.0",
"version": "21.0.0",
"files": {
"f8f3154528da0601b814868afeeb0474f8644916293f783617ff6d56a6d92035": {
"f9728b2d2c64ff638563c8cc4a3e8eb9ddf3074e2ae01d68fde11aeda0968fb1": {
"source": {
"path": "RuleStack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "f8f3154528da0601b814868afeeb0474f8644916293f783617ff6d56a6d92035.json",
"objectKey": "f9728b2d2c64ff638563c8cc4a3e8eb9ddf3074e2ae01d68fde11aeda0968fb1.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@
"prefix": "sensitive-"
}
}
],
"suffix": [
{
"suffix": ".com"
}
],
"equalsIgnoreCase": [
{
"equals-ignore-case": "ignore case"
}
]
},
"detail-type": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"20.0.0"}
{"version":"21.0.0"}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"version": "20.0.0",
"version": "21.0.0",
"testCases": {
"IntegTest-BatchDefaultEnvVarsStack/DefaultTest": {
"stacks": [
"RuleStack"
],
"assertionStack": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert"
"assertionStack": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert",
"assertionStackName": "IntegTestBatchDefaultEnvVarsStackDefaultTestDeployAssertC15EFFF2"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
{
"version": "20.0.0",
"version": "21.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"RuleStack.assets": {
"type": "cdk:asset-manifest",
"properties": {
Expand All @@ -23,7 +17,7 @@
"validateOnSynth": false,
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f8f3154528da0601b814868afeeb0474f8644916293f783617ff6d56a6d92035.json",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f9728b2d2c64ff638563c8cc4a3e8eb9ddf3074e2ae01d68fde11aeda0968fb1.json",
"requiresBootstrapStackVersion": 6,
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
"additionalDependencies": [
Expand Down Expand Up @@ -106,6 +100,12 @@
]
},
"displayName": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert"
},
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
}
}
}
74 changes: 59 additions & 15 deletions packages/@aws-cdk/aws-events/test/integ.rule.js.snapshot/tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@
"id": "App",
"path": "",
"children": {
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
}
},
"RuleStack": {
"id": "RuleStack",
"path": "RuleStack",
Expand Down Expand Up @@ -131,6 +123,16 @@
"prefix": "sensitive-"
}
}
],
"suffix": [
{
"suffix": ".com"
}
],
"equalsIgnoreCase": [
{
"equals-ignore-case": "ignore case"
}
]
},
"detail-type": [
Expand Down Expand Up @@ -172,11 +174,27 @@
"fqn": "@aws-cdk/aws-events.Rule",
"version": "0.0.0"
}
},
"BootstrapVersion": {
"id": "BootstrapVersion",
"path": "RuleStack/BootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnParameter",
"version": "0.0.0"
}
},
"CheckBootstrapVersion": {
"id": "CheckBootstrapVersion",
"path": "RuleStack/CheckBootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnRule",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
},
"IntegTest-BatchDefaultEnvVarsStack": {
Expand All @@ -192,15 +210,33 @@
"path": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/Default",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"version": "10.1.154"
}
},
"DeployAssert": {
"id": "DeployAssert",
"path": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert",
"children": {
"BootstrapVersion": {
"id": "BootstrapVersion",
"path": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert/BootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnParameter",
"version": "0.0.0"
}
},
"CheckBootstrapVersion": {
"id": "CheckBootstrapVersion",
"path": "IntegTest-BatchDefaultEnvVarsStack/DefaultTest/DeployAssert/CheckBootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnRule",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
}
},
Expand All @@ -214,11 +250,19 @@
"fqn": "@aws-cdk/integ-tests.IntegTest",
"version": "0.0.0"
}
},
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.154"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.App",
"version": "0.0.0"
}
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-events/test/integ.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ new Rule(stack, 'MyRule', {
state: Match.anythingBut('initializing'),
limit: Match.anythingBut(100, 200, 300),
notPrefixedBy: Match.anythingButPrefix('sensitive-'),
suffix: Match.suffix('.com'),
equalsIgnoreCase: Match.equalsIgnoreCase('ignore case'),
},
detailType: ['detailType1'],
id: ['id1', 'id2'],
Expand Down
20 changes: 19 additions & 1 deletion packages/@aws-cdk/aws-events/test/matchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,22 @@ describe(Match, () => {

expect(() => stack.resolve(Match.anyOf())).toThrow(/A list of matchers must contain at least one element/);
});
});

test('prefix', () => {
expect(stack.resolve(Match.prefix('foo'))).toEqual([
{ prefix: 'foo' },
]);
});

test('suffix', () => {
expect(stack.resolve(Match.suffix('foo'))).toEqual([
{ suffix: 'foo' },
]);
});

test('equalsIgnoreCase', () => {
expect(stack.resolve(Match.equalsIgnoreCase('foo'))).toEqual([
{ 'equals-ignore-case': 'foo' },
]);
});
});

0 comments on commit 680a755

Please sign in to comment.