Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion alibabacloud_oss_v2/models/object_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,15 +1043,76 @@ def __init__(
"name": "Object"
}

class ObjectIdentifier(serde.Model):
"""The identifier of an object to delete."""

_attribute_map = {
"key": {"tag": "xml", "rename": "Key", "required": True},
"version_id": {"tag": "xml", "rename": "VersionId"},
}

_xml_map = {
"name": "Object"
}

def __init__(
self,
key: Optional[str] = None,
version_id: Optional[str] = None,
**kwargs: Any
) -> None:
"""
Args:
key (str, required): The name of the object.
version_id (str, optional): The version ID of the object.
"""
super().__init__(**kwargs)
self.key = key
self.version_id = version_id


class Delete(serde.Model):
"""The container for the objects to delete."""

_attribute_map = {
"objects": {"tag": "xml", "rename": "Object", "type": "[ObjectIdentifier]"},
"quiet": {"tag": "xml", "rename": "Quiet", "type": "bool"},
}

_xml_map = {
"name": "Delete"
}

_dependency_map = {
"ObjectIdentifier": {"new": lambda: ObjectIdentifier()},
}

def __init__(
self,
objects: Optional[List[ObjectIdentifier]] = None,
quiet: Optional[bool] = None,
**kwargs: Any
) -> None:
"""
Args:
objects (List[ObjectIdentifier], optional): The list of objects to delete.
quiet (bool, optional): Specifies whether to enable the Quiet return mode.
"""
super().__init__(**kwargs)
self.objects = objects
self.quiet = quiet


class DeleteMultipleObjectsRequest(serde.RequestModel):
"""The request for the DeleteMultipleObjects operation."""

_attribute_map = {
"bucket": {"tag": "input", "position": "host", "required": True},
"encoding_type": {"tag": "input", "position": "query", "rename": "encoding-type"},
"objects": {"tag": "input", "position": "nop", "required": True},
"objects": {"tag": "input", "position": "nop"},
"quiet": {"tag": "input", "position": "nop"},
"request_payer": {"tag": "input", "position": "header", "rename": "x-oss-request-payer"},
"delete": {"tag": "input", "position": "body", "rename": "nop"},
}

def __init__(
Expand All @@ -1061,23 +1122,28 @@ def __init__(
objects: Optional[List[DeleteObject]] = None,
quiet: Optional[bool] = None,
request_payer: Optional[str] = None,
delete: Optional[Delete] = None,
**kwargs: Any
) -> None:
"""
Args:
bucket (str, required): The name of the bucket.
encoding_type (str, optional): The encoding type of the object names in the response. Valid value: url
objects ([DeleteObject], optional): The container that stores information about you want to delete objects.
This parameter is deprecated. Use 'delete' parameter instead.
quiet (bool, optional): Specifies whether to enable the Quiet return mode.
The DeleteMultipleObjects operation provides the following return modes: Valid value: true,false
This parameter is deprecated. Use 'delete' parameter instead.
request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs.
delete (Delete, optional): The container that stores information about you want to delete objects.
"""
super().__init__(**kwargs)
self.bucket = bucket
self.encoding_type = encoding_type
self.objects = objects
self.quiet = quiet
self.request_payer = request_payer
self.delete = delete


class DeletedInfo(serde.Model):
Expand Down
54 changes: 41 additions & 13 deletions alibabacloud_oss_v2/serde_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,20 +235,48 @@ def serialize_delete_objects(request: serde.Model, op_input: OperationInput) ->
if not isinstance(request, DeleteMultipleObjectsRequest):
raise exceptions.SerializationError(error=f'Unsupport type {type(request)}')

# Check if both old and new parameters are set or neither is set
has_old_params = request.objects is not None
has_new_params = request.delete is not None

if (has_old_params and has_new_params) or (not has_old_params and not has_new_params):
raise exceptions.SerializationError(
error='Either old parameters (objects, quiet) or new parameter (delete) must be set, but not both'
)

xml = '<Delete>'
if request.quiet is not None:
xml += f'<Quiet>{"true" if request.quiet else "false"}</Quiet>'

if isinstance(request.objects, List):
for _, o in enumerate(request.objects):
xml += '<Object>'
key = utils.safety_str(o.key)
if len(key) > 0:
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
vid = utils.safety_str(o.version_id)
if len(vid) > 0:
xml += f'<VersionId>{vid}</VersionId>'
xml += '</Object>'

# Handle new parameter (delete)
if has_new_params:
if request.delete.quiet is not None:
xml += f'<Quiet>{"true" if request.delete.quiet else "false"}</Quiet>'

if isinstance(request.delete.objects, List):
for _, o in enumerate(request.delete.objects):
xml += '<Object>'
key = utils.safety_str(o.key)
if len(key) > 0:
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
vid = utils.safety_str(o.version_id)
if len(vid) > 0:
xml += f'<VersionId>{vid}</VersionId>'
xml += '</Object>'

# Handle old parameters (objects, quiet)
elif has_old_params:
if request.quiet is not None:
xml += f'<Quiet>{"true" if request.quiet else "false"}</Quiet>'

if isinstance(request.objects, List):
for _, o in enumerate(request.objects):
xml += '<Object>'
key = utils.safety_str(o.key)
if len(key) > 0:
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
vid = utils.safety_str(o.version_id)
if len(vid) > 0:
xml += f'<VersionId>{vid}</VersionId>'
xml += '</Object>'

xml += '</Delete>'

Expand Down
25 changes: 18 additions & 7 deletions sample/delete_multiple_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,27 @@ def main():

client = oss.Client(cfg)

# If deleting multiple items, please follow the following format
# If deleting multiple items, please follow the following format (deprecated)
# objects = [oss.DeleteObject(key=args.key), oss.DeleteObject(key=args.key2)],

# result = client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
# bucket=args.bucket,
# encoding_type='url',
# objects=[oss.DeleteObject(key=args.key)],
# ))

# New mode using Delete parameter
delete_request = oss.Delete(
objects=[
oss.ObjectIdentifier(key=args.key)
],
quiet=False
)

result = client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
bucket=args.bucket,
encoding_type='url',
objects=[oss.DeleteObject(key=args.key)],
delete=delete_request,
))

print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' key: {result.deleted_objects[0].key},'
Expand All @@ -44,5 +56,4 @@ def main():


if __name__ == "__main__":
main()

main()
76 changes: 76 additions & 0 deletions tests/integration/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,82 @@ def test_delete_multiple_objects(self):
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

def test_delete_multiple_objects_with_delete_parameter(self):
# Test the new Delete parameter mode
length = 1234
data = random_str(length)
key1 = OBJECTNAME_PREFIX + random_str(16)
key2 = OBJECTNAME_PREFIX + random_str(16)

# Put objects to be deleted
result1 = self.client.put_object(oss.PutObjectRequest(
bucket=self.bucket_name,
key=key1,
body=data,
))
self.assertIsNotNone(result1)
self.assertEqual(200, result1.status_code)

result2 = self.client.put_object(oss.PutObjectRequest(
bucket=self.bucket_name,
key=key2,
body=data,
))
self.assertIsNotNone(result2)
self.assertEqual(200, result2.status_code)

# Delete multiple objects using the new Delete parameter
delete_request = oss.Delete(
objects=[
oss.ObjectIdentifier(key=key1),
oss.ObjectIdentifier(key=key2)
],
quiet=False
)

result = self.client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
bucket=self.bucket_name,
delete=delete_request,
))

self.assertIsInstance(result, oss.DeleteMultipleObjectsResult)
self.assertEqual(200, result.status_code)
self.assertIsNotNone(result.headers.get('x-oss-request-id'))
self.assertEqual(2, len(result.deleted_objects))

deleted_keys = [obj.key for obj in result.deleted_objects]
self.assertIn(key1, deleted_keys)
self.assertIn(key2, deleted_keys)

# Verify objects are deleted
try:
self.client.head_object(oss.HeadObjectRequest(
bucket=self.bucket_name,
key=key1,
))
self.fail("Should have raised an exception")
except Exception as err:
self.assertIsInstance(err, oss.exceptions.OperationError)
err = cast(oss.exceptions.OperationError, err)
serr = err.unwrap()
self.assertIsInstance(serr, oss.exceptions.ServiceError)
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

try:
self.client.head_object(oss.HeadObjectRequest(
bucket=self.bucket_name,
key=key2,
))
self.fail("Should have raised an exception")
except Exception as err:
self.assertIsInstance(err, oss.exceptions.OperationError)
err = cast(oss.exceptions.OperationError, err)
serr = err.unwrap()
self.assertIsInstance(serr, oss.exceptions.ServiceError)
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

def test_restore_object(self):
length = 123
data = random_str(length)
Expand Down
76 changes: 76 additions & 0 deletions tests/integration/test_object_basic_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,82 @@ async def test_delete_multiple_objects(self):
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

async def test_delete_multiple_objects_with_delete_parameter(self):
# Test the new Delete parameter mode
length = 1234
data = random_str(length)
key1 = OBJECTNAME_PREFIX + random_str(16)
key2 = OBJECTNAME_PREFIX + random_str(16)

# Put objects to be deleted
result1 = await self.async_client.put_object(oss.PutObjectRequest(
bucket=self.bucket_name,
key=key1,
body=data,
))
self.assertIsNotNone(result1)
self.assertEqual(200, result1.status_code)

result2 = await self.async_client.put_object(oss.PutObjectRequest(
bucket=self.bucket_name,
key=key2,
body=data,
))
self.assertIsNotNone(result2)
self.assertEqual(200, result2.status_code)

# Delete multiple objects using the new Delete parameter
delete_request = oss.Delete(
objects=[
oss.ObjectIdentifier(key=key1),
oss.ObjectIdentifier(key=key2)
],
quiet=False
)

result = await self.async_client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
bucket=self.bucket_name,
delete=delete_request,
))

self.assertIsInstance(result, oss.DeleteMultipleObjectsResult)
self.assertEqual(200, result.status_code)
self.assertIsNotNone(result.headers.get('x-oss-request-id'))
self.assertEqual(2, len(result.deleted_objects))

deleted_keys = [obj.key for obj in result.deleted_objects]
self.assertIn(key1, deleted_keys)
self.assertIn(key2, deleted_keys)

# Verify objects are deleted
try:
await self.async_client.head_object(oss.HeadObjectRequest(
bucket=self.bucket_name,
key=key1,
))
self.fail("Should have raised an exception")
except Exception as err:
self.assertIsInstance(err, oss.exceptions.OperationError)
err = cast(oss.exceptions.OperationError, err)
serr = err.unwrap()
self.assertIsInstance(serr, oss.exceptions.ServiceError)
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

try:
await self.async_client.head_object(oss.HeadObjectRequest(
bucket=self.bucket_name,
key=key2,
))
self.fail("Should have raised an exception")
except Exception as err:
self.assertIsInstance(err, oss.exceptions.OperationError)
err = cast(oss.exceptions.OperationError, err)
serr = err.unwrap()
self.assertIsInstance(serr, oss.exceptions.ServiceError)
serr = cast(oss.exceptions.ServiceError, serr)
self.assertIn('NoSuchKey', serr.code)

async def test_restore_object(self):
length = 123
data = random_str(length)
Expand Down
Loading
Loading