Skip to content

Commit abfd2ce

Browse files
JuliehzlUbuntu
and
Ubuntu
authored
Support v2019-12-12 for blob, file share, file datalake (#37)
* add new version * upgrade version * upgrade version * refine Co-authored-by: Ubuntu <zunli@zuhvm.etyrgwjlsqfeplvzbzef2qjagg.cbnx.internal.cloudapp.net>
1 parent 9d43eb1 commit abfd2ce

File tree

158 files changed

+41249
-470
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

158 files changed

+41249
-470
lines changed

README.rst

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ Handles multi-API versions of Azure Storage Data Plane originally from https://g
1717

1818
Change Log
1919
----------
20+
0.4.1
21+
+++++
22+
* Add tags support for blob
23+
* Add new api support for azure-multiapi-storagev2:
24+
- filedatalake
25+
- v2019-12-12
26+
- fileshare
27+
- v2019-12-12
28+
2029
0.4.0
2130
+++++
2231
* Add v2019-12-12 for azure.multiapi.storagev2.blob

azure/multiapi/storagev2/blob/v2019_12_12/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@
5252
CustomerProvidedEncryptionKey,
5353
ContainerEncryptionScope,
5454
BlobQueryError,
55-
DelimitedJSON,
55+
DelimitedJsonDialect,
5656
DelimitedTextDialect,
5757
ObjectReplicationPolicy,
5858
ObjectReplicationRule
5959
)
60+
from ._list_blobs_helper import BlobPrefix
6061

6162
__version__ = VERSION
6263

@@ -195,6 +196,7 @@ def download_blob_from_url(
195196
'CorsRule',
196197
'ContainerProperties',
197198
'BlobProperties',
199+
'BlobPrefix',
198200
'FilteredBlob',
199201
'LeaseProperties',
200202
'ContentSettings',
@@ -215,7 +217,7 @@ def download_blob_from_url(
215217
'PartialBatchErrorException',
216218
'ContainerEncryptionScope',
217219
'BlobQueryError',
218-
'DelimitedJSON',
220+
'DelimitedJsonDialect',
219221
'DelimitedTextDialect',
220222
'BlobQueryReader',
221223
'ObjectReplicationPolicy',

azure/multiapi/storagev2/blob/v2019_12_12/_blob_client.py

+243-14
Large diffs are not rendered by default.

azure/multiapi/storagev2/blob/v2019_12_12/_blob_service_client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
from ._shared.models import UserDelegationKey
4040
from ._lease import BlobLeaseClient
4141
from ._models import (
42-
BlobProperties,
4342
ContainerProperties,
43+
BlobProperties,
4444
PublicAccess,
4545
BlobAnalyticsLogging,
4646
Metrics,

azure/multiapi/storagev2/blob/v2019_12_12/_container_client.py

+70-8
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@
4040
from ._models import ( # pylint: disable=unused-import
4141
ContainerProperties,
4242
BlobProperties,
43-
BlobPropertiesPaged,
44-
BlobType,
45-
BlobPrefix)
43+
BlobType)
44+
from ._list_blobs_helper import BlobPrefix, BlobPropertiesPaged
4645
from ._lease import BlobLeaseClient, get_access_conditions
4746
from ._blob_client import BlobClient
4847

@@ -783,6 +782,12 @@ def upload_blob(
783782
and act according to the condition specified by the `match_condition` parameter.
784783
:keyword ~azure.core.MatchConditions match_condition:
785784
The match condition to use upon the etag.
785+
:keyword str if_tags_match_condition
786+
Specify a SQL where clause on blob tags to operate only on blob with a matching value.
787+
eg. "\"tagname\"='my tag'"
788+
789+
.. versionadded:: 12.4.0
790+
786791
:keyword int timeout:
787792
The timeout parameter is expressed in seconds. This method may make
788793
multiple calls to the Azure service and the timeout will apply to
@@ -893,6 +898,12 @@ def delete_blob(
893898
and act according to the condition specified by the `match_condition` parameter.
894899
:keyword ~azure.core.MatchConditions match_condition:
895900
The match condition to use upon the etag.
901+
:keyword str if_tags_match_condition
902+
Specify a SQL where clause on blob tags to operate only on blob with a matching value.
903+
eg. "\"tagname\"='my tag'"
904+
905+
.. versionadded:: 12.4.0
906+
896907
:keyword int timeout:
897908
The timeout parameter is expressed in seconds.
898909
:rtype: None
@@ -952,6 +963,12 @@ def download_blob(self, blob, offset=None, length=None, **kwargs):
952963
and act according to the condition specified by the `match_condition` parameter.
953964
:keyword ~azure.core.MatchConditions match_condition:
954965
The match condition to use upon the etag.
966+
:keyword str if_tags_match_condition
967+
Specify a SQL where clause on blob tags to operate only on blob with a matching value.
968+
eg. "\"tagname\"='my tag'"
969+
970+
.. versionadded:: 12.4.0
971+
955972
:keyword ~azure.storage.blob.CustomerProvidedEncryptionKey cpk:
956973
Encrypts the data on the service-side with the given key.
957974
Use of customer-provided keys must be done over HTTPS.
@@ -998,6 +1015,9 @@ def _generate_delete_blobs_subrequest_options(
9981015
if_none_match = None
9991016
if modified_access_conditions is not None:
10001017
if_none_match = modified_access_conditions.if_none_match
1018+
if_tags = None
1019+
if modified_access_conditions is not None:
1020+
if_tags = modified_access_conditions.if_tags
10011021

10021022
# Construct parameters
10031023
timeout = kwargs.pop('timeout', None)
@@ -1027,6 +1047,8 @@ def _generate_delete_blobs_subrequest_options(
10271047
if if_none_match is not None:
10281048
header_parameters['If-None-Match'] = self._client._serialize.header( # pylint: disable=protected-access
10291049
"if_none_match", if_none_match, 'str')
1050+
if if_tags is not None:
1051+
header_parameters['x-ms-if-tags'] = self._client._serialize.header("if_tags", if_tags, 'str') # pylint: disable=protected-access
10301052

10311053
return query_parameters, header_parameters
10321054

@@ -1039,6 +1061,7 @@ def _generate_delete_blobs_options(self,
10391061
delete_snapshots = kwargs.pop('delete_snapshots', None)
10401062
if_modified_since = kwargs.pop('if_modified_since', None)
10411063
if_unmodified_since = kwargs.pop('if_unmodified_since', None)
1064+
if_tags_match_condition = kwargs.pop('if_tags_match_condition', None)
10421065
kwargs.update({'raise_on_any_failure': raise_on_any_failure,
10431066
'sas': self._query_str.replace('?', '&'),
10441067
'timeout': '&timeout=' + str(timeout) if timeout else ""
@@ -1057,18 +1080,21 @@ def _generate_delete_blobs_options(self,
10571080
if_modified_since=if_modified_since or blob.get('if_modified_since'),
10581081
if_unmodified_since=if_unmodified_since or blob.get('if_unmodified_since'),
10591082
etag=blob.get('etag'),
1083+
if_tags_match_condition=if_tags_match_condition or blob.get('if_tags_match_condition'),
10601084
match_condition=blob.get('match_condition') or MatchConditions.IfNotModified if blob.get('etag')
10611085
else None,
10621086
timeout=blob.get('timeout'),
10631087
)
1064-
query_parameters, header_parameters = self._generate_delete_blobs_subrequest_options(**options)
10651088
except AttributeError:
1066-
query_parameters, header_parameters = self._generate_delete_blobs_subrequest_options(
1089+
options = BlobClient._generic_delete_blob_options( # pylint: disable=protected-access
10671090
delete_snapshots=delete_snapshots,
10681091
if_modified_since=if_modified_since,
1069-
if_unmodified_since=if_unmodified_since
1092+
if_unmodified_since=if_unmodified_since,
1093+
if_tags_match_condition=if_tags_match_condition
10701094
)
10711095

1096+
query_parameters, header_parameters = self._generate_delete_blobs_subrequest_options(**options)
1097+
10721098
req = HttpRequest(
10731099
"DELETE",
10741100
"/{}/{}{}".format(quote(container_name), quote(blob_name, safe='/~'), self._query_str),
@@ -1113,6 +1139,8 @@ def delete_blobs(self, *blobs, **kwargs):
11131139
key: 'etag', value type: str
11141140
match the etag or not:
11151141
key: 'match_condition', value type: MatchConditions
1142+
tags match condition:
1143+
key: 'if_tags_match_condition', value type: str
11161144
lease:
11171145
key: 'lease_id', value type: Union[str, LeaseClient]
11181146
timeout for subrequest:
@@ -1135,6 +1163,12 @@ def delete_blobs(self, *blobs, **kwargs):
11351163
If a date is passed in without timezone info, it is assumed to be UTC.
11361164
Specify this header to perform the operation only if
11371165
the resource has not been modified since the specified date/time.
1166+
:keyword str if_tags_match_condition
1167+
Specify a SQL where clause on blob tags to operate only on blob with a matching value.
1168+
eg. "\"tagname\"='my tag'"
1169+
1170+
.. versionadded:: 12.4.0
1171+
11381172
:keyword bool raise_on_any_failure:
11391173
This is a boolean param which defaults to True. When this is set, an exception
11401174
is raised even if there is a single operation failure.
@@ -1152,19 +1186,25 @@ def delete_blobs(self, *blobs, **kwargs):
11521186
:dedent: 8
11531187
:caption: Deleting multiple blobs.
11541188
"""
1189+
if len(blobs) == 0:
1190+
return iter(list())
1191+
11551192
reqs, options = self._generate_delete_blobs_options(*blobs, **kwargs)
11561193

11571194
return self._batch_send(*reqs, **options)
11581195

11591196
def _generate_set_tiers_subrequest_options(
1160-
self, tier, rehydrate_priority=None, lease_access_conditions=None, **kwargs
1197+
self, tier, snapshot=None, version_id=None, rehydrate_priority=None, lease_access_conditions=None, **kwargs
11611198
):
11621199
"""This code is a copy from _generated.
11631200
11641201
Once Autorest is able to provide request preparation this code should be removed.
11651202
"""
11661203
if not tier:
11671204
raise ValueError("A blob tier must be specified")
1205+
if snapshot and version_id:
1206+
raise ValueError("Snapshot and version_id cannot be set at the same time")
1207+
if_tags = kwargs.pop('if_tags', None)
11681208

11691209
lease_id = None
11701210
if lease_access_conditions is not None:
@@ -1174,6 +1214,10 @@ def _generate_set_tiers_subrequest_options(
11741214
timeout = kwargs.pop('timeout', None)
11751215
# Construct parameters
11761216
query_parameters = {}
1217+
if snapshot is not None:
1218+
query_parameters['snapshot'] = self._client._serialize.query("snapshot", snapshot, 'str') # pylint: disable=protected-access
1219+
if version_id is not None:
1220+
query_parameters['versionid'] = self._client._serialize.query("version_id", version_id, 'str') # pylint: disable=protected-access
11771221
if timeout is not None:
11781222
query_parameters['timeout'] = self._client._serialize.query("timeout", timeout, 'int', minimum=0) # pylint: disable=protected-access
11791223
query_parameters['comp'] = self._client._serialize.query("comp", comp, 'str') # pylint: disable=protected-access, specify-parameter-names-in-call
@@ -1186,6 +1230,8 @@ def _generate_set_tiers_subrequest_options(
11861230
"rehydrate_priority", rehydrate_priority, 'str')
11871231
if lease_id is not None:
11881232
header_parameters['x-ms-lease-id'] = self._client._serialize.header("lease_id", lease_id, 'str') # pylint: disable=protected-access
1233+
if if_tags is not None:
1234+
header_parameters['x-ms-if-tags'] = self._client._serialize.header("if_tags", if_tags, 'str') # pylint: disable=protected-access
11891235

11901236
return query_parameters, header_parameters
11911237

@@ -1197,6 +1243,7 @@ def _generate_set_tiers_options(self,
11971243
timeout = kwargs.pop('timeout', None)
11981244
raise_on_any_failure = kwargs.pop('raise_on_any_failure', True)
11991245
rehydrate_priority = kwargs.pop('rehydrate_priority', None)
1246+
if_tags = kwargs.pop('if_tags_match_condition', None)
12001247
kwargs.update({'raise_on_any_failure': raise_on_any_failure,
12011248
'sas': self._query_str.replace('?', '&'),
12021249
'timeout': '&timeout=' + str(timeout) if timeout else ""
@@ -1211,13 +1258,16 @@ def _generate_set_tiers_options(self,
12111258
tier = blob_tier or blob.get('blob_tier')
12121259
query_parameters, header_parameters = self._generate_set_tiers_subrequest_options(
12131260
tier=tier,
1261+
snapshot=blob.get('snapshot'),
1262+
version_id=blob.get('version_id'),
12141263
rehydrate_priority=rehydrate_priority or blob.get('rehydrate_priority'),
12151264
lease_access_conditions=blob.get('lease_id'),
1265+
if_tags=if_tags or blob.get('if_tags_match_condition'),
12161266
timeout=timeout or blob.get('timeout')
12171267
)
12181268
except AttributeError:
12191269
query_parameters, header_parameters = self._generate_set_tiers_subrequest_options(
1220-
blob_tier, rehydrate_priority=rehydrate_priority)
1270+
blob_tier, rehydrate_priority=rehydrate_priority, if_tags=if_tags)
12211271

12221272
req = HttpRequest(
12231273
"PUT",
@@ -1270,12 +1320,24 @@ def set_standard_blob_tier_blobs(
12701320
key: 'rehydrate_priority', value type: RehydratePriority
12711321
lease:
12721322
key: 'lease_id', value type: Union[str, LeaseClient]
1323+
snapshot:
1324+
key: "snapshost", value type: str
1325+
version id:
1326+
key: "version_id", value type: str
1327+
tags match condition:
1328+
key: 'if_tags_match_condition', value type: str
12731329
timeout for subrequest:
12741330
key: 'timeout', value type: int
12751331
12761332
:type blobs: list[str], list[dict], or list[~azure.storage.blob.BlobProperties]
12771333
:keyword ~azure.storage.blob.RehydratePriority rehydrate_priority:
12781334
Indicates the priority with which to rehydrate an archived blob
1335+
:keyword str if_tags_match_condition
1336+
Specify a SQL where clause on blob tags to operate only on blob with a matching value.
1337+
eg. "\"tagname\"='my tag'"
1338+
1339+
.. versionadded:: 12.4.0
1340+
12791341
:keyword int timeout:
12801342
The timeout parameter is expressed in seconds.
12811343
:keyword bool raise_on_any_failure:

azure/multiapi/storagev2/blob/v2019_12_12/_deserialize.py

+58-9
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
TYPE_CHECKING
1010
)
1111

12+
from ._models import BlobType, CopyProperties, ContentSettings, LeaseProperties, BlobProperties
13+
from ._shared.models import get_enum_value
14+
1215
from ._shared.response_handlers import deserialize_metadata
13-
from ._models import BlobProperties, ContainerProperties, BlobAnalyticsLogging, Metrics, CorsRule, RetentionPolicy, \
16+
from ._models import ContainerProperties, BlobAnalyticsLogging, Metrics, CorsRule, RetentionPolicy, \
1417
StaticWebsite, ObjectReplicationPolicy, ObjectReplicationRule
1518

1619
if TYPE_CHECKING:
@@ -20,7 +23,7 @@
2023
def deserialize_blob_properties(response, obj, headers):
2124
blob_properties = BlobProperties(
2225
metadata=deserialize_metadata(response, obj, headers),
23-
object_replication_source_properties=deserialize_ors_policies(response),
26+
object_replication_source_properties=deserialize_ors_policies(response.headers),
2427
**headers
2528
)
2629
if 'Content-Range' in headers:
@@ -31,20 +34,21 @@ def deserialize_blob_properties(response, obj, headers):
3134
return blob_properties
3235

3336

34-
def deserialize_ors_policies(response):
37+
def deserialize_ors_policies(policy_dictionary):
38+
39+
if policy_dictionary is None:
40+
return None
3541
# For source blobs (blobs that have policy ids and rule ids applied to them),
3642
# the header will be formatted as "x-ms-or-<policy_id>_<rule_id>: {Complete, Failed}".
3743
# The value of this header is the status of the replication.
38-
or_policy_status_headers = {key: val for key, val in response.headers.items()
39-
if key.startswith('x-ms-or') and key != 'x-ms-or-policy-id'}
44+
or_policy_status_headers = {key: val for key, val in policy_dictionary.items()
45+
if 'or-' in key and key != 'x-ms-or-policy-id'}
4046

4147
parsed_result = {}
4248

43-
# all the ors headers have the same prefix, so we note down its length here to avoid recalculating it repeatedly
44-
header_prefix_length = len('x-ms-or-')
45-
4649
for key, val in or_policy_status_headers.items():
47-
policy_and_rule_ids = key[header_prefix_length:].split('_')
50+
# list blobs gives or-policy_rule and get blob properties gives x-ms-or-policy_rule
51+
policy_and_rule_ids = key.split('or-')[1].split('_')
4852
policy_id = policy_and_rule_ids[0]
4953
rule_id = policy_and_rule_ids[1]
5054

@@ -106,3 +110,48 @@ def service_properties_deserialize(generated):
106110
'delete_retention_policy': RetentionPolicy._from_generated(generated.delete_retention_policy), # pylint: disable=protected-access
107111
'static_website': StaticWebsite._from_generated(generated.static_website), # pylint: disable=protected-access
108112
}
113+
114+
115+
def get_blob_properties_from_generated_code(generated):
116+
blob = BlobProperties()
117+
blob.name = generated.name
118+
blob_type = get_enum_value(generated.properties.blob_type)
119+
blob.blob_type = BlobType(blob_type) if blob_type else None
120+
blob.etag = generated.properties.etag
121+
blob.deleted = generated.deleted
122+
blob.snapshot = generated.snapshot
123+
blob.is_append_blob_sealed = generated.properties.is_sealed
124+
blob.metadata = generated.metadata.additional_properties if generated.metadata else {}
125+
blob.encrypted_metadata = generated.metadata.encrypted if generated.metadata else None
126+
blob.lease = LeaseProperties._from_generated(generated) # pylint: disable=protected-access
127+
blob.copy = CopyProperties._from_generated(generated) # pylint: disable=protected-access
128+
blob.last_modified = generated.properties.last_modified
129+
blob.creation_time = generated.properties.creation_time
130+
blob.content_settings = ContentSettings._from_generated(generated) # pylint: disable=protected-access
131+
blob.size = generated.properties.content_length
132+
blob.page_blob_sequence_number = generated.properties.blob_sequence_number
133+
blob.server_encrypted = generated.properties.server_encrypted
134+
blob.encryption_scope = generated.properties.encryption_scope
135+
blob.deleted_time = generated.properties.deleted_time
136+
blob.remaining_retention_days = generated.properties.remaining_retention_days
137+
blob.blob_tier = generated.properties.access_tier
138+
blob.rehydrate_priority = generated.properties.rehydrate_priority
139+
blob.blob_tier_inferred = generated.properties.access_tier_inferred
140+
blob.archive_status = generated.properties.archive_status
141+
blob.blob_tier_change_time = generated.properties.access_tier_change_time
142+
blob.version_id = generated.version_id
143+
blob.is_current_version = generated.is_current_version
144+
blob.tag_count = generated.properties.tag_count
145+
blob.tags = parse_tags(generated.blob_tags) # pylint: disable=protected-access
146+
blob.object_replication_source_properties = deserialize_ors_policies(generated.object_replication_metadata)
147+
return blob
148+
149+
150+
def parse_tags(generated_tags):
151+
# type: (Optional[List[BlobTag]]) -> Union[Dict[str, str], None]
152+
"""Deserialize a list of BlobTag objects into a dict.
153+
"""
154+
if generated_tags:
155+
tag_dict = {t.key: t.value for t in generated_tags.blob_tag_set}
156+
return tag_dict
157+
return None

azure/multiapi/storagev2/blob/v2019_12_12/_download.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class StorageStreamDownloader(object): # pylint: disable=too-many-instance-attr
258258
The properties of the blob being downloaded. If only a range of the data is being
259259
downloaded, this will be reflected in the properties.
260260
:ivar int size:
261-
The size of the total data in the stream. This will be the byte range if speficied,
261+
The size of the total data in the stream. This will be the byte range if specified,
262262
otherwise the total size of the blob.
263263
"""
264264

0 commit comments

Comments
 (0)