44import json
55from abc import abstractmethod
66from enum import Enum
7- from typing import Callable , Union , Optional , TYPE_CHECKING , List , Set , Dict
7+ from typing import Callable , IO , Union , Optional , TYPE_CHECKING , List , Set , Dict
88
99import requests
1010
@@ -49,6 +49,7 @@ class CommandType(Enum):
4949 TIME_SERIES_BULK_INSERT = "TIME_SERIES_BULK_INSERT"
5050 TIME_SERIES_COPY = "TIME_SERIES_COPY"
5151 BATCH_PATCH = "BatchPATCH"
52+ BATCH_TRACK_CHANGES = "BatchTrackChanges"
5253 CLIENT_ANY_COMMAND = "CLIENT_ANY_COMMAND"
5354 CLIENT_MODIFY_DOCUMENT_COMMAND = "CLIENT_MODIFY_DOCUMENT_COMMAND"
5455
@@ -81,6 +82,8 @@ def from_csharp_value_str(cls, value: str) -> CommandType:
8182 return cls .COUNTERS
8283 elif value == "BatchPATCH" :
8384 return cls .BATCH_PATCH
85+ elif value == "BatchTrackChanges" :
86+ return cls .BATCH_TRACK_CHANGES
8487 elif value == "ForceRevisionCreation" :
8588 return cls .FORCE_REVISION_CREATION
8689 elif value == "TimeSeries" :
@@ -119,7 +122,7 @@ def __init__(
119122 self .__attachment_streams = []
120123 stream = command .stream
121124 if stream is None :
122- continue # remote-only attachment — no stream to track
125+ continue # remote-only attachment has no local stream
123126 if stream in self .__attachment_streams :
124127 raise RuntimeError (
125128 "It is forbidden to re-use the same stream for more than one attachment. "
@@ -270,6 +273,27 @@ def serialize(self, conventions: DocumentConventions) -> dict:
270273 pass
271274
272275
276+ class BatchTrackChangesCommandData (CommandData ):
277+ # Emitted in OptimisticConcurrencyMode.WRITES_AND_READS: carries the change
278+ # vector of every tracked entity not already covered by a PUT/DELETE, so
279+ # the server can verify none of them changed underneath us.
280+ def __init__ (self , tracked_entities : Dict [str , str ], ids_to_skip : Set [str ]):
281+ super ().__init__ (command_type = CommandType .BATCH_TRACK_CHANGES )
282+ self .tracked_entities = tracked_entities
283+ self ._ids_to_skip = ids_to_skip
284+
285+ def serialize (self , conventions : DocumentConventions ) -> dict :
286+ tracked = {
287+ entity_id : change_vector
288+ for entity_id , change_vector in self .tracked_entities .items ()
289+ if entity_id not in self ._ids_to_skip
290+ }
291+ return {
292+ "Type" : str (CommandType .BATCH_TRACK_CHANGES ),
293+ "TrackedEntities" : tracked ,
294+ }
295+
296+
273297class DeleteCommandData (CommandData ):
274298 def __init__ (self , key : str , change_vector : str , original_change_vector : str = None ):
275299 super (DeleteCommandData , self ).__init__ (key = key , command_type = CommandType .DELETE , change_vector = change_vector )
@@ -534,7 +558,7 @@ def __init__(
534558 self ,
535559 document_id : str ,
536560 name : str ,
537- stream : bytes ,
561+ stream : Union [ bytes , IO [ bytes ]] ,
538562 content_type : str ,
539563 change_vector : str ,
540564 remote_parameters : Optional ["RemoteAttachmentParameters" ] = None ,
@@ -554,11 +578,11 @@ def __init__(
554578 self .__size_in_bytes = size_in_bytes
555579
556580 @property
557- def stream (self ):
581+ def stream (self ) -> Union [ bytes , IO [ bytes ]] :
558582 return self .__stream
559583
560584 @property
561- def content_type (self ):
585+ def content_type (self ) -> str :
562586 return self .__content_type
563587
564588 @property
0 commit comments