From 1440709e4c5a4fa33953f63921e358eb5189057c Mon Sep 17 00:00:00 2001 From: Tom Noble <53005340+TSNoble@users.noreply.github.com> Date: Thu, 1 Apr 2021 10:31:10 +0100 Subject: [PATCH] Enhancement/3821 (#3822) * Add _does_event_match_pattern() to EventsBackend and use when determining whether to archive an event * Add comment to _does_event_item_match_pattern_item() * Expand test case for Archive EventFilter * Apply black formatting Co-authored-by: Tom Noble --- moto/events/models.py | 25 +++++++++++++-- tests/test_events/test_events.py | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/moto/events/models.py b/moto/events/models.py index 6818aa3ec..eb8d41422 100644 --- a/moto/events/models.py +++ b/moto/events/models.py @@ -92,6 +92,24 @@ class Rule(CloudFormationModel): if index is not None: self.targets.pop(index) + def _does_event_match_pattern(self, event, pattern): + if not pattern: + return True + event_pattern_pairs = [(event.get(k), v) for k, v in pattern.items()] + for event_item, pattern_item in event_pattern_pairs: + if not self._does_event_item_match_pattern_item(event_item, pattern_item): + return False + return True + + def _does_event_item_match_pattern_item(self, event_item, pattern_item): + # Only supports "key: [value]" filters currently + if not event_item: + return False + if isinstance(pattern_item, list): + return event_item in pattern_item + if isinstance(pattern_item, dict): + return self._does_event_match_pattern(event_item, pattern_item) + def send_to_targets(self, event_bus_name, event): event_bus_name = event_bus_name.split("/")[-1] if event_bus_name != self.event_bus_name: @@ -186,9 +204,12 @@ class Rule(CloudFormationModel): def _send_to_events_archive(self, resource_id, event): archive_name, archive_uuid = resource_id.split(":") archive = events_backends[self.region_name].archives.get(archive_name) - + pattern = archive.event_pattern if archive.uuid == archive_uuid: - archive.events.append(event) + event = json.loads(json.dumps(event)) + pattern = json.loads(pattern) if pattern else None + if self._does_event_match_pattern(event, pattern): + archive.events.append(event) def _send_to_sqs_queue(self, resource_id, event): from moto.sqs import sqs_backends diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py index 073ba56cd..ee92e8707 100644 --- a/tests/test_events/test_events.py +++ b/tests/test_events/test_events.py @@ -1488,6 +1488,59 @@ def test_archive_event_with_bus_arn(): response["SizeBytes"].should.be.greater_than(0) +@mock_events +def test_event_not_routed_to_archive_when_detail_does_not_match_pattern(): + # given + client = boto3.client("events", "eu-central-1") + event_bus_arn = "arn:aws:events:eu-central-1:{}:event-bus/default".format( + ACCOUNT_ID + ) + client.create_archive( + ArchiveName="archive-with-dict-filter", + EventSourceArn=event_bus_arn, + EventPattern=json.dumps({"detail": {"foo": ["bar"]}}), + ) + client.create_archive( + ArchiveName="archive-with-list-filter", + EventSourceArn=event_bus_arn, + EventPattern=json.dumps({"source": ["foo", "bar"]}), + ) + + # when + event_matching_detail = { + "Source": "source", + "DetailType": "type", + "Detail": '{"foo": "bar"}', + } + event_not_matching_detail = { + "Source": "source", + "DetailType": "type", + "Detail": '{"foo": "baz"}', + } + event_matching_source_foo = {"Source": "foo", "DetailType": "type", "Detail": "{}"} + event_matching_source_bar = {"Source": "bar", "DetailType": "type", "Detail": "{}"} + event_not_matching_source = {"Source": "baz", "DetailType": "type", "Detail": "{}"} + + client.put_events( + Entries=[ + event_matching_detail, + event_not_matching_detail, + event_matching_source_foo, + event_matching_source_bar, + event_not_matching_source, + ] + ) + + # then + response = client.describe_archive(ArchiveName="archive-with-dict-filter") + response["EventCount"].should.equal(1) + response["SizeBytes"].should.be.greater_than(0) + + response = client.describe_archive(ArchiveName="archive-with-list-filter") + response["EventCount"].should.equal(2) + response["SizeBytes"].should.be.greater_than(0) + + @mock_events def test_start_replay(): # given