comparison src/browser/sat_browser/json.py @ 679:a90cc8fc9605

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 16:15:18 +0100
parents src/browser/libervia_main.py@3eb3a2c0c011 src/browser/libervia_main.py@849ffb24d5bf
children 801eb94aa869
comparison
equal deleted inserted replaced
590:1bffc4c244c3 679:a90cc8fc9605
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Libervia: a Salut à Toi frontend
5 # Copyright (C) 2011, 2012, 2013, 2014 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
30 from sat_browser.constants import Const as C
31
32
33 class LiberviaMethodProxy(object):
34 """This class manage calling for one method"""
35
36 def __init__(self, parent, method):
37 self._parent = parent
38 self._method = method
39
40 def call(self, *args, **kwargs):
41 """Method called when self._method attribue is used in JSON_PROXY_PARENT
42
43 This method manage callback/errback in kwargs, and profile(_key) removing
44 @param *args: positional arguments of self._method
45 @param **kwargs: keyword arguments of self._method
46 """
47 callback=kwargs.pop('callback', None)
48 errback=kwargs.pop('errback', None)
49
50 # as profile is linked to browser session and managed server side, we remove them
51 profile_removed = False
52 try:
53 kwargs['profile'] # FIXME: workaround for pyjamas bug: KeyError is not raised with del
54 del kwargs['profile']
55 profile_removed = True
56 except KeyError:
57 pass
58
59 try:
60 kwargs['profile_key'] # FIXME: workaround for pyjamas bug: KeyError is not raised iwith del
61 del kwargs['profile_key']
62 profile_removed = True
63 except KeyError:
64 pass
65
66 if not profile_removed and args:
67 # if profile was not in kwargs, there is most probably one in args
68 args = list(args)
69 assert isinstance(args[-1], basestring) # Detect when we want to remove a callback (or something else) instead of the profile
70 del args[-1]
71
72 if kwargs:
73 # kwargs should be empty here, we don't manage keyword arguments on bridge calls
74 log.error("kwargs is not empty after treatment on method call: kwargs={}".format(kwargs))
75
76 id_ = self._parent.callMethod(self._method, args)
77
78 # callback or errback are managed in parent LiberviaJsonProxy with call id
79 if callback is not None:
80 self._parent.cb[id_] = callback
81 if errback is not None:
82 self._parent.eb[id_] = errback
83
84
85 class LiberviaJsonProxy(JSONService.JSONService):
86
87 def __init__(self, url, methods):
88 self._serviceURL = url
89 self.methods = methods
90 JSONService.JSONService.__init__(self, url, self)
91 self.cb = {}
92 self.eb = {}
93 self._registerMethods(methods)
94
95 def _registerMethods(self, methods):
96 if methods:
97 for method in methods:
98 log.debug("Registering JSON method call [{}]".format(method))
99 setattr(self,
100 method,
101 getattr(LiberviaMethodProxy(self, method), 'call')
102 )
103
104 def callMethod(self, method, params, handler = None):
105 ret = super(LiberviaJsonProxy, self).callMethod(method, params, handler)
106 return ret
107
108 def call(self, method, cb, *args):
109 # FIXME: deprecated call method, must be removed once it's not used anymore
110 id_ = self.callMethod(method, args)
111 log.debug("call: method={} [id={}], args={}".format(method, id_, args))
112 if cb:
113 if isinstance(cb, tuple):
114 if len(cb) != 2:
115 log.error("tuple syntax for bridge.call is (callback, errback), aborting")
116 return
117 if cb[0] is not None:
118 self.cb[id_] = cb[0]
119 self.eb[id_] = cb[1]
120 else:
121 self.cb[id_] = cb
122
123 def onRemoteResponse(self, response, request_info):
124 try:
125 _cb = self.cb[request_info.id]
126 except KeyError:
127 pass
128 else:
129 # if isinstance(_cb, tuple):
130 # #we have arguments attached to the callback
131 # #we send them after the answer
132 # callback, args = _cb
133 # callback(response, *args)
134 # else:
135 # #No additional argument, we call directly the callback
136 _cb(response)
137 del self.cb[request_info.id]
138
139 try:
140 del self.eb[request_info.id]
141 except KeyError:
142 pass
143
144 def onRemoteError(self, code, errobj, request_info):
145 """def dump(obj):
146 print "\n\nDUMPING %s\n\n" % obj
147 for i in dir(obj):
148 print "%s: %s" % (i, getattr(obj,i))"""
149 try:
150 _eb = self.eb[request_info.id]
151 except KeyError:
152 if code != 0:
153 log.error("Internal server error")
154 """for o in code, error, request_info:
155 dump(o)"""
156 else:
157 if isinstance(errobj['message'], dict):
158 log.error("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString']))
159 else:
160 log.error("%s" % errobj['message'])
161 else:
162 _eb((code, errobj))
163 del self.eb[request_info.id]
164
165 try:
166 del self.cb[request_info.id]
167 except KeyError:
168 pass
169
170
171 class RegisterCall(LiberviaJsonProxy):
172 def __init__(self):
173 LiberviaJsonProxy.__init__(self, "/register_api",
174 ["isRegistered", "isConnected", "asyncConnect", "registerParams", "getMenus"])
175
176
177 class BridgeCall(LiberviaJsonProxy):
178 def __init__(self):
179 LiberviaJsonProxy.__init__(self, "/json_api",
180 ["getContacts", "addContact", "sendMessage", "sendMblog", "sendMblogComment",
181 "getMblogs", "getMassiveMblogs", "getMblogComments",
182 "getHistory", "getPresenceStatuses", "joinMUC", "mucLeave", "getRoomsJoined",
183 "getRoomsSubjects", "inviteMUC", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady",
184 "tarotGamePlayCards", "launchRadioCollective", "getMblogs", "getMblogsWithComments",
185 "getWaitingSub", "subscription", "delContact", "updateContact", "getCard",
186 "getEntityData", "getParamsUI", "asyncGetParamA", "setParam", "launchAction",
187 "disconnect", "chatStateComposing", "getNewAccountDomain", "confirmationAnswer",
188 "syntaxConvert", "getAccountDialogUI", "getMainResource", "getWaitingConf", "getEntitiesData",
189 ])
190 def __call__(self, *args, **kwargs):
191 return LiberviaJsonProxy.__call__(self, *args, **kwargs)
192
193 def getConfig(self, dummy1, dummy2): # FIXME
194 log.warning("getConfig is not implemeted in Libervia yet")
195 return ''
196
197 def isConnected(self, dummy): # FIXME
198 log.warning("isConnected is not implemeted in Libervia as for now profile is connected if session is opened")
199 return True
200
201 def getAvatarFile(self, hash_, callback=None):
202 log.warning("getAvatarFile only return hash in Libervia")
203 if callback is not None:
204 callback(hash_)
205 return hash_
206
207
208 class BridgeSignals(LiberviaJsonProxy):
209 RETRY_BASE_DELAY = 1000
210
211 def __init__(self, host):
212 self.host = host
213 self.retry_delay = self.RETRY_BASE_DELAY
214 LiberviaJsonProxy.__init__(self, "/json_signal_api",
215 ["getSignals"])
216 self._signals = {} # key: signal name, value: callback
217
218 def onRemoteResponse(self, response, request_info):
219 if self.retry_delay != self.RETRY_BASE_DELAY:
220 log.info("Connection with server restablished")
221 self.retry_delay = self.RETRY_BASE_DELAY
222 LiberviaJsonProxy.onRemoteResponse(self, response, request_info)
223
224 def onRemoteError(self, code, errobj, request_info):
225 if errobj['message'] == 'Empty Response':
226 log.warning(u"Empty reponse bridgeSignal\ncode={}\nrequest_info: id={} method={} handler={}".format(code, request_info.id, request_info.method, request_info.handler))
227 # FIXME: to check/replace by a proper session end on disconnected signal
228 # Window.getLocation().reload() # XXX: reset page in case of session ended.
229 # FIXME: Should be done more properly without hard reload
230 LiberviaJsonProxy.onRemoteError(self, code, errobj, request_info)
231 #we now try to reconnect
232 if isinstance(errobj['message'], dict) and errobj['message']['faultCode'] == 0:
233 Window.alert('You are not allowed to connect to server')
234 else:
235 def _timerCb(timer):
236 log.info("Trying to reconnect to server...")
237 self.getSignals(callback=self.signalHandler)
238 # TODO: display a notification to user
239 log.warning("Lost connection, trying to reconnect in {} s".format(self.retry_delay/1000))
240 Timer(notify=_timerCb).schedule(self.retry_delay)
241 self.retry_delay *= 2
242
243 def register(self, name, callback, with_profile=True):
244 """Register a signal
245
246 @param: name of the signal to register
247 @param callback: method to call
248 @param with_profile: True if the original bridge method need a profile
249 """
250 log.debug("Registering signal {}".format(name))
251 if name in self._signals:
252 log.error("Trying to register and already registered signal ({})".format(name))
253 else:
254 self._signals[name] = (callback, with_profile)
255
256 def signalHandler(self, signal_data):
257 self.getSignals(callback=self.signalHandler)
258 if len(signal_data) == 1:
259 signal_data.append([])
260 log.debug("Got signal ==> name: %s, params: %s" % (signal_data[0], signal_data[1]))
261 name, args = signal_data
262 try:
263 callback, with_profile = self._signals[name]
264 except KeyError:
265 log.warning("Ignoring {} signal: no handler registered !".format(name))
266 return
267 if with_profile:
268 args.append(C.PROF_KEY_NONE)
269 callback(*args)