diff --git a/moto/s3/models.py b/moto/s3/models.py
index 76da68657..07aaee51b 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -219,6 +219,10 @@ class S3Backend(BaseBackend):
bucket = self.buckets[bucket_name]
return bucket.multiparts[multipart_id].list_parts()
+ def get_all_multiparts(self, bucket_name):
+ bucket = self.buckets[bucket_name]
+ return bucket.multiparts
+
def set_part(self, bucket_name, multipart_id, part_id, value):
bucket = self.buckets[bucket_name]
multipart = bucket.multiparts[multipart_id]
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index d24bfb3ff..d3bbce390 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -35,7 +35,7 @@ class ResponseObject(object):
def _bucket_response(self, request, full_url, headers):
parsed_url = urlparse(full_url)
- querystring = parse_qs(parsed_url.query)
+ querystring = parse_qs(parsed_url.query, keep_blank_values=True)
method = request.method
bucket_name = self.bucket_name_from_url(full_url)
@@ -64,6 +64,16 @@ class ResponseObject(object):
return 404, headers, ""
def _bucket_response_get(self, bucket_name, querystring, headers):
+ if 'uploads' in querystring:
+ for unsup in ('delimiter', 'prefix', 'max-uploads'):
+ if unsup in querystring:
+ raise NotImplementedError("Listing multipart uploads with {} has not been implemented yet.".format(unsup))
+ multiparts = list(self.backend.get_all_multiparts(bucket_name).itervalues())
+ template = Template(S3_ALL_MULTIPARTS)
+ return 200, headers, template.render(
+ bucket_name=bucket_name,
+ uploads=multiparts)
+
bucket = self.backend.get_bucket(bucket_name)
if bucket:
prefix = querystring.get('prefix', [None])[0]
@@ -460,3 +470,29 @@ S3_MULTIPART_COMPLETE_TOO_SMALL_ERROR = """asdfasdfsdafds
sdfgdsfgdsfgdfsdsfgdfs
"""
+
+S3_ALL_MULTIPARTS = """
+
+ {{ bucket_name }}
+
+
+ 1000
+ False
+ {% for upload in uploads %}
+
+ {{ upload.key_name }}
+ {{ upload.id }}
+
+ arn:aws:iam::111122223333:user/user1-11111a31-17b5-4fb7-9df5-b111111f13de
+ user1-11111a31-17b5-4fb7-9df5-b111111f13de
+
+
+ 75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a
+ OwnerDisplayName
+
+ STANDARD
+ 2010-11-10T20:48:33.000Z
+
+ {% endfor %}
+
+"""
diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py
index 9d9a5f063..e4775dfa8 100644
--- a/tests/test_s3/test_s3.py
+++ b/tests/test_s3/test_s3.py
@@ -127,6 +127,27 @@ def test_multipart_etag():
'"140f92a6df9f9e415f74a1463bcee9bb-2"')
+@mock_s3
+def test_list_multiparts():
+ # Create Bucket so that test can run
+ conn = boto.connect_s3('the_key', 'the_secret')
+ bucket = conn.create_bucket('mybucket')
+
+ multipart1 = bucket.initiate_multipart_upload("one-key")
+ multipart2 = bucket.initiate_multipart_upload("two-key")
+ uploads = bucket.get_all_multipart_uploads()
+ uploads.should.have.length_of(2)
+ dict([(u.key_name, u.id) for u in uploads]).should.equal(
+ {'one-key': multipart1.id, 'two-key': multipart2.id})
+ multipart2.cancel_upload()
+ uploads = bucket.get_all_multipart_uploads()
+ uploads.should.have.length_of(1)
+ uploads[0].key_name.should.equal("one-key")
+ multipart1.cancel_upload()
+ uploads = bucket.get_all_multipart_uploads()
+ uploads.should.be.empty
+
+
@mock_s3
def test_missing_key():
conn = boto.connect_s3('the_key', 'the_secret')