From 5c4a8fde31ac0a701a4e32d8793c2d8fe6146048 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Thu, 25 Apr 2024 19:05:53 +0200 Subject: [PATCH 01/28] feat: add invalid event pattern exeption --- localstack/services/events/models_v2.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index 89b232fe30291..833f6817fb82c 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -94,3 +94,9 @@ class ValidationException(ServiceException): code: str = "ValidationException" sender_fault: bool = True status_code: int = 400 + + +class InvalidEventPatternException(ServiceException): + code: str = "InvalidEventPatternException" + sender_fault: bool = True + status_code: int = 400 From 71dc9b3f259375ee0c8beba81d2702179c4e91a5 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:01:55 +0200 Subject: [PATCH 02/28] feat: add pattern loader to pattern dict --- localstack/services/events/rule.py | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/localstack/services/events/rule.py b/localstack/services/events/rule.py index aad9d985b09a5..b096dee8dc15f 100644 --- a/localstack/services/events/rule.py +++ b/localstack/services/events/rule.py @@ -1,3 +1,4 @@ +import json import re from typing import Optional @@ -19,7 +20,13 @@ TargetIdList, TargetList, ) -from localstack.services.events.models_v2 import Rule, TargetDict, ValidationException +from localstack.services.events.models_v2 import ( + EventPatternDict, + InvalidEventPatternException, + Rule, + TargetDict, + ValidationException, +) TARGET_ID_REGEX = re.compile(r"^[\.\-_A-Za-z0-9]+$") TARGET_ARN_REGEX = re.compile(r"arn:[\d\w:\-/]*") @@ -59,6 +66,7 @@ def __init__( targets, managed_by, ) + self.event_pattern = self.load_event_pattern(event_pattern) @property def arn(self) -> Arn: @@ -155,6 +163,19 @@ def validate_targets_input(self, targets: TargetList) -> PutTargetsResultEntryLi return validation_errors + def load_event_pattern(self, raw_pattern: Optional[str]) -> EventPatternDict: + """Loads and validates an event pattern from a JSON string.""" + if raw_pattern is None: + return {} + + try: + pattern = json.loads(raw_pattern) + except json.JSONDecodeError: + raise InvalidEventPatternException(reason="Invalid JSON") + + self._validate_event_pattern(pattern) + return pattern + def _validate_input( self, event_pattern: Optional[EventPattern], @@ -182,5 +203,20 @@ def _check_target_limit_reached(self) -> bool: return True return False + def _validate_event_pattern(self, pattern): + """Validates that the event pattern is correctly structured.""" + for attr, value in pattern.items(): + if isinstance(value, dict): + self._validate_event_pattern(value) + elif isinstance(value, list): + if not value: + raise InvalidEventPatternException("Empty arrays are not allowed") + if not all(isinstance(item, (dict, str)) for item in value): + raise InvalidEventPatternException( + f"All items in '{attr}' array must be dictionaries or strings" + ) + else: + raise InvalidEventPatternException(f"'{attr}' must be an object or an array") + RuleServiceDict = dict[Arn, RuleService] From 99841deda8af1754c8c70634f425fe01bc63cbfc Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:02:07 +0200 Subject: [PATCH 03/28] feat: add validate event pattern --- localstack/services/events/provider_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index c82497e2d5323..ed1718301dee9 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -345,7 +345,7 @@ def put_targets( rule_service = self.get_rule_service(context, rule, event_bus_name) failed_entries = rule_service.add_targets(targets) rule_arn = rule_service.arn - for target in targets: + for target in targets: # TODO only add successful targets self.create_target_sender(target, region, account_id, rule_arn) response = PutTargetsResponse( From 10c7b4a64943ab3e800878d306122eeae94421c1 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:07:16 +0200 Subject: [PATCH 04/28] feat: add filter event based on rule pattern --- localstack/services/events/models_v2.py | 4 +- localstack/services/events/provider_v2.py | 90 ++++++++++++++++++----- localstack/services/events/target.py | 3 +- 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index 833f6817fb82c..b58a60a1863fd 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Optional +from typing import Any, Optional from localstack.aws.api.core import ServiceException from localstack.aws.api.events import ( @@ -59,6 +59,8 @@ def __post_init__(self): RuleDict = dict[RuleName, Rule] +EventPatternDict = dict[str, Any] + @dataclass class EventBus: diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index ed1718301dee9..4f8e00f6f9463 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -1,5 +1,7 @@ import base64 +import json import logging +from datetime import datetime, timezone from typing import Optional from localstack.aws.api import RequestContext, handler @@ -22,6 +24,7 @@ ListRulesResponse, ListTargetsByRuleResponse, NextToken, + PutEventsRequestEntry, PutEventsRequestEntryList, PutEventsResponse, PutPartnerEventsRequestEntryList, @@ -59,7 +62,9 @@ ) from localstack.services.events.rule import RuleService, RuleServiceDict from localstack.services.events.target import TargetSender, TargetSenderDict, TargetSenderFactory +from localstack.services.events.utils import matches_event from localstack.services.plugins import ServiceLifecycleHook +from localstack.utils.strings import long_uid LOG = logging.getLogger(__name__) @@ -79,6 +84,39 @@ def get_filtered_dict(name_prefix: str, input_dict: dict) -> dict: return {name: value for name, value in input_dict.items() if name.startswith(name_prefix)} +def get_event_time(event: PutEventsRequestEntry) -> str: + event_time = datetime.now(timezone.utc) + if event_timestamp := event.get("Time"): + try: + # use time from event if provided + event_time = event_timestamp.replace(tzinfo=timezone.utc) + except ValueError: + # use current time if event time is invalid + LOG.debug( + "Could not parse the `Time` parameter, falling back to current time for the following Event: '%s'", + event, + ) + formatted_time_string = event_time.strftime("%Y-%m-%dT%H:%M:%SZ") + return formatted_time_string + + +def format_event(event: PutEventsRequestEntry, region: str, account_id: str) -> dict: + # See https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html + formatted_event = { + "version": "0", + "id": str(long_uid()), + "detail-type": event.get("DetailType"), + "source": event.get("Source"), + "account": account_id, + "time": get_event_time(event), + "region": region, + "resources": event.get("Resources", []), + "detail": json.loads(event.get("Detail", "{}")), + } + + return formatted_event + + class EventsProvider(EventsApi, ServiceLifecycleHook): # api methods are grouped by resource type and sorted in hierarchical order # each group is sorted alphabetically @@ -384,9 +422,14 @@ def put_events( endpoint_id: EndpointId = None, **kwargs, ) -> PutEventsResponse: - failed_entries = self._put_entries(context, entries) + entries_uuids, failed_entries = self._put_entries(context, entries) - response = PutEventsResponse(FailedEntryCount=len(failed_entries), Entries=failed_entries) + response = PutEventsResponse( + Entries=[{"EventId": id} for id in entries_uuids], FailedEntryCount=0 + ) + if failed_entries: + response["FailedEntryCount"] = len(failed_entries) + response["FailedEntries"] = failed_entries return response @handler("PutPartnerEvents") @@ -578,25 +621,36 @@ def _delete_target_sender(self, ids: TargetIdList, rule) -> None: except KeyError: LOG.error(f"Error deleting target service {target_arn}.") - def _put_entries(self, context: RequestContext, entries: PutEventsRequestEntryList) -> list: + def _put_entries( + self, context: RequestContext, entries: PutEventsRequestEntryList + ) -> tuple[list, list]: + entries_uuids = [] failed_entries = [] for event in entries: event_bus_name = event.get("EventBusName", "default") + event = format_event(event, context.region, context.account_id) + entries_uuids.append(event["id"]) store = self.get_store(context) - event_bus = self.get_event_bus(event_bus_name, store) - # TODO add pattern matching + try: + event_bus = self.get_event_bus(event_bus_name, store) + except ResourceNotFoundException: + # ignore events for non-existing event buses + continue matching_rules = [rule for rule in event_bus.rules.values()] for rule in matching_rules: - for target in rule.targets.values(): - target_sender = self._target_sender_store[target["Arn"]] - try: - target_sender.send_event(event) - except Exception as error: - failed_entries.append( - { - "Entry": event, - "ErrorCode": "InternalException", - "ErrorMessage": str(error), - } - ) - return failed_entries + rule_service = self.get_rule_service(context, rule.name, event_bus_name) + event_pattern = rule_service.event_pattern + if matches_event(event_pattern, event): + for target in rule.targets.values(): + target_sender = self._target_sender_store[target["Arn"]] + try: + target_sender.send_event(event) + except Exception as error: + failed_entries.append( + { + "Entry": event, + "ErrorCode": "InternalException", + "ErrorMessage": str(error), + } + ) + return entries_uuids, failed_entries diff --git a/localstack/services/events/target.py b/localstack/services/events/target.py index bb2d423bacaf8..385311d7c783c 100644 --- a/localstack/services/events/target.py +++ b/localstack/services/events/target.py @@ -7,6 +7,7 @@ from localstack.aws.api.events import ( Arn, + PutEventsRequestEntry, Target, ) from localstack.aws.connect import connect_to @@ -53,7 +54,7 @@ def client(self): return self._client @abstractmethod - def send_event(self): + def send_event(self, event: PutEventsRequestEntry): pass def _validate_input(self, target: Target): From 8dc03dbd5f9ab073e567144331378b683055e87e Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:25:25 +0200 Subject: [PATCH 05/28] feat: remove skip marker from passing tests targets --- localstack/services/events/target.py | 1 + tests/aws/services/events/test_events_integrations.py | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/localstack/services/events/target.py b/localstack/services/events/target.py index 385311d7c783c..3a2500ecc5b57 100644 --- a/localstack/services/events/target.py +++ b/localstack/services/events/target.py @@ -175,6 +175,7 @@ def send_event(self, event): def _validate_input(self, target: Target): super()._validate_input(target) + # TODO add validated test to check if RoleArn is mandatory if not collections.get_safe(target, "$.RoleArn"): raise ValueError("RoleArn is required for Kinesis target") if not collections.get_safe(target, "$.KinesisParameters.PartitionKeyPath"): diff --git a/tests/aws/services/events/test_events_integrations.py b/tests/aws/services/events/test_events_integrations.py index 2124fdca6204c..984972dbd8f1d 100644 --- a/tests/aws/services/events/test_events_integrations.py +++ b/tests/aws/services/events/test_events_integrations.py @@ -35,7 +35,6 @@ def test_put_events_with_target_sqs(put_events_with_filter_to_sqs): @markers.aws.unknown -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_sqs_new_region(aws_client_factory): events_client = aws_client_factory(region_name="eu-west-1").events queue_name = "queue-{}".format(short_uid()) @@ -77,7 +76,6 @@ def test_put_events_with_target_sqs_new_region(aws_client_factory): @markers.aws.unknown -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_sqs_event_detail_match(put_events_with_filter_to_sqs): entries1 = [ { @@ -106,7 +104,6 @@ def test_put_events_with_target_sqs_event_detail_match(put_events_with_filter_to @markers.aws.unknown @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_sns( monkeypatch, sns_subscription, @@ -175,7 +172,6 @@ def test_put_events_with_target_sns( @markers.aws.unknown -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_lambda(create_lambda_function, cleanups, aws_client, clean_up): rule_name = f"rule-{short_uid()}" function_name = f"lambda-func-{short_uid()}" @@ -237,7 +233,6 @@ def test_put_events_with_target_lambda(create_lambda_function, cleanups, aws_cli @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_lambda_list_entry( create_lambda_function, cleanups, aws_client, clean_up, snapshot ): @@ -334,7 +329,6 @@ def test_put_events_with_target_lambda_list_entry( @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_lambda_list_entries_partial_match( create_lambda_function, cleanups, aws_client, clean_up, snapshot ): @@ -483,7 +477,6 @@ def check_invocation(): @markers.aws.unknown -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_firehose(aws_client, clean_up): s3_bucket = "s3-{}".format(short_uid()) s3_prefix = "testeventdata" From 6cc917100b519e439bd872d5bdfc8292712070ef Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:25:41 +0200 Subject: [PATCH 06/28] feat: add comment about alphabetical order for target workers --- localstack/services/events/target.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/localstack/services/events/target.py b/localstack/services/events/target.py index 3a2500ecc5b57..6a22f4e08d059 100644 --- a/localstack/services/events/target.py +++ b/localstack/services/events/target.py @@ -84,6 +84,8 @@ def _initialize_client(self) -> BaseClient: TargetSenderDict = dict[Arn, TargetSender] +# Target Senders are ordered alphabetically by service name + class ApiGatewayTargetSender(TargetSender): def send_event(self, event): From 5360ecfd0372b83eb2e9f19da7cf52767d20a4cc Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:41:45 +0200 Subject: [PATCH 07/28] feat: remove skip marker from passing tests events --- tests/aws/services/events/test_events.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 0dc10211621a9..79adf824db19a 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -139,7 +139,6 @@ def test_list_tags_for_resource(self, aws_client, clean_up): clean_up(rule_name=rule_name) @markers.aws.unknown - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs): pattern = {"detail": {"event": {"data": {"type": ["1", "2"]}}}} entries1 = [ @@ -171,7 +170,6 @@ def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs): ) @markers.aws.validated - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_nested_event_pattern(self, put_events_with_filter_to_sqs): pattern = {"detail": {"event": {"data": {"type": ["1"]}}}} entries1 = [ @@ -527,7 +525,6 @@ def test_create_connection_validations(self, aws_client): assert "must satisfy enum value set: [BASIC, OAUTH_CLIENT_CREDENTIALS, API_KEY]" in message @markers.aws.unknown - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_event_without_source(self, aws_client_factory): events_client = aws_client_factory(region_name="eu-west-1").events @@ -535,7 +532,6 @@ def test_put_event_without_source(self, aws_client_factory): assert response.get("Entries") @markers.aws.unknown - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_event_without_detail(self, aws_client_factory): events_client = aws_client_factory(region_name="eu-west-1").events @@ -591,7 +587,7 @@ def test_put_target_id_validation( ], ) - @markers.aws.validated + @markers.aws.validated # TODO move to tests_event_patterns @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_event_pattern(self, aws_client, snapshot, account_id, region_name): response = aws_client.events.test_event_pattern( @@ -636,7 +632,6 @@ def test_event_pattern(self, aws_client, snapshot, account_id, region_name): snapshot.match("eventbridge-test-event-pattern-response-no-match", response) @markers.aws.validated - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_time( self, aws_client, @@ -817,7 +812,6 @@ def test_list_event_buses_with_limit(self, create_event_bus, aws_client, snapsho @markers.aws.unknown @pytest.mark.skipif(is_aws_cloud(), reason="not validated") @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_into_event_bus( self, monkeypatch, @@ -885,7 +879,6 @@ def test_put_events_into_event_bus( aws_client.sqs.delete_queue(QueueUrl=queue_url) @markers.aws.validated - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") # TODO simplify and use sqs as target def test_put_events_to_default_eventbus_for_custom_eventbus( self, @@ -1019,7 +1012,6 @@ def test_put_events_to_default_eventbus_for_custom_eventbus( assert_valid_event(received_event) @markers.aws.validated # TODO fix condition for this test, only succeeds if run on its own - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_nonexistent_event_bus( self, aws_client, From f6ebd00b2694a72edaad1effdd7b54a4e7f65e95 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:50:17 +0200 Subject: [PATCH 08/28] feat: remove skip marker from passing tets rules --- tests/aws/services/events/test_events_rules.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/aws/services/events/test_events_rules.py b/tests/aws/services/events/test_events_rules.py index 63758ca8db630..648c395652871 100644 --- a/tests/aws/services/events/test_events_rules.py +++ b/tests/aws/services/events/test_events_rules.py @@ -18,7 +18,6 @@ @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_rule(aws_client, snapshot, clean_up): rule_name = f"rule-{short_uid()}" snapshot.add_transformer(snapshot.transform.regex(rule_name, "")) @@ -38,7 +37,6 @@ def test_put_rule(aws_client, snapshot, clean_up): @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_rule_disable(aws_client, clean_up): rule_name = f"rule-{short_uid()}" aws_client.events.put_rule(Name=rule_name, ScheduleExpression="rate(1 minute)") @@ -54,6 +52,8 @@ def test_rule_disable(aws_client, clean_up): @markers.aws.validated +# TODO move to test_events_schedules.py +@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") @pytest.mark.parametrize( "expression", [ @@ -76,7 +76,6 @@ def test_rule_disable(aws_client, clean_up): " rate(10 minutes)", ], ) -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_rule_invalid_rate_schedule_expression(expression, aws_client): with pytest.raises(ClientError) as e: aws_client.events.put_rule(Name=f"rule-{short_uid()}", ScheduleExpression=expression) @@ -88,7 +87,6 @@ def test_put_rule_invalid_rate_schedule_expression(expression, aws_client): @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_rule_anything_but_to_sqs(put_events_with_filter_to_sqs, snapshot): snapshot.add_transformer( [ @@ -142,7 +140,6 @@ def test_put_events_with_rule_anything_but_to_sqs(put_events_with_filter_to_sqs, @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_rule_exists_true_to_sqs(put_events_with_filter_to_sqs, snapshot): """ Exists matching True condition: https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-exists-matching @@ -190,7 +187,6 @@ def test_put_events_with_rule_exists_true_to_sqs(put_events_with_filter_to_sqs, @markers.aws.validated -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_rule_exists_false_to_sqs(put_events_with_filter_to_sqs, snapshot): """ Exists matching False condition: https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-exists-matching @@ -339,8 +335,9 @@ def test_put_event_with_content_base_rule_in_pattern(aws_client, clean_up): @markers.aws.validated -@pytest.mark.parametrize("schedule_expression", ["rate(1 minute)", "rate(1 day)", "rate(1 hour)"]) +# TODO move to test_events_schedules.py @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") +@pytest.mark.parametrize("schedule_expression", ["rate(1 minute)", "rate(1 day)", "rate(1 hour)"]) def test_create_rule_with_one_unit_in_singular_should_succeed( schedule_expression, aws_client, clean_up ): From b663463e17e39d1e388d7b41c3363729af691188 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Tue, 16 Apr 2024 09:43:41 +0200 Subject: [PATCH 09/28] feat: add api test event pattern --- localstack/services/events/provider_v2.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index 4f8e00f6f9463..506b4d51d8fda 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -46,6 +46,7 @@ TargetId, TargetIdList, TargetList, + TestEventPatternResponse, ) from localstack.aws.api.events import EventBus as ApiTypeEventBus from localstack.aws.api.events import Rule as ApiTypeRule @@ -341,6 +342,19 @@ def put_rule( response = PutRuleResponse(RuleArn=rule_service.arn) return response + @handler("TestEventPattern") + def test_event_pattern( + self, context: RequestContext, event_pattern: EventPattern, event: str, **kwargs + ) -> TestEventPatternResponse: + """Test event pattern uses EventBridge event pattern matching: + https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html + """ + event_pattern_dict = json.loads(event_pattern) + event_dict = json.loads(event) + result = matches_event(event_pattern_dict, event_dict) + + return TestEventPatternResponse(Result=result) + ######### # Targets ######### From bf850f261cdbb293aa51c9955ffc856173ca91cc Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 15 Apr 2024 21:55:44 +0200 Subject: [PATCH 10/28] feat: remove skip marker from passing tets pattern --- tests/aws/services/events/test_event_patterns.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/aws/services/events/test_event_patterns.py b/tests/aws/services/events/test_event_patterns.py index 7c31c3731bee2..fee9902930c35 100644 --- a/tests/aws/services/events/test_event_patterns.py +++ b/tests/aws/services/events/test_event_patterns.py @@ -8,7 +8,6 @@ from localstack.testing.aws.util import is_aws_cloud from localstack.testing.pytest import markers -from tests.aws.services.events.helper_functions import is_v2_provider THIS_FOLDER: str = os.path.dirname(os.path.realpath(__file__)) REQUEST_TEMPLATE_DIR = os.path.join(THIS_FOLDER, "event_pattern_templates") @@ -80,7 +79,6 @@ def list_files_with_suffix(directory_path: str, suffix: str) -> List[str]: # TODO: extend these test cases based on the open source docs + tests: https://github.com/aws/event-ruler # For example, "JSON Array Matching", "And and Or Relationship among fields with Ruler", rule validation, # and exception handling. -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") @pytest.mark.parametrize( "request_template,label", request_template_tuples, ids=[t[1] for t in request_template_tuples] ) @@ -118,7 +116,6 @@ def test_test_event_pattern(aws_client, snapshot, request_template, label): assert response["Result"] -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") @markers.aws.validated def test_test_event_pattern_with_multi_key(aws_client): """Test the special case of a duplicate JSON key separately because it requires working around the @@ -140,7 +137,6 @@ def test_test_event_pattern_with_multi_key(aws_client): assert response["Result"] -@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") @markers.aws.validated def test_test_event_pattern_with_escape_characters(aws_client): r"""Test the special case of using escape characters separately because it requires working around JSON escaping. From b42c2e4eedd4e617700798cb5628d1dbd6b46c7a Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Fri, 26 Apr 2024 10:49:13 +0200 Subject: [PATCH 11/28] feat: test add snapshot to sqs target --- .../events/test_events_integrations.py | 11 +++++-- .../test_events_integrations.snapshot.json | 29 +++++++++++++++++++ .../test_events_integrations.validation.json | 2 +- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/tests/aws/services/events/test_events_integrations.py b/tests/aws/services/events/test_events_integrations.py index 984972dbd8f1d..372b9f7e0a555 100644 --- a/tests/aws/services/events/test_events_integrations.py +++ b/tests/aws/services/events/test_events_integrations.py @@ -20,7 +20,7 @@ @markers.aws.validated -def test_put_events_with_target_sqs(put_events_with_filter_to_sqs): +def test_put_events_with_target_sqs(put_events_with_filter_to_sqs, snapshot): entries = [ { "Source": TEST_EVENT_PATTERN["source"][0], @@ -28,10 +28,17 @@ def test_put_events_with_target_sqs(put_events_with_filter_to_sqs): "Detail": json.dumps(EVENT_DETAIL), } ] - put_events_with_filter_to_sqs( + message = put_events_with_filter_to_sqs( pattern=TEST_EVENT_PATTERN, entries_asserts=[(entries, True)], ) + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("ReceiptHandle", reference_replacement=False), + snapshot.transform.key_value("MD5OfBody", reference_replacement=False), + ], + ) + snapshot.match("message", message) @markers.aws.unknown diff --git a/tests/aws/services/events/test_events_integrations.snapshot.json b/tests/aws/services/events/test_events_integrations.snapshot.json index a52a6f84d34b3..9a4c20d2f2db9 100644 --- a/tests/aws/services/events/test_events_integrations.snapshot.json +++ b/tests/aws/services/events/test_events_integrations.snapshot.json @@ -100,5 +100,34 @@ } ] } + }, + "tests/aws/services/events/test_events_integrations.py::test_put_events_with_target_sqs": { + "recorded-date": "26-04-2024, 08:43:27", + "recorded-content": { + "message": [ + { + "MessageId": "", + "ReceiptHandle": "receipt-handle", + "MD5OfBody": "m-d5-of-body", + "Body": { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events_integrations.validation.json b/tests/aws/services/events/test_events_integrations.validation.json index 6d701cbe30a64..7de094e66181b 100644 --- a/tests/aws/services/events/test_events_integrations.validation.json +++ b/tests/aws/services/events/test_events_integrations.validation.json @@ -6,7 +6,7 @@ "last_validated_date": "2024-04-08T17:33:44+00:00" }, "tests/aws/services/events/test_events_integrations.py::test_put_events_with_target_sqs": { - "last_validated_date": "2024-03-26T15:49:59+00:00" + "last_validated_date": "2024-04-26T08:43:27+00:00" }, "tests/aws/services/events/test_events_integrations.py::test_put_events_with_target_sqs_event_detail_match": { "last_validated_date": "2024-03-26T15:50:07+00:00" From a93c0f9e60c0f7ddf9157f015335f287bf9389d9 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 10:21:23 +0200 Subject: [PATCH 12/28] refactor: rename process entry function --- localstack/services/events/provider_v2.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index 506b4d51d8fda..f937e7e5c886e 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -436,10 +436,10 @@ def put_events( endpoint_id: EndpointId = None, **kwargs, ) -> PutEventsResponse: - entries_uuids, failed_entries = self._put_entries(context, entries) + processed_entries_uuids, failed_entries = self._process_entries(context, entries) response = PutEventsResponse( - Entries=[{"EventId": id} for id in entries_uuids], FailedEntryCount=0 + Entries=[{"EventId": id} for id in processed_entries_uuids], FailedEntryCount=0 ) if failed_entries: response["FailedEntryCount"] = len(failed_entries) @@ -635,15 +635,15 @@ def _delete_target_sender(self, ids: TargetIdList, rule) -> None: except KeyError: LOG.error(f"Error deleting target service {target_arn}.") - def _put_entries( + def _process_entries( self, context: RequestContext, entries: PutEventsRequestEntryList ) -> tuple[list, list]: - entries_uuids = [] + processed_entries_uuids = [] failed_entries = [] for event in entries: event_bus_name = event.get("EventBusName", "default") event = format_event(event, context.region, context.account_id) - entries_uuids.append(event["id"]) + processed_entries_uuids.append(event["id"]) store = self.get_store(context) try: event_bus = self.get_event_bus(event_bus_name, store) @@ -667,4 +667,4 @@ def _put_entries( "ErrorMessage": str(error), } ) - return entries_uuids, failed_entries + return processed_entries_uuids, failed_entries From 4066cfd3b1c7f33b0b35bc5a601ad54e29ac9fec Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 10:26:13 +0200 Subject: [PATCH 13/28] fix: use api stub for pattern exception --- localstack/services/events/models_v2.py | 6 ------ localstack/services/events/rule.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index b58a60a1863fd..b02a370b18ac1 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -96,9 +96,3 @@ class ValidationException(ServiceException): code: str = "ValidationException" sender_fault: bool = True status_code: int = 400 - - -class InvalidEventPatternException(ServiceException): - code: str = "InvalidEventPatternException" - sender_fault: bool = True - status_code: int = 400 diff --git a/localstack/services/events/rule.py b/localstack/services/events/rule.py index b096dee8dc15f..ca7a3499fdecb 100644 --- a/localstack/services/events/rule.py +++ b/localstack/services/events/rule.py @@ -6,6 +6,7 @@ Arn, EventBusName, EventPattern, + InvalidEventPatternException, LimitExceededException, ManagedBy, PutTargetsResultEntryList, @@ -22,7 +23,6 @@ ) from localstack.services.events.models_v2 import ( EventPatternDict, - InvalidEventPatternException, Rule, TargetDict, ValidationException, From ede757d5aab38a37564258ea0a1a891fc4d55002 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 12:35:42 +0200 Subject: [PATCH 14/28] feat: add experimental rule matching --- localstack/services/events/models_v2.py | 8 ++++++++ localstack/services/events/provider_v2.py | 24 +++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index b02a370b18ac1..af7d874da326a 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -96,3 +96,11 @@ class ValidationException(ServiceException): code: str = "ValidationException" sender_fault: bool = True status_code: int = 400 + + +class InternalInvalidEventPatternException(Exception): + reason: str + + def __init__(self, reason=None, message=None) -> None: + self.reason = reason + self.message = message or f"Event pattern is not valid. Reason: {reason}" diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index f937e7e5c886e..dee6afc97146f 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -4,6 +4,7 @@ from datetime import datetime, timezone from typing import Optional +from localstack import config from localstack.aws.api import RequestContext, handler from localstack.aws.api.events import ( Arn, @@ -18,6 +19,7 @@ EventPattern, EventsApi, EventSourceName, + InvalidEventPatternException, LimitMax100, ListEventBusesResponse, ListRuleNamesByTargetResponse, @@ -51,10 +53,12 @@ from localstack.aws.api.events import EventBus as ApiTypeEventBus from localstack.aws.api.events import Rule as ApiTypeRule from localstack.services.events.event_bus import EventBusService, EventBusServiceDict +from localstack.services.events.event_ruler import matches_rule from localstack.services.events.models_v2 import ( EventBus, EventBusDict, EventsStore, + InternalInvalidEventPatternException, Rule, RuleDict, TargetDict, @@ -349,9 +353,15 @@ def test_event_pattern( """Test event pattern uses EventBridge event pattern matching: https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html """ - event_pattern_dict = json.loads(event_pattern) - event_dict = json.loads(event) - result = matches_event(event_pattern_dict, event_dict) + if config.EVENT_RULE_ENGINE == "java": + try: + result = matches_rule(event, event_pattern) + except InternalInvalidEventPatternException as e: + raise InvalidEventPatternException(e.message) from e + else: + event_pattern_dict = json.loads(event_pattern) + event_dict = json.loads(event) + result = matches_event(event_pattern_dict, event_dict) return TestEventPatternResponse(Result=result) @@ -654,7 +664,13 @@ def _process_entries( for rule in matching_rules: rule_service = self.get_rule_service(context, rule.name, event_bus_name) event_pattern = rule_service.event_pattern - if matches_event(event_pattern, event): + if config.EVENT_RULE_ENGINE == "java": + event_str = json.dumps(event) + event_pattern_str = json.dumps(event_pattern) + matches_result = matches_rule(event_str, event_pattern_str) + else: + matches_result = matches_event(event, event_pattern) + if matches_result: for target in rule.targets.values(): target_sender = self._target_sender_store[target["Arn"]] try: From 28354be670dbb5caee0c934ff757cddbd479d498 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 12:40:37 +0200 Subject: [PATCH 15/28] feat: set failed entry count directly --- localstack/services/events/provider_v2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index dee6afc97146f..590a91980e092 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -449,10 +449,10 @@ def put_events( processed_entries_uuids, failed_entries = self._process_entries(context, entries) response = PutEventsResponse( - Entries=[{"EventId": id} for id in processed_entries_uuids], FailedEntryCount=0 + Entries=[{"EventId": id} for id in processed_entries_uuids], + FailedEntryCount=len(failed_entries), ) if failed_entries: - response["FailedEntryCount"] = len(failed_entries) response["FailedEntries"] = failed_entries return response From f0530848ac11c684d4f2f4063d629592f72be319 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 13:51:29 +0200 Subject: [PATCH 16/28] feat: validate input pattern tests --- tests/aws/services/events/test_events.py | 26 +++++++-- .../services/events/test_events.snapshot.json | 55 +++++++++++++++++++ .../events/test_events.validation.json | 4 +- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 79adf824db19a..05441f74a050d 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -138,8 +138,8 @@ def test_list_tags_for_resource(self, aws_client, clean_up): # clean up clean_up(rule_name=rule_name) - @markers.aws.unknown - def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs): + @markers.aws.validated + def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs, snapshot): pattern = {"detail": {"event": {"data": {"type": ["1", "2"]}}}} entries1 = [ { @@ -163,14 +163,22 @@ def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs): } ] entries_asserts = [(entries1, True), (entries2, True), (entries3, False)] - put_events_with_filter_to_sqs( + messages = put_events_with_filter_to_sqs( pattern=pattern, entries_asserts=entries_asserts, input_path="$.detail", ) + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("MD5OfBody"), + snapshot.transform.key_value("ReceiptHandle"), + ] + ) + snapshot.match("messages", messages) + @markers.aws.validated - def test_put_events_with_nested_event_pattern(self, put_events_with_filter_to_sqs): + def test_put_events_with_nested_event_pattern(self, put_events_with_filter_to_sqs, snapshot): pattern = {"detail": {"event": {"data": {"type": ["1"]}}}} entries1 = [ { @@ -194,12 +202,20 @@ def test_put_events_with_nested_event_pattern(self, put_events_with_filter_to_sq } ] entries_asserts = [(entries1, True), (entries2, False), (entries3, False)] - put_events_with_filter_to_sqs( + messages = put_events_with_filter_to_sqs( pattern=pattern, entries_asserts=entries_asserts, input_path="$.detail", ) + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("MD5OfBody"), + snapshot.transform.key_value("ReceiptHandle"), + ] + ) + snapshot.match("messages", messages) + @markers.aws.unknown @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_scheduled_expression_events( diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 8c113ce47c615..d9775601d30ea 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -1142,5 +1142,60 @@ } } } + }, + "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_values_in_array": { + "recorded-date": "29-04-2024, 11:47:10", + "recorded-content": { + "messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "event": { + "data": { + "type": [ + "3", + "1" + ] + } + } + } + }, + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "event": { + "data": { + "type": [ + "2" + ] + } + } + } + } + ] + } + }, + "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_nested_event_pattern": { + "recorded-date": "29-04-2024, 11:48:23", + "recorded-content": { + "messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "event": { + "data": { + "type": "1" + } + } + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 9c1bb67593892..e776d4661c48e 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -90,10 +90,10 @@ "last_validated_date": "2024-03-26T14:09:45+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_nested_event_pattern": { - "last_validated_date": "2024-03-26T14:07:10+00:00" + "last_validated_date": "2024-04-29T11:48:23+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_values_in_array": { - "last_validated_date": "2024-03-26T14:06:58+00:00" + "last_validated_date": "2024-04-29T11:47:10+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_target_id_validation": { "last_validated_date": "2024-04-18T15:47:35+00:00" From 084c8667bb527a313a762874c8b9093acde02682 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 14:13:20 +0200 Subject: [PATCH 17/28] feat: clean test put events with time --- tests/aws/services/events/test_events.py | 97 +++++++------------ .../services/events/test_events.snapshot.json | 58 +++++++---- .../events/test_events.validation.json | 2 +- 3 files changed, 79 insertions(+), 78 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 05441f74a050d..97154997e0f18 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -648,73 +648,50 @@ def test_event_pattern(self, aws_client, snapshot, account_id, region_name): snapshot.match("eventbridge-test-event-pattern-response-no-match", response) @markers.aws.validated - def test_put_events_time( - self, - aws_client, - sqs_create_queue, - sqs_get_queue_arn, - events_put_rule, - snapshot, - ): - default_bus_rule_name = f"rule-{short_uid()}" - default_bus_target_id = f"test-target-default-b-{short_uid()}" + def test_put_events_time(self, put_events_with_filter_to_sqs, snapshot): + pattern = {"source": ["MySource"], "detail-type": ["CustomType"]} + entries1 = [ + { + "Source": pattern["source"][0], + "DetailType": pattern["detail-type"][0], + "Detail": json.dumps({"message": "short time"}), + "Time": "2022-01-01", + }, + ] + entries2 = [ + { + "Source": pattern["source"][0], + "DetailType": pattern["detail-type"][0], + "Detail": json.dumps({"message": "new time"}), + "Time": "01-01-2022T00:00:00Z", + }, + ] + entries3 = [ + { + "Source": pattern["source"][0], + "DetailType": pattern["detail-type"][0], + "Detail": json.dumps({"message": "long time"}), + "Time": "2022-01-01 00:00:00Z", + }, + ] + entries_asserts = [(entries1, True), (entries2, True), (entries3, True)] + messages = put_events_with_filter_to_sqs( + pattern=pattern, + entries_asserts=entries_asserts, + ) snapshot.add_transformer( [ - snapshot.transform.key_value("MD5OfBody"), # the event contains a timestamp + snapshot.transform.key_value("MD5OfBody"), snapshot.transform.key_value("ReceiptHandle"), ] ) + snapshot.match("messages", messages) - queue_url = sqs_create_queue() - queue_arn = sqs_get_queue_arn(queue_url) - - rule_on_default_bus = events_put_rule( - Name=default_bus_rule_name, - EventPattern=json.dumps({"detail-type": ["CustomType"], "source": ["MySource"]}), - State="ENABLED", - ) - - allow_event_rule_to_sqs_queue( - aws_client=aws_client, - event_rule_arn=rule_on_default_bus["RuleArn"], - sqs_queue_arn=queue_arn, - sqs_queue_url=queue_url, - ) - - aws_client.events.put_targets( - Rule=default_bus_rule_name, - Targets=[{"Id": default_bus_target_id, "Arn": queue_arn}], - ) - - # create an entry with a defined time - entries = [ - { - "Source": "MySource", - "DetailType": "CustomType", - "Detail": json.dumps({"message": "for the default event bus"}), - "Time": datetime(year=2022, day=1, month=1), - } - ] - response = aws_client.events.put_events(Entries=entries) - snapshot.match("put-events", response) - - def _get_sqs_messages(): - resp = aws_client.sqs.receive_message( - QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=1 - ) - msgs = resp.get("Messages") - assert len(msgs) == 1 - aws_client.sqs.delete_message( - QueueUrl=queue_url, ReceiptHandle=msgs[0]["ReceiptHandle"] - ) - return msgs - - messages = retry(_get_sqs_messages, retries=5, sleep=0.1) - snapshot.match("get-events", messages) - - message_body = json.loads(messages[0]["Body"]) - assert message_body["time"] == "2022-01-01T00:00:00Z" + # check for correct time strings in the messages + for message in messages: + message_body = json.loads(message["Body"]) + assert message_body["time"] == "2022-01-01T00:00:00Z" class TestEventBus: diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index d9775601d30ea..cfc957e263e3e 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -44,28 +44,16 @@ } }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_time": { - "recorded-date": "26-03-2024, 14:09:45", + "recorded-date": "29-04-2024, 12:11:50", "recorded-content": { - "put-events": { - "Entries": [ - { - "EventId": "" - } - ], - "FailedEntryCount": 0, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "get-events": [ + "messages": [ { - "MessageId": "", + "MessageId": "", "ReceiptHandle": "", "MD5OfBody": "", "Body": { "version": "0", - "id": "", + "id": "", "detail-type": "CustomType", "source": "MySource", "account": "111111111111", @@ -73,7 +61,43 @@ "region": "", "resources": [], "detail": { - "message": "for the default event bus" + "message": "short time" + } + } + }, + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "version": "0", + "id": "", + "detail-type": "CustomType", + "source": "MySource", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "message": "new time" + } + } + }, + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "version": "0", + "id": "", + "detail-type": "CustomType", + "source": "MySource", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "message": "long time" } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index e776d4661c48e..9160538e3aae7 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -87,7 +87,7 @@ "last_validated_date": "2024-03-26T14:07:16+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_time": { - "last_validated_date": "2024-03-26T14:09:45+00:00" + "last_validated_date": "2024-04-29T12:11:50+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_nested_event_pattern": { "last_validated_date": "2024-04-29T11:48:23+00:00" From f86c5c68a76868b045b9631a553190b9974313f7 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 14:52:05 +0200 Subject: [PATCH 18/28] feat: validate test put event without source --- tests/aws/services/events/test_events.py | 32 ++++++++++++------- .../services/events/test_events.snapshot.json | 18 +++++++++++ .../events/test_events.validation.json | 3 ++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 97154997e0f18..b2dd597b00296 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -26,10 +26,6 @@ from tests.aws.services.events.conftest import assert_valid_event, sqs_collect_messages from tests.aws.services.events.helper_functions import is_v2_provider -THIS_FOLDER = os.path.dirname(os.path.realpath(__file__)) - -TEST_EVENT_BUS_NAME = "command-bus-dev" - EVENT_DETAIL = {"command": "update-account", "payload": {"acc_id": "0a787ecb-4015", "sf_id": "baz"}} TEST_EVENT_PATTERN = { @@ -38,6 +34,16 @@ "detail": {"command": ["update-account"]}, } +TEST_EVENT_PATTERN_NO_DETAIL = { + "source": ["core.update-account-command"], + "detail-type": ["core.update-account-command"], +} + +TEST_EVENT_PATTERN_NO_SOURCE = { + "detail-type": ["core.update-account-command"], + "detail": {"command": ["update-account"]}, +} + API_DESTINATION_AUTHS = [ { "type": "BASIC", @@ -76,6 +82,17 @@ class TestEvents: + @markers.aws.validated + def test_put_events_without_source(self, snapshot, aws_client): + entries = [ + { + "DetailType": TEST_EVENT_PATTERN_NO_SOURCE["detail-type"][0], + "Detail": json.dumps(EVENT_DETAIL), + }, + ] + response = aws_client.events.put_events(Entries=entries) + snapshot.match("put-events", response) + @markers.aws.unknown @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_events_written_to_disk_are_timestamp_prefixed_for_chronological_ordering( @@ -540,13 +557,6 @@ def test_create_connection_validations(self, aws_client): assert "must have length less than or equal to 64" in message assert "must satisfy enum value set: [BASIC, OAUTH_CLIENT_CREDENTIALS, API_KEY]" in message - @markers.aws.unknown - def test_put_event_without_source(self, aws_client_factory): - events_client = aws_client_factory(region_name="eu-west-1").events - - response = events_client.put_events(Entries=[{"DetailType": "Test", "Detail": "{}"}]) - assert response.get("Entries") - @markers.aws.unknown def test_put_event_without_detail(self, aws_client_factory): events_client = aws_client_factory(region_name="eu-west-1").events diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index cfc957e263e3e..282699875103d 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -1221,5 +1221,23 @@ } ] } + }, + "tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": { + "recorded-date": "29-04-2024, 12:47:09", + "recorded-content": { + "put-events": { + "Entries": [ + { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument." + } + ], + "FailedEntryCount": 1, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 9160538e3aae7..c37114422fe72 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -95,6 +95,9 @@ "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_values_in_array": { "last_validated_date": "2024-04-29T11:47:10+00:00" }, + "tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": { + "last_validated_date": "2024-04-29T12:47:09+00:00" + }, "tests/aws/services/events/test_events.py::TestEvents::test_put_target_id_validation": { "last_validated_date": "2024-04-18T15:47:35+00:00" }, From e7fa9e13e7fdd325a01cef5d735ab37a156c9e71 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 15:24:43 +0200 Subject: [PATCH 19/28] refactor: events tests --- tests/aws/services/events/test_events.py | 404 ++--- .../services/events/test_events.snapshot.json | 1563 ++++++++--------- .../events/test_events.validation.json | 111 +- 3 files changed, 981 insertions(+), 1097 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index b2dd597b00296..74ba7ff38679f 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -7,7 +7,6 @@ import os import time import uuid -from datetime import datetime import pytest from botocore.exceptions import ClientError @@ -93,6 +92,62 @@ def test_put_events_without_source(self, snapshot, aws_client): response = aws_client.events.put_events(Entries=entries) snapshot.match("put-events", response) + @markers.aws.unknown + def test_put_event_without_detail(self, snapshot, aws_client): + entries = [ + { + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + }, + ] + response = aws_client.events.put_events(Entries=entries) + snapshot.match("put-events", response) + + @markers.aws.validated + def test_put_events_time(self, put_events_with_filter_to_sqs, snapshot): + entries1 = [ + { + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps({"message": "short time"}), + "Time": "2022-01-01", + }, + ] + entries2 = [ + { + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps({"message": "new time"}), + "Time": "01-01-2022T00:00:00Z", + }, + ] + entries3 = [ + { + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps({"message": "long time"}), + "Time": "2022-01-01 00:00:00Z", + }, + ] + entries_asserts = [(entries1, True), (entries2, True), (entries3, True)] + messages = put_events_with_filter_to_sqs( + pattern=TEST_EVENT_PATTERN_NO_DETAIL, + entries_asserts=entries_asserts, + ) + + snapshot.add_transformer( + [ + snapshot.transform.key_value("MD5OfBody"), + snapshot.transform.key_value("ReceiptHandle"), + ] + ) + snapshot.match("messages", messages) + + # check for correct time strings in the messages + for message in messages: + message_body = json.loads(message["Body"]) + assert message_body["time"] == "2022-01-01T00:00:00Z" + @markers.aws.unknown @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_events_written_to_disk_are_timestamp_prefixed_for_chronological_ordering( @@ -155,84 +210,6 @@ def test_list_tags_for_resource(self, aws_client, clean_up): # clean up clean_up(rule_name=rule_name) - @markers.aws.validated - def test_put_events_with_values_in_array(self, put_events_with_filter_to_sqs, snapshot): - pattern = {"detail": {"event": {"data": {"type": ["1", "2"]}}}} - entries1 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"event": {"data": {"type": ["3", "1"]}}}), - } - ] - entries2 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"event": {"data": {"type": ["2"]}}}), - } - ] - entries3 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"event": {"data": {"type": ["3"]}}}), - } - ] - entries_asserts = [(entries1, True), (entries2, True), (entries3, False)] - messages = put_events_with_filter_to_sqs( - pattern=pattern, - entries_asserts=entries_asserts, - input_path="$.detail", - ) - - snapshot.add_transformers_list( - [ - snapshot.transform.key_value("MD5OfBody"), - snapshot.transform.key_value("ReceiptHandle"), - ] - ) - snapshot.match("messages", messages) - - @markers.aws.validated - def test_put_events_with_nested_event_pattern(self, put_events_with_filter_to_sqs, snapshot): - pattern = {"detail": {"event": {"data": {"type": ["1"]}}}} - entries1 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"event": {"data": {"type": "1"}}}), - } - ] - entries2 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"event": {"data": {"type": "2"}}}), - } - ] - entries3 = [ - { - "Source": "test", - "DetailType": "test", - "Detail": json.dumps({"hello": "world"}), - } - ] - entries_asserts = [(entries1, True), (entries2, False), (entries3, False)] - messages = put_events_with_filter_to_sqs( - pattern=pattern, - entries_asserts=entries_asserts, - input_path="$.detail", - ) - - snapshot.add_transformers_list( - [ - snapshot.transform.key_value("MD5OfBody"), - snapshot.transform.key_value("ReceiptHandle"), - ] - ) - snapshot.match("messages", messages) - @markers.aws.unknown @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_scheduled_expression_events( @@ -557,152 +534,6 @@ def test_create_connection_validations(self, aws_client): assert "must have length less than or equal to 64" in message assert "must satisfy enum value set: [BASIC, OAUTH_CLIENT_CREDENTIALS, API_KEY]" in message - @markers.aws.unknown - def test_put_event_without_detail(self, aws_client_factory): - events_client = aws_client_factory(region_name="eu-west-1").events - - response = events_client.put_events( - Entries=[ - { - "DetailType": "Test", - } - ] - ) - assert response.get("Entries") - - @markers.aws.validated - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") - def test_put_target_id_validation( - self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client - ): - rule_name = f"rule-{short_uid()}" - queue_url = sqs_create_queue() - queue_arn = sqs_get_queue_arn(queue_url) - - events_put_rule( - Name=rule_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" - ) - - target_id = "!@#$@!#$" - with pytest.raises(ClientError) as e: - aws_client.events.put_targets( - Rule=rule_name, - Targets=[ - {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, - ], - ) - snapshot.add_transformer(snapshot.transform.regex(target_id, "invalid-target-id")) - snapshot.match("put-targets-invalid-id-error", e.value.response) - - target_id = f"{long_uid()}-{long_uid()}-extra" - with pytest.raises(ClientError) as e: - aws_client.events.put_targets( - Rule=rule_name, - Targets=[ - {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, - ], - ) - snapshot.add_transformer(snapshot.transform.regex(target_id, "second-invalid-target-id")) - snapshot.match("put-targets-length-error", e.value.response) - - target_id = f"test-With_valid.Characters-{short_uid()}" - aws_client.events.put_targets( - Rule=rule_name, - Targets=[ - {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, - ], - ) - - @markers.aws.validated # TODO move to tests_event_patterns - @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") - def test_event_pattern(self, aws_client, snapshot, account_id, region_name): - response = aws_client.events.test_event_pattern( - Event=json.dumps( - { - "id": "1", - "source": "order", - "detail-type": "Test", - "account": account_id, - "region": region_name, - "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), - } - ), - EventPattern=json.dumps( - { - "source": ["order"], - "detail-type": ["Test"], - } - ), - ) - snapshot.match("eventbridge-test-event-pattern-response", response) - - # negative test, source is not matched - response = aws_client.events.test_event_pattern( - Event=json.dumps( - { - "id": "1", - "source": "order", - "detail-type": "Test", - "account": account_id, - "region": region_name, - "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), - } - ), - EventPattern=json.dumps( - { - "source": ["shipment"], - "detail-type": ["Test"], - } - ), - ) - snapshot.match("eventbridge-test-event-pattern-response-no-match", response) - - @markers.aws.validated - def test_put_events_time(self, put_events_with_filter_to_sqs, snapshot): - pattern = {"source": ["MySource"], "detail-type": ["CustomType"]} - entries1 = [ - { - "Source": pattern["source"][0], - "DetailType": pattern["detail-type"][0], - "Detail": json.dumps({"message": "short time"}), - "Time": "2022-01-01", - }, - ] - entries2 = [ - { - "Source": pattern["source"][0], - "DetailType": pattern["detail-type"][0], - "Detail": json.dumps({"message": "new time"}), - "Time": "01-01-2022T00:00:00Z", - }, - ] - entries3 = [ - { - "Source": pattern["source"][0], - "DetailType": pattern["detail-type"][0], - "Detail": json.dumps({"message": "long time"}), - "Time": "2022-01-01 00:00:00Z", - }, - ] - entries_asserts = [(entries1, True), (entries2, True), (entries3, True)] - messages = put_events_with_filter_to_sqs( - pattern=pattern, - entries_asserts=entries_asserts, - ) - - snapshot.add_transformer( - [ - snapshot.transform.key_value("MD5OfBody"), - snapshot.transform.key_value("ReceiptHandle"), - ] - ) - snapshot.match("messages", messages) - - # check for correct time strings in the messages - for message in messages: - message_body = json.loads(message["Body"]) - assert message_body["time"] == "2022-01-01T00:00:00Z" - class TestEventBus: @markers.aws.validated @@ -1297,6 +1128,86 @@ def test_update_rule_with_targets( snapshot.match("list-targets-after-update", response) +class TestEventPattern: + @markers.aws.validated + def test_put_events_pattern_with_values_in_array(self, put_events_with_filter_to_sqs, snapshot): + pattern = {"detail": {"event": {"data": {"type": ["1", "2"]}}}} + entries1 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"event": {"data": {"type": ["3", "1"]}}}), + } + ] + entries2 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"event": {"data": {"type": ["2"]}}}), + } + ] + entries3 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"event": {"data": {"type": ["3"]}}}), + } + ] + entries_asserts = [(entries1, True), (entries2, True), (entries3, False)] + messages = put_events_with_filter_to_sqs( + pattern=pattern, + entries_asserts=entries_asserts, + input_path="$.detail", + ) + + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("MD5OfBody"), + snapshot.transform.key_value("ReceiptHandle"), + ] + ) + snapshot.match("messages", messages) + + @markers.aws.validated + def test_put_events_pattern_nested(self, put_events_with_filter_to_sqs, snapshot): + pattern = {"detail": {"event": {"data": {"type": ["1"]}}}} + entries1 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"event": {"data": {"type": "1"}}}), + } + ] + entries2 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"event": {"data": {"type": "2"}}}), + } + ] + entries3 = [ + { + "Source": "test", + "DetailType": "test", + "Detail": json.dumps({"hello": "world"}), + } + ] + entries_asserts = [(entries1, True), (entries2, False), (entries3, False)] + messages = put_events_with_filter_to_sqs( + pattern=pattern, + entries_asserts=entries_asserts, + input_path="$.detail", + ) + + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("MD5OfBody"), + snapshot.transform.key_value("ReceiptHandle"), + ] + ) + snapshot.match("messages", messages) + + class TestEventTarget: @markers.aws.validated @pytest.mark.parametrize("bus_name", ["custom", "default"]) @@ -1406,3 +1317,46 @@ def test_list_target_by_rule_limit( Rule=rule_name, NextToken=response["NextToken"] ) snapshot.match("list-targets-limit-next-token", response) + + @markers.aws.validated + @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") + def test_put_target_id_validation( + self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client + ): + rule_name = f"rule-{short_uid()}" + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + + events_put_rule( + Name=rule_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + + target_id = "!@#$@!#$" + with pytest.raises(ClientError) as e: + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) + snapshot.add_transformer(snapshot.transform.regex(target_id, "invalid-target-id")) + snapshot.match("put-targets-invalid-id-error", e.value.response) + + target_id = f"{long_uid()}-{long_uid()}-extra" + with pytest.raises(ClientError) as e: + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) + snapshot.add_transformer(snapshot.transform.regex(target_id, "second-invalid-target-id")) + snapshot.match("put-targets-length-error", e.value.response) + + target_id = f"test-With_valid.Characters-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 282699875103d..424fa08196021 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -1,41 +1,33 @@ { - "tests/aws/services/events/test_events.py::TestEvents::test_put_target_id_validation": { - "recorded-date": "18-04-2024, 15:47:35", + "tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": { + "recorded-date": "29-04-2024, 13:15:31", "recorded-content": { - "put-targets-invalid-id-error": { - "Error": { - "Code": "ValidationException", - "Message": "1 validation error detected: Value '!@#$@!#$' at 'targets.1.member.id' failed to satisfy constraint: Member must satisfy regular expression pattern: [\\.\\-_A-Za-z0-9]+" - }, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 400 - } - }, - "put-targets-length-error": { - "Error": { - "Code": "ValidationException", - "Message": "1 validation error detected: Value 'second-invalid-target-id' at 'targets.1.member.id' failed to satisfy constraint: Member must have length less than or equal to 64" - }, + "put-events": { + "Entries": [ + { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument." + } + ], + "FailedEntryCount": 1, "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 400 + "HTTPStatusCode": 200 } } } }, - "tests/aws/services/events/test_events.py::TestEvents::test_event_pattern": { - "recorded-date": "26-03-2024, 14:07:19", + "tests/aws/services/events/test_events.py::TestEvents::test_put_event_without_detail": { + "recorded-date": "29-04-2024, 13:15:31", "recorded-content": { - "eventbridge-test-event-pattern-response": { - "Result": true, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "eventbridge-test-event-pattern-response-no-match": { - "Result": false, + "put-events": { + "Entries": [ + { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter Detail is not valid. Reason: Detail is a required argument." + } + ], + "FailedEntryCount": 1, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -44,7 +36,7 @@ } }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_time": { - "recorded-date": "29-04-2024, 12:11:50", + "recorded-date": "29-04-2024, 13:15:34", "recorded-content": { "messages": [ { @@ -54,8 +46,8 @@ "Body": { "version": "0", "id": "", - "detail-type": "CustomType", - "source": "MySource", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", "account": "111111111111", "time": "date", "region": "", @@ -72,8 +64,8 @@ "Body": { "version": "0", "id": "", - "detail-type": "CustomType", - "source": "MySource", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", "account": "111111111111", "time": "date", "region": "", @@ -90,8 +82,8 @@ "Body": { "version": "0", "id": "", - "detail-type": "CustomType", - "source": "MySource", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", "account": "111111111111", "time": "date", "region": "", @@ -104,23 +96,44 @@ ] } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_rule[custom]": { - "recorded-date": "04-04-2024, 10:47:20", + "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions0]": { + "recorded-date": "29-04-2024, 13:15:44", "recorded-content": { - "put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule//", + "create-custom-event-bus-us-east-1": { + "EventBusArn": "arn:aws:events::111111111111:event-bus/", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_rule[default]": { - "recorded-date": "04-04-2024, 10:47:21", - "recorded-content": { - "put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule/", + }, + "list-event-buses-after-create-us-east-1": { + "EventBuses": [ + { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "describe-custom-event-bus-us-east-1": { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "delete-custom-event-bus-us-east-1": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-event-buses-after-delete-us-east-1": { + "EventBuses": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -128,36 +141,21 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[custom]": { - "recorded-date": "22-04-2024, 13:07:35", + "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions1]": { + "recorded-date": "29-04-2024, 13:15:47", "recorded-content": { - "put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule//", + "create-custom-event-bus-us-east-1": { + "EventBusArn": "arn:aws:events::111111111111:event-bus/", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules": { - "Rules": [ + "list-event-buses-after-create-us-east-1": { + "EventBuses": [ { - "Arn": "arn:aws:events::111111111111:rule//", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED" + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" } ], "ResponseMetadata": { @@ -165,75 +163,53 @@ "HTTPStatusCode": 200 } }, - "describe-rule": { - "Arn": "arn:aws:events::111111111111:rule//", - "CreatedBy": "111111111111", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED", + "describe-custom-event-bus-us-east-1": { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "delete-rule": { + "create-custom-event-bus-us-west-1": { + "EventBusArn": "arn:aws:events::111111111111:event-bus/", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules-after-delete": { - "Rules": [], + "list-event-buses-after-create-us-west-1": { + "EventBuses": [ + { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[default]": { - "recorded-date": "22-04-2024, 13:07:36", - "recorded-content": { - "put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule/", + }, + "describe-custom-event-bus-us-west-1": { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules": { - "Rules": [ + "create-custom-event-bus-eu-central-1": { + "EventBusArn": "arn:aws:events::111111111111:event-bus/", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-event-buses-after-create-eu-central-1": { + "EventBuses": [ { - "Arn": "arn:aws:events::111111111111:rule/", - "EventBusName": "default", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED" + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" } ], "ResponseMetadata": { @@ -241,220 +217,48 @@ "HTTPStatusCode": 200 } }, - "describe-rule": { - "Arn": "arn:aws:events::111111111111:rule/", - "CreatedBy": "111111111111", - "EventBusName": "default", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED", + "describe-custom-event-bus-eu-central-1": { + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "delete-rule": { + "delete-custom-event-bus-us-east-1": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules-after-delete": { - "Rules": [], + "list-event-buses-after-delete-us-east-1": { + "EventBuses": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_multiple_rules_with_same_name": { - "recorded-date": "22-04-2024, 13:07:38", - "recorded-content": { - "put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule//", + }, + "delete-custom-event-bus-us-west-1": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "re-put-rule": { - "RuleArn": "arn:aws:events::111111111111:rule//", + "list-event-buses-after-delete-us-west-1": { + "EventBuses": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules": { - "Rules": [ - { - "Arn": "arn:aws:events::111111111111:rule//", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED" - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_list_rule_with_limit": { - "recorded-date": "22-04-2024, 13:07:40", - "recorded-content": { - "list-rules-limit": { - "NextToken": "", - "Rules": [ - { - "Arn": "arn:aws:events::111111111111:rule//-0", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-0", - "State": "ENABLED" - }, - { - "Arn": "arn:aws:events::111111111111:rule//-1", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-1", - "State": "ENABLED" - }, - { - "Arn": "arn:aws:events::111111111111:rule//-2", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-2", - "State": "ENABLED" - } - ], + "delete-custom-event-bus-eu-central-1": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-rules-limit-next-token": { - "Rules": [ - { - "Arn": "arn:aws:events::111111111111:rule//-3", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-3", - "State": "ENABLED" - }, - { - "Arn": "arn:aws:events::111111111111:rule//-4", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-4", - "State": "ENABLED" - }, - { - "Arn": "arn:aws:events::111111111111:rule//-5", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "-5", - "State": "ENABLED" - } - ], + "list-event-buses-after-delete-eu-central-1": { + "EventBuses": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 @@ -462,158 +266,33 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_describe_nonexistent_rule": { - "recorded-date": "22-04-2024, 13:07:42", + "tests/aws/services/events/test_events.py::TestEventBus::test_create_multiple_event_buses_same_name": { + "recorded-date": "29-04-2024, 13:15:47", "recorded-content": { - "describe-not-existing-rule-error": " does not exist on EventBus default.') tblen=3>" + "create-multiple-event-buses-same-name": " already exists.') tblen=4>" } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[custom]": { - "recorded-date": "22-04-2024, 13:07:43", + "tests/aws/services/events/test_events.py::TestEventBus::test_describe_delete_not_existing_event_bus": { + "recorded-date": "29-04-2024, 13:15:49", "recorded-content": { - "disable-rule": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-rule-disabled": { - "Arn": "arn:aws:events::111111111111:rule//", - "CreatedBy": "111111111111", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "DISABLED", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "enable-rule": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-rule-enabled": { - "Arn": "arn:aws:events::111111111111:rule//", - "CreatedBy": "111111111111", - "EventBusName": "", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } + "describe-not-existing-event-bus-error": " does not exist.') tblen=3>", + "delete-not-existing-event-bus": " does not exist.') tblen=3>" } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[default]": { - "recorded-date": "22-04-2024, 13:07:45", + "tests/aws/services/events/test_events.py::TestEventBus::test_delete_default_event_bus": { + "recorded-date": "29-04-2024, 13:15:49", "recorded-content": { - "disable-rule": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-rule-disabled": { - "Arn": "arn:aws:events::111111111111:rule/", - "CreatedBy": "111111111111", - "EventBusName": "default", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "DISABLED", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "enable-rule": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-rule-enabled": { - "Arn": "arn:aws:events::111111111111:rule/", - "CreatedBy": "111111111111", - "EventBusName": "default", - "EventPattern": { - "source": [ - "core.update-account-command" - ], - "detail-type": [ - "core.update-account-command" - ], - "detail": { - "command": [ - "update-account" - ] - } - }, - "Name": "", - "State": "ENABLED", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } + "delete-default-event-bus-error": "" } }, - "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[custom]": { - "recorded-date": "22-04-2024, 13:07:17", + "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_prefix": { + "recorded-date": "29-04-2024, 13:15:50", "recorded-content": { - "put-target": { - "FailedEntries": [], - "FailedEntryCount": 0, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-targets": { - "Targets": [ + "list-event-buses-prefix-complete-name": { + "EventBuses": [ { - "Arn": "", - "Id": "" + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" } ], "ResponseMetadata": { @@ -621,150 +300,57 @@ "HTTPStatusCode": 200 } }, - "remove-target": { - "FailedEntries": [], - "FailedEntryCount": 0, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-targets-after-delete": { - "Targets": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[default]": { - "recorded-date": "22-04-2024, 13:07:18", - "recorded-content": { - "put-target": { - "FailedEntries": [], - "FailedEntryCount": 0, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-targets": { - "Targets": [ + "list-event-buses-prefix": { + "EventBuses": [ { - "Arn": "", - "Id": "" + "Arn": "arn:aws:events::111111111111:event-bus/", + "Name": "" } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - }, - "remove-target": { - "FailedEntries": [], - "FailedEntryCount": 0, - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-targets-after-delete": { - "Targets": [], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } } } }, - "tests/aws/services/events/test_events.py::TestEventTarget::test_add_exceed_fife_targets_per_rule": { - "recorded-date": "22-04-2024, 13:07:20", - "recorded-content": { - "put-targets-client-error": "" - } - }, - "tests/aws/services/events/test_events.py::TestEventTarget::test_list_target_by_rule_limit": { - "recorded-date": "22-04-2024, 13:07:22", + "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_limit": { + "recorded-date": "29-04-2024, 13:15:52", "recorded-content": { - "list-targets-limit": { - "NextToken": "", - "Targets": [ + "list-event-buses-limit": { + "EventBuses": [ { - "Arn": "", - "Id": "0" + "Arn": "arn:aws:events::111111111111:event-bus/-0", + "Name": "-0" }, { - "Arn": "", - "Id": "1" + "Arn": "arn:aws:events::111111111111:event-bus/-1", + "Name": "-1" }, { - "Arn": "", - "Id": "2" + "Arn": "arn:aws:events::111111111111:event-bus/-2", + "Name": "-2" } ], + "NextToken": "", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-targets-limit-next-token": { - "Targets": [ + "list-event-buses-limit-next-token": { + "EventBuses": [ { - "Arn": "", - "Id": "3" + "Arn": "arn:aws:events::111111111111:event-bus/-3", + "Name": "-3" }, { - "Arn": "", - "Id": "4" - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_delete_rule_with_targets": { - "recorded-date": "22-04-2024, 13:07:46", - "recorded-content": { - "delete-rule-with-targets-error": "" - } - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_delete_default_event_bus": { - "recorded-date": "16-04-2024, 15:10:07", - "recorded-content": { - "delete-default-event-bus-error": "" - } - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_update_rule_with_targets": { - "recorded-date": "22-04-2024, 13:07:48", - "recorded-content": { - "list-targets": { - "Targets": [ - { - "Arn": "", - "Id": "" - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "update-rule": { - "RuleArn": "arn:aws:events::111111111111:rule/", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-targets-after-update": { - "Targets": [ + "Arn": "arn:aws:events::111111111111:event-bus/-4", + "Name": "-4" + }, { - "Arn": "", - "Id": "" + "Arn": "arn:aws:events::111111111111:event-bus/-5", + "Name": "-5" } ], "ResponseMetadata": { @@ -774,120 +360,165 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions0]": { - "recorded-date": "23-04-2024, 06:11:32", + "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_to_default_eventbus_for_custom_eventbus": { + "recorded-date": "29-04-2024, 13:16:20", "recorded-content": { - "create-custom-event-bus-us-east-1": { - "EventBusArn": "arn:aws:events::111111111111:event-bus/", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-event-buses-after-create-us-east-1": { - "EventBuses": [ - { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "describe-custom-event-bus-us-east-1": { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "", - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "delete-custom-event-bus-us-east-1": { - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - }, - "list-event-buses-after-delete-us-east-1": { - "EventBuses": [], + "create-custom-event-bus": { + "EventBusArn": "arn:aws:events::111111111111:event-bus/", "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, - "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions1]": { - "recorded-date": "23-04-2024, 06:11:34", - "recorded-content": { - "create-custom-event-bus-us-east-1": { - "EventBusArn": "arn:aws:events::111111111111:event-bus/", + "HTTPStatusCode": 200 + } + }, + "create-rule-1": { + "RuleArn": "arn:aws:events::111111111111:rule/", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-create-us-east-1": { - "EventBuses": [ - { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" - } - ], + "create-rule-2": { + "RuleArn": "arn:aws:events::111111111111:rule//", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "describe-custom-event-bus-us-east-1": { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "", + "put-target-1": { + "FailedEntries": [], + "FailedEntryCount": 0, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "create-custom-event-bus-us-west-1": { - "EventBusArn": "arn:aws:events::111111111111:event-bus/", + "put-target-2": { + "FailedEntries": [], + "FailedEntryCount": 0, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-create-us-west-1": { - "EventBuses": [ + "get-events": { + "Messages": [ { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" + "Body": { + "version": "0", + "id": "", + "detail-type": "Object Created", + "source": "aws.s3", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [ + "arn:aws:s3:::" + ], + "detail": { + "version": "0", + "bucket": { + "name": "" + }, + "object": { + "key": "", + "size": 4, + "etag": "8d777f385d3dfec8815d20f7496026dc", + "sequencer": "object-sequencer" + }, + "request-id": "request-id", + "requester": "", + "source-ip-address": "", + "reason": "PutObject" + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ] + } + } + }, + "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_nonexistent_event_bus": { + "recorded-date": "29-04-2024, 13:18:57", + "recorded-content": { + "put-events": { + "Entries": [ + { + "EventId": "" + }, + { + "EventId": "" } ], + "FailedEntryCount": 0, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "describe-custom-event-bus-us-west-1": { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "", + "get-events": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "version": "0", + "id": "", + "detail-type": "CustomType", + "source": "MySource", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "message": "for the default event bus" + } + } + } + ], + "non-existent-bus-error": { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Event bus does not exist." + }, "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 200 + "HTTPStatusCode": 400 } - }, - "create-custom-event-bus-eu-central-1": { - "EventBusArn": "arn:aws:events::111111111111:event-bus/", + } + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[custom]": { + "recorded-date": "29-04-2024, 13:16:41", + "recorded-content": { + "put-rule": { + "RuleArn": "arn:aws:events::111111111111:rule//", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-create-eu-central-1": { - "EventBuses": [ + "list-rules": { + "Rules": [ { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" + "Arn": "arn:aws:events::111111111111:rule//", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED" } ], "ResponseMetadata": { @@ -895,94 +526,158 @@ "HTTPStatusCode": 200 } }, - "describe-custom-event-bus-eu-central-1": { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "", + "describe-rule": { + "Arn": "arn:aws:events::111111111111:rule//", + "CreatedBy": "111111111111", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "delete-custom-event-bus-us-east-1": { + "delete-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-delete-us-east-1": { - "EventBuses": [], + "list-rules-after-delete": { + "Rules": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[default]": { + "recorded-date": "29-04-2024, 13:16:43", + "recorded-content": { + "put-rule": { + "RuleArn": "arn:aws:events::111111111111:rule/", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "delete-custom-event-bus-us-west-1": { + "list-rules": { + "Rules": [ + { + "Arn": "arn:aws:events::111111111111:rule/", + "EventBusName": "default", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED" + } + ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-delete-us-west-1": { - "EventBuses": [], + "describe-rule": { + "Arn": "arn:aws:events::111111111111:rule/", + "CreatedBy": "111111111111", + "EventBusName": "default", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "delete-custom-event-bus-eu-central-1": { + "delete-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-after-delete-eu-central-1": { - "EventBuses": [], + "list-rules-after-delete": { + "Rules": [], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } } - } - }, - "tests/aws/services/events/test_events.py::TestEventBus::test_create_multiple_event_buses_same_name": { - "recorded-date": "22-04-2024, 13:11:10", - "recorded-content": { - "create-multiple-event-buses-same-name": " already exists.') tblen=4>" - } - }, - "tests/aws/services/events/test_events.py::TestEventBus::test_describe_delete_not_existing_event_bus": { - "recorded-date": "22-04-2024, 13:11:12", - "recorded-content": { - "describe-not-existing-event-bus-error": " does not exist.') tblen=3>", - "delete-not-existing-event-bus": " does not exist.') tblen=3>" - } - }, - "tests/aws/services/events/test_events.py::TestEventBus::test_delete_default_event_bus": { - "recorded-date": "22-04-2024, 13:11:12", - "recorded-content": { - "delete-default-event-bus-error": "" - } - }, - "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_prefix": { - "recorded-date": "22-04-2024, 13:19:19", - "recorded-content": { - "list-event-buses-prefix-complete-name": { - "EventBuses": [ - { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" - } - ], + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_put_multiple_rules_with_same_name": { + "recorded-date": "29-04-2024, 13:16:44", + "recorded-content": { + "put-rule": { + "RuleArn": "arn:aws:events::111111111111:rule//", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-prefix": { - "EventBuses": [ + "re-put-rule": { + "RuleArn": "arn:aws:events::111111111111:rule//", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-rules": { + "Rules": [ { - "Arn": "arn:aws:events::111111111111:event-bus/", - "Name": "" + "Arn": "arn:aws:events::111111111111:rule//", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED" } ], "ResponseMetadata": { @@ -992,43 +687,133 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_limit": { - "recorded-date": "22-04-2024, 13:11:15", + "tests/aws/services/events/test_events.py::TestEventRule::test_list_rule_with_limit": { + "recorded-date": "29-04-2024, 13:16:47", "recorded-content": { - "list-event-buses-limit": { - "EventBuses": [ + "list-rules-limit": { + "NextToken": "", + "Rules": [ { - "Arn": "arn:aws:events::111111111111:event-bus/-0", - "Name": "-0" + "Arn": "arn:aws:events::111111111111:rule//-0", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-0", + "State": "ENABLED" }, { - "Arn": "arn:aws:events::111111111111:event-bus/-1", - "Name": "-1" + "Arn": "arn:aws:events::111111111111:rule//-1", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-1", + "State": "ENABLED" }, { - "Arn": "arn:aws:events::111111111111:event-bus/-2", - "Name": "-2" + "Arn": "arn:aws:events::111111111111:rule//-2", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-2", + "State": "ENABLED" } ], - "NextToken": "", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "list-event-buses-limit-next-token": { - "EventBuses": [ + "list-rules-limit-next-token": { + "Rules": [ { - "Arn": "arn:aws:events::111111111111:event-bus/-3", - "Name": "-3" + "Arn": "arn:aws:events::111111111111:rule//-3", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-3", + "State": "ENABLED" }, { - "Arn": "arn:aws:events::111111111111:event-bus/-4", - "Name": "-4" + "Arn": "arn:aws:events::111111111111:rule//-4", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-4", + "State": "ENABLED" }, { - "Arn": "arn:aws:events::111111111111:event-bus/-5", - "Name": "-5" + "Arn": "arn:aws:events::111111111111:rule//-5", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "-5", + "State": "ENABLED" } ], "ResponseMetadata": { @@ -1038,137 +823,186 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_to_default_eventbus_for_custom_eventbus": { - "recorded-date": "22-04-2024, 13:11:43", + "tests/aws/services/events/test_events.py::TestEventRule::test_describe_nonexistent_rule": { + "recorded-date": "29-04-2024, 13:16:49", "recorded-content": { - "create-custom-event-bus": { - "EventBusArn": "arn:aws:events::111111111111:event-bus/", + "describe-not-existing-rule-error": " does not exist on EventBus default.') tblen=3>" + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[custom]": { + "recorded-date": "29-04-2024, 13:16:50", + "recorded-content": { + "disable-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "create-rule-1": { - "RuleArn": "arn:aws:events::111111111111:rule/", + "describe-rule-disabled": { + "Arn": "arn:aws:events::111111111111:rule//", + "CreatedBy": "111111111111", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "DISABLED", "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "create-rule-2": { - "RuleArn": "arn:aws:events::111111111111:rule//", + "enable-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "put-target-1": { - "FailedEntries": [], - "FailedEntryCount": 0, + "describe-rule-enabled": { + "Arn": "arn:aws:events::111111111111:rule//", + "CreatedBy": "111111111111", + "EventBusName": "", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "ENABLED", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[default]": { + "recorded-date": "29-04-2024, 13:16:52", + "recorded-content": { + "disable-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "put-target-2": { - "FailedEntries": [], - "FailedEntryCount": 0, + "describe-rule-disabled": { + "Arn": "arn:aws:events::111111111111:rule/", + "CreatedBy": "111111111111", + "EventBusName": "default", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] + } + }, + "Name": "", + "State": "DISABLED", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "enable-rule": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "get-events": { - "Messages": [ - { - "Body": { - "version": "0", - "id": "", - "detail-type": "Object Created", - "source": "aws.s3", - "account": "111111111111", - "time": "date", - "region": "", - "resources": [ - "arn:aws:s3:::" - ], - "detail": { - "version": "0", - "bucket": { - "name": "" - }, - "object": { - "key": "", - "size": 4, - "etag": "8d777f385d3dfec8815d20f7496026dc", - "sequencer": "object-sequencer" - }, - "request-id": "request-id", - "requester": "", - "source-ip-address": "", - "reason": "PutObject" - } - }, - "MD5OfBody": "", - "MessageId": "", - "ReceiptHandle": "" + "describe-rule-enabled": { + "Arn": "arn:aws:events::111111111111:rule/", + "CreatedBy": "111111111111", + "EventBusName": "default", + "EventPattern": { + "source": [ + "core.update-account-command" + ], + "detail-type": [ + "core.update-account-command" + ], + "detail": { + "command": [ + "update-account" + ] } - ] + }, + "Name": "", + "State": "ENABLED", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } } } }, - "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_nonexistent_event_bus": { - "recorded-date": "22-04-2024, 13:12:23", + "tests/aws/services/events/test_events.py::TestEventRule::test_delete_rule_with_targets": { + "recorded-date": "29-04-2024, 13:16:53", "recorded-content": { - "put-events": { - "Entries": [ - { - "EventId": "" - }, + "delete-rule-with-targets-error": "" + } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_update_rule_with_targets": { + "recorded-date": "29-04-2024, 13:16:55", + "recorded-content": { + "list-targets": { + "Targets": [ { - "EventId": "" + "Arn": "", + "Id": "" } ], - "FailedEntryCount": 0, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } }, - "get-events": [ - { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", - "Body": { - "version": "0", - "id": "", - "detail-type": "CustomType", - "source": "MySource", - "account": "111111111111", - "time": "date", - "region": "", - "resources": [], - "detail": { - "message": "for the default event bus" - } - } + "update-rule": { + "RuleArn": "arn:aws:events::111111111111:rule/", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 } - ], - "non-existent-bus-error": { - "Error": { - "Code": "ResourceNotFoundException", - "Message": "Event bus does not exist." - }, + }, + "list-targets-after-update": { + "Targets": [ + { + "Arn": "", + "Id": "" + } + ], "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 400 + "HTTPStatusCode": 200 } } } }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_values_in_array": { - "recorded-date": "29-04-2024, 11:47:10", + "tests/aws/services/events/test_events.py::TestEventPattern::test_put_events_pattern_with_values_in_array": { + "recorded-date": "29-04-2024, 13:17:04", "recorded-content": { "messages": [ { @@ -1203,8 +1037,8 @@ ] } }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_nested_event_pattern": { - "recorded-date": "29-04-2024, 11:48:23", + "tests/aws/services/events/test_events.py::TestEventPattern::test_put_events_pattern_nested": { + "recorded-date": "29-04-2024, 13:17:16", "recorded-content": { "messages": [ { @@ -1222,22 +1056,157 @@ ] } }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": { - "recorded-date": "29-04-2024, 12:47:09", + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[custom]": { + "recorded-date": "29-04-2024, 13:17:18", "recorded-content": { - "put-events": { - "Entries": [ + "put-target": { + "FailedEntries": [], + "FailedEntryCount": 0, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets": { + "Targets": [ { - "ErrorCode": "InvalidArgument", - "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument." + "Arn": "", + "Id": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "remove-target": { + "FailedEntries": [], + "FailedEntryCount": 0, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets-after-delete": { + "Targets": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[default]": { + "recorded-date": "29-04-2024, 13:17:20", + "recorded-content": { + "put-target": { + "FailedEntries": [], + "FailedEntryCount": 0, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets": { + "Targets": [ + { + "Arn": "", + "Id": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "remove-target": { + "FailedEntries": [], + "FailedEntryCount": 0, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets-after-delete": { + "Targets": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_add_exceed_fife_targets_per_rule": { + "recorded-date": "29-04-2024, 13:17:21", + "recorded-content": { + "put-targets-client-error": "" + } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_list_target_by_rule_limit": { + "recorded-date": "29-04-2024, 13:17:23", + "recorded-content": { + "list-targets-limit": { + "NextToken": "", + "Targets": [ + { + "Arn": "", + "Id": "0" + }, + { + "Arn": "", + "Id": "1" + }, + { + "Arn": "", + "Id": "2" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets-limit-next-token": { + "Targets": [ + { + "Arn": "", + "Id": "3" + }, + { + "Arn": "", + "Id": "4" } ], - "FailedEntryCount": 1, "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } } } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_target_id_validation": { + "recorded-date": "29-04-2024, 13:17:26", + "recorded-content": { + "put-targets-invalid-id-error": { + "Error": { + "Code": "ValidationException", + "Message": "1 validation error detected: Value '!@#$@!#$' at 'targets.1.member.id' failed to satisfy constraint: Member must satisfy regular expression pattern: [\\.\\-_A-Za-z0-9]+" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "put-targets-length-error": { + "Error": { + "Code": "ValidationException", + "Message": "1 validation error detected: Value 'second-invalid-target-id' at 'targets.1.member.id' failed to satisfy constraint: Member must have length less than or equal to 64" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index c37114422fe72..dd22b7d3333c7 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,131 +1,92 @@ { "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions0]": { - "last_validated_date": "2024-04-23T06:11:32+00:00" + "last_validated_date": "2024-04-29T13:15:44+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_create_list_describe_delete_custom_event_buses[regions1]": { - "last_validated_date": "2024-04-23T06:11:34+00:00" + "last_validated_date": "2024-04-29T13:15:47+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_create_multiple_event_buses_same_name": { - "last_validated_date": "2024-04-22T13:11:10+00:00" + "last_validated_date": "2024-04-29T13:15:47+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_delete_default_event_bus": { - "last_validated_date": "2024-04-22T13:11:12+00:00" + "last_validated_date": "2024-04-29T13:15:49+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_describe_delete_not_existing_event_bus": { - "last_validated_date": "2024-04-22T13:11:12+00:00" + "last_validated_date": "2024-04-29T13:15:49+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_limit": { - "last_validated_date": "2024-04-22T13:11:15+00:00" + "last_validated_date": "2024-04-29T13:15:52+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_list_event_buses_with_prefix": { - "last_validated_date": "2024-04-22T13:19:19+00:00" + "last_validated_date": "2024-04-29T13:15:50+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_nonexistent_event_bus": { - "last_validated_date": "2024-04-22T13:12:23+00:00" + "last_validated_date": "2024-04-29T13:18:57+00:00" }, "tests/aws/services/events/test_events.py::TestEventBus::test_put_events_to_default_eventbus_for_custom_eventbus": { - "last_validated_date": "2024-04-22T13:11:43+00:00" + "last_validated_date": "2024-04-29T13:16:20+00:00" + }, + "tests/aws/services/events/test_events.py::TestEventPattern::test_put_events_pattern_nested": { + "last_validated_date": "2024-04-29T13:17:16+00:00" + }, + "tests/aws/services/events/test_events.py::TestEventPattern::test_put_events_pattern_with_values_in_array": { + "last_validated_date": "2024-04-29T13:17:04+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_delete_rule_with_targets": { - "last_validated_date": "2024-04-22T13:07:46+00:00" + "last_validated_date": "2024-04-29T13:16:53+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_describe_nonexistent_rule": { - "last_validated_date": "2024-04-22T13:07:42+00:00" + "last_validated_date": "2024-04-29T13:16:49+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[custom]": { - "last_validated_date": "2024-04-22T13:07:43+00:00" + "last_validated_date": "2024-04-29T13:16:50+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_disable_re_enable_rule[default]": { - "last_validated_date": "2024-04-22T13:07:45+00:00" + "last_validated_date": "2024-04-29T13:16:52+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_list_rule_with_limit": { - "last_validated_date": "2024-04-22T13:07:40+00:00" + "last_validated_date": "2024-04-29T13:16:47+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[custom]": { - "last_validated_date": "2024-04-22T13:07:35+00:00" + "last_validated_date": "2024-04-29T13:16:41+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_put_list_with_prefix_describe_delete_rule[default]": { - "last_validated_date": "2024-04-22T13:07:36+00:00" + "last_validated_date": "2024-04-29T13:16:43+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_put_multiple_rules_with_same_name": { - "last_validated_date": "2024-04-22T13:07:38+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_rule[custom]": { - "last_validated_date": "2024-04-04T10:47:20+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventRule::test_put_rule[default]": { - "last_validated_date": "2024-04-04T10:47:21+00:00" + "last_validated_date": "2024-04-29T13:16:44+00:00" }, "tests/aws/services/events/test_events.py::TestEventRule::test_update_rule_with_targets": { - "last_validated_date": "2024-04-22T13:07:48+00:00" + "last_validated_date": "2024-04-29T13:16:55+00:00" }, "tests/aws/services/events/test_events.py::TestEventTarget::test_add_exceed_fife_targets_per_rule": { - "last_validated_date": "2024-04-22T13:07:20+00:00" + "last_validated_date": "2024-04-29T13:17:21+00:00" }, "tests/aws/services/events/test_events.py::TestEventTarget::test_list_target_by_rule_limit": { - "last_validated_date": "2024-04-22T13:07:22+00:00" + "last_validated_date": "2024-04-29T13:17:23+00:00" }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[custom]": { - "last_validated_date": "2024-04-22T13:07:17+00:00" + "last_validated_date": "2024-04-29T13:17:18+00:00" }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_list_remove_target[default]": { - "last_validated_date": "2024-04-22T13:07:18+00:00" + "last_validated_date": "2024-04-29T13:17:20+00:00" }, - "tests/aws/services/events/test_events.py::TestEvents::test_create_connection_validations": { - "last_validated_date": "2024-03-26T14:07:16+00:00" + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_target_id_validation": { + "last_validated_date": "2024-04-29T13:17:26+00:00" }, - "tests/aws/services/events/test_events.py::TestEvents::test_event_pattern": { - "last_validated_date": "2024-03-26T14:07:19+00:00" + "tests/aws/services/events/test_events.py::TestEvents::test_create_connection_validations": { + "last_validated_date": "2024-04-29T13:15:43+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_list_tags_for_resource": { - "last_validated_date": "2024-03-26T14:06:51+00:00" + "last_validated_date": "2024-04-29T13:15:38+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_event_without_detail": { - "last_validated_date": "2024-03-26T14:07:16+00:00" - }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_event_without_source": { - "last_validated_date": "2024-03-26T14:07:16+00:00" + "last_validated_date": "2024-04-29T13:15:31+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_time": { - "last_validated_date": "2024-04-29T12:11:50+00:00" - }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_nested_event_pattern": { - "last_validated_date": "2024-04-29T11:48:23+00:00" - }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_events_with_values_in_array": { - "last_validated_date": "2024-04-29T11:47:10+00:00" + "last_validated_date": "2024-04-29T13:15:34+00:00" }, "tests/aws/services/events/test_events.py::TestEvents::test_put_events_without_source": { - "last_validated_date": "2024-04-29T12:47:09+00:00" - }, - "tests/aws/services/events/test_events.py::TestEvents::test_put_target_id_validation": { - "last_validated_date": "2024-04-18T15:47:35+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_create_custom_event_bus": { - "last_validated_date": "2024-03-27T09:15:34+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_create_list_describe_delete_custom_event_buses[regions0]": { - "last_validated_date": "2024-04-03T13:49:07+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_create_list_describe_delete_custom_event_buses[regions1]": { - "last_validated_date": "2024-04-03T13:49:10+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_create_multiple_event_buses_same_name": { - "last_validated_date": "2024-04-16T15:09:51+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_delete_default_event_bus": { - "last_validated_date": "2024-04-16T15:10:07+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_describe_delete_not_existing_event_bus": { - "last_validated_date": "2024-04-17T07:32:19+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_list_event_buses_with_limit": { - "last_validated_date": "2024-04-03T14:53:31+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_put_events_nonexistent_event_bus": { - "last_validated_date": "2024-04-16T15:10:30+00:00" - }, - "tests/aws/services/events/test_events.py::TestEventsEventBus::test_put_events_to_default_eventbus_for_custom_eventbus": { - "last_validated_date": "2024-03-26T14:07:59+00:00" + "last_validated_date": "2024-04-29T13:15:31+00:00" } } From bad5497f7f031e4bc82fb82d321078b4af9ad381 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 16:11:07 +0200 Subject: [PATCH 20/28] fix: use dynamic event bus name --- tests/aws/services/events/test_events_rules.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/aws/services/events/test_events_rules.py b/tests/aws/services/events/test_events_rules.py index 648c395652871..1926910cc8261 100644 --- a/tests/aws/services/events/test_events_rules.py +++ b/tests/aws/services/events/test_events_rules.py @@ -14,7 +14,7 @@ from localstack.utils.sync import poll_condition from tests.aws.services.events.conftest import assert_valid_event, sqs_collect_messages from tests.aws.services.events.helper_functions import is_v2_provider -from tests.aws.services.events.test_events import TEST_EVENT_BUS_NAME, TEST_EVENT_PATTERN +from tests.aws.services.events.test_events import TEST_EVENT_PATTERN @markers.aws.validated @@ -239,6 +239,7 @@ def test_put_event_with_content_base_rule_in_pattern(aws_client, clean_up): queue_name = f"queue-{short_uid()}" rule_name = f"rule-{short_uid()}" target_id = f"target-{short_uid()}" + event_bus_name = f"event-bus-{short_uid()}" queue_url = aws_client.sqs.create_queue(QueueName=queue_name)["QueueUrl"] queue_arn = arns.sqs_queue_arn(queue_name, TEST_AWS_ACCOUNT_ID, TEST_AWS_REGION_NAME) @@ -272,7 +273,7 @@ def test_put_event_with_content_base_rule_in_pattern(aws_client, clean_up): } event = { - "EventBusName": TEST_EVENT_BUS_NAME, + "EventBusName": event_bus_name, "Source": "core.update-account-command", "DetailType": "core.app.backend", "Detail": json.dumps( @@ -299,16 +300,16 @@ def test_put_event_with_content_base_rule_in_pattern(aws_client, clean_up): ), } - aws_client.events.create_event_bus(Name=TEST_EVENT_BUS_NAME) + aws_client.events.create_event_bus(Name=event_bus_name) aws_client.events.put_rule( Name=rule_name, - EventBusName=TEST_EVENT_BUS_NAME, + EventBusName=event_bus_name, EventPattern=json.dumps(pattern), ) aws_client.events.put_targets( Rule=rule_name, - EventBusName=TEST_EVENT_BUS_NAME, + EventBusName=event_bus_name, Targets=[{"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}], ) aws_client.events.put_events(Entries=[event]) @@ -327,7 +328,7 @@ def test_put_event_with_content_base_rule_in_pattern(aws_client, clean_up): # clean up clean_up( - bus_name=TEST_EVENT_BUS_NAME, + bus_name=event_bus_name, rule_name=rule_name, target_ids=target_id, queue_url=queue_url, From 989becd504e3bd60f1652af6f393cfaa320403b7 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 29 Apr 2024 16:12:38 +0200 Subject: [PATCH 21/28] feat: move test event pattern to correct test file --- .../services/events/test_event_patterns.py | 45 +++++++++++++++++++ .../events/test_event_patterns.snapshot.json | 19 ++++++++ .../test_event_patterns.validation.json | 3 ++ 3 files changed, 67 insertions(+) diff --git a/tests/aws/services/events/test_event_patterns.py b/tests/aws/services/events/test_event_patterns.py index fee9902930c35..a739af6e9c5f4 100644 --- a/tests/aws/services/events/test_event_patterns.py +++ b/tests/aws/services/events/test_event_patterns.py @@ -1,5 +1,6 @@ import json import os +from datetime import datetime from pathlib import Path from typing import List, Tuple @@ -155,3 +156,47 @@ def test_test_event_pattern_with_escape_characters(aws_client): EventPattern=event_pattern, ) assert response["Result"] + + +@markers.aws.validated +def test_event_pattern_source(aws_client, snapshot, account_id, region_name): + response = aws_client.events.test_event_pattern( + Event=json.dumps( + { + "id": "1", + "source": "order", + "detail-type": "Test", + "account": account_id, + "region": region_name, + "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), + } + ), + EventPattern=json.dumps( + { + "source": ["order"], + "detail-type": ["Test"], + } + ), + ) + snapshot.match("eventbridge-test-event-pattern-response", response) + + # negative test, source is not matched + response = aws_client.events.test_event_pattern( + Event=json.dumps( + { + "id": "1", + "source": "order", + "detail-type": "Test", + "account": account_id, + "region": region_name, + "time": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), + } + ), + EventPattern=json.dumps( + { + "source": ["shipment"], + "detail-type": ["Test"], + } + ), + ) + snapshot.match("eventbridge-test-event-pattern-response-no-match", response) diff --git a/tests/aws/services/events/test_event_patterns.snapshot.json b/tests/aws/services/events/test_event_patterns.snapshot.json index 1f50547e900b6..39ed013c1b17e 100644 --- a/tests/aws/services/events/test_event_patterns.snapshot.json +++ b/tests/aws/services/events/test_event_patterns.snapshot.json @@ -430,5 +430,24 @@ "tests/aws/services/events/test_event_patterns.py::test_test_event_pattern[exists_dynamodb_NEG]": { "recorded-date": "09-04-2024, 16:51:59", "recorded-content": {} + }, + "tests/aws/services/events/test_event_patterns.py::test_event_pattern_source": { + "recorded-date": "29-04-2024, 14:12:14", + "recorded-content": { + "eventbridge-test-event-pattern-response": { + "Result": true, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "eventbridge-test-event-pattern-response-no-match": { + "Result": false, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/events/test_event_patterns.validation.json b/tests/aws/services/events/test_event_patterns.validation.json index ef63c055e6d26..d4bc0e6bd2f69 100644 --- a/tests/aws/services/events/test_event_patterns.validation.json +++ b/tests/aws/services/events/test_event_patterns.validation.json @@ -1,4 +1,7 @@ { + "tests/aws/services/events/test_event_patterns.py::test_event_pattern_source": { + "last_validated_date": "2024-04-29T14:12:14+00:00" + }, "tests/aws/services/events/test_event_patterns.py::test_test_event_pattern[arrays]": { "last_validated_date": "2024-04-08T19:33:55+00:00" }, From 3a99a863ae649f22c5b94f8d12dfb253292a2c95 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 6 May 2024 11:54:18 +0200 Subject: [PATCH 22/28] feat: use only java pattern matching engine --- localstack/services/events/models_v2.py | 4 +-- localstack/services/events/provider_v2.py | 25 +++++------------- localstack/services/events/rule.py | 32 ----------------------- 3 files changed, 8 insertions(+), 53 deletions(-) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index af7d874da326a..5800dc1d48ccd 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Any, Optional +from typing import Optional from localstack.aws.api.core import ServiceException from localstack.aws.api.events import ( @@ -59,8 +59,6 @@ def __post_init__(self): RuleDict = dict[RuleName, Rule] -EventPatternDict = dict[str, Any] - @dataclass class EventBus: diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index 590a91980e092..0d5f6fb67c1fd 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -353,15 +353,10 @@ def test_event_pattern( """Test event pattern uses EventBridge event pattern matching: https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html """ - if config.EVENT_RULE_ENGINE == "java": - try: - result = matches_rule(event, event_pattern) - except InternalInvalidEventPatternException as e: - raise InvalidEventPatternException(e.message) from e - else: - event_pattern_dict = json.loads(event_pattern) - event_dict = json.loads(event) - result = matches_event(event_pattern_dict, event_dict) + try: + result = matches_rule(event, event_pattern) + except InternalInvalidEventPatternException as e: + raise InvalidEventPatternException(e.message) from e return TestEventPatternResponse(Result=result) @@ -662,15 +657,9 @@ def _process_entries( continue matching_rules = [rule for rule in event_bus.rules.values()] for rule in matching_rules: - rule_service = self.get_rule_service(context, rule.name, event_bus_name) - event_pattern = rule_service.event_pattern - if config.EVENT_RULE_ENGINE == "java": - event_str = json.dumps(event) - event_pattern_str = json.dumps(event_pattern) - matches_result = matches_rule(event_str, event_pattern_str) - else: - matches_result = matches_event(event, event_pattern) - if matches_result: + event_pattern = rule.event_pattern + event_str = json.dumps(event) + if matches_rule(event_str, event_pattern): for target in rule.targets.values(): target_sender = self._target_sender_store[target["Arn"]] try: diff --git a/localstack/services/events/rule.py b/localstack/services/events/rule.py index ca7a3499fdecb..61011ccec6286 100644 --- a/localstack/services/events/rule.py +++ b/localstack/services/events/rule.py @@ -1,4 +1,3 @@ -import json import re from typing import Optional @@ -6,7 +5,6 @@ Arn, EventBusName, EventPattern, - InvalidEventPatternException, LimitExceededException, ManagedBy, PutTargetsResultEntryList, @@ -22,7 +20,6 @@ TargetList, ) from localstack.services.events.models_v2 import ( - EventPatternDict, Rule, TargetDict, ValidationException, @@ -66,7 +63,6 @@ def __init__( targets, managed_by, ) - self.event_pattern = self.load_event_pattern(event_pattern) @property def arn(self) -> Arn: @@ -163,19 +159,6 @@ def validate_targets_input(self, targets: TargetList) -> PutTargetsResultEntryLi return validation_errors - def load_event_pattern(self, raw_pattern: Optional[str]) -> EventPatternDict: - """Loads and validates an event pattern from a JSON string.""" - if raw_pattern is None: - return {} - - try: - pattern = json.loads(raw_pattern) - except json.JSONDecodeError: - raise InvalidEventPatternException(reason="Invalid JSON") - - self._validate_event_pattern(pattern) - return pattern - def _validate_input( self, event_pattern: Optional[EventPattern], @@ -203,20 +186,5 @@ def _check_target_limit_reached(self) -> bool: return True return False - def _validate_event_pattern(self, pattern): - """Validates that the event pattern is correctly structured.""" - for attr, value in pattern.items(): - if isinstance(value, dict): - self._validate_event_pattern(value) - elif isinstance(value, list): - if not value: - raise InvalidEventPatternException("Empty arrays are not allowed") - if not all(isinstance(item, (dict, str)) for item in value): - raise InvalidEventPatternException( - f"All items in '{attr}' array must be dictionaries or strings" - ) - else: - raise InvalidEventPatternException(f"'{attr}' must be an object or an array") - RuleServiceDict = dict[Arn, RuleService] From ca62eb2c942d2cce95e4051a22df08201da85386 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 6 May 2024 12:07:24 +0200 Subject: [PATCH 23/28] feat: remove java engin env variable check --- localstack/services/events/event_ruler.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/localstack/services/events/event_ruler.py b/localstack/services/events/event_ruler.py index a9210689566cd..0a7ef73eaf78c 100644 --- a/localstack/services/events/event_ruler.py +++ b/localstack/services/events/event_ruler.py @@ -3,7 +3,6 @@ from functools import cache from pathlib import Path -from localstack import config from localstack.services.events.packages import event_ruler_package from localstack.services.events.utils import InvalidEventPatternException from localstack.utils.objects import singleton_factory @@ -43,8 +42,6 @@ def matches_rule(event: str, rule: str) -> bool: There is a single static boolean method Ruler.matchesRule(event, rule) - both arguments are provided as JSON strings. """ - if config.EVENT_RULE_ENGINE != "java": - raise NotImplementedError("Set EVENT_RULE_ENGINE=java to enable the Java Event Ruler.") start_jvm() import jpype.imports # noqa F401: required for importing Java modules From 55eb484b1e3fb4d8c307589a1432864faea4f423 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 6 May 2024 13:40:40 +0200 Subject: [PATCH 24/28] feat: add event validation --- localstack/services/events/provider_v2.py | 51 ++++++++++++++++------- tests/aws/services/events/test_events.py | 2 + 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index 0d5f6fb67c1fd..68de5c2d3e143 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -4,7 +4,6 @@ from datetime import datetime, timezone from typing import Optional -from localstack import config from localstack.aws.api import RequestContext, handler from localstack.aws.api.events import ( Arn, @@ -29,6 +28,8 @@ PutEventsRequestEntry, PutEventsRequestEntryList, PutEventsResponse, + PutEventsResultEntry, + PutEventsResultEntryList, PutPartnerEventsRequestEntryList, PutPartnerEventsResponse, PutRuleResponse, @@ -67,7 +68,6 @@ ) from localstack.services.events.rule import RuleService, RuleServiceDict from localstack.services.events.target import TargetSender, TargetSenderDict, TargetSenderFactory -from localstack.services.events.utils import matches_event from localstack.services.plugins import ServiceLifecycleHook from localstack.utils.strings import long_uid @@ -105,6 +105,24 @@ def get_event_time(event: PutEventsRequestEntry) -> str: return formatted_time_string +def validate_event(event: PutEventsRequestEntry) -> None | PutEventsResultEntry: + if not event.get("Source"): + return { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument.", + } + elif not event.get("DetailType"): + return { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter DetailType is not valid. Reason: DetailType is a required argument.", + } + elif not event.get("Detail"): + return { + "ErrorCode": "InvalidArgument", + "ErrorMessage": "Parameter Detail is not valid. Reason: Detail is a required argument.", + } + + def format_event(event: PutEventsRequestEntry, region: str, account_id: str) -> dict: # See https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html formatted_event = { @@ -441,14 +459,12 @@ def put_events( endpoint_id: EndpointId = None, **kwargs, ) -> PutEventsResponse: - processed_entries_uuids, failed_entries = self._process_entries(context, entries) + entries, failed_entry_count = self._process_entries(context, entries) response = PutEventsResponse( - Entries=[{"EventId": id} for id in processed_entries_uuids], - FailedEntryCount=len(failed_entries), + Entries=entries, + FailedEntryCount=failed_entry_count, ) - if failed_entries: - response["FailedEntries"] = failed_entries return response @handler("PutPartnerEvents") @@ -642,18 +658,22 @@ def _delete_target_sender(self, ids: TargetIdList, rule) -> None: def _process_entries( self, context: RequestContext, entries: PutEventsRequestEntryList - ) -> tuple[list, list]: - processed_entries_uuids = [] - failed_entries = [] + ) -> tuple[PutEventsResultEntryList, int]: + processed_entries = [] + failed_entry_count = 0 for event in entries: event_bus_name = event.get("EventBusName", "default") + if event_failed_validation := validate_event(event): + processed_entries.append(event_failed_validation) + failed_entry_count += 1 + continue event = format_event(event, context.region, context.account_id) - processed_entries_uuids.append(event["id"]) store = self.get_store(context) try: event_bus = self.get_event_bus(event_bus_name, store) except ResourceNotFoundException: - # ignore events for non-existing event buses + # ignore events for non-existing event buses but add processed event + processed_entries.append({"EventId": event["id"]}) continue matching_rules = [rule for rule in event_bus.rules.values()] for rule in matching_rules: @@ -664,12 +684,13 @@ def _process_entries( target_sender = self._target_sender_store[target["Arn"]] try: target_sender.send_event(event) + processed_entries.append({"EventId": event["id"]}) except Exception as error: - failed_entries.append( + processed_entries.append( { - "Entry": event, "ErrorCode": "InternalException", "ErrorMessage": str(error), } ) - return processed_entries_uuids, failed_entries + failed_entry_count += 1 + return processed_entries, failed_entry_count diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 74ba7ff38679f..0af72a310011c 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1130,6 +1130,7 @@ def test_update_rule_with_targets( class TestEventPattern: @markers.aws.validated + @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_pattern_with_values_in_array(self, put_events_with_filter_to_sqs, snapshot): pattern = {"detail": {"event": {"data": {"type": ["1", "2"]}}}} entries1 = [ @@ -1169,6 +1170,7 @@ def test_put_events_pattern_with_values_in_array(self, put_events_with_filter_to snapshot.match("messages", messages) @markers.aws.validated + @pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_pattern_nested(self, put_events_with_filter_to_sqs, snapshot): pattern = {"detail": {"event": {"data": {"type": ["1"]}}}} entries1 = [ From 12efbacd537779ac0233069d80672635fdf53761 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 6 May 2024 15:48:15 +0200 Subject: [PATCH 25/28] feat: skip unvalidated failing test --- tests/aws/services/events/test_events_integrations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/aws/services/events/test_events_integrations.py b/tests/aws/services/events/test_events_integrations.py index 372b9f7e0a555..6fe71f709d10a 100644 --- a/tests/aws/services/events/test_events_integrations.py +++ b/tests/aws/services/events/test_events_integrations.py @@ -42,6 +42,7 @@ def test_put_events_with_target_sqs(put_events_with_filter_to_sqs, snapshot): @markers.aws.unknown +@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") def test_put_events_with_target_sqs_new_region(aws_client_factory): events_client = aws_client_factory(region_name="eu-west-1").events queue_name = "queue-{}".format(short_uid()) From b011d8a65e0df5b8a025b1eccf59e90a4b7e38ad Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Mon, 6 May 2024 16:12:15 +0200 Subject: [PATCH 26/28] fix: skip tests not supported by v1 provider --- tests/aws/services/events/test_events.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 0af72a310011c..865966378d057 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -82,6 +82,10 @@ class TestEvents: @markers.aws.validated + @pytest.mark.skipif( + not is_v2_provider(), + reason="V1 provider does not support this feature", + ) def test_put_events_without_source(self, snapshot, aws_client): entries = [ { @@ -93,6 +97,10 @@ def test_put_events_without_source(self, snapshot, aws_client): snapshot.match("put-events", response) @markers.aws.unknown + @pytest.mark.skipif( + not is_v2_provider(), + reason="V1 provider does not support this feature", + ) def test_put_event_without_detail(self, snapshot, aws_client): entries = [ { From 81d63c0bcf34bcf70377b1858bb736165682c209 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Tue, 7 May 2024 12:27:15 +0200 Subject: [PATCH 27/28] refactor: reuse existing error type definition --- localstack/services/events/models_v2.py | 8 -------- localstack/services/events/provider_v2.py | 4 +++- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/localstack/services/events/models_v2.py b/localstack/services/events/models_v2.py index 5800dc1d48ccd..89b232fe30291 100644 --- a/localstack/services/events/models_v2.py +++ b/localstack/services/events/models_v2.py @@ -94,11 +94,3 @@ class ValidationException(ServiceException): code: str = "ValidationException" sender_fault: bool = True status_code: int = 400 - - -class InternalInvalidEventPatternException(Exception): - reason: str - - def __init__(self, reason=None, message=None) -> None: - self.reason = reason - self.message = message or f"Event pattern is not valid. Reason: {reason}" diff --git a/localstack/services/events/provider_v2.py b/localstack/services/events/provider_v2.py index 68de5c2d3e143..2c8544b09ee0e 100644 --- a/localstack/services/events/provider_v2.py +++ b/localstack/services/events/provider_v2.py @@ -59,7 +59,6 @@ EventBus, EventBusDict, EventsStore, - InternalInvalidEventPatternException, Rule, RuleDict, TargetDict, @@ -68,6 +67,9 @@ ) from localstack.services.events.rule import RuleService, RuleServiceDict from localstack.services.events.target import TargetSender, TargetSenderDict, TargetSenderFactory +from localstack.services.events.utils import ( + InvalidEventPatternException as InternalInvalidEventPatternException, +) from localstack.services.plugins import ServiceLifecycleHook from localstack.utils.strings import long_uid From aae673a5e9ba24c572ec7e900b8cca1495846761 Mon Sep 17 00:00:00 2001 From: Maximilian Hoheiser Date: Tue, 7 May 2024 12:45:39 +0200 Subject: [PATCH 28/28] feat: update test markers --- tests/aws/services/events/test_events.py | 2 +- .../events/test_events_integrations.py | 21 +++++++++++++------ .../test_events_integrations.snapshot.json | 15 +++++++++++++ .../test_events_integrations.validation.json | 2 +- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 865966378d057..6b3c8ee9ec244 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -651,7 +651,7 @@ def test_list_event_buses_with_limit(self, create_event_bus, aws_client, snapsho ) snapshot.match("list-event-buses-limit-next-token", response) - @markers.aws.unknown + @markers.aws.needs_fixing # TODO use fixture setup_sqs_queue_as_event_target to simplify @pytest.mark.skipif(is_aws_cloud(), reason="not validated") @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) def test_put_events_into_event_bus( diff --git a/tests/aws/services/events/test_events_integrations.py b/tests/aws/services/events/test_events_integrations.py index 6fe71f709d10a..9c8659322d498 100644 --- a/tests/aws/services/events/test_events_integrations.py +++ b/tests/aws/services/events/test_events_integrations.py @@ -83,8 +83,9 @@ def test_put_events_with_target_sqs_new_region(aws_client_factory): assert "EventId" in response.get("Entries")[0] -@markers.aws.unknown -def test_put_events_with_target_sqs_event_detail_match(put_events_with_filter_to_sqs): +@markers.aws.validated +@pytest.mark.skipif(is_v2_provider(), reason="V2 provider does not support this feature yet") +def test_put_events_with_target_sqs_event_detail_match(put_events_with_filter_to_sqs, snapshot): entries1 = [ { "Source": TEST_EVENT_PATTERN["source"][0], @@ -100,17 +101,25 @@ def test_put_events_with_target_sqs_event_detail_match(put_events_with_filter_to } ] entries_asserts = [(entries1, True), (entries2, False)] - put_events_with_filter_to_sqs( + messages = put_events_with_filter_to_sqs( pattern={"detail": {"EventType": ["0", "1"]}}, entries_asserts=entries_asserts, input_path="$.detail", ) + snapshot.add_transformers_list( + [ + snapshot.transform.key_value("ReceiptHandle", reference_replacement=False), + snapshot.transform.key_value("MD5OfBody", reference_replacement=False), + ], + ) + snapshot.match("messages", messages) + # TODO: further unify/parameterize the tests for the different target types below -@markers.aws.unknown +@markers.aws.needs_fixing @pytest.mark.parametrize("strategy", ["standard", "domain", "path"]) def test_put_events_with_target_sns( monkeypatch, @@ -179,7 +188,7 @@ def test_put_events_with_target_sns( ) -@markers.aws.unknown +@markers.aws.needs_fixing def test_put_events_with_target_lambda(create_lambda_function, cleanups, aws_client, clean_up): rule_name = f"rule-{short_uid()}" function_name = f"lambda-func-{short_uid()}" @@ -484,7 +493,7 @@ def check_invocation(): retry(check_invocation, sleep=5, retries=15) -@markers.aws.unknown +@markers.aws.needs_fixing def test_put_events_with_target_firehose(aws_client, clean_up): s3_bucket = "s3-{}".format(short_uid()) s3_prefix = "testeventdata" diff --git a/tests/aws/services/events/test_events_integrations.snapshot.json b/tests/aws/services/events/test_events_integrations.snapshot.json index 9a4c20d2f2db9..e27cdb2a10e9e 100644 --- a/tests/aws/services/events/test_events_integrations.snapshot.json +++ b/tests/aws/services/events/test_events_integrations.snapshot.json @@ -129,5 +129,20 @@ } ] } + }, + "tests/aws/services/events/test_events_integrations.py::test_put_events_with_target_sqs_event_detail_match": { + "recorded-date": "07-05-2024, 10:40:38", + "recorded-content": { + "messages": [ + { + "MessageId": "", + "ReceiptHandle": "receipt-handle", + "MD5OfBody": "m-d5-of-body", + "Body": { + "EventType": "1" + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events_integrations.validation.json b/tests/aws/services/events/test_events_integrations.validation.json index 7de094e66181b..bf2860707799f 100644 --- a/tests/aws/services/events/test_events_integrations.validation.json +++ b/tests/aws/services/events/test_events_integrations.validation.json @@ -9,7 +9,7 @@ "last_validated_date": "2024-04-26T08:43:27+00:00" }, "tests/aws/services/events/test_events_integrations.py::test_put_events_with_target_sqs_event_detail_match": { - "last_validated_date": "2024-03-26T15:50:07+00:00" + "last_validated_date": "2024-05-07T10:40:38+00:00" }, "tests/aws/services/events/test_events_integrations.py::test_should_ignore_schedules_for_put_event": { "last_validated_date": "2024-03-26T15:51:47+00:00"