comparison wokkel/rsm.py @ 21:54f834e40341

tmp (wokkel/rsm): various improvments: - restored original tmp.wokkel.pubsub.PubSubService._toResponse_items - changed arguments order in RSMRequest.__init__ to have most common arguments first - added __str__ methods - better parsing/toElement - better handling of optional elements/attributes (count/index) - _toResponse_items handler RSM set elements without modifying original pubsub _toResponse_items - renamed parse to fromElement for coherency
author Goffi <goffi@goffi.org>
date Tue, 05 Jan 2016 23:20:20 +0100
parents 80f9a1a3d002
children 777b4e63fc8a
comparison
equal deleted inserted replaced
20:81f9b53ec7e4 21:54f834e40341
43 """ 43 """
44 An expected RSM element has not been found. 44 An expected RSM element has not been found.
45 """ 45 """
46 46
47 47
48 class RSMRequest(): 48 class RSMRequest(object):
49 """ 49 """
50 A Result Set Management request. 50 A Result Set Management request.
51 51
52 @ivar max_: limit on the number of retrieved items. 52 @ivar max_: limit on the number of retrieved items.
53 @itype max_: C{int} or C{unicode} 53 @itype max_: C{int} or C{unicode}
60 60
61 @ivar before: ID of the element immediately following the page. 61 @ivar before: ID of the element immediately following the page.
62 @itype before: C{unicode} 62 @itype before: C{unicode}
63 """ 63 """
64 64
65 def __init__(self, max_=10, index=None, after=None, before=None): 65 def __init__(self, max_=10, after=None, before=None, index=None):
66 max_ = int(max_) 66 self.max = int(max_)
67 assert max_ >= 0
68 self.max = max_
69 67
70 if index is not None: 68 if index is not None:
71 assert after is None and before is None 69 assert after is None and before is None
72 index = int(index) 70 index = int(index)
73 assert index >= 0
74 self.index = index 71 self.index = index
75 72
76 if after is not None: 73 if after is not None:
77 assert before is None 74 assert before is None
78 assert isinstance(after, unicode) 75 assert isinstance(after, basestring)
79 self.after = after 76 self.after = after
80 77
81 if before is not None: 78 if before is not None:
82 assert isinstance(before, unicode) 79 assert isinstance(before, basestring)
83 self.before = before 80 self.before = before
84 81
82 def __str__(self):
83 return "RSM Request: max={0.max} after={0.after} before={0.before} index={0.index}".format(self)
84
85 @classmethod 85 @classmethod
86 def parse(cls, element): 86 def fromElement(cls, element):
87 """Parse the given request element. 87 """Parse the given request element.
88 88
89 @param element: request containing a set element. 89 @param element: request containing a set element.
90 @type element: L{domish.Element} 90 @type element: L{domish.Element}
91 91
95 try: 95 try:
96 set_elt = element.elements(NS_RSM, 'set').next() 96 set_elt = element.elements(NS_RSM, 'set').next()
97 except StopIteration: 97 except StopIteration:
98 raise RSMNotFoundError() 98 raise RSMNotFoundError()
99 99
100 request = RSMRequest() 100 try:
101 for elt in set_elt.elements(): 101 before_elt = set_elt.elements(NS_RSM, 'before').next()
102 if elt.name in ('before', 'after'): 102 except StopIteration:
103 setattr(request, elt.name, ''.join(elt.children)) 103 before = None
104 elif elt.name in ('max', 'index'): 104 else:
105 setattr(request, elt.name, int(''.join(elt.children))) 105 before = unicode(before_elt)
106 106
107 if request.max is None: 107 try:
108 after_elt = set_elt.elements(NS_RSM, 'after').next()
109 except StopIteration:
110 after = None
111 else:
112 after = unicode(after_elt)
113
114 try:
115 max_elt = set_elt.elements(NS_RSM, 'max').next()
116 except StopIteration:
117 # FIXME: even if it doesn't make a lot of sense without it
118 # <max/> element is not mandatory in XEP-0059
108 raise RSMError("RSM request is missing its 'max' element") 119 raise RSMError("RSM request is missing its 'max' element")
109 120 else:
110 return request 121 try:
122 max_ = int(unicode(max_elt))
123 except ValueError:
124 raise RSMError("bad value for 'max' element")
125
126 try:
127 index_elt = set_elt.elements(NS_RSM, 'index').next()
128 except StopIteration:
129 index = None
130 else:
131 try:
132 index = int(unicode(index_elt))
133 except ValueError:
134 raise RSMError("bad value for 'index' element")
135
136 return RSMRequest(max_, after, before, index)
111 137
112 def toElement(self): 138 def toElement(self):
113 """ 139 """
114 Return the DOM representation of this RSM request. 140 Return the DOM representation of this RSM request.
115 141
116 @rtype: L{domish.Element} 142 @rtype: L{domish.Element}
117 """ 143 """
118 set_elt = domish.Element((NS_RSM, 'set')) 144 set_elt = domish.Element((NS_RSM, 'set'))
119 set_elt.addElement('max').addContent(unicode(self.max)) 145 set_elt.addElement('max', content=unicode(self.max))
120 146
121 if self.index is not None: 147 if self.index is not None:
122 set_elt.addElement('index').addContent(unicode(self.index)) 148 set_elt.addElement('index', content=unicode(self.index))
123 149
124 if self.before is not None: 150 if self.before is not None:
125 if self.before == '': # request the last page 151 if self.before == '': # request the last page
126 set_elt.addElement('before') 152 set_elt.addElement('before')
127 else: 153 else:
128 set_elt.addElement('before').addContent(self.before) 154 set_elt.addElement('before', content=self.before)
129 155
130 if self.after is not None: 156 if self.after is not None:
131 set_elt.addElement('after').addContent(self.after) 157 set_elt.addElement('after', content=self.after)
132 158
133 return set_elt 159 return set_elt
134 160
135 def render(self, element): 161 def render(self, element):
136 """Embed the DOM representation of this RSM request in the given element. 162 """Embed the DOM representation of this RSM request in the given element.
148 element.addChild(set_elt) 174 element.addChild(set_elt)
149 175
150 return set_elt 176 return set_elt
151 177
152 178
153 class RSMResponse(): 179 class RSMResponse(object):
154 """ 180 """
155 A Result Set Management response. 181 A Result Set Management response.
182
183 @ivar first: ID of the first element of the returned page.
184 @itype first: C{unicode}
185
186 @ivar last: ID of the last element of the returned page.
187 @itype last: C{unicode}
188
189 @ivar index: starting index of the returned page.
190 @itype index: C{int}
156 191
157 @ivar count: total number of items. 192 @ivar count: total number of items.
158 @itype count: C{int} 193 @itype count: C{int}
159 194
160 @ivar index: starting index of the returned page. 195 """
161 @itype index: C{int} 196
162 197 def __init__(self, first=None, last=None, index=None, count=None):
163 @ivar first: ID of the first element of the returned page. 198 if first is None:
164 @itype first: C{unicode} 199 assert last is None and index is None
165 200 if last is None:
166 @ivar last: ID of the last element of the returned page. 201 assert first is None
167 @itype last: C{unicode}
168 """
169
170 def __init__(self, count=0, index=None, first=None, last=None):
171 assert isinstance(count, int) and count >= 0
172 self.count = count
173 if index is not None:
174 assert isinstance(index, int) and index >= 0
175 assert isinstance(first, unicode)
176 assert isinstance(last, unicode)
177 else:
178 assert first is None and last is None
179 self.index = index
180 self.first = first 202 self.first = first
181 self.last = last 203 self.last = last
204 if count is not None:
205 self.count = int(count)
206 else:
207 self.count = None
208 if index is not None:
209 self.index = int(index)
210 else:
211 self.index = None
212
213 def __str__(self):
214 return "RSM Request: first={0.first} last={0.last} index={0.index} count={0.count}".format(self)
182 215
183 @classmethod 216 @classmethod
184 def parse(cls, element): 217 def fromElement(cls, element):
185 """Parse the given response element. 218 """Parse the given response element.
186 219
187 @param element: response element. 220 @param element: response element.
188 @type element: L{domish.Element} 221 @type element: L{domish.Element}
189 222
193 try: 226 try:
194 set_elt = element.elements(NS_RSM, 'set').next() 227 set_elt = element.elements(NS_RSM, 'set').next()
195 except StopIteration: 228 except StopIteration:
196 raise RSMNotFoundError() 229 raise RSMNotFoundError()
197 230
198 response = RSMResponse() 231 try:
199 for elt in list(set_elt.elements()): 232 first_elt = set_elt.elements(NS_RSM, 'first').next()
200 if elt.name in ('first', 'last'): 233 except StopIteration:
201 setattr(response, elt.name, ''.join(elt.children)) 234 first = None
202 if elt.name == 'first': 235 index = None
203 response.index = int(elt.getAttribute("index")) 236 else:
204 elif elt.name == 'count': 237 first = unicode(first_elt)
205 response.count = int(''.join(elt.children)) 238 try:
206 239 index = int(first_elt['index'])
207 if response.count is None: 240 except KeyError:
208 raise RSMError("RSM response is missing its 'count' element") 241 index = None
209 242 except ValueError:
210 return response 243 raise RSMError("bad index in RSM response")
244
245 try:
246 last_elt = set_elt.elements(NS_RSM, 'last').next()
247 except StopIteration:
248 if first is not None:
249 raise RSMError("RSM response is missing its 'last' element")
250 else:
251 last = None
252 else:
253 if first is None:
254 raise RSMError("RSM response is missing its 'first' element")
255 last = unicode(last_elt)
256
257 try:
258 count_elt = set_elt.elements(NS_RSM, 'count').next()
259 except StopIteration:
260 count = None
261 else:
262 try:
263 count = int(unicode(count_elt))
264 except ValueError:
265 raise RSMError("invalid count in RSM response")
266
267 return RSMResponse(first, last, index, count)
211 268
212 def toElement(self): 269 def toElement(self):
213 """ 270 """
214 Return the DOM representation of this RSM request. 271 Return the DOM representation of this RSM request.
215 272
216 @rtype: L{domish.Element} 273 @rtype: L{domish.Element}
217 """ 274 """
218 set_elt = domish.Element((NS_RSM, 'set')) 275 set_elt = domish.Element((NS_RSM, 'set'))
219 set_elt.addElement('count').addContent(unicode(self.count)) 276 if self.first is not None:
220 277 first_elt = set_elt.addElement('first', content=self.first)
221 if self.index is not None: 278 if self.index is not None:
222 first_elt = set_elt.addElement('first') 279 first_elt['index'] = unicode(self.index)
223 first_elt.addContent(self.first) 280
224 first_elt['index'] = unicode(self.index) 281 set_elt.addElement('last', content=self.last)
225 282
226 set_elt.addElement('last').addContent(self.last) 283 if self.count is not None:
284 set_elt.addElement('count', content=unicode(self.count))
227 285
228 return set_elt 286 return set_elt
229 287
230 def render(self, element): 288 def render(self, element):
231 """Embed the DOM representation of this RSM response in the given element. 289 """Embed the DOM representation of this RSM response in the given element.
245 303
246 @return: a dict of strings. 304 @return: a dict of strings.
247 @rtype: C{dict} binding C{unicode} to C{unicode} 305 @rtype: C{dict} binding C{unicode} to C{unicode}
248 """ 306 """
249 result = {} 307 result = {}
250 for attr in ('count', 'index', 'first', 'last'): 308 for attr in ('first', 'last', 'index', 'count'):
251 value = getattr(self, attr) 309 value = getattr(self, attr)
252 if value is not None: 310 if value is not None:
253 result[attr] = unicode(value) 311 result[attr] = unicode(value)
254 return result 312 return result
255 313
268 self._parameters = copy.deepcopy(pubsub.PubSubRequest._parameters) 326 self._parameters = copy.deepcopy(pubsub.PubSubRequest._parameters)
269 self._parameters['items'].append('rsm') 327 self._parameters['items'].append('rsm')
270 328
271 def _parse_rsm(self, verbElement): 329 def _parse_rsm(self, verbElement):
272 try: 330 try:
273 self.rsm = RSMRequest.parse(verbElement.parent) 331 self.rsm = RSMRequest.fromElement(verbElement.parent)
274 except RSMNotFoundError: 332 except RSMNotFoundError:
275 self.rsm = None 333 self.rsm = None
276 334
277 def _render_rsm(self, verbElement): 335 def _render_rsm(self, verbElement):
278 if self.rsm: 336 if self.rsm:
329 for element in iq.pubsub.items.elements(pubsub.NS_PUBSUB, 'item'): 387 for element in iq.pubsub.items.elements(pubsub.NS_PUBSUB, 'item'):
330 items.append(element) 388 items.append(element)
331 389
332 if request.rsm: 390 if request.rsm:
333 try: 391 try:
334 response = RSMResponse.parse(iq.pubsub) 392 response = RSMResponse.fromElement(iq.pubsub)
335 if response is not None: 393 if response is not None:
336 self._rsm_responses[ext_data['id']] = response 394 self._rsm_responses[ext_data['id']] = response
337 except RSMNotFoundError: # target pubsub server doesn't support RSM 395 except RSMNotFoundError: # target pubsub server doesn't support RSM
338 pass 396 pass
339 return items 397 return items
362 class PubSubService(pubsub.PubSubService): 420 class PubSubService(pubsub.PubSubService):
363 """PubSubService extension to handle RSM.""" 421 """PubSubService extension to handle RSM."""
364 422
365 _request_class = PubSubRequest 423 _request_class = PubSubRequest
366 424
367 def _toResponse_items(self, result, resource, request): 425 def _toResponse_items(self, elts, resource, request):
368 response = pubsub.PubSubService._toResponse_items(self, result, 426 # default method only manage <item/> elements
427 # but we need to add RSM set element
428 rsm_elt = None
429 for idx, elt in enumerate(reversed(elts)):
430 if elt.name == "set" and elt.uri == NS_RSM:
431 rsm_elt = elts.pop(-1-idx)
432 break
433
434 response = pubsub.PubSubService._toResponse_items(self, elts,
369 resource, request) 435 resource, request)
370 for item in result: 436 if rsm_elt is not None:
371 if isinstance(item, tuple): 437 response.addChild(rsm_elt)
372 # XXX: see sat_pubsub.pgsql_storage.LeafNode.getItemsById return value
373 item = item[0]
374 if item.name == "set":
375 response.addChild(item)
376 break
377 438
378 return response 439 return response