source:
ralphm-patches/pubsub_request.patch
@
13:42b22edb29bb
Last change on this file since 13:42b22edb29bb was 13:42b22edb29bb, checked in by Ralph Meijer <ralphm@…>, 13 years ago | |
---|---|
File size: 93.9 KB |
-
wokkel/generic.py
Add PubSubRequest that parses requests and replaces separate parameters. The publish-subscribe protocol has a large number of optional features. Adding support for a feature often means more information needs to be passed, and required changing the signature of the methods that need to be overridden by adaptors. This change adds the new PubSubRequest class that features a fromElement method that creates a new instance, parses a request and then fills the instance attributes where appropriate. This instance is then the only argument that is passed to the corresponding handler methods. diff -r e35e291de060 wokkel/generic.py
a b 10 10 from zope.interface import implements 11 11 12 12 from twisted.internet import defer, protocol 13 from twisted.words.protocols.jabber import error, xmlstream13 from twisted.words.protocols.jabber import error, jid, xmlstream 14 14 from twisted.words.protocols.jabber.xmlstream import toResponse 15 15 from twisted.words.xish import domish, utility 16 16 … … 162 162 self.sink.send = lambda obj: self.source.dispatch(obj) 163 163 164 164 165 166 class Stanza(object): 167 """ 168 Abstract representation of a stanza. 169 170 @ivar sender: The sending entity. 171 @type sender: L{jid.JID} 172 @ivar recipient: The receiving entity. 173 @type recipient: L{jid.JID} 174 """ 175 176 sender = None 177 recipient = None 178 stanzaType = None 179 180 @classmethod 181 def fromElement(Class, element): 182 stanza = Class() 183 stanza.parseElement(element) 184 return stanza 185 186 187 def parseElement(self, element): 188 self.sender = jid.internJID(element['from']) 189 if element.hasAttribute('from'): 190 self.sender = jid.internJID(element['from']) 191 if element.hasAttribute('to'): 192 self.recipient = jid.internJID(element['to']) 193 self.stanzaType = element.getAttribute('type') 194 195 165 196 class DeferredXmlStreamFactory(BootstrapMixin, protocol.ClientFactory): 166 197 protocol = xmlstream.XmlStream 167 198 -
wokkel/pubsub.py
diff -r e35e291de060 wokkel/pubsub.py
a b 16 16 from twisted.words.protocols.jabber import jid, error, xmlstream 17 17 from twisted.words.xish import domish 18 18 19 from wokkel import disco, data_form, shim19 from wokkel import disco, data_form, generic, shim 20 20 from wokkel.subprotocols import IQHandlerMixin, XMPPHandler 21 21 from wokkel.iwokkel import IPubSubClient, IPubSubService 22 22 … … 31 31 NS_PUBSUB_OWNER = NS_PUBSUB + "#owner" 32 32 NS_PUBSUB_NODE_CONFIG = NS_PUBSUB + "#node_config" 33 33 NS_PUBSUB_META_DATA = NS_PUBSUB + "#meta-data" 34 NS_PUBSUB_SUBSCRIBE_OPTIONS = NS_PUBSUB + "#subscribe_options" 34 35 35 # In publish-subscribe namespace XPath query selector. 36 IN_NS_PUBSUB = '[@xmlns="' + NS_PUBSUB + '"]' 37 IN_NS_PUBSUB_OWNER = '[@xmlns="' + NS_PUBSUB_OWNER + '"]' 38 39 # Publish-subscribe XPath queries 40 PUBSUB_ELEMENT = '/pubsub' + IN_NS_PUBSUB 41 PUBSUB_OWNER_ELEMENT = '/pubsub' + IN_NS_PUBSUB_OWNER 42 PUBSUB_GET = IQ_GET + PUBSUB_ELEMENT 43 PUBSUB_SET = IQ_SET + PUBSUB_ELEMENT 44 PUBSUB_OWNER_GET = IQ_GET + PUBSUB_OWNER_ELEMENT 45 PUBSUB_OWNER_SET = IQ_SET + PUBSUB_OWNER_ELEMENT 46 47 # Publish-subscribe command XPath queries 48 PUBSUB_PUBLISH = PUBSUB_SET + '/publish' + IN_NS_PUBSUB 49 PUBSUB_CREATE = PUBSUB_SET + '/create' + IN_NS_PUBSUB 50 PUBSUB_SUBSCRIBE = PUBSUB_SET + '/subscribe' + IN_NS_PUBSUB 51 PUBSUB_UNSUBSCRIBE = PUBSUB_SET + '/unsubscribe' + IN_NS_PUBSUB 52 PUBSUB_OPTIONS_GET = PUBSUB_GET + '/options' + IN_NS_PUBSUB 53 PUBSUB_OPTIONS_SET = PUBSUB_SET + '/options' + IN_NS_PUBSUB 54 PUBSUB_DEFAULT = PUBSUB_OWNER_GET + '/default' + IN_NS_PUBSUB_OWNER 55 PUBSUB_CONFIGURE_GET = PUBSUB_OWNER_GET + '/configure' + IN_NS_PUBSUB_OWNER 56 PUBSUB_CONFIGURE_SET = PUBSUB_OWNER_SET + '/configure' + IN_NS_PUBSUB_OWNER 57 PUBSUB_SUBSCRIPTIONS = PUBSUB_GET + '/subscriptions' + IN_NS_PUBSUB 58 PUBSUB_AFFILIATIONS = PUBSUB_GET + '/affiliations' + IN_NS_PUBSUB 59 PUBSUB_AFFILIATIONS_GET = PUBSUB_OWNER_GET + '/affiliations' + \ 60 IN_NS_PUBSUB_OWNER 61 PUBSUB_AFFILIATIONS_SET = PUBSUB_OWNER_SET + '/affiliations' + \ 62 IN_NS_PUBSUB_OWNER 63 PUBSUB_SUBSCRIPTIONS_GET = PUBSUB_OWNER_GET + '/subscriptions' + \ 64 IN_NS_PUBSUB_OWNER 65 PUBSUB_SUBSCRIPTIONS_SET = PUBSUB_OWNER_SET + '/subscriptions' + \ 66 IN_NS_PUBSUB_OWNER 67 PUBSUB_ITEMS = PUBSUB_GET + '/items' + IN_NS_PUBSUB 68 PUBSUB_RETRACT = PUBSUB_SET + '/retract' + IN_NS_PUBSUB 69 PUBSUB_PURGE = PUBSUB_OWNER_SET + '/purge' + IN_NS_PUBSUB_OWNER 70 PUBSUB_DELETE = PUBSUB_OWNER_SET + '/delete' + IN_NS_PUBSUB_OWNER 36 # XPath to match pubsub requests 37 PUBSUB_REQUEST = '/iq[@type="get" or @type="set"]/' + \ 38 'pubsub[@xmlns="' + NS_PUBSUB + '" or ' + \ 39 '@xmlns="' + NS_PUBSUB_OWNER + '"]' 71 40 72 41 class SubscriptionPending(Exception): 73 42 """ … … 98 67 99 68 100 69 101 class BadRequest( PubSubError):70 class BadRequest(error.StanzaError): 102 71 """ 103 72 Bad request stanza error. 104 73 """ 105 74 def __init__(self, pubsubCondition=None, text=None): 106 PubSubError.__init__(self, 'bad-request', pubsubCondition, text) 75 if pubsubCondition: 76 appCondition = domish.Element((NS_PUBSUB_ERRORS, pubsubCondition)) 77 else: 78 appCondition = None 79 error.StanzaError.__init__(self, 'bad-request', 80 text=text, 81 appCondition=appCondition) 107 82 108 83 109 84 … … 167 142 168 143 169 144 170 class _PubSubRequest(xmlstream.IQ):145 class PubSubRequest(generic.Stanza): 171 146 """ 172 Publishsubscribe request.147 A publish-subscribe request. 173 148 174 @ivar verb: Request verb 175 @type verb: C{str} 176 @ivar namespace: Request namespace. 177 @type namespace: C{str} 178 @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'} 179 @type method: C{str} 180 @ivar command: Command element of the request. This is the direct child of 181 the C{pubsub} element in the C{namespace} with the name 182 C{verb}. 149 The set of instance variables used depends on the type of request. If 150 a variable is not applicable or not passed in the request, its value is 151 C{None}. 152 153 @ivar verb: The type of publish-subscribe request. See L{_requestVerbMap}. 154 @type verb: C{str}. 155 156 @ivar affiliations: Affiliations to be modified. 157 @type affiliations: C{set} 158 @ivar items: The items to be published, as L{domish.Element}s. 159 @type items: C{list} 160 @ivar itemIdentifiers: Identifiers of the items to be retrieved or 161 retracted. 162 @type itemIdentifiers: C{set} 163 @ivar maxItems: Maximum number of items to retrieve. 164 @type maxItems: C{int}. 165 @ivar nodeIdentifier: Identifier of the node the request is about. 166 @type nodeIdentifier: C{unicode} 167 @ivar nodeType: The type of node that should be created, or for which the 168 configuration is retrieved. C{'leaf'} or C{'collection'}. 169 @type nodeType: C{str} 170 @ivar options: Configurations options for nodes, subscriptions and publish 171 requests. 172 @type options: L{data_form.Form} 173 @ivar subscriber: The subscribing entity. 174 @type subscriber: L{JID} 175 @ivar subscriptionIdentifier: Identifier for a specific subscription. 176 @type subscriptionIdentifier: C{unicode} 177 @ivar subscriptions: Subscriptions to be modified, as a set of 178 L{Subscription}. 179 @type subscriptions: C{set} 183 180 """ 184 181 185 def __init__(self, xs, verb, namespace=NS_PUBSUB, method='set'): 186 xmlstream.IQ.__init__(self, xs, method) 187 self.addElement((namespace, 'pubsub')) 182 verb = None 188 183 189 self.command = self.pubsub.addElement(verb) 184 affiliations = None 185 items = None 186 itemIdentifiers = None 187 maxItems = None 188 nodeIdentifier = None 189 nodeType = None 190 options = None 191 subscriber = None 192 subscriptionIdentifier = None 193 subscriptions = None 190 194 195 # Map request iq type and subelement name to request verb 196 _requestVerbMap = { 197 ('set', NS_PUBSUB, 'publish'): 'publish', 198 ('set', NS_PUBSUB, 'subscribe'): 'subscribe', 199 ('set', NS_PUBSUB, 'unsubscribe'): 'unsubscribe', 200 ('get', NS_PUBSUB, 'options'): 'optionsGet', 201 ('set', NS_PUBSUB, 'options'): 'optionsSet', 202 ('get', NS_PUBSUB, 'subscriptions'): 'subscriptions', 203 ('get', NS_PUBSUB, 'affiliations'): 'affiliations', 204 ('set', NS_PUBSUB, 'create'): 'create', 205 ('get', NS_PUBSUB_OWNER, 'default'): 'default', 206 ('get', NS_PUBSUB_OWNER, 'configure'): 'configureGet', 207 ('set', NS_PUBSUB_OWNER, 'configure'): 'configureSet', 208 ('get', NS_PUBSUB, 'items'): 'items', 209 ('set', NS_PUBSUB, 'retract'): 'retract', 210 ('set', NS_PUBSUB_OWNER, 'purge'): 'purge', 211 ('set', NS_PUBSUB_OWNER, 'delete'): 'delete', 212 ('get', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsGet', 213 ('set', NS_PUBSUB_OWNER, 'affiliations'): 'affiliationsSet', 214 ('get', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsGet', 215 ('set', NS_PUBSUB_OWNER, 'subscriptions'): 'subscriptionsSet', 216 } 191 217 192 def send(self, to): 218 # Map request verb to request iq type and subelement name 219 _verbRequestMap = dict(((v, k) for k, v in _requestVerbMap.iteritems())) 220 221 # Map request verb to parameter handler names 222 _parameters = { 223 'publish': ['node', 'items'], 224 'subscribe': ['nodeOrEmpty', 'jid'], 225 'unsubscribe': ['nodeOrEmpty', 'jid'], 226 'optionsGet': ['nodeOrEmpty', 'jid'], 227 'optionsSet': ['nodeOrEmpty', 'jid', 'options'], 228 'subscriptions': [], 229 'affiliations': [], 230 'create': ['nodeOrNone'], 231 'default': ['default'], 232 'configureGet': ['nodeOrEmpty'], 233 'configureSet': ['nodeOrEmpty', 'configure'], 234 'items': ['node', 'maxItems', 'itemIdentifiers'], 235 'retract': ['node', 'itemIdentifiers'], 236 'purge': ['node'], 237 'delete': ['node'], 238 'affiliationsGet': [], 239 'affiliationsSet': [], 240 'subscriptionsGet': [], 241 'subscriptionsSet': [], 242 } 243 244 def __init__(self, verb=None): 245 self.verb = verb 246 247 248 @staticmethod 249 def _findForm(element, formNamespace): 193 250 """ 194 Send out request.251 Find a Data Form. 195 252 196 Extends L{xmlstream.IQ.send} by requiring the C{to} parameter to be 197 a L{JID} instance. 253 Look for an element that represents a Data Form with the specified 254 form namespace as a child element of the given element. 255 """ 256 if not element: 257 return None 198 258 199 @param to: Entity to send the request to. 200 @type to: L{JID} 259 form = None 260 for child in element.elements(): 261 try: 262 form = data_form.Form.fromElement(child) 263 except data_form.Error: 264 continue 265 266 if form.formNamespace != NS_PUBSUB_NODE_CONFIG: 267 continue 268 269 return form 270 271 272 def _parse_node(self, verbElement): 201 273 """ 202 destination = to.full() 203 return xmlstream.IQ.send(self, destination) 274 Parse the required node identifier out of the verbElement. 275 """ 276 try: 277 self.nodeIdentifier = verbElement["node"] 278 except KeyError: 279 raise BadRequest('nodeid-required') 280 281 282 def _render_node(self, verbElement): 283 """ 284 Render the required node identifier on the verbElement. 285 """ 286 if not self.nodeIdentifier: 287 raise Exception("Node identifier is required") 288 289 verbElement['node'] = self.nodeIdentifier 290 291 292 def _parse_nodeOrEmpty(self, verbElement): 293 """ 294 Parse the node identifier out of the verbElement. May be empty. 295 """ 296 self.nodeIdentifier = verbElement.getAttribute("node", '') 297 298 299 def _render_nodeOrEmpty(self, verbElement): 300 """ 301 Render the node identifier on the verbElement. May be empty. 302 """ 303 if self.nodeIdentifier: 304 verbElement['node'] = self.nodeIdentifier 305 306 307 def _parse_nodeOrNone(self, verbElement): 308 """ 309 Parse the optional node identifier out of the verbElement. 310 """ 311 self.nodeIdentifier = verbElement.getAttribute("node") 312 313 314 def _render_nodeOrNone(self, verbElement): 315 """ 316 Render the optional node identifier on the verbElement. 317 """ 318 if self.nodeIdentifier: 319 verbElement['node'] = self.nodeIdentifier 320 321 322 def _parse_items(self, verbElement): 323 """ 324 Parse items out of the verbElement for publish requests. 325 """ 326 self.items = [] 327 for element in verbElement.elements(): 328 if element.uri == NS_PUBSUB and element.name == 'item': 329 self.items.append(element) 330 331 332 def _render_items(self, verbElement): 333 """ 334 Render items into the verbElement for publish requests. 335 """ 336 if self.items: 337 for item in self.items: 338 verbElement.addChild(item) 339 340 341 def _parse_jid(self, verbElement): 342 """ 343 Parse subscriber out of the verbElement for un-/subscribe requests. 344 """ 345 try: 346 self.subscriber = jid.internJID(verbElement["jid"]) 347 except KeyError: 348 raise BadRequest('jid-required') 349 350 351 def _render_jid(self, verbElement): 352 """ 353 Render subscriber into the verbElement for un-/subscribe requests. 354 """ 355 verbElement['jid'] = self.subscriber.full() 356 357 358 def _parse_default(self, verbElement): 359 """ 360 Parse node type out of a request for the default node configuration. 361 """ 362 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG) 363 if form and form.formType == 'submit': 364 values = form.getValues() 365 self.nodeType = values.get('pubsub#node_type', 'leaf') 366 else: 367 self.nodeType = 'leaf' 368 369 370 def _parse_configure(self, verbElement): 371 """ 372 Parse options out of a request for setting the node configuration. 373 """ 374 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_NODE_CONFIG) 375 if form: 376 if form.formType == 'submit': 377 self.options = form.getValues() 378 elif form.formType == 'cancel': 379 self.options = {} 380 else: 381 raise BadRequest(text="Unexpected form type %r" % form.formType) 382 else: 383 raise BadRequest(text="Missing configuration form") 384 385 386 387 def _parse_itemIdentifiers(self, verbElement): 388 """ 389 Parse item identifiers out of items and retract requests. 390 """ 391 self.itemIdentifiers = [] 392 for element in verbElement.elements(): 393 if element.uri == NS_PUBSUB and element.name == 'item': 394 try: 395 self.itemIdentifiers.append(element["id"]) 396 except KeyError: 397 raise BadRequest() 398 399 400 def _render_itemIdentifiers(self, verbElement): 401 """ 402 Render item identifiers into items and retract requests. 403 """ 404 if self.itemIdentifiers: 405 for itemIdentifier in self.itemIdentifiers: 406 item = verbElement.addElement('item') 407 item['id'] = itemIdentifier 408 409 410 def _parse_maxItems(self, verbElement): 411 """ 412 Parse maximum items out of an items request. 413 """ 414 value = verbElement.getAttribute('max_items') 415 416 if value: 417 try: 418 self.maxItems = int(value) 419 except ValueError: 420 raise BadRequest(text="Field max_items requires a positive " + 421 "integer value") 422 423 424 def _render_maxItems(self, verbElement): 425 """ 426 Parse maximum items into an items request. 427 """ 428 if self.maxItems: 429 verbElement['max_items'] = unicode(self.maxItems) 430 431 432 def _parse_options(self, verbElement): 433 form = PubSubRequest._findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS) 434 if form: 435 if form.formType == 'submit': 436 self.options = form.getValues() 437 elif form.formType == 'cancel': 438 self.options = {} 439 else: 440 raise BadRequest(text="Unexpected form type %r" % form.formType) 441 else: 442 raise BadRequest(text="Missing options form") 443 444 def parseElement(self, element): 445 """ 446 Parse the publish-subscribe verb and parameters out of a request. 447 """ 448 generic.Stanza.parseElement(self, element) 449 450 for child in element.pubsub.elements(): 451 key = (self.stanzaType, child.uri, child.name) 452 try: 453 verb = self._requestVerbMap[key] 454 except KeyError: 455 continue 456 else: 457 self.verb = verb 458 break 459 460 if not self.verb: 461 raise NotImplementedError() 462 463 for parameter in self._parameters[verb]: 464 getattr(self, '_parse_%s' % parameter)(child) 465 466 467 def send(self, xs): 468 """ 469 Send this request to its recipient. 470 471 This renders all of the relevant parameters for this specific 472 requests into an L{xmlstream.IQ}, and invoke its C{send} method. 473 This returns a deferred that fires upon reception of a response. See 474 L{xmlstream.IQ} for details. 475 476 @param xs: The XML stream to send the request on. 477 @type xs: L{xmlstream.XmlStream} 478 @rtype: L{defer.Deferred}. 479 """ 480 481 try: 482 (self.stanzaType, 483 childURI, 484 childName) = self._verbRequestMap[self.verb] 485 except KeyError: 486 raise NotImplementedError() 487 488 iq = xmlstream.IQ(xs, self.stanzaType) 489 iq.addElement((childURI, 'pubsub')) 490 verbElement = iq.pubsub.addElement(childName) 491 492 if self.sender: 493 iq['from'] = self.sender.full() 494 if self.recipient: 495 iq['to'] = self.recipient.full() 496 497 for parameter in self._parameters[self.verb]: 498 getattr(self, '_render_%s' % parameter)(verbElement) 499 500 return iq.send() 204 501 205 502 206 503 … … 336 633 @param nodeIdentifier: Optional suggestion for the id of the node. 337 634 @type nodeIdentifier: C{unicode} 338 635 """ 339 340 341 request = _PubSubRequest(self.xmlstream, 'create') 342 if nodeIdentifier: 343 request.command['node'] = nodeIdentifier 636 request = PubSubRequest('create') 637 request.recipient = service 638 request.nodeIdentifier = nodeIdentifier 344 639 345 640 def cb(iq): 346 641 try: … … 350 645 new_node = nodeIdentifier 351 646 return new_node 352 647 353 return request.send(service).addCallback(cb) 648 d = request.send(self.xmlstream) 649 d.addCallback(cb) 650 return d 354 651 355 652 356 653 def deleteNode(self, service, nodeIdentifier): … … 362 659 @param nodeIdentifier: The identifier of the node. 363 660 @type nodeIdentifier: C{unicode} 364 661 """ 365 request = _PubSubRequest(self.xmlstream, 'delete', NS_PUBSUB_OWNER) 366 request.command['node'] = nodeIdentifier 367 return request.send(service) 662 request = PubSubRequest('delete') 663 request.recipient = service 664 request.nodeIdentifier = nodeIdentifier 665 return request.send(self.xmlstream) 368 666 369 667 370 668 def subscribe(self, service, nodeIdentifier, subscriber): … … 379 677 will get notifications of new published items. 380 678 @type subscriber: L{JID} 381 679 """ 382 request = _PubSubRequest(self.xmlstream,'subscribe')383 if nodeIdentifier:384 request.command['node']= nodeIdentifier385 request. command['jid'] = subscriber.full()680 request = PubSubRequest('subscribe') 681 request.recipient = service 682 request.nodeIdentifier = nodeIdentifier 683 request.subscriber = subscriber 386 684 387 685 def cb(iq): 388 686 subscription = iq.pubsub.subscription["subscription"] … … 397 695 # yielded a stanza error. 398 696 return None 399 697 400 return request.send(service).addCallback(cb) 698 d = request.send(self.xmlstream) 699 d.addCallback(cb) 700 return d 401 701 402 702 403 703 def unsubscribe(self, service, nodeIdentifier, subscriber): … … 411 711 @param subscriber: The entity to unsubscribe from the node. 412 712 @type subscriber: L{JID} 413 713 """ 414 request = _PubSubRequest(self.xmlstream,'unsubscribe')415 if nodeIdentifier:416 request.command['node']= nodeIdentifier417 request. command['jid'] = subscriber.full()418 return request.send(se rvice)714 request = PubSubRequest('unsubscribe') 715 request.recipient = service 716 request.nodeIdentifier = nodeIdentifier 717 request.subscriber = subscriber 718 return request.send(self.xmlstream) 419 719 420 720 421 721 def publish(self, service, nodeIdentifier, items=None): … … 429 729 @param items: Optional list of L{Item}s to publish. 430 730 @type items: C{list} 431 731 """ 432 request = _PubSubRequest(self.xmlstream, 'publish') 433 request.command['node'] = nodeIdentifier 434 if items: 435 for item in items: 436 request.command.addChild(item) 437 438 return request.send(service) 732 request = PubSubRequest('publish') 733 request.recipient = service 734 request.nodeIdentifier = nodeIdentifier 735 request.items = items 736 return request.send(self.xmlstream) 439 737 440 738 441 739 def items(self, service, nodeIdentifier, maxItems=None): … … 449 747 @param maxItems: Optional limit on the number of retrieved items. 450 748 @type maxItems: C{int} 451 749 """ 452 request = _PubSubRequest(self.xmlstream, 'items', method='get')453 if nodeIdentifier:454 request.command['node']= nodeIdentifier750 request = PubSubRequest('items') 751 request.recipient = service 752 request.nodeIdentifier = nodeIdentifier 455 753 if maxItems: 456 request. command["max_items"]= str(int(maxItems))754 request.maxItems = str(int(maxItems)) 457 755 458 756 def cb(iq): 459 757 items = [] … … 462 760 items.append(element) 463 761 return items 464 762 465 return request.send(service).addCallback(cb) 763 d = request.send(self.xmlstream) 764 d.addCallback(cb) 765 return d 466 766 467 767 468 768 … … 497 797 implements(IPubSubService) 498 798 499 799 iqHandlers = { 500 PUBSUB_PUBLISH: '_onPublish', 501 PUBSUB_CREATE: '_onCreate', 502 PUBSUB_SUBSCRIBE: '_onSubscribe', 503 PUBSUB_OPTIONS_GET: '_onOptionsGet', 504 PUBSUB_OPTIONS_SET: '_onOptionsSet', 505 PUBSUB_AFFILIATIONS: '_onAffiliations', 506 PUBSUB_ITEMS: '_onItems', 507 PUBSUB_RETRACT: '_onRetract', 508 PUBSUB_SUBSCRIPTIONS: '_onSubscriptions', 509 PUBSUB_UNSUBSCRIBE: '_onUnsubscribe', 510 511 PUBSUB_AFFILIATIONS_GET: '_onAffiliationsGet', 512 PUBSUB_AFFILIATIONS_SET: '_onAffiliationsSet', 513 PUBSUB_CONFIGURE_GET: '_onConfigureGet', 514 PUBSUB_CONFIGURE_SET: '_onConfigureSet', 515 PUBSUB_DEFAULT: '_onDefault', 516 PUBSUB_PURGE: '_onPurge', 517 PUBSUB_DELETE: '_onDelete', 518 PUBSUB_SUBSCRIPTIONS_GET: '_onSubscriptionsGet', 519 PUBSUB_SUBSCRIPTIONS_SET: '_onSubscriptionsSet', 520 800 '/*': '_onPubSubRequest', 521 801 } 522 802 523 803 … … 530 810 531 811 532 812 def connectionMade(self): 533 self.xmlstream.addObserver(PUBSUB_GET, self.handleRequest) 534 self.xmlstream.addObserver(PUBSUB_SET, self.handleRequest) 535 self.xmlstream.addObserver(PUBSUB_OWNER_GET, self.handleRequest) 536 self.xmlstream.addObserver(PUBSUB_OWNER_SET, self.handleRequest) 813 self.xmlstream.addObserver(PUBSUB_REQUEST, self.handleRequest) 537 814 538 815 539 816 def getDiscoInfo(self, requestor, target, nodeIdentifier): … … 585 862 return d 586 863 587 864 588 def _findForm(self, element, formNamespace): 589 if not element: 590 return None 865 def _onPubSubRequest(self, iq): 866 request = PubSubRequest.fromElement(iq) 867 handler = getattr(self, '_on_%s' % request.verb) 868 return handler(request) 591 869 592 form = None593 for child in element.elements():594 try:595 form = data_form.Form.fromElement(child)596 except data_form.Error:597 continue598 870 599 if form.formNamespace != NS_PUBSUB_NODE_CONFIG: 600 continue 871 def _on_publish(self, request): 872 return self.publish(request.sender, request.recipient, 873 request.nodeIdentifier, request.items) 601 874 602 return form603 875 604 605 def _getParameter_node(self, commandElement): 606 try: 607 return commandElement["node"] 608 except KeyError: 609 raise BadRequest('nodeid-required') 610 611 612 def _getParameter_nodeOrEmpty(self, commandElement): 613 return commandElement.getAttribute("node", '') 614 615 616 def _getParameter_jid(self, commandElement): 617 try: 618 return jid.internJID(commandElement["jid"]) 619 except KeyError: 620 raise BadRequest('jid-required') 621 622 623 def _getParameter_max_items(self, commandElement): 624 value = commandElement.getAttribute('max_items') 625 626 if value: 627 try: 628 return int(value) 629 except ValueError: 630 raise BadRequest(text="Field max_items requires a positive " + 631 "integer value") 632 else: 633 return None 634 635 636 def _getParameters(self, iq, *names): 637 requestor = jid.internJID(iq["from"]).userhostJID() 638 service = jid.internJID(iq["to"]) 639 640 params = [requestor, service] 641 642 if names: 643 command = names[0] 644 commandElement = getattr(iq.pubsub, command) 645 if not commandElement: 646 raise Exception("Could not find command element %r" % command) 647 648 for name in names[1:]: 649 try: 650 getter = getattr(self, '_getParameter_' + name) 651 except KeyError: 652 raise Exception("No parameter getter for this name") 653 654 params.append(getter(commandElement)) 655 656 return params 657 658 659 def _onPublish(self, iq): 660 requestor, service, nodeIdentifier = self._getParameters( 661 iq, 'publish', 'node') 662 663 items = [] 664 for element in iq.pubsub.publish.elements(): 665 if element.uri == NS_PUBSUB and element.name == 'item': 666 items.append(element) 667 668 return self.publish(requestor, service, nodeIdentifier, items) 669 670 671 def _onSubscribe(self, iq): 672 requestor, service, nodeIdentifier, subscriber = self._getParameters( 673 iq, 'subscribe', 'nodeOrEmpty', 'jid') 876 def _on_subscribe(self, request): 674 877 675 878 def toResponse(result): 676 879 response = domish.Element((NS_PUBSUB, "pubsub")) … … 681 884 subscription["subscription"] = result.state 682 885 return response 683 886 684 d = self.subscribe(requestor, service, nodeIdentifier, subscriber) 887 d = self.subscribe(request.sender, request.recipient, 888 request.nodeIdentifier, request.subscriber) 685 889 d.addCallback(toResponse) 686 890 return d 687 891 688 892 689 def _on Unsubscribe(self, iq):690 re questor, service, nodeIdentifier, subscriber = self._getParameters(691 iq, 'unsubscribe', 'nodeOrEmpty', 'jid')893 def _on_unsubscribe(self, request): 894 return self.unsubscribe(request.sender, request.recipient, 895 request.nodeIdentifier, request.subscriber) 692 896 693 return self.unsubscribe(requestor, service, nodeIdentifier, subscriber)694 897 695 696 def _onOptionsGet(self, iq): 898 def _on_optionsGet(self, request): 697 899 raise Unsupported('subscription-options') 698 900 699 901 700 def _on OptionsSet(self, iq):902 def _on_optionsSet(self, request): 701 903 raise Unsupported('subscription-options') 702 904 703 905 704 def _onSubscriptions(self, iq): 705 requestor, service = self._getParameters(iq) 906 def _on_subscriptions(self, request): 706 907 707 908 def toResponse(result): 708 909 response = domish.Element((NS_PUBSUB, 'pubsub')) … … 714 915 item['subscription'] = subscription.state 715 916 return response 716 917 717 d = self.subscriptions(request or, service)918 d = self.subscriptions(request.sender, request.recipient) 718 919 d.addCallback(toResponse) 719 920 return d 720 921 721 922 722 def _onAffiliations(self, iq): 723 requestor, service = self._getParameters(iq) 923 def _on_affiliations(self, request): 724 924 725 925 def toResponse(result): 726 926 response = domish.Element((NS_PUBSUB, 'pubsub')) … … 733 933 734 934 return response 735 935 736 d = self.affiliations(request or, service)936 d = self.affiliations(request.sender, request.recipient) 737 937 d.addCallback(toResponse) 738 938 return d 739 939 740 940 741 def _onCreate(self, iq): 742 requestor, service = self._getParameters(iq) 743 nodeIdentifier = iq.pubsub.create.getAttribute("node") 941 def _on_create(self, request): 744 942 745 943 def toResponse(result): 746 if not nodeIdentifier ornodeIdentifier != result:944 if not request.nodeIdentifier or request.nodeIdentifier != result: 747 945 response = domish.Element((NS_PUBSUB, 'pubsub')) 748 946 create = response.addElement('create') 749 947 create['node'] = result … … 751 949 else: 752 950 return None 753 951 754 d = self.create(requestor, service, nodeIdentifier) 952 d = self.create(request.sender, request.recipient, 953 request.nodeIdentifier) 755 954 d.addCallback(toResponse) 756 955 return d 757 956 … … 771 970 fields.append(data_form.Field.fromDict(option)) 772 971 return fields 773 972 973 774 974 def _formFromConfiguration(self, values): 775 975 options = self.getConfigurationOptions() 776 976 fields = self._makeFields(options, values) … … 780 980 781 981 return form 782 982 983 783 984 def _checkConfiguration(self, values): 784 985 options = self.getConfigurationOptions() 785 986 processedValues = {} … … 805 1006 return processedValues 806 1007 807 1008 808 def _onDefault(self, iq): 809 requestor, service = self._getParameters(iq) 1009 def _on_default(self, request): 810 1010 811 1011 def toResponse(options): 812 1012 response = domish.Element((NS_PUBSUB_OWNER, "pubsub")) … … 814 1014 default.addChild(self._formFromConfiguration(options).toElement()) 815 1015 return response 816 1016 817 form = self._findForm(iq.pubsub.config, NS_PUBSUB_NODE_CONFIG) 818 values = form and form.formType == 'result' and form.getValues() or {} 819 nodeType = values.get('pubsub#node_type', 'leaf') 820 821 if nodeType not in ('leaf', 'collections'): 1017 if request.nodeType not in ('leaf', 'collection'): 822 1018 return defer.fail(error.StanzaError('not-acceptable')) 823 1019 824 d = self.getDefaultConfiguration(requestor, service, nodeType) 1020 d = self.getDefaultConfiguration(request.sender, request.recipient, 1021 request.nodeType) 825 1022 d.addCallback(toResponse) 826 1023 return d 827 1024 828 1025 829 def _onConfigureGet(self, iq): 830 requestor, service, nodeIdentifier = self._getParameters( 831 iq, 'configure', 'nodeOrEmpty') 832 1026 def _on_configureGet(self, request): 833 1027 def toResponse(options): 834 1028 response = domish.Element((NS_PUBSUB_OWNER, "pubsub")) 835 1029 configure = response.addElement("configure") 836 configure.addChild(self._formFromConfiguration(options).toElement()) 1030 form = self._formFromConfiguration(options) 1031 configure.addChild(form.toElement()) 837 1032 838 if nodeIdentifier:839 configure["node"] = nodeIdentifier1033 if request.nodeIdentifier: 1034 configure["node"] = request.nodeIdentifier 840 1035 841 1036 return response 842 1037 843 d = self.getConfiguration(requestor, service, nodeIdentifier) 1038 d = self.getConfiguration(request.sender, request.recipient, 1039 request.nodeIdentifier) 844 1040 d.addCallback(toResponse) 845 1041 return d 846 1042 847 1043 848 def _onConfigureSet(self, iq): 849 requestor, service, nodeIdentifier = self._getParameters( 850 iq, 'configure', 'nodeOrEmpty') 1044 def _on_configureSet(self, request): 1045 if request.options: 1046 request.options = self._checkConfiguration(request.options) 1047 return self.setConfiguration(request.sender, request.recipient, 1048 request.nodeIdentifier, 1049 request.options) 1050 else: 1051 return None 851 1052 852 # Search configuration form with correct FORM_TYPE and process it853 1053 854 form = self._findForm(iq.pubsub.configure, NS_PUBSUB_NODE_CONFIG)855 1054 856 if form: 857 if form.formType == 'submit': 858 options = self._checkConfiguration(form.getValues()) 859 860 return self.setConfiguration(requestor, service, 861 nodeIdentifier, options) 862 elif form.formType == 'cancel': 863 return None 864 865 raise BadRequest() 866 867 868 def _onItems(self, iq): 869 requestor, service, nodeIdentifier, maxItems = self._getParameters( 870 iq, 'items', 'nodeOrEmpty', 'max_items') 871 872 itemIdentifiers = [] 873 for child in iq.pubsub.items.elements(): 874 if child.name == 'item' and child.uri == NS_PUBSUB: 875 try: 876 itemIdentifiers.append(child["id"]) 877 except KeyError: 878 raise BadRequest() 1055 def _on_items(self, request): 879 1056 880 1057 def toResponse(result): 881 1058 response = domish.Element((NS_PUBSUB, 'pubsub')) 882 1059 items = response.addElement('items') 883 if nodeIdentifier: 884 items["node"] = nodeIdentifier 1060 items["node"] = request.nodeIdentifier 885 1061 886 1062 for item in result: 887 1063 items.addChild(item) 888 1064 889 1065 return response 890 1066 891 d = self.items(requestor, service, nodeIdentifier, maxItems, 892 itemIdentifiers) 1067 d = self.items(request.sender, request.recipient, 1068 request.nodeIdentifier, request.maxItems, 1069 request.itemIdentifiers) 893 1070 d.addCallback(toResponse) 894 1071 return d 895 1072 896 1073 897 def _on Retract(self, iq):898 re questor, service, nodeIdentifier = self._getParameters(899 iq, 'retract', 'node')1074 def _on_retract(self, request): 1075 return self.retract(request.sender, request.recipient, 1076 request.nodeIdentifier, request.itemIdentifiers) 900 1077 901 itemIdentifiers = []902 for child in iq.pubsub.retract.elements():903 if child.uri == NS_PUBSUB and child.name == 'item':904 try:905 itemIdentifiers.append(child["id"])906 except KeyError:907 raise BadRequest()908 1078 909 return self.retract(requestor, service, nodeIdentifier, 910 itemIdentifiers) 1079 def _on_purge(self, request): 1080 return self.purge(request.sender, request.recipient, 1081 request.nodeIdentifier) 911 1082 912 1083 913 def _onPurge(self, iq): 914 requestor, service, nodeIdentifier = self._getParameters( 915 iq, 'purge', 'node') 916 return self.purge(requestor, service, nodeIdentifier) 1084 def _on_delete(self, request): 1085 return self.delete(request.sender, request.recipient, 1086 request.nodeIdentifier) 917 1087 918 1088 919 def _onDelete(self, iq): 920 requestor, service, nodeIdentifier = self._getParameters( 921 iq, 'delete', 'node') 922 return self.delete(requestor, service, nodeIdentifier) 923 924 925 def _onAffiliationsGet(self, iq): 1089 def _on_affiliationsGet(self, iq): 926 1090 raise Unsupported('modify-affiliations') 927 1091 928 1092 929 def _on AffiliationsSet(self, iq):1093 def _on_affiliationsSet(self, iq): 930 1094 raise Unsupported('modify-affiliations') 931 1095 932 1096 933 def _on SubscriptionsGet(self, iq):1097 def _on_subscriptionsGet(self, iq): 934 1098 raise Unsupported('manage-subscriptions') 935 1099 936 1100 937 def _on SubscriptionsSet(self, iq):1101 def _on_subscriptionsSet(self, iq): 938 1102 raise Unsupported('manage-subscriptions') 939 1103 940 1104 # public methods -
wokkel/test/test_pubsub.py
diff -r e35e291de060 wokkel/test/test_pubsub.py
a b 15 15 from twisted.words.protocols.jabber.xmlstream import toResponse 16 16 17 17 from wokkel import data_form, disco, iwokkel, pubsub, shim 18 from wokkel.generic import parseXml 18 19 from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub 19 20 20 21 NS_PUBSUB = 'http://jabber.org/protocol/pubsub' … … 487 488 488 489 489 490 491 class PubSubRequestTest(unittest.TestCase): 492 493 def test_fromElementPublish(self): 494 """ 495 Test parsing a publish request. 496 """ 497 498 xml = """ 499 <iq type='set' to='pubsub.example.org' 500 from='user@example.org'> 501 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 502 <publish node='test'/> 503 </pubsub> 504 </iq> 505 """ 506 507 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 508 self.assertEqual('publish', request.verb) 509 self.assertEqual(JID('user@example.org'), request.sender) 510 self.assertEqual(JID('pubsub.example.org'), request.recipient) 511 self.assertEqual('test', request.nodeIdentifier) 512 self.assertEqual([], request.items) 513 514 515 def test_fromElementPublishItems(self): 516 """ 517 Test parsing a publish request with items. 518 """ 519 520 xml = """ 521 <iq type='set' to='pubsub.example.org' 522 from='user@example.org'> 523 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 524 <publish node='test'> 525 <item id="item1"/> 526 <item id="item2"/> 527 </publish> 528 </pubsub> 529 </iq> 530 """ 531 532 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 533 self.assertEqual(2, len(request.items)) 534 self.assertEqual(u'item1', request.items[0]["id"]) 535 self.assertEqual(u'item2', request.items[1]["id"]) 536 537 538 def test_fromElementPublishNoNode(self): 539 """ 540 A publish request to the root node should raise an exception. 541 """ 542 xml = """ 543 <iq type='set' to='pubsub.example.org' 544 from='user@example.org'> 545 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 546 <publish/> 547 </pubsub> 548 </iq> 549 """ 550 551 err = self.assertRaises(error.StanzaError, 552 pubsub.PubSubRequest.fromElement, 553 parseXml(xml)) 554 self.assertEqual('bad-request', err.condition) 555 self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri) 556 self.assertEqual('nodeid-required', err.appCondition.name) 557 558 559 def test_fromElementSubscribe(self): 560 """ 561 Test parsing a subscription request. 562 """ 563 564 xml = """ 565 <iq type='set' to='pubsub.example.org' 566 from='user@example.org'> 567 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 568 <subscribe node='test' jid='user@example.org/Home'/> 569 </pubsub> 570 </iq> 571 """ 572 573 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 574 self.assertEqual('subscribe', request.verb) 575 self.assertEqual(JID('user@example.org'), request.sender) 576 self.assertEqual(JID('pubsub.example.org'), request.recipient) 577 self.assertEqual('test', request.nodeIdentifier) 578 self.assertEqual(JID('user@example.org/Home'), request.subscriber) 579 580 581 def test_fromElementSubscribeEmptyNode(self): 582 """ 583 Test parsing a subscription request to the root node. 584 """ 585 586 xml = """ 587 <iq type='set' to='pubsub.example.org' 588 from='user@example.org'> 589 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 590 <subscribe jid='user@example.org/Home'/> 591 </pubsub> 592 </iq> 593 """ 594 595 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 596 self.assertEqual('', request.nodeIdentifier) 597 598 599 def test_fromElementSubscribeNoJID(self): 600 """ 601 Subscribe requests without a JID should raise a bad-request exception. 602 """ 603 xml = """ 604 <iq type='set' to='pubsub.example.org' 605 from='user@example.org'> 606 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 607 <subscribe node='test'/> 608 </pubsub> 609 </iq> 610 """ 611 err = self.assertRaises(error.StanzaError, 612 pubsub.PubSubRequest.fromElement, 613 parseXml(xml)) 614 self.assertEqual('bad-request', err.condition) 615 self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri) 616 self.assertEqual('jid-required', err.appCondition.name) 617 618 def test_fromElementUnsubscribe(self): 619 """ 620 Test parsing an unsubscription request. 621 """ 622 623 xml = """ 624 <iq type='set' to='pubsub.example.org' 625 from='user@example.org'> 626 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 627 <unsubscribe node='test' jid='user@example.org/Home'/> 628 </pubsub> 629 </iq> 630 """ 631 632 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 633 self.assertEqual('unsubscribe', request.verb) 634 self.assertEqual(JID('user@example.org'), request.sender) 635 self.assertEqual(JID('pubsub.example.org'), request.recipient) 636 self.assertEqual('test', request.nodeIdentifier) 637 self.assertEqual(JID('user@example.org/Home'), request.subscriber) 638 639 640 def test_fromElementUnsubscribeNoJID(self): 641 """ 642 Unsubscribe requests without a JID should raise a bad-request exception. 643 """ 644 xml = """ 645 <iq type='set' to='pubsub.example.org' 646 from='user@example.org'> 647 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 648 <unsubscribe node='test'/> 649 </pubsub> 650 </iq> 651 """ 652 err = self.assertRaises(error.StanzaError, 653 pubsub.PubSubRequest.fromElement, 654 parseXml(xml)) 655 self.assertEqual('bad-request', err.condition) 656 self.assertEqual(NS_PUBSUB_ERRORS, err.appCondition.uri) 657 self.assertEqual('jid-required', err.appCondition.name) 658 659 660 def test_fromElementOptionsGet(self): 661 """ 662 Test parsing a request for getting subscription options. 663 """ 664 665 xml = """ 666 <iq type='get' to='pubsub.example.org' 667 from='user@example.org'> 668 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 669 <options node='test' jid='user@example.org/Home'/> 670 </pubsub> 671 </iq> 672 """ 673 674 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 675 self.assertEqual('optionsGet', request.verb) 676 677 678 def test_fromElementOptionsSet(self): 679 """ 680 Test parsing a request for setting subscription options. 681 """ 682 683 xml = """ 684 <iq type='set' to='pubsub.example.org' 685 from='user@example.org'> 686 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 687 <options node='test' jid='user@example.org/Home'> 688 <x xmlns='jabber:x:data' type='submit'> 689 <field var='FORM_TYPE' type='hidden'> 690 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 691 </field> 692 <field var='pubsub#deliver'><value>1</value></field> 693 </x> 694 </options> 695 </pubsub> 696 </iq> 697 """ 698 699 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 700 self.assertEqual('optionsSet', request.verb) 701 self.assertEqual(JID('user@example.org'), request.sender) 702 self.assertEqual(JID('pubsub.example.org'), request.recipient) 703 self.assertEqual('test', request.nodeIdentifier) 704 self.assertEqual(JID('user@example.org/Home'), request.subscriber) 705 self.assertEqual({'pubsub#deliver': '1'}, request.options) 706 707 708 def test_fromElementOptionsSetCancel(self): 709 """ 710 Test parsing a request for cancelling setting subscription options. 711 """ 712 713 xml = """ 714 <iq type='set' to='pubsub.example.org' 715 from='user@example.org'> 716 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 717 <options node='test' jid='user@example.org/Home'> 718 <x xmlns='jabber:x:data' type='cancel'/> 719 </options> 720 </pubsub> 721 </iq> 722 """ 723 724 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 725 self.assertEqual({}, request.options) 726 727 728 def test_fromElementOptionsSetBadFormType(self): 729 """ 730 On a options set request unknown fields should be ignored. 731 """ 732 733 xml = """ 734 <iq type='set' to='pubsub.example.org' 735 from='user@example.org'> 736 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 737 <options node='test' jid='user@example.org/Home'> 738 <x xmlns='jabber:x:data' type='result'> 739 <field var='FORM_TYPE' type='hidden'> 740 <value>http://jabber.org/protocol/pubsub#node_config</value> 741 </field> 742 <field var='pubsub#deliver'><value>1</value></field> 743 </x> 744 </options> 745 </pubsub> 746 </iq> 747 """ 748 749 err = self.assertRaises(error.StanzaError, 750 pubsub.PubSubRequest.fromElement, 751 parseXml(xml)) 752 self.assertEqual('bad-request', err.condition) 753 self.assertEqual(None, err.appCondition) 754 755 756 def test_fromElementOptionsSetNoForm(self): 757 """ 758 On a options set request a form is required. 759 """ 760 761 xml = """ 762 <iq type='set' to='pubsub.example.org' 763 from='user@example.org'> 764 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 765 <options node='test' jid='user@example.org/Home'/> 766 </pubsub> 767 </iq> 768 """ 769 err = self.assertRaises(error.StanzaError, 770 pubsub.PubSubRequest.fromElement, 771 parseXml(xml)) 772 self.assertEqual('bad-request', err.condition) 773 self.assertEqual(None, err.appCondition) 774 775 776 def test_fromElementSubscriptions(self): 777 """ 778 Test parsing a request for all subscriptions. 779 """ 780 781 xml = """ 782 <iq type='get' to='pubsub.example.org' 783 from='user@example.org'> 784 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 785 <subscriptions/> 786 </pubsub> 787 </iq> 788 """ 789 790 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 791 self.assertEqual('subscriptions', request.verb) 792 self.assertEqual(JID('user@example.org'), request.sender) 793 self.assertEqual(JID('pubsub.example.org'), request.recipient) 794 795 796 def test_fromElementAffiliations(self): 797 """ 798 Test parsing a request for all affiliations. 799 """ 800 801 xml = """ 802 <iq type='get' to='pubsub.example.org' 803 from='user@example.org'> 804 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 805 <affiliations/> 806 </pubsub> 807 </iq> 808 """ 809 810 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 811 self.assertEqual('affiliations', request.verb) 812 self.assertEqual(JID('user@example.org'), request.sender) 813 self.assertEqual(JID('pubsub.example.org'), request.recipient) 814 815 816 def test_fromElementCreate(self): 817 """ 818 Test parsing a request to create a node. 819 """ 820 821 xml = """ 822 <iq type='set' to='pubsub.example.org' 823 from='user@example.org'> 824 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 825 <create node='mynode'/> 826 </pubsub> 827 </iq> 828 """ 829 830 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 831 self.assertEqual('create', request.verb) 832 self.assertEqual(JID('user@example.org'), request.sender) 833 self.assertEqual(JID('pubsub.example.org'), request.recipient) 834 self.assertEqual('mynode', request.nodeIdentifier) 835 836 837 def test_fromElementCreateInstant(self): 838 """ 839 Test parsing a request to create an instant node. 840 """ 841 842 xml = """ 843 <iq type='set' to='pubsub.example.org' 844 from='user@example.org'> 845 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 846 <create/> 847 </pubsub> 848 </iq> 849 """ 850 851 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 852 self.assertIdentical(None, request.nodeIdentifier) 853 854 855 def test_fromElementDefault(self): 856 """ 857 Test parsing a request for the default node configuration. 858 """ 859 860 xml = """ 861 <iq type='get' to='pubsub.example.org' 862 from='user@example.org'> 863 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 864 <default/> 865 </pubsub> 866 </iq> 867 """ 868 869 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 870 self.assertEqual('default', request.verb) 871 self.assertEqual(JID('user@example.org'), request.sender) 872 self.assertEqual(JID('pubsub.example.org'), request.recipient) 873 self.assertEqual('leaf', request.nodeType) 874 875 876 def test_fromElementDefaultCollection(self): 877 """ 878 Parsing a request for the default configuration extracts the node type. 879 """ 880 881 xml = """ 882 <iq type='get' to='pubsub.example.org' 883 from='user@example.org'> 884 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 885 <default> 886 <x xmlns='jabber:x:data' type='submit'> 887 <field var='FORM_TYPE' type='hidden'> 888 <value>http://jabber.org/protocol/pubsub#node_config</value> 889 </field> 890 <field var='pubsub#node_type'> 891 <value>collection</value> 892 </field> 893 </x> 894 </default> 895 896 </pubsub> 897 </iq> 898 """ 899 900 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 901 self.assertEqual('collection', request.nodeType) 902 903 904 def test_fromElementConfigureGet(self): 905 """ 906 Test parsing a node configuration get request. 907 """ 908 909 xml = """ 910 <iq type='get' to='pubsub.example.org' 911 from='user@example.org'> 912 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 913 <configure node='test'/> 914 </pubsub> 915 </iq> 916 """ 917 918 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 919 self.assertEqual('configureGet', request.verb) 920 self.assertEqual(JID('user@example.org'), request.sender) 921 self.assertEqual(JID('pubsub.example.org'), request.recipient) 922 self.assertEqual('test', request.nodeIdentifier) 923 924 925 def test_fromElementConfigureSet(self): 926 """ 927 On a node configuration set request the Data Form is parsed. 928 """ 929 930 xml = """ 931 <iq type='set' to='pubsub.example.org' 932 from='user@example.org'> 933 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 934 <configure node='test'> 935 <x xmlns='jabber:x:data' type='submit'> 936 <field var='FORM_TYPE' type='hidden'> 937 <value>http://jabber.org/protocol/pubsub#node_config</value> 938 </field> 939 <field var='pubsub#deliver_payloads'><value>0</value></field> 940 <field var='pubsub#persist_items'><value>1</value></field> 941 </x> 942 </configure> 943 </pubsub> 944 </iq> 945 """ 946 947 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 948 self.assertEqual('configureSet', request.verb) 949 self.assertEqual(JID('user@example.org'), request.sender) 950 self.assertEqual(JID('pubsub.example.org'), request.recipient) 951 self.assertEqual('test', request.nodeIdentifier) 952 self.assertEqual({'pubsub#deliver_payloads': '0', 953 'pubsub#persist_items': '1'}, request.options) 954 955 956 def test_fromElementConfigureSetCancel(self): 957 """ 958 The node configuration is cancelled, so no options. 959 """ 960 961 xml = """ 962 <iq type='set' to='pubsub.example.org' 963 from='user@example.org'> 964 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 965 <configure node='test'> 966 <x xmlns='jabber:x:data' type='cancel'/> 967 </configure> 968 </pubsub> 969 </iq> 970 """ 971 972 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 973 self.assertEqual({}, request.options) 974 975 976 def test_fromElementConfigureSetBadFormType(self): 977 """ 978 On a node configuration set request unknown fields should be ignored. 979 """ 980 981 xml = """ 982 <iq type='set' to='pubsub.example.org' 983 from='user@example.org'> 984 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 985 <configure node='test'> 986 <x xmlns='jabber:x:data' type='result'> 987 <field var='FORM_TYPE' type='hidden'> 988 <value>http://jabber.org/protocol/pubsub#node_config</value> 989 </field> 990 <field var='pubsub#deliver_payloads'><value>0</value></field> 991 <field var='x-myfield'><value>1</value></field> 992 </x> 993 </configure> 994 </pubsub> 995 </iq> 996 """ 997 998 err = self.assertRaises(error.StanzaError, 999 pubsub.PubSubRequest.fromElement, 1000 parseXml(xml)) 1001 self.assertEqual('bad-request', err.condition) 1002 self.assertEqual(None, err.appCondition) 1003 1004 1005 def test_fromElementConfigureSetNoForm(self): 1006 """ 1007 On a node configuration set request a form is required. 1008 """ 1009 1010 xml = """ 1011 <iq type='set' to='pubsub.example.org' 1012 from='user@example.org'> 1013 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1014 <configure node='test'/> 1015 </pubsub> 1016 </iq> 1017 """ 1018 err = self.assertRaises(error.StanzaError, 1019 pubsub.PubSubRequest.fromElement, 1020 parseXml(xml)) 1021 self.assertEqual('bad-request', err.condition) 1022 self.assertEqual(None, err.appCondition) 1023 1024 1025 def test_fromElementItems(self): 1026 """ 1027 Test parsing an items request. 1028 """ 1029 xml = """ 1030 <iq type='get' to='pubsub.example.org' 1031 from='user@example.org'> 1032 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1033 <items node='test'/> 1034 </pubsub> 1035 </iq> 1036 """ 1037 1038 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1039 self.assertEqual('items', request.verb) 1040 self.assertEqual(JID('user@example.org'), request.sender) 1041 self.assertEqual(JID('pubsub.example.org'), request.recipient) 1042 self.assertEqual('test', request.nodeIdentifier) 1043 self.assertIdentical(None, request.maxItems) 1044 self.assertEqual([], request.itemIdentifiers) 1045 1046 1047 def test_fromElementRetract(self): 1048 """ 1049 Test parsing a retract request. 1050 """ 1051 1052 xml = """ 1053 <iq type='set' to='pubsub.example.org' 1054 from='user@example.org'> 1055 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1056 <retract node='test'> 1057 <item id='item1'/> 1058 <item id='item2'/> 1059 </retract> 1060 </pubsub> 1061 </iq> 1062 """ 1063 1064 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1065 self.assertEqual('retract', request.verb) 1066 self.assertEqual(JID('user@example.org'), request.sender) 1067 self.assertEqual(JID('pubsub.example.org'), request.recipient) 1068 self.assertEqual('test', request.nodeIdentifier) 1069 self.assertEqual(['item1', 'item2'], request.itemIdentifiers) 1070 1071 1072 def test_fromElementPurge(self): 1073 """ 1074 Test parsing a purge request. 1075 """ 1076 1077 xml = """ 1078 <iq type='set' to='pubsub.example.org' 1079 from='user@example.org'> 1080 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1081 <purge node='test'/> 1082 </pubsub> 1083 </iq> 1084 """ 1085 1086 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1087 self.assertEqual('purge', request.verb) 1088 self.assertEqual(JID('user@example.org'), request.sender) 1089 self.assertEqual(JID('pubsub.example.org'), request.recipient) 1090 self.assertEqual('test', request.nodeIdentifier) 1091 1092 1093 def test_fromElementDelete(self): 1094 """ 1095 Test parsing a delete request. 1096 """ 1097 1098 xml = """ 1099 <iq type='set' to='pubsub.example.org' 1100 from='user@example.org'> 1101 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1102 <delete node='test'/> 1103 </pubsub> 1104 </iq> 1105 """ 1106 1107 request = pubsub.PubSubRequest.fromElement(parseXml(xml)) 1108 self.assertEqual('delete', request.verb) 1109 self.assertEqual(JID('user@example.org'), request.sender) 1110 self.assertEqual(JID('pubsub.example.org'), request.recipient) 1111 self.assertEqual('test', request.nodeIdentifier) 1112 1113 1114 490 1115 class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin): 491 1116 """ 492 1117 Tests for L{pubsub.PubSubService}. … … 504 1129 verify.verifyObject(iwokkel.IPubSubService, self.service) 505 1130 506 1131 1132 def test_connectionMade(self): 1133 """ 1134 Verify setup of observers in L{pubsub.connectionMade}. 1135 """ 1136 requests = [] 1137 1138 def handleRequest(iq): 1139 requests.append(iq) 1140 1141 self.service.xmlstream = self.stub.xmlstream 1142 self.service.handleRequest = handleRequest 1143 self.service.connectionMade() 1144 1145 for namespace in (NS_PUBSUB, NS_PUBSUB_OWNER): 1146 for stanzaType in ('get', 'set'): 1147 iq = domish.Element((None, 'iq')) 1148 iq['type'] = stanzaType 1149 iq.addElement((namespace, 'pubsub')) 1150 self.stub.xmlstream.dispatch(iq) 1151 1152 self.assertEqual(4, len(requests)) 1153 1154 507 1155 def test_getDiscoInfo(self): 508 1156 """ 509 1157 Test getDiscoInfo calls getNodeInfo and returns some minimal info. … … 569 1217 return d 570 1218 571 1219 572 def test_onPublishNoNode(self):573 """574 The root node is always a collection, publishing is a bad request.575 """576 xml = """577 <iq type='set' to='pubsub.example.org'578 from='user@example.org'>579 <pubsub xmlns='http://jabber.org/protocol/pubsub'>580 <publish/>581 </pubsub>582 </iq>583 """584 585 def cb(result):586 self.assertEquals('bad-request', result.condition)587 588 d = self.handleRequest(xml)589 self.assertFailure(d, error.StanzaError)590 d.addCallback(cb)591 return d592 593 594 1220 def test_onPublish(self): 595 1221 """ 596 1222 A publish request should result in L{PubSubService.publish} being … … 607 1233 """ 608 1234 609 1235 def publish(requestor, service, nodeIdentifier, items): 610 self.assertEqual(JID('user@example.org'), requestor)611 self.assertEqual(JID('pubsub.example.org'), service)612 self.assertEqual('test', nodeIdentifier)613 self.assertEqual([], items)614 1236 return defer.succeed(None) 615 1237 616 1238 self.service.publish = publish 1239 verify.verifyObject(iwokkel.IPubSubService, self.service) 617 1240 return self.handleRequest(xml) 618 1241 619 1242 1243 def test_onSubscribe(self): 1244 """ 1245 A successful subscription should return the current subscription. 1246 """ 1247 1248 xml = """ 1249 <iq type='set' to='pubsub.example.org' 1250 from='user@example.org'> 1251 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1252 <subscribe node='test' jid='user@example.org/Home'/> 1253 </pubsub> 1254 </iq> 1255 """ 1256 1257 def subscribe(requestor, service, nodeIdentifier, subscriber): 1258 return defer.succeed(pubsub.Subscription(nodeIdentifier, 1259 subscriber, 1260 'subscribed')) 1261 1262 def cb(element): 1263 self.assertEqual('pubsub', element.name) 1264 self.assertEqual(NS_PUBSUB, element.uri) 1265 subscription = element.subscription 1266 self.assertEqual(NS_PUBSUB, subscription.uri) 1267 self.assertEqual('test', subscription['node']) 1268 self.assertEqual('user@example.org/Home', subscription['jid']) 1269 self.assertEqual('subscribed', subscription['subscription']) 1270 1271 self.service.subscribe = subscribe 1272 verify.verifyObject(iwokkel.IPubSubService, self.service) 1273 d = self.handleRequest(xml) 1274 d.addCallback(cb) 1275 return d 1276 1277 1278 def test_onSubscribeEmptyNode(self): 1279 """ 1280 A successful subscription on root node should return no node attribute. 1281 """ 1282 1283 xml = """ 1284 <iq type='set' to='pubsub.example.org' 1285 from='user@example.org'> 1286 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1287 <subscribe jid='user@example.org/Home'/> 1288 </pubsub> 1289 </iq> 1290 """ 1291 1292 def subscribe(requestor, service, nodeIdentifier, subscriber): 1293 return defer.succeed(pubsub.Subscription(nodeIdentifier, 1294 subscriber, 1295 'subscribed')) 1296 1297 def cb(element): 1298 self.assertFalse(element.subscription.hasAttribute('node')) 1299 1300 self.service.subscribe = subscribe 1301 verify.verifyObject(iwokkel.IPubSubService, self.service) 1302 d = self.handleRequest(xml) 1303 d.addCallback(cb) 1304 return d 1305 1306 1307 def test_onUnsubscribe(self): 1308 """ 1309 A successful unsubscription should return an empty response. 1310 """ 1311 1312 xml = """ 1313 <iq type='set' to='pubsub.example.org' 1314 from='user@example.org'> 1315 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1316 <unsubscribe node='test' jid='user@example.org/Home'/> 1317 </pubsub> 1318 </iq> 1319 """ 1320 1321 def unsubscribe(requestor, service, nodeIdentifier, subscriber): 1322 return defer.succeed(None) 1323 1324 def cb(element): 1325 self.assertIdentical(None, element) 1326 1327 self.service.unsubscribe = unsubscribe 1328 verify.verifyObject(iwokkel.IPubSubService, self.service) 1329 d = self.handleRequest(xml) 1330 d.addCallback(cb) 1331 return d 1332 1333 620 1334 def test_onOptionsGet(self): 621 1335 """ 622 Subscription options arenot supported.1336 Getting subscription options is not supported. 623 1337 """ 624 1338 625 1339 xml = """ 626 1340 <iq type='get' to='pubsub.example.org' 627 1341 from='user@example.org'> 628 1342 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 629 <options/> 1343 <options node='test' jid='user@example.org/Home'/> 1344 </pubsub> 1345 </iq> 1346 """ 1347 1348 def cb(result): 1349 self.assertEquals('feature-not-implemented', result.condition) 1350 self.assertEquals('unsupported', result.appCondition.name) 1351 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 1352 1353 d = self.handleRequest(xml) 1354 self.assertFailure(d, error.StanzaError) 1355 d.addCallback(cb) 1356 return d 1357 1358 1359 def test_onOptionsSet(self): 1360 """ 1361 Setting subscription options is not supported. 1362 """ 1363 1364 xml = """ 1365 <iq type='set' to='pubsub.example.org' 1366 from='user@example.org'> 1367 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1368 <options node='test' jid='user@example.org/Home'> 1369 <x xmlns='jabber:x:data' type='submit'> 1370 <field var='FORM_TYPE' type='hidden'> 1371 <value>http://jabber.org/protocol/pubsub#subscribe_options</value> 1372 </field> 1373 <field var='pubsub#deliver'><value>1</value></field> 1374 </x> 1375 </options> 630 1376 </pubsub> 631 1377 </iq> 632 1378 """ … … 673 1419 674 1420 675 1421 def subscriptions(requestor, service): 676 self.assertEqual(JID('user@example.org'), requestor)677 self.assertEqual(JID('pubsub.example.org'), service)678 1422 subscription = pubsub.Subscription('test', JID('user@example.org'), 679 1423 'subscribed') 680 1424 return defer.succeed([subscription]) 681 1425 682 1426 self.service.subscriptions = subscriptions 1427 verify.verifyObject(iwokkel.IPubSubService, self.service) 1428 d = self.handleRequest(xml) 1429 d.addCallback(cb) 1430 return d 1431 1432 1433 def test_onAffiliations(self): 1434 """ 1435 A subscriptions request should result in 1436 L{PubSubService.affiliations} being called and the result prepared 1437 for the response. 1438 """ 1439 1440 xml = """ 1441 <iq type='get' to='pubsub.example.org' 1442 from='user@example.org'> 1443 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1444 <affiliations/> 1445 </pubsub> 1446 </iq> 1447 """ 1448 1449 def cb(element): 1450 self.assertEqual('pubsub', element.name) 1451 self.assertEqual(NS_PUBSUB, element.uri) 1452 self.assertEqual(NS_PUBSUB, element.affiliations.uri) 1453 children = list(element.affiliations.elements()) 1454 self.assertEqual(1, len(children)) 1455 affiliation = children[0] 1456 self.assertEqual('affiliation', affiliation.name) 1457 self.assertEqual(NS_PUBSUB, affiliation.uri) 1458 self.assertEqual('test', affiliation['node']) 1459 self.assertEqual('owner', affiliation['affiliation']) 1460 1461 1462 def affiliations(requestor, service): 1463 affiliation = ('test', 'owner') 1464 return defer.succeed([affiliation]) 1465 1466 self.service.affiliations = affiliations 1467 verify.verifyObject(iwokkel.IPubSubService, self.service) 1468 d = self.handleRequest(xml) 1469 d.addCallback(cb) 1470 return d 1471 1472 1473 def test_onCreate(self): 1474 """ 1475 Replies to create node requests don't return the created node. 1476 """ 1477 1478 xml = """ 1479 <iq type='set' to='pubsub.example.org' 1480 from='user@example.org'> 1481 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1482 <create node='mynode'/> 1483 </pubsub> 1484 </iq> 1485 """ 1486 1487 def create(requestor, service, nodeIdentifier): 1488 return defer.succeed(nodeIdentifier) 1489 1490 def cb(element): 1491 self.assertIdentical(None, element) 1492 1493 self.service.create = create 1494 verify.verifyObject(iwokkel.IPubSubService, self.service) 1495 d = self.handleRequest(xml) 1496 d.addCallback(cb) 1497 return d 1498 1499 1500 def test_onCreateChanged(self): 1501 """ 1502 Replies to create node requests return the created node if changed. 1503 """ 1504 1505 xml = """ 1506 <iq type='set' to='pubsub.example.org' 1507 from='user@example.org'> 1508 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1509 <create node='mynode'/> 1510 </pubsub> 1511 </iq> 1512 """ 1513 1514 def create(requestor, service, nodeIdentifier): 1515 return defer.succeed(u'myrenamednode') 1516 1517 def cb(element): 1518 self.assertEqual('pubsub', element.name) 1519 self.assertEqual(NS_PUBSUB, element.uri) 1520 self.assertEqual(NS_PUBSUB, element.create.uri) 1521 self.assertEqual(u'myrenamednode', 1522 element.create.getAttribute('node')) 1523 1524 self.service.create = create 1525 verify.verifyObject(iwokkel.IPubSubService, self.service) 1526 d = self.handleRequest(xml) 1527 d.addCallback(cb) 1528 return d 1529 1530 1531 def test_onCreateInstant(self): 1532 """ 1533 Replies to create instant node requests return the created node. 1534 """ 1535 1536 xml = """ 1537 <iq type='set' to='pubsub.example.org' 1538 from='user@example.org'> 1539 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 1540 <create/> 1541 </pubsub> 1542 </iq> 1543 """ 1544 1545 def create(requestor, service, nodeIdentifier): 1546 return defer.succeed(u'random') 1547 1548 def cb(element): 1549 self.assertEqual('pubsub', element.name) 1550 self.assertEqual(NS_PUBSUB, element.uri) 1551 self.assertEqual(NS_PUBSUB, element.create.uri) 1552 self.assertEqual(u'random', element.create.getAttribute('node')) 1553 1554 self.service.create = create 1555 verify.verifyObject(iwokkel.IPubSubService, self.service) 683 1556 d = self.handleRequest(xml) 684 1557 d.addCallback(cb) 685 1558 return d … … 711 1584 } 712 1585 713 1586 def getDefaultConfiguration(requestor, service, nodeType): 714 self.assertEqual(JID('user@example.org'), requestor)715 self.assertEqual(JID('pubsub.example.org'), service)716 self.assertEqual('leaf', nodeType)717 1587 return defer.succeed({}) 718 1588 719 1589 def cb(element): … … 731 1601 return d 732 1602 733 1603 1604 def test_onDefaultCollection(self): 1605 """ 1606 Responses to default requests should depend on passed node type. 1607 """ 1608 1609 xml = """ 1610 <iq type='get' to='pubsub.example.org' 1611 from='user@example.org'> 1612 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1613 <default> 1614 <x xmlns='jabber:x:data' type='submit'> 1615 <field var='FORM_TYPE' type='hidden'> 1616 <value>http://jabber.org/protocol/pubsub#node_config</value> 1617 </field> 1618 <field var='pubsub#node_type'> 1619 <value>collection</value> 1620 </field> 1621 </x> 1622 </default> 1623 1624 </pubsub> 1625 </iq> 1626 """ 1627 1628 def getConfigurationOptions(): 1629 return { 1630 "pubsub#deliver_payloads": 1631 {"type": "boolean", 1632 "label": "Deliver payloads with event notifications"} 1633 } 1634 1635 def getDefaultConfiguration(requestor, service, nodeType): 1636 return defer.succeed({}) 1637 1638 self.service.getConfigurationOptions = getConfigurationOptions 1639 self.service.getDefaultConfiguration = getDefaultConfiguration 1640 verify.verifyObject(iwokkel.IPubSubService, self.service) 1641 return self.handleRequest(xml) 1642 1643 1644 def test_onDefaultUnknownNodeType(self): 1645 """ 1646 A default request should result in 1647 L{PubSubService.getDefaultConfiguration} being called. 1648 """ 1649 1650 xml = """ 1651 <iq type='get' to='pubsub.example.org' 1652 from='user@example.org'> 1653 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1654 <default> 1655 <x xmlns='jabber:x:data' type='submit'> 1656 <field var='FORM_TYPE' type='hidden'> 1657 <value>http://jabber.org/protocol/pubsub#node_config</value> 1658 </field> 1659 <field var='pubsub#node_type'> 1660 <value>unknown</value> 1661 </field> 1662 </x> 1663 </default> 1664 1665 </pubsub> 1666 </iq> 1667 """ 1668 1669 def getDefaultConfiguration(requestor, service, nodeType): 1670 self.fail("Unexpected call to getConfiguration") 1671 1672 def cb(result): 1673 self.assertEquals('not-acceptable', result.condition) 1674 1675 self.service.getDefaultConfiguration = getDefaultConfiguration 1676 verify.verifyObject(iwokkel.IPubSubService, self.service) 1677 d = self.handleRequest(xml) 1678 self.assertFailure(d, error.StanzaError) 1679 d.addCallback(cb) 1680 return d 1681 1682 734 1683 def test_onConfigureGet(self): 735 1684 """ 736 1685 On a node configuration get request L{PubSubService.getConfiguration} … … 760 1709 } 761 1710 762 1711 def getConfiguration(requestor, service, nodeIdentifier): 763 self.assertEqual(JID('user@example.org'), requestor)764 self.assertEqual(JID('pubsub.example.org'), service)765 self.assertEqual('test', nodeIdentifier)766 767 1712 return defer.succeed({'pubsub#deliver_payloads': '0', 768 1713 'pubsub#persist_items': '1', 769 'pubsub#owner': JID('user@example.org')}) 1714 'pubsub#owner': JID('user@example.org'), 1715 'x-myfield': ['a', 'b']}) 770 1716 771 1717 def cb(element): 772 1718 self.assertEqual('pubsub', element.name) … … 794 1740 field.typeCheck() 795 1741 self.assertEqual(JID('user@example.org'), field.value) 796 1742 1743 self.assertNotIn('x-myfield', fields) 1744 1745 797 1746 self.service.getConfigurationOptions = getConfigurationOptions 798 1747 self.service.getConfiguration = getConfiguration 1748 verify.verifyObject(iwokkel.IPubSubService, self.service) 799 1749 d = self.handleRequest(xml) 800 1750 d.addCallback(cb) 801 1751 return d … … 835 1785 } 836 1786 837 1787 def setConfiguration(requestor, service, nodeIdentifier, options): 838 self.assertEqual(JID('user@example.org'), requestor)839 self.assertEqual(JID('pubsub.example.org'), service)840 self.assertEqual('test', nodeIdentifier)841 1788 self.assertEqual({'pubsub#deliver_payloads': False, 842 1789 'pubsub#persist_items': True}, options) 843 1790 return defer.succeed(None) 844 1791 845 1792 self.service.getConfigurationOptions = getConfigurationOptions 846 1793 self.service.setConfiguration = setConfiguration 1794 verify.verifyObject(iwokkel.IPubSubService, self.service) 847 1795 return self.handleRequest(xml) 848 1796 849 1797 … … 872 1820 self.fail("Unexpected call to setConfiguration") 873 1821 874 1822 self.service.setConfiguration = setConfiguration 1823 verify.verifyObject(iwokkel.IPubSubService, self.service) 875 1824 return self.handleRequest(xml) 876 1825 877 1826 … … 912 1861 913 1862 self.service.getConfigurationOptions = getConfigurationOptions 914 1863 self.service.setConfiguration = setConfiguration 1864 verify.verifyObject(iwokkel.IPubSubService, self.service) 915 1865 return self.handleRequest(xml) 916 1866 917 1867 1868 def test_onConfigureSetBadFormType(self): 1869 """ 1870 On a node configuration set request unknown fields should be ignored. 1871 """ 1872 1873 xml = """ 1874 <iq type='set' to='pubsub.example.org' 1875 from='user@example.org'> 1876 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 1877 <configure node='test'> 1878 <x xmlns='jabber:x:data' type='result'> 1879 <field var='FORM_TYPE' type='hidden'> 1880 <value>http://jabber.org/protocol/pubsub#node_config</value> 1881 </field> 1882 <field var='pubsub#deliver_payloads'><value>0</value></field> 1883 <field var='x-myfield'><value>1</value></field> 1884 </x> 1885 </configure> 1886 </pubsub> 1887 </iq> 1888 """ 1889 1890 def cb(result): 1891 self.assertEquals('bad-request', result.condition) 1892 1893 d = self.handleRequest(xml) 1894 self.assertFailure(d, error.StanzaError) 1895 d.addCallback(cb) 1896 return d 1897 1898 918 1899 def test_onItems(self): 919 1900 """ 920 1901 On a items request, return all items for the given node. … … 928 1909 </iq> 929 1910 """ 930 1911 931 def items(requestor, service, nodeIdentifier, maxItems, items): 932 self.assertEqual(JID('user@example.org'), requestor) 933 self.assertEqual(JID('pubsub.example.org'), service) 934 self.assertEqual('test', nodeIdentifier) 935 self.assertIdentical(None, maxItems) 936 self.assertEqual([], items) 1912 def items(requestor, service, nodeIdentifier, maxItems, 1913 itemIdentifiers): 937 1914 return defer.succeed([pubsub.Item('current')]) 938 1915 939 1916 def cb(element): … … 971 1948 """ 972 1949 973 1950 def retract(requestor, service, nodeIdentifier, itemIdentifiers): 974 self.assertEqual(JID('user@example.org'), requestor)975 self.assertEqual(JID('pubsub.example.org'), service)976 self.assertEqual('test', nodeIdentifier)977 self.assertEqual(['item1', 'item2'], itemIdentifiers)978 1951 return defer.succeed(None) 979 1952 980 1953 self.service.retract = retract … … 997 1970 """ 998 1971 999 1972 def purge(requestor, service, nodeIdentifier): 1000 self.assertEqual(JID('user@example.org'), requestor)1001 self.assertEqual(JID('pubsub.example.org'), service)1002 self.assertEqual('test', nodeIdentifier)1003 1973 return defer.succeed(None) 1004 1974 1005 1975 self.service.purge = purge … … 1022 1992 """ 1023 1993 1024 1994 def delete(requestor, service, nodeIdentifier): 1025 self.assertEqual(JID('user@example.org'), requestor)1026 self.assertEqual(JID('pubsub.example.org'), service)1027 self.assertEqual('test', nodeIdentifier)1028 1995 return defer.succeed(None) 1029 1996 1030 1997 self.service.delete = delete … … 1076 2043 self.assertEqual(NS_PUBSUB_EVENT, message.event.delete.redirect.uri) 1077 2044 self.assertTrue(message.event.delete.redirect.hasAttribute('uri')) 1078 2045 self.assertEqual(redirectURI, message.event.delete.redirect['uri']) 2046 2047 2048 def test_onSubscriptionsGet(self): 2049 """ 2050 Getting subscription options is not supported. 2051 """ 2052 2053 xml = """ 2054 <iq type='get' to='pubsub.example.org' 2055 from='user@example.org'> 2056 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2057 <subscriptions/> 2058 </pubsub> 2059 </iq> 2060 """ 2061 2062 def cb(result): 2063 self.assertEquals('feature-not-implemented', result.condition) 2064 self.assertEquals('unsupported', result.appCondition.name) 2065 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2066 self.assertEquals('manage-subscriptions', 2067 result.appCondition['feature']) 2068 2069 d = self.handleRequest(xml) 2070 self.assertFailure(d, error.StanzaError) 2071 d.addCallback(cb) 2072 return d 2073 2074 2075 def test_onSubscriptionsSet(self): 2076 """ 2077 Setting subscription options is not supported. 2078 """ 2079 2080 xml = """ 2081 <iq type='set' to='pubsub.example.org' 2082 from='user@example.org'> 2083 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2084 <subscriptions/> 2085 </pubsub> 2086 </iq> 2087 """ 2088 2089 def cb(result): 2090 self.assertEquals('feature-not-implemented', result.condition) 2091 self.assertEquals('unsupported', result.appCondition.name) 2092 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2093 self.assertEquals('manage-subscriptions', 2094 result.appCondition['feature']) 2095 2096 d = self.handleRequest(xml) 2097 self.assertFailure(d, error.StanzaError) 2098 d.addCallback(cb) 2099 return d 2100 2101 2102 def test_onAffiliationsGet(self): 2103 """ 2104 Getting subscription options is not supported. 2105 """ 2106 2107 xml = """ 2108 <iq type='get' to='pubsub.example.org' 2109 from='user@example.org'> 2110 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2111 <affiliations/> 2112 </pubsub> 2113 </iq> 2114 """ 2115 2116 def cb(result): 2117 self.assertEquals('feature-not-implemented', result.condition) 2118 self.assertEquals('unsupported', result.appCondition.name) 2119 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2120 self.assertEquals('modify-affiliations', 2121 result.appCondition['feature']) 2122 2123 d = self.handleRequest(xml) 2124 self.assertFailure(d, error.StanzaError) 2125 d.addCallback(cb) 2126 return d 2127 2128 2129 def test_onAffiliationsSet(self): 2130 """ 2131 Setting subscription options is not supported. 2132 """ 2133 2134 xml = """ 2135 <iq type='set' to='pubsub.example.org' 2136 from='user@example.org'> 2137 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2138 <affiliations/> 2139 </pubsub> 2140 </iq> 2141 """ 2142 2143 def cb(result): 2144 self.assertEquals('feature-not-implemented', result.condition) 2145 self.assertEquals('unsupported', result.appCondition.name) 2146 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2147 self.assertEquals('modify-affiliations', 2148 result.appCondition['feature']) 2149 2150 d = self.handleRequest(xml) 2151 self.assertFailure(d, error.StanzaError) 2152 d.addCallback(cb) 2153 return d 2154 2155 2156 def test_publish(self): 2157 """ 2158 Non-overridden L{PubSubService.publish} yields unsupported error. 2159 """ 2160 2161 xml = """ 2162 <iq type='set' to='pubsub.example.org' 2163 from='user@example.org'> 2164 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2165 <publish node='mynode'/> 2166 </pubsub> 2167 </iq> 2168 """ 2169 2170 def cb(result): 2171 self.assertEquals('feature-not-implemented', result.condition) 2172 self.assertEquals('unsupported', result.appCondition.name) 2173 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2174 self.assertEquals('publish', result.appCondition['feature']) 2175 2176 d = self.handleRequest(xml) 2177 self.assertFailure(d, error.StanzaError) 2178 d.addCallback(cb) 2179 return d 2180 2181 2182 def test_subscribe(self): 2183 """ 2184 Non-overridden L{PubSubService.subscribe} yields unsupported error. 2185 """ 2186 2187 xml = """ 2188 <iq type='set' to='pubsub.example.org' 2189 from='user@example.org'> 2190 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2191 <subscribe node='test' jid='user@example.org/Home'/> 2192 </pubsub> 2193 </iq> 2194 """ 2195 2196 def cb(result): 2197 self.assertEquals('feature-not-implemented', result.condition) 2198 self.assertEquals('unsupported', result.appCondition.name) 2199 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2200 self.assertEquals('subscribe', result.appCondition['feature']) 2201 2202 d = self.handleRequest(xml) 2203 self.assertFailure(d, error.StanzaError) 2204 d.addCallback(cb) 2205 return d 2206 2207 2208 def test_unsubscribe(self): 2209 """ 2210 Non-overridden L{PubSubService.unsubscribe} yields unsupported error. 2211 """ 2212 2213 xml = """ 2214 <iq type='set' to='pubsub.example.org' 2215 from='user@example.org'> 2216 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2217 <unsubscribe node='test' jid='user@example.org/Home'/> 2218 </pubsub> 2219 </iq> 2220 """ 2221 2222 def cb(result): 2223 self.assertEquals('feature-not-implemented', result.condition) 2224 self.assertEquals('unsupported', result.appCondition.name) 2225 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2226 self.assertEquals('subscribe', result.appCondition['feature']) 2227 2228 d = self.handleRequest(xml) 2229 self.assertFailure(d, error.StanzaError) 2230 d.addCallback(cb) 2231 return d 2232 2233 2234 def test_subscriptions(self): 2235 """ 2236 Non-overridden L{PubSubService.subscriptions} yields unsupported error. 2237 """ 2238 2239 xml = """ 2240 <iq type='get' to='pubsub.example.org' 2241 from='user@example.org'> 2242 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2243 <subscriptions/> 2244 </pubsub> 2245 </iq> 2246 """ 2247 2248 def cb(result): 2249 self.assertEquals('feature-not-implemented', result.condition) 2250 self.assertEquals('unsupported', result.appCondition.name) 2251 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2252 self.assertEquals('retrieve-subscriptions', 2253 result.appCondition['feature']) 2254 2255 d = self.handleRequest(xml) 2256 self.assertFailure(d, error.StanzaError) 2257 d.addCallback(cb) 2258 return d 2259 2260 2261 def test_affiliations(self): 2262 """ 2263 Non-overridden L{PubSubService.affiliations} yields unsupported error. 2264 """ 2265 2266 xml = """ 2267 <iq type='get' to='pubsub.example.org' 2268 from='user@example.org'> 2269 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2270 <affiliations/> 2271 </pubsub> 2272 </iq> 2273 """ 2274 2275 def cb(result): 2276 self.assertEquals('feature-not-implemented', result.condition) 2277 self.assertEquals('unsupported', result.appCondition.name) 2278 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2279 self.assertEquals('retrieve-affiliations', 2280 result.appCondition['feature']) 2281 2282 d = self.handleRequest(xml) 2283 self.assertFailure(d, error.StanzaError) 2284 d.addCallback(cb) 2285 return d 2286 2287 2288 def test_create(self): 2289 """ 2290 Non-overridden L{PubSubService.create} yields unsupported error. 2291 """ 2292 2293 xml = """ 2294 <iq type='set' to='pubsub.example.org' 2295 from='user@example.org'> 2296 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2297 <create node='mynode'/> 2298 </pubsub> 2299 </iq> 2300 """ 2301 2302 def cb(result): 2303 self.assertEquals('feature-not-implemented', result.condition) 2304 self.assertEquals('unsupported', result.appCondition.name) 2305 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2306 self.assertEquals('create-nodes', result.appCondition['feature']) 2307 2308 d = self.handleRequest(xml) 2309 self.assertFailure(d, error.StanzaError) 2310 d.addCallback(cb) 2311 return d 2312 2313 2314 def test_getDefaultConfiguration(self): 2315 """ 2316 Non-overridden L{PubSubService.getDefaultConfiguration} yields 2317 unsupported error. 2318 """ 2319 2320 xml = """ 2321 <iq type='get' to='pubsub.example.org' 2322 from='user@example.org'> 2323 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2324 <default/> 2325 </pubsub> 2326 </iq> 2327 """ 2328 2329 def cb(result): 2330 self.assertEquals('feature-not-implemented', result.condition) 2331 self.assertEquals('unsupported', result.appCondition.name) 2332 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2333 self.assertEquals('retrieve-default', result.appCondition['feature']) 2334 2335 d = self.handleRequest(xml) 2336 self.assertFailure(d, error.StanzaError) 2337 d.addCallback(cb) 2338 return d 2339 2340 2341 def test_getConfiguration(self): 2342 """ 2343 Non-overridden L{PubSubService.getConfiguration} yields unsupported 2344 error. 2345 """ 2346 2347 xml = """ 2348 <iq type='get' to='pubsub.example.org' 2349 from='user@example.org'> 2350 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2351 <configure/> 2352 </pubsub> 2353 </iq> 2354 """ 2355 2356 def cb(result): 2357 self.assertEquals('feature-not-implemented', result.condition) 2358 self.assertEquals('unsupported', result.appCondition.name) 2359 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2360 self.assertEquals('config-node', result.appCondition['feature']) 2361 2362 d = self.handleRequest(xml) 2363 self.assertFailure(d, error.StanzaError) 2364 d.addCallback(cb) 2365 return d 2366 2367 2368 def test_setConfiguration(self): 2369 """ 2370 Non-overridden L{PubSubService.setConfiguration} yields unsupported 2371 error. 2372 """ 2373 2374 xml = """ 2375 <iq type='set' to='pubsub.example.org' 2376 from='user@example.org'> 2377 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2378 <configure node='test'> 2379 <x xmlns='jabber:x:data' type='submit'> 2380 <field var='FORM_TYPE' type='hidden'> 2381 <value>http://jabber.org/protocol/pubsub#node_config</value> 2382 </field> 2383 <field var='pubsub#deliver_payloads'><value>0</value></field> 2384 <field var='pubsub#persist_items'><value>1</value></field> 2385 </x> 2386 </configure> 2387 </pubsub> 2388 </iq> 2389 """ 2390 2391 def cb(result): 2392 self.assertEquals('feature-not-implemented', result.condition) 2393 self.assertEquals('unsupported', result.appCondition.name) 2394 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2395 self.assertEquals('config-node', result.appCondition['feature']) 2396 2397 d = self.handleRequest(xml) 2398 self.assertFailure(d, error.StanzaError) 2399 d.addCallback(cb) 2400 return d 2401 2402 2403 def test_items(self): 2404 """ 2405 Non-overridden L{PubSubService.items} yields unsupported error. 2406 """ 2407 xml = """ 2408 <iq type='get' to='pubsub.example.org' 2409 from='user@example.org'> 2410 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2411 <items node='test'/> 2412 </pubsub> 2413 </iq> 2414 """ 2415 2416 def cb(result): 2417 self.assertEquals('feature-not-implemented', result.condition) 2418 self.assertEquals('unsupported', result.appCondition.name) 2419 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2420 self.assertEquals('retrieve-items', result.appCondition['feature']) 2421 2422 d = self.handleRequest(xml) 2423 self.assertFailure(d, error.StanzaError) 2424 d.addCallback(cb) 2425 return d 2426 2427 2428 def test_retract(self): 2429 """ 2430 Non-overridden L{PubSubService.retract} yields unsupported error. 2431 """ 2432 xml = """ 2433 <iq type='set' to='pubsub.example.org' 2434 from='user@example.org'> 2435 <pubsub xmlns='http://jabber.org/protocol/pubsub'> 2436 <retract node='test'> 2437 <item id='item1'/> 2438 <item id='item2'/> 2439 </retract> 2440 </pubsub> 2441 </iq> 2442 """ 2443 2444 def cb(result): 2445 self.assertEquals('feature-not-implemented', result.condition) 2446 self.assertEquals('unsupported', result.appCondition.name) 2447 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2448 self.assertEquals('retract-items', result.appCondition['feature']) 2449 2450 d = self.handleRequest(xml) 2451 self.assertFailure(d, error.StanzaError) 2452 d.addCallback(cb) 2453 return d 2454 2455 2456 def test_purge(self): 2457 """ 2458 Non-overridden L{PubSubService.purge} yields unsupported error. 2459 """ 2460 xml = """ 2461 <iq type='set' to='pubsub.example.org' 2462 from='user@example.org'> 2463 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2464 <purge node='test'/> 2465 </pubsub> 2466 </iq> 2467 """ 2468 2469 def cb(result): 2470 self.assertEquals('feature-not-implemented', result.condition) 2471 self.assertEquals('unsupported', result.appCondition.name) 2472 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2473 self.assertEquals('purge-nodes', result.appCondition['feature']) 2474 2475 d = self.handleRequest(xml) 2476 self.assertFailure(d, error.StanzaError) 2477 d.addCallback(cb) 2478 return d 2479 2480 2481 def test_delete(self): 2482 """ 2483 Non-overridden L{PubSubService.delete} yields unsupported error. 2484 """ 2485 xml = """ 2486 <iq type='set' to='pubsub.example.org' 2487 from='user@example.org'> 2488 <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'> 2489 <delete node='test'/> 2490 </pubsub> 2491 </iq> 2492 """ 2493 2494 def cb(result): 2495 self.assertEquals('feature-not-implemented', result.condition) 2496 self.assertEquals('unsupported', result.appCondition.name) 2497 self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri) 2498 self.assertEquals('delete-nodes', result.appCondition['feature']) 2499 2500 d = self.handleRequest(xml) 2501 self.assertFailure(d, error.StanzaError) 2502 d.addCallback(cb) 2503 return d
Note: See TracBrowser
for help on using the repository browser.