# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict, Optional, Union
from azure.storage.blob._serialize import _get_match_headers
from ._shared import encode_base64
from ._generated.models import ModifiedAccessConditions, PathHTTPHeaders, \
SourceModifiedAccessConditions, LeaseAccessConditions, CpkInfo
_SUPPORTED_API_VERSIONS = [
'2019-02-02',
'2019-07-07',
'2019-10-10',
'2019-12-12',
'2020-02-10',
'2020-04-08',
'2020-06-12',
'2020-08-04',
'2020-10-02',
'2020-12-06',
'2021-02-12',
'2021-04-10',
'2021-06-08',
'2021-08-06',
'2021-12-02',
'2022-11-02',
'2023-01-03',
'2023-05-03',
'2023-08-03',
'2023-11-03',
'2024-05-04',
'2024-08-04',
'2024-11-04',
'2025-01-05',
'2025-05-05',
] # This list must be in chronological order!
def get_api_version(kwargs):
# type: (Dict[str, Any]) -> str
api_version = kwargs.get('api_version', None)
if api_version and api_version not in _SUPPORTED_API_VERSIONS:
versions = '\n'.join(_SUPPORTED_API_VERSIONS)
raise ValueError(f"Unsupported API version '{api_version}'. Please select from:\n{versions}")
return api_version or _SUPPORTED_API_VERSIONS[-1]
def compare_api_versions(version1: str, version2: str) -> int:
v1 = _SUPPORTED_API_VERSIONS.index(version1)
v2 = _SUPPORTED_API_VERSIONS.index(version2)
if v1 == v2:
return 0
if v1 < v2:
return -1
return 1
def convert_dfs_url_to_blob_url(dfs_account_url):
return dfs_account_url.replace('.dfs.', '.blob.', 1)
def convert_datetime_to_rfc1123(date):
weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][date.weekday()]
month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"][date.month - 1]
return f"{weekday}, {date.day:02} {month} {date.year:04} {date.hour:02}:{date.minute:02}:{date.second:02} GMT"
def add_metadata_headers(metadata=None):
# type: (Optional[Dict[str, str]]) -> str
if not metadata:
return None
headers = []
if metadata:
for key, value in metadata.items():
headers.append(key + '=')
headers.append(encode_base64(value))
headers.append(',')
if headers:
del headers[-1]
return ''.join(headers)
def get_mod_conditions(kwargs):
# type: (Dict[str, Any]) -> ModifiedAccessConditions
if_match, if_none_match = _get_match_headers(kwargs, 'match_condition', 'etag')
return ModifiedAccessConditions(
if_modified_since=kwargs.pop('if_modified_since', None),
if_unmodified_since=kwargs.pop('if_unmodified_since', None),
if_match=if_match or kwargs.pop('if_match', None),
if_none_match=if_none_match or kwargs.pop('if_none_match', None)
)
def get_source_mod_conditions(kwargs):
# type: (Dict[str, Any]) -> SourceModifiedAccessConditions
if_match, if_none_match = _get_match_headers(kwargs, 'source_match_condition', 'source_etag')
return SourceModifiedAccessConditions(
source_if_modified_since=kwargs.pop('source_if_modified_since', None),
source_if_unmodified_since=kwargs.pop('source_if_unmodified_since', None),
source_if_match=if_match or kwargs.pop('source_if_match', None),
source_if_none_match=if_none_match or kwargs.pop('source_if_none_match', None)
)
def get_path_http_headers(content_settings):
path_headers = PathHTTPHeaders(
cache_control=content_settings.cache_control,
content_type=content_settings.content_type,
content_md5=bytearray(content_settings.content_md5) if content_settings.content_md5 else None,
content_encoding=content_settings.content_encoding,
content_language=content_settings.content_language,
content_disposition=content_settings.content_disposition
)
return path_headers
def get_access_conditions(lease):
# type: (Optional[Union[BlobLeaseClient, str]]) -> Union[LeaseAccessConditions, None]
try:
lease_id = lease.id # type: ignore
except AttributeError:
lease_id = lease # type: ignore
return LeaseAccessConditions(lease_id=lease_id) if lease_id else None
def get_lease_id(lease):
if not lease:
return ""
try:
lease_id = lease.id
except AttributeError:
lease_id = lease
return lease_id
def get_lease_action_properties(kwargs: Dict[str, Any]) -> Dict[str, Any]:
lease_action = kwargs.pop('lease_action', None)
lease_duration = kwargs.pop('lease_duration', None)
lease = kwargs.pop('lease', None)
try:
lease_id = lease.id
except AttributeError:
lease_id = lease
proposed_lease_id = None
access_conditions = None
# Acquiring a new lease
if lease_action in ['acquire', 'acquire-release']:
# Use provided lease id as the new lease id
proposed_lease_id = lease_id
# Assign a default lease duration if not provided
lease_duration = lease_duration or -1
else:
# Use lease id as access conditions
access_conditions = LeaseAccessConditions(lease_id=lease_id) if lease_id else None
return {
'lease_action': lease_action,
'lease_duration': lease_duration,
'proposed_lease_id': proposed_lease_id,
'lease_access_conditions': access_conditions
}
def get_cpk_info(scheme, kwargs):
# type: (str, Dict[str, Any]) -> CpkInfo
cpk = kwargs.pop('cpk', None)
if cpk:
if scheme.lower() != 'https':
raise ValueError("Customer provided encryption key must be used over HTTPS.")
return CpkInfo(
encryption_key=cpk.key_value,
encryption_key_sha256=cpk.key_hash,
encryption_algorithm=cpk.algorithm)
return None