Mercurial > libervia-web
comparison browser/sat_browser/json.py @ 1124:28e3eb3bb217
files reorganisation and installation rework:
- files have been reorganised to follow other SàT projects and usual Python organisation (no more "/src" directory)
- VERSION file is now used, as for other SàT projects
- replace the overcomplicated setup.py be a more sane one. Pyjamas part is not compiled anymore by setup.py, it must be done separatly
- removed check for data_dir if it's empty
- installation tested working in virtual env
- libervia launching script is now in bin/libervia
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 25 Aug 2018 17:59:48 +0200 |
parents | src/browser/sat_browser/json.py@63a4b8fe9782 |
children | 2af117bfe6cc |
comparison
equal
deleted
inserted
replaced
1123:63a4b8fe9782 | 1124:28e3eb3bb217 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2011-2018 Jérôme Poisson <goffi@goffi.org> | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 ### logging configuration ### | |
22 from sat.core.log import getLogger | |
23 log = getLogger(__name__) | |
24 ### | |
25 | |
26 from pyjamas.Timer import Timer | |
27 from pyjamas import Window | |
28 from pyjamas import JSONService | |
29 import time | |
30 from sat_browser import main_panel | |
31 | |
32 from sat_browser.constants import Const as C | |
33 import random | |
34 | |
35 | |
36 class LiberviaMethodProxy(object): | |
37 """This class manage calling for one method""" | |
38 | |
39 def __init__(self, parent, method): | |
40 self._parent = parent | |
41 self._method = method | |
42 | |
43 def call(self, *args, **kwargs): | |
44 """Method called when self._method attribue is used in JSON_PROXY_PARENT | |
45 | |
46 This method manage callback/errback in kwargs, and profile(_key) removing | |
47 @param *args: positional arguments of self._method | |
48 @param **kwargs: keyword arguments of self._method | |
49 """ | |
50 callback=kwargs.pop('callback', None) | |
51 errback=kwargs.pop('errback', None) | |
52 | |
53 # as profile is linked to browser session and managed server side, we remove them | |
54 profile_removed = False | |
55 try: | |
56 kwargs['profile'] # FIXME: workaround for pyjamas bug: KeyError is not raised with del | |
57 del kwargs['profile'] | |
58 profile_removed = True | |
59 except KeyError: | |
60 pass | |
61 | |
62 try: | |
63 kwargs['profile_key'] # FIXME: workaround for pyjamas bug: KeyError is not raised iwith del | |
64 del kwargs['profile_key'] | |
65 profile_removed = True | |
66 except KeyError: | |
67 pass | |
68 | |
69 if not profile_removed and args: | |
70 # if profile was not in kwargs, there is most probably one in args | |
71 args = list(args) | |
72 assert isinstance(args[-1], basestring) # Detect when we want to remove a callback (or something else) instead of the profile | |
73 del args[-1] | |
74 | |
75 if kwargs: | |
76 # kwargs should be empty here, we don't manage keyword arguments on bridge calls | |
77 log.error(u"kwargs is not empty after treatment on method call: kwargs={}".format(kwargs)) | |
78 | |
79 id_ = self._parent.callMethod(self._method, args) | |
80 | |
81 # callback or errback are managed in parent LiberviaJsonProxy with call id | |
82 if callback is not None: | |
83 self._parent.cb[id_] = callback | |
84 if errback is not None: | |
85 self._parent.eb[id_] = errback | |
86 | |
87 | |
88 class LiberviaJsonProxy(JSONService.JSONService): | |
89 | |
90 def __init__(self, url, methods): | |
91 self._serviceURL = url | |
92 self.methods = methods | |
93 JSONService.JSONService.__init__(self, url, self) | |
94 self.cb = {} | |
95 self.eb = {} | |
96 self._registerMethods(methods) | |
97 | |
98 def _registerMethods(self, methods): | |
99 if methods: | |
100 for method in methods: | |
101 log.debug(u"Registering JSON method call [{}]".format(method)) | |
102 setattr(self, | |
103 method, | |
104 getattr(LiberviaMethodProxy(self, method), 'call') | |
105 ) | |
106 | |
107 def callMethod(self, method, params, handler = None): | |
108 ret = super(LiberviaJsonProxy, self).callMethod(method, params, handler) | |
109 return ret | |
110 | |
111 def call(self, method, cb, *args): | |
112 # FIXME: deprecated call method, must be removed once it's not used anymore | |
113 id_ = self.callMethod(method, args) | |
114 log.debug(u"call: method={} [id={}], args={}".format(method, id_, args)) | |
115 if cb: | |
116 if isinstance(cb, tuple): | |
117 if len(cb) != 2: | |
118 log.error("tuple syntax for bridge.call is (callback, errback), aborting") | |
119 return | |
120 if cb[0] is not None: | |
121 self.cb[id_] = cb[0] | |
122 self.eb[id_] = cb[1] | |
123 else: | |
124 self.cb[id_] = cb | |
125 | |
126 def onRemoteResponse(self, response, request_info): | |
127 try: | |
128 _cb = self.cb[request_info.id] | |
129 except KeyError: | |
130 pass | |
131 else: | |
132 _cb(response) | |
133 del self.cb[request_info.id] | |
134 | |
135 try: | |
136 del self.eb[request_info.id] | |
137 except KeyError: | |
138 pass | |
139 | |
140 def onRemoteError(self, code, errobj, request_info): | |
141 """def dump(obj): | |
142 print "\n\nDUMPING %s\n\n" % obj | |
143 for i in dir(obj): | |
144 print "%s: %s" % (i, getattr(obj,i))""" | |
145 try: | |
146 _eb = self.eb[request_info.id] | |
147 except KeyError: | |
148 if code != 0: | |
149 log.error("Internal server error") | |
150 """for o in code, error, request_info: | |
151 dump(o)""" | |
152 else: | |
153 if isinstance(errobj['message'], dict): | |
154 log.error(u"Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString'])) | |
155 else: | |
156 log.error(u"%s" % errobj['message']) | |
157 else: | |
158 _eb((code, errobj)) | |
159 del self.eb[request_info.id] | |
160 | |
161 try: | |
162 del self.cb[request_info.id] | |
163 except KeyError: | |
164 pass | |
165 | |
166 | |
167 class RegisterCall(LiberviaJsonProxy): | |
168 def __init__(self): | |
169 LiberviaJsonProxy.__init__(self, "/register_api", | |
170 ["getSessionMetadata", "isConnected", "connect", "registerParams", "menusGet"]) | |
171 | |
172 | |
173 class BridgeCall(LiberviaJsonProxy): | |
174 def __init__(self): | |
175 LiberviaJsonProxy.__init__(self, "/json_api", | |
176 ["getContacts", "addContact", "messageSend", | |
177 "psNodeDelete", "psRetractItem", "psRetractItems", | |
178 "mbSend", "mbRetract", "mbGet", "mbGetFromMany", "mbGetFromManyRTResult", | |
179 "mbGetFromManyWithComments", "mbGetFromManyWithCommentsRTResult", | |
180 "historyGet", "getPresenceStatuses", "joinMUC", "mucLeave", "mucGetRoomsJoined", | |
181 "inviteMUC", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady", | |
182 "tarotGamePlayCards", "launchRadioCollective", | |
183 "getWaitingSub", "subscription", "delContact", "updateContact", "avatarGet", | |
184 "getEntityData", "getParamsUI", "asyncGetParamA", "setParam", "launchAction", | |
185 "disconnect", "chatStateComposing", "getNewAccountDomain", | |
186 "syntaxConvert", "getAccountDialogUI", "getMainResource", "getEntitiesData", | |
187 "getVersion", "getLiberviaVersion", "mucGetDefaultService", "getFeatures", | |
188 "namespacesGet", | |
189 ]) | |
190 | |
191 def __call__(self, *args, **kwargs): | |
192 return LiberviaJsonProxy.__call__(self, *args, **kwargs) | |
193 | |
194 def getConfig(self, dummy1, dummy2): # FIXME | |
195 log.warning("getConfig is not implemeted in Libervia yet") | |
196 return '' | |
197 | |
198 def isConnected(self, dummy, callback): # FIXME | |
199 log.warning("isConnected is not implemeted in Libervia as for now profile is connected if session is opened") | |
200 callback(True) | |
201 | |
202 def encryptionPluginsGet(self, callback, errback): | |
203 """e2e encryption have no sense if made on backend, so we ignore this call""" | |
204 callback([]) | |
205 | |
206 def bridgeConnect(self, callback, errback): | |
207 callback() | |
208 | |
209 | |
210 class BridgeSignals(LiberviaJsonProxy): | |
211 | |
212 def __init__(self, host): | |
213 self.host = host | |
214 self.retry_time = None | |
215 self.retry_nb = 0 | |
216 self.retry_warning = None | |
217 self.retry_timer = None | |
218 LiberviaJsonProxy.__init__(self, "/json_signal_api", | |
219 ["getSignals"]) | |
220 self._signals = {} # key: signal name, value: callback | |
221 | |
222 def onRemoteResponse(self, response, request_info): | |
223 if self.retry_time: | |
224 log.info("Connection with server restablished") | |
225 self.retry_nb = 0 | |
226 self.retry_time = None | |
227 LiberviaJsonProxy.onRemoteResponse(self, response, request_info) | |
228 | |
229 def onRemoteError(self, code, errobj, request_info): | |
230 if errobj['message'] == 'Empty Response': | |
231 log.warning(u"Empty reponse bridgeSignal\ncode={}\nrequest_info: id={} method={} handler={}".format(code, request_info.id, request_info.method, request_info.handler)) | |
232 # FIXME: to check/replace by a proper session end on disconnected signal | |
233 # Window.getLocation().reload() # XXX: reset page in case of session ended. | |
234 # FIXME: Should be done more properly without hard reload | |
235 LiberviaJsonProxy.onRemoteError(self, code, errobj, request_info) | |
236 #we now try to reconnect | |
237 if isinstance(errobj['message'], dict) and errobj['message']['faultCode'] == 0: | |
238 Window.alert('You are not allowed to connect to server') | |
239 else: | |
240 def _timerCb(dummy): | |
241 current = time.time() | |
242 if current > self.retry_time: | |
243 msg = "Trying to reconnect to server..." | |
244 log.info(msg) | |
245 self.retry_warning.showWarning("INFO", msg) | |
246 self.retry_timer.cancel() | |
247 self.retry_warning = self.retry_timer = None | |
248 self.getSignals(callback=self.signalHandler, profile=None) | |
249 else: | |
250 remaining = int(self.retry_time - current) | |
251 msg_html = u"Connection with server lost. Retrying in <strong>{}</strong> s".format(remaining) | |
252 self.retry_warning.showWarning("WARNING", msg_html, None) | |
253 | |
254 if self.retry_nb < 3: | |
255 retry_delay = 1 | |
256 elif self.retry_nb < 10: | |
257 retry_delay = random.randint(1,10) | |
258 else: | |
259 retry_delay = random.randint(1,60) | |
260 self.retry_nb += 1 | |
261 log.warning(u"Lost connection, trying to reconnect in {} s (try #{})".format(retry_delay, self.retry_nb)) | |
262 self.retry_time = time.time() + retry_delay | |
263 self.retry_warning = main_panel.WarningPopup() | |
264 self.retry_timer = Timer(notify=_timerCb) | |
265 self.retry_timer.scheduleRepeating(1000) | |
266 _timerCb(None) | |
267 | |
268 def register_signal(self, name, callback, with_profile=True): | |
269 """Register a signal | |
270 | |
271 @param: name of the signal to register | |
272 @param callback: method to call | |
273 @param with_profile: True if the original bridge method need a profile | |
274 """ | |
275 log.debug(u"Registering signal {}".format(name)) | |
276 if name in self._signals: | |
277 log.error(u"Trying to register and already registered signal ({})".format(name)) | |
278 else: | |
279 self._signals[name] = (callback, with_profile) | |
280 | |
281 def signalHandler(self, signal_data): | |
282 self.getSignals(callback=self.signalHandler, profile=None) | |
283 if len(signal_data) == 1: | |
284 signal_data.append([]) | |
285 log.debug(u"Got signal ==> name: %s, params: %s" % (signal_data[0], signal_data[1])) | |
286 name, args = signal_data | |
287 try: | |
288 callback, with_profile = self._signals[name] | |
289 except KeyError: | |
290 log.warning(u"Ignoring {} signal: no handler registered !".format(name)) | |
291 return | |
292 if with_profile: | |
293 args.append(C.PROF_KEY_NONE) | |
294 if not self.host._profile_plugged: | |
295 log.debug("Profile is not plugged, we cache the signal") | |
296 self.host.signals_cache[C.PROF_KEY_NONE].append((name, callback, args, {})) | |
297 else: | |
298 callback(*args) |