Skip to content

Commit f36ff51

Browse files
normalize parameter naming in DeleteMultipleObjectsRequest
1 parent a410247 commit f36ff51

File tree

6 files changed

+482
-22
lines changed

6 files changed

+482
-22
lines changed

alibabacloud_oss_v2/models/object_basic.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,15 +1043,76 @@ def __init__(
10431043
"name": "Object"
10441044
}
10451045

1046+
class ObjectIdentifier(serde.Model):
1047+
"""The identifier of an object to delete."""
1048+
1049+
_attribute_map = {
1050+
"key": {"tag": "xml", "rename": "Key", "required": True},
1051+
"version_id": {"tag": "xml", "rename": "VersionId"},
1052+
}
1053+
1054+
_xml_map = {
1055+
"name": "Object"
1056+
}
1057+
1058+
def __init__(
1059+
self,
1060+
key: Optional[str] = None,
1061+
version_id: Optional[str] = None,
1062+
**kwargs: Any
1063+
) -> None:
1064+
"""
1065+
Args:
1066+
key (str, required): The name of the object.
1067+
version_id (str, optional): The version ID of the object.
1068+
"""
1069+
super().__init__(**kwargs)
1070+
self.key = key
1071+
self.version_id = version_id
1072+
1073+
1074+
class Delete(serde.Model):
1075+
"""The container for the objects to delete."""
1076+
1077+
_attribute_map = {
1078+
"objects": {"tag": "xml", "rename": "Object", "type": "[ObjectIdentifier]"},
1079+
"quiet": {"tag": "xml", "rename": "Quiet", "type": "bool"},
1080+
}
1081+
1082+
_xml_map = {
1083+
"name": "Delete"
1084+
}
1085+
1086+
_dependency_map = {
1087+
"ObjectIdentifier": {"new": lambda: ObjectIdentifier()},
1088+
}
1089+
1090+
def __init__(
1091+
self,
1092+
objects: Optional[List[ObjectIdentifier]] = None,
1093+
quiet: Optional[bool] = None,
1094+
**kwargs: Any
1095+
) -> None:
1096+
"""
1097+
Args:
1098+
objects (List[ObjectIdentifier], optional): The list of objects to delete.
1099+
quiet (bool, optional): Specifies whether to enable the Quiet return mode.
1100+
"""
1101+
super().__init__(**kwargs)
1102+
self.objects = objects
1103+
self.quiet = quiet
1104+
1105+
10461106
class DeleteMultipleObjectsRequest(serde.RequestModel):
10471107
"""The request for the DeleteMultipleObjects operation."""
10481108

10491109
_attribute_map = {
10501110
"bucket": {"tag": "input", "position": "host", "required": True},
10511111
"encoding_type": {"tag": "input", "position": "query", "rename": "encoding-type"},
1052-
"objects": {"tag": "input", "position": "nop", "required": True},
1112+
"objects": {"tag": "input", "position": "nop"},
10531113
"quiet": {"tag": "input", "position": "nop"},
10541114
"request_payer": {"tag": "input", "position": "header", "rename": "x-oss-request-payer"},
1115+
"delete": {"tag": "input", "position": "body", "rename": "nop"},
10551116
}
10561117

10571118
def __init__(
@@ -1061,23 +1122,28 @@ def __init__(
10611122
objects: Optional[List[DeleteObject]] = None,
10621123
quiet: Optional[bool] = None,
10631124
request_payer: Optional[str] = None,
1125+
delete: Optional[Delete] = None,
10641126
**kwargs: Any
10651127
) -> None:
10661128
"""
10671129
Args:
10681130
bucket (str, required): The name of the bucket.
10691131
encoding_type (str, optional): The encoding type of the object names in the response. Valid value: url
10701132
objects ([DeleteObject], optional): The container that stores information about you want to delete objects.
1133+
This parameter is deprecated. Use 'delete' parameter instead.
10711134
quiet (bool, optional): Specifies whether to enable the Quiet return mode.
10721135
The DeleteMultipleObjects operation provides the following return modes: Valid value: true,false
1136+
This parameter is deprecated. Use 'delete' parameter instead.
10731137
request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs.
1138+
delete (Delete, optional): The container that stores information about you want to delete objects.
10741139
"""
10751140
super().__init__(**kwargs)
10761141
self.bucket = bucket
10771142
self.encoding_type = encoding_type
10781143
self.objects = objects
10791144
self.quiet = quiet
10801145
self.request_payer = request_payer
1146+
self.delete = delete
10811147

10821148

10831149
class DeletedInfo(serde.Model):

alibabacloud_oss_v2/serde_utils.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -235,20 +235,48 @@ def serialize_delete_objects(request: serde.Model, op_input: OperationInput) ->
235235
if not isinstance(request, DeleteMultipleObjectsRequest):
236236
raise exceptions.SerializationError(error=f'Unsupport type {type(request)}')
237237

238+
# Check if both old and new parameters are set or neither is set
239+
has_old_params = request.objects is not None
240+
has_new_params = request.delete is not None
241+
242+
if (has_old_params and has_new_params) or (not has_old_params and not has_new_params):
243+
raise exceptions.SerializationError(
244+
error='Either old parameters (objects, quiet) or new parameter (delete) must be set, but not both'
245+
)
246+
238247
xml = '<Delete>'
239-
if request.quiet is not None:
240-
xml += f'<Quiet>{"true" if request.quiet else "false"}</Quiet>'
241-
242-
if isinstance(request.objects, List):
243-
for _, o in enumerate(request.objects):
244-
xml += '<Object>'
245-
key = utils.safety_str(o.key)
246-
if len(key) > 0:
247-
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
248-
vid = utils.safety_str(o.version_id)
249-
if len(vid) > 0:
250-
xml += f'<VersionId>{vid}</VersionId>'
251-
xml += '</Object>'
248+
249+
# Handle new parameter (delete)
250+
if has_new_params:
251+
if request.delete.quiet is not None:
252+
xml += f'<Quiet>{"true" if request.delete.quiet else "false"}</Quiet>'
253+
254+
if isinstance(request.delete.objects, List):
255+
for _, o in enumerate(request.delete.objects):
256+
xml += '<Object>'
257+
key = utils.safety_str(o.key)
258+
if len(key) > 0:
259+
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
260+
vid = utils.safety_str(o.version_id)
261+
if len(vid) > 0:
262+
xml += f'<VersionId>{vid}</VersionId>'
263+
xml += '</Object>'
264+
265+
# Handle old parameters (objects, quiet)
266+
elif has_old_params:
267+
if request.quiet is not None:
268+
xml += f'<Quiet>{"true" if request.quiet else "false"}</Quiet>'
269+
270+
if isinstance(request.objects, List):
271+
for _, o in enumerate(request.objects):
272+
xml += '<Object>'
273+
key = utils.safety_str(o.key)
274+
if len(key) > 0:
275+
xml += f'<Key>{utils.escape_xml_value(key)}</Key>'
276+
vid = utils.safety_str(o.version_id)
277+
if len(vid) > 0:
278+
xml += f'<VersionId>{vid}</VersionId>'
279+
xml += '</Object>'
252280

253281
xml += '</Delete>'
254282

sample/delete_multiple_objects.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,27 @@ def main():
2424

2525
client = oss.Client(cfg)
2626

27-
# If deleting multiple items, please follow the following format
27+
# If deleting multiple items, please follow the following format (deprecated)
2828
# objects = [oss.DeleteObject(key=args.key), oss.DeleteObject(key=args.key2)],
29-
29+
# result = client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
30+
# bucket=args.bucket,
31+
# encoding_type='url',
32+
# objects=[oss.DeleteObject(key=args.key)],
33+
# ))
34+
35+
# New mode using Delete parameter
36+
delete_request = oss.Delete(
37+
objects=[
38+
oss.ObjectIdentifier(key=args.key)
39+
],
40+
quiet=False
41+
)
42+
3043
result = client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
3144
bucket=args.bucket,
32-
encoding_type='url',
33-
objects=[oss.DeleteObject(key=args.key)],
45+
delete=delete_request,
3446
))
35-
47+
3648
print(f'status code: {result.status_code},'
3749
f' request id: {result.request_id},'
3850
f' key: {result.deleted_objects[0].key},'
@@ -44,5 +56,4 @@ def main():
4456

4557

4658
if __name__ == "__main__":
47-
main()
48-
59+
main()

tests/integration/test_client.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,82 @@ def test_delete_multiple_objects(self):
10221022
serr = cast(oss.exceptions.ServiceError, serr)
10231023
self.assertIn('NoSuchKey', serr.code)
10241024

1025+
def test_delete_multiple_objects_with_delete_parameter(self):
1026+
# Test the new Delete parameter mode
1027+
length = 1234
1028+
data = random_str(length)
1029+
key1 = OBJECTNAME_PREFIX + random_str(16)
1030+
key2 = OBJECTNAME_PREFIX + random_str(16)
1031+
1032+
# Put objects to be deleted
1033+
result1 = self.client.put_object(oss.PutObjectRequest(
1034+
bucket=self.bucket_name,
1035+
key=key1,
1036+
body=data,
1037+
))
1038+
self.assertIsNotNone(result1)
1039+
self.assertEqual(200, result1.status_code)
1040+
1041+
result2 = self.client.put_object(oss.PutObjectRequest(
1042+
bucket=self.bucket_name,
1043+
key=key2,
1044+
body=data,
1045+
))
1046+
self.assertIsNotNone(result2)
1047+
self.assertEqual(200, result2.status_code)
1048+
1049+
# Delete multiple objects using the new Delete parameter
1050+
delete_request = oss.Delete(
1051+
objects=[
1052+
oss.ObjectIdentifier(key=key1),
1053+
oss.ObjectIdentifier(key=key2)
1054+
],
1055+
quiet=False
1056+
)
1057+
1058+
result = self.client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
1059+
bucket=self.bucket_name,
1060+
delete=delete_request,
1061+
))
1062+
1063+
self.assertIsInstance(result, oss.DeleteMultipleObjectsResult)
1064+
self.assertEqual(200, result.status_code)
1065+
self.assertIsNotNone(result.headers.get('x-oss-request-id'))
1066+
self.assertEqual(2, len(result.deleted_objects))
1067+
1068+
deleted_keys = [obj.key for obj in result.deleted_objects]
1069+
self.assertIn(key1, deleted_keys)
1070+
self.assertIn(key2, deleted_keys)
1071+
1072+
# Verify objects are deleted
1073+
try:
1074+
self.client.head_object(oss.HeadObjectRequest(
1075+
bucket=self.bucket_name,
1076+
key=key1,
1077+
))
1078+
self.fail("Should have raised an exception")
1079+
except Exception as err:
1080+
self.assertIsInstance(err, oss.exceptions.OperationError)
1081+
err = cast(oss.exceptions.OperationError, err)
1082+
serr = err.unwrap()
1083+
self.assertIsInstance(serr, oss.exceptions.ServiceError)
1084+
serr = cast(oss.exceptions.ServiceError, serr)
1085+
self.assertIn('NoSuchKey', serr.code)
1086+
1087+
try:
1088+
self.client.head_object(oss.HeadObjectRequest(
1089+
bucket=self.bucket_name,
1090+
key=key2,
1091+
))
1092+
self.fail("Should have raised an exception")
1093+
except Exception as err:
1094+
self.assertIsInstance(err, oss.exceptions.OperationError)
1095+
err = cast(oss.exceptions.OperationError, err)
1096+
serr = err.unwrap()
1097+
self.assertIsInstance(serr, oss.exceptions.ServiceError)
1098+
serr = cast(oss.exceptions.ServiceError, serr)
1099+
self.assertIn('NoSuchKey', serr.code)
1100+
10251101
def test_restore_object(self):
10261102
length = 123
10271103
data = random_str(length)

tests/integration/test_object_basic_async.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,82 @@ async def test_delete_multiple_objects(self):
356356
serr = cast(oss.exceptions.ServiceError, serr)
357357
self.assertIn('NoSuchKey', serr.code)
358358

359+
async def test_delete_multiple_objects_with_delete_parameter(self):
360+
# Test the new Delete parameter mode
361+
length = 1234
362+
data = random_str(length)
363+
key1 = OBJECTNAME_PREFIX + random_str(16)
364+
key2 = OBJECTNAME_PREFIX + random_str(16)
365+
366+
# Put objects to be deleted
367+
result1 = await self.async_client.put_object(oss.PutObjectRequest(
368+
bucket=self.bucket_name,
369+
key=key1,
370+
body=data,
371+
))
372+
self.assertIsNotNone(result1)
373+
self.assertEqual(200, result1.status_code)
374+
375+
result2 = await self.async_client.put_object(oss.PutObjectRequest(
376+
bucket=self.bucket_name,
377+
key=key2,
378+
body=data,
379+
))
380+
self.assertIsNotNone(result2)
381+
self.assertEqual(200, result2.status_code)
382+
383+
# Delete multiple objects using the new Delete parameter
384+
delete_request = oss.Delete(
385+
objects=[
386+
oss.ObjectIdentifier(key=key1),
387+
oss.ObjectIdentifier(key=key2)
388+
],
389+
quiet=False
390+
)
391+
392+
result = await self.async_client.delete_multiple_objects(oss.DeleteMultipleObjectsRequest(
393+
bucket=self.bucket_name,
394+
delete=delete_request,
395+
))
396+
397+
self.assertIsInstance(result, oss.DeleteMultipleObjectsResult)
398+
self.assertEqual(200, result.status_code)
399+
self.assertIsNotNone(result.headers.get('x-oss-request-id'))
400+
self.assertEqual(2, len(result.deleted_objects))
401+
402+
deleted_keys = [obj.key for obj in result.deleted_objects]
403+
self.assertIn(key1, deleted_keys)
404+
self.assertIn(key2, deleted_keys)
405+
406+
# Verify objects are deleted
407+
try:
408+
await self.async_client.head_object(oss.HeadObjectRequest(
409+
bucket=self.bucket_name,
410+
key=key1,
411+
))
412+
self.fail("Should have raised an exception")
413+
except Exception as err:
414+
self.assertIsInstance(err, oss.exceptions.OperationError)
415+
err = cast(oss.exceptions.OperationError, err)
416+
serr = err.unwrap()
417+
self.assertIsInstance(serr, oss.exceptions.ServiceError)
418+
serr = cast(oss.exceptions.ServiceError, serr)
419+
self.assertIn('NoSuchKey', serr.code)
420+
421+
try:
422+
await self.async_client.head_object(oss.HeadObjectRequest(
423+
bucket=self.bucket_name,
424+
key=key2,
425+
))
426+
self.fail("Should have raised an exception")
427+
except Exception as err:
428+
self.assertIsInstance(err, oss.exceptions.OperationError)
429+
err = cast(oss.exceptions.OperationError, err)
430+
serr = err.unwrap()
431+
self.assertIsInstance(serr, oss.exceptions.ServiceError)
432+
serr = cast(oss.exceptions.ServiceError, serr)
433+
self.assertIn('NoSuchKey', serr.code)
434+
359435
async def test_restore_object(self):
360436
length = 123
361437
data = random_str(length)

0 commit comments

Comments
 (0)