From 40a33f8d24c1d7b386e6d32bf9840821ff2b4b57 Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Mon, 3 Nov 2025 14:33:49 +0800 Subject: [PATCH 1/6] Update import path syntax in vectors test cases. --- tests/integration/vector/test_paginator.py | 2 +- tests/integration/vector/test_vector_basic_client.py | 2 +- tests/integration/vector/test_vector_bucket_basic_client.py | 2 +- tests/integration/vector/test_vector_bucket_logging_client.py | 2 +- tests/integration/vector/test_vector_bucket_policy_client.py | 2 +- tests/integration/vector/test_vector_index_client.py | 2 +- tests/unit/vectors/models/test_index_basic.py | 2 +- tests/unit/vectors/models/test_vector_basic.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/integration/vector/test_paginator.py b/tests/integration/vector/test_paginator.py index 019a47a..cd1d14d 100644 --- a/tests/integration/vector/test_paginator.py +++ b/tests/integration/vector/test_paginator.py @@ -1,7 +1,7 @@ # pylint: skip-file import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_bucket_name, random_short_bucket_name +from .. import TestIntegrationVectors, random_short_bucket_name class TestPaginatorBasic(TestIntegrationVectors): def test_list_vector_buckets_paginator(self): diff --git a/tests/integration/vector/test_vector_basic_client.py b/tests/integration/vector/test_vector_basic_client.py index 94970ed..be3c8e7 100644 --- a/tests/integration/vector/test_vector_basic_client.py +++ b/tests/integration/vector/test_vector_basic_client.py @@ -2,7 +2,7 @@ from ast import literal_eval import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_short_bucket_name +from .. import TestIntegrationVectors, random_short_bucket_name class TestVectorBasic(TestIntegrationVectors): diff --git a/tests/integration/vector/test_vector_bucket_basic_client.py b/tests/integration/vector/test_vector_bucket_basic_client.py index 0dd0cf0..9213b70 100644 --- a/tests/integration/vector/test_vector_bucket_basic_client.py +++ b/tests/integration/vector/test_vector_bucket_basic_client.py @@ -1,7 +1,7 @@ # pylint: skip-file import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_short_bucket_name, REGION, USER_ID +from .. import TestIntegrationVectors, random_short_bucket_name, REGION, USER_ID class TestVectorBucketBasic(TestIntegrationVectors): diff --git a/tests/integration/vector/test_vector_bucket_logging_client.py b/tests/integration/vector/test_vector_bucket_logging_client.py index 90606da..c2aaa91 100644 --- a/tests/integration/vector/test_vector_bucket_logging_client.py +++ b/tests/integration/vector/test_vector_bucket_logging_client.py @@ -2,7 +2,7 @@ import alibabacloud_oss_v2 as oss import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_short_bucket_name, random_bucket_name +from .. import TestIntegrationVectors, random_short_bucket_name, random_bucket_name class TestVectorBucketLogging(TestIntegrationVectors): diff --git a/tests/integration/vector/test_vector_bucket_policy_client.py b/tests/integration/vector/test_vector_bucket_policy_client.py index e00fbf6..84bbd18 100644 --- a/tests/integration/vector/test_vector_bucket_policy_client.py +++ b/tests/integration/vector/test_vector_bucket_policy_client.py @@ -1,7 +1,7 @@ # pylint: skip-file import time import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_short_bucket_name +from .. import TestIntegrationVectors, random_short_bucket_name class TestVectorBucketBasic(TestIntegrationVectors): diff --git a/tests/integration/vector/test_vector_index_client.py b/tests/integration/vector/test_vector_index_client.py index 73193a7..80f624c 100644 --- a/tests/integration/vector/test_vector_index_client.py +++ b/tests/integration/vector/test_vector_index_client.py @@ -1,6 +1,6 @@ # pylint: skip-file import alibabacloud_oss_v2.vectors as oss_vectors -from tests.integration import TestIntegrationVectors, random_short_bucket_name +from .. import TestIntegrationVectors, random_short_bucket_name class TestVectorIndex(TestIntegrationVectors): diff --git a/tests/unit/vectors/models/test_index_basic.py b/tests/unit/vectors/models/test_index_basic.py index 1ea4235..14efecb 100644 --- a/tests/unit/vectors/models/test_index_basic.py +++ b/tests/unit/vectors/models/test_index_basic.py @@ -5,7 +5,7 @@ from alibabacloud_oss_v2.vectors.operations import _serde from alibabacloud_oss_v2.vectors.models import index_basic as model from alibabacloud_oss_v2.types import OperationInput, OperationOutput, CaseInsensitiveDict -from tests.unit import MockHttpResponse +from ... import MockHttpResponse class TestPutVectorIndex(unittest.TestCase): diff --git a/tests/unit/vectors/models/test_vector_basic.py b/tests/unit/vectors/models/test_vector_basic.py index 6cb1cdb..2efeaa2 100644 --- a/tests/unit/vectors/models/test_vector_basic.py +++ b/tests/unit/vectors/models/test_vector_basic.py @@ -5,7 +5,7 @@ from alibabacloud_oss_v2.vectors.operations import _serde from alibabacloud_oss_v2.vectors.models import vector_basic as model from alibabacloud_oss_v2.types import OperationInput, OperationOutput, CaseInsensitiveDict -from tests.unit import MockHttpResponse +from ... import MockHttpResponse class TestPutVectors(unittest.TestCase): From dcd9c3805291d02de9e701c9c8374029e00350c1 Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Mon, 3 Nov 2025 15:09:28 +0800 Subject: [PATCH 2/6] mark the bucket parameter as required --- alibabacloud_oss_v2/models/access_point.py | 6 ++-- .../access_point_public_access_block.py | 12 ++++---- alibabacloud_oss_v2/models/bucket_basic.py | 2 +- alibabacloud_oss_v2/models/bucket_logging.py | 6 ++-- alibabacloud_oss_v2/models/bucket_policy.py | 6 ++-- .../models/bucket_public_access_block.py | 18 +++++------ .../models/bucket_resource_group.py | 6 ++-- alibabacloud_oss_v2/models/bucket_style.py | 6 ++-- .../vectors/models/bucket_basic.py | 2 +- .../vectors/models/index_basic.py | 22 +++++++------- .../vectors/models/vector_basic.py | 30 +++++++++---------- 11 files changed, 58 insertions(+), 58 deletions(-) diff --git a/alibabacloud_oss_v2/models/access_point.py b/alibabacloud_oss_v2/models/access_point.py index bd9684f..26cefdc 100644 --- a/alibabacloud_oss_v2/models/access_point.py +++ b/alibabacloud_oss_v2/models/access_point.py @@ -442,21 +442,21 @@ class PutAccessPointPolicyRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, 'access_point_name': {'tag': 'input', 'position': 'header', 'rename': 'x-oss-access-point-name', 'type': 'str'}, 'body': {'tag': 'input', 'position': 'body', 'rename': 'nop'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, access_point_name: Optional[str] = None, body: Optional[BodyType] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. access_point_name (str, optional): The name of the access point. body (BodyType, optional): The configurations of the access point policy. """ diff --git a/alibabacloud_oss_v2/models/access_point_public_access_block.py b/alibabacloud_oss_v2/models/access_point_public_access_block.py index 6525716..a96b872 100644 --- a/alibabacloud_oss_v2/models/access_point_public_access_block.py +++ b/alibabacloud_oss_v2/models/access_point_public_access_block.py @@ -71,19 +71,19 @@ class GetAccessPointPublicAccessBlockRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, 'access_point_name': {'tag': 'input', 'position': 'query', 'rename': 'x-oss-access-point-name', 'type': 'str'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, access_point_name: Optional[str] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. access_point_name (str, optional): The name of the access point. """ super().__init__(**kwargs) @@ -123,19 +123,19 @@ class DeleteAccessPointPublicAccessBlockRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, 'access_point_name': {'tag': 'input', 'position': 'query', 'rename': 'x-oss-access-point-name', 'type': 'str'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, access_point_name: Optional[str] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. access_point_name (str, optional): The name of the access point. """ super().__init__(**kwargs) diff --git a/alibabacloud_oss_v2/models/bucket_basic.py b/alibabacloud_oss_v2/models/bucket_basic.py index 6b60c62..74444c1 100644 --- a/alibabacloud_oss_v2/models/bucket_basic.py +++ b/alibabacloud_oss_v2/models/bucket_basic.py @@ -84,7 +84,7 @@ class DeleteBucketRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ diff --git a/alibabacloud_oss_v2/models/bucket_logging.py b/alibabacloud_oss_v2/models/bucket_logging.py index 5499427..567438f 100644 --- a/alibabacloud_oss_v2/models/bucket_logging.py +++ b/alibabacloud_oss_v2/models/bucket_logging.py @@ -344,17 +344,17 @@ class DeleteUserDefinedLogFieldsConfigRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): + bucket (str, required): """ super().__init__(**kwargs) self.bucket = bucket diff --git a/alibabacloud_oss_v2/models/bucket_policy.py b/alibabacloud_oss_v2/models/bucket_policy.py index c56ef71..e7c0c5d 100644 --- a/alibabacloud_oss_v2/models/bucket_policy.py +++ b/alibabacloud_oss_v2/models/bucket_policy.py @@ -137,17 +137,17 @@ class GetBucketPolicyStatusRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. """ super().__init__(**kwargs) self.bucket = bucket diff --git a/alibabacloud_oss_v2/models/bucket_public_access_block.py b/alibabacloud_oss_v2/models/bucket_public_access_block.py index ab4237a..f55472c 100644 --- a/alibabacloud_oss_v2/models/bucket_public_access_block.py +++ b/alibabacloud_oss_v2/models/bucket_public_access_block.py @@ -37,17 +37,17 @@ class GetBucketPublicAccessBlockRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): + bucket (str, required): """ super().__init__(**kwargs) self.bucket = bucket @@ -84,19 +84,19 @@ class PutBucketPublicAccessBlockRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, 'public_access_block_configuration': {'tag': 'input', 'position': 'body', 'rename': 'PublicAccessBlockConfiguration', 'type': 'xml'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, public_access_block_configuration: Optional[PublicAccessBlockConfiguration] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. public_access_block_configuration (PublicAccessBlockConfiguration, optional): Request body. """ super().__init__(**kwargs) @@ -115,17 +115,17 @@ class DeleteBucketPublicAccessBlockRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. """ super().__init__(**kwargs) self.bucket = bucket diff --git a/alibabacloud_oss_v2/models/bucket_resource_group.py b/alibabacloud_oss_v2/models/bucket_resource_group.py index 322ec91..cf399c2 100644 --- a/alibabacloud_oss_v2/models/bucket_resource_group.py +++ b/alibabacloud_oss_v2/models/bucket_resource_group.py @@ -84,19 +84,19 @@ class PutBucketResourceGroupRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, 'bucket_resource_group_configuration': {'tag': 'input', 'position': 'body', 'rename': 'BucketResourceGroupConfiguration', 'type': 'xml'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, bucket_resource_group_configuration: Optional[BucketResourceGroupConfiguration] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The bucket for which you want to modify the ID of the resource group. + bucket (str, required): The bucket for which you want to modify the ID of the resource group. bucket_resource_group_configuration (BucketResourceGroupConfiguration, optional): The request body schema. """ super().__init__(**kwargs) diff --git a/alibabacloud_oss_v2/models/bucket_style.py b/alibabacloud_oss_v2/models/bucket_style.py index 12a43ed..d4a67e7 100644 --- a/alibabacloud_oss_v2/models/bucket_style.py +++ b/alibabacloud_oss_v2/models/bucket_style.py @@ -144,17 +144,17 @@ class ListStyleRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'host', 'rename': 'bucket', 'type': 'str', 'required': True}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. """ super().__init__(**kwargs) self.bucket = bucket diff --git a/alibabacloud_oss_v2/vectors/models/bucket_basic.py b/alibabacloud_oss_v2/vectors/models/bucket_basic.py index e53ea36..6e0ccbd 100644 --- a/alibabacloud_oss_v2/vectors/models/bucket_basic.py +++ b/alibabacloud_oss_v2/vectors/models/bucket_basic.py @@ -136,7 +136,7 @@ class DeleteVectorBucketRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, **kwargs: Any ) -> None: """ diff --git a/alibabacloud_oss_v2/vectors/models/index_basic.py b/alibabacloud_oss_v2/vectors/models/index_basic.py index 150c9d5..8851765 100644 --- a/alibabacloud_oss_v2/vectors/models/index_basic.py +++ b/alibabacloud_oss_v2/vectors/models/index_basic.py @@ -21,7 +21,7 @@ class PutVectorIndexRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, data_type: Optional[str] = None, dimension: Optional[int] = None, distance_metric: Optional[str] = None, @@ -31,7 +31,7 @@ def __init__( ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. data_type (str, optional): The type of data for the vector index. dimension (int, optional): The dimension of the vector data. distance_metric (str, optional): The distance measurement function has the following optional values: @@ -62,19 +62,19 @@ class GetVectorIndexRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', "required": True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index. """ super().__init__(**kwargs) @@ -112,7 +112,7 @@ class ListVectorIndexesRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', "required": True}, 'max_results': {'tag': 'input', 'position': 'body', 'rename': 'maxResults', 'type': 'int'}, 'next_token': {'tag': 'input', 'position': 'body', 'rename': 'nextToken', 'type': 'str'}, 'prefix': {'tag': 'input', 'position': 'body', 'rename': 'prefix', 'type': 'str'}, @@ -120,7 +120,7 @@ class ListVectorIndexesRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, max_results: Optional[int] = None, next_token: Optional[str] = None, prefix: Optional[str] = None, @@ -128,7 +128,7 @@ def __init__( ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. max_results (int, optional): The maximum number of indexes to return. next_token (str, optional): The token for the next page of indexes. prefix (str, optional): The prefix to filter indexes by name. @@ -174,19 +174,19 @@ class DeleteVectorIndexRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', "required": True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index to delete. """ super().__init__(**kwargs) diff --git a/alibabacloud_oss_v2/vectors/models/vector_basic.py b/alibabacloud_oss_v2/vectors/models/vector_basic.py index 0120b84..81ba96f 100644 --- a/alibabacloud_oss_v2/vectors/models/vector_basic.py +++ b/alibabacloud_oss_v2/vectors/models/vector_basic.py @@ -7,21 +7,21 @@ class PutVectorsRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', 'required': True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, 'vectors': {'tag': 'input', 'position': 'body', 'rename': 'vectors', 'type': '[dict]'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, vectors: Optional[List[Dict]] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index. vectors (List[Dict], optional): The list of vectors to put. """ @@ -47,7 +47,7 @@ class GetVectorsRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', 'required': True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, 'keys': {'tag': 'input', 'position': 'body', 'rename': 'keys', 'type': '[str]'}, 'return_data': {'tag': 'input', 'position': 'body', 'rename': 'returnData', 'type': 'bool'}, @@ -56,7 +56,7 @@ class GetVectorsRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, keys: Optional[List[str]] = None, return_data: Optional[bool] = None, @@ -65,7 +65,7 @@ def __init__( ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index. keys (List[str], optional): The list of vector keys to retrieve. return_data (bool, optional): Whether to return vector data. @@ -108,7 +108,7 @@ class ListVectorsRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', 'required': True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, 'max_results': {'tag': 'input', 'position': 'body', 'rename': 'maxResults', 'type': 'int'}, 'next_token': {'tag': 'input', 'position': 'body', 'rename': 'nextToken', 'type': 'str'}, @@ -120,7 +120,7 @@ class ListVectorsRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, max_results: Optional[int] = None, next_token: Optional[str] = None, @@ -132,7 +132,7 @@ def __init__( ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index. max_results (int, optional): The maximum number of vectors to return. next_token (str, optional): The token for the next page of vectors. @@ -185,21 +185,21 @@ class DeleteVectorsRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', 'required': True}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, 'keys': {'tag': 'input', 'position': 'body', 'rename': 'keys', 'type': '[str]'}, } def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, index_name: Optional[str] = None, keys: Optional[List[str]] = None, **kwargs: Any ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. index_name (str, optional): The name of the index. keys (List[str], optional): The list of vector keys to delete. """ @@ -222,7 +222,7 @@ class QueryVectorsRequest(serde.RequestModel): """ _attribute_map = { - 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str'}, + 'bucket': {'tag': 'input', 'position': 'path', 'rename': 'bucket', 'type': 'str', 'required': True}, 'filter': {'tag': 'input', 'position': 'body', 'rename': 'filter', 'type': 'dict'}, 'index_name': {'tag': 'input', 'position': 'body', 'rename': 'indexName', 'type': 'str'}, 'query_vector': {'tag': 'input', 'position': 'body', 'rename': 'queryVector', 'type': 'dict'}, @@ -233,7 +233,7 @@ class QueryVectorsRequest(serde.RequestModel): def __init__( self, - bucket: Optional[str] = None, + bucket: str = None, filter: Optional[Dict] = None, index_name: Optional[str] = None, query_vector: Optional[Dict] = None, @@ -244,7 +244,7 @@ def __init__( ) -> None: """ Args: - bucket (str, optional): The name of the bucket. + bucket (str, required): The name of the bucket. filter (Dict, optional): The filter conditions for querying vectors. index_name (str, optional): The name of the index. query_vector (Dict, optional): The query vector data. From 5d4944f246b84b0f60734373b7a646d5bfa149e4 Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Mon, 22 Sep 2025 15:44:51 +0800 Subject: [PATCH 3/6] the uploader supports progress. --- alibabacloud_oss_v2/checkpoint.py | 2 +- alibabacloud_oss_v2/uploader.py | 5 ++ sample/progress_uploader_file.py | 58 +++++++++++++ sample/progress_uploader_from.py | 60 +++++++++++++ tests/integration/test_client.py | 137 ++++++++++++++++++++++++++++++ 5 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 sample/progress_uploader_file.py create mode 100644 sample/progress_uploader_from.py diff --git a/alibabacloud_oss_v2/checkpoint.py b/alibabacloud_oss_v2/checkpoint.py index 675ee99..b1467f9 100644 --- a/alibabacloud_oss_v2/checkpoint.py +++ b/alibabacloud_oss_v2/checkpoint.py @@ -265,7 +265,7 @@ def __init__( h.update(absfilepath.encode()) src_hash = h.hexdigest() - if len(basedir) == 0: + if basedir is None or len(basedir) == 0: dirbase = gettempdir() else: dirbase = os.path.dirname(basedir) diff --git a/alibabacloud_oss_v2/uploader.py b/alibabacloud_oss_v2/uploader.py index 4829b5a..a8e4019 100644 --- a/alibabacloud_oss_v2/uploader.py +++ b/alibabacloud_oss_v2/uploader.py @@ -439,6 +439,7 @@ def adjust_source(self): self._reader_pos = next_offset self._part_number = part_number + 1 self._ccrc = ccrc + self._transferred = next_offset def set_reader(self, reader) ->IO[bytes]: @@ -691,6 +692,10 @@ def _update_upload_result(self, result): if self._check_crc and hash_crc64 is not None: self._ccrc = Crc64.combine(self._ccrc, int(hash_crc64), size) + self._transferred += size + if self._request.progress_fn is not None: + self._request.progress_fn(size, self._transferred, self._total_size) + def _assert_crc_same(self, headers: MutableMapping): if not self._check_crc: diff --git a/sample/progress_uploader_file.py b/sample/progress_uploader_file.py new file mode 100644 index 0000000..a9c39b0 --- /dev/null +++ b/sample/progress_uploader_file.py @@ -0,0 +1,58 @@ +import argparse +import alibabacloud_oss_v2 as oss + +parser = argparse.ArgumentParser(description="progress upload file sample") +parser.add_argument('--region', help='The region in which the bucket is located.', required=True) +parser.add_argument('--bucket', help='The name of the bucket.', required=True) +parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS') +parser.add_argument('--key', help='The name of the object.', required=True) +parser.add_argument('--file_path', help='The path of Upload file.', required=True) + + +class UploadProgress: + def __init__(self): + self.bytes_transferred = 0 + + def __call__(self, increment, written, total): + self.bytes_transferred += increment + rate = int(100 * (float(written) / float(total))) + print(f'\r{rate}% ', end='') + + +def main(): + + args = parser.parse_args() + + # Loading credentials values from the environment variables + credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider() + + # Using the SDK's default configuration + cfg = oss.config.load_default() + cfg.credentials_provider = credentials_provider + cfg.region = args.region + if args.endpoint is not None: + cfg.endpoint = args.endpoint + + client = oss.Client(cfg) + + up_loader = client.uploader(part_size=100*1024, + parallel_num=5, + leave_parts_on_error=True, + enable_checkpoint=True, + ) + + # Create progress tracker + progress_tracker = UploadProgress() + + result = up_loader.upload_file(oss.PutObjectRequest( + bucket=args.bucket, + key=args.key, + progress_fn=progress_tracker, + ), filepath=args.file_path) + + print(vars(result)) + + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/sample/progress_uploader_from.py b/sample/progress_uploader_from.py new file mode 100644 index 0000000..b22c9fc --- /dev/null +++ b/sample/progress_uploader_from.py @@ -0,0 +1,60 @@ +import argparse +import alibabacloud_oss_v2 as oss + +parser = argparse.ArgumentParser(description="progress upload from sample") +parser.add_argument('--region', help='The region in which the bucket is located.', required=True) +parser.add_argument('--bucket', help='The name of the bucket.', required=True) +parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS') +parser.add_argument('--key', help='The name of the object.', required=True) +parser.add_argument('--file_path', help='The path of Upload file.', required=True) + + +class UploadProgress: + def __init__(self): + self.bytes_transferred = 0 + + def __call__(self, increment, written, total): + self.bytes_transferred += increment + rate = int(100 * (float(written) / float(total))) + print(f'\r{rate}% ') + + +def main(): + + args = parser.parse_args() + + # Loading credentials values from the environment variables + credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider() + + # Using the SDK's default configuration + cfg = oss.config.load_default() + cfg.credentials_provider = credentials_provider + cfg.region = args.region + if args.endpoint is not None: + cfg.endpoint = args.endpoint + + client = oss.Client(cfg) + + up_loader = client.uploader() + + # up_loader = client.uploader(part_size=100*1024, + # parallel_num=5, + # leave_parts_on_error=True, + # enable_checkpoint=True, + # checkpoint_dir=args.file_path) + + # Create progress tracker + progress_tracker = UploadProgress() + + with open(file=args.file_path, mode='rb') as f: + result = up_loader.upload_from(oss.PutObjectRequest( + bucket=args.bucket, + key=args.key, + progress_fn=progress_tracker, + ), reader=f) + + print(vars(result)) + +if __name__ == "__main__": + main() + diff --git a/tests/integration/test_client.py b/tests/integration/test_client.py index f513e4a..fb075c0 100644 --- a/tests/integration/test_client.py +++ b/tests/integration/test_client.py @@ -4500,3 +4500,140 @@ def _progress_fn(n, written, total): self.assertEqual("Multipart", hresult.object_type) self.assertEqual(src_crc64, hresult.hash_crc64) + +class TestUploader(TestIntegration): + def test_uploader_progress_with_single_upload(self): + length = 100 * 1024 + 123 + data = random_str(length) + object_name = OBJECTNAME_PREFIX + random_str(16) + + global bytes_added, total_bytes_transferred, total_bytes_expected, last_written + bytes_added = 0 + total_bytes_transferred = 0 + total_bytes_expected = 0 + last_written = 0 + + def _progress_fn(n, written, total): + global last_written + global bytes_added + global total_bytes_transferred + global total_bytes_expected + + n = written - last_written + bytes_added += n + total_bytes_transferred = written + last_written = written + total_bytes_expected = total + + uploader = self.client.uploader() + + result = uploader.upload_from(oss.PutObjectRequest( + bucket=self.bucket_name, + key=object_name, + progress_fn=_progress_fn + ), io.StringIO(data), + part_size=500 * 1024, + parallel_num=5, + leave_parts_on_error=True + ) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + self.assertEqual(total_bytes_transferred, length) + self.assertEqual(total_bytes_expected, length) + + hresult = self.client.head_object(oss.HeadObjectRequest( + bucket=self.bucket_name, + key=object_name + )) + self.assertEqual("Normal", hresult.object_type) + self.assertEqual(length, hresult.content_length) + + def test_uploader_progress_with_single_multipart(self): + length = 500 * 1024 + 123 + data = random_str(length) + object_name = OBJECTNAME_PREFIX + random_str(16) + + global bytes_added, total_bytes_transferred, total_bytes_expected, last_written + bytes_added = 0 + total_bytes_transferred = 0 + total_bytes_expected = 0 + last_written = 0 + + def _progress_fn(n, written, total): + global last_written + global bytes_added + global total_bytes_transferred + global total_bytes_expected + + n = written - last_written + bytes_added += n + total_bytes_transferred = written + last_written = written + total_bytes_expected = total + + uploader = self.client.uploader() + result = uploader.upload_from(oss.PutObjectRequest( + bucket=self.bucket_name, + key=object_name, + progress_fn=_progress_fn + ), io.StringIO(data), + part_size=100 * 1024, + parallel_num=1, + leave_parts_on_error=True, + ) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + self.assertEqual(total_bytes_transferred, length) + self.assertEqual(total_bytes_expected, length) + + hresult = self.client.head_object(oss.HeadObjectRequest( + bucket=self.bucket_name, + key=object_name + )) + self.assertEqual("Multipart", hresult.object_type) + self.assertEqual(length, hresult.content_length) + + def test_uploader_progress_with_thread_multipart(self): + length = 500 * 1024 + 123 + data = random_str(length) + object_name = OBJECTNAME_PREFIX + random_str(16) + + global bytes_added, total_bytes_transferred, total_bytes_expected, last_written + bytes_added = 0 + total_bytes_transferred = 0 + total_bytes_expected = 0 + last_written = 0 + + def _progress_fn(n, written, total): + global last_written + global bytes_added + global total_bytes_transferred + global total_bytes_expected + + n = written - last_written + bytes_added += n + total_bytes_transferred = written + last_written = written + total_bytes_expected = total + + uploader = self.client.uploader() + result = uploader.upload_from(oss.PutObjectRequest( + bucket=self.bucket_name, + key=object_name, + progress_fn=_progress_fn + ), io.StringIO(data), + part_size=100 * 1024, + parallel_num=3, + leave_parts_on_error=True, + ) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + self.assertEqual(total_bytes_transferred, length) + self.assertEqual(total_bytes_expected, length) + + hresult = self.client.head_object(oss.HeadObjectRequest( + bucket=self.bucket_name, + key=object_name + )) + self.assertEqual("Multipart", hresult.object_type) + self.assertEqual(length, hresult.content_length) From a410247cf1cee2205f0de3b4c8bf396a54ddf43d Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Thu, 6 Nov 2025 16:43:25 +0800 Subject: [PATCH 4/6] routing rule supports more parameters --- alibabacloud_oss_v2/models/bucket_website.py | 368 +++++++++- sample/put_bucket_website.py | 81 +++ .../integration/test_bucket_website_client.py | 543 +++++++++++++- tests/unit/models/test_bucket_website.py | 683 +++++++++++++++++- 4 files changed, 1653 insertions(+), 22 deletions(-) diff --git a/alibabacloud_oss_v2/models/bucket_website.py b/alibabacloud_oss_v2/models/bucket_website.py index c1a712e..711412e 100644 --- a/alibabacloud_oss_v2/models/bucket_website.py +++ b/alibabacloud_oss_v2/models/bucket_website.py @@ -99,12 +99,14 @@ def __init__( class RoutingRuleIncludeHeader(serde.Model): """ - 只有请求中包含了指定Header且值为指定值时,才能匹配此规则。该容器最多可指定10个。 + The rule will only be matched when the request contains the specified Header with the specified value. Up to 10 of these can be specified in the container. """ - _attribute_map = { + _attribute_map = { 'key': {'tag': 'xml', 'rename': 'Key', 'type': 'str'}, 'equals': {'tag': 'xml', 'rename': 'Equals', 'type': 'str'}, + 'starts_with': {'tag': 'xml', 'rename': 'StartsWith', 'type': 'str'}, + 'ends_with': {'tag': 'xml', 'rename': 'EndsWith', 'type': 'str'}, } _xml_map = { @@ -115,18 +117,59 @@ def __init__( self, key: Optional[str] = None, equals: Optional[str] = None, + starts_with: Optional[str] = None, + ends_with: Optional[str] = None, **kwargs: Any ) -> None: """ - Args: - key (str, optional): 只有请求中包含了此Header且值为Equals的指定值时,才能匹配此规则。 - equals (str, optional): 只有请求中包含了Key指定的Header且值为指定值时,才能匹配此规则。 + key (str, optional): The rule will only be matched when the request contains this Header and its value is equal to the specified value. + equals (str, optional): The rule will only be matched when the request contains this Header and its value is equal to the specified value. + starts_with (str, optional): The rule will only be matched when the request contains this Header and its value starts with the specified value. + ends_with (str, optional): The rule will only be matched when the request contains this Header and its value ends with the specified value. """ super().__init__(**kwargs) self.key = key self.equals = equals + self.starts_with = starts_with + self.ends_with = ends_with +class MirrorAuth(serde.Model): + """ + The authentication information for the origin server in mirror-based back-to-origin. + """ + + _attribute_map = { + 'access_key_id': {'tag': 'xml', 'rename': 'AccessKeyId', 'type': 'str'}, + 'access_key_secret': {'tag': 'xml', 'rename': 'AccessKeySecret', 'type': 'str'}, + 'auth_type': {'tag': 'xml', 'rename': 'AuthType', 'type': 'str'}, + 'region': {'tag': 'xml', 'rename': 'Region', 'type': 'str'}, + } + + _xml_map = { + 'name': 'MirrorAuth' + } + + def __init__( + self, + access_key_id: Optional[str] = None, + access_key_secret: Optional[str] = None, + auth_type: Optional[str] = None, + region: Optional[str] = None, + **kwargs: Any + ) -> None: + """ + access_key_id (str, optional): The access key id for signature. + access_key_secret (str, optional): The access key secret for signature. + auth_type (str, optional): The authentication type. + region (str, optional): The sign region for signature. + """ + super().__init__(**kwargs) + self.access_key_id = access_key_id + self.access_key_secret = access_key_secret + self.auth_type = auth_type + self.region = region + class RoutingRuleCondition(serde.Model): """ The matching condition. If all of the specified conditions are met, the rule is run. A rule is considered matched only when the rule meets the conditions that are specified by all nodes in Condition. This parameter must be specified if RoutingRule is specified. @@ -210,6 +253,185 @@ def __init__( self.removes = removes self.sets = sets +class MirrorTagging(serde.Model): + """ + The rule list for setting tags. + """ + + _attribute_map = { + 'key': {'tag': 'xml', 'rename': 'Key', 'type': 'str'}, + 'value': {'tag': 'xml', 'rename': 'Value', 'type': 'str'}, + } + + _xml_map = { + 'name': 'Taggings' + } + + def __init__( + self, + key: Optional[str] = None, + value: Optional[str] = None, + **kwargs: Any + ) -> None: + """ + key (str, optional): The tag key. + value (str, optional): The rule for setting tag value for a specific tag key. + """ + super().__init__(**kwargs) + self.key = key + self.value = value + + +class MirrorTaggings(serde.Model): + """ + The rules for setting tags when saving files during mirror-based back-to-origin. + """ + + _attribute_map = { + 'taggings': {'tag': 'xml', 'rename': 'Taggings', 'type': '[MirrorTagging]'}, + } + + _xml_map = { + 'name': 'MirrorTaggings' + } + + _dependency_map = { + 'Taggings': {'new': lambda: MirrorTagging()}, + } + + def __init__( + self, + taggings: Optional[List[MirrorTagging]] = None, + **kwargs: Any + ) -> None: + """ + taggings (List[MirrorTagging], optional): The rule list for setting tags. + """ + super().__init__(**kwargs) + self.taggings = taggings + +class MirrorMultiAlternate(serde.Model): + """ + The configuration list for multiple origins. + """ + + _attribute_map = { + 'mirror_multi_alternate_number': {'tag': 'xml', 'rename': 'MirrorMultiAlternateNumber', 'type': 'int'}, + 'mirror_multi_alternate_url': {'tag': 'xml', 'rename': 'MirrorMultiAlternateURL', 'type': 'str'}, + 'mirror_multi_alternate_vpc_id': {'tag': 'xml', 'rename': 'MirrorMultiAlternateVpcId', 'type': 'str'}, + 'mirror_multi_alternate_dst_region': {'tag': 'xml', 'rename': 'MirrorMultiAlternateDstRegion', 'type': 'str'}, + } + + _xml_map = { + 'name': 'MirrorMultiAlternate' + } + + def __init__( + self, + mirror_multi_alternate_number: Optional[int] = None, + mirror_multi_alternate_url: Optional[str] = None, + mirror_multi_alternate_vpc_id: Optional[str] = None, + mirror_multi_alternate_dst_region: Optional[str] = None, + **kwargs: Any + ) -> None: + """ + mirror_multi_alternate_number (int, optional): The distinct number of a specific origin. + mirror_multi_alternate_url (str, optional): The URL for a specific origin. + mirror_multi_alternate_vpc_id (str, optional): The VPC ID for a specific origin. + mirror_multi_alternate_dst_region (str, optional): The region for a specific origin. + """ + super().__init__(**kwargs) + self.mirror_multi_alternate_number = mirror_multi_alternate_number + self.mirror_multi_alternate_url = mirror_multi_alternate_url + self.mirror_multi_alternate_vpc_id = mirror_multi_alternate_vpc_id + self.mirror_multi_alternate_dst_region = mirror_multi_alternate_dst_region + + + +class MirrorMultiAlternates(serde.Model): + """ + The container to store the configuration for multiple origins in mirror-based back-to-origin. + """ + + _attribute_map = { + 'mirror_multi_alternates': {'tag': 'xml', 'rename': 'MirrorMultiAlternate', 'type': '[MirrorMultiAlternate]'}, + } + + _xml_map = { + 'name': 'MirrorMultiAlternates' + } + + _dependency_map = { + 'MirrorMultiAlternate': {'new': lambda: MirrorMultiAlternate()}, + } + + def __init__( + self, + mirror_multi_alternates: Optional[List[MirrorMultiAlternate]] = None, + **kwargs: Any + ) -> None: + """ + mirror_multi_alternates (List[MirrorMultiAlternate], optional): The configuration list for multiple origins. + """ + super().__init__(**kwargs) + self.mirror_multi_alternates = mirror_multi_alternates + +class ReturnHeader(serde.Model): + """ + The rule list for setting response headers in mirror-based back-to-origin. + """ + + _attribute_map = { + 'value': {'tag': 'xml', 'rename': 'Value', 'type': 'str'}, + 'key': {'tag': 'xml', 'rename': 'Key', 'type': 'str'}, + } + + _xml_map = { + 'name': 'ReturnHeader' + } + + def __init__( + self, + value: Optional[str] = None, + key: Optional[str] = None, + **kwargs: Any + ) -> None: + """ + value (str, optional): The rule for setting response header value for a specific header. + key (str, optional): The response header. + """ + super().__init__(**kwargs) + self.value = value + self.key = key + +class MirrorReturnHeaders(serde.Model): + """ + Container to store the rules for setting response headers in mirror-based back-to-origin. + """ + + _attribute_map = { + 'return_headers': {'tag': 'xml', 'rename': 'ReturnHeader', 'type': '[ReturnHeader]'}, + } + + _xml_map = { + 'name': 'MirrorReturnHeaders' + } + + _dependency_map = { + 'ReturnHeader': {'new': lambda: ReturnHeader()}, + } + + def __init__( + self, + return_headers: Optional[List[ReturnHeader]] = None, + **kwargs: Any + ) -> None: + """ + return_headers (List[ReturnHeader], optional): The rule list for setting response headers in mirror-based back-to-origin. + """ + super().__init__(**kwargs) + self.return_headers = return_headers + class RoutingRuleRedirect(serde.Model): """ @@ -232,6 +454,28 @@ class RoutingRuleRedirect(serde.Model): 'mirror_follow_redirect': {'tag': 'xml', 'rename': 'MirrorFollowRedirect', 'type': 'bool'}, 'mirror_check_md5': {'tag': 'xml', 'rename': 'MirrorCheckMd5', 'type': 'bool'}, 'mirror_pass_original_slashes': {'tag': 'xml', 'rename': 'MirrorPassOriginalSlashes', 'type': 'bool'}, + 'mirror_allow_video_snapshot': {'tag': 'xml', 'rename': 'MirrorAllowVideoSnapshot', 'type': 'bool'}, + 'mirror_async_status': {'tag': 'xml', 'rename': 'MirrorAsyncStatus', 'type': 'int'}, + 'mirror_taggings': {'tag': 'xml', 'rename': 'MirrorTaggings', 'type': 'MirrorTaggings'}, + 'mirror_auth': {'tag': 'xml', 'rename': 'MirrorAuth', 'type': 'MirrorAuth'}, + 'mirror_dst_region': {'tag': 'xml', 'rename': 'MirrorDstRegion', 'type': 'str'}, + 'mirror_dst_vpc_id': {'tag': 'xml', 'rename': 'MirrorDstVpcId', 'type': 'str'}, + 'mirror_tunnel_id': {'tag': 'xml', 'rename': 'MirrorTunnelId', 'type': 'str'}, + 'mirror_role': {'tag': 'xml', 'rename': 'MirrorRole', 'type': 'str'}, + 'mirror_using_role': {'tag': 'xml', 'rename': 'MirrorUsingRole', 'type': 'bool'}, + 'mirror_return_headers': {'tag': 'xml', 'rename': 'MirrorReturnHeaders', 'type': 'MirrorReturnHeaders'}, + 'mirror_proxy_pass': {'tag': 'xml', 'rename': 'MirrorProxyPass', 'type': 'bool'}, + 'mirror_is_express_tunnel': {'tag': 'xml', 'rename': 'MirrorIsExpressTunnel', 'type': 'bool'}, + 'mirror_dst_slave_vpc_id': {'tag': 'xml', 'rename': 'MirrorDstSlaveVpcId', 'type': 'str'}, + 'mirror_allow_head_object': {'tag': 'xml', 'rename': 'MirrorAllowHeadObject', 'type': 'bool'}, + 'transparent_mirror_response_codes': {'tag': 'xml', 'rename': 'TransparentMirrorResponseCodes', 'type': 'str'}, + 'mirror_save_oss_meta': {'tag': 'xml', 'rename': 'MirrorSaveOssMeta', 'type': 'bool'}, + 'mirror_allow_get_image_info': {'tag': 'xml', 'rename': 'MirrorAllowGetImageInfo', 'type': 'bool'}, + 'mirror_url_probe': {'tag': 'xml', 'rename': 'MirrorURLProbe', 'type': 'str'}, + 'mirror_url_slave': {'tag': 'xml', 'rename': 'MirrorURLSlave', 'type': 'str'}, + 'mirror_user_last_modified': {'tag': 'xml', 'rename': 'MirrorUserLastModified', 'type': 'bool'}, + 'mirror_switch_all_errors': {'tag': 'xml', 'rename': 'MirrorSwitchAllErrors', 'type': 'bool'}, + 'mirror_multi_alternates': {'tag': 'xml', 'rename': 'MirrorMultiAlternates', 'type': 'MirrorMultiAlternates'}, } _xml_map = { @@ -240,6 +484,10 @@ class RoutingRuleRedirect(serde.Model): _dependency_map = { 'MirrorHeaders': {'new': lambda: MirrorHeaders()}, + 'MirrorTaggings': {'new': lambda: MirrorTaggings()}, + 'MirrorAuth': {'new': lambda: MirrorAuth()}, + 'MirrorReturnHeaders': {'new': lambda: MirrorReturnHeaders()}, + 'MirrorMultiAlternates': {'new': lambda: MirrorMultiAlternates()}, } def __init__( @@ -259,6 +507,28 @@ def __init__( mirror_follow_redirect: Optional[bool] = None, mirror_check_md5: Optional[bool] = None, mirror_pass_original_slashes: Optional[bool] = None, + mirror_allow_video_snapshot: Optional[bool] = None, + mirror_async_status: Optional[int] = None, + mirror_taggings: Optional[MirrorTaggings] = None, + mirror_auth: Optional[MirrorAuth] = None, + mirror_dst_region: Optional[str] = None, + mirror_dst_vpc_id: Optional[str] = None, + mirror_tunnel_id: Optional[str] = None, + mirror_role: Optional[str] = None, + mirror_using_role: Optional[bool] = None, + mirror_return_headers: Optional[MirrorReturnHeaders] = None, + mirror_proxy_pass: Optional[bool] = None, + mirror_is_express_tunnel: Optional[bool] = None, + mirror_dst_slave_vpc_id: Optional[str] = None, + mirror_allow_head_object: Optional[bool] = None, + transparent_mirror_response_codes: Optional[str] = None, + mirror_save_oss_meta: Optional[bool] = None, + mirror_allow_get_image_info: Optional[bool] = None, + mirror_url_probe: Optional[str] = None, + mirror_url_slave: Optional[str] = None, + mirror_user_last_modified: Optional[bool] = None, + mirror_switch_all_errors: Optional[bool] = None, + mirror_multi_alternates: Optional[MirrorMultiAlternates] = None, **kwargs: Any ) -> None: """ @@ -278,6 +548,41 @@ def __init__( mirror_follow_redirect (bool, optional): Specifies whether to redirect the access to the address specified by Location if the origin returns an HTTP 3xx status code. This parameter takes effect only when the value of RedirectType is Mirror. For example, when a mirroring-based back-to-origin request is initiated, the origin returns 302 and Location is specified.* If you set MirrorFollowRedirect to true, OSS continues requesting the resource at the address specified by Location. The access can be redirected up to 10 times. If the access is redirected more than 10 times, the mirroring-based back-to-origin request fails.* If you set MirrorFollowRedirect to false, OSS returns 302 and passes through Location.Default value: true. mirror_check_md5 (bool, optional): Specifies whether to check the MD5 hash of the body of the response returned by the origin. This parameter takes effect only when the value of RedirectType is Mirror. When **MirrorCheckMd5** is set to true and the response returned by the origin includes the Content-Md5 header, OSS checks whether the MD5 hash of the obtained data matches the header value. If the MD5 hash of the obtained data does not match the header value, the obtained data is not stored in OSS. Default value: false. mirror_pass_original_slashes (bool, optional): Whether to transparently pass through to the origin server + mirror_allow_video_snapshot (bool, optional): Whether to allow take video snapshot in mirror-based back-to-origin. + mirror_async_status (int, optional): The HTTP status codes that trigger the asynchronous pull mode in mirror-based back-to-origin. + mirror_taggings (MirrorTaggings, optional): The rules for setting tags when saving files during mirror-based back-to-origin. + mirror_auth (MirrorAuth, optional): The authentication information for the origin server in mirror-based back-to-origin. + mirror_dst_region (str, optional): The VPC region for mirror-based back-to-origin express tunnel. + mirror_dst_vpc_id (str, optional): The VPC ID for mirror-based back-to-origin express tunnel. + mirror_tunnel_id (str, optional): The tunnel ID for mirror-based back-to-origin. + mirror_role (str, optional): The role name used for mirror-based back-to-origin. + mirror_using_role (bool, optional): Whether to use role for mirror-based back-to-origin. + mirror_return_headers (MirrorReturnHeaders, optional): Container to store the rules for setting response headers in mirror-based back-to-origin. + mirror_proxy_pass (bool, optional): Not save data in web-based back-to-origin. + mirror_is_express_tunnel (bool, optional): Mirror-based back-to-origin with express tunnel. + mirror_dst_slave_vpc_id (str, optional): The slave VPC ID for mirror-based back-to-origin express tunnel. + mirror_allow_head_object (bool, optional): Whether to allow take HeadObject in mirror-based back-to-origin. + transparent_mirror_response_codes (str, optional): Specify which status codes returned by the origin server should be passed through to the client along with the body. The value should be HTTP status codes such as 4xx, 5xx, etc., separated by commas (,), for example, 400,404. This setting takes effect only when RedirectType is set to Mirror. When OSS requests content from the origin server, if the origin server returns one of the status codes specified in this parameter, OSS will pass through the status code and body returned by the origin server to the client. If the 404 status code is specified in this parameter, the configured ErrorDocument will be ineffective. + mirror_save_oss_meta (bool, optional): Whether to save OSS metadata in mirror-based back-to-origin. + mirror_allow_get_image_info (bool, optional): Whether to allow getting image info in mirror-based back-to-origin. + mirror_url_probe (str, optional): The probe URL for mirror-based back-to-origin. + mirror_url_slave (str, optional): The slave URL for mirror-based back-to-origin. + mirror_user_last_modified (bool, optional): Whether to use last modified in mirror-based back-to-origin. + mirror_switch_all_errors (bool, optional): Whether to switch all errors in mirror-based back-to-origin. + mirror_multi_alternates (MirrorMultiAlternates, optional): The multi alternates for mirror-based back-to-origin.ected. This parameter can be set to the ${key} variable, which indicates the object name in the request. For example, if ReplaceKeyWith is set to `prefix/${key}.suffix` and the object to access is test, the value of the Location header is `http://example.com/prefix/test.suffix`. + mirror_save_oss_meta (bool, optional): Whether to save the OSS meta in mirror-based back-to-origin. + mirror_allow_get_image_info (bool, optional): Whether to allow get image info in mirror-based back-to-origin. + mirror_follow_redirect (bool, optional): Specifies whether to redirect the access to the address specified by Location if the origin returns an HTTP 3xx status code. This parameter takes effect only when the value of RedirectType is Mirror. For example, when a mirroring-based back-to-origin request is initiated, the origin returns 302 and Location is specified.* If you set MirrorFollowRedirect to true, OSS continues requesting the resource at the address specified by Location. The access can be redirected up to 10 times. If the access is redirected more than 10 times, the mirroring-based back-to-origin request fails.* If you set MirrorFollowRedirect to false, OSS returns 302 and passes through Location.Default value: true. + mirror_url_probe (str, optional): The URL for probing the origin server in mirror-based back-to-origin. + protocol (str, optional): The protocol used for redirection. This parameter takes effect only when RedirectType is set to External or AliCDN. For example, if you access an object named test, Protocol is set to https, and Hostname is set to `example.com`, the value of the Location header is `https://example.com/test`. Valid values: **http** and **https**. + enable_replace_prefix (bool, optional): If this parameter is set to true, the prefix of the object names is replaced with the value specified by ReplaceKeyPrefixWith. If this parameter is not specified or empty, the prefix of object names is truncated. When the ReplaceKeyWith parameter is not empty, the EnableReplacePrefix parameter cannot be set to true.Default value: false. + mirror_pass_original_slashes (bool, optional): Whether to transparently pass through to the origin server + mirror_url_slave (str, optional): The URL for the slave origin server in mirror-based back-to-origin. + mirror_user_last_modified (bool, optional): Whether to use the last modified time of the user in mirror-based back-to-origin. + mirror_switch_all_errors (bool, optional): Whether to switch all errors in mirror-based back-to-origin. + mirror_sni (bool, optional): Whether to pass through SNI in mirror-based back-to-origin. + mirror_check_md5 (bool, optional): Specifies whether to check the MD5 hash of the body of the response returned by the origin. This parameter takes effect only when the value of RedirectType is Mirror. When **MirrorCheckMd5** is set to true and the response returned by the origin includes the Content-Md5 header, OSS checks whether the MD5 hash of the obtained data matches the header value. If the MD5 hash of the obtained data does not match the header value, the obtained data is not stored in OSS. Default value: false. + mirror_multi_alternates (MirrorMultiAlternates, optional): The rules for setting multiple alternates in mirror-based back-to-origin. """ super().__init__(**kwargs) self.mirror_url = mirror_url @@ -295,6 +600,54 @@ def __init__( self.mirror_follow_redirect = mirror_follow_redirect self.mirror_check_md5 = mirror_check_md5 self.mirror_pass_original_slashes = mirror_pass_original_slashes + self.mirror_allow_video_snapshot = mirror_allow_video_snapshot + self.mirror_async_status = mirror_async_status + self.mirror_taggings = mirror_taggings + self.mirror_auth = mirror_auth + self.mirror_dst_region = mirror_dst_region + self.mirror_dst_vpc_id = mirror_dst_vpc_id + self.mirror_tunnel_id = mirror_tunnel_id + self.mirror_role = mirror_role + self.mirror_using_role = mirror_using_role + self.mirror_return_headers = mirror_return_headers + self.mirror_proxy_pass = mirror_proxy_pass + self.mirror_is_express_tunnel = mirror_is_express_tunnel + self.mirror_dst_slave_vpc_id = mirror_dst_slave_vpc_id + self.mirror_allow_head_object = mirror_allow_head_object + self.transparent_mirror_response_codes = transparent_mirror_response_codes + self.mirror_save_oss_meta = mirror_save_oss_meta + self.mirror_allow_get_image_info = mirror_allow_get_image_info + self.mirror_url_probe = mirror_url_probe + self.mirror_url_slave = mirror_url_slave + self.mirror_user_last_modified = mirror_user_last_modified + self.mirror_switch_all_errors = mirror_switch_all_errors + self.mirror_multi_alternates = mirror_multi_alternates + + +class RoutingRuleLuaConfig(serde.Model): + """ + Lua script config for the routing rule. + """ + + _attribute_map = { + 'script': {'tag': 'xml', 'rename': 'Script', 'type': 'str'}, + } + + _xml_map = { + 'name': 'LuaConfig' + } + + def __init__( + self, + script: Optional[str] = None, + **kwargs: Any + ) -> None: + """ + Args: + script (str, optional): The name of the Lua script. + """ + super().__init__(**kwargs) + self.script = script class RoutingRule(serde.Model): @@ -306,6 +659,7 @@ class RoutingRule(serde.Model): 'rule_number': {'tag': 'xml', 'rename': 'RuleNumber', 'type': 'int'}, 'condition': {'tag': 'xml', 'rename': 'Condition', 'type': 'RoutingRuleCondition'}, 'redirect': {'tag': 'xml', 'rename': 'Redirect', 'type': 'RoutingRuleRedirect'}, + 'lua_config': {'tag': 'xml', 'rename': 'LuaConfig', 'type': 'RoutingRuleLuaConfig'}, } _xml_map = { @@ -315,6 +669,7 @@ class RoutingRule(serde.Model): _dependency_map = { 'RoutingRuleCondition': {'new': lambda: RoutingRuleCondition()}, 'RoutingRuleRedirect': {'new': lambda: RoutingRuleRedirect()}, + 'RoutingRuleLuaConfig': {'new': lambda: RoutingRuleLuaConfig()}, } def __init__( @@ -322,6 +677,7 @@ def __init__( rule_number: Optional[int] = None, condition: Optional[RoutingRuleCondition] = None, redirect: Optional[RoutingRuleRedirect] = None, + lua_config: Optional[RoutingRuleLuaConfig] = None, **kwargs: Any ) -> None: """ @@ -329,11 +685,13 @@ def __init__( rule_number (int, optional): The sequence number that is used to match and run the redirection rules. OSS matches redirection rules based on this parameter. If a match succeeds, only the rule is run and the subsequent rules are not run. This parameter must be specified if RoutingRule is specified. condition (RoutingRuleCondition, optional): The matching condition. If all of the specified conditions are met, the rule is run. A rule is considered matched only when the rule meets the conditions that are specified by all nodes in Condition. This parameter must be specified if RoutingRule is specified. redirect (RoutingRuleRedirect, optional): The operation to perform after the rule is matched. This parameter must be specified if RoutingRule is specified. + lua_config (RoutingRuleLuaConfig, optional): The Lua script config of this rule. """ super().__init__(**kwargs) self.rule_number = rule_number self.condition = condition self.redirect = redirect + self.lua_config = lua_config class RoutingRules(serde.Model): diff --git a/sample/put_bucket_website.py b/sample/put_bucket_website.py index 0db1ce3..4cdcefa 100644 --- a/sample/put_bucket_website.py +++ b/sample/put_bucket_website.py @@ -110,6 +110,87 @@ def main(): redirect_type='External', host_name='example.com', ), + ), oss.RoutingRule( + rule_number=4, + condition=oss.RoutingRuleCondition( + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + redirect_type='Mirror', + mirror_url='http://example.com/', + ), + lua_config=oss.RoutingRuleLuaConfig( + script='test.lua', + ), + ), oss.RoutingRule( + rule_number=5, + condition=oss.RoutingRuleCondition( + key_suffix_equals='abc/', + http_error_code_returned_equals=404, + include_headers=[oss.RoutingRuleIncludeHeader( + key='key1', + equals='value1', + ), oss.RoutingRuleIncludeHeader( + key='key2', + equals='value2', + )], + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + redirect_type='Mirror', + mirror_url='http://example.com/', + mirror_allow_video_snapshot=True, + + mirror_taggings=oss.MirrorTaggings( + taggings=[oss.MirrorTagging( + key='tag-key1', + value='tag-value1', + ), oss.MirrorTagging( + key='tag-key2', + value='tag-value2', + )], + ), + mirror_auth=oss.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='S3V4', + region='cn-hangzhou', + ), + mirror_dst_region='oss-cn-hangzhou', + mirror_role='test-role', + mirror_using_role=True, + mirror_return_headers=oss.MirrorReturnHeaders( + return_headers=[oss.ReturnHeader( + key='header-key1', + value='header-value1', + ), oss.ReturnHeader( + key='header-key2', + value='header-value2', + )], + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=True, + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.example.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=False, + mirror_multi_alternates=oss.MirrorMultiAlternates( + mirror_multi_alternates=[oss.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://alternate1.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-1', + mirror_multi_alternate_dst_region='oss-cn-shanghai', + ), oss.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://alternate2.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-2', + mirror_multi_alternate_dst_region='oss-cn-beijing', + )], + ), + ), )], ), ), diff --git a/tests/integration/test_bucket_website_client.py b/tests/integration/test_bucket_website_client.py index 5fc1ac8..fe0cd89 100644 --- a/tests/integration/test_bucket_website_client.py +++ b/tests/integration/test_bucket_website_client.py @@ -191,6 +191,277 @@ def test_bucket_website(self): self.assertEqual(24, len(result.request_id)) self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + def test_bucket_website_new_redirect(self): + # create bucket + bucket_name = random_bucket_name() + result = self.client.put_bucket(oss.PutBucketRequest( + bucket=bucket_name, + acl='private', + create_bucket_configuration=oss.CreateBucketConfiguration( + storage_class='IA' + ) + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # put bucket website + result = self.client.put_bucket_website(oss.PutBucketWebsiteRequest( + bucket=bucket_name, + website_configuration=oss.WebsiteConfiguration( + index_document=oss.IndexDocument( + suffix='index.html', + support_sub_dir=True, + type=0, + ), + error_document=oss.ErrorDocument( + key='error.html', + http_status=404, + ), + routing_rules=oss.RoutingRules( + routing_rules=[oss.RoutingRule( + rule_number=1, + condition=oss.RoutingRuleCondition( + key_suffix_equals='abc/', + http_error_code_returned_equals=404, + include_headers=[oss.RoutingRuleIncludeHeader( + key='key1', + equals='value1', + ), oss.RoutingRuleIncludeHeader( + key='key2', + equals='value2', + )], + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + mirror_url='http://example.com/', + enable_replace_prefix=True, + pass_query_string=False, + mirror_headers=oss.MirrorHeaders( + pass_all=True, + passs=['myheader-key1', 'myheader-key2'], + removes=['myheader-key3', 'myheader-key4'], + sets=[oss.MirrorHeadersSet( + key='myheader-key5', + value='myheader-value', + ), oss.MirrorHeadersSet( + key='myheader-key6', + value='myheader-value2', + )], + ), + mirror_sni=False, + replace_key_prefix_with='abc/', + redirect_type='Mirror', + mirror_pass_query_string=False, + mirror_follow_redirect=True, + mirror_check_md5=True, + mirror_pass_original_slashes=True, + mirror_allow_video_snapshot=True, + # mirror_async_status=302, + mirror_taggings=oss.MirrorTaggings( + taggings=[oss.MirrorTagging( + key='tag-key1', + value='tag-value1', + ), oss.MirrorTagging( + key='tag-key2', + value='tag-value2', + )], + ), + mirror_auth=oss.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='S3V4', + region='cn-hangzhou', + ), + mirror_dst_region='oss-cn-hangzhou', + # mirror_dst_vpc_id='vpc-123456', + # mirror_tunnel_id='tunnel-123456', + mirror_role='test-role', + mirror_using_role=True, + mirror_return_headers=oss.MirrorReturnHeaders( + return_headers=[oss.ReturnHeader( + key='header-key1', + value='header-value1', + ), oss.ReturnHeader( + key='header-key2', + value='header-value2', + )], + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=True, + # mirror_dst_slave_vpc_id='vpc-slave-123456', + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.example.com/', + # mirror_url_slave='http://slave.example.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=False, + mirror_multi_alternates=oss.MirrorMultiAlternates( + mirror_multi_alternates=[oss.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://alternate1.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-1', + mirror_multi_alternate_dst_region='oss-cn-shanghai', + ), oss.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://alternate2.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-2', + mirror_multi_alternate_dst_region='oss-cn-beijing', + )], + ), + ), + )], + ), + ), + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # get bucket website + result = self.client.get_bucket_website(oss.GetBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + self.assertEqual('index.html', result.website_configuration.index_document.suffix) + self.assertEqual(True, result.website_configuration.index_document.support_sub_dir) + self.assertEqual(0, result.website_configuration.index_document.type) + self.assertEqual('error.html', result.website_configuration.error_document.key) + self.assertEqual(404, result.website_configuration.error_document.http_status) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].rule_number) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].condition.key_suffix_equals) + self.assertEqual(404, result.website_configuration.routing_rules.routing_rules[0].condition.http_error_code_returned_equals) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].equals) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].equals) + self.assertEqual('key', result.website_configuration.routing_rules.routing_rules[0].condition.key_prefix_equals) + self.assertEqual('http://example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.enable_replace_prefix) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.pass_query_string) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.pass_all) + self.assertEqual('myheader-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[0]) + self.assertEqual('myheader-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[1]) + self.assertEqual('myheader-key3', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[0]) + self.assertEqual('myheader-key4', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[1]) + self.assertEqual('myheader-key5', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].key) + self.assertEqual('myheader-value', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].value) + self.assertEqual('myheader-key6', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].key) + self.assertEqual('myheader-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].value) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_sni) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].redirect.replace_key_prefix_with) + self.assertEqual('Mirror', result.website_configuration.routing_rules.routing_rules[0].redirect.redirect_type) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_query_string) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_follow_redirect) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_check_md5) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_original_slashes) + # New fields verification + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_video_snapshot) + self.assertEqual('tag-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('tag-value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('tag-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[1].key) + self.assertEqual('tag-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[1].value) + self.assertEqual('test-access-key-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_id) + self.assertEqual('S3V4', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.auth_type) + self.assertEqual('cn-hangzhou', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.region) + self.assertEqual('oss-cn-hangzhou', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_region) + self.assertEqual('test-role', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_role) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_using_role) + self.assertEqual('header-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('header-value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual('header-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[1].key) + self.assertEqual('header-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[1].value) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_is_express_tunnel) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_head_object) + self.assertEqual('404,500', result.website_configuration.routing_rules.routing_rules[0].redirect.transparent_mirror_response_codes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_save_oss_meta) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_probe) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_user_last_modified) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_switch_all_errors) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://alternate1.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('vpc-alternate-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('oss-cn-shanghai', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_number) + self.assertEqual('http://alternate2.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_url) + self.assertEqual('vpc-alternate-2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_vpc_id) + self.assertEqual('oss-cn-beijing', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_dst_region) + + # delete bucket website + result = self.client.delete_bucket_website(oss.DeleteBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(204, result.status_code) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + def test_bucket_website_lua_config(self): + # create bucket + bucket_name = random_bucket_name() + result = self.client.put_bucket(oss.PutBucketRequest( + bucket=bucket_name, + acl='private', + create_bucket_configuration=oss.CreateBucketConfiguration( + storage_class='IA' + ) + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # put bucket website with lua_config + result = self.client.put_bucket_website(oss.PutBucketWebsiteRequest( + bucket=bucket_name, + website_configuration=oss.WebsiteConfiguration( + index_document=oss.IndexDocument( + suffix='index.html', + ), + routing_rules=oss.RoutingRules( + routing_rules=[oss.RoutingRule( + rule_number=1, + condition=oss.RoutingRuleCondition( + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + redirect_type='Mirror', + mirror_url='http://example.com/', + ), + lua_config=oss.RoutingRuleLuaConfig( + script='test.lua', + ), + )], + ), + ), + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + + # get bucket website and verify lua_config + result = self.client.get_bucket_website(oss.GetBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual('test.lua', result.website_configuration.routing_rules.routing_rules[0].lua_config.script) + + # delete bucket website + result = self.client.delete_bucket_website(oss.DeleteBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(204, result.status_code) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + def test_bucket_website_v1(self): # create bucket bucket_name = random_bucket_name() @@ -383,6 +654,276 @@ def test_bucket_website_v1(self): self.assertEqual(24, len(result.request_id)) self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + def test_bucket_website_new_redirect_v1(self): + # create bucket + bucket_name = random_bucket_name() + result = self.signv1_client.put_bucket(oss.PutBucketRequest( + bucket=bucket_name, + acl='private', + create_bucket_configuration=oss.CreateBucketConfiguration( + storage_class='IA' + ) + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # put bucket website + result = self.signv1_client.put_bucket_website(oss.PutBucketWebsiteRequest( + bucket=bucket_name, + website_configuration=oss.WebsiteConfiguration( + index_document=oss.IndexDocument( + suffix='index.html', + support_sub_dir=True, + type=0, + ), + error_document=oss.ErrorDocument( + key='error.html', + http_status=404, + ), + routing_rules=oss.RoutingRules( + routing_rules=[oss.RoutingRule( + rule_number=1, + condition=oss.RoutingRuleCondition( + key_suffix_equals='abc/', + http_error_code_returned_equals=404, + include_headers=[oss.RoutingRuleIncludeHeader( + key='key1', + equals='value1', + ), oss.RoutingRuleIncludeHeader( + key='key2', + equals='value2', + )], + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + mirror_url='http://example.com/', + enable_replace_prefix=True, + pass_query_string=False, + mirror_headers=oss.MirrorHeaders( + pass_all=True, + passs=['myheader-key1', 'myheader-key2'], + removes=['myheader-key3', 'myheader-key4'], + sets=[oss.MirrorHeadersSet( + key='myheader-key5', + value='myheader-value', + ), oss.MirrorHeadersSet( + key='myheader-key6', + value='myheader-value2', + )], + ), + mirror_sni=False, + replace_key_prefix_with='abc/', + redirect_type='Mirror', + mirror_pass_query_string=False, + mirror_follow_redirect=True, + mirror_check_md5=True, + mirror_pass_original_slashes=True, + mirror_allow_video_snapshot=True, + # mirror_async_status=302, + mirror_taggings=oss.MirrorTaggings( + taggings=[oss.MirrorTagging( + key='tag-key1', + value='tag-value1', + ), oss.MirrorTagging( + key='tag-key2', + value='tag-value2', + )], + ), + mirror_auth=oss.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='S3V4', + region='cn-hangzhou', + ), + mirror_dst_region='oss-cn-hangzhou', + # mirror_dst_vpc_id='vpc-123456', + # mirror_tunnel_id='tunnel-123456', + mirror_role='test-role', + mirror_using_role=True, + mirror_return_headers=oss.MirrorReturnHeaders( + return_headers=[oss.ReturnHeader( + key='header-key1', + value='header-value1', + ), oss.ReturnHeader( + key='header-key2', + value='header-value2', + )], + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=True, + # mirror_dst_slave_vpc_id='vpc-slave-123456', + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.example.com/', + # mirror_url_slave='http://slave.example.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=False, + mirror_multi_alternates=oss.MirrorMultiAlternates( + mirror_multi_alternates=[oss.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://alternate1.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-1', + mirror_multi_alternate_dst_region='oss-cn-shanghai', + ), oss.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://alternate2.example.com/', + mirror_multi_alternate_vpc_id='vpc-alternate-2', + mirror_multi_alternate_dst_region='oss-cn-beijing', + )], + ), + ), + )], + ), + ), + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # get bucket website + result = self.signv1_client.get_bucket_website(oss.GetBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + self.assertEqual('index.html', result.website_configuration.index_document.suffix) + self.assertEqual(True, result.website_configuration.index_document.support_sub_dir) + self.assertEqual(0, result.website_configuration.index_document.type) + self.assertEqual('error.html', result.website_configuration.error_document.key) + self.assertEqual(404, result.website_configuration.error_document.http_status) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].rule_number) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].condition.key_suffix_equals) + self.assertEqual(404, result.website_configuration.routing_rules.routing_rules[0].condition.http_error_code_returned_equals) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].equals) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].equals) + self.assertEqual('key', result.website_configuration.routing_rules.routing_rules[0].condition.key_prefix_equals) + self.assertEqual('http://example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.enable_replace_prefix) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.pass_query_string) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.pass_all) + self.assertEqual('myheader-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[0]) + self.assertEqual('myheader-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[1]) + self.assertEqual('myheader-key3', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[0]) + self.assertEqual('myheader-key4', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[1]) + self.assertEqual('myheader-key5', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].key) + self.assertEqual('myheader-value', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].value) + self.assertEqual('myheader-key6', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].key) + self.assertEqual('myheader-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].value) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_sni) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].redirect.replace_key_prefix_with) + self.assertEqual('Mirror', result.website_configuration.routing_rules.routing_rules[0].redirect.redirect_type) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_query_string) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_follow_redirect) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_check_md5) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_original_slashes) + # New fields verification + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_video_snapshot) + self.assertEqual('tag-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('tag-value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('tag-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[1].key) + self.assertEqual('tag-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[1].value) + self.assertEqual('test-access-key-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_id) + self.assertEqual('S3V4', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.auth_type) + self.assertEqual('cn-hangzhou', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.region) + self.assertEqual('oss-cn-hangzhou', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_region) + self.assertEqual('test-role', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_role) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_using_role) + self.assertEqual('header-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('header-value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual('header-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[1].key) + self.assertEqual('header-value2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[1].value) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_is_express_tunnel) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_head_object) + self.assertEqual('404,500', result.website_configuration.routing_rules.routing_rules[0].redirect.transparent_mirror_response_codes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_save_oss_meta) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_probe) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_user_last_modified) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_switch_all_errors) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://alternate1.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('vpc-alternate-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('oss-cn-shanghai', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_number) + self.assertEqual('http://alternate2.example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_url) + self.assertEqual('vpc-alternate-2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_vpc_id) + self.assertEqual('oss-cn-beijing', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[1].mirror_multi_alternate_dst_region) + + # delete bucket website + result = self.signv1_client.delete_bucket_website(oss.DeleteBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(204, result.status_code) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + def test_bucket_website_lua_config_v1(self): + # create bucket + bucket_name = random_bucket_name() + result = self.signv1_client.put_bucket(oss.PutBucketRequest( + bucket=bucket_name, + acl='private', + create_bucket_configuration=oss.CreateBucketConfiguration( + storage_class='IA' + ) + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + + # put bucket website with lua_config + result = self.signv1_client.put_bucket_website(oss.PutBucketWebsiteRequest( + bucket=bucket_name, + website_configuration=oss.WebsiteConfiguration( + index_document=oss.IndexDocument( + suffix='index.html', + ), + routing_rules=oss.RoutingRules( + routing_rules=[oss.RoutingRule( + rule_number=1, + condition=oss.RoutingRuleCondition( + key_prefix_equals='key', + ), + redirect=oss.RoutingRuleRedirect( + redirect_type='Mirror', + mirror_url='http://example.com/', + ), + lua_config=oss.RoutingRuleLuaConfig( + script='test.lua', + ), + )], + ), + ), + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + + # get bucket website and verify lua_config + result = self.signv1_client.get_bucket_website(oss.GetBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(200, result.status_code) + self.assertEqual('OK', result.status) + self.assertEqual('test.lua', result.website_configuration.routing_rules.routing_rules[0].lua_config.script) + + # delete bucket website + result = self.signv1_client.delete_bucket_website(oss.DeleteBucketWebsiteRequest( + bucket=bucket_name, + )) + self.assertEqual(204, result.status_code) + self.assertEqual(24, len(result.request_id)) + self.assertEqual(24, len(result.headers.get('x-oss-request-id'))) + def test_bucket_website_fail(self): # create bucket bucket_name = random_bucket_name() @@ -445,4 +986,4 @@ def test_bucket_website_fail(self): serr = cast(oss.exceptions.ServiceError, ope.unwrap()) self.assertEqual(403, serr.status_code) self.assertEqual(24, len(serr.request_id)) - self.assertEqual('InvalidAccessKeyId', serr.code) \ No newline at end of file + self.assertEqual('InvalidAccessKeyId', serr.code) diff --git a/tests/unit/models/test_bucket_website.py b/tests/unit/models/test_bucket_website.py index 786a1a4..d4fe85b 100644 --- a/tests/unit/models/test_bucket_website.py +++ b/tests/unit/models/test_bucket_website.py @@ -71,6 +71,53 @@ def test_constructor_request(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=False, + mirror_allow_video_snapshot=True, + mirror_async_status=1, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key1', + value='value1' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='type', + region='region' + ), + mirror_dst_region='region', + mirror_dst_vpc_id='vpc-id', + mirror_tunnel_id='tunnel-id', + mirror_role='role', + mirror_using_role=True, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key1', + value='value1' + )] + ), + mirror_proxy_pass=True, + mirror_is_express_tunnel=True, + mirror_dst_slave_vpc_id='slave-vpc-id', + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.com/', + mirror_url_slave='http://slave.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=True, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://multi1.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-1', + mirror_multi_alternate_dst_region='multi-region-1' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='test.lua', ), ), model.RoutingRule( rule_number=6052, @@ -113,6 +160,53 @@ def test_constructor_request(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=True, + mirror_allow_video_snapshot=False, + mirror_async_status=2, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key2', + value='value2' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id-2', + access_key_secret='test-access-key-secret-2', + auth_type='type2', + region='region2' + ), + mirror_dst_region='region2', + mirror_dst_vpc_id='vpc-id-2', + mirror_tunnel_id='tunnel-id-2', + mirror_role='role2', + mirror_using_role=False, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key2', + value='value2' + )] + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=False, + mirror_dst_slave_vpc_id='slave-vpc-id-2', + mirror_allow_head_object=False, + transparent_mirror_response_codes='403', + mirror_save_oss_meta=False, + mirror_allow_get_image_info=False, + mirror_url_probe='http://probe2.com/', + mirror_url_slave='http://slave2.com/', + mirror_user_last_modified=False, + mirror_switch_all_errors=False, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://multi2.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-2', + mirror_multi_alternate_dst_region='multi-region-2' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='script2.lua', ), )], ), @@ -155,6 +249,37 @@ def test_constructor_request(self): self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_follow_redirect) self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_check_md5) self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_original_slashes) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_video_snapshot) + self.assertEqual(1, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_async_status) + self.assertEqual('key1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_secret) + self.assertEqual('type', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.auth_type) + self.assertEqual('region', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.region) + self.assertEqual('region', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_region) + self.assertEqual('vpc-id', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_tunnel_id) + self.assertEqual('role', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_role) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_using_role) + self.assertEqual('key1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_proxy_pass) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_is_express_tunnel) + self.assertEqual('slave-vpc-id', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_head_object) + self.assertEqual('404,500', request.website_configuration.routing_rules.routing_rules[0].redirect.transparent_mirror_response_codes) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_save_oss_meta) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe.com/', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_probe) + self.assertEqual('http://slave.com/', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_slave) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_user_last_modified) + self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_switch_all_errors) + self.assertEqual(1, request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi1.com/', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-1', request.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual('test.lua', request.website_configuration.routing_rules.routing_rules[0].lua_config.script) self.assertEqual(6052, request.website_configuration.routing_rules.routing_rules[1].rule_number) self.assertEqual('bbc/', request.website_configuration.routing_rules.routing_rules[1].condition.key_suffix_equals) self.assertEqual(403, request.website_configuration.routing_rules.routing_rules[1].condition.http_error_code_returned_equals) @@ -186,6 +311,37 @@ def test_constructor_request(self): self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_follow_redirect) self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_check_md5) self.assertEqual(True, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_pass_original_slashes) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_video_snapshot) + self.assertEqual(2, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_async_status) + self.assertEqual('key2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_secret) + self.assertEqual('type2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.auth_type) + self.assertEqual('region2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.region) + self.assertEqual('region2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_region) + self.assertEqual('vpc-id-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_tunnel_id) + self.assertEqual('role2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_role) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_using_role) + self.assertEqual('key2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_proxy_pass) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_is_express_tunnel) + self.assertEqual('slave-vpc-id-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_head_object) + self.assertEqual('403', request.website_configuration.routing_rules.routing_rules[1].redirect.transparent_mirror_response_codes) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_save_oss_meta) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe2.com/', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_probe) + self.assertEqual('http://slave2.com/', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_slave) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_user_last_modified) + self.assertEqual(False, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_switch_all_errors) + self.assertEqual(2, request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi2.com/', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-2', request.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual('script2.lua', request.website_configuration.routing_rules.routing_rules[1].lua_config.script) def test_serialize_request(self): request = model.PutBucketWebsiteRequest( @@ -242,6 +398,53 @@ def test_serialize_request(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=False, + mirror_allow_video_snapshot=True, + mirror_async_status=1, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key1', + value='value1' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='type', + region='region' + ), + mirror_dst_region='region', + mirror_dst_vpc_id='vpc-id', + mirror_tunnel_id='tunnel-id', + mirror_role='role', + mirror_using_role=True, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key1', + value='value1' + )] + ), + mirror_proxy_pass=True, + mirror_is_express_tunnel=True, + mirror_dst_slave_vpc_id='slave-vpc-id', + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.com/', + mirror_url_slave='http://slave.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=True, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://multi1.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-1', + mirror_multi_alternate_dst_region='multi-region-1' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='test.lua', ), ), model.RoutingRule( rule_number=6052, @@ -284,6 +487,53 @@ def test_serialize_request(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=True, + mirror_allow_video_snapshot=False, + mirror_async_status=2, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key2', + value='value2' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id-2', + access_key_secret='test-access-key-secret-2', + auth_type='type2', + region='region2' + ), + mirror_dst_region='region2', + mirror_dst_vpc_id='vpc-id-2', + mirror_tunnel_id='tunnel-id-2', + mirror_role='role2', + mirror_using_role=False, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key2', + value='value2' + )] + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=False, + mirror_dst_slave_vpc_id='slave-vpc-id-2', + mirror_allow_head_object=False, + transparent_mirror_response_codes='403', + mirror_save_oss_meta=False, + mirror_allow_get_image_info=False, + mirror_url_probe='http://probe2.com/', + mirror_url_slave='http://slave2.com/', + mirror_user_last_modified=False, + mirror_switch_all_errors=False, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://multi2.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-2', + mirror_multi_alternate_dst_region='multi-region-2' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='script2.lua', ), )], ), @@ -418,6 +668,53 @@ def test_constructor_result(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=False, + mirror_allow_video_snapshot=True, + mirror_async_status=1, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key1', + value='value1' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id', + access_key_secret='test-access-key-secret', + auth_type='type', + region='region' + ), + mirror_dst_region='region', + mirror_dst_vpc_id='vpc-id', + mirror_tunnel_id='tunnel-id', + mirror_role='role', + mirror_using_role=True, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key1', + value='value1' + )] + ), + mirror_proxy_pass=True, + mirror_is_express_tunnel=True, + mirror_dst_slave_vpc_id='slave-vpc-id', + mirror_allow_head_object=True, + transparent_mirror_response_codes='404,500', + mirror_save_oss_meta=True, + mirror_allow_get_image_info=True, + mirror_url_probe='http://probe.com/', + mirror_url_slave='http://slave.com/', + mirror_user_last_modified=True, + mirror_switch_all_errors=True, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=1, + mirror_multi_alternate_url='http://multi1.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-1', + mirror_multi_alternate_dst_region='multi-region-1' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='test.lua', ), ), model.RoutingRule( rule_number=6052, @@ -460,6 +757,53 @@ def test_constructor_result(self): mirror_follow_redirect=True, mirror_check_md5=False, mirror_pass_original_slashes=True, + mirror_allow_video_snapshot=False, + mirror_async_status=2, + mirror_taggings=model.MirrorTaggings( + taggings=[model.MirrorTagging( + key='key2', + value='value2' + )] + ), + mirror_auth=model.MirrorAuth( + access_key_id='test-access-key-id-2', + access_key_secret='test-access-key-secret-2', + auth_type='type2', + region='region2' + ), + mirror_dst_region='region2', + mirror_dst_vpc_id='vpc-id-2', + mirror_tunnel_id='tunnel-id-2', + mirror_role='role2', + mirror_using_role=False, + mirror_return_headers=model.MirrorReturnHeaders( + return_headers=[model.ReturnHeader( + key='key2', + value='value2' + )] + ), + mirror_proxy_pass=False, + mirror_is_express_tunnel=False, + mirror_dst_slave_vpc_id='slave-vpc-id-2', + mirror_allow_head_object=False, + transparent_mirror_response_codes='403', + mirror_save_oss_meta=False, + mirror_allow_get_image_info=False, + mirror_url_probe='http://probe2.com/', + mirror_url_slave='http://slave2.com/', + mirror_user_last_modified=False, + mirror_switch_all_errors=False, + mirror_multi_alternates=model.MirrorMultiAlternates( + mirror_multi_alternates=[model.MirrorMultiAlternate( + mirror_multi_alternate_number=2, + mirror_multi_alternate_url='http://multi2.com/', + mirror_multi_alternate_vpc_id='multi-vpc-id-2', + mirror_multi_alternate_dst_region='multi-region-2' + )] + ) + ), + lua_config=model.RoutingRuleLuaConfig( + script='script2.lua', ), )], ), @@ -501,6 +845,68 @@ def test_constructor_result(self): self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_follow_redirect) self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_check_md5) self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_original_slashes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_video_snapshot) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_async_status) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_secret) + self.assertEqual('type', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.auth_type) + self.assertEqual('region', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.region) + self.assertEqual('region', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_region) + self.assertEqual('vpc-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_tunnel_id) + self.assertEqual('role', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_role) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_using_role) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_proxy_pass) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_is_express_tunnel) + self.assertEqual('slave-vpc-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_head_object) + self.assertEqual('404,500', result.website_configuration.routing_rules.routing_rules[0].redirect.transparent_mirror_response_codes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_save_oss_meta) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_probe) + self.assertEqual('http://slave.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_slave) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_user_last_modified) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_switch_all_errors) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi1.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual('test.lua', result.website_configuration.routing_rules.routing_rules[0].lua_config.script) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_video_snapshot) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_async_status) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_secret) + self.assertEqual('type2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.auth_type) + self.assertEqual('region2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.region) + self.assertEqual('region2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_region) + self.assertEqual('vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_tunnel_id) + self.assertEqual('role2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_role) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_using_role) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_proxy_pass) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_is_express_tunnel) + self.assertEqual('slave-vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_head_object) + self.assertEqual('403', result.website_configuration.routing_rules.routing_rules[1].redirect.transparent_mirror_response_codes) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_save_oss_meta) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_probe) + self.assertEqual('http://slave2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_slave) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_user_last_modified) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_switch_all_errors) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual('script2.lua', result.website_configuration.routing_rules.routing_rules[1].lua_config.script) self.assertEqual(6052, result.website_configuration.routing_rules.routing_rules[1].rule_number) self.assertEqual('bbc/', result.website_configuration.routing_rules.routing_rules[1].condition.key_suffix_equals) self.assertEqual(403, result.website_configuration.routing_rules.routing_rules[1].condition.http_error_code_returned_equals) @@ -532,6 +938,7 @@ def test_constructor_result(self): self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_follow_redirect) self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_check_md5) self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_pass_original_slashes) + self.assertEqual('script2.lua', result.website_configuration.routing_rules.routing_rules[1].lua_config.script) def test_deserialize_result(self): xml_data = r''' @@ -545,6 +952,8 @@ def test_deserialize_result(self): index.html + true + 0 error.html @@ -556,6 +965,15 @@ def test_deserialize_result(self): abc/ 404 + abc/ + + key1 + value1 + + + key2 + value2 + Mirror @@ -564,6 +982,58 @@ def test_deserialize_result(self): true true false + false + true + 1 + false + http + abc/ + 203 + aab/ + example.com + true + region + vpc-id + tunnel-id + role + true + + + key1 + value1 + + + true + true + slave-vpc-id + true + 404,500 + true + true + http://probe.com/ + http://slave.com/ + true + true + + + key1 + value1 + + + + test-access-key-id + test-access-key-secret + type + region + + + + 1 + http://multi1.com/ + multi-vpc-id-1 + multi-region-1 + + true myheader-key1 @@ -572,29 +1042,111 @@ def test_deserialize_result(self): myheader-key4 myheader-key5 - myheader-value5 + myheader-value + + + myheader-key6 + myheader-valu2 + + + 2 - host - test.oss-cn-beijing-internal.aliyuncs.com + key21 + value21 + + + key22 + value22U abc/ - 404 + 403 + bbc/ AliCDN - http + https example.com - false - prefix/${key}.suffix - 301 + true + prefix/${key}.suffix + 2970 + prefix/${key} + true + http://example.com/ + false + true + false + true + false + 2 + true + region2 + vpc-id-2 + tunnel-id-2 + role2 + false + + + key2 + value2 + + + false + false + slave-vpc-id-2 + false + 403 + false + false + http://probe2.com/ + http://slave2.com/ + false + false + + + key2 + value2 + + + + test-access-key-id-2 + test-access-key-secret-2 + type2 + region2 + + + + 2 + http://multi2.com/ + multi-vpc-id-2 + multi-region-2 + + + + true + myheader-key21 + myheader-key22 + myheader-key23 + myheader-key24 + + myheader-key25 + myheader-value2 + + + myheader-key26 + myheader-value22 + + + + + @@ -612,35 +1164,134 @@ def test_deserialize_result(self): serde.deserialize_output(result, op_output, custom_deserializer=deserializer) self.assertEqual('OK', result.status) self.assertEqual('index.html', result.website_configuration.index_document.suffix) + self.assertEqual(True, result.website_configuration.index_document.support_sub_dir) + self.assertEqual(0, result.website_configuration.index_document.type) self.assertEqual('error.html', result.website_configuration.error_document.key) self.assertEqual(404, result.website_configuration.error_document.http_status) self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].rule_number) self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].condition.key_prefix_equals) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].condition.key_suffix_equals) self.assertEqual(404, result.website_configuration.routing_rules.routing_rules[0].condition.http_error_code_returned_equals) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[0].equals) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[0].condition.include_headers[1].equals) self.assertEqual('Mirror', result.website_configuration.routing_rules.routing_rules[0].redirect.redirect_type) self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.pass_query_string) self.assertEqual('http://example.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url) + self.assertEqual('aab/', result.website_configuration.routing_rules.routing_rules[0].redirect.replace_key_with) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.enable_replace_prefix) self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_query_string) self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_follow_redirect) self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_check_md5) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_pass_original_slashes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_video_snapshot) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_async_status) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_sni) + self.assertEqual('http', result.website_configuration.routing_rules.routing_rules[0].redirect.protocol) + self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[0].redirect.replace_key_prefix_with) + self.assertEqual(203, result.website_configuration.routing_rules.routing_rules[0].redirect.http_redirect_code) + self.assertEqual('example.com', result.website_configuration.routing_rules.routing_rules[0].redirect.host_name) self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.pass_all) self.assertEqual('myheader-key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[0]) self.assertEqual('myheader-key2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.passs[1]) self.assertEqual('myheader-key3', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[0]) self.assertEqual('myheader-key4', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.removes[1]) self.assertEqual('myheader-key5', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].key) - self.assertEqual('myheader-value5', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].value) + self.assertEqual('myheader-value', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[0].value) + self.assertEqual('myheader-key6', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].key) + self.assertEqual('myheader-valu2', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_headers.sets[1].value) + self.assertEqual('region', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_region) + self.assertEqual('vpc-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_tunnel_id) + self.assertEqual('role', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_role) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_using_role) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_proxy_pass) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_is_express_tunnel) + self.assertEqual('slave-vpc-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_head_object) + self.assertEqual('404,500', result.website_configuration.routing_rules.routing_rules[0].redirect.transparent_mirror_response_codes) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_save_oss_meta) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_probe) + self.assertEqual('http://slave.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_url_slave) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_user_last_modified) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_switch_all_errors) + self.assertEqual('key1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.access_key_secret) + self.assertEqual('type', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.auth_type) + self.assertEqual('region', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_auth.region) + self.assertEqual(1, result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi1.com/', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-1', result.website_configuration.routing_rules.routing_rules[0].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual('test.lua', result.website_configuration.routing_rules.routing_rules[0].lua_config.script) self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[1].rule_number) - self.assertEqual('host', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[0].key) - self.assertEqual('test.oss-cn-beijing-internal.aliyuncs.com', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[0].equals) + self.assertEqual('key21', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[0].key) + self.assertEqual('value21', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[0].equals) + self.assertEqual('key22', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[1].key) + self.assertEqual('value22U', result.website_configuration.routing_rules.routing_rules[1].condition.include_headers[1].equals) self.assertEqual('abc/', result.website_configuration.routing_rules.routing_rules[1].condition.key_prefix_equals) - self.assertEqual(404, result.website_configuration.routing_rules.routing_rules[1].condition.http_error_code_returned_equals) + self.assertEqual('bbc/', result.website_configuration.routing_rules.routing_rules[1].condition.key_suffix_equals) + self.assertEqual(403, result.website_configuration.routing_rules.routing_rules[1].condition.http_error_code_returned_equals) self.assertEqual('AliCDN', result.website_configuration.routing_rules.routing_rules[1].redirect.redirect_type) - self.assertEqual('http', result.website_configuration.routing_rules.routing_rules[1].redirect.protocol) + self.assertEqual('https', result.website_configuration.routing_rules.routing_rules[1].redirect.protocol) self.assertEqual('example.com', result.website_configuration.routing_rules.routing_rules[1].redirect.host_name) - self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.pass_query_string) - self.assertEqual('prefix/${key}.suffix', result.website_configuration.routing_rules.routing_rules[1].redirect.replace_key_with) - self.assertEqual(301, result.website_configuration.routing_rules.routing_rules[1].redirect.http_redirect_code) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.pass_query_string) + self.assertEqual('prefix/${key}.suffix', result.website_configuration.routing_rules.routing_rules[1].redirect.replace_key_prefix_with) + self.assertEqual(2970, result.website_configuration.routing_rules.routing_rules[1].redirect.http_redirect_code) + self.assertEqual('prefix/${key}', result.website_configuration.routing_rules.routing_rules[1].redirect.replace_key_with) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.enable_replace_prefix) + self.assertEqual('http://example.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_pass_query_string) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_follow_redirect) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_check_md5) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_pass_original_slashes) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_video_snapshot) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_async_status) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_sni) + self.assertEqual('slave-vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_slave_vpc_id) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_head_object) + self.assertEqual('403', result.website_configuration.routing_rules.routing_rules[1].redirect.transparent_mirror_response_codes) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_save_oss_meta) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_allow_get_image_info) + self.assertEqual('http://probe2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_probe) + self.assertEqual('http://slave2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_url_slave) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_user_last_modified) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_switch_all_errors) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_taggings.taggings[0].value) + self.assertEqual('test-access-key-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_id) + self.assertEqual('test-access-key-secret-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.access_key_secret) + self.assertEqual('type2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.auth_type) + self.assertEqual('region2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_auth.region) + self.assertEqual('region2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_region) + self.assertEqual('vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_dst_vpc_id) + self.assertEqual('tunnel-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_tunnel_id) + self.assertEqual('role2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_role) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_using_role) + self.assertEqual('key2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].key) + self.assertEqual('value2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_return_headers.return_headers[0].value) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_proxy_pass) + self.assertEqual(False, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_is_express_tunnel) + self.assertEqual(2, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_number) + self.assertEqual('http://multi2.com/', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_url) + self.assertEqual('multi-vpc-id-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_vpc_id) + self.assertEqual('multi-region-2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_multi_alternates.mirror_multi_alternates[0].mirror_multi_alternate_dst_region) + self.assertEqual(True, result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.pass_all) + self.assertEqual('myheader-key21', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.passs[0]) + self.assertEqual('myheader-key22', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.passs[1]) + self.assertEqual('myheader-key23', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.removes[0]) + self.assertEqual('myheader-key24', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.removes[1]) + self.assertEqual('myheader-key25', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.sets[0].key) + self.assertEqual('myheader-value2', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.sets[0].value) + self.assertEqual('myheader-key26', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.sets[1].key) + self.assertEqual('myheader-value22', result.website_configuration.routing_rules.routing_rules[1].redirect.mirror_headers.sets[1].value) + self.assertEqual('script2.lua', result.website_configuration.routing_rules.routing_rules[1].lua_config.script) class TestDeleteBucketWebsite(unittest.TestCase): From 691dd16c6b2297bae0be7df23cdbe195fd6eeacc Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Thu, 6 Nov 2025 17:31:36 +0800 Subject: [PATCH 5/6] normalize parameter naming in DeleteMultipleObjectsRequest --- alibabacloud_oss_v2/models/object_basic.py | 68 +++++- alibabacloud_oss_v2/serde_utils.py | 57 +++-- sample/delete_multiple_objects.py | 25 +- tests/integration/test_client.py | 76 +++++++ tests/integration/test_object_basic_async.py | 76 +++++++ tests/unit/models/test_object_basic.py | 228 ++++++++++++++++++- 6 files changed, 508 insertions(+), 22 deletions(-) diff --git a/alibabacloud_oss_v2/models/object_basic.py b/alibabacloud_oss_v2/models/object_basic.py index 3ad5e94..4debb0d 100644 --- a/alibabacloud_oss_v2/models/object_basic.py +++ b/alibabacloud_oss_v2/models/object_basic.py @@ -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__( @@ -1061,6 +1122,7 @@ def __init__( objects: Optional[List[DeleteObject]] = None, quiet: Optional[bool] = None, request_payer: Optional[str] = None, + delete: Optional[Delete] = None, **kwargs: Any ) -> None: """ @@ -1068,9 +1130,12 @@ def __init__( 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 @@ -1078,6 +1143,7 @@ def __init__( self.objects = objects self.quiet = quiet self.request_payer = request_payer + self.delete = delete class DeletedInfo(serde.Model): diff --git a/alibabacloud_oss_v2/serde_utils.py b/alibabacloud_oss_v2/serde_utils.py index cd65b4a..e5e9cad 100644 --- a/alibabacloud_oss_v2/serde_utils.py +++ b/alibabacloud_oss_v2/serde_utils.py @@ -235,20 +235,51 @@ 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 + if request.objects is not None and request.delete is not None: + raise exceptions.SerializationError( + error='Either old parameters (objects, quiet) or new parameter (delete) is set, but not both' + ) + xml = '' - if request.quiet is not None: - xml += f'{"true" if request.quiet else "false"}' - - if isinstance(request.objects, List): - for _, o in enumerate(request.objects): - xml += '' - key = utils.safety_str(o.key) - if len(key) > 0: - xml += f'{utils.escape_xml_value(key)}' - vid = utils.safety_str(o.version_id) - if len(vid) > 0: - xml += f'{vid}' - xml += '' + + # Handle new parameter (delete) + if request.delete is not None: + if request.delete.objects is None: + raise exceptions.ParamRequiredError(field='delete.objects') + + if request.delete.quiet is not None: + xml += f'{"true" if request.delete.quiet else "false"}' + + if isinstance(request.delete.objects, List): + for _, o in enumerate(request.delete.objects): + xml += '' + key = utils.safety_str(o.key) + if len(key) > 0: + xml += f'{utils.escape_xml_value(key)}' + vid = utils.safety_str(o.version_id) + if len(vid) > 0: + xml += f'{vid}' + xml += '' + + # Handle old parameters (objects, quiet) + else: + if request.objects is None: + raise exceptions.ParamRequiredError(field='request.objects') + + if request.quiet is not None: + xml += f'{"true" if request.quiet else "false"}' + + if isinstance(request.objects, List): + for _, o in enumerate(request.objects): + xml += '' + key = utils.safety_str(o.key) + if len(key) > 0: + xml += f'{utils.escape_xml_value(key)}' + vid = utils.safety_str(o.version_id) + if len(vid) > 0: + xml += f'{vid}' + xml += '' xml += '' diff --git a/sample/delete_multiple_objects.py b/sample/delete_multiple_objects.py index 28c5c16..ab0f238 100644 --- a/sample/delete_multiple_objects.py +++ b/sample/delete_multiple_objects.py @@ -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},' @@ -44,5 +56,4 @@ def main(): if __name__ == "__main__": - main() - + main() \ No newline at end of file diff --git a/tests/integration/test_client.py b/tests/integration/test_client.py index fb075c0..a793eae 100644 --- a/tests/integration/test_client.py +++ b/tests/integration/test_client.py @@ -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) diff --git a/tests/integration/test_object_basic_async.py b/tests/integration/test_object_basic_async.py index 2257075..46ea9d0 100644 --- a/tests/integration/test_object_basic_async.py +++ b/tests/integration/test_object_basic_async.py @@ -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) diff --git a/tests/unit/models/test_object_basic.py b/tests/unit/models/test_object_basic.py index 8c05cb9..6aea585 100644 --- a/tests/unit/models/test_object_basic.py +++ b/tests/unit/models/test_object_basic.py @@ -2,7 +2,7 @@ import datetime import unittest import xml.etree.ElementTree as ET -from alibabacloud_oss_v2 import serde +from alibabacloud_oss_v2 import serde, serde_utils from alibabacloud_oss_v2.models import object_basic as model from alibabacloud_oss_v2.types import OperationInput, OperationOutput, CaseInsensitiveDict from .. import MockHttpResponse @@ -1413,6 +1413,232 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + def test_delete_multiple_objects_request_with_delete(self): + # Test constructor with Delete parameter + request = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + delete=model.Delete( + quiet=True, + objects=[ + model.ObjectIdentifier( + key='key1', + version_id='version1' + ), + model.ObjectIdentifier( + key='key2', + version_id='version2' + ) + ] + ) + ) + self.assertIsNotNone(request.bucket) + self.assertIsNotNone(request.delete) + self.assertIsNone(request.encoding_type) + self.assertIsNone(request.objects) + self.assertIsNone(request.quiet) + self.assertIsNone(request.request_payer) + self.assertEqual('bucket_name', request.bucket) + self.assertTrue(request.delete.quiet) + self.assertEqual(2, len(request.delete.objects)) + self.assertEqual('key1', request.delete.objects[0].key) + self.assertEqual('version1', request.delete.objects[0].version_id) + self.assertEqual('key2', request.delete.objects[1].key) + self.assertEqual('version2', request.delete.objects[1].version_id) + self.assertIsInstance(request, serde.RequestModel) + + # Test constructor with both old and new parameters - should work for construction + request = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + objects=[model.DeleteObject(key='old_key')], + quiet=False, + delete=model.Delete( + quiet=True, + objects=[ + model.ObjectIdentifier(key='new_key') + ] + ), + encoding_type='url', + request_payer='requester' + ) + self.assertEqual('bucket_name', request.bucket) + # New parameter should be preserved + self.assertIsNotNone(request.delete) + self.assertTrue(request.delete.quiet) + self.assertEqual(1, len(request.delete.objects)) + self.assertEqual('new_key', request.delete.objects[0].key) + # Old parameters should also be preserved + self.assertIsNotNone(request.objects) + self.assertEqual(1, len(request.objects)) + self.assertEqual('old_key', request.objects[0].key) + self.assertFalse(request.quiet) + self.assertEqual('url', request.encoding_type) + self.assertEqual('requester', request.request_payer) + + def test_serialize_delete_multiple_objects_request_with_delete(self): + # Test serialization with new Delete parameter + request_new = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + delete=model.Delete( + quiet=True, + objects=[ + model.ObjectIdentifier( + key='key1', + version_id='version1' + ), + model.ObjectIdentifier( + key='key2' + ) + ] + ), + request_payer='requester' + ) + + op_input_new = serde.serialize_input( + request_new, + OperationInput( + op_name='DeleteMultipleObjects', + method='POST', + bucket=request_new.bucket, + ), + custom_serializer=[ + serde_utils.serialize_delete_objects, + serde_utils.add_content_md5, + ], + ) + self.assertEqual('DeleteMultipleObjects', op_input_new.op_name) + self.assertEqual('POST', op_input_new.method) + self.assertEqual('bucket_name', op_input_new.bucket) + self.assertEqual('requester', op_input_new.headers.get('x-oss-request-payer')) + + # Check if the body contains the correct XML for new parameter + import xml.etree.ElementTree as ET + root_new = ET.fromstring(op_input_new.body) + self.assertEqual('Delete', root_new.tag) + self.assertEqual('true', root_new.findtext('Quiet')) + objects_new = root_new.findall('Object') + self.assertEqual(2, len(objects_new)) + self.assertEqual('key1', objects_new[0].findtext('Key')) + self.assertEqual('version1', objects_new[0].findtext('VersionId')) + self.assertEqual('key2', objects_new[1].findtext('Key')) + self.assertIsNone(objects_new[1].find('VersionId')) + + # Test serialization with old parameters + request_old = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + objects=[ + model.DeleteObject( + key='key1', + version_id='version1' + ), + model.DeleteObject( + key='key2' + ) + ], + quiet=True, + request_payer='requester' + ) + + op_input_old = serde.serialize_input( + request_old, + OperationInput( + op_name='DeleteMultipleObjects', + method='POST', + bucket=request_old.bucket, + ), + custom_serializer=[ + serde_utils.serialize_delete_objects, + serde_utils.add_content_md5, + ], + ) + self.assertEqual('DeleteMultipleObjects', op_input_old.op_name) + self.assertEqual('POST', op_input_old.method) + self.assertEqual('bucket_name', op_input_old.bucket) + self.assertEqual('requester', op_input_old.headers.get('x-oss-request-payer')) + + # Check if the body contains the correct XML for old parameters + root_old = ET.fromstring(op_input_old.body) + self.assertEqual('Delete', root_old.tag) + self.assertEqual('true', root_old.findtext('Quiet')) + objects_old = root_old.findall('Object') + self.assertEqual(2, len(objects_old)) + self.assertEqual('key1', objects_old[0].findtext('Key')) + self.assertEqual('version1', objects_old[0].findtext('VersionId')) + self.assertEqual('key2', objects_old[1].findtext('Key')) + self.assertIsNone(objects_old[1].find('VersionId')) + + # Compare the XML generated by new and old parameters - they should be identical + self.assertEqual(op_input_new.body, op_input_old.body) + + def test_serialize_delete_multiple_objects_request_error_conditions(self): + # Test serialization error when neither old nor new parameters are set + request = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + ) + + with self.assertRaises(Exception) as context: + serde.serialize_input( + request, + OperationInput( + op_name='DeleteMultipleObjects', + method='POST', + bucket=request.bucket, + ), + custom_serializer=[ + serde_utils.serialize_delete_objects, + serde_utils.add_content_md5, + ], + ) + self.assertIn('missing required field, request.objects', str(context.exception)) + + # Test serialization error when new parameters objects is not set + request = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + delete=model.Delete( + quiet=True, + ), + ) + + with self.assertRaises(Exception) as context: + serde.serialize_input( + request, + OperationInput( + op_name='DeleteMultipleObjects', + method='POST', + bucket=request.bucket, + ), + custom_serializer=[ + serde_utils.serialize_delete_objects, + serde_utils.add_content_md5, + ], + ) + self.assertIn('missing required field, delete.objects', str(context.exception)) + + # Test serialization error when both old and new parameters are set + request = model.DeleteMultipleObjectsRequest( + bucket='bucket_name', + delete=model.Delete( + quiet=True, + objects=[model.ObjectIdentifier(key='new_key')] + ), + objects=[model.DeleteObject(key='old_key')], + ) + + with self.assertRaises(Exception) as context: + serde.serialize_input( + request, + OperationInput( + op_name='DeleteMultipleObjects', + method='POST', + bucket=request.bucket, + ), + custom_serializer=[ + serde_utils.serialize_delete_objects, + serde_utils.add_content_md5, + ], + ) + self.assertIn('Either old parameters', str(context.exception)) + + def test_serialize_request(self): request = model.DeleteMultipleObjectsRequest( bucket='bucket_name', From 908740653175fb2892627170935c85e8d1fc2995 Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Thu, 6 Nov 2025 19:17:07 +0800 Subject: [PATCH 6/6] normalize parameter naming --- alibabacloud_oss_v2/models/object_basic.py | 49 ++- sample/put_object_acl.py | 2 +- tests/integration/test_object_basic.py | 399 +++++++++++++++++++++ tests/unit/models/test_object_basic.py | 153 ++++++++ 4 files changed, 594 insertions(+), 9 deletions(-) create mode 100644 tests/integration/test_object_basic.py diff --git a/alibabacloud_oss_v2/models/object_basic.py b/alibabacloud_oss_v2/models/object_basic.py index 4debb0d..125b8d0 100644 --- a/alibabacloud_oss_v2/models/object_basic.py +++ b/alibabacloud_oss_v2/models/object_basic.py @@ -63,6 +63,7 @@ def __init__( request_payer: Optional[str] = None, body: Optional[BodyType] = None, progress_fn: Optional[Any] = None, + object_acl: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -96,11 +97,14 @@ def __init__( request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs. body (BodyType,optional): Object data. progress_fn (Any,optional): Progress callback function. + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. """ super().__init__(**kwargs) self.bucket = bucket self.key = key - self.acl = acl + self.acl = object_acl if object_acl is not None else acl self.storage_class = storage_class self.metadata = metadata self.cache_control = cache_control @@ -650,6 +654,7 @@ def __init__( request_payer: Optional[str] = None, body: Optional[BodyType] = None, progress_fn: Optional[Any] = None, + object_acl: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -686,12 +691,15 @@ def __init__( request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs. body (BodyType,optional): Object data. progress_fn (Any,optional): Progress callback function. + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. """ super().__init__(**kwargs) self.bucket = bucket self.key = key self.position = position - self.acl = acl + self.acl = object_acl if object_acl is not None else acl self.storage_class = storage_class self.metadata = metadata self.cache_control = cache_control @@ -823,6 +831,7 @@ def __init__( traffic_limit: Optional[int] = None, request_payer: Optional[str] = None, progress_fn: Optional[Any] = None, + object_acl: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -877,6 +886,9 @@ def __init__( The speed limit value ranges from 245760 to 838860800, with a unit of bit/s. request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs. progress_fn (Any,optional): Progress callback function, it works in Copier.copy only. + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. """ super().__init__(**kwargs) self.bucket = bucket @@ -888,7 +900,7 @@ def __init__( self.if_none_match = if_none_match self.if_modified_since = if_modified_since self.if_unmodified_since = if_unmodified_since - self.acl = acl + self.acl = object_acl if object_acl is not None else acl self.storage_class = storage_class self.metadata = metadata self.cache_control = cache_control @@ -1434,6 +1446,7 @@ def __init__( acl: Optional[str] = None, version_id: Optional[str] = None, request_payer: Optional[str] = None, + object_acl: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -1443,11 +1456,14 @@ def __init__( acl (str, required): The access control list (ACL) of the object. version_id (str, optional): The version ID of the source object. request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. """ super().__init__(**kwargs) self.bucket = bucket self.key = key - self.acl = acl + self.acl = object_acl if object_acl is not None else acl self.version_id = version_id self.request_payer = request_payer @@ -1966,6 +1982,7 @@ def __init__( forbid_overwrite: Optional[bool] = None, encoding_type: Optional[str] = None, request_payer: Optional[str] = None, + object_acl: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -1983,12 +2000,15 @@ def __init__( overwrites an existing object that has the same name. encoding_type (str, optional): The encoding type of the object names in the response. Valid value: url. request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs. + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. """ super().__init__(**kwargs) self.bucket = bucket self.key = key self.upload_id = upload_id - self.acl = acl + self.acl = object_acl if object_acl is not None else acl self.complete_multipart_upload = complete_multipart_upload self.complete_all = complete_all self.callback = callback @@ -2454,6 +2474,8 @@ def __init__( metadata: Optional[MutableMapping] = None, forbid_overwrite: Optional[bool] = None, request_payer: Optional[str] = None, + object_acl: Optional[str] = None, + symlink_target: Optional[str] = None, **kwargs: Any ) -> None: """ @@ -2467,12 +2489,18 @@ def __init__( forbid_overwrite (bool, optional): Specifies whether the object that is uploaded by calling the PutObject operation overwrites an existing object that has the same name. request_payer (str, optional): To indicate that the requester is aware that the request and data download will incur costs. + object_acl (str, optional): The access control list (ACL) of the object. + The object_acl parameter has the same functionality as the acl parameter. it is the standardized name for acl. + If both exist simultaneously, the value of object_acl will take precedence. + symlink_target (str, optional): The destination object to which the symbolic link points. + The symlink_target parameter has the same functionality as the target parameter. it is the standardized name for target. + If both exist simultaneously, the value of symlink_target will take precedence. """ super().__init__(**kwargs) self.bucket = bucket self.key = key - self.target = target - self.acl = acl + self.target = symlink_target if symlink_target is not None else target + self.acl = object_acl if object_acl is not None else acl self.storage_class = storage_class self.metadata = metadata self.forbid_overwrite = forbid_overwrite @@ -2546,19 +2574,24 @@ def __init__( target: Optional[str] = None, etag: Optional[str] = None, metadata: Optional[MutableMapping] = None, + symlink_target: Optional[str] = None, **kwargs: Any ) -> None: """ Args: version_id (str, optional): Version of the object. target (str, optional): Indicates the target object that the symbol link directs to. + This parameter is deprecated. Use 'symlink_target' parameter instead etag (str, optional): The entity tag (ETag). An ETag is created when an object is created to identify the content of the object. metadata (MutableMapping, optional): A map of metadata to store with the object. + symlink_target (str, optional): The destination object to which the symbolic link points. + The symlink_target parameter has the same functionality as the target parameter. it is the standardized name for target. + If both exist simultaneously, the value of symlink_target will take precedence. """ super().__init__(**kwargs) self.version_id = version_id - self.target = target + self.target = symlink_target if symlink_target is not None else target self.etag = etag self.metadata = metadata diff --git a/sample/put_object_acl.py b/sample/put_object_acl.py index d85749d..1470ecf 100644 --- a/sample/put_object_acl.py +++ b/sample/put_object_acl.py @@ -28,7 +28,7 @@ def main(): result = client.put_object_acl(oss.PutObjectAclRequest( bucket=args.bucket, key=args.key, - acl=args.acl, + object_acl=args.acl, )) print(f'status code: {result.status_code},' diff --git a/tests/integration/test_object_basic.py b/tests/integration/test_object_basic.py new file mode 100644 index 0000000..49a89d4 --- /dev/null +++ b/tests/integration/test_object_basic.py @@ -0,0 +1,399 @@ +# pylint: skip-file +from typing import cast +import alibabacloud_oss_v2 as oss +from . import ( + TestIntegration, + random_str, + OBJECTNAME_PREFIX, +) +from urllib.parse import quote, unquote + +class TestObjectBasicV2(TestIntegration): + + def test_put_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('private', result.acl) + + def test_put_object_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + object_acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('private', result.acl) + + result = self.client.put_object_acl(oss.PutObjectAclRequest( + bucket=self.bucket_name, + key=key, + object_acl=oss.ObjectACLType.PUBLICREAD + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.PutObjectAclResult) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_append_object_acl(self): + data1 = b'hello' + key = OBJECTNAME_PREFIX + random_str(16) + 'append_object' + result = self.client.append_object(oss.AppendObjectRequest( + bucket=self.bucket_name, + key=key, + position=0, + body=data1, + acl='public-read' + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + self.assertEqual(5, result.next_position) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_append_object_object_acl(self): + data1 = b'hello' + key = OBJECTNAME_PREFIX + random_str(16) + 'append_object' + result = self.client.append_object(oss.AppendObjectRequest( + bucket=self.bucket_name, + key=key, + position=0, + body=data1, + object_acl='public-read' + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + self.assertEqual(5, result.next_position) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_copy_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + dst_key = key + '-copy' + result = self.client.copy_object(oss.CopyObjectRequest( + bucket=self.bucket_name, + key=dst_key, + source_key=key, + acl=oss.ObjectACLType.PUBLICREAD, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=dst_key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_copy_object_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + object_acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + dst_key = key + '-copy' + result = self.client.copy_object(oss.CopyObjectRequest( + bucket=self.bucket_name, + key=dst_key, + source_key=key, + object_acl=oss.ObjectACLType.PUBLICREAD, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=dst_key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_mutilpart_object_acl(self): + length1 = 100*1024 + data1 = random_str(length1) + key = OBJECTNAME_PREFIX + random_str(16) + + result = self.client.initiate_multipart_upload(oss.InitiateMultipartUploadRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.InitiateMultipartUploadResult) + self.assertEqual(200, result.status_code) + self.assertEqual(self.bucket_name, result.bucket) + self.assertEqual(key, result.key) + self.assertIsNotNone(key, result.upload_id) + + presult1 = self.client.upload_part(oss.UploadPartRequest( + bucket=self.bucket_name, + key=key, + part_number=1, + upload_id=result.upload_id, + body=data1, + )) + self.assertIsNotNone(presult1) + self.assertIsInstance(presult1, oss.UploadPartResult) + self.assertEqual(200, presult1.status_code) + self.assertIsNotNone(presult1.content_md5) + self.assertIsNotNone(presult1.etag) + self.assertIsNotNone(presult1.hash_crc64) + + cresult = self.client.complete_multipart_upload(oss.CompleteMultipartUploadRequest( + bucket=self.bucket_name, + key=key, + upload_id=result.upload_id, + complete_multipart_upload=oss.CompleteMultipartUpload( + parts=[ + oss.UploadPart(part_number=1, etag=presult1.etag), + ] + ), + acl='public-read', + )) + self.assertIsNotNone(cresult) + self.assertIsInstance(cresult, oss.CompleteMultipartUploadResult) + self.assertEqual(200, cresult.status_code) + self.assertEqual(self.bucket_name, cresult.bucket) + self.assertEqual(key, cresult.key) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_mutilpart_object_object_acl(self): + length1 = 100*1024 + data1 = random_str(length1) + length2 = 1234 + data2 = random_str(length2) + key = OBJECTNAME_PREFIX + random_str(16) + + result = self.client.initiate_multipart_upload(oss.InitiateMultipartUploadRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.InitiateMultipartUploadResult) + self.assertEqual(200, result.status_code) + self.assertEqual(self.bucket_name, result.bucket) + self.assertEqual(key, result.key) + self.assertIsNotNone(key, result.upload_id) + + presult1 = self.client.upload_part(oss.UploadPartRequest( + bucket=self.bucket_name, + key=key, + part_number=1, + upload_id=result.upload_id, + body=data1, + )) + self.assertIsNotNone(presult1) + self.assertIsInstance(presult1, oss.UploadPartResult) + self.assertEqual(200, presult1.status_code) + self.assertIsNotNone(presult1.content_md5) + self.assertIsNotNone(presult1.etag) + self.assertIsNotNone(presult1.hash_crc64) + + presult2 = self.client.upload_part(oss.UploadPartRequest( + bucket=self.bucket_name, + key=key, + part_number=2, + upload_id=result.upload_id, + body=data2, + )) + self.assertIsNotNone(presult2) + self.assertIsInstance(presult2, oss.UploadPartResult) + self.assertEqual(200, presult2.status_code) + self.assertIsNotNone(presult2.content_md5) + self.assertIsNotNone(presult2.etag) + self.assertIsNotNone(presult2.hash_crc64) + + cresult = self.client.complete_multipart_upload(oss.CompleteMultipartUploadRequest( + bucket=self.bucket_name, + key=key, + upload_id=result.upload_id, + body=data2, + complete_multipart_upload=oss.CompleteMultipartUpload( + parts=[ + oss.UploadPart(part_number=1, etag=presult1.etag), + oss.UploadPart(part_number=2, etag=presult2.etag), + ] + ), + object_acl='public-read', + )) + self.assertIsNotNone(cresult) + self.assertIsInstance(cresult, oss.CompleteMultipartUploadResult) + self.assertEqual(200, cresult.status_code) + self.assertEqual(self.bucket_name, cresult.bucket) + self.assertEqual(key, cresult.key) + self.assertIsNotNone(cresult.etag) + self.assertIsNotNone(cresult.hash_crc64) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_symlink_object_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('private', result.acl) + + sym_key = key + '-sym' + result = self.client.put_symlink(oss.PutSymlinkRequest( + bucket=self.bucket_name, + key=sym_key, + acl='public-read', + target=key, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=sym_key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) + + def test_symlink_object_object_acl(self): + length = 123 + data = random_str(length) + key = OBJECTNAME_PREFIX + random_str(16) + result = self.client.put_object(oss.PutObjectRequest( + bucket=self.bucket_name, + key=key, + object_acl=oss.ObjectACLType.PRIVATE, + body=data, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('private', result.acl) + + sym_key = key + '-sym' + result = self.client.put_symlink(oss.PutSymlinkRequest( + bucket=self.bucket_name, + key=sym_key, + object_acl='public-read', + symlink_target=key, + )) + self.assertIsNotNone(result) + self.assertEqual(200, result.status_code) + + result = self.client.get_object_acl(oss.GetObjectAclRequest( + bucket=self.bucket_name, + key=sym_key, + )) + self.assertIsNotNone(result) + self.assertIsInstance(result, oss.GetObjectAclResult) + self.assertEqual(200, result.status_code) + self.assertEqual('public-read', result.acl) diff --git a/tests/unit/models/test_object_basic.py b/tests/unit/models/test_object_basic.py index 6aea585..4ffa812 100644 --- a/tests/unit/models/test_object_basic.py +++ b/tests/unit/models/test_object_basic.py @@ -123,6 +123,26 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.PutObjectRequest( + bucket='bucket-test', + key='key-test', + object_acl='public-read', + ) + self.assertEqual('bucket-test', request.bucket) + self.assertEqual('key-test', request.key) + self.assertEqual('public-read', request.acl) + + request = model.PutObjectRequest( + bucket='bucket-test', + key='key-test', + acl='public-read', + object_acl='private', + ) + self.assertEqual('bucket-test', request.bucket) + self.assertEqual('key-test', request.key) + self.assertEqual('private', request.acl) + def test_serialize_request(self): request = model.PutObjectRequest( @@ -856,6 +876,30 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.AppendObjectRequest( + bucket='bucket_name', + key='example-object-2.jpg', + position=0, + object_acl='public-read', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual(0, request.position) + self.assertEqual('public-read', request.acl) + + request = model.AppendObjectRequest( + bucket='bucket_name', + key='example-object-2.jpg', + position=0, + acl = 'public-read', + object_acl='private', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual(0, request.position) + self.assertEqual('private', request.acl) + def test_serialize_request(self): request = model.AppendObjectRequest( bucket='bucket_name', @@ -1090,6 +1134,31 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.CopyObjectRequest( + bucket='bucket_name', + key='example-object-2.jpg', + source_key='source-invalid-key', + object_acl='public-read', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('source-invalid-key', request.source_key) + self.assertEqual('public-read', request.acl) + + request = model.CopyObjectRequest( + bucket='bucket_name', + key='example-object-2.jpg', + source_key='source-invalid-key', + acl='public-read', + object_acl='private', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('source-invalid-key', request.source_key) + self.assertEqual('private', request.acl) + + def test_serialize_request(self): request = model.CopyObjectRequest( bucket='bucket_name', @@ -2085,6 +2154,26 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.PutObjectAclRequest( + bucket='bucket_name', + key='example-object-2.jpg', + object_acl='public-read', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('public-read', request.acl) + + request = model.PutObjectAclRequest( + bucket='bucket_name', + key='example-object-2.jpg', + acl='public-read', + object_acl='private', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('private', request.acl) + def test_serialize_request(self): request = model.PutObjectAclRequest( bucket='bucket_name', @@ -2895,6 +2984,31 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.CompleteMultipartUploadRequest( + bucket='bucket_name', + key='example-object-2.jpg', + upload_id='0004B9894A22E5B1888A1E29F823****', + object_acl='public-read', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('0004B9894A22E5B1888A1E29F823****', request.upload_id) + self.assertEqual('public-read', request.acl) + + request = model.CompleteMultipartUploadRequest( + bucket='bucket_name', + key='example-object-2.jpg', + upload_id='0004B9894A22E5B1888A1E29F823****', + acl='public-read', + object_acl='private', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('0004B9894A22E5B1888A1E29F823****', request.upload_id) + self.assertEqual('private', request.acl) + + def test_serialize_request(self): request = model.CompleteMultipartUploadRequest( bucket='bucket_name', @@ -3619,6 +3733,32 @@ def test_constructor_request(self): self.assertDictEqual({'parm1': 'value1'}, request.parameters) self.assertEqual('hello world', request.payload) + # Test standardized name + request = model.PutSymlinkRequest( + bucket='bucket_name', + key='example-object-2.jpg', + symlink_target='target-object', + object_acl='private', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('target-object', request.target) + self.assertEqual('private', request.acl) + + request = model.PutSymlinkRequest( + bucket='bucket_name', + key='example-object-2.jpg', + symlink='target-object', + acl='private', + symlink_target='target-object-1', + object_acl='public-read', + ) + self.assertEqual('bucket_name', request.bucket) + self.assertEqual('example-object-2.jpg', request.key) + self.assertEqual('target-object-1', request.target) + self.assertEqual('public-read', request.acl) + + def test_serialize_request(self): request = model.PutSymlinkRequest( bucket='bucket_name', @@ -3802,6 +3942,19 @@ def test_constructor_result(self): self.assertEqual('CAEQNhiBgMDJgZCA0BYiIDc4MGZjZGI2OTBjOTRmNTE5NmU5NmFhZjhjYmY0****', result.version_id) self.assertFalse(hasattr(result, 'invalid_field')) + # Test standardized name + result = model.GetSymlinkResult( + symlink_target='target-object', + ) + self.assertEqual('target-object', result.target) + + result = model.GetSymlinkResult( + target='target-object', + symlink_target='target-object-1', + ) + self.assertEqual('target-object-1', result.target) + + def test_deserialize_result(self): xml_data = None result = model.GetSymlinkResult()