29
29
from abc import ABC , abstractmethod
30
30
from typing import Optional , Union , List , Any
31
31
32
- from azure .cosmos import http_constants
32
+ from azure .cosmos import http_constants , PartitionKey
33
33
from azure .cosmos ._change_feed .aio .change_feed_start_from import ChangeFeedStartFromETagAndFeedRange , \
34
34
ChangeFeedStartFromInternal
35
35
from azure .cosmos ._change_feed .aio .composite_continuation_token import CompositeContinuationToken
36
36
from azure .cosmos ._change_feed .aio .feed_range_composite_continuation_token import FeedRangeCompositeContinuation
37
+ from azure .cosmos ._change_feed .feed_range import FeedRangeEpk , FeedRangePartitionKey , FeedRange
37
38
from azure .cosmos ._routing .aio .routing_map_provider import SmartRoutingMapProvider
38
39
from azure .cosmos ._routing .routing_range import Range
39
40
from azure .cosmos ._utils import is_key_exists_and_not_none
@@ -49,15 +50,22 @@ def populate_feed_options(self, feed_options: dict[str, any]) -> None:
49
50
pass
50
51
51
52
@abstractmethod
52
- async def populate_request_headers (self , routing_provider : SmartRoutingMapProvider , request_headers : dict [str , any ]) -> None :
53
+ async def populate_request_headers (
54
+ self ,
55
+ routing_provider : SmartRoutingMapProvider ,
56
+ request_headers : dict [str , any ]) -> None :
53
57
pass
54
58
55
59
@abstractmethod
56
60
def apply_server_response_continuation (self , continuation : str ) -> None :
57
61
pass
58
62
59
63
@staticmethod
60
- def from_json (container_link : str , container_rid : str , data : dict [str , Any ]):
64
+ def from_json (
65
+ container_link : str ,
66
+ container_rid : str ,
67
+ partition_key_definition : PartitionKey ,
68
+ data : dict [str , Any ]):
61
69
if is_key_exists_and_not_none (data , "partitionKeyRangeId" ) or is_key_exists_and_not_none (data , "continuationPkRangeId" ):
62
70
return ChangeFeedStateV1 .from_json (container_link , container_rid , data )
63
71
else :
@@ -69,11 +77,11 @@ def from_json(container_link: str, container_rid: str, data: dict[str, Any]):
69
77
if version is None :
70
78
raise ValueError ("Invalid base64 encoded continuation string [Missing version]" )
71
79
elif version == "V2" :
72
- return ChangeFeedStateV2 .from_continuation (container_link , container_rid , continuation_json )
80
+ return ChangeFeedStateV2 .from_continuation (container_link , container_rid , partition_key_definition , continuation_json )
73
81
else :
74
82
raise ValueError ("Invalid base64 encoded continuation string [Invalid version]" )
75
83
# when there is no continuation token, by default construct ChangeFeedStateV2
76
- return ChangeFeedStateV2 .from_initial_state (container_link , container_rid , data )
84
+ return ChangeFeedStateV2 .from_initial_state (container_link , container_rid , partition_key_definition , data )
77
85
78
86
class ChangeFeedStateV1 (ChangeFeedState ):
79
87
"""Change feed state v1 implementation. This is used when partition key range id is used or the continuation is just simple _etag
@@ -110,7 +118,10 @@ def from_json(cls, container_link: str, container_rid: str, data: dict[str, Any]
110
118
data .get ("continuationPkRangeId" )
111
119
)
112
120
113
- async def populate_request_headers (self , routing_provider : SmartRoutingMapProvider , headers : dict [str , Any ]) -> None :
121
+ async def populate_request_headers (
122
+ self ,
123
+ routing_provider : SmartRoutingMapProvider ,
124
+ headers : dict [str , Any ]) -> None :
114
125
headers [http_constants .HttpHeaders .AIM ] = http_constants .HttpHeaders .IncrementalFeedHeaderValue
115
126
116
127
# When a merge happens, the child partition will contain documents ordered by LSN but the _ts/creation time
@@ -140,7 +151,8 @@ def __init__(
140
151
self ,
141
152
container_link : str ,
142
153
container_rid : str ,
143
- feed_range : Range ,
154
+ partition_key_definition : PartitionKey ,
155
+ feed_range : FeedRange ,
144
156
change_feed_start_from : ChangeFeedStartFromInternal ,
145
157
continuation : Optional [FeedRangeCompositeContinuation ] = None ):
146
158
@@ -151,7 +163,9 @@ def __init__(
151
163
self ._continuation = continuation
152
164
if self ._continuation is None :
153
165
composite_continuation_token_queue = collections .deque ()
154
- composite_continuation_token_queue .append (CompositeContinuationToken (self ._feed_range , None ))
166
+ composite_continuation_token_queue .append (CompositeContinuationToken (
167
+ self ._feed_range .get_normalized_range (partition_key_definition ),
168
+ None ))
155
169
self ._continuation = \
156
170
FeedRangeCompositeContinuation (self ._container_rid , self ._feed_range , composite_continuation_token_queue )
157
171
@@ -168,7 +182,10 @@ def to_dict(self) -> dict[str, Any]:
168
182
self .continuation_property_name : self ._continuation .to_dict ()
169
183
}
170
184
171
- async def populate_request_headers (self , routing_provider : SmartRoutingMapProvider , headers : dict [str , any ]) -> None :
185
+ async def populate_request_headers (
186
+ self ,
187
+ routing_provider : SmartRoutingMapProvider ,
188
+ headers : dict [str , any ]) -> None :
172
189
headers [http_constants .HttpHeaders .AIM ] = http_constants .HttpHeaders .IncrementalFeedHeaderValue
173
190
174
191
# When a merge happens, the child partition will contain documents ordered by LSN but the _ts/creation time
@@ -224,6 +241,7 @@ def from_continuation(
224
241
cls ,
225
242
container_link : str ,
226
243
container_rid : str ,
244
+ partition_key_definition : PartitionKey ,
227
245
continuation_json : dict [str , Any ]) -> 'ChangeFeedStateV2' :
228
246
229
247
container_rid_from_continuation = continuation_json .get (ChangeFeedStateV2 .container_rid_property_name )
@@ -244,6 +262,7 @@ def from_continuation(
244
262
return ChangeFeedStateV2 (
245
263
container_link = container_link ,
246
264
container_rid = container_rid ,
265
+ partition_key_definition = partition_key_definition ,
247
266
feed_range = continuation .feed_range ,
248
267
change_feed_start_from = change_feed_start_from ,
249
268
continuation = continuation )
@@ -253,26 +272,29 @@ def from_initial_state(
253
272
cls ,
254
273
container_link : str ,
255
274
collection_rid : str ,
275
+ partition_key_definition : PartitionKey ,
256
276
data : dict [str , Any ]) -> 'ChangeFeedStateV2' :
257
277
258
278
if is_key_exists_and_not_none (data , "feedRange" ):
259
279
feed_range_str = base64 .b64decode (data ["feedRange" ]).decode ('utf-8' )
260
280
feed_range_json = json .loads (feed_range_str )
261
- feed_range = Range .ParseFromDict (feed_range_json )
262
- elif is_key_exists_and_not_none (data , "partitionKeyFeedRange " ):
263
- feed_range = data ["partitionKeyFeedRange" ]
281
+ feed_range = FeedRangeEpk ( Range .ParseFromDict (feed_range_json ) )
282
+ elif is_key_exists_and_not_none (data , "partitionKey " ):
283
+ feed_range = FeedRangePartitionKey ( data ["partitionKey" ])
264
284
else :
265
285
# default to full range
266
- feed_range = Range (
267
- "" ,
268
- "FF" ,
269
- True ,
270
- False )
286
+ feed_range = FeedRangeEpk (
287
+ Range (
288
+ "" ,
289
+ "FF" ,
290
+ True ,
291
+ False ))
271
292
272
293
change_feed_start_from = ChangeFeedStartFromInternal .from_start_time (data .get ("startTime" ))
273
294
return cls (
274
295
container_link = container_link ,
275
296
container_rid = collection_rid ,
297
+ partition_key_definition = partition_key_definition ,
276
298
feed_range = feed_range ,
277
299
change_feed_start_from = change_feed_start_from ,
278
300
continuation = None )
0 commit comments