Include response headers when deleting objects. (#3313)

* Return delete meta.

* Add tests.

* Lint fixes.

Co-authored-by: Leo Sutic <leo.sutic@matterport.com>
This commit is contained in:
Leo Sutic 2020-09-15 14:29:09 +02:00 committed by GitHub
parent 9f0f230d13
commit 94543f6e48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 7 deletions

View File

@ -1620,7 +1620,9 @@ class S3Backend(BaseBackend):
def _set_delete_marker(self, bucket_name, key_name): def _set_delete_marker(self, bucket_name, key_name):
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
bucket.keys[key_name] = FakeDeleteMarker(key=bucket.keys[key_name]) delete_marker = FakeDeleteMarker(key=bucket.keys[key_name])
bucket.keys[key_name] = delete_marker
return delete_marker
def delete_object_tagging(self, bucket_name, key_name, version_id=None): def delete_object_tagging(self, bucket_name, key_name, version_id=None):
key = self.get_object(bucket_name, key_name, version_id=version_id) key = self.get_object(bucket_name, key_name, version_id=version_id)
@ -1630,15 +1632,26 @@ class S3Backend(BaseBackend):
key_name = clean_key_name(key_name) key_name = clean_key_name(key_name)
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
response_meta = {}
try: try:
if not bucket.is_versioned: if not bucket.is_versioned:
bucket.keys.pop(key_name) bucket.keys.pop(key_name)
else: else:
if version_id is None: if version_id is None:
self._set_delete_marker(bucket_name, key_name) delete_marker = self._set_delete_marker(bucket_name, key_name)
response_meta["version-id"] = delete_marker.version_id
else: else:
if key_name not in bucket.keys: if key_name not in bucket.keys:
raise KeyError raise KeyError
response_meta["delete-marker"] = "false"
for key in bucket.keys.getlist(key_name):
if str(key.version_id) == str(version_id):
if type(key) is FakeDeleteMarker:
response_meta["delete-marker"] = "true"
break
bucket.keys.setlist( bucket.keys.setlist(
key_name, key_name,
[ [
@ -1650,9 +1663,9 @@ class S3Backend(BaseBackend):
if not bucket.keys.getlist(key_name): if not bucket.keys.getlist(key_name):
bucket.keys.pop(key_name) bucket.keys.pop(key_name)
return True return True, response_meta
except KeyError: except KeyError:
return False return False, None
def copy_key( def copy_key(
self, self,

View File

@ -902,7 +902,7 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
key_name = object_["Key"] key_name = object_["Key"]
version_id = object_.get("VersionId", None) version_id = object_.get("VersionId", None)
success = self.backend.delete_object( success, _ = self.backend.delete_object(
bucket_name, undo_clean_key_name(key_name), version_id=version_id bucket_name, undo_clean_key_name(key_name), version_id=version_id
) )
if success: if success:
@ -1666,8 +1666,14 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
) )
template = self.response_template(S3_DELETE_KEY_TAGGING_RESPONSE) template = self.response_template(S3_DELETE_KEY_TAGGING_RESPONSE)
return 204, {}, template.render(version_id=version_id) return 204, {}, template.render(version_id=version_id)
self.backend.delete_object(bucket_name, key_name, version_id=version_id) success, response_meta = self.backend.delete_object(
return 204, {}, "" bucket_name, key_name, version_id=version_id
)
response_headers = {}
if response_meta is not None:
for k in response_meta:
response_headers["x-amz-{}".format(k)] = response_meta[k]
return 204, response_headers, ""
def _complete_multipart_body(self, body): def _complete_multipart_body(self, body):
ps = minidom.parseString(body).getElementsByTagName("Part") ps = minidom.parseString(body).getElementsByTagName("Part")

View File

@ -2315,6 +2315,30 @@ def test_boto3_delete_versioned_bucket():
client.delete_bucket(Bucket="blah") client.delete_bucket(Bucket="blah")
@mock_s3
def test_boto3_delete_versioned_bucket_returns_meta():
client = boto3.client("s3", region_name=DEFAULT_REGION_NAME)
client.create_bucket(Bucket="blah")
client.put_bucket_versioning(
Bucket="blah", VersioningConfiguration={"Status": "Enabled"}
)
put_resp = client.put_object(Bucket="blah", Key="test1", Body=b"test1")
# Delete the object
del_resp = client.delete_object(Bucket="blah", Key="test1")
assert "DeleteMarker" not in del_resp
assert del_resp["VersionId"] is not None
# Delete the delete marker
del_resp2 = client.delete_object(
Bucket="blah", Key="test1", VersionId=del_resp["VersionId"]
)
assert del_resp2["DeleteMarker"] == True
assert "VersionId" not in del_resp2
@mock_s3 @mock_s3
def test_boto3_get_object_if_modified_since(): def test_boto3_get_object_if_modified_since():
s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME) s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME)