From d4602b9caeb683e40b6a4fe378c25ba4d68a4be1 Mon Sep 17 00:00:00 2001 From: Konstantinos Koukopoulos Date: Wed, 2 Apr 2014 19:03:40 +0300 Subject: [PATCH] support listing all multipart uploads --- moto/s3/models.py | 4 ++++ moto/s3/responses.py | 38 +++++++++++++++++++++++++++++++++++++- tests/test_s3/test_s3.py | 21 +++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) 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')