### Is there an existing issue for this?

- [X] I have searched the existing issues

### Enhancement description

Currently, I'm using Localstack to test locally a lambda function that takes s3`ObjectRestore:Completed` notifications as inputs and it would be really great to have support for these events.

I know that right now as a workaround I can invoke the lambda function manually using a payload with the same shape that s3 uses, but it's better to have the process run as close as it would run in AWS.

Thanks for creating and maintaining localstack, it's really great!

### 🧑‍💻 Implementation

Not sure, but happy to help if you can give me some pointers.

### Anything else?

_No response_
"""
        Fix parsing of CORS Rules from moto, you can set empty origin in AWS. Replace None by an empty string
        """
        cors_rules = fn(*args, **kwargs)
        for rule in cors_rules:
            if rule["AllowedOrigin"] is None:
                rule["AllowedOrigin"] = ""
        return cors_rules
    @patch(moto_s3_responses.S3Response.is_delete_keys)
    def s3_response_is_delete_keys(fn, self):
        """
        Old provider had a fix for a ticket, concerning 'x-id' - there is no documentation on AWS about this, but it is probably still valid
        original comment: Temporary fix until moto supports x-id and DeleteObjects (#3931)
        """
        return get_safe(self.querystring, "$.x-id.0") == "DeleteObjects" or fn(self)
    @patch(moto_s3_responses.S3ResponseInstance.parse_bucket_name_from_url, pass_target=False)
    def parse_bucket_name_from_url(self, request, url):
        """
        Requests going to moto will never be subdomain based, as they passed through the VirtualHost forwarder.
        We know the bucket is in the path, we can directly return it.
        """
        path = urlparse(url).path
        return path.split("/")[1]
    @patch(moto_s3_responses.S3ResponseInstance.subdomain_based_buckets, pass_target=False)
    def subdomain_based_buckets(self, request):