Skip to content

Commit cdb6d52

Browse files
author
annie-mac
committed
expose feedRange as a class type
1 parent 2ae90af commit cdb6d52

File tree

8 files changed

+140
-40
lines changed

8 files changed

+140
-40
lines changed

sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
)
4343
from .partition_key import PartitionKey
4444
from .permission import Permission
45+
from ._feed_range import FeedRange
4546

4647
__all__ = (
4748
"CosmosClient",
@@ -64,5 +65,6 @@
6465
"TriggerType",
6566
"ConnectionRetryPolicy",
6667
"ThroughputProperties",
68+
"FeedRange"
6769
)
6870
__version__ = VERSION

sdk/cosmos/azure-cosmos/azure/cosmos/_change_feed/change_feed_state.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from azure.cosmos._change_feed.change_feed_start_from import ChangeFeedStartFromInternal, \
3535
ChangeFeedStartFromETagAndFeedRange
3636
from azure.cosmos._change_feed.composite_continuation_token import CompositeContinuationToken
37-
from azure.cosmos._change_feed.feed_range import FeedRange, FeedRangeEpk, FeedRangePartitionKey
37+
from azure.cosmos._change_feed.feed_range_internal import FeedRangeInternal, FeedRangeInternalEpk, FeedRangeInternalPartitionKey
3838
from azure.cosmos._change_feed.feed_range_composite_continuation_token import FeedRangeCompositeContinuation
3939
from azure.cosmos._routing.aio.routing_map_provider import SmartRoutingMapProvider as AsyncSmartRoutingMapProvider
4040
from azure.cosmos._routing.routing_map_provider import SmartRoutingMapProvider
@@ -79,7 +79,7 @@ def apply_server_response_continuation(self, continuation: str, has_modified_res
7979
def from_json(
8080
container_link: str,
8181
container_rid: str,
82-
change_feed_state_context: Dict[str, Any]):
82+
change_feed_state_context: Dict[str, Any]) -> 'ChangeFeedState':
8383

8484
if (change_feed_state_context.get("partitionKeyRangeId")
8585
or change_feed_state_context.get("continuationPkRangeId")):
@@ -184,7 +184,7 @@ def __init__(
184184
self,
185185
container_link: str,
186186
container_rid: str,
187-
feed_range: FeedRange,
187+
feed_range: FeedRangeInternal,
188188
change_feed_start_from: ChangeFeedStartFromInternal,
189189
continuation: Optional[FeedRangeCompositeContinuation]
190190
) -> None:
@@ -380,22 +380,20 @@ def from_initial_state(
380380
collection_rid: str,
381381
change_feed_state_context: Dict[str, Any]) -> 'ChangeFeedStateV2':
382382

383-
feed_range: Optional[FeedRange] = None
383+
feed_range: Optional[FeedRangeInternal] = None
384384
if change_feed_state_context.get("feedRange"):
385-
feed_range_str = base64.b64decode(change_feed_state_context["feedRange"]).decode('utf-8')
386-
feed_range_json = json.loads(feed_range_str)
387-
feed_range = FeedRangeEpk(Range.ParseFromDict(feed_range_json))
385+
feed_range = change_feed_state_context.get("feedRange")
388386
elif change_feed_state_context.get("partitionKey"):
389387
if change_feed_state_context.get("partitionKeyFeedRange"):
390388
feed_range =\
391-
FeedRangePartitionKey(
389+
FeedRangeInternalPartitionKey(
392390
change_feed_state_context["partitionKey"],
393391
change_feed_state_context["partitionKeyFeedRange"])
394392
else:
395393
raise ValueError("partitionKey is in the changeFeedStateContext, but missing partitionKeyFeedRange")
396394
else:
397395
# default to full range
398-
feed_range = FeedRangeEpk(
396+
feed_range = FeedRangeInternalEpk(
399397
Range(
400398
"",
401399
"FF",

sdk/cosmos/azure-cosmos/azure/cosmos/_change_feed/feed_range_composite_continuation_token.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from typing import Any, Deque, Dict, Optional
2727

2828
from azure.cosmos._change_feed.composite_continuation_token import CompositeContinuationToken
29-
from azure.cosmos._change_feed.feed_range import FeedRange, FeedRangeEpk, FeedRangePartitionKey
29+
from azure.cosmos._change_feed.feed_range_internal import FeedRangeInternal, FeedRangeInternalEpk, FeedRangeInternalPartitionKey
3030
from azure.cosmos._routing.routing_map_provider import SmartRoutingMapProvider
3131
from azure.cosmos._routing.aio.routing_map_provider import SmartRoutingMapProvider as AsyncSmartRoutingMapProvider
3232
from azure.cosmos._routing.routing_range import Range
@@ -39,7 +39,7 @@ class FeedRangeCompositeContinuation:
3939
def __init__(
4040
self,
4141
container_rid: str,
42-
feed_range: FeedRange,
42+
feed_range: FeedRangeInternal,
4343
continuation: Deque[CompositeContinuationToken]) -> None:
4444
if container_rid is None:
4545
raise ValueError("container_rid is missing")
@@ -87,11 +87,11 @@ def from_json(cls, data) -> 'FeedRangeCompositeContinuation':
8787
for child_range_continuation_token in continuation_data]
8888

8989
# parsing feed range
90-
feed_range: Optional[FeedRange] = None
91-
if data.get(FeedRangeEpk.type_property_name):
92-
feed_range = FeedRangeEpk.from_json(data)
93-
elif data.get(FeedRangePartitionKey.type_property_name):
94-
feed_range = FeedRangePartitionKey.from_json(data, continuation[0].feed_range)
90+
feed_range: Optional[FeedRangeInternal] = None
91+
if data.get(FeedRangeInternalEpk.type_property_name):
92+
feed_range = FeedRangeInternalEpk.from_json(data)
93+
elif data.get(FeedRangeInternalPartitionKey.type_property_name):
94+
feed_range = FeedRangeInternalPartitionKey.from_json(data, continuation[0].feed_range)
9595
else:
9696
raise ValueError("Invalid feed range composite continuation token [Missing feed range scope]")
9797

@@ -171,5 +171,5 @@ def apply_not_modified_response(self) -> None:
171171
self._initial_no_result_range = self._current_token.feed_range
172172

173173
@property
174-
def feed_range(self) -> FeedRange:
174+
def feed_range(self) -> FeedRangeInternal:
175175
return self._feed_range

sdk/cosmos/azure-cosmos/azure/cosmos/_change_feed/feed_range.py renamed to sdk/cosmos/azure-cosmos/azure/cosmos/_change_feed/feed_range_internal.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from azure.cosmos.partition_key import _Undefined, _Empty
3030

3131

32-
class FeedRange(ABC):
32+
class FeedRangeInternal(ABC):
3333

3434
@abstractmethod
3535
def get_normalized_range(self) -> Range:
@@ -39,7 +39,7 @@ def get_normalized_range(self) -> Range:
3939
def to_dict(self) -> Dict[str, Any]:
4040
pass
4141

42-
class FeedRangePartitionKey(FeedRange):
42+
class FeedRangeInternalPartitionKey(FeedRangeInternal):
4343
type_property_name = "PK"
4444

4545
def __init__(
@@ -69,7 +69,7 @@ def to_dict(self) -> Dict[str, Any]:
6969
return { self.type_property_name: self._pk_value }
7070

7171
@classmethod
72-
def from_json(cls, data: Dict[str, Any], feed_range: Range) -> 'FeedRangePartitionKey':
72+
def from_json(cls, data: Dict[str, Any], feed_range: Range) -> 'FeedRangeInternalPartitionKey':
7373
if data.get(cls.type_property_name):
7474
pk_value = data.get(cls.type_property_name)
7575
if not pk_value:
@@ -80,11 +80,11 @@ def from_json(cls, data: Dict[str, Any], feed_range: Range) -> 'FeedRangePartiti
8080
return cls(list(pk_value), feed_range)
8181
return cls(data[cls.type_property_name], feed_range)
8282

83-
raise ValueError(f"Can not parse FeedRangePartitionKey from the json,"
83+
raise ValueError(f"Can not parse FeedRangeInternalPartitionKey from the json,"
8484
f" there is no property {cls.type_property_name}")
8585

8686

87-
class FeedRangeEpk(FeedRange):
87+
class FeedRangeInternalEpk(FeedRangeInternal):
8888
type_property_name = "Range"
8989

9090
def __init__(self, feed_range: Range) -> None:
@@ -102,8 +102,9 @@ def to_dict(self) -> Dict[str, Any]:
102102
}
103103

104104
@classmethod
105-
def from_json(cls, data: Dict[str, Any]) -> 'FeedRangeEpk':
105+
def from_json(cls, data: Dict[str, Any]) -> 'FeedRangeInternalEpk':
106106
if data.get(cls.type_property_name):
107107
feed_range = Range.ParseFromDict(data.get(cls.type_property_name))
108108
return cls(feed_range)
109-
raise ValueError(f"Can not parse FeedRangeEPK from the json, there is no property {cls.type_property_name}")
109+
raise ValueError(f"Can not parse FeedRangeInternalEPK from the json,"
110+
f" there is no property {cls.type_property_name}")
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
2+
# The MIT License (MIT)
3+
# Copyright (c) 2014 Microsoft Corporation
4+
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
import base64
24+
import json
25+
from abc import ABC, abstractmethod
26+
from typing import Any, Dict
27+
28+
from azure.cosmos._change_feed.feed_range_internal import FeedRangeInternal, FeedRangeInternalEpk
29+
from azure.cosmos._routing.routing_range import Range
30+
31+
32+
class FeedRange(ABC):
33+
"""Represents a single feed range in an Azure Cosmos DB SQL API container. """
34+
35+
def to_string(self) -> str:
36+
"""Get a json representation of the feed range.
37+
The returned json string can be used to create a new feed range from it.
38+
:return: A json representation of the feed range.
39+
:rtype: str
40+
"""
41+
return self._to_base64_encoded_string()
42+
43+
@staticmethod
44+
def from_string(json_str: str) -> 'FeedRange':
45+
"""
46+
Create a feed range from previously obtained string representation.
47+
48+
:param json_str: A string representation of a feed range.
49+
:return: A feed range.
50+
:rtype: ~azure.cosmos.FeedRange
51+
"""
52+
feed_range_json_str = base64.b64decode(json_str).decode('utf-8')
53+
feed_range_json = json.loads(feed_range_json_str)
54+
if feed_range_json.get(FeedRangeEpk.type_property_name):
55+
return FeedRangeEpk._from_json(feed_range_json)
56+
else:
57+
raise ValueError("Invalid feed range base64 encoded string [Wrong feed range type]")
58+
59+
@abstractmethod
60+
def _to_dict(self) -> Dict[str, Any]:
61+
pass
62+
63+
@abstractmethod
64+
def _to_feed_range_internal(self) -> 'FeedRangeInternal':
65+
pass
66+
67+
def _to_base64_encoded_string(self) -> str:
68+
data_json = json.dumps(self._to_dict())
69+
json_bytes = data_json.encode('utf-8')
70+
# Encode the bytes to a Base64 string
71+
base64_bytes = base64.b64encode(json_bytes)
72+
# Convert the Base64 bytes to a string
73+
return base64_bytes.decode('utf-8')
74+
75+
class FeedRangeEpk (FeedRange):
76+
type_property_name = "Range"
77+
78+
def __init__(self, feed_range: Range) -> None:
79+
if feed_range is None:
80+
raise ValueError("feed_range cannot be None")
81+
82+
self._feed_range = feed_range
83+
84+
def _to_dict(self) -> Dict[str, Any]:
85+
return {
86+
self.type_property_name: self._feed_range.to_dict()
87+
}
88+
89+
def _to_feed_range_internal(self) -> 'FeedRangeInternal':
90+
return FeedRangeInternalEpk(self._feed_range)
91+
92+
@classmethod
93+
def _from_json(cls, data: Dict[str, Any]) -> 'FeedRange':
94+
if data.get(cls.type_property_name):
95+
feed_range = Range.ParseFromDict(data.get(cls.type_property_name))
96+
return cls(feed_range)
97+
raise ValueError(f"Can not parse FeedRangeEPK from the json, there is no property {cls.type_property_name}")

sdk/cosmos/azure-cosmos/azure/cosmos/_routing/routing_range.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@
2525
import base64
2626
import binascii
2727
import json
28-
from typing import Dict, Any
2928

3029

31-
def partition_key_range_to_range_string(partition_key_range: Dict[str, Any]) -> str:
32-
return Range.PartitionKeyRangeToRange(partition_key_range).to_base64_encoded_string()
33-
3430
class PartitionKeyRange(object):
3531
"""Partition Key Range Constants"""
3632

sdk/cosmos/azure-cosmos/azure/cosmos/aio/_container.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
GenerateGuidId,
4242
_set_properties_cache
4343
)
44-
from .._routing.routing_range import Range, partition_key_range_to_range_string
44+
from .._feed_range import FeedRange, FeedRangeEpk
45+
from .._routing.routing_range import Range
4546
from ..offer import ThroughputProperties
4647
from ..partition_key import (
4748
NonePartitionKeyValue,
@@ -526,15 +527,16 @@ def query_items_change_feed(
526527
def query_items_change_feed(
527528
self,
528529
*,
529-
feed_range: str,
530+
feed_range: FeedRange,
530531
max_item_count: Optional[int] = None,
531532
start_time: Optional[Union[datetime, Literal["Now", "Beginning"]]] = None,
532533
priority: Optional[Literal["High", "Low"]] = None,
533534
**kwargs: Any
534535
) -> AsyncItemPaged[Dict[str, Any]]:
535536
"""Get a sorted list of items that were changed, in the order in which they were modified.
536537
537-
:keyword str feed_range: The feed range that is used to define the scope.
538+
:keyword feed_range: The feed range that is used to define the scope.
539+
:type feed_range: ~azure.cosmos.FeedRange
538540
:keyword int max_item_count: Max number of items to be returned in the enumeration operation.
539541
:keyword start_time: The start time to start processing chang feed items.
540542
Beginning: Processing the change feed items from the beginning of the change feed.
@@ -659,7 +661,8 @@ def query_items_change_feed( # pylint: disable=unused-argument
659661
self._get_epk_range_for_partition_key(kwargs.pop('partition_key'))
660662

661663
if kwargs.get("feed_range") is not None:
662-
change_feed_state_context["feedRange"] = kwargs.pop('feed_range')
664+
feed_range: FeedRange = kwargs.pop('feed_range')
665+
change_feed_state_context["feedRange"] = feed_range._to_feed_range_internal()
663666

664667
feed_options["containerProperties"] = self._get_properties()
665668
feed_options["changeFeedStateContext"] = change_feed_state_context
@@ -1243,7 +1246,7 @@ async def read_feed_ranges(
12431246
*,
12441247
force_refresh: Optional[bool] = False,
12451248
**kwargs: Any
1246-
) -> List[str]:
1249+
) -> List[FeedRange]:
12471250
""" Obtains a list of feed ranges that can be used to parallelize feed operations.
12481251
12491252
:keyword bool force_refresh:
@@ -1262,4 +1265,4 @@ async def read_feed_ranges(
12621265
[Range("", "FF", True, False)],
12631266
**kwargs)
12641267

1265-
return [partition_key_range_to_range_string(partitionKeyRange) for partitionKeyRange in partition_key_ranges]
1268+
return [FeedRangeEpk(Range.PartitionKeyRangeToRange(partitionKeyRange)) for partitionKeyRange in partition_key_ranges]

sdk/cosmos/azure-cosmos/azure/cosmos/container.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
_set_properties_cache
4040
)
4141
from ._cosmos_client_connection import CosmosClientConnection
42-
from ._routing.routing_range import Range, partition_key_range_to_range_string
42+
from ._feed_range import FeedRange, FeedRangeEpk
43+
from ._routing.routing_range import Range
4344
from .offer import Offer, ThroughputProperties
4445
from .partition_key import (
4546
NonePartitionKeyValue,
@@ -352,7 +353,7 @@ def query_items_change_feed(
352353
def query_items_change_feed(
353354
self,
354355
*,
355-
feed_range: str,
356+
feed_range: FeedRange,
356357
max_item_count: Optional[int] = None,
357358
start_time: Optional[Union[datetime, Literal["Now", "Beginning"]]] = None,
358359
priority: Optional[Literal["High", "Low"]] = None,
@@ -361,7 +362,8 @@ def query_items_change_feed(
361362

362363
"""Get a sorted list of items that were changed, in the order in which they were modified.
363364
364-
:keyword str feed_range: The feed range that is used to define the scope.
365+
:keyword feed_range: The feed range that is used to define the scope.
366+
:type feed_range: ~azure.cosmos.FeedRange
365367
:keyword int max_item_count: Max number of items to be returned in the enumeration operation.
366368
:keyword start_time: The start time to start processing chang feed items.
367369
Beginning: Processing the change feed items from the beginning of the change feed.
@@ -500,7 +502,8 @@ def query_items_change_feed(
500502
self._get_epk_range_for_partition_key(kwargs.pop('partition_key'))
501503

502504
if kwargs.get("feed_range") is not None:
503-
change_feed_state_context["feedRange"] = kwargs.pop('feed_range')
505+
feed_range: FeedRange = kwargs.pop('feed_range')
506+
change_feed_state_context["feedRange"] = feed_range._to_feed_range_internal()
504507

505508
container_properties = self._get_properties()
506509
feed_options["changeFeedStateContext"] = change_feed_state_context
@@ -1310,7 +1313,7 @@ def read_feed_ranges(
13101313
self,
13111314
*,
13121315
force_refresh: Optional[bool] = False,
1313-
**kwargs: Any) -> List[str]:
1316+
**kwargs: Any) -> List[FeedRange]:
13141317

13151318
""" Obtains a list of feed ranges that can be used to parallelize feed operations.
13161319
@@ -1329,4 +1332,4 @@ def read_feed_ranges(
13291332
[Range("", "FF", True, False)], # default to full range
13301333
**kwargs)
13311334

1332-
return [partition_key_range_to_range_string(partitionKeyRange) for partitionKeyRange in partition_key_ranges]
1335+
return [FeedRangeEpk(Range.PartitionKeyRangeToRange(partitionKeyRange)) for partitionKeyRange in partition_key_ranges]

0 commit comments

Comments
 (0)