Skip to content

Commit fd35273

Browse files
committed
- Fixing time series range deletion
- Adding events to handle various actions in the client
1 parent 40b882e commit fd35273

File tree

8 files changed

+78
-7
lines changed

8 files changed

+78
-7
lines changed

pyravendb/raven_operations/timeseries_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def to_json(self):
9393
if self.appends:
9494
self.appends = next(Utils.sort_iterable(self.appends, key=lambda ao: ao.timestamp.timestamp()))
9595
return {"Name": self.name, "Appends": [a.to_json() for a in self.appends] if self.appends else self.appends,
96-
"Removals": [r.to_json() for r in self.removals] if self.removals else self.removals}
96+
"Deletes": [r.to_json() for r in self.removals] if self.removals else self.removals}
9797

9898
class AppendOperation:
9999
def __init__(self, timestamp: datetime, values: List[float] or float, tag: Optional[str] = None):

pyravendb/store/document_session.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ def __init__(self, database, document_store, requests_executor, session_id, **kw
4848
self.no_tracking = kwargs.get("no_tracking", False)
4949
self.no_caching = kwargs.get("no_caching", False)
5050
self.advanced = Advanced(self)
51+
self._events = None
52+
53+
@property
54+
def readonly_events(self):
55+
if self._events is None:
56+
return self._document_store.events.clone()
57+
return self._events
58+
59+
@property
60+
def events(self):
61+
if self._events is None:
62+
self._events = self._document_store.events.clone()
63+
return self._events
5164

5265
def __enter__(self):
5366
return self
@@ -141,6 +154,7 @@ def _convert_and_save_entity(self, key, document, object_type, nested_object_typ
141154
if key not in self._documents_by_id:
142155
entity, metadata, original_metadata, original_document = Utils.convert_to_entity(document, object_type,
143156
self.conventions,
157+
self.readonly_events,
144158
nested_object_types)
145159
self.save_entity(key, entity, original_metadata, metadata, original_document)
146160

@@ -235,6 +249,7 @@ def delete_by_entity(self, entity):
235249
if "Raven-Read-Only" in self._documents_by_entity[entity]["original_metadata"]:
236250
raise exceptions.InvalidOperationException(
237251
"{0} is marked as read only and cannot be deleted".format(entity))
252+
238253
self._included_documents_by_id.pop(self._documents_by_entity[entity]["key"], None)
239254
self._known_missing_ids.add(self._documents_by_entity[entity]["key"])
240255
self._deleted_entities.add(entity)
@@ -375,6 +390,8 @@ def _update_batch_result(self, batch_result, data):
375390
document_info["metadata"] = item
376391
document_info["key"] = item["@id"]
377392
document_info["original_value"] = entity.__dict__.copy()
393+
self.readonly_events.after_save_change(self, item["@id"], entity)
394+
378395
elif item["Type"] == "Counters":
379396
cache = self._counters_by_document_id.get(item["Id"], None)
380397
if not cache:
@@ -415,13 +432,15 @@ def _prepare_for_puts_commands(self, data):
415432
change_vector = None
416433
if self.advanced.use_optimistic_concurrency and self._documents_by_entity[entity][
417434
"force_concurrency_check"] != "disabled" or self._documents_by_entity[entity][
418-
"force_concurrency_check"] == "forced":
435+
"force_concurrency_check"] == "forced":
419436
change_vector = self._documents_by_entity[entity]["change_vector"]
437+
self.readonly_events.before_store(self, key, entity)
420438
data.entities.append(entity)
421439
if key:
422440
self._documents_by_id.pop(key)
423441
document = entity.__dict__.copy()
424442
document.pop('Id', None)
443+
425444
data.commands.append(commands_data.PutCommandData(key, change_vector, document, metadata))
426445

427446
def _has_change(self, entity):
@@ -477,6 +496,7 @@ def stream(self, query):
477496
results = ijson.backend.common.items(parser, "Results")
478497
for result in next(results, None):
479498
document, metadata, _, _ = Utils.convert_to_entity(result, query.object_type, self.session.conventions,
499+
self.session.readonly_events,
480500
query.nested_object_types)
481501
yield {"document": document, "metadata": metadata, "id": metadata.get("@id", None),
482502
"change-vector": metadata.get("@change-vector", None)}

pyravendb/store/document_store.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ def __init__(self, urls=None, database=None, certificate=None):
3535
self._maintenance_operation_executor = None
3636
self.subscriptions = DocumentSubscriptions(self)
3737
self._database_changes = {}
38+
self._events = DocumentEvents()
39+
40+
@property
41+
def events(self):
42+
return self._events
3843

3944
@property
4045
def conventions(self):
@@ -133,13 +138,31 @@ def open_session(self, database=None, request_executor=None):
133138
self._assert_initialize()
134139
session_id = uuid.uuid4()
135140
requests_executor = request_executor if request_executor is not None else self.get_request_executor(database)
136-
return DocumentSession(database, self, requests_executor, session_id)
141+
session = DocumentSession(database, self, requests_executor, session_id)
142+
self.events.session_created(session)
143+
return session
137144

138145
def generate_id(self, db_name, entity):
139146
if self.generator:
140147
return self.generator.generate_document_key(db_name, entity)
141148

142149

150+
class DocumentEvents:
151+
def __init__(self):
152+
self.before_store = lambda session, doc_id, entity: None
153+
self.before_query = lambda session, query: None
154+
self.after_save_change = lambda session, doc_id, entity: None
155+
self.before_delete = lambda session, doc_id, entity: None
156+
self.session_created = lambda session: None
157+
self.before_conversion_to_entity = lambda document, metadata, type_from_metadata: None
158+
self.after_conversion_to_entity = lambda entity, document, metadata: None
159+
160+
def clone(self):
161+
other = DocumentEvents()
162+
other.__dict__ = self.__dict__.copy()
163+
return other
164+
165+
143166
# ------------------------------Operation executors ---------------------------->
144167
class MaintenanceOperationExecutor:
145168
def __init__(self, document_store, database_name=None):

pyravendb/store/session_query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,8 @@ def _execute_query(self):
802802
self.session.save_includes(response_includes)
803803
for result in response_results:
804804
entity, metadata, original_metadata, original_document = Utils.convert_to_entity(result, self.object_type,
805-
conventions,
805+
self.session.conventions,
806+
self.session.readonly_events,
806807
self.nested_object_types)
807808
if self.object_type != dict and not self.fields_to_fetch:
808809
self.session.save_entity(key=original_metadata.get("@id", None), entity=entity,

pyravendb/subscriptions/subscription.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ def initialize(self):
344344

345345
entity, metadata, _, _ = Utils.convert_to_entity(item['Data'], self._object_type,
346346
self._store.conventions,
347+
self._store.events,
347348
nested_object_types=self._nested_object_type)
348349
if not metadata:
349350
raise InvalidOperationException("Document must have a @metadata field")

pyravendb/tests/session_tests/test_conversion.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ def __init__(self, td, dt):
1010
self.dt = dt
1111

1212

13+
class Item(object):
14+
def __init__(self, val):
15+
self.val = val
16+
17+
1318
class TestConversion(TestBase):
1419
def setUp(self):
1520
super(TestConversion, self).setUp()
@@ -25,6 +30,20 @@ def tearDown(self):
2530
super(TestConversion, self).tearDown()
2631
self.delete_all_topology_files()
2732

33+
def test_before_store(self):
34+
def update_item(session, doc_id, entity):
35+
entity.val = 2
36+
37+
with self.store.open_session() as session:
38+
session.events.before_store = update_item
39+
session.store(Item(1), "item/1")
40+
session.save_changes()
41+
42+
with self.store.open_session() as session:
43+
time = session.load("item/1", object_type=Item)
44+
self.assertEqual(2, time.val)
45+
46+
2847
def test_load_timedelta_and_datetime(self):
2948
with self.store.open_session() as session:
3049
times = session.load("times/3", object_type=Time, nested_object_types={"td": timedelta, "dt": datetime})

pyravendb/tests/subscription_test/test_subscription.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def test_create_and_run_subscription_with_object(self):
6161
self.assertEqual(len(self.results), 0)
6262
with self.store.subscriptions.get_subscription_worker(connection_options, object_type=User) as subscription:
6363
subscription.run(self.process_documents)
64-
self.event.wait(timeout=5)
64+
self.event.wait(timeout=60)
6565

6666
self.assertEqual(len(self.results), 2)
6767
for item in self.results:

pyravendb/tools/utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,27 @@ def initialize_object(obj, object_type, convert_to_snake_case=None):
9898
return o
9999

100100
@staticmethod
101-
def convert_to_entity(document, object_type, conventions, nested_object_types=None):
101+
def convert_to_entity(document, object_type, conventions, events, nested_object_types=None):
102+
102103
metadata = document.pop("@metadata")
103104
original_document = deepcopy(document)
104105
original_metadata = deepcopy(metadata)
105106
type_from_metadata = conventions.try_get_type_from_metadata(metadata)
106107
mapper = conventions.mappers.get(object_type, None)
107108

109+
events.before_conversion_to_entity(document, metadata, type_from_metadata)
110+
108111
if object_type == dict:
112+
events.after_conversion_to_entity(document, document, metadata)
109113
return document, metadata, original_metadata, original_document
110114

111115
if type_from_metadata is None:
112116
if object_type is not None:
113117
metadata["Raven-Python-Type"] = "{0}.{1}".format(object_type.__module__, object_type.__name__)
114118
else: # no type defined on document or during load, return a dict
115-
return _DynamicStructure(**document), metadata, original_metadata, original_document
119+
dyn = _DynamicStructure(**document)
120+
events["after_conversion_to_entity"](dyn, document, metadata)
121+
return dyn, metadata, original_metadata, original_document
116122
else:
117123
object_from_metadata = Utils.import_class(type_from_metadata)
118124
if object_from_metadata is not None:
@@ -156,6 +162,7 @@ def convert_to_entity(document, object_type, conventions, nested_object_types=No
156162

157163
if 'Id' in entity.__dict__:
158164
entity.Id = metadata.get('@id', None)
165+
events.after_conversion_to_entity(entity, document, metadata)
159166
return entity, metadata, original_metadata, original_document
160167

161168
@staticmethod

0 commit comments

Comments
 (0)