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.
Files changed (87) hide show
  1. vortex/DataWrapTuple.py +27 -0
  2. vortex/DeferUtil.py +224 -0
  3. vortex/Jsonable.py +304 -0
  4. vortex/Payload.py +115 -0
  5. vortex/PayloadEndpoint.py +216 -0
  6. vortex/PayloadEndpointTest.py +164 -0
  7. vortex/PayloadEnvelope.py +166 -0
  8. vortex/PayloadFilterKeys.py +15 -0
  9. vortex/PayloadIO.py +152 -0
  10. vortex/PayloadPriority.py +11 -0
  11. vortex/PayloadResponse.py +173 -0
  12. vortex/PayloadResponseTest.py +39 -0
  13. vortex/PayloadTest.py +136 -0
  14. vortex/PerformTestActionTuple.py +30 -0
  15. vortex/SerialiseUtil.py +273 -0
  16. vortex/TestTuple.py +61 -0
  17. vortex/Tuple.py +968 -0
  18. vortex/TupleAction.py +72 -0
  19. vortex/TupleActionVortex.py +5 -0
  20. vortex/TupleDataForTest.py +25 -0
  21. vortex/TuplePerfTest.py +71 -0
  22. vortex/TuplePolymorphicTupleTypeFieldArgTest.py +74 -0
  23. vortex/TupleRestfulTest.py +140 -0
  24. vortex/TupleSelector.py +105 -0
  25. vortex/TupleTest.py +127 -0
  26. vortex/VortexABC.py +55 -0
  27. vortex/VortexClientHttp.py +308 -0
  28. vortex/VortexClientHttpTest.py +71 -0
  29. vortex/VortexClientTcp.py +355 -0
  30. vortex/VortexClientWebsocketFactory.py +383 -0
  31. vortex/VortexConnectionABC.py +88 -0
  32. vortex/VortexFactory.py +932 -0
  33. vortex/VortexPayloadProtocol.py +180 -0
  34. vortex/VortexServer.py +367 -0
  35. vortex/VortexServerConnection.py +127 -0
  36. vortex/VortexServerHttpResource.py +193 -0
  37. vortex/VortexServerTcp.py +104 -0
  38. vortex/VortexServerWebsocket.py +287 -0
  39. vortex/VortexTcpMemoryLeakPayloadTest.py +611 -0
  40. vortex/VortexTcpMemoryLeakRawTest.py +87 -0
  41. vortex/VortexTest.py +22 -0
  42. vortex/VortexUtil.py +314 -0
  43. vortex/VortexWritePushProducer.py +321 -0
  44. vortex/__init__.py +10 -0
  45. vortex/data_loader/TupleDataLiveLockManager.py +383 -0
  46. vortex/data_loader/TupleDataLoader.py +326 -0
  47. vortex/data_loader/TupleDataLoaderDelegate.py +90 -0
  48. vortex/data_loader/TupleDataLoaderTupleABC.py +10 -0
  49. vortex/data_loader/TupleDataLoaderTuples.py +53 -0
  50. vortex/data_loader/__init__.py +0 -0
  51. vortex/event_bus/TupleChangeEventABC.py +15 -0
  52. vortex/event_bus/TupleChangeEventBus.py +192 -0
  53. vortex/event_bus/TupleChangeEventBusObserverABC.py +11 -0
  54. vortex/event_bus/__init__.py +0 -0
  55. vortex/handler/TupleActionProcessor.py +270 -0
  56. vortex/handler/TupleActionProcessorProxy.py +218 -0
  57. vortex/handler/TupleActionProcessorTestHandler.py +27 -0
  58. vortex/handler/TupleDataActionClient.py +67 -0
  59. vortex/handler/TupleDataObservableCache.py +201 -0
  60. vortex/handler/TupleDataObservableHandler.py +371 -0
  61. vortex/handler/TupleDataObservableProxyHandler.py +340 -0
  62. vortex/handler/TupleDataObservableTestHandler.py +60 -0
  63. vortex/handler/TupleDataObserverClient.py +158 -0
  64. vortex/handler/VortexJSTupleLoaderTestHandler.py +42 -0
  65. vortex/handler/__init__.py +0 -0
  66. vortex/restful/GzippedDataHttpClient.py +170 -0
  67. vortex/restful/GzippedDataHttpClientTest.py +103 -0
  68. vortex/restful/GzippedPayloadHttpClient.py +181 -0
  69. vortex/restful/RestfulHttpClient.py +107 -0
  70. vortex/restful/RestfulHttpClientTest.py +60 -0
  71. vortex/restful/RestfulResource.py +153 -0
  72. vortex/restful/RestfulResourceTest.py +100 -0
  73. vortex/restful/__init__.py +0 -0
  74. vortex/rpc/RPC.py +446 -0
  75. vortex/rpc/RPCTest.py +12 -0
  76. vortex/rpc/__init__.py +0 -0
  77. vortex/sqla_orm/OrmCrudHandler.py +431 -0
  78. vortex/sqla_orm/TupleProviderForDb.py +42 -0
  79. vortex/sqla_orm/VortexTupleJson.py +24 -0
  80. vortex/sqla_orm/__init__.py +0 -0
  81. vortex/storage/TupleStorageSqlite.py +359 -0
  82. vortex/storage/__init__.py +0 -0
  83. vortexpy-4.0.23.dist-info/LICENSE +21 -0
  84. vortexpy-4.0.23.dist-info/METADATA +43 -0
  85. vortexpy-4.0.23.dist-info/RECORD +87 -0
  86. vortexpy-4.0.23.dist-info/WHEEL +5 -0
  87. vortexpy-4.0.23.dist-info/top_level.txt +1 -0
@@ -0,0 +1,216 @@
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 inspect
10
+ import logging
11
+ import types
12
+ import weakref
13
+ from copy import copy
14
+ from typing import Callable, Optional, Dict, Union
15
+
16
+ from vortex.PayloadEnvelope import PayloadEnvelope
17
+ from vortex.PayloadIO import PayloadIO
18
+ from vortex.VortexABC import SendVortexMsgResponseCallable
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ # TODO: typings needs to support keyword args before tbis will work.
24
+ # PayloadEndpointProcessCallable = Callable[
25
+ # [
26
+ # ...
27
+ # # PayloadEnvelope, # payloadEnvelope
28
+ # # str, # vortexUuid
29
+ # # str, # vortexName
30
+ # # Any, # httpSession
31
+ # # SendVortexMsgResponseCallable # sendResponse
32
+ # ],
33
+ # Optional[Deferred] # Return
34
+ # ]
35
+
36
+
37
+ class PayloadEndpoint:
38
+ """
39
+ The payload endpoint is responsible for matching payloads filters against
40
+ filters defined in the endpoint. If the end point filters are within the
41
+ payload filter then the payload will be passed to the supplied callable.
42
+ """
43
+
44
+ PERMITTER = None
45
+
46
+ def __init__(
47
+ self,
48
+ filt: Dict,
49
+ callable_,
50
+ acceptOnlyFromVortex: Optional[Union[str, tuple]] = None,
51
+ ignoreFromVortex: Optional[Union[str, tuple]] = None,
52
+ ) -> None:
53
+ """
54
+ :param filt: The filter to match against payloads
55
+ :param callable_: This will be called and passed the payload if it matches
56
+ :param acceptOnlyFromVortex: Accept payloads only from this vortex,
57
+ The vortex can be str or tuple of str, or None to accept from any.
58
+ """
59
+ self._acceptOnlyFromVortex = acceptOnlyFromVortex
60
+ if isinstance(self._acceptOnlyFromVortex, str):
61
+ self._acceptOnlyFromVortex = (self._acceptOnlyFromVortex,)
62
+
63
+ self._ignoreFromVortex = ignoreFromVortex
64
+ if isinstance(self._ignoreFromVortex, str):
65
+ self._ignoreFromVortex = (self._ignoreFromVortex,)
66
+
67
+ self._keyCheck(filt)
68
+
69
+ self._wref: Callable[[], Optional[Callable]] = None
70
+ if isinstance(callable_, types.FunctionType):
71
+ w = None
72
+ if hasattr(callable_, "_endpointWeakClass"):
73
+ w = callable_._endpointWeakClass
74
+
75
+ else:
76
+
77
+ class W:
78
+ def __init__(self, callable_):
79
+ self._callable = callable_
80
+ self._callable._endpointWeakClass = self
81
+
82
+ def __call__(self, payload):
83
+ self._callable(payload)
84
+
85
+ w = W(callable_)
86
+
87
+ self._wref = weakref.ref(w)
88
+
89
+ else:
90
+ weakObject = weakref.ref(callable_.__self__)
91
+ weakMethod = weakref.ref(callable_.__func__)
92
+
93
+ if callable_.__func__.__name__ == "func":
94
+ logger.warning(
95
+ "The registered callback is 'func' this is likely"
96
+ " a method wrapped in @deferToThreadWrapWithLogger, %s",
97
+ filt,
98
+ )
99
+
100
+ def getCallable():
101
+ obj = weakObject()
102
+ func = weakMethod()
103
+ if obj and func:
104
+ return getattr(obj, func.__name__)
105
+ return None
106
+
107
+ self._wref = getCallable
108
+
109
+ self._filt = filt
110
+ PayloadIO().add(self)
111
+
112
+ def _keyCheck(self, filt):
113
+ if not "key" in filt:
114
+ e = Exception(
115
+ "There is no 'key' in the payload filt"
116
+ ", There must be one for routing"
117
+ )
118
+ logger.exception(e)
119
+ raise e
120
+
121
+ @property
122
+ def filt(self):
123
+ return copy(self._filt)
124
+
125
+ def check(self, payloadEnvelope: PayloadEnvelope, vortexName: str) -> bool:
126
+
127
+ # Filter for the backend vortexes.
128
+ if self._ignoreFromVortex and vortexName in self._ignoreFromVortex:
129
+ return False
130
+
131
+ # Filter for the backend vortexes.
132
+ if (
133
+ self._acceptOnlyFromVortex
134
+ and vortexName not in self._acceptOnlyFromVortex
135
+ ):
136
+ return False
137
+
138
+ def removeUnhashable(filt):
139
+ items = set()
140
+ for key, value in list(filt.items()):
141
+ # We can compare an array of primitives
142
+ if isinstance(value, list):
143
+ value = tuple(sorted(value))
144
+
145
+ # We don't compare complex structures
146
+ if isinstance(value, dict):
147
+ continue
148
+
149
+ if inspect.isclass(value) and (
150
+ issubclass(value, dict) or issubclass(value, list)
151
+ ):
152
+ raise Exception(
153
+ "Class type passed instead of an instance"
154
+ " key:%s, value:%s" % (key, value)
155
+ )
156
+
157
+ items.add((key, value))
158
+ return items
159
+
160
+ theirFilt = removeUnhashable(payloadEnvelope.filt)
161
+ ourFilt = removeUnhashable(self._filt)
162
+
163
+ return set(ourFilt).issubset(theirFilt)
164
+
165
+ def process(
166
+ self,
167
+ payloadEnvelope: PayloadEnvelope,
168
+ vortexUuid: str,
169
+ vortexName: str,
170
+ httpSession,
171
+ sendResponse: SendVortexMsgResponseCallable,
172
+ ):
173
+
174
+ if not self.check(payloadEnvelope, vortexName):
175
+ return
176
+
177
+ if self.PERMITTER and not self.PERMITTER.check(
178
+ payloadEnvelope, httpSession
179
+ ):
180
+ logger.debug("Permission failed for %s", payloadEnvelope.filt)
181
+ return
182
+
183
+ callable_ = self._wref()
184
+ if callable_:
185
+ return callable_(
186
+ payloadEnvelope=payloadEnvelope,
187
+ vortexUuid=vortexUuid,
188
+ vortexName=vortexName,
189
+ httpSession=httpSession,
190
+ sendResponse=sendResponse,
191
+ )
192
+ else:
193
+ PayloadIO().remove(self)
194
+
195
+ def _callableExpired(self, expiredCallable):
196
+ pass
197
+ # PayloadIO().remove(self)
198
+
199
+ def __repr__(self):
200
+ callable_ = self._wref()
201
+ if callable_:
202
+ try:
203
+ callbackStr = "%s.%s" % (
204
+ callable_.__self__.__class__.__name__,
205
+ callable_.__name__,
206
+ )
207
+ except Exception as e:
208
+ callbackStr = str(e)
209
+ else:
210
+ callbackStr = "None"
211
+
212
+ s = "Payload id=%s\nfilt=%s\ncallback=%s"
213
+ return s % (id(self), self._filt, callbackStr)
214
+
215
+ def shutdown(self):
216
+ PayloadIO().remove(self)
@@ -0,0 +1,164 @@
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
+
10
+ from twisted.trial import unittest
11
+
12
+ from vortex.Payload import Payload
13
+ from vortex.PayloadEndpoint import PayloadEndpoint
14
+ from vortex.PayloadEnvelope import PayloadEnvelope
15
+ from vortex.PayloadIO import PayloadIO
16
+
17
+
18
+ class PayloadEndpointPyTestAssignPayload:
19
+ def __init__(self, unitTest):
20
+ self._ut = unitTest
21
+
22
+ def process(self, payloadEnvelope: PayloadEnvelope, **kwargs):
23
+ self._ut.deliveredPayload = payloadEnvelope
24
+
25
+
26
+ class PayloadEndpointPyTest(unittest.TestCase):
27
+ def setUp(self):
28
+ PayloadIO._instance = None
29
+
30
+ self.deliveredPayloadEnvelope = None
31
+
32
+ def _testBuild(self, plFilt, epFilt):
33
+ payload = Payload()
34
+ payload.filt.update(plFilt)
35
+ for x in range(6):
36
+ payload.filt["%s" % x] = x
37
+
38
+ def processPayload(payloadEnvelope: PayloadEnvelope, **kwargs):
39
+ self.deliveredPayloadEnvelope = payloadEnvelope
40
+
41
+ self._keepFuncInScope = processPayload
42
+
43
+ PayloadEndpoint(epFilt, processPayload)
44
+
45
+ PayloadIO().process(
46
+ payloadEnvelope=payload.makePayloadEnvelope(),
47
+ vortexUuid="test",
48
+ vortexName="test",
49
+ httpSession=None,
50
+ sendResponse=lambda x: x,
51
+ )
52
+
53
+ return payload
54
+
55
+ def testFiltMatches(self):
56
+ plFilt = {"key": "unittest", "This matches": 555}
57
+ epFilt = {"key": "unittest", "This matches": 555}
58
+
59
+ payload = self._testBuild(plFilt, epFilt)
60
+
61
+ self.assertEqual(
62
+ payload,
63
+ self.deliveredPayloadEnvelope,
64
+ "PayloadIO/PayloadEndpoint delivery error",
65
+ )
66
+
67
+ def testFiltValueUnmatched(self):
68
+ plFilt = {"key": "unittest", "This matches": 555}
69
+ epFilt = {"key": "unittest", "This matches": 0}
70
+
71
+ self._testBuild(plFilt, epFilt)
72
+
73
+ self.assertEqual(
74
+ self.deliveredPayloadEnvelope,
75
+ None,
76
+ "PayloadIO/PayloadEndpoint unmatched value test error",
77
+ )
78
+
79
+ def testFiltKeyUnmatched(self):
80
+ plFilt = {"key": "unittest", "This matches": 555}
81
+ epFilt = {"key": "unittest", "This doesnt matches": 555}
82
+
83
+ self._testBuild(plFilt, epFilt)
84
+
85
+ self.assertEqual(
86
+ self.deliveredPayloadEnvelope,
87
+ None,
88
+ "PayloadIO/PayloadEndpoint unmatched value test error",
89
+ )
90
+
91
+ def testFunctionGoesOutOfScope(self):
92
+ filt = {"key": "unittest", "This matches": 555}
93
+
94
+ payload = Payload()
95
+ payload.filt = filt
96
+
97
+ def subScope():
98
+ def outOfScopeFunc(
99
+ payloadEnvelope: PayloadEnvelope, *args, **kwargs
100
+ ):
101
+ self.deliveredPayloadEnvelope = payloadEnvelope
102
+
103
+ PayloadEndpoint(filt, outOfScopeFunc)
104
+
105
+ PayloadIO().process(
106
+ payloadEnvelope=payload.makePayloadEnvelope(),
107
+ vortexUuid="test",
108
+ vortexName="test",
109
+ httpSession=None,
110
+ sendResponse=lambda x: x,
111
+ )
112
+
113
+ self.assertEqual(
114
+ self.deliveredPayloadEnvelope,
115
+ None,
116
+ "PayloadIO/PayloadEndpoint unmatched value test error",
117
+ )
118
+
119
+ def testClassGoesOutOdScope(self):
120
+ filt = {"key": "unittest", "This matches": 555}
121
+
122
+ payload = Payload()
123
+ payload.filt = filt
124
+
125
+ def subScope():
126
+ inst = PayloadEndpointPyTestAssignPayload(self)
127
+ PayloadEndpoint(filt, inst.process)
128
+
129
+ PayloadIO().process(
130
+ payloadEnvelope=payload.makePayloadEnvelope(),
131
+ vortexUuid="test",
132
+ vortexName="test",
133
+ httpSession=None,
134
+ sendResponse=lambda x: x,
135
+ )
136
+
137
+ self.assertEqual(
138
+ self.deliveredPayloadEnvelope,
139
+ None,
140
+ "PayloadIO/PayloadEndpoint unmatched value test error",
141
+ )
142
+
143
+ def testClassStaysInScope(self):
144
+ filt = {"key": "unittest", "This matches": 555}
145
+
146
+ payload = Payload()
147
+ payload.filt = filt
148
+
149
+ inst = PayloadEndpointPyTestAssignPayload(self)
150
+ PayloadEndpoint(filt, inst.process)
151
+
152
+ PayloadIO().process(
153
+ payloadEnvelope=payload.makePayloadEnvelope(),
154
+ vortexUuid="test",
155
+ vortexName="test",
156
+ httpSession=None,
157
+ sendResponse=lambda x: x,
158
+ )
159
+
160
+ self.assertEqual(
161
+ self.deliveredPayloadEnvelope,
162
+ payload,
163
+ "PayloadIO/PayloadEndpoint unmatched value test error",
164
+ )
@@ -0,0 +1,166 @@
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
+
10
+ import base64
11
+ import json
12
+ import logging
13
+ from base64 import b64decode
14
+ from base64 import b64encode
15
+ from datetime import datetime
16
+ from typing import Any
17
+ from typing import Dict
18
+ from typing import List
19
+ from typing import Optional
20
+ from typing import Union
21
+
22
+ import pytz
23
+
24
+ from .DeferUtil import deferToThreadWrapWithLogger
25
+ from .DeferUtil import noMainThread
26
+ from .Jsonable import Jsonable
27
+ from .Payload import Payload
28
+ from .SerialiseUtil import T_RAPUI_PAYLOAD_ENVELOPE
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ PayloadEnvelopeList = List["PayloadEnvelope"]
33
+ VortexMsgList = List[bytes]
34
+
35
+
36
+ class NoPayloadException(Exception):
37
+ pass
38
+
39
+
40
+ class PayloadEnvelope(Jsonable):
41
+ """Payload
42
+ This object represents a hierarchy of data transferred between client and server
43
+ """
44
+
45
+ __fieldNames__ = ["filt", "data", "result", "date"]
46
+ __rapuiSerialiseType__ = T_RAPUI_PAYLOAD_ENVELOPE
47
+
48
+ vortexUuidKey = "__vortexUuid__"
49
+ vortexNameKey = "__vortexName__"
50
+
51
+ def __init__(
52
+ self,
53
+ filt: Optional[Dict] = None,
54
+ encodedPayload: Optional[str] = None,
55
+ result=None,
56
+ date: Optional[datetime] = None,
57
+ data: Optional[Any] = None,
58
+ ) -> None:
59
+ Jsonable.__init__(self)
60
+ """Constructor"""
61
+ assert not (
62
+ data and encodedPayload
63
+ ), "You can not pass encodedData and encodedPayload"
64
+
65
+ self.filt: Dict = {} if filt is None else filt
66
+ self.data: Union[Any, str, None] = data if data else encodedPayload
67
+ self.result = result
68
+ self.date: datetime = date if date else datetime.now(pytz.utc)
69
+
70
+ if isinstance(filt, str):
71
+ self.filt = {self.filt: None}
72
+
73
+ # -------------------------------------------
74
+ # PayloadEnvelope Methods
75
+
76
+ @property
77
+ def encodedPayload(self) -> Optional[str]:
78
+ # self.data could be from VortexJS/Typescript where we allow `str` data
79
+ assert self.data is None or isinstance(
80
+ self.data, str
81
+ ), "Encoded payload is not None or a string"
82
+ return self.data
83
+
84
+ @encodedPayload.setter
85
+ def encodedPayload(self, val: Optional[str]):
86
+ # This value should only be set from users of VortexPy and
87
+ # Payload.toEncodedPayload so we only allow str or None
88
+ assert val is None or isinstance(
89
+ val, str
90
+ ), "Encoded payload is not None or a string"
91
+ self.data = val
92
+
93
+ def isEmpty(self):
94
+ return (
95
+ (
96
+ not self.filt
97
+ or (
98
+ self.vortexNameKey in self.filt
99
+ and self.vortexUuidKey in self.filt
100
+ and len(self.filt) == 2
101
+ )
102
+ )
103
+ and not self.encodedPayload
104
+ and not self.result
105
+ )
106
+
107
+ # -------------------------------------------
108
+ # Decode Helper Methods
109
+
110
+ def decodePayload(self) -> Payload:
111
+ if not self.encodedPayload:
112
+ if self.result:
113
+ logger.debug(
114
+ "encodedPayload is None, but maybe there was an error: %s",
115
+ self.result,
116
+ )
117
+ raise NoPayloadException()
118
+
119
+ noMainThread()
120
+ return Payload().fromEncodedPayload(self.encodedPayload)
121
+
122
+ @deferToThreadWrapWithLogger(logger)
123
+ def decodePayloadDefer(self) -> "Payload":
124
+ return self.decodePayload()
125
+
126
+ # -------------------------------------------
127
+ # JSON Related methods
128
+ def _fromJson(self, jsonStr):
129
+ jsonDict = json.loads(jsonStr)
130
+
131
+ assert jsonDict[Jsonable.JSON_CLASS_TYPE] == self.__rapuiSerialiseType__
132
+ return self.fromJsonDict(jsonDict)
133
+
134
+ def _toJson(self) -> str:
135
+ return json.dumps(self.toJsonDict())
136
+
137
+ # -------------------------------------------
138
+ # VortexServer Message Methods
139
+ def toVortexMsg(self, base64Encode=False) -> bytes:
140
+ jsonStr = self._toJson().encode()
141
+ if base64Encode:
142
+ return b64encode(jsonStr)
143
+ return jsonStr
144
+
145
+ @deferToThreadWrapWithLogger(logger)
146
+ def toVortexMsgDefer(self, base64Encode=False) -> bytes:
147
+ return self.toVortexMsg(base64Encode=base64Encode)
148
+
149
+ def fromVortexMsg(
150
+ self,
151
+ vortexMsg: bytes,
152
+ ) -> "PayloadEnvelope":
153
+ if vortexMsg.startswith(b"{"):
154
+ jsonStr = vortexMsg.decode()
155
+ else:
156
+ jsonStr = b64decode(vortexMsg).decode()
157
+ return self._fromJson(jsonStr)
158
+
159
+ @deferToThreadWrapWithLogger(logger)
160
+ def fromVortexMsgDefer(self, vortexMsg: bytes) -> "PayloadEnvelope":
161
+ return self.fromVortexMsg(vortexMsg)
162
+
163
+ @classmethod
164
+ @deferToThreadWrapWithLogger(logger)
165
+ def base64EncodeDefer(cls, vortexMsg) -> bytes:
166
+ return base64.b64encode(vortexMsg)
@@ -0,0 +1,15 @@
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
+
10
+ rapuiServerEcho = "rapuiServerEcho"
11
+ rapuiClientEcho = "rapuiClientEcho"
12
+ rapuiVortexUuid = "rapuiVortexUuid"
13
+
14
+ plIdKey = "id"
15
+ plDeleteKey = "delete"
vortex/PayloadIO.py ADDED
@@ -0,0 +1,152 @@
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 datetime import datetime
11
+
12
+ import pytz
13
+ from twisted.internet import reactor
14
+ from twisted.internet.defer import Deferred
15
+ from twisted.python.failure import Failure
16
+
17
+ from vortex.DeferUtil import vortexLogFailure
18
+ from vortex.PayloadEnvelope import PayloadEnvelope
19
+ from vortex.VortexABC import SendVortexMsgResponseCallable
20
+
21
+ logger = logging.getLogger(name="PayloadIO")
22
+
23
+
24
+ class PayloadIO(object):
25
+ """
26
+ PayloadIO, Processes payloads received from the vortex and distributes
27
+ them to where they need to go.
28
+ """
29
+
30
+ # Singleton
31
+ _instance = None
32
+
33
+ def __new__(cls):
34
+ if not cls._instance:
35
+ cls._instance = super(PayloadIO, cls).__new__(cls)
36
+ cls._instance.__singleton_init__()
37
+ return cls._instance
38
+
39
+ def __singleton_init__(self):
40
+ self._endpoints = set()
41
+
42
+ def remove(self, endpoint):
43
+ if endpoint in self._endpoints:
44
+ self._endpoints.remove(endpoint)
45
+
46
+ def add(self, endpoint):
47
+ self._endpoints.add(endpoint)
48
+
49
+ @property
50
+ def endpoints(self):
51
+ """Endpoints property
52
+
53
+ @:return a copy of the list of endpoints
54
+ """
55
+ return list(self._endpoints)
56
+
57
+ def process(
58
+ self,
59
+ payloadEnvelope: PayloadEnvelope,
60
+ vortexUuid: str,
61
+ vortexName: str,
62
+ httpSession,
63
+ sendResponse: SendVortexMsgResponseCallable,
64
+ ):
65
+ from vortex.VortexFactory import VortexFactory
66
+
67
+ # noinspection PyProtectedMember
68
+ if VortexFactory._DEBUG_LOGGING:
69
+ logger.debug(
70
+ "Received payloadEnvelope from %s:%s for %s",
71
+ vortexName,
72
+ vortexUuid,
73
+ payloadEnvelope.filt,
74
+ )
75
+
76
+ immutableEndpoints = list(self._endpoints)
77
+ for endpoint in immutableEndpoints:
78
+ if not endpoint.check(payloadEnvelope, vortexName):
79
+ continue
80
+
81
+ reactor.callLater(
82
+ 0,
83
+ self._processLater,
84
+ endpoint,
85
+ payloadEnvelope,
86
+ vortexUuid,
87
+ vortexName,
88
+ httpSession,
89
+ sendResponse,
90
+ )
91
+
92
+ def _processLater(
93
+ self,
94
+ endpoint,
95
+ payloadEnvelope: PayloadEnvelope,
96
+ vortexUuid: str,
97
+ vortexName: str,
98
+ httpSession,
99
+ sendResponse: SendVortexMsgResponseCallable,
100
+ ):
101
+ startDate = datetime.now(pytz.utc)
102
+
103
+ def respondToException(failure):
104
+ """Respond To Exception
105
+ Putting the exception into a failure messes with the stack, hence the
106
+ common function
107
+ """
108
+ try:
109
+ sendResponse(
110
+ PayloadEnvelope(
111
+ filt=payloadEnvelope.filt,
112
+ result=str(failure.getTraceback()),
113
+ ).toVortexMsg()
114
+ )
115
+ except Exception as e:
116
+ logger.exception(e)
117
+
118
+ vortexLogFailure(failure, logger)
119
+ logger.error(payloadEnvelope.filt)
120
+
121
+ def callback(value, blocking=False):
122
+ # A blocking call taking more than 100ms is BAD
123
+ # Otherwise a call taking more than a 1s is just poor performance.
124
+ secondsTaken = (datetime.now(pytz.utc) - startDate).total_seconds()
125
+ if secondsTaken > 0.1 and blocking or secondsTaken > 2.0:
126
+ func = logger.critical if blocking else logger.debug
127
+ func(
128
+ "%s Payload endpoint took %s\npayload.filt=%s\n%s",
129
+ "BLOCKING " if blocking else "",
130
+ secondsTaken,
131
+ payloadEnvelope.filt,
132
+ endpoint,
133
+ )
134
+
135
+ try:
136
+ d = endpoint.process(
137
+ payloadEnvelope,
138
+ vortexUuid,
139
+ vortexName,
140
+ httpSession,
141
+ sendResponse,
142
+ )
143
+ if isinstance(d, Deferred):
144
+ d.addCallback(callback)
145
+ d.addErrback(respondToException)
146
+ elif hasattr(d, "addCallback"):
147
+ raise Exception("isinstance FAILED")
148
+ else:
149
+ callback(True, blocking=True)
150
+
151
+ except Exception as e:
152
+ respondToException(Failure(e))