Repository: getmoto/moto

Issue:
Feature: S3 list_object_versions Implement Prefix-argument

I'm trying to list list object version in moto mock_s3, but got an error saying Called get_bucket_versions with some of delimiter, encoding_type, key_marker, version_id_marker.
I saw encoding-type is been resolved, but both put_object and upload_file would not put in valid value for delimiter, key_marker, and version_id_marker.

```
@mock_s3
    def test_get_object_versions(self):
        s3_resource = boto3.resource('s3')
        s3_client = boto3.client('s3')
        s3_client.create_bucket(Bucket='test-bucket')
        bucket_versioning = s3_resource.BucketVersioning('test-bucket')
        bucket_versioning.enable()
        s3_client.upload_file('test-key','test-bucket','test-key')
        time.sleep(1)
        s3_client.upload_file('test-key','test-bucket','test-key')
        time.sleep(1)
        s3_client.upload_file('test-key','test-bucket','test-key')
        print(s3_client.list_object_versions(Bucket='test-bucket',Prefix='test'))
```

Traceback:
```
Traceback (most recent call last):
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/core/models.py", line 80, in wrapper
    result = func(*args, **kwargs)
  File "test_aws_firmware_python_library_s3_client.py", line 67, in test_get_object_versions
    print(s3_client.list_object_versions(Bucket='test-bucket',Prefix='test'))
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/client.py", line 316, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/client.py", line 622, in _make_api_call
    operation_model, request_dict, request_context)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/client.py", line 641, in _make_request
    return self._endpoint.make_request(operation_model, request_dict)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/endpoint.py", line 102, in make_request
    return self._send_request(request_dict, operation_model)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/endpoint.py", line 137, in _send_request
    success_response, exception):
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/endpoint.py", line 256, in _needs_retry
    caught_exception=caught_exception, request_dict=request_dict)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 356, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 228, in emit
    return self._emit(event_name, kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 211, in _emit
    response = handler(**kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 183, in __call__
    if self._checker(attempts, response, caught_exception):
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 251, in __call__
    caught_exception)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 269, in _should_retry
    return self._checker(attempt_number, response, caught_exception)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 317, in __call__
    caught_exception)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 223, in __call__
    attempt_number, caught_exception)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/retryhandler.py", line 359, in _check_caught_exception
    raise caught_exception
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/endpoint.py", line 197, in _do_get_response
    responses = self._event_emitter.emit(event_name, request=request)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 356, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 228, in emit
    return self._emit(event_name, kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/botocore/hooks.py", line 211, in _emit
    response = handler(**kwargs)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/core/models.py", line 298, in __call__
    status, headers, body = response_callback(request, request.url, request.headers)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/s3/responses.py", line 116, in bucket_response
    response = self._bucket_response(request, full_url, headers)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/s3/responses.py", line 155, in _bucket_response
    return self._bucket_response_get(bucket_name, querystring, headers)
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/s3/responses.py", line 284, in _bucket_response_get
    prefix=prefix
  File "/home/zhxny/workplace/AWSFirmwarePythonLibrary/env/AWSFirmwarePythonLibrary-1.0/runtime/lib/python3.6/site-packages/moto/s3/models.py", line 759, in get_bucket_versions
    "Called get_bucket_versions with some of delimiter, encoding_type, key_marker, version_id_marker")
NotImplementedError: Called get_bucket_versions with some of delimiter, encoding_type, key_marker, version_id_marker
```

Is there anyway to skip around this.
Thanks,


Discussion:
Hi @XinyuZhao0215, this should work without providing the Prefix-argument.
Will mark it as an enhancement to also make it work with the prefix

Resolving patch:
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -483,7 +483,7 @@ def _bucket_response_get(self, bucket_name, querystring):
                     delete_marker_list=delete_marker_list,
                     latest_versions=latest_versions,
                     bucket=bucket,
-                    prefix="",
+                    prefix=prefix,
                     max_keys=1000,
                     delimiter="",
                     is_truncated="false",


Test patch:
diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py
--- a/tests/test_s3/test_s3.py
+++ b/tests/test_s3/test_s3.py
@@ -5145,3 +5145,21 @@ def test_object_headers():
     res.should.have.key("ServerSideEncryption")
     res.should.have.key("SSEKMSKeyId")
     res.should.have.key("BucketKeyEnabled")
+
+
+@mock_s3
+def test_get_object_versions_with_prefix():
+    bucket_name = "testbucket-3113"
+    s3_resource = boto3.resource("s3")
+    s3_client = boto3.client("s3")
+    s3_client.create_bucket(Bucket=bucket_name)
+    bucket_versioning = s3_resource.BucketVersioning(bucket_name)
+    bucket_versioning.enable()
+    s3_client.put_object(Bucket=bucket_name, Body=b"test", Key="file.txt")
+    s3_client.put_object(Bucket=bucket_name, Body=b"test", Key="file.txt")
+    s3_client.put_object(Bucket=bucket_name, Body=b"alttest", Key="altfile.txt")
+    s3_client.put_object(Bucket=bucket_name, Body=b"test", Key="file.txt")
+
+    versions = s3_client.list_object_versions(Bucket=bucket_name, Prefix="file")
+    versions["Versions"].should.have.length_of(3)
+    versions["Prefix"].should.equal("file")