# encoding: utf-8
"""
OOS Python API Example
"""
import datetime
import json
import traceback
import oos
import ooscore.exceptions as exceptions
from ooscore.client import Config

ACCESS_KEY = 'your_accessKey'
SECRET_KEY = 'your_secretKey'
API_VERSION = '2006-03-01'
ENDPOINT = 'your_domain'
SIGNATURE_VERSION = 's3'  # Algorithm version used for calculating signatures, v2 version:'s3', v4 version:'s3v4'
SERVICE_NAME = 's3'
BUCKET = 'your_bucket'  # bucket name
KEY = 'your_key'  # object name
MULTIPART_UPLOAD_KEY = 'your_multipartUploadKey'  # multipart upload object name
UPLOAD_FILE = 'your_uploadFile'
DOWNLOAD_FILE_PATH = 'your_downloadFilePath'

# Preprocessing, setting up access to OOS services
if ENDPOINT.lower().find("http") < 0 and ENDPOINT.lower().find("https") < 0:
    ENDPOINT = "http://" + ENDPOINT
try:
    _config = Config(endpoint_url=ENDPOINT, signature_version=SIGNATURE_VERSION,
                     s3={'payload_signing_enabled': True})
    client = oos.client(service_name=SERVICE_NAME, endpoint_url=ENDPOINT, api_version=API_VERSION,
                        access_key_id=ACCESS_KEY, secret_access_key=SECRET_KEY,
                        config=_config)
except Exception as ex:
    print(traceback.format_exc())
    print(ex)
    exit(-1)


def pretty_print(res):
    """
    Print the return body to the console
    """
    print(json.dumps(res, sort_keys=True, indent=4, default=str, ensure_ascii=False))


def handle_error(fn):
    """
    Exception handling
    """

    def inner():
        try:
            fn()
        except exceptions.ClientError as e:
            print('\n Response code: {0}\n Error message: {1}\n Resource: {2}\n request id: {3}'
                  .format(e.response['Error']['Code'],
                          e.response['Error']['Message'],
                          e.response['Error']['Resource'],
                          e.response['ResponseMetadata']['RequestId']
                          ))

    return inner


# Service: List Bucket
@handle_error
def list_bucket_example():
    """
    For services that make Get requests, all Buckets owned by the requester are returned, where "/" represents the root directory
    This API is only valid for validating users, and cannot be performed by anonymous users
    """
    response = client.list_buckets()
    pretty_print(response)
    # Print the Bucket information in the return body to the console
    for bucket in response['Buckets']:
        print('Bucket: {0}'.format(bucket['Name']))


# Service: Get Regions
@handle_error
def get_regions_example():
    """
    Get a list of index locations and data locations in the resource pool
    This API is only open to endpoints that use object storage networks
    """
    response = client.get_regions()
    pretty_print(response)


# Bucket: Put Bucket
@handle_error
def put_bucket_example():
    """
    This operation is used to create a new Bucket
    Not all characters are supported in the Bucket naming scheme, see the OOS Developer Documentation for the Bucket naming convention
    """
    response = client.create_bucket(
        # Set Bucket's ACL (Access Control List): private; public-read; public-read-write
        ACL='private',
        # Name of the bucket
        Bucket=BUCKET,
        # Set the container for the Bucket index location and the data location
        CreateBucketConfiguration={
            # Set the index location of the Bucket
            'MetadataLocationConstraint':
                {'Location': 'QingDao'},
            # Set the data location of the Bucket
            'DataLocationConstraint': {
                # Type of data location: Local | Specified
                'Type': 'Specified',
                # Specified data location
                'LocationList': ['QingDao'],
                # The scheduling policy when specifying data
                'ScheduleStrategy': 'Allowed'}
        }
    )
    print(response['Location'])
    pretty_print(response)


# Bucket: Get Bucket location
@handle_error
def get_bucket_location_example():
    """
    This operation is used to obtain the index location and data location of the Bucket
    Only root users and sub-users with the GET Bucket location permission can perform this operation
    This API is only open to endpoints that use object storage networks
    """
    response = client.get_bucket_location(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Get Bucket ACL
@handle_error
def get_bucket_acl_example():
    """
    This operation is used to obtain the ACL information of the bucket
    Only users with the GET Bucket ACL permission can perform this operation
    """
    response = client.get_bucket_acl(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Get Bucket (List Objects)
@handle_error
def get_bucket_example():
    """
    This operation is used to return some or all (up to 1,000 at a time) of the object information in the Bucket
    Users can set selection criteria in the request element to get a subset of the objects in the Bucket
    To perform this operation, you need to have read permission on the Bucket to be operated
    """
    response = client.list_objects(
        # Name of the bucket
        Bucket=BUCKET,
        # Set the maximum number of entries to be returned in the response
        MaxKeys=10,
        # Delimiter, a character used to group keywords, only supports "/"
        # Delimiter = '/',
        # Page identification
        # Marker = 'your_marker'
        # Used to restrict the results returned, which must start with this prefix
        # Prefix = 'your_prefix',
        # Specify the encoding type of the response, value: url
        # EncodingType = 'url'
    )
    pretty_print(response)


# Bucket: Delete bucket
@handle_error
def delete_bucket_example():
    """
    This operation is used to delete a Bucket, but it requires that there are no objects in the deleted Bucket,
    that is, all objects in the Bucket have been deleted
    """
    response = client.delete_bucket(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Put Bucket Policy
@handle_error
def put_bucket_policy_example():
    """
    Adding Policy to the URL of the PUT operation allows you to add or modify the policy
    If a Policy already exists in the Bucket, this operation will replace the original Policy
    Only root users and users with PUT Bucket Policy permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    policy = {
        "Version": "2012-10-17",
        "Id": "http referer policy example",
        "Statement": [
            {
                "Effect": "Allow",
                "Sid": "1",
                "Principal": {
                    "AWS": "*"
                },
                "Action": ["s3:*"],
                "Resource": "arn:aws:s3:::" + BUCKET + "/*"
            }
        ]
    }
    response = client.put_bucket_policy(
        # Name of the bucket
        Bucket=BUCKET,
        # A JSON string containing Policy statements.
        Policy=json.dumps(policy)
    )
    pretty_print(response)


# Bucket: Get Bucket Policy
@handle_error
def get_bucket_policy_example():
    """
    Adding Policy to the URL of the GET operation allows you to obtain the Policy of the specified bucket
    Only root users and users with GET Bucket Policy permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    If the Bucket does not have a Policy, a 404 NoSuchPolicy error will be returned
    """
    response = client.get_bucket_policy(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)
    pretty_print(json.loads(response['Policy']))


# Bucket: Delete bucket Policy
@handle_error
def delete_bucket_policy_example():
    """
    Adding Policy to the URL of the DELETE operation allows you to delete the Policy of the specified Bucket
    Only root users and sub-users with DELETE Bucket Policy permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.delete_bucket_policy(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Put Bucket WebSite
@handle_error
def put_bucket_website_example():
    """
    Adding a website to the URL of the PUT operation allows you to set the website configuration
    If a Policy already exists in the website, this operation will replace the original website
    Only root users and sub-users with PUT Bucket Website permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.put_bucket_website(
        # Name of the bucket
        Bucket=BUCKET,
        # Requested container
        WebsiteConfiguration={
            'ErrorDocument': {
                # If a 4XX error occurs, the specified Object will be returned
                'Key': 'error1.html'
            },
            'IndexDocument': {
                # When requesting a path on the website endpoint, Suffix will be appended to the request
                'Suffix': 'index1.html'
            }
        }
    )
    pretty_print(response)


# Bucket: Get Bucket WebSite
@handle_error
def get_bucket_website_example():
    """
    Adding a website to the URL of the GET operation allows you to obtain the website of the specified Bucket
    Only root users and sub-users with GET Bucket Website permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.get_bucket_website(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Delete Bucket WebSite
@handle_error
def delete_bucket_website_example():
    """
    Adding a website to the URL of the Delete operation allows you to delete the website of the specified Bucket
    Only root users and sub-users with DELETE Bucket Website permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    If the Bucket does not have a website, 200 OK is returned
    """
    response = client.delete_bucket_website(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: List Multipart Uploads
@handle_error
def list_multipart_uploads_example():
    """
    This API is used to list all multipart upload processes
    that have been initialized through Initiate Multipart Upload request, but have not completed or terminated
    """
    response = client.list_multipart_uploads(
        # Name of the bucket
        Bucket=BUCKET,
        # Set the maximum number of returned multipart upload processes, value: [1, 1000]
        MaxUploads=100,
        # Delimiter, a character used to group keywords, only supports "/"
        # Delimiter = '/',
        # Used to restrict the results returned, which must start with this prefix
        # Prefix = 'your_prefix',
        # Specify the encoding type of the response, value: url
        # EncodingType = 'url',
        # Along with the upload-id-marker parameter, this parameter specifies the location after which the list operation starts
        # KeyMarker = 'your_KeyMarker',
        # Along with the key-marker parameter, this parameter specifies the location after which the list operation starts
        # UploadIdMarker = 'your_UploadIdMarker'
    )
    pretty_print(response)


# Bucket: Put Bucket Logging
@handle_error
def put_bucket_logging_example():
    """
    Adding logging to the URL of the PUT operation allows you to add/modify/delete logging
    If the Bucket already has a logging, this operation will replace the original logging
    Only root users and sub-users with PUT Bucket logging permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.put_bucket_logging(
        # Name of the bucket
        Bucket=BUCKET,
        # Requested container
        BucketLoggingStatus={
            # The container for log information, which needs to be included when starting the log
            'LoggingEnabled': {
                # Specify the Bucket to which you want to save logs, and OOS stores logs to this bucket
                'TargetBucket': '{0}'.format(BUCKET),
                # The generated log file will be named with this prefix
                'TargetPrefix': '{0}_log'.format(BUCKET)
            }
        }
    )
    pretty_print(response)


# Bucket: Get Bucket Logging
@handle_error
def get_bucket_logging_example():
    """
    Adding logging to the URL of the GET operation allows you to obtain the logging for the specified bucket
    Only root users and sub-users with GET Bucket logging permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.get_bucket_logging(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Head Bucket
@handle_error
def head_bucket_example():
    """
    This operation is used to determine whether the Bucket exists and whether the user has permission to access it
    If the Bucket exists and the user has permission to access it, this operation returns 200 OK.
    Otherwise, returns 404 not exist, or 403 no permission
    """
    response = client.head_bucket(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Put Bucket Lifecycle
@handle_error
def put_bucket_lifecycle_example():
    """
    Objects stored in OOS sometimes need to have a lifecycle
    For example, a user may upload some periodic log files to the Bucket, and after a period of time, the user may no longer need these log objects
    Previously, users had to manually delete these unused objects themselves.
    Now, they can use the object expiration feature to specify the lifecycle of objects in the Bucket
    Only root users and sub users with PUT Bucket Lifecycle permission can perform this operation
    """
    # Expire in 10 days, demonstrating hourly processing in Python SDK using Date method
    d = datetime.date.today() + datetime.timedelta(days=10)
    expire_date = datetime.datetime.combine(d, datetime.datetime.min.time())
    response = client.put_bucket_lifecycle(
        # Name of the bucket
        Bucket=BUCKET,
        # Lifecycle rule container with up to 1000 rules
        LifecycleConfiguration={
            # Configure a container with one lifecycle rule
            'Rules': [
                {
                    # Specify the expiration time container for the lifecycle rule
                    'Expiration': {
                        # Specify the effective date of the lifecycle rule
                        'Date': expire_date
                    },
                    # Specify the transition storage class of the lifecycle rule
                    # 'Transitions': {
                    #     'Date': expire_date
                    # },
                    # The unique identifier of the rule
                    'ID': 'lifecycle',
                    # Specify the file prefix that uses the lifecycle rule
                    'Prefix': 'test',
                    # Specify the state of the lifecycle rule
                    'Status': 'Enabled'
                }
            ]
        }
    )
    pretty_print(response)


# Bucket: Get Bucket Lifecycle
@handle_error
def get_bucket_lifecycle_example():
    """
    This API is used to return the configured Bucket lifecycle
    """
    response = client.get_bucket_lifecycle(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Delete Bucket Lifecycle
@handle_error
def delete_bucket_lifecycle_example():
    """
    This API is used to delete the configured Bucket lifecycle,
    and OOS will delete all lifecycle configuration rules of the specified Bucket
    The user's object will never expire, and OOS will no longer automatically delete the object
    Only root users and sub users with DELETE Bucket Lifecycle permission can perform this operation
    """
    response = client.delete_bucket_lifecycle(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Put Bucket CORS
@handle_error
def put_bucket_cors_example():
    """
    Cross-Origin Resource Sharing (CORS) defines how a client Web application interacts with resources in one domain and another
    It is a restriction set by the browser for security reasons, that is, the same origin policy
    With CORS, users can build rich client-side web applications while selectively allowing cross-domain access to OOS resources
    """
    response = client.put_bucket_cors(
        # Name of the bucket
        Bucket=BUCKET,
        # Container with up to 100 CORSRules elements
        CORSConfiguration={
            # Allow cross domain sources and methods for users
            'CORSRules': [
                {
                    # Allow cross domain sources
                    'AllowedOrigins': ['https://www.ctyun.cn'],
                    # Allow cross domain HTTP methods
                    'AllowedMethods': ['PUT', 'POST', 'DELETE'],
                    # Control whether the header specified in the Access-Control-RequestHeaders header in the precheck OPTIONS request is allowed
                    'AllowedHeaders': ['*']
                },
                {
                    'AllowedOrigins': ['https://oos.ctyun.cn'],
                    'AllowedMethods': ['PUT', 'POST'],
                    'AllowedHeaders': ['*']
                }
            ]
        }
    )
    pretty_print(response)


# Bucket: Get Bucket CORS
@handle_error
def get_bucket_cors_example():
    """
    Return cross domain configuration information for the Bucket
    Only root users and sub-users with GET Bucket CORS permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.get_bucket_cors(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Delete Bucket CORS
@handle_error
def delete_bucket_cors_example():
    """
    Delete cross domain configuration information for the Bucket
    Only root users and sub-users with DELETE Bucket CORS permission can perform this operation,
    otherwise a 403 AccessDenied error will be returned
    """
    response = client.delete_bucket_cors(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: PUT Bucket Object Lock
@handle_error
def put_bucket_object_lock_example():
    """
    This operation can enable the compliance retention feature, which takes effect on all objects in the bucket once enabled
    Only root users and privileged sub-users can perform this operation, while anonymous users cannot
    After enabling the Bucket compliance retention feature,
    no user (including the root user) can modify or delete objects in this Bucket that are in the compliance retention period
    """
    response = client.put_bucket_object_lock(
        # Name of the bucket
        Bucket=BUCKET,
        # Container for compliant retention of configuration information
        ObjectLockConfiguration={
            # Whether compliance retention feature is enabled for the Bucket
            'ObjectLockEnabled': 'Disabled',
            # Set up compliance retention rules
            'Rule': {
                # Default compliance retention configuration
                'DefaultRetention': {
                    # Compliance retention mode
                    'Mode': 'COMPLIANCE',
                    # Compliance retention days
                    'Days': 1
                }
            }
        }
    )
    pretty_print(response)


# Bucket: GET Bucket Object Lock
@handle_error
def get_bucket_object_lock_example():
    """
    This operation allows you to obtain the configuration information about Bucket compliance retention
    Only root users and privileged sub-users can perform this operation
    """
    response = client.get_bucket_object_lock(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: DELETE Bucket Object Lock
@handle_error
def delete_bucket_object_lock_example():
    """
    This operation allows you to delete the compliance retention configuration information that is not enabled
    Only root users and privileged sub-users can perform this operation
    """
    response = client.delete_bucket_object_lock(
        # Name of the bucket
        Bucket=BUCKET
    )
    pretty_print(response)


# Bucket: Put Bucket Inventory Configuration
@handle_error
def put_bucket_inventory_example():
    """
    This operation is used to put the bucket inventory configuration
    The request requires the permission to set the bucket inventory and the permission to delete the bucket inventory.
    """
    print('Put Bucket Inventory Configuration')
    response = client.put_bucket_inventory(
        # Source bucket name for executing the inventory task
        Bucket=BUCKET,
        # Specified inventory name: Only lowercase letters, numbers, hyphens (-), and underscores (_) are allowed. It cannot start or end with a hyphen (-) or underscore (_), and must be 1 to 64 characters long.
        Id='test11',
        # Inventory configuration
        InventoryConfiguration={
            # Store inventory results
            'Destination': {
                # Bucket information where the inventory results are stored after export
                'OOSBucketDestination': {
                    # Bucket for storing exported inventory files
                    'Bucket': 'arn:ctyun:oos:::'+BUCKET,
                    # Inventory file format: CSV
                    'Format': 'CSV',
                    # (optional) Inventory file storage path prefix: 0~512 characters
                    'Prefix': 'Inventory-'
                }},
            # Flag indicating whether the inventory feature is enabled
            'IsEnabled': True,
            # The inventory will filter out files in the source bucket that match the prefix(0~1024 characters) setting
            # The defalut value is empty string ""
            'Filter': {'Prefix': ''},
            # Same as the Id in the request parameter.
            'Id': 'test11',
            # (optional) Set the configuration items to be included in the inventory results
            'OptionalFields': [
                # The size of the Object
                'Size',
                # The last modified time of the Object
                'LastModifiedDate',
                # The storage class of the Object: STANDARD, STANDARD_IA.
                'StorageClass',
                # The ETag of the Object
                'ETag',
                # Indicates whether the Object was uploaded via multipart upload
                'IsMultipartUploaded'],
            # A container that stores the periodic information for inventory exports
            # Valid values for Frequency: Daily, Weekly.
            'Schedule': {'Frequency': 'Daily'}
        }
    )
    pretty_print(response)


# Bucket: Get Bucket Inventory Configuration
@handle_error
def get_bucket_inventory_example():
    """
    This operation is used to get the bucket inventory configuration
    The request requires permission to access the bucket inventory.
    """
    print('Get Bucket Inventory Configuration')
    response = client.get_bucket_inventory(
        # Source bucket name for executing the inventory task
        Bucket=BUCKET,
        # Specified inventory name: Only lowercase letters, numbers, hyphens (-), and underscores (_) are allowed. It cannot start or end with a hyphen (-) or underscore (_), and must be 1 to 64 characters long.
        Id='test11'
    )
    pretty_print(response)


# Bucket: Delete Bucket Inventory Configuration
@handle_error
def delete_bucket_inventory_example():
    """
    This operation is used to delete the bucket inventory configuration
    The request requires the permission to set the bucket inventory and the permission to delete the bucket inventory.
    """
    print('Delete Bucket Inventory Configuration')
    response = client.delete_bucket_inventory(
        # Source bucket name for executing the inventory task
        Bucket=BUCKET,
        # Specified inventory name: Only lowercase letters, numbers, hyphens (-), and underscores (_) are allowed. It cannot start or end with a hyphen (-) or underscore (_), and must be 1 to 64 characters long.
        Id='test11'
    )
    pretty_print(response)


# Bucket: List Bucket Inventory Configuration
@handle_error
def list_bucket_inventory_example():
    """
    This operation is used to list all bucket inventory configurations
    The request requires permission to access the bucket inventory.
    """
    print('List Bucket Inventory Configuration')
    response = client.list_bucket_inventory(
        # Source bucket name for executing the inventory task
        Bucket=BUCKET,
        # If a single request cannot return all the inventory configuration items, subsequent requests need to include the NextContinuationToken parameter from the previous response.
        ContinuationToken=''
    )
    pretty_print(response)


# Object: Put Object
@handle_error
def put_object_example():
    """
    This operation is used to add an object to the specified Bucket
    The requester is required to have write permission to the Bucket, and the user must add the complete object
    """
    with open(UPLOAD_FILE, 'rb') as data:
        response = client.put_object(
            # Set the location of data storage
            DataLocation='type=Specified,location=QingDao,scheduleStrategy=Allowed',
            # Name of the bucket
            Bucket=BUCKET,
            # Object name
            Key=KEY,
            # Object data
            Body=data,
            # Storage type of data
            StorageClass='STANDARD',
            # Standard MIME types are used to describe the content format
            ContentType='application/octet-stream',
            # Specify whether to overwrite object with the same name when performing a putObject operation.
            ForbidOverWrite=False  # Note: This configuration is supported only by some resource pools.
        )
    pretty_print(response)


# Object: Get Object
@handle_error
def get_object_example():
    """
    This operation is used to retrieve object from OOS. To perform this operation,
    you must have read permission on the bucket where the object is located.
    If the bucket has the public-read permission, anonymous users can also perform read operations in an unauthorized manner.
    """
    response = client.get_object(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name
        Key=KEY
    )
    pretty_print(response)
    body = response['Body']
    with open(DOWNLOAD_FILE_PATH, 'wb') as fd:
        # Download the OOS bucket file to local, and set the block size of the read response stream to reduce the memory load
        for chunk in iter(lambda: body.read(4096), b''):
            fd.write(chunk)
    print('Done')


# Object: Delete Object
@handle_error
def delete_object_example():
    """
    This operation will remove the specified object.
    You must have the WRITE permission of the bucket where the object is located before you can delete the object.
    """
    response = client.delete_object(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name
        Key=KEY,
    )
    pretty_print(response)


# Object: Put Object-Copy
@handle_error
def put_object_copy_example():
    """
    This operation is used to create a copy of the object stored in OOS.
    It is similar to performing a GET and then performing a PUT.
    """
    response = client.copy_object(
        # Name of the bucket
        Bucket=BUCKET,
        # Name of the source Bucket and object
        CopySource={'Bucket': BUCKET, 'Key': KEY},
        # Name of the copied object
        Key='{0}-Copy'.format(KEY),
        # Set the location of data storage
        DataLocation='type=Specified,location=ChengDu,scheduleStrategy=Allowed',
        # Specify whether to overwrite object with the same name when performing a putObject-copy operation.
        ForbidOverWrite=False,  # Note: This configuration is supported only by some resource pools.
        # Storage type of the target file
        # StorageClass= 'STANDARD',
        # Indicate whether the metadata is a copy of the source file or is overwritten by the metadata provided by the request header, values: COPY | REPLACE
        # MetadataDirective= 'COPY',
        # Set to only perform the object copy operation when the source object's Etag matches the given Etag
        # CopySourceIfMatch='your_etag',
        # Set to only perform the object copy operation when the source object's Etag does not match the given Etag
        # CopySourceIfNoneMatch='your_etag',
        # Set to only perform the object copy operation if the source object has been modified after the specified time.
        # CopySourceIfModifiedSince='your_date',
        # Set to only perform the object copy operation if the source object has not been modified since the specified time.
        # CopySourceIfUnmodifiedSince='your_date'
    )
    pretty_print(response)


# Object: Initial Multipart Upload
# Object: Upload Part
# Object: Complete Multipart Upload
# Object: List Part
@handle_error
def multipart_upload_example():
    """
    Initialize a multipart upload operation and return an upload ID,
    which is used to merge all the parts uploaded in the multipart upload operation into a single object
    """
    print('Initial Multipart Upload')
    upload = client.create_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Set the data location of the Bucket
        DataLocation='type=Specified,location=ChengDu,scheduleStrategy=Allowed'
    )
    pretty_print(upload)

    """
    Implement the upload of parts in the multipart upload operation
    """
    print("Upload Part")
    MultipartUpload = {
        'Part': []
    }
    with open(UPLOAD_FILE, 'rb') as fd:
        num = 0
        for chunk in iter(lambda: fd.read(5 * 1024 * 1024), b''):
            num += 1
            part = client.upload_part(
                Body=chunk,
                # Name of the bucket
                Bucket=BUCKET,
                # Object name corresponding to the multipart upload
                Key=MULTIPART_UPLOAD_KEY,
                # Multipart number for identifying parts
                PartNumber=num,
                # Multipart upload ID
                UploadId=upload['UploadId']
            )
            # MultipartUpload['Part '] is a container for a part
            MultipartUpload['Part'].append({
                # Multipart number for identifying parts
                'PartNumber': num,
                # Etag content returned when the part upload is completed
                'ETag': part['ETag']
            })
            pretty_print(part)

    """
    List all the parts that have been uploaded in a single multipart upload process
    """
    print('List Parts')
    ls = client.list_parts(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Set the maximum number of parts returned in the response body
        MaxParts=10,
        # Multipart upload ID
        UploadId=upload['UploadId']
    )
    pretty_print(ls)

    """
    Complete a multipart upload process by merging previously uploaded parts
    """
    print('Complete Multipart Upload')
    complete = client.complete_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Multipart upload ID
        UploadId=upload['UploadId'],
        # Requested container
        MultipartUpload=MultipartUpload
    )
    pretty_print(complete)
    print('Done')


# Object: Abort Multipart Upload
@handle_error
def abort_multipart_example():
    """
    This API is used to terminate a multipart upload operation
    """
    upload = client.create_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Set the data location of the Bucket
        DataLocation='type=Specified,location=ChengDu,scheduleStrategy=Allowed'
    )
    pretty_print(upload)

    # Terminate this multipart upload operation
    response = client.abort_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Multipart upload ID
        UploadId=upload['UploadId']
    )
    pretty_print(response)


# Object: Copy Part
@handle_error
def copy_part_example():
    """
    The existing object can be uploaded as a part in multipart upload and copied to generate a new part
    """
    upload = client.create_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY
    )
    pretty_print(upload)

    part = client.upload_part_copy(
        # Name of the bucket
        Bucket=BUCKET,
        # Specify the name and object name of the source Bucket
        CopySource={'Bucket': BUCKET, 'Key': KEY},
        Key=MULTIPART_UPLOAD_KEY,
        # The copied multipart number
        PartNumber=1,
        # ID used to identify the copy of a part in multipart upload
        UploadId=upload['UploadId']
    )
    pretty_print(part)

    MultipartUpload = {
        'Part': [
            {'PartNumber': 1,
             'ETag': part['CopyPartResult']['ETag']}
        ]
    }

    complete = client.complete_multipart_upload(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name corresponding to the multipart upload
        Key=MULTIPART_UPLOAD_KEY,
        # Multipart upload ID
        UploadId=upload['UploadId'],
        # Requested container
        MultipartUpload=MultipartUpload
    )
    pretty_print(complete)
    print('Done')


# Object: Delete Multiple Objects
@handle_error
def delete_multiple_example():
    """
    The batch delete object feature allows you to delete multiple objects in a Bucket with one HTTP request
    """
    response = client.delete_objects(
        # Name of the bucket
        Bucket=BUCKET,
        # Requested container
        Delete={
            # Container that contains the deleted object
            'Objects': [
                {
                    # Name of the deleted object
                    'Key': 'test'
                },
                {
                    # Name of the deleted object
                    'Key': 'test-Copy'
                }
            ]
        }
    )
    pretty_print(response)


# Object: Generate Shared link
@handle_error
def generate_shared_link_example():
    """
    For private or read-only buckets, you can generate a shared link to the object to share it with others,
    and also set a rate limit in the link to control the download speed
    """
    shared_link = client.generate_presigned_url(
        ClientMethod='get_object',
        Params={
            # Name of the bucket
            'Bucket': BUCKET,
            # Object name
            'Key': KEY
        },
        # Expire In
        ExpiresIn=3600
    )
    print(shared_link)


# Object: Head Object
@handle_error
def head_object_example():
    """
    This operation is used to get metadata information about an object without returning the data itself
    """
    response = client.head_object(
        # Name of the bucket
        Bucket=BUCKET,
        # Object name
        Key=KEY
    )
    pretty_print(response)


if __name__ == '__main__':
    try:
        """
        Service related APIs
        """
        list_bucket_example()
        # get_regions_example()

        """
        Bucket related APIs
        """
        # put_bucket_example()
        # get_bucket_location_example()
        # get_bucket_acl_example()
        # get_bucket_example()
        # delete_bucket_example()
        # put_bucket_policy_example()
        # get_bucket_policy_example()
        # delete_bucket_policy_example()
        # put_bucket_website_example()
        # get_bucket_website_example()
        # delete_bucket_website_example()
        # list_multipart_uploads_example()
        # put_bucket_logging_example()
        # get_bucket_logging_example()
        # head_bucket_example()
        # put_bucket_lifecycle_example()
        # get_bucket_lifecycle_example()
        # delete_bucket_lifecycle_example()
        # put_bucket_cors_example()
        # get_bucket_cors_example()
        # delete_bucket_cors_example()
        # put_bucket_object_lock_example()
        # get_bucket_object_lock_example()
        # delete_bucket_object_lock_example()
        # put_bucket_inventory_example()
        # get_bucket_inventory_example()
        # delete_bucket_inventory_example()
        # list_bucket_inventory_example()

        """
        Object related APIs
        """
        # put_object_example()
        # get_object_example()
        # delete_object_example()
        # put_object_copy_example()
        # multipart_upload_example()
        # abort_multipart_example()
        # copy_part_example()
        # delete_multiple_example()
        # generate_shared_link_example()
        # head_object_example()

    except Exception as ex:
        print(traceback.format_exc())
        print(ex)
