Skip to content

Commit 61811f2

Browse files
authored
[Tables] Various small fixes (Azure#18964)
* match condition refactor * Return TableItem on create * Deserialization updates * Support extra metadata * Added some tests * Added async recordings * Updated changelog * Updated changelog * Docstring feedback
1 parent 1151698 commit 61811f2

File tree

349 files changed

+24935
-12518
lines changed

Some content is hidden

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

349 files changed

+24935
-12518
lines changed

sdk/tables/azure-data-tables/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
## 12.0.0 (unreleased)
44
**Breaking**
55
* EdmType.Binary data in entities will now be deserialized as `bytes` in Python 3 and `str` in Python 2, rather than an `EdmProperty` instance. Likewise on serialization, `bytes` in Python 3 and `str` in Python 2 will be interpreted as binary (this is unchanged for Python 3, but breaking for Python 2, where `str` was previously serialized as EdmType.String)
6+
* `TableClient.create_table` now returns an instance of `TableItem`.
67

78
**Fixes**
89
* Fixed support for Cosmos emulator endpoint, via URL/credential or connection string.
910
* Fixed table name from URL parsing in `TableClient.from_table_url` classmethod.
1011
* The `account_name` attribute on clients will now be pulled from an `AzureNamedKeyCredential` if used.
12+
* Any additional odata metadata is returned in entitys metadata.
13+
* The timestamp in entity metadata is now deserialized to a timestamp.
14+
* If the `prefer` header is added in the `create_entity` operation, the echo will be returned.
15+
* Errors raised on a 412 if-not-match error will now be a specific `azure.core.exceptions.ResourceModifiedError`.
16+
* `EdmType.DOUBLE` values are now explicitly typed in the request payload.
1117

1218
## 12.0.0b7 (2021-05-11)
1319
**Breaking**

sdk/tables/azure-data-tables/azure/data/tables/_deserialize.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
6+
from typing import Dict, Optional, Any
77
from uuid import UUID
88
import logging
99
import datetime
@@ -200,11 +200,14 @@ def _convert_to_entity(entry_element):
200200
entity[name] = new_property
201201

202202
# extract etag from entry
203-
etag = odata.get("etag")
204-
if timestamp and not etag:
205-
etag = "W/\"datetime'" + url_quote(timestamp) + "'\""
206-
207-
entity._metadata = {'etag': etag, 'timestamp': timestamp} # pylint: disable=protected-access
203+
etag = odata.pop("etag", None)
204+
odata.pop("metadata", None)
205+
if timestamp:
206+
if not etag:
207+
etag = "W/\"datetime'" + url_quote(timestamp) + "'\""
208+
timestamp = _from_entity_datetime(timestamp)
209+
odata.update({'etag': etag, 'timestamp': timestamp})
210+
entity._metadata = odata # pylint: disable=protected-access
208211
return entity
209212

210213

@@ -252,10 +255,15 @@ def _return_context_and_deserialized(
252255
return response.context['location_mode'], deserialized, response_headers
253256

254257

255-
def _trim_service_metadata(metadata):
256-
# type: (dict[str,str]) -> None
257-
return {
258+
def _trim_service_metadata(metadata, content=None):
259+
# type: (Dict[str,str], Optional[Dict[str, Any]]) -> None
260+
result = {
258261
"date": metadata.pop("date", None),
259262
"etag": metadata.pop("etag", None),
260263
"version": metadata.pop("version", None),
261264
}
265+
preference = metadata.pop('preference_applied', None)
266+
if preference:
267+
result["preference_applied"] = preference
268+
result["content"] = content
269+
return result

sdk/tables/azure-data-tables/azure/data/tables/_error.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ def _decode_error(response, error_message=None, error_type=None, **kwargs):
9595
try:
9696
if not error_type:
9797
error_code = TableErrorCode(error_code)
98-
if error_code in [TableErrorCode.condition_not_met]:
98+
if error_code in [
99+
TableErrorCode.condition_not_met,
100+
TableErrorCode.update_condition_not_satisfied
101+
]:
99102
error_type = ResourceModifiedError
100103
elif error_code in [
101104
TableErrorCode.invalid_authentication_info,

sdk/tables/azure-data-tables/azure/data/tables/_serialize.py

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,16 @@
2020
from ._error import _ERROR_VALUE_TOO_LARGE, _ERROR_TYPE_NOT_SUPPORTED
2121

2222

23-
def _get_match_headers(kwargs, match_param, etag_param):
24-
if_match = None
25-
if_none_match = None
26-
match_condition = kwargs.pop(match_param, None)
23+
def _get_match_headers(etag, match_condition):
2724
if match_condition == MatchConditions.IfNotModified:
28-
if_match = kwargs.pop(etag_param, None)
29-
if not if_match:
30-
raise ValueError(
31-
"'{}' specified without '{}'.".format(match_param, etag_param)
32-
)
33-
elif match_condition == MatchConditions.IfPresent:
34-
if_match = "*"
35-
elif match_condition == MatchConditions.IfModified:
36-
if_none_match = kwargs.pop(etag_param, None)
37-
if not if_none_match:
38-
raise ValueError(
39-
"'{}' specified without '{}'.".format(match_param, etag_param)
40-
)
41-
elif match_condition == MatchConditions.IfMissing:
42-
if_none_match = "*"
43-
elif match_condition == MatchConditions.Unconditionally:
44-
if_none_match = "*"
45-
elif match_condition is None:
46-
if kwargs.get(etag_param):
47-
raise ValueError(
48-
"'{}' specified without '{}'.".format(etag_param, match_param)
49-
)
50-
else:
51-
raise TypeError("Invalid match condition: {}".format(match_condition))
52-
return if_match, if_none_match
25+
if not etag:
26+
raise ValueError("IfNotModified must be specified with etag.")
27+
return etag
28+
if match_condition == MatchConditions.Unconditionally:
29+
if etag:
30+
raise ValueError("Etag is not supported for an Unconditional operation.")
31+
return "*"
32+
raise ValueError("Unsupported match condition: {}".format(match_condition))
5333

5434

5535
def _parameter_filter_substitution(parameters, query_filter):
@@ -116,7 +96,7 @@ def _to_entity_float(value):
11696
return EdmType.DOUBLE, "Infinity"
11797
if value == float("-inf"):
11898
return EdmType.DOUBLE, "-Infinity"
119-
return None, value
99+
return EdmType.DOUBLE, value
120100

121101

122102
def _to_entity_guid(value):
@@ -210,7 +190,6 @@ def _add_entity_properties(source):
210190
properties = {}
211191

212192
to_send = dict(source) # shallow copy
213-
to_send.pop("_metadata", None)
214193

215194
# set properties type for types we know if value has no type info.
216195
# if value has type info, then set the type to value.type

sdk/tables/azure-data-tables/azure/data/tables/_table_batch.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
Optional
1313
)
1414

15+
from azure.core import MatchConditions
16+
1517
from ._common_conversion import _transform_patch_to_cosmos_post
1618
from ._models import UpdateMode
1719
from ._serialize import _get_match_headers, _add_entity_properties
@@ -247,15 +249,9 @@ def update(
247249
etag = entity.metadata.get("etag", None)
248250
except (AttributeError, TypeError):
249251
pass
250-
251-
if_match, _ = _get_match_headers(
252-
kwargs=dict(
253-
kwargs,
254-
etag=etag,
255-
match_condition=match_condition,
256-
),
257-
etag_param="etag",
258-
match_param="match_condition",
252+
if_match = _get_match_headers(
253+
etag=etag,
254+
match_condition=match_condition or MatchConditions.Unconditionally,
259255
)
260256

261257
partition_key = temp["PartitionKey"]
@@ -266,7 +262,7 @@ def update(
266262
table=self.table_name,
267263
partition_key=partition_key,
268264
row_key=row_key,
269-
if_match=if_match or "*",
265+
if_match=if_match,
270266
table_entity_properties=temp,
271267
**kwargs
272268
)
@@ -275,7 +271,7 @@ def update(
275271
table=self.table_name,
276272
partition_key=partition_key,
277273
row_key=row_key,
278-
if_match=if_match or "*",
274+
if_match=if_match,
279275
table_entity_properties=temp,
280276
**kwargs
281277
)
@@ -533,23 +529,16 @@ def delete(
533529
etag = entity.metadata.get("etag", None)
534530
except (AttributeError, TypeError):
535531
pass
536-
537-
if_match, _ = _get_match_headers(
538-
kwargs=dict(
539-
kwargs,
540-
etag=etag,
541-
match_condition=match_condition,
542-
),
543-
etag_param="etag",
544-
match_param="match_condition",
532+
if_match = _get_match_headers(
533+
etag=etag,
534+
match_condition=match_condition or MatchConditions.Unconditionally,
545535
)
546536

547-
548537
self._batch_delete_entity(
549538
table=self.table_name,
550539
partition_key=partition_key,
551540
row_key=row_key,
552-
if_match=if_match or "*",
541+
if_match=if_match,
553542
**kwargs
554543
)
555544

0 commit comments

Comments
 (0)