Repository: getmoto/moto

Issue:
Missing RequestId From mocked S3 service API response
This was using `botocore` at `1.19.12` and `moto` at `1.3.16`

# What Actually Happened

When I use `moto` to mock `s3`, I expected to see `RequestId` in the `ResponseMetadata`. This was my setup:

```
@mock_s3
def test_s3_put(self):
    params = dict(Key="foo", Bucket="mybucket", Body=b"bar")
    s3 = self.session.create_client("s3", region_name="us-west-2")
    location = {"LocationConstraint": "us-west-2"}
    s3.create_bucket(Bucket="mybucket", CreateBucketConfiguration=location)
    s3.put_object(**params)
    response = s3.get_object(Bucket="mybucket", Key="foo")
    print('Response to S3 GetObject: ', response)
```

However, when I printed out the response, I only saw the following:

```
{
    'ResponseMetadata': {
        'HTTPStatusCode': 200, 
        'HTTPHeaders': {
            'content-md5': 'N7UdGUp1E+RbVvZSTy1R8g==',
            'etag': '"37b51d194a7513e45b56f6524f2d51f2"',
            'last-modified': 'Fri, 06 Nov 2020 23:29:30 GMT',
            'content-length': '3'
        },
        'RetryAttempts': 0
    }, 
    'LastModified': datetime.datetime(2020,11,6,23,29,30,tzinfo=tzutc()), 
    'ContentLength': 3, 
    'ETag': '"37b51d194a7513e45b56f6524f2d51f2"', 
    'Metadata': {}, 
    'Body': <botocore.response.StreamingBody object at 0x10722f460>
}
```

# What I Expected to Happen

This is compared to my sample application where I do this:
```
s3_client = boto3.client('s3')
response = s3_client.get_object(Bucket='mybucket', Key='my_file')
print(response)
```


And I get a response like this from S3:
```
{
    'ResponseMetadata': {
        'RequestId': 'FF5D971A11D3B6D6',
        'HostId': 'jiUP1wkSGUjx2OyXDfOzhNZhfxjYUW91FI+V1pURh8QeDCMj+9bsxRhUXLANylOlkHh4llr/COU=',
        'HTTPStatusCode': 200,
        'HTTPHeaders': {
            'x-amz-id-2': 'jiUP1wkSGUjx2OyXDfOzhNZhfxjYUW91FI+V1pURh8QeDCMj+9bsxRhUXLANylOlkHh4llr/COU=',
            'x-amz-request-id': 'FF5D971A11D3B6D6',
            'date': 'Fri,
            06 Nov 2020 23:24:12 GMT',
            'last-modified': 'Fri,
            09 Oct 2020 04:36:48 GMT',
            'etag': '"e336233045ad091013c89ed3d6463de1"',
            'accept-ranges': 'bytes',
            'content-type': 'binary/octet-stream',
            'content-length': '63',
            'server': 'AmazonS3'
        },
        'RetryAttempts': 0
    },
    'AcceptRanges': 'bytes',
    'LastModified': datetime.datetime(2020, 10, 9, 4, 36, 48, tzinfo=tzutc()),
    'ContentLength': 63,
    'ETag': '"e336233045ad091013c89ed3d6463de1"',
    'ContentType': 'binary/octet-stream',
    'Metadata': {},
    'Body': <botocore.response.StreamingBody object at 0x10e380e20>
}
```


Please let me know if I'm missing something here.


Discussion:
Hi @NathanielRN, thanks for raising this!
In other services we tend to use the `@amzn_request_id`-annotation to automatically add a RequestId to every response, but that doesn't happen in S3. Marking it as an enhancement!
@bblommers Awesome! Thank you for your quick and helpful answer :) Really appreciate it!

Resolving patch:
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -8,6 +8,7 @@
 from botocore.awsrequest import AWSPreparedRequest
 
 from moto.core.utils import (
+    amzn_request_id,
     str_to_rfc_1123_datetime,
     py2_strip_unicode_keys,
     unix_time_millis,
@@ -258,6 +259,7 @@ def ambiguous_response(self, request, full_url, headers):
             # Using path-based buckets
             return self.bucket_response(request, full_url, headers)
 
+    @amzn_request_id
     def bucket_response(self, request, full_url, headers):
         self.method = request.method
         self.path = self._get_path(request)
@@ -1025,6 +1027,7 @@ def _handle_v4_chunk_signatures(self, body, content_length):
             line = body_io.readline()
         return bytes(new_body)
 
+    @amzn_request_id
     def key_or_control_response(self, request, full_url, headers):
         # Key and Control are lumped in because splitting out the regex is too much of a pain :/
         self.method = request.method


Test patch:
diff --git a/tests/test_s3/test_s3_metadata.py b/tests/test_s3/test_s3_metadata.py
new file mode 100644
--- /dev/null
+++ b/tests/test_s3/test_s3_metadata.py
@@ -0,0 +1,27 @@
+import boto3
+
+from moto import mock_s3
+from moto.s3.responses import DEFAULT_REGION_NAME
+
+import sure  # noqa
+
+
+@mock_s3
+def test_s3_returns_requestid():
+    s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME)
+    resp = s3.create_bucket(Bucket="mybucket")
+    _check_metadata(resp)
+
+    resp = s3.put_object(Bucket="mybucket", Key="steve", Body=b"is awesome")
+    _check_metadata(resp)
+
+    resp = s3.get_object(Bucket="mybucket", Key="steve")
+    _check_metadata(resp)
+
+
+def _check_metadata(resp):
+    meta = resp["ResponseMetadata"]
+    headers = meta["HTTPHeaders"]
+    meta.should.have.key("RequestId")
+    headers.should.have.key("x-amzn-requestid")
+    meta["RequestId"].should.equal(headers["x-amzn-requestid"])