1
2
3
4
5
6
7
8
9
10
11
12
13 import inspect
14 import logging
15 log = logging.getLogger('extdirect')
19
20 from Products.ZenUtils.jsonutils import json, unjson
24 """
25 Encapsulation of the simple protocol used to send results and messages to
26 the front end.
27 """
28 _data = None
29 - def __init__(self, msg=None, success=True, **kwargs):
35
36 @property
39
42
43 @staticmethod
44 - def fail(msg=None, **kwargs):
46
47 @staticmethod
50
53 """
54 Basic Ext.Direct router class.
55
56 Ext.Direct allows one to create an API that communicates with a single URL,
57 which then routes requests to the appropriate method. The client-side API
58 object matches the server-side API object.
59
60 This base class parses an Ext.Direct request, which contains the name of
61 the method and any data that should be passed, and routes the data to the
62 approriate method. It then receives the output of that call and puts it
63 into the data structure expected by Ext.Direct.
64
65 Call an instance of this class with the JSON from an Ext.Direct request.
66 """
68 """
69 """
70
71 body = unjson(body)
72 self._body = body
73
74 if isinstance(body, list):
75 directRequests = body
76 elif isinstance(body, dict):
77 directRequests = [body]
78 else:
79 raise DirectException("Body is not a supported type: %s" % body)
80
81 directResponses = []
82 for directRequest in directRequests:
83 directResponses.append(self._processDirectRequest(directRequest))
84
85 if len(directResponses) == 1:
86 directResponses = directResponses[0]
87
88 return json(directResponses)
89
91
92
93 action = directRequest.get('action')
94 clsname = self.__class__.__name__
95 if action != clsname:
96 raise DirectException(("Action specified in request ('%s') is"
97 " not named %s.") % (action, clsname))
98
99
100 method = directRequest.get('method')
101 if not method:
102 raise DirectException("No method specified. Is this a valid"
103 " Ext.Direct request?")
104 try:
105 _targetfn = getattr(self, method)
106 except AttributeError:
107 raise DirectException("'%s' is not the name of a method on %s" % (
108 method, clsname
109 ))
110
111
112
113 data = directRequest.get('data')
114 if not data:
115 data = {}
116 else:
117 data = data[0]
118
119 if isinstance(data, (int, basestring)):
120 data = {'id': data}
121
122
123 data = dict((str(k), v) for k,v in data.iteritems())
124 self._data = data
125
126
127 try:
128 result = _targetfn(**data)
129 except Exception, e:
130 log.exception(e)
131 message = e.__class__.__name__ + ' ' + str(e)
132 return {
133 'type':'exception',
134 'message':message
135 }
136
137 if isinstance(result, DirectResponse):
138 result = result.data
139
140 return {
141 'type':'rpc',
142 'tid': directRequest['tid'],
143 'action': action,
144 'method': method,
145 'result': result
146 }
147
150 """
151 Turns a L{DirectRouter} subclass into JavaScript object representing the
152 config of the client-side API.
153
154 Inspects the given subclass and retrieves the names of all public methods,
155 then defines those as actions on the Ext.Direct provider, and creates the
156 JS that adds the provider.
157
158 See http://extjs.com/products/extjs/direct.php for a full explanation of
159 protocols and features of Ext.Direct.
160 """
161 - def __init__(self, routercls, url, timeout, ns=None):
162 """
163 @param routercls: A L{DirectRouter} subclass
164 @type routercls: class
165 @param url: The url at which C{routercls} is available
166 @type url: str
167 @param ns: The client-side namespace in which the provider should live.
168 The provider will be available at [ns].[routercls.__name__].
169 For example, if ns is 'Zenoss.remote' and routercls is named
170 'EventConsole', client-side code would call
171 C{Zenoss.remote.EventConsole.my_method(params, callback)}.
172 """
173 self.routercls = routercls
174 self.url = url
175 self.ns = ns
176 self.timeout = timeout
177
179 actions = []
180 for name, value in inspect.getmembers(self.routercls):
181 if name.startswith("_"):
182 continue
183 if inspect.ismethod(value):
184
185
186
187
188
189
190
191 arglen = 1
192
193 actions.append({'name':name, 'len':arglen})
194 config = {
195 'id': self.routercls.__name__,
196 'type': 'remoting',
197 'url': self.url,
198 'timeout': self.timeout,
199 'enableBuffer': 100,
200 'actions': {
201 self.routercls.__name__: actions
202 }
203 }
204 if self.ns:
205 config['namespace'] = self.ns
206 return config
207
209 """
210 Generate and return an Ext.Direct provider definition, wrapped in a
211 <script> tag and ready for inclusion in an HTML document.
212 """
213 config = self._config()
214 source = "\nExt.Direct.addProvider(%s);\n" % json(config)
215 return source.strip()
216