We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
EventBridge PutEvents not working when Detail is missing
# Type of request: This is a ...

[X ] bug report
[ ] feature request

# Detailed description
Calling PutEvents operation returns 500 if no `Detail` is specified. This case is similar to #3043

## Expected behavior
According to the doc https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html `Details` is optional. 

```
 Detail
    A valid JSON string. There is no other schema imposed. The JSON string may contain fields and nested subobjects.
    Type: String
    Required: No
```

So this should works and set `Detail` as `{}`

```
eb_client = boto3.client('events', endpoint_url='http://localhost:4587')
eb_client.put_events(
    Entries=[
        {
            'DetailType': 'Test'
        }
    ]
)
```

## Actual behavior
500 is returned

# Steps to reproduce

1. Start LocalStack with SERVICES=events
2. Run the client code

## Command used to start LocalStack

```
docker run --rm --name localstack -p 4587:4587 -e SERVICES=events -e DEBUG=true localstack/localstack
```

## Client code (AWS SDK code snippet, or sequence of "awslocal" commands)

CLI:

```
aws events put-events --endpoint-url http://localhost:4587 --entries '[{"DetailType": "Test"}]'
```

Or via python:

```
eb_client = boto3.client('events', endpoint_url='http://localhost:4587')
eb_client.put_events(
    Entries=[
        {
            'DetailType': 'Test',
            'Detail': '{}'
        }
    ]
)
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `localstack/services/events/events_starter.py`
Content:
```
1 import re
2 import datetime
3 import json
4 import uuid
5 import logging
6 import ipaddress
7 from moto.events.models import Rule as rule_model
8 from moto.events.responses import EventsHandler as events_handler
9 from localstack import config
10 from localstack.constants import APPLICATION_AMZ_JSON_1_1, TEST_AWS_ACCOUNT_ID
11 from localstack.utils.aws import aws_stack
12 from localstack.utils.common import short_uid, extract_jsonpath
13 from localstack.services.infra import start_moto_server
14 from localstack.services.events.scheduler import JobScheduler
15 from localstack.services.events.events_listener import _create_and_register_temp_dir, _dump_events_to_files
16 
17 
18 LOG = logging.getLogger(__name__)
19 
20 DEFAULT_EVENT_BUS_NAME = 'default'
21 
22 # Event rules storage
23 EVENT_RULES = {
24     DEFAULT_EVENT_BUS_NAME: set()
25 }
26 
27 CONTENT_BASE_FILTER_KEYWORDS = [
28     'prefix', 'anything-but', 'numeric', 'cidr', 'exists'
29 ]
30 
31 
32 def filter_event_with_target_input_path(target, event):
33     input_path = target.get('InputPath')
34     if input_path:
35         event = extract_jsonpath(event, input_path)
36     return event
37 
38 
39 def filter_event_based_on_event_format(self, rule, event):
40     def filter_event(event_pattern, event):
41         for key, value in event_pattern.items():
42             event_value = event.get(key.lower())
43             if not event_value:
44                 return False
45 
46             if isinstance(value, list) and not identify_content_base_parameter_in_pattern(value):
47                 if isinstance(event_value, list) and \
48                    get_two_lists_intersection(value, event_value) == []:
49                     return False
50                 elif not isinstance(event_value, list) and \
51                         isinstance(event_value, (str, int)) and \
52                         event_value not in value:
53                     return False
54 
55             elif isinstance(value, list) and identify_content_base_parameter_in_pattern(value):
56                 if not filter_event_with_content_base_parameter(value, event_value):
57                     return False
58 
59             elif isinstance(value, (str, int)):
60                 try:
61                     if isinstance(json.loads(value), dict) and \
62                        not filter_event(json.loads(value), event_value):
63                         return False
64                 except json.decoder.JSONDecodeError:
65                     return False
66         return True
67 
68     rule_information = self.events_backend.describe_rule(rule)
69     if rule_information.event_pattern:
70         event_pattern = json.loads(rule_information.event_pattern)
71         if not filter_event(event_pattern, event):
72             return False
73     return True
74 
75 
76 def process_events(event, targets):
77     for target in targets:
78         arn = target['Arn']
79         changed_event = filter_event_with_target_input_path(target, event)
80         aws_stack.send_event_to_target(arn, changed_event)
81 
82 
83 def apply_patches():
84     # Fix events arn
85     def rule_model_generate_arn(self, name):
86         return 'arn:aws:events:{region_name}:{account_id}:rule/{name}'.format(
87             region_name=self.region_name, account_id=TEST_AWS_ACCOUNT_ID, name=name
88         )
89 
90     events_handler_put_rule_orig = events_handler.put_rule
91 
92     def events_handler_put_rule(self):
93         name = self._get_param('Name')
94         event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME
95 
96         if event_bus not in EVENT_RULES:
97             EVENT_RULES[event_bus] = set()
98 
99         EVENT_RULES[event_bus].add(name)
100 
101         return events_handler_put_rule_orig(self)
102 
103     events_handler_delete_rule_orig = events_handler.delete_rule
104 
105     def events_handler_delete_rule(self):
106         name = self._get_param('Name')
107         event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME
108 
109         rules_set = EVENT_RULES.get(event_bus, set())
110         if name not in rules_set:
111             return self.error('ValidationException', 'Rule "%s" not found for event bus "%s"' % (name, event_bus))
112         rules_set.remove(name)
113 
114         return events_handler_delete_rule_orig(self)
115 
116     # 2101 Events put-targets does not respond
117     def events_handler_put_targets(self):
118         rule_name = self._get_param('Rule')
119         targets = self._get_param('Targets')
120 
121         if not rule_name:
122             return self.error('ValidationException', 'Parameter Rule is required.')
123 
124         if not targets:
125             return self.error('ValidationException', 'Parameter Targets is required.')
126 
127         if not self.events_backend.put_targets(rule_name, targets):
128             return self.error(
129                 'ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.'
130             )
131 
132         return json.dumps({'FailedEntryCount': 0, 'FailedEntries': []}), self.response_headers
133 
134     def events_handler_put_events(self):
135         entries = self._get_param('Entries')
136         events = list(
137             map(lambda event: {'event': event, 'uuid': str(uuid.uuid4())}, entries)
138         )
139 
140         _create_and_register_temp_dir()
141         _dump_events_to_files(events)
142 
143         for event_envelope in events:
144             event = event_envelope['event']
145             event_bus = event.get('EventBusName') or DEFAULT_EVENT_BUS_NAME
146 
147             rules = EVENT_RULES.get(event_bus, [])
148 
149             formatted_event = {
150                 'version': '0',
151                 'id': event_envelope['uuid'],
152                 'detail-type': event.get('DetailType'),
153                 'source': event.get('Source'),
154                 'account': TEST_AWS_ACCOUNT_ID,
155                 'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
156                 'region': self.region,
157                 'resources': event.get('Resources', []),
158                 'detail': json.loads(event.get('Detail')),
159             }
160 
161             targets = []
162             for rule in rules:
163                 if filter_event_based_on_event_format(self, rule, formatted_event):
164                     targets.extend(self.events_backend.list_targets_by_rule(rule)['Targets'])
165 
166             # process event
167             process_events(formatted_event, targets)
168 
169         content = {
170             'Entries': list(map(lambda event: {'EventId': event['uuid']}, events))
171         }
172 
173         self.response_headers.update({
174             'Content-Type': APPLICATION_AMZ_JSON_1_1,
175             'x-amzn-RequestId': short_uid()
176         })
177 
178         return json.dumps(content), self.response_headers
179 
180     rule_model._generate_arn = rule_model_generate_arn
181     events_handler.put_rule = events_handler_put_rule
182     events_handler.delete_rule = events_handler_delete_rule
183     events_handler.put_targets = events_handler_put_targets
184     events_handler.put_events = events_handler_put_events
185 
186 
187 def start_scheduler():
188     JobScheduler.start()
189 
190 
191 def start_events(port=None, asynchronous=None, update_listener=None):
192     port = port or config.PORT_EVENTS
193 
194     apply_patches()
195     start_scheduler()
196 
197     return start_moto_server(
198         key='events',
199         port=port,
200         name='Cloudwatch Events',
201         asynchronous=asynchronous,
202         update_listener=update_listener
203     )
204 
205 
206 # ---------------
207 # HELPER METHODS
208 # ---------------
209 
210 
211 def get_two_lists_intersection(lst1, lst2):
212     lst3 = [value for value in lst1 if value in lst2]
213     return lst3
214 
215 
216 def identify_content_base_parameter_in_pattern(parameters):
217     if any([list(param.keys())[0] in CONTENT_BASE_FILTER_KEYWORDS for param in parameters if isinstance(param, dict)]):
218         return True
219 
220 
221 def filter_event_with_content_base_parameter(pattern_value, event_value):
222     for element in pattern_value:
223         if (isinstance(element, (str, int))) \
224            and (event_value == element or element in event_value):
225             return True
226         elif isinstance(element, dict):
227             element_key = list(element.keys())[0]
228             element_value = element.get(element_key)
229             if element_key.lower() == 'prefix':
230                 if re.match(r'^{}'.format(element_value), event_value):
231                     return True
232             elif element_key.lower() == 'exists':
233                 if element_value and event_value:
234                     return True
235                 elif not element_value and not event_value:
236                     return True
237             elif element_key.lower() == 'cidr':
238                 ips = [str(ip) for ip in ipaddress.IPv4Network(element_value)]
239                 if event_value in ips:
240                     return True
241             elif element_key.lower() == 'numeric':
242                 if check_valid_numeric_content_base_rule(element_value):
243                     for index in range(len(element_value)):
244                         if isinstance(element_value[index], int):
245                             continue
246                         if element_value[index] == '>' and \
247                             isinstance(element_value[index + 1], int) and \
248                                 event_value <= element_value[index + 1]:
249                             break
250                         elif element_value[index] == '>=' and \
251                             isinstance(element_value[index + 1], int) and \
252                                 event_value < element_value[index + 1]:
253                             break
254                         elif element_value[index] == '<' and \
255                             isinstance(element_value[index + 1], int) and \
256                                 event_value >= element_value[index + 1]:
257                             break
258                         elif element_value[index] == '<=' and \
259                             isinstance(element_value[index + 1], int) and \
260                                 event_value > element_value[index + 1]:
261                             break
262                     else:
263                         return True
264 
265             elif element_key.lower() == 'anything-but':
266                 if isinstance(element_value, list) and \
267                         event_value not in element_value:
268                     return True
269                 elif (isinstance(element_value, (str, int))) and \
270                         event_value != element_value:
271                     return True
272                 elif isinstance(element_value, dict):
273                     nested_key = list(element_value)[0]
274                     if nested_key == 'prefix' and \
275                             not re.match(r'^{}'.format(element_value.get(nested_key)), event_value):
276                         return True
277     return False
278 
279 
280 def check_valid_numeric_content_base_rule(list_of_operators):
281     if len(list_of_operators) > 4:
282         return False
283 
284     if '=' in list_of_operators:
285         return False
286 
287     if len(list_of_operators) > 2:
288         upper_limit = None
289         lower_limit = None
290         for index in range(len(list_of_operators)):
291             if not isinstance(list_of_operators[index], int) and \
292                '<' in list_of_operators[index]:
293                 upper_limit = list_of_operators[index + 1]
294             if not isinstance(list_of_operators[index], int) and \
295                '>' in list_of_operators[index]:
296                 lower_limit = list_of_operators[index + 1]
297             if upper_limit and lower_limit and upper_limit < lower_limit:
298                 return False
299             index = index + 1
300     return True
301
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/localstack/services/events/events_starter.py b/localstack/services/events/events_starter.py
--- a/localstack/services/events/events_starter.py
+++ b/localstack/services/events/events_starter.py
@@ -155,7 +155,7 @@
                 'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
                 'region': self.region,
                 'resources': event.get('Resources', []),
-                'detail': json.loads(event.get('Detail')),
+                'detail': json.loads(event.get('Detail', '{}')),
             }
 
             targets = []

{"golden_diff": "diff --git a/localstack/services/events/events_starter.py b/localstack/services/events/events_starter.py\n--- a/localstack/services/events/events_starter.py\n+++ b/localstack/services/events/events_starter.py\n@@ -155,7 +155,7 @@\n                 'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n                 'region': self.region,\n                 'resources': event.get('Resources', []),\n-                'detail': json.loads(event.get('Detail')),\n+                'detail': json.loads(event.get('Detail', '{}')),\n             }\n \n             targets = []\n", "issue": "EventBridge PutEvents not working when Detail is missing\n# Type of request: This is a ...\r\n\r\n[X ] bug report\r\n[ ] feature request\r\n\r\n# Detailed description\r\nCalling PutEvents operation returns 500 if no `Detail` is specified. This case is similar to #3043\r\n\r\n## Expected behavior\r\nAccording to the doc https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html `Details` is optional. \r\n\r\n```\r\n Detail\r\n    A valid JSON string. There is no other schema imposed. The JSON string may contain fields and nested subobjects.\r\n    Type: String\r\n    Required: No\r\n```\r\n\r\nSo this should works and set `Detail` as `{}`\r\n\r\n```\r\neb_client = boto3.client('events', endpoint_url='http://localhost:4587')\r\neb_client.put_events(\r\n    Entries=[\r\n        {\r\n            'DetailType': 'Test'\r\n        }\r\n    ]\r\n)\r\n```\r\n\r\n## Actual behavior\r\n500 is returned\r\n\r\n# Steps to reproduce\r\n\r\n1. Start LocalStack with SERVICES=events\r\n2. Run the client code\r\n\r\n## Command used to start LocalStack\r\n\r\n```\r\ndocker run --rm --name localstack -p 4587:4587 -e SERVICES=events -e DEBUG=true localstack/localstack\r\n```\r\n\r\n## Client code (AWS SDK code snippet, or sequence of \"awslocal\" commands)\r\n\r\nCLI:\r\n\r\n```\r\naws events put-events --endpoint-url http://localhost:4587 --entries '[{\"DetailType\": \"Test\"}]'\r\n```\r\n\r\nOr via python:\r\n\r\n```\r\neb_client = boto3.client('events', endpoint_url='http://localhost:4587')\r\neb_client.put_events(\r\n    Entries=[\r\n        {\r\n            'DetailType': 'Test',\r\n            'Detail': '{}'\r\n        }\r\n    ]\r\n)\r\n```\r\n\n", "before_files": [{"content": "import re\nimport datetime\nimport json\nimport uuid\nimport logging\nimport ipaddress\nfrom moto.events.models import Rule as rule_model\nfrom moto.events.responses import EventsHandler as events_handler\nfrom localstack import config\nfrom localstack.constants import APPLICATION_AMZ_JSON_1_1, TEST_AWS_ACCOUNT_ID\nfrom localstack.utils.aws import aws_stack\nfrom localstack.utils.common import short_uid, extract_jsonpath\nfrom localstack.services.infra import start_moto_server\nfrom localstack.services.events.scheduler import JobScheduler\nfrom localstack.services.events.events_listener import _create_and_register_temp_dir, _dump_events_to_files\n\n\nLOG = logging.getLogger(__name__)\n\nDEFAULT_EVENT_BUS_NAME = 'default'\n\n# Event rules storage\nEVENT_RULES = {\n    DEFAULT_EVENT_BUS_NAME: set()\n}\n\nCONTENT_BASE_FILTER_KEYWORDS = [\n    'prefix', 'anything-but', 'numeric', 'cidr', 'exists'\n]\n\n\ndef filter_event_with_target_input_path(target, event):\n    input_path = target.get('InputPath')\n    if input_path:\n        event = extract_jsonpath(event, input_path)\n    return event\n\n\ndef filter_event_based_on_event_format(self, rule, event):\n    def filter_event(event_pattern, event):\n        for key, value in event_pattern.items():\n            event_value = event.get(key.lower())\n            if not event_value:\n                return False\n\n            if isinstance(value, list) and not identify_content_base_parameter_in_pattern(value):\n                if isinstance(event_value, list) and \\\n                   get_two_lists_intersection(value, event_value) == []:\n                    return False\n                elif not isinstance(event_value, list) and \\\n                        isinstance(event_value, (str, int)) and \\\n                        event_value not in value:\n                    return False\n\n            elif isinstance(value, list) and identify_content_base_parameter_in_pattern(value):\n                if not filter_event_with_content_base_parameter(value, event_value):\n                    return False\n\n            elif isinstance(value, (str, int)):\n                try:\n                    if isinstance(json.loads(value), dict) and \\\n                       not filter_event(json.loads(value), event_value):\n                        return False\n                except json.decoder.JSONDecodeError:\n                    return False\n        return True\n\n    rule_information = self.events_backend.describe_rule(rule)\n    if rule_information.event_pattern:\n        event_pattern = json.loads(rule_information.event_pattern)\n        if not filter_event(event_pattern, event):\n            return False\n    return True\n\n\ndef process_events(event, targets):\n    for target in targets:\n        arn = target['Arn']\n        changed_event = filter_event_with_target_input_path(target, event)\n        aws_stack.send_event_to_target(arn, changed_event)\n\n\ndef apply_patches():\n    # Fix events arn\n    def rule_model_generate_arn(self, name):\n        return 'arn:aws:events:{region_name}:{account_id}:rule/{name}'.format(\n            region_name=self.region_name, account_id=TEST_AWS_ACCOUNT_ID, name=name\n        )\n\n    events_handler_put_rule_orig = events_handler.put_rule\n\n    def events_handler_put_rule(self):\n        name = self._get_param('Name')\n        event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n        if event_bus not in EVENT_RULES:\n            EVENT_RULES[event_bus] = set()\n\n        EVENT_RULES[event_bus].add(name)\n\n        return events_handler_put_rule_orig(self)\n\n    events_handler_delete_rule_orig = events_handler.delete_rule\n\n    def events_handler_delete_rule(self):\n        name = self._get_param('Name')\n        event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n        rules_set = EVENT_RULES.get(event_bus, set())\n        if name not in rules_set:\n            return self.error('ValidationException', 'Rule \"%s\" not found for event bus \"%s\"' % (name, event_bus))\n        rules_set.remove(name)\n\n        return events_handler_delete_rule_orig(self)\n\n    # 2101 Events put-targets does not respond\n    def events_handler_put_targets(self):\n        rule_name = self._get_param('Rule')\n        targets = self._get_param('Targets')\n\n        if not rule_name:\n            return self.error('ValidationException', 'Parameter Rule is required.')\n\n        if not targets:\n            return self.error('ValidationException', 'Parameter Targets is required.')\n\n        if not self.events_backend.put_targets(rule_name, targets):\n            return self.error(\n                'ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.'\n            )\n\n        return json.dumps({'FailedEntryCount': 0, 'FailedEntries': []}), self.response_headers\n\n    def events_handler_put_events(self):\n        entries = self._get_param('Entries')\n        events = list(\n            map(lambda event: {'event': event, 'uuid': str(uuid.uuid4())}, entries)\n        )\n\n        _create_and_register_temp_dir()\n        _dump_events_to_files(events)\n\n        for event_envelope in events:\n            event = event_envelope['event']\n            event_bus = event.get('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n            rules = EVENT_RULES.get(event_bus, [])\n\n            formatted_event = {\n                'version': '0',\n                'id': event_envelope['uuid'],\n                'detail-type': event.get('DetailType'),\n                'source': event.get('Source'),\n                'account': TEST_AWS_ACCOUNT_ID,\n                'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n                'region': self.region,\n                'resources': event.get('Resources', []),\n                'detail': json.loads(event.get('Detail')),\n            }\n\n            targets = []\n            for rule in rules:\n                if filter_event_based_on_event_format(self, rule, formatted_event):\n                    targets.extend(self.events_backend.list_targets_by_rule(rule)['Targets'])\n\n            # process event\n            process_events(formatted_event, targets)\n\n        content = {\n            'Entries': list(map(lambda event: {'EventId': event['uuid']}, events))\n        }\n\n        self.response_headers.update({\n            'Content-Type': APPLICATION_AMZ_JSON_1_1,\n            'x-amzn-RequestId': short_uid()\n        })\n\n        return json.dumps(content), self.response_headers\n\n    rule_model._generate_arn = rule_model_generate_arn\n    events_handler.put_rule = events_handler_put_rule\n    events_handler.delete_rule = events_handler_delete_rule\n    events_handler.put_targets = events_handler_put_targets\n    events_handler.put_events = events_handler_put_events\n\n\ndef start_scheduler():\n    JobScheduler.start()\n\n\ndef start_events(port=None, asynchronous=None, update_listener=None):\n    port = port or config.PORT_EVENTS\n\n    apply_patches()\n    start_scheduler()\n\n    return start_moto_server(\n        key='events',\n        port=port,\n        name='Cloudwatch Events',\n        asynchronous=asynchronous,\n        update_listener=update_listener\n    )\n\n\n# ---------------\n# HELPER METHODS\n# ---------------\n\n\ndef get_two_lists_intersection(lst1, lst2):\n    lst3 = [value for value in lst1 if value in lst2]\n    return lst3\n\n\ndef identify_content_base_parameter_in_pattern(parameters):\n    if any([list(param.keys())[0] in CONTENT_BASE_FILTER_KEYWORDS for param in parameters if isinstance(param, dict)]):\n        return True\n\n\ndef filter_event_with_content_base_parameter(pattern_value, event_value):\n    for element in pattern_value:\n        if (isinstance(element, (str, int))) \\\n           and (event_value == element or element in event_value):\n            return True\n        elif isinstance(element, dict):\n            element_key = list(element.keys())[0]\n            element_value = element.get(element_key)\n            if element_key.lower() == 'prefix':\n                if re.match(r'^{}'.format(element_value), event_value):\n                    return True\n            elif element_key.lower() == 'exists':\n                if element_value and event_value:\n                    return True\n                elif not element_value and not event_value:\n                    return True\n            elif element_key.lower() == 'cidr':\n                ips = [str(ip) for ip in ipaddress.IPv4Network(element_value)]\n                if event_value in ips:\n                    return True\n            elif element_key.lower() == 'numeric':\n                if check_valid_numeric_content_base_rule(element_value):\n                    for index in range(len(element_value)):\n                        if isinstance(element_value[index], int):\n                            continue\n                        if element_value[index] == '>' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value <= element_value[index + 1]:\n                            break\n                        elif element_value[index] == '>=' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value < element_value[index + 1]:\n                            break\n                        elif element_value[index] == '<' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value >= element_value[index + 1]:\n                            break\n                        elif element_value[index] == '<=' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value > element_value[index + 1]:\n                            break\n                    else:\n                        return True\n\n            elif element_key.lower() == 'anything-but':\n                if isinstance(element_value, list) and \\\n                        event_value not in element_value:\n                    return True\n                elif (isinstance(element_value, (str, int))) and \\\n                        event_value != element_value:\n                    return True\n                elif isinstance(element_value, dict):\n                    nested_key = list(element_value)[0]\n                    if nested_key == 'prefix' and \\\n                            not re.match(r'^{}'.format(element_value.get(nested_key)), event_value):\n                        return True\n    return False\n\n\ndef check_valid_numeric_content_base_rule(list_of_operators):\n    if len(list_of_operators) > 4:\n        return False\n\n    if '=' in list_of_operators:\n        return False\n\n    if len(list_of_operators) > 2:\n        upper_limit = None\n        lower_limit = None\n        for index in range(len(list_of_operators)):\n            if not isinstance(list_of_operators[index], int) and \\\n               '<' in list_of_operators[index]:\n                upper_limit = list_of_operators[index + 1]\n            if not isinstance(list_of_operators[index], int) and \\\n               '>' in list_of_operators[index]:\n                lower_limit = list_of_operators[index + 1]\n            if upper_limit and lower_limit and upper_limit < lower_limit:\n                return False\n            index = index + 1\n    return True\n", "path": "localstack/services/events/events_starter.py"}], "after_files": [{"content": "import re\nimport datetime\nimport json\nimport uuid\nimport logging\nimport ipaddress\nfrom moto.events.models import Rule as rule_model\nfrom moto.events.responses import EventsHandler as events_handler\nfrom localstack import config\nfrom localstack.constants import APPLICATION_AMZ_JSON_1_1, TEST_AWS_ACCOUNT_ID\nfrom localstack.utils.aws import aws_stack\nfrom localstack.utils.common import short_uid, extract_jsonpath\nfrom localstack.services.infra import start_moto_server\nfrom localstack.services.events.scheduler import JobScheduler\nfrom localstack.services.events.events_listener import _create_and_register_temp_dir, _dump_events_to_files\n\n\nLOG = logging.getLogger(__name__)\n\nDEFAULT_EVENT_BUS_NAME = 'default'\n\n# Event rules storage\nEVENT_RULES = {\n    DEFAULT_EVENT_BUS_NAME: set()\n}\n\nCONTENT_BASE_FILTER_KEYWORDS = [\n    'prefix', 'anything-but', 'numeric', 'cidr', 'exists'\n]\n\n\ndef filter_event_with_target_input_path(target, event):\n    input_path = target.get('InputPath')\n    if input_path:\n        event = extract_jsonpath(event, input_path)\n    return event\n\n\ndef filter_event_based_on_event_format(self, rule, event):\n    def filter_event(event_pattern, event):\n        for key, value in event_pattern.items():\n            event_value = event.get(key.lower())\n            if not event_value:\n                return False\n\n            if isinstance(value, list) and not identify_content_base_parameter_in_pattern(value):\n                if isinstance(event_value, list) and \\\n                   get_two_lists_intersection(value, event_value) == []:\n                    return False\n                elif not isinstance(event_value, list) and \\\n                        isinstance(event_value, (str, int)) and \\\n                        event_value not in value:\n                    return False\n\n            elif isinstance(value, list) and identify_content_base_parameter_in_pattern(value):\n                if not filter_event_with_content_base_parameter(value, event_value):\n                    return False\n\n            elif isinstance(value, (str, int)):\n                try:\n                    if isinstance(json.loads(value), dict) and \\\n                       not filter_event(json.loads(value), event_value):\n                        return False\n                except json.decoder.JSONDecodeError:\n                    return False\n        return True\n\n    rule_information = self.events_backend.describe_rule(rule)\n    if rule_information.event_pattern:\n        event_pattern = json.loads(rule_information.event_pattern)\n        if not filter_event(event_pattern, event):\n            return False\n    return True\n\n\ndef process_events(event, targets):\n    for target in targets:\n        arn = target['Arn']\n        changed_event = filter_event_with_target_input_path(target, event)\n        aws_stack.send_event_to_target(arn, changed_event)\n\n\ndef apply_patches():\n    # Fix events arn\n    def rule_model_generate_arn(self, name):\n        return 'arn:aws:events:{region_name}:{account_id}:rule/{name}'.format(\n            region_name=self.region_name, account_id=TEST_AWS_ACCOUNT_ID, name=name\n        )\n\n    events_handler_put_rule_orig = events_handler.put_rule\n\n    def events_handler_put_rule(self):\n        name = self._get_param('Name')\n        event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n        if event_bus not in EVENT_RULES:\n            EVENT_RULES[event_bus] = set()\n\n        EVENT_RULES[event_bus].add(name)\n\n        return events_handler_put_rule_orig(self)\n\n    events_handler_delete_rule_orig = events_handler.delete_rule\n\n    def events_handler_delete_rule(self):\n        name = self._get_param('Name')\n        event_bus = self._get_param('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n        rules_set = EVENT_RULES.get(event_bus, set())\n        if name not in rules_set:\n            return self.error('ValidationException', 'Rule \"%s\" not found for event bus \"%s\"' % (name, event_bus))\n        rules_set.remove(name)\n\n        return events_handler_delete_rule_orig(self)\n\n    # 2101 Events put-targets does not respond\n    def events_handler_put_targets(self):\n        rule_name = self._get_param('Rule')\n        targets = self._get_param('Targets')\n\n        if not rule_name:\n            return self.error('ValidationException', 'Parameter Rule is required.')\n\n        if not targets:\n            return self.error('ValidationException', 'Parameter Targets is required.')\n\n        if not self.events_backend.put_targets(rule_name, targets):\n            return self.error(\n                'ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.'\n            )\n\n        return json.dumps({'FailedEntryCount': 0, 'FailedEntries': []}), self.response_headers\n\n    def events_handler_put_events(self):\n        entries = self._get_param('Entries')\n        events = list(\n            map(lambda event: {'event': event, 'uuid': str(uuid.uuid4())}, entries)\n        )\n\n        _create_and_register_temp_dir()\n        _dump_events_to_files(events)\n\n        for event_envelope in events:\n            event = event_envelope['event']\n            event_bus = event.get('EventBusName') or DEFAULT_EVENT_BUS_NAME\n\n            rules = EVENT_RULES.get(event_bus, [])\n\n            formatted_event = {\n                'version': '0',\n                'id': event_envelope['uuid'],\n                'detail-type': event.get('DetailType'),\n                'source': event.get('Source'),\n                'account': TEST_AWS_ACCOUNT_ID,\n                'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),\n                'region': self.region,\n                'resources': event.get('Resources', []),\n                'detail': json.loads(event.get('Detail', '{}')),\n            }\n\n            targets = []\n            for rule in rules:\n                if filter_event_based_on_event_format(self, rule, formatted_event):\n                    targets.extend(self.events_backend.list_targets_by_rule(rule)['Targets'])\n\n            # process event\n            process_events(formatted_event, targets)\n\n        content = {\n            'Entries': list(map(lambda event: {'EventId': event['uuid']}, events))\n        }\n\n        self.response_headers.update({\n            'Content-Type': APPLICATION_AMZ_JSON_1_1,\n            'x-amzn-RequestId': short_uid()\n        })\n\n        return json.dumps(content), self.response_headers\n\n    rule_model._generate_arn = rule_model_generate_arn\n    events_handler.put_rule = events_handler_put_rule\n    events_handler.delete_rule = events_handler_delete_rule\n    events_handler.put_targets = events_handler_put_targets\n    events_handler.put_events = events_handler_put_events\n\n\ndef start_scheduler():\n    JobScheduler.start()\n\n\ndef start_events(port=None, asynchronous=None, update_listener=None):\n    port = port or config.PORT_EVENTS\n\n    apply_patches()\n    start_scheduler()\n\n    return start_moto_server(\n        key='events',\n        port=port,\n        name='Cloudwatch Events',\n        asynchronous=asynchronous,\n        update_listener=update_listener\n    )\n\n\n# ---------------\n# HELPER METHODS\n# ---------------\n\n\ndef get_two_lists_intersection(lst1, lst2):\n    lst3 = [value for value in lst1 if value in lst2]\n    return lst3\n\n\ndef identify_content_base_parameter_in_pattern(parameters):\n    if any([list(param.keys())[0] in CONTENT_BASE_FILTER_KEYWORDS for param in parameters if isinstance(param, dict)]):\n        return True\n\n\ndef filter_event_with_content_base_parameter(pattern_value, event_value):\n    for element in pattern_value:\n        if (isinstance(element, (str, int))) \\\n           and (event_value == element or element in event_value):\n            return True\n        elif isinstance(element, dict):\n            element_key = list(element.keys())[0]\n            element_value = element.get(element_key)\n            if element_key.lower() == 'prefix':\n                if re.match(r'^{}'.format(element_value), event_value):\n                    return True\n            elif element_key.lower() == 'exists':\n                if element_value and event_value:\n                    return True\n                elif not element_value and not event_value:\n                    return True\n            elif element_key.lower() == 'cidr':\n                ips = [str(ip) for ip in ipaddress.IPv4Network(element_value)]\n                if event_value in ips:\n                    return True\n            elif element_key.lower() == 'numeric':\n                if check_valid_numeric_content_base_rule(element_value):\n                    for index in range(len(element_value)):\n                        if isinstance(element_value[index], int):\n                            continue\n                        if element_value[index] == '>' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value <= element_value[index + 1]:\n                            break\n                        elif element_value[index] == '>=' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value < element_value[index + 1]:\n                            break\n                        elif element_value[index] == '<' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value >= element_value[index + 1]:\n                            break\n                        elif element_value[index] == '<=' and \\\n                            isinstance(element_value[index + 1], int) and \\\n                                event_value > element_value[index + 1]:\n                            break\n                    else:\n                        return True\n\n            elif element_key.lower() == 'anything-but':\n                if isinstance(element_value, list) and \\\n                        event_value not in element_value:\n                    return True\n                elif (isinstance(element_value, (str, int))) and \\\n                        event_value != element_value:\n                    return True\n                elif isinstance(element_value, dict):\n                    nested_key = list(element_value)[0]\n                    if nested_key == 'prefix' and \\\n                            not re.match(r'^{}'.format(element_value.get(nested_key)), event_value):\n                        return True\n    return False\n\n\ndef check_valid_numeric_content_base_rule(list_of_operators):\n    if len(list_of_operators) > 4:\n        return False\n\n    if '=' in list_of_operators:\n        return False\n\n    if len(list_of_operators) > 2:\n        upper_limit = None\n        lower_limit = None\n        for index in range(len(list_of_operators)):\n            if not isinstance(list_of_operators[index], int) and \\\n               '<' in list_of_operators[index]:\n                upper_limit = list_of_operators[index + 1]\n            if not isinstance(list_of_operators[index], int) and \\\n               '>' in list_of_operators[index]:\n                lower_limit = list_of_operators[index + 1]\n            if upper_limit and lower_limit and upper_limit < lower_limit:\n                return False\n            index = index + 1\n    return True\n", "path": "localstack/services/events/events_starter.py"}]}