Fix S3 bucket list objects order and delimiters. Closes #14.
This commit is contained in:
parent
98d7a50790
commit
be26daaff4
@ -87,20 +87,22 @@ class S3Backend(BaseBackend):
|
|||||||
if bucket:
|
if bucket:
|
||||||
return bucket.keys.get(key_name)
|
return bucket.keys.get(key_name)
|
||||||
|
|
||||||
def prefix_query(self, bucket, prefix):
|
def prefix_query(self, bucket, prefix, delimiter):
|
||||||
key_results = set()
|
key_results = set()
|
||||||
folder_results = set()
|
folder_results = set()
|
||||||
if prefix:
|
if prefix:
|
||||||
for key_name, key in bucket.keys.iteritems():
|
for key_name, key in bucket.keys.iteritems():
|
||||||
if key_name.startswith(prefix):
|
if key_name.startswith(prefix):
|
||||||
if '/' in key_name.lstrip(prefix):
|
if delimiter and '/' in key_name.lstrip(prefix):
|
||||||
key_without_prefix = key_name.lstrip(prefix).split("/")[0]
|
# If delimiter, we need to split out folder_results
|
||||||
|
key_without_prefix = "{}/".format(key_name.lstrip(prefix).split("/")[0])
|
||||||
folder_results.add("{}{}".format(prefix, key_without_prefix))
|
folder_results.add("{}{}".format(prefix, key_without_prefix))
|
||||||
else:
|
else:
|
||||||
key_results.add(key)
|
key_results.add(key)
|
||||||
else:
|
else:
|
||||||
for key_name, key in bucket.keys.iteritems():
|
for key_name, key in bucket.keys.iteritems():
|
||||||
if '/' in key_name:
|
if delimiter and '/' in key_name:
|
||||||
|
# If delimiter, we need to split out folder_results
|
||||||
folder_results.add(key_name.split("/")[0])
|
folder_results.add(key_name.split("/")[0])
|
||||||
else:
|
else:
|
||||||
key_results.add(key)
|
key_results.add(key)
|
||||||
|
@ -27,11 +27,13 @@ def bucket_response(uri, method, body, headers):
|
|||||||
bucket = s3_backend.get_bucket(bucket_name)
|
bucket = s3_backend.get_bucket(bucket_name)
|
||||||
if bucket:
|
if bucket:
|
||||||
prefix = querystring.get('prefix', [None])[0]
|
prefix = querystring.get('prefix', [None])[0]
|
||||||
result_keys, result_folders = s3_backend.prefix_query(bucket, prefix)
|
delimiter = querystring.get('delimiter')
|
||||||
|
result_keys, result_folders = s3_backend.prefix_query(bucket, prefix, delimiter)
|
||||||
template = Template(S3_BUCKET_GET_RESPONSE)
|
template = Template(S3_BUCKET_GET_RESPONSE)
|
||||||
return template.render(
|
return template.render(
|
||||||
bucket=bucket,
|
bucket=bucket,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
|
delimiter=delimiter,
|
||||||
result_keys=result_keys,
|
result_keys=result_keys,
|
||||||
result_folders=result_folders
|
result_folders=result_folders
|
||||||
)
|
)
|
||||||
@ -128,7 +130,7 @@ S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
|||||||
<Name>{{ bucket.name }}</Name>
|
<Name>{{ bucket.name }}</Name>
|
||||||
<Prefix>{{ prefix }}</Prefix>
|
<Prefix>{{ prefix }}</Prefix>
|
||||||
<MaxKeys>1000</MaxKeys>
|
<MaxKeys>1000</MaxKeys>
|
||||||
<Delimiter>/</Delimiter>
|
<Delimiter>{{ delimiter }}</Delimiter>
|
||||||
<IsTruncated>false</IsTruncated>
|
<IsTruncated>false</IsTruncated>
|
||||||
{% for key in result_keys %}
|
{% for key in result_keys %}
|
||||||
<Contents>
|
<Contents>
|
||||||
@ -144,11 +146,13 @@ S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
|||||||
<StorageClass>STANDARD</StorageClass>
|
<StorageClass>STANDARD</StorageClass>
|
||||||
</Contents>
|
</Contents>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for folder in result_folders %}
|
{% if delimiter %}
|
||||||
<CommonPrefixes>
|
{% for folder in result_folders %}
|
||||||
<Prefix>{{ folder }}</Prefix>
|
<CommonPrefixes>
|
||||||
</CommonPrefixes>
|
<Prefix>{{ folder }}</Prefix>
|
||||||
{% endfor %}
|
</CommonPrefixes>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</ListBucketResult>"""
|
</ListBucketResult>"""
|
||||||
|
|
||||||
S3_BUCKET_CREATE_RESPONSE = """<CreateBucketResponse xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
S3_BUCKET_CREATE_RESPONSE = """<CreateBucketResponse xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||||
|
@ -108,42 +108,6 @@ def test_last_modified():
|
|||||||
bucket.get_key("the-key").last_modified.should.equal('Sun, 01 Jan 2012 12:00:00 GMT')
|
bucket.get_key("the-key").last_modified.should.equal('Sun, 01 Jan 2012 12:00:00 GMT')
|
||||||
|
|
||||||
|
|
||||||
@mock_s3
|
|
||||||
def test_get_all_keys():
|
|
||||||
conn = boto.connect_s3('the_key', 'the_secret')
|
|
||||||
bucket = conn.create_bucket("foobar")
|
|
||||||
key = Key(bucket)
|
|
||||||
key.key = "the-key"
|
|
||||||
key.set_contents_from_string("some value")
|
|
||||||
|
|
||||||
key2 = Key(bucket)
|
|
||||||
key2.key = "folder/some-stuff"
|
|
||||||
key2.set_contents_from_string("some value")
|
|
||||||
|
|
||||||
key3 = Key(bucket)
|
|
||||||
key3.key = "folder/more-folder/foobar"
|
|
||||||
key3.set_contents_from_string("some value")
|
|
||||||
|
|
||||||
key4 = Key(bucket)
|
|
||||||
key4.key = "a-key"
|
|
||||||
key4.set_contents_from_string("some value")
|
|
||||||
|
|
||||||
keys = bucket.get_all_keys()
|
|
||||||
keys.should.have.length_of(3)
|
|
||||||
|
|
||||||
keys[0].name.should.equal("a-key")
|
|
||||||
keys[1].name.should.equal("the-key")
|
|
||||||
|
|
||||||
# Prefix
|
|
||||||
keys[2].name.should.equal("folder")
|
|
||||||
|
|
||||||
keys = bucket.get_all_keys(prefix="folder/")
|
|
||||||
keys.should.have.length_of(2)
|
|
||||||
|
|
||||||
keys[0].name.should.equal("folder/some-stuff")
|
|
||||||
keys[1].name.should.equal("folder/more-folder")
|
|
||||||
|
|
||||||
|
|
||||||
@mock_s3
|
@mock_s3
|
||||||
def test_missing_bucket():
|
def test_missing_bucket():
|
||||||
conn = boto.connect_s3('the_key', 'the_secret')
|
conn = boto.connect_s3('the_key', 'the_secret')
|
||||||
@ -218,3 +182,37 @@ def test_key_with_special_characters():
|
|||||||
key_list = bucket.list('test_list_keys_2/', '/')
|
key_list = bucket.list('test_list_keys_2/', '/')
|
||||||
keys = [x for x in key_list]
|
keys = [x for x in key_list]
|
||||||
keys[0].name.should.equal("test_list_keys_2/x?y")
|
keys[0].name.should.equal("test_list_keys_2/x?y")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_bucket_key_listing_order():
|
||||||
|
conn = boto.connect_s3()
|
||||||
|
bucket = conn.create_bucket('test_bucket')
|
||||||
|
prefix = 'toplevel/'
|
||||||
|
|
||||||
|
def store(name):
|
||||||
|
k = Key(bucket, prefix + name)
|
||||||
|
k.set_contents_from_string('somedata')
|
||||||
|
|
||||||
|
names = ['x/key', 'y.key1', 'y.key2', 'y.key3', 'x/y/key', 'x/y/z/key']
|
||||||
|
|
||||||
|
for name in names:
|
||||||
|
store(name)
|
||||||
|
|
||||||
|
delimiter = None
|
||||||
|
keys = [x.name for x in bucket.list(prefix, delimiter)]
|
||||||
|
keys.should.equal([
|
||||||
|
'toplevel/x/key', 'toplevel/x/y/key', 'toplevel/x/y/z/key',
|
||||||
|
'toplevel/y.key1', 'toplevel/y.key2', 'toplevel/y.key3'
|
||||||
|
])
|
||||||
|
|
||||||
|
delimiter = '/'
|
||||||
|
keys = [x.name for x in bucket.list(prefix, delimiter)]
|
||||||
|
keys.should.equal([
|
||||||
|
'toplevel/y.key1', 'toplevel/y.key2', 'toplevel/y.key3', 'toplevel/x/'
|
||||||
|
])
|
||||||
|
|
||||||
|
# Test delimiter with no prefix
|
||||||
|
delimiter = '/'
|
||||||
|
keys = [x.name for x in bucket.list(prefix=None, delimiter=delimiter)]
|
||||||
|
keys.should.equal(['toplevel'])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user