1
2
3
4
5
6
7
8
9
10
11
12
13 import inspect
14 import logging
15 from Products.ZenUtils.jsonutils import json, unjson
16 import transaction
17 from uuid import uuid4
18
19 log = logging.getLogger('extdirect')
23
27 """
28 Encapsulation of the simple protocol used to send results and messages to
29 the front end.
30 """
31 _data = None
32 - def __init__(self, msg=None, success=True, **kwargs):
38
39 @property
42
43 @property
46
49
50 @staticmethod
51 - def exception(exception, message=None, **kwargs):
60
61 @staticmethod
62 - def fail(msg=None, **kwargs):
63 """
64 If the request has failed for a non-database-impacting reason
65 (e.g., "device already exists"), use this.
66 """
67 return DirectResponse(msg, success=False, type='error', **kwargs)
68
69 @staticmethod
72
75
77 """
78 Encapsulation of the response of a method call for Ext Direct.
79 """
80 - def __init__(self, tid, action, method, uuid):
87
89 return {
90 'tid': self.tid,
91 'type' : self.type,
92 'action': self.action,
93 'method': self.method,
94 'uuid': self.uuid,
95 'result': self.result
96 }
97
99 """
100 Basic Ext.Direct router class.
101
102 Ext.Direct allows one to create an API that communicates with a single URL,
103 which then routes requests to the appropriate method. The client-side API
104 object matches the server-side API object.
105
106 This base class parses an Ext.Direct request, which contains the name of
107 the method and any data that should be passed, and routes the data to the
108 approriate method. It then receives the output of that call and puts it
109 into the data structure expected by Ext.Direct.
110
111 Call an instance of this class with the JSON from an Ext.Direct request.
112 """
113
114 @json
116
117 body = unjson(body)
118 self._body = body
119
120 if isinstance(body, list):
121 directRequests = body
122 elif isinstance(body, dict):
123 directRequests = [body]
124 else:
125 raise DirectException("Body is not a supported type: %s" % body)
126
127 directResponses = []
128 for directRequest in directRequests:
129 directResponses.append(self._processDirectRequest(directRequest))
130
131 if len(directResponses) == 1:
132 directResponses = directResponses[0]
133
134 return directResponses
135
137 savepoint = transaction.savepoint()
138
139
140 uuid = str(uuid4())
141
142
143 action = directRequest.get('action')
144 clsname = self.__class__.__name__
145 if action != clsname:
146 raise DirectException(("Action specified in request ('%s') is"
147 " not named %s.") % (action, clsname))
148
149
150 method = directRequest.get('method')
151 if not method:
152 raise DirectException("No method specified. Is this a valid"
153 " Ext.Direct request?")
154 try:
155 _targetfn = getattr(self, method)
156 except AttributeError:
157 raise DirectException("'%s' is not the name of a method on %s" % (
158 method, clsname
159 ))
160
161
162
163 data = directRequest.get('data')
164 if not data:
165 data = {}
166 else:
167 data = data[0]
168
169 if isinstance(data, (int, basestring)):
170 data = {'id': data}
171
172
173 data = dict((str(k), v) for k,v in data.iteritems())
174 self._data = data
175 response = DirectMethodResponse(tid=directRequest['tid'], method=method, action=action, uuid=uuid)
176
177
178 try:
179 response.result = _targetfn(**data)
180 except Exception as e:
181 log.error('DirectRouter directRequest = %s', directRequest)
182 log.exception('DirectRouter suppressed the following exception (Response %s):' % response.uuid)
183 response.result = DirectResponse.exception(e)
184
185 if isinstance(response.result, DirectResponse) and response.result.type == 'exception':
186 savepoint.rollback()
187
188 return response
189
192 """
193 Turns a L{DirectRouter} subclass into JavaScript object representing the
194 config of the client-side API.
195
196 Inspects the given subclass and retrieves the names of all public methods,
197 then defines those as actions on the Ext.Direct provider, and creates the
198 JS that adds the provider.
199
200 See http://extjs.com/products/extjs/direct.php for a full explanation of
201 protocols and features of Ext.Direct.
202 """
203 - def __init__(self, routercls, url, timeout, ns=None):
204 """
205 @param routercls: A L{DirectRouter} subclass
206 @type routercls: class
207 @param url: The url at which C{routercls} is available
208 @type url: str
209 @param ns: The client-side namespace in which the provider should live.
210 The provider will be available at [ns].[routercls.__name__].
211 For example, if ns is 'Zenoss.remote' and routercls is named
212 'EventConsole', client-side code would call
213 C{Zenoss.remote.EventConsole.my_method(params, callback)}.
214 """
215 self.routercls = routercls
216 self.url = url
217 self.ns = ns
218 self.timeout = timeout
219
221 actions = []
222 for name, value in inspect.getmembers(self.routercls):
223 if name.startswith("_"):
224 continue
225 if inspect.ismethod(value):
226
227
228
229
230
231
232
233 arglen = 1
234
235 actions.append({'name':name, 'len':arglen})
236 config = {
237 'id': self.routercls.__name__,
238 'type': 'remoting',
239 'url': self.url,
240 'timeout': self.timeout,
241 'enableBuffer': 100,
242 'actions': {
243 self.routercls.__name__: actions
244 }
245 }
246 if self.ns:
247 config['namespace'] = self.ns
248 return config
249
251 """
252 Generate and return an Ext.Direct provider definition, wrapped in a
253 <script> tag and ready for inclusion in an HTML document.
254 """
255 config = self._config()
256 source = "\nExt.Direct.addProvider(%s);\n" % json(config)
257 return source.strip()
258