vortexpy 4.0.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vortex/DataWrapTuple.py +27 -0
- vortex/DeferUtil.py +224 -0
- vortex/Jsonable.py +304 -0
- vortex/Payload.py +115 -0
- vortex/PayloadEndpoint.py +216 -0
- vortex/PayloadEndpointTest.py +164 -0
- vortex/PayloadEnvelope.py +166 -0
- vortex/PayloadFilterKeys.py +15 -0
- vortex/PayloadIO.py +152 -0
- vortex/PayloadPriority.py +11 -0
- vortex/PayloadResponse.py +173 -0
- vortex/PayloadResponseTest.py +39 -0
- vortex/PayloadTest.py +136 -0
- vortex/PerformTestActionTuple.py +30 -0
- vortex/SerialiseUtil.py +273 -0
- vortex/TestTuple.py +61 -0
- vortex/Tuple.py +968 -0
- vortex/TupleAction.py +72 -0
- vortex/TupleActionVortex.py +5 -0
- vortex/TupleDataForTest.py +25 -0
- vortex/TuplePerfTest.py +71 -0
- vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
- vortex/TupleRestfulTest.py +140 -0
- vortex/TupleSelector.py +105 -0
- vortex/TupleTest.py +127 -0
- vortex/VortexABC.py +55 -0
- vortex/VortexClientHttp.py +308 -0
- vortex/VortexClientHttpTest.py +71 -0
- vortex/VortexClientTcp.py +355 -0
- vortex/VortexClientWebsocketFactory.py +383 -0
- vortex/VortexConnectionABC.py +88 -0
- vortex/VortexFactory.py +932 -0
- vortex/VortexPayloadProtocol.py +180 -0
- vortex/VortexServer.py +367 -0
- vortex/VortexServerConnection.py +127 -0
- vortex/VortexServerHttpResource.py +193 -0
- vortex/VortexServerTcp.py +104 -0
- vortex/VortexServerWebsocket.py +287 -0
- vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
- vortex/VortexTcpMemoryLeakRawTest.py +87 -0
- vortex/VortexTest.py +22 -0
- vortex/VortexUtil.py +314 -0
- vortex/VortexWritePushProducer.py +321 -0
- vortex/__init__.py +10 -0
- vortex/data_loader/TupleDataLiveLockManager.py +383 -0
- vortex/data_loader/TupleDataLoader.py +326 -0
- vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
- vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
- vortex/data_loader/TupleDataLoaderTuples.py +53 -0
- vortex/data_loader/__init__.py +0 -0
- vortex/event_bus/TupleChangeEventABC.py +15 -0
- vortex/event_bus/TupleChangeEventBus.py +192 -0
- vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
- vortex/event_bus/__init__.py +0 -0
- vortex/handler/TupleActionProcessor.py +270 -0
- vortex/handler/TupleActionProcessorProxy.py +218 -0
- vortex/handler/TupleActionProcessorTestHandler.py +27 -0
- vortex/handler/TupleDataActionClient.py +67 -0
- vortex/handler/TupleDataObservableCache.py +201 -0
- vortex/handler/TupleDataObservableHandler.py +371 -0
- vortex/handler/TupleDataObservableProxyHandler.py +340 -0
- vortex/handler/TupleDataObservableTestHandler.py +60 -0
- vortex/handler/TupleDataObserverClient.py +158 -0
- vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
- vortex/handler/__init__.py +0 -0
- vortex/restful/GzippedDataHttpClient.py +170 -0
- vortex/restful/GzippedDataHttpClientTest.py +103 -0
- vortex/restful/GzippedPayloadHttpClient.py +181 -0
- vortex/restful/RestfulHttpClient.py +107 -0
- vortex/restful/RestfulHttpClientTest.py +60 -0
- vortex/restful/RestfulResource.py +153 -0
- vortex/restful/RestfulResourceTest.py +100 -0
- vortex/restful/__init__.py +0 -0
- vortex/rpc/RPC.py +446 -0
- vortex/rpc/RPCTest.py +12 -0
- vortex/rpc/__init__.py +0 -0
- vortex/sqla_orm/OrmCrudHandler.py +431 -0
- vortex/sqla_orm/TupleProviderForDb.py +42 -0
- vortex/sqla_orm/VortexTupleJson.py +24 -0
- vortex/sqla_orm/__init__.py +0 -0
- vortex/storage/TupleStorageSqlite.py +359 -0
- vortex/storage/__init__.py +0 -0
- vortexpy-4.0.23.dist-info/LICENSE +21 -0
- vortexpy-4.0.23.dist-info/METADATA +43 -0
- vortexpy-4.0.23.dist-info/RECORD +87 -0
- vortexpy-4.0.23.dist-info/WHEEL +5 -0
- vortexpy-4.0.23.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""
|
|
2
|
+
* Created by Synerty Pty Ltd
|
|
3
|
+
*
|
|
4
|
+
* This software is open source, the MIT license applies.
|
|
5
|
+
*
|
|
6
|
+
* Website : http://www.synerty.com
|
|
7
|
+
* Support : support@synerty.com
|
|
8
|
+
"""
|
|
9
|
+
import logging
|
|
10
|
+
from copy import copy
|
|
11
|
+
from typing import Callable, List
|
|
12
|
+
from typing import Union
|
|
13
|
+
|
|
14
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
15
|
+
from sqlalchemy.orm.session import Session
|
|
16
|
+
from twisted.internet.defer import inlineCallbacks
|
|
17
|
+
from twisted.internet.threads import deferToThread
|
|
18
|
+
|
|
19
|
+
from vortex.DeferUtil import deferToThreadWrapWithLogger
|
|
20
|
+
from vortex.Payload import Payload
|
|
21
|
+
from vortex.PayloadEndpoint import PayloadEndpoint
|
|
22
|
+
from vortex.PayloadEnvelope import VortexMsgList, PayloadEnvelope
|
|
23
|
+
from vortex.PayloadFilterKeys import plIdKey, plDeleteKey
|
|
24
|
+
from vortex.Tuple import Tuple
|
|
25
|
+
from vortex.VortexFactory import VortexFactory
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OrmCrudHandlerExtension(object):
|
|
31
|
+
def uiData(self, tuple_, tuples, session, payloadFilt):
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
def afterCreate(self, tuple_, tuples, session, payloadFilt):
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
def afterRetrieve(self, tuple_, tuples, session, payloadFilt):
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
def beforeUpdate(self, tuple_, tuples, session, payloadFilt):
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
def middleUpdate(self, tuple_, tuples, session, payloadFilt):
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
def afterUpdate(self, tuple_, tuples, session, payloadFilt):
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
def afterUpdateCommit(self, tuple_, tuples, session, payloadFilt):
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
def beforeDelete(self, tuple_, tuples, session, payloadFilt):
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def afterDeleteCommit(self, tuple_, tuples, session, payloadFilt):
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class _OrmCrudExtensionProcessor(object):
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.extensions = {}
|
|
62
|
+
|
|
63
|
+
def addExtensionClassDecorator(self, Tuple):
|
|
64
|
+
def f(cls):
|
|
65
|
+
self.extensions[Tuple.tupleType()] = cls()
|
|
66
|
+
return cls
|
|
67
|
+
|
|
68
|
+
return f
|
|
69
|
+
|
|
70
|
+
def addExtensionObject(self, Tuple, ormCrudHandlerExtension):
|
|
71
|
+
self.extensions[Tuple.tupleType()] = ormCrudHandlerExtension
|
|
72
|
+
|
|
73
|
+
def uiData(self, tuples, session, payloadFilt):
|
|
74
|
+
if not self.extensions:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
for tuple_ in tuples:
|
|
78
|
+
if not hasattr(tuple_, "uiData"):
|
|
79
|
+
continue
|
|
80
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
81
|
+
if extension:
|
|
82
|
+
extension.uiData(tuple_, tuples, session, payloadFilt)
|
|
83
|
+
|
|
84
|
+
def afterCreate(self, tuples, session, payloadFilt):
|
|
85
|
+
if not self.extensions:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
for tuple_ in tuples:
|
|
89
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
90
|
+
if extension:
|
|
91
|
+
extension.afterCreate(tuple_, tuples, session, payloadFilt)
|
|
92
|
+
|
|
93
|
+
self.uiData(tuples, session, payloadFilt)
|
|
94
|
+
|
|
95
|
+
def afterRetrieve(self, tuples, session, payloadFilt):
|
|
96
|
+
if not self.extensions:
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
for tuple_ in tuples:
|
|
100
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
101
|
+
if extension:
|
|
102
|
+
extension.afterRetrieve(tuple_, tuples, session, payloadFilt)
|
|
103
|
+
|
|
104
|
+
self.uiData(tuples, session, payloadFilt)
|
|
105
|
+
|
|
106
|
+
def beforeUpdate(self, tuples, session, payloadFilt):
|
|
107
|
+
if not self.extensions:
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
for tuple_ in tuples:
|
|
111
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
112
|
+
if extension:
|
|
113
|
+
extension.beforeUpdate(tuple_, tuples, session, payloadFilt)
|
|
114
|
+
|
|
115
|
+
def middleUpdate(self, tuples, session, payloadFilt):
|
|
116
|
+
if not self.extensions:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
for tuple_ in tuples:
|
|
120
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
121
|
+
if extension:
|
|
122
|
+
extension.middleUpdate(tuple_, tuples, session, payloadFilt)
|
|
123
|
+
|
|
124
|
+
def afterUpdate(self, tuples, session, payloadFilt):
|
|
125
|
+
if not self.extensions:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
for tuple_ in tuples:
|
|
129
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
130
|
+
if extension:
|
|
131
|
+
extension.afterUpdate(tuple_, tuples, session, payloadFilt)
|
|
132
|
+
|
|
133
|
+
def afterUpdateCommit(self, tuples, session, payloadFilt):
|
|
134
|
+
if not self.extensions:
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
for tuple_ in tuples:
|
|
138
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
139
|
+
if extension:
|
|
140
|
+
extension.afterUpdateCommit(
|
|
141
|
+
tuple_, tuples, session, payloadFilt
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
self.uiData(tuples, session, payloadFilt)
|
|
145
|
+
|
|
146
|
+
def beforeDelete(self, tuples, session, payloadFilt):
|
|
147
|
+
if not self.extensions:
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
for tuple_ in tuples:
|
|
151
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
152
|
+
if extension:
|
|
153
|
+
extension.beforeDelete(tuple_, tuples, session, payloadFilt)
|
|
154
|
+
|
|
155
|
+
def afterDeleteCommit(self, tuples, session, payloadFilt):
|
|
156
|
+
if not self.extensions:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
for tuple_ in tuples:
|
|
160
|
+
extension = self.extensions.get(tuple_.tupleType())
|
|
161
|
+
if extension:
|
|
162
|
+
extension.afterDeleteCommit(
|
|
163
|
+
tuple_, tuples, session, payloadFilt
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class OrmCrudHandler(object):
|
|
168
|
+
UPDATE = 1
|
|
169
|
+
CREATE = 2
|
|
170
|
+
DELETE = 3
|
|
171
|
+
QUERY = 4
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self, sessionFunctor, Declarative, payloadFilter, retreiveAll=False
|
|
175
|
+
):
|
|
176
|
+
"""Create CRUD Hanlder
|
|
177
|
+
|
|
178
|
+
This handler will perform crud operations for an SQLAlchemy Declarative
|
|
179
|
+
|
|
180
|
+
"""
|
|
181
|
+
self._sessionFunctor = sessionFunctor
|
|
182
|
+
self._Declarative = Declarative
|
|
183
|
+
self._payloadFilter = (
|
|
184
|
+
payloadFilter
|
|
185
|
+
if isinstance(payloadFilter, dict)
|
|
186
|
+
else {"key": payloadFilter}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
self._ep = PayloadEndpoint(self._payloadFilter, self._process)
|
|
190
|
+
|
|
191
|
+
self._ext = _OrmCrudExtensionProcessor()
|
|
192
|
+
|
|
193
|
+
self._retreiveAll = retreiveAll
|
|
194
|
+
|
|
195
|
+
def shutdown(self):
|
|
196
|
+
self._ep.shutdown()
|
|
197
|
+
|
|
198
|
+
def addExtension(self, Tuple, ormCrudHandlerExtension=None):
|
|
199
|
+
if ormCrudHandlerExtension:
|
|
200
|
+
self._ext.addExtensionObject(Tuple, ormCrudHandlerExtension)
|
|
201
|
+
return
|
|
202
|
+
return self._ext.addExtensionClassDecorator(Tuple)
|
|
203
|
+
|
|
204
|
+
@inlineCallbacks
|
|
205
|
+
def _process(self, *args, **kwargs):
|
|
206
|
+
val = yield self._processInThread(*args, **kwargs)
|
|
207
|
+
return val
|
|
208
|
+
|
|
209
|
+
@deferToThreadWrapWithLogger(logger)
|
|
210
|
+
def _processInThread(
|
|
211
|
+
self,
|
|
212
|
+
payloadEnvelope: PayloadEnvelope,
|
|
213
|
+
vortexUuid: str,
|
|
214
|
+
sendResponse: Callable[[Union[VortexMsgList, bytes]], None],
|
|
215
|
+
**kwargs
|
|
216
|
+
):
|
|
217
|
+
|
|
218
|
+
# Execute preprocess functions
|
|
219
|
+
if self.preProcess(payloadEnvelope, vortexUuid, **kwargs) != None:
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
# Create reply payload replyFilt
|
|
223
|
+
|
|
224
|
+
replyFilt = copy(self._payloadFilter)
|
|
225
|
+
if payloadEnvelope.filt:
|
|
226
|
+
replyFilt.update(payloadEnvelope.filt)
|
|
227
|
+
|
|
228
|
+
# Get data from the payload
|
|
229
|
+
phId = payloadEnvelope.filt.get(plIdKey)
|
|
230
|
+
delete = payloadEnvelope.filt.get(plDeleteKey, False)
|
|
231
|
+
|
|
232
|
+
# Setup variables to populate
|
|
233
|
+
replyPayloadEnvelope: PayloadEnvelope = None
|
|
234
|
+
action = None
|
|
235
|
+
|
|
236
|
+
tuples = []
|
|
237
|
+
if payloadEnvelope.encodedPayload:
|
|
238
|
+
tuples = payloadEnvelope.decodePayload().tuples
|
|
239
|
+
|
|
240
|
+
session = self._getSession()
|
|
241
|
+
|
|
242
|
+
# Execute the action
|
|
243
|
+
try:
|
|
244
|
+
if delete == True:
|
|
245
|
+
action = self.DELETE
|
|
246
|
+
replyPayloadEnvelope = self._delete(
|
|
247
|
+
session, tuples, phId, payloadEnvelope.filt
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
elif len(tuples):
|
|
251
|
+
action = self.UPDATE
|
|
252
|
+
replyPayloadEnvelope = self._update(
|
|
253
|
+
session, tuples, payloadEnvelope.filt
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
elif phId != None:
|
|
257
|
+
action = self.QUERY
|
|
258
|
+
replyPayloadEnvelope = self._retrieve(
|
|
259
|
+
session, phId, payloadEnvelope.filt
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
elif len(tuples) == 0:
|
|
263
|
+
action = self.CREATE
|
|
264
|
+
replyPayloadEnvelope = self._create(
|
|
265
|
+
session, payloadEnvelope.filt
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
else:
|
|
269
|
+
session.close()
|
|
270
|
+
raise Exception("Invalid ORM CRUD parameter state")
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
replyPayloadEnvelope = PayloadEnvelope(
|
|
274
|
+
result=str(e), filt=replyFilt
|
|
275
|
+
)
|
|
276
|
+
sendResponse(replyPayloadEnvelope.toVortexMsg())
|
|
277
|
+
try:
|
|
278
|
+
session.rollback()
|
|
279
|
+
except:
|
|
280
|
+
pass
|
|
281
|
+
session.close()
|
|
282
|
+
raise
|
|
283
|
+
|
|
284
|
+
# Prefer reply filt, if not combine our accpt filt with the filt we were sent
|
|
285
|
+
|
|
286
|
+
# Ensure any delegates are playing nice with the result
|
|
287
|
+
if (
|
|
288
|
+
action in (self.DELETE, self.UPDATE)
|
|
289
|
+
and replyPayloadEnvelope.result is None
|
|
290
|
+
):
|
|
291
|
+
replyPayloadEnvelope.result = True
|
|
292
|
+
|
|
293
|
+
replyPayloadEnvelope.filt = replyFilt
|
|
294
|
+
sendResponse(replyPayloadEnvelope.toVortexMsg())
|
|
295
|
+
|
|
296
|
+
# Execute the post process function
|
|
297
|
+
self.postProcess(action, payloadEnvelope.filt, vortexUuid)
|
|
298
|
+
session.commit()
|
|
299
|
+
session.close()
|
|
300
|
+
|
|
301
|
+
def _getSession(self):
|
|
302
|
+
if isinstance(self._sessionFunctor, Session):
|
|
303
|
+
return self._sessionFunctor
|
|
304
|
+
|
|
305
|
+
return self._sessionFunctor()
|
|
306
|
+
|
|
307
|
+
def _getDeclarativeById(self, session, id_):
|
|
308
|
+
qry = session.query(self._Declarative)
|
|
309
|
+
if self._retreiveAll and id_ is None:
|
|
310
|
+
return qry.all()
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
return qry.filter(self._Declarative.id == id_).one()
|
|
314
|
+
except NoResultFound as e:
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
def createDeclarative(self, session, payloadFilt):
|
|
318
|
+
if self._retreiveAll:
|
|
319
|
+
return session.query(self._Declarative).all()
|
|
320
|
+
|
|
321
|
+
return [self._Declarative()]
|
|
322
|
+
|
|
323
|
+
def _getDeclarativeByTuple(self, session, tuple_):
|
|
324
|
+
T = tuple_.__class__
|
|
325
|
+
return session.query(T).filter(T.id == tuple_.id).one()
|
|
326
|
+
|
|
327
|
+
def _create(self, session, payloadFilt) -> PayloadEnvelope:
|
|
328
|
+
tuples = self.createDeclarative(session, payloadFilt)
|
|
329
|
+
payload = Payload(tuples=tuples)
|
|
330
|
+
self._ext.afterCreate(payload.tuples, session, payloadFilt)
|
|
331
|
+
return payload.makePayloadEnvelope()
|
|
332
|
+
|
|
333
|
+
def _retrieve(
|
|
334
|
+
self, session, filtId, payloadFilt, obj=None, **kwargs
|
|
335
|
+
) -> PayloadEnvelope:
|
|
336
|
+
ph = obj if obj else self._getDeclarativeById(session, filtId)
|
|
337
|
+
payload = Payload()
|
|
338
|
+
payload.tuples = [ph] if ph else []
|
|
339
|
+
self._ext.afterRetrieve(payload.tuples, session, payloadFilt)
|
|
340
|
+
return payload.makePayloadEnvelope()
|
|
341
|
+
|
|
342
|
+
def _update(self, session, tuples, payloadFilt) -> PayloadEnvelope:
|
|
343
|
+
self._ext.beforeUpdate(tuples, session, payloadFilt)
|
|
344
|
+
|
|
345
|
+
# Add everything first.
|
|
346
|
+
for tupleObj in tuples:
|
|
347
|
+
if tupleObj is None:
|
|
348
|
+
raise Exception("None/null was present in array of tuples")
|
|
349
|
+
|
|
350
|
+
# Make sure it's not ''
|
|
351
|
+
if tupleObj.id == "":
|
|
352
|
+
tupleObj.id = None
|
|
353
|
+
|
|
354
|
+
if tupleObj.id is None:
|
|
355
|
+
session.add(tupleObj)
|
|
356
|
+
|
|
357
|
+
self._ext.middleUpdate(tuples, session, payloadFilt)
|
|
358
|
+
|
|
359
|
+
# Now merge with the session
|
|
360
|
+
returnTuples = []
|
|
361
|
+
for tupleObj in tuples:
|
|
362
|
+
if tupleObj.id is None:
|
|
363
|
+
# If this was a create, then we can just return this tuple
|
|
364
|
+
returnTuples.append(tupleObj)
|
|
365
|
+
|
|
366
|
+
else:
|
|
367
|
+
# Otherwise use the merge method to perform the update magic.
|
|
368
|
+
# and add the resulting merged tuple to the return list
|
|
369
|
+
returnTuples.append(session.merge(tupleObj))
|
|
370
|
+
|
|
371
|
+
self._ext.afterUpdate(returnTuples, session, payloadFilt)
|
|
372
|
+
|
|
373
|
+
session.commit()
|
|
374
|
+
|
|
375
|
+
self._ext.afterUpdateCommit(returnTuples, session, payloadFilt)
|
|
376
|
+
|
|
377
|
+
return Payload(tuples=returnTuples).makePayloadEnvelope(result=True)
|
|
378
|
+
|
|
379
|
+
def _delete(self, session, tuples, filtId, payloadFilt) -> PayloadEnvelope:
|
|
380
|
+
self._ext.beforeDelete(tuples, session, payloadFilt)
|
|
381
|
+
|
|
382
|
+
if len(tuples):
|
|
383
|
+
phIds = [t.id for t in tuples]
|
|
384
|
+
else:
|
|
385
|
+
phIds = [filtId]
|
|
386
|
+
|
|
387
|
+
for phId in phIds:
|
|
388
|
+
ph = self._getDeclarativeById(session, phId)
|
|
389
|
+
try:
|
|
390
|
+
# Try to iterate it
|
|
391
|
+
for item in iter(ph):
|
|
392
|
+
session.delete(item)
|
|
393
|
+
|
|
394
|
+
except TypeError:
|
|
395
|
+
# If it's not an iterator
|
|
396
|
+
if ph is not None:
|
|
397
|
+
session.delete(ph)
|
|
398
|
+
|
|
399
|
+
session.commit()
|
|
400
|
+
|
|
401
|
+
returnTuples: List[Tuple] = []
|
|
402
|
+
if self._retreiveAll:
|
|
403
|
+
returnTuples = self.createDeclarative(session, payloadFilt)
|
|
404
|
+
|
|
405
|
+
self._ext.afterDeleteCommit(tuples, session, payloadFilt)
|
|
406
|
+
return Payload(tuples=returnTuples).makePayloadEnvelope(result=True)
|
|
407
|
+
|
|
408
|
+
def sendModelUpdate(
|
|
409
|
+
self, objId, vortexUuid=None, session=None, obj=None, **kwargs
|
|
410
|
+
):
|
|
411
|
+
session = session if session else self._getSession()
|
|
412
|
+
pl = self._retrieve(session, objId, self._payloadFilter, obj=obj)
|
|
413
|
+
pl.filt.update(self._payloadFilter)
|
|
414
|
+
pl.filt[plIdKey] = objId
|
|
415
|
+
VortexFactory.sendVortexMsg(pl.toVortexMsg(), destVortexUuid=vortexUuid)
|
|
416
|
+
|
|
417
|
+
def preProcess(self, payload, vortextUuid, **kwargs):
|
|
418
|
+
pass
|
|
419
|
+
|
|
420
|
+
def postProcess(self, action, payloadFilt, vortextUuid):
|
|
421
|
+
pass
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class OrmCrudHandlerInThread(OrmCrudHandler):
|
|
425
|
+
def _process(self, payload, **kwargs):
|
|
426
|
+
return deferToThread(OrmCrudHandler.process, self, payload, **kwargs)
|
|
427
|
+
|
|
428
|
+
def sendModelUpdate(self, objId, **kwargs):
|
|
429
|
+
return deferToThread(
|
|
430
|
+
OrmCrudHandler.sendModelUpdate, self, objId, **kwargs
|
|
431
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from vortex.DeferUtil import deferToThreadWrapWithLogger
|
|
4
|
+
from vortex.Payload import Payload
|
|
5
|
+
from vortex.Tuple import tupleForTupleName
|
|
6
|
+
from vortex.TupleSelector import TupleSelector
|
|
7
|
+
from vortex.handler.TupleDataObservableHandler import TuplesProviderABC
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TuplesProviderForDB(TuplesProviderABC):
|
|
13
|
+
def __init__(self, ormSessionCreatorFunc):
|
|
14
|
+
self._ormSessionCreatorFunc = ormSessionCreatorFunc
|
|
15
|
+
|
|
16
|
+
@deferToThreadWrapWithLogger(logger)
|
|
17
|
+
def makeVortexMsg(self, filt: dict, tupleSelector: TupleSelector) -> bytes:
|
|
18
|
+
"""Make VortexMsg for DB
|
|
19
|
+
|
|
20
|
+
Considerations for this method.
|
|
21
|
+
|
|
22
|
+
#. It must return a vortexMsg (bytes), this ensures all the database access
|
|
23
|
+
and lazy loading is completed in this thread, in this session.
|
|
24
|
+
|
|
25
|
+
#. It must ensure the session is closed on/after exit.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
ormSession = self._ormSessionCreatorFunc()
|
|
30
|
+
try:
|
|
31
|
+
TupleClass = tupleForTupleName(tupleSelector.name)
|
|
32
|
+
qry = ormSession.query(TupleClass)
|
|
33
|
+
for key, value in tupleSelector.selector.items():
|
|
34
|
+
qry = qry.filter(getattr(TupleClass, key) == value)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
Payload(filt=filt, tuples=qry.all())
|
|
38
|
+
.makePayloadEnvelope()
|
|
39
|
+
.toVortexMsg()
|
|
40
|
+
)
|
|
41
|
+
finally:
|
|
42
|
+
ormSession.close()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import TypeDecorator, JSON
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from vortex.Tuple import Tuple
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VortexTupleJSON(TypeDecorator):
|
|
10
|
+
impl = JSON()
|
|
11
|
+
|
|
12
|
+
def __init__(self, TupleClass, *args: Any, **kwargs: Any):
|
|
13
|
+
super().__init__(self, *args, **kwargs)
|
|
14
|
+
self._TupleClass = TupleClass
|
|
15
|
+
|
|
16
|
+
def process_bind_param(self, value: Tuple, dialect):
|
|
17
|
+
return json.dumps(value.tupleToRestfulJsonDict())
|
|
18
|
+
|
|
19
|
+
def process_result_value(self, value, dialect):
|
|
20
|
+
valueDict = json.loads(value)
|
|
21
|
+
|
|
22
|
+
return Tuple.restfulJsonDictToTupleWithValidation(
|
|
23
|
+
valueDict, self._TupleClass
|
|
24
|
+
)
|
|
File without changes
|