Changeset 46:4ee1f9c08b22
- Timestamp:
- Dec 26, 2008, 2:40:11 PM (13 years ago)
- Branch:
- default
- Convert:
- svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@140
- Location:
- wokkel
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
wokkel/disco.py
r31 r46 18 18 from wokkel.subprotocols import IQHandlerMixin, XMPPHandler 19 19 20 NS = 'http://jabber.org/protocol/disco'21 NS_ INFO = NS+ '#info'22 NS_ ITEMS = NS+ '#items'20 NS_DISCO = 'http://jabber.org/protocol/disco' 21 NS_DISCO_INFO = NS_DISCO + '#info' 22 NS_DISCO_ITEMS = NS_DISCO + '#items' 23 23 24 24 IQ_GET = '/iq[@type="get"]' 25 DISCO_INFO = IQ_GET + '/query[@xmlns="' + NS_ INFO + '"]'26 DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_ ITEMS + '"]'25 DISCO_INFO = IQ_GET + '/query[@xmlns="' + NS_DISCO_INFO + '"]' 26 DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_DISCO_ITEMS + '"]' 27 27 28 28 class DiscoFeature(domish.Element): … … 32 32 33 33 def __init__(self, feature): 34 domish.Element.__init__(self, (NS_ INFO, 'feature'),34 domish.Element.__init__(self, (NS_DISCO_INFO, 'feature'), 35 35 attribs={'var': feature}) 36 36 … … 42 42 43 43 def __init__(self, category, type, name = None): 44 domish.Element.__init__(self, (NS_ INFO, 'identity'),44 domish.Element.__init__(self, (NS_DISCO_INFO, 'identity'), 45 45 attribs={'category': category, 46 46 'type': type}) … … 55 55 56 56 def __init__(self, jid, node='', name=None): 57 domish.Element.__init__(self, (NS_ ITEMS, 'item'),57 domish.Element.__init__(self, (NS_DISCO_ITEMS, 'item'), 58 58 attribs={'jid': jid.full()}) 59 59 if node: … … 64 64 65 65 66 66 67 class DiscoHandler(XMPPHandler, IQHandlerMixin): 67 68 """ … … 80 81 self.xmlstream.addObserver(DISCO_ITEMS, self.handleRequest) 81 82 82 def _error(self, failure):83 failure.trap(defer.FirstError)84 return failure.value.subFailure85 83 86 84 def _onDiscoInfo(self, iq): 85 """ 86 Called for incoming disco info requests. 87 88 @param iq: The request iq element. 89 @type iq: L{Element<twisted.words.xish.domish.Element>} 90 """ 87 91 requestor = jid.internJID(iq["from"]) 88 92 target = jid.internJID(iq["to"]) 89 93 nodeIdentifier = iq.query.getAttribute("node", '') 90 94 91 def toResponse(results): 92 info = [] 93 for i in results: 94 info.extend(i[1]) 95 95 def toResponse(info): 96 96 if nodeIdentifier and not info: 97 97 raise error.StanzaError('item-not-found') 98 98 else: 99 response = domish.Element((NS_INFO, 'query')) 99 response = domish.Element((NS_DISCO_INFO, 'query')) 100 if nodeIdentifier: 101 response['node'] = nodeIdentifier 100 102 101 103 for item in info: … … 104 106 return response 105 107 106 dl = [] 107 for handler in self.parent: 108 if IDisco.providedBy(handler): 109 dl.append(handler.getDiscoInfo(requestor, target, 110 nodeIdentifier)) 111 112 d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1) 113 d.addCallbacks(toResponse, self._error) 108 d = self.info(requestor, target, nodeIdentifier) 109 d.addCallback(toResponse) 114 110 return d 115 111 112 116 113 def _onDiscoItems(self, iq): 114 """ 115 Called for incoming disco items requests. 116 117 @param iq: The request iq element. 118 @type iq: L{Element<twisted.words.xish.domish.Element>} 119 """ 117 120 requestor = jid.internJID(iq["from"]) 118 121 target = jid.internJID(iq["to"]) 119 122 nodeIdentifier = iq.query.getAttribute("node", '') 120 123 121 def toResponse(results): 122 items = [] 123 for i in results: 124 items.extend(i[1]) 125 126 response = domish.Element((NS_ITEMS, 'query')) 124 def toResponse(items): 125 response = domish.Element((NS_DISCO_ITEMS, 'query')) 126 if nodeIdentifier: 127 response['node'] = nodeIdentifier 127 128 128 129 for item in items: … … 131 132 return response 132 133 133 dl = [] 134 for handler in self.parent: 135 if IDisco.providedBy(handler): 136 dl.append(handler.getDiscoItems(requestor, target, 137 nodeIdentifier)) 138 139 d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1) 140 d.addCallbacks(toResponse, self._error) 134 d = self.items(requestor, target, nodeIdentifier) 135 d.addCallback(toResponse) 141 136 return d 137 138 139 def _gatherResults(self, deferredList): 140 """ 141 Gather results from a list of deferreds. 142 143 Similar to L{defer.gatherResults}, but flattens the returned results, 144 consumes errors after the first one and fires the errback of the 145 returned deferred with the failure of the first deferred that fires its 146 errback. 147 148 @param deferredList: List of deferreds for which the results should be 149 gathered. 150 @type deferredList: C{list} 151 @return: Deferred that fires with a list of gathered results. 152 @rtype: L{defer.Deferred} 153 """ 154 def cb(resultList): 155 results = [] 156 for success, value in resultList: 157 results.extend(value) 158 return results 159 160 def eb(failure): 161 failure.trap(defer.FirstError) 162 return failure.value.subFailure 163 164 d = defer.DeferredList(deferredList, fireOnOneErrback=1, 165 consumeErrors=1) 166 d.addCallbacks(cb, eb) 167 return d 168 169 170 def info(self, requestor, target, nodeIdentifier): 171 """ 172 Inspect all sibling protocol handlers for disco info. 173 174 Calls the L{getDiscoInfo<IDisco.getDiscoInfo>} method on all child 175 handlers of the parent, that provide L{IDisco}. 176 177 @param requestor: The entity that sent the request. 178 @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>} 179 @param target: The entity the request was sent to. 180 @type target: L{JID<twisted.words.protocols.jabber.jid.JID>} 181 @param nodeIdentifier: The optional node being queried, or C{''}. 182 @type nodeIdentifier: C{unicode} 183 @return: Deferred with the gathered results from sibling handlers. 184 @rtype: L{defer.Deferred} 185 """ 186 dl = [handler.getDiscoInfo(requestor, target, nodeIdentifier) 187 for handler in self.parent 188 if IDisco.providedBy(handler)] 189 return self._gatherResults(dl) 190 191 192 def items(self, requestor, target, nodeIdentifier): 193 """ 194 Inspect all sibling protocol handlers for disco items. 195 196 Calls the L{getDiscoItems<IDisco.getDiscoItems>} method on all child 197 handlers of the parent, that provide L{IDisco}. 198 199 @param requestor: The entity that sent the request. 200 @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>} 201 @param target: The entity the request was sent to. 202 @type target: L{JID<twisted.words.protocols.jabber.jid.JID>} 203 @param nodeIdentifier: The optional node being queried, or C{''}. 204 @type nodeIdentifier: C{unicode} 205 @return: Deferred with the gathered results from sibling handlers. 206 @rtype: L{defer.Deferred} 207 """ 208 dl = [handler.getDiscoItems(requestor, target, nodeIdentifier) 209 for handler in self.parent 210 if IDisco.providedBy(handler)] 211 return self._gatherResults(dl) -
wokkel/test/helpers.py
r10 r46 6 6 """ 7 7 8 from twisted.internet import defer 9 from twisted.words.xish import xpath 8 10 from twisted.words.xish.utility import EventDispatcher 11 12 from wokkel.generic import parseXml 9 13 10 14 class XmlStreamStub(object): … … 55 59 """ 56 60 self.xmlstream.dispatch(obj) 61 62 63 class TestableRequestHandlerMixin(object): 64 """ 65 Mixin for testing XMPPHandlers that process iq requests. 66 67 Handlers that use L{wokkel.subprotocols.IQHandlerMixin} define a 68 C{iqHandlers} attribute that lists the handlers to be called for iq 69 requests. This mixin provides L{handleRequest} to mimic the handler 70 processing for easier testing. 71 """ 72 73 def handleRequest(self, xml): 74 """ 75 Find a handler and call it directly. 76 77 @param xml: XML stanza that may yield a handler being called. 78 @type xml: C{str}. 79 @return: Deferred that fires with the result of a handler for this 80 stanza. If no handler was found, the deferred has its errback 81 called with a C{NotImplementedError} exception. 82 """ 83 handler = None 84 iq = parseXml(xml) 85 for queryString, method in self.service.iqHandlers.iteritems(): 86 if xpath.internQuery(queryString).matches(iq): 87 handler = getattr(self.service, method) 88 89 if handler: 90 d = defer.maybeDeferred(handler, iq) 91 else: 92 d = defer.fail(NotImplementedError()) 93 94 return d -
wokkel/test/test_disco.py
r31 r46 8 8 from twisted.internet import defer 9 9 from twisted.trial import unittest 10 from twisted.words. xish.xmlstream import XmlStreamFactory10 from twisted.words.protocols.jabber.jid import JID 11 11 from zope.interface import implements 12 12 13 from wokkel.subprotocols import XMPPHandler, StreamManager14 15 13 from wokkel import disco 14 from wokkel.subprotocols import XMPPHandler 15 from wokkel.test.helpers import TestableRequestHandlerMixin 16 16 17 17 18 NS_DISCO_INFO = 'http://jabber.org/protocol/disco#info' 18 19 NS_DISCO_ITEMS = 'http://jabber.org/protocol/disco#items' 19 20 20 class DiscoResponder(XMPPHandler): 21 implements(disco.IDisco) 22 23 def getDiscoInfo(self, requestor, target, nodeIdentifier): 24 if not nodeIdentifier: 21 22 class DiscoHandlerTest(unittest.TestCase, TestableRequestHandlerMixin): 23 """ 24 Tests for L{disco.DiscoHandler}. 25 """ 26 27 def setUp(self): 28 self.service = disco.DiscoHandler() 29 30 31 def test_onDiscoInfo(self): 32 """ 33 C{onDiscoInfo} should process an info request and return a response. 34 35 The request should be parsed, C{info} called with the extracted 36 parameters, and then the result should be formatted into a proper 37 response element. 38 """ 39 xml = """<iq from='test@example.com' to='example.com' 40 type='get'> 41 <query xmlns='%s'/> 42 </iq>""" % NS_DISCO_INFO 43 44 def cb(element): 45 self.assertEqual('query', element.name) 46 self.assertEqual(NS_DISCO_INFO, element.uri) 47 self.assertEqual(NS_DISCO_INFO, element.identity.uri) 48 self.assertEqual('dummy', element.identity['category']) 49 self.assertEqual('generic', element.identity['type']) 50 self.assertEqual('Generic Dummy Entity', element.identity['name']) 51 self.assertEqual(NS_DISCO_INFO, element.feature.uri) 52 self.assertEqual('jabber:iq:version', element.feature['var']) 53 54 def info(requestor, target, nodeIdentifier): 55 self.assertEqual(JID('test@example.com'), requestor) 56 self.assertEqual(JID('example.com'), target) 57 self.assertEqual('', nodeIdentifier) 58 25 59 return defer.succeed([ 26 60 disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), 27 61 disco.DiscoFeature('jabber:iq:version') 28 62 ]) 29 else: 30 return defer.succeed([]) 31 32 class DiscoHandlerTest(unittest.TestCase): 33 def test_DiscoInfo(self): 34 factory = XmlStreamFactory() 35 sm = StreamManager(factory) 36 disco.DiscoHandler().setHandlerParent(sm) 37 DiscoResponder().setHandlerParent(sm) 38 xs = factory.buildProtocol(None) 39 output = [] 40 xs.send = output.append 41 xs.connectionMade() 42 xs.dispatch(xs, "//event/stream/authd") 43 xs.dataReceived("<stream>") 44 xs.dataReceived("""<iq from='test@example.com' to='example.com' 45 type='get'> 46 <query xmlns='%s'/> 47 </iq>""" % NS_DISCO_INFO) 48 reply = output[0] 49 self.assertEqual(NS_DISCO_INFO, reply.query.uri) 50 self.assertEqual(NS_DISCO_INFO, reply.query.identity.uri) 51 self.assertEqual('dummy', reply.query.identity['category']) 52 self.assertEqual('generic', reply.query.identity['type']) 53 self.assertEqual('Generic Dummy Entity', reply.query.identity['name']) 54 self.assertEqual(NS_DISCO_INFO, reply.query.feature.uri) 55 self.assertEqual('jabber:iq:version', reply.query.feature['var']) 56 63 64 self.service.info = info 65 d = self.handleRequest(xml) 66 d.addCallback(cb) 67 return d 68 69 def test_onDiscoInfoWithNode(self): 70 """ 71 An info request for a node should return it in the response. 72 """ 73 xml = """<iq from='test@example.com' to='example.com' 74 type='get'> 75 <query xmlns='%s' node='test'/> 76 </iq>""" % NS_DISCO_INFO 77 78 def cb(element): 79 self.assertTrue(element.hasAttribute('node')) 80 self.assertEqual('test', element['node']) 81 82 def info(requestor, target, nodeIdentifier): 83 self.assertEqual('test', nodeIdentifier) 84 85 return defer.succeed([ 86 disco.DiscoFeature('jabber:iq:version') 87 ]) 88 89 self.service.info = info 90 d = self.handleRequest(xml) 91 d.addCallback(cb) 92 return d 93 94 95 def test_onDiscoItems(self): 96 """ 97 C{onDiscoItems} should process an items request and return a response. 98 99 The request should be parsed, C{items} called with the extracted 100 parameters, and then the result should be formatted into a proper 101 response element. 102 """ 103 xml = """<iq from='test@example.com' to='example.com' 104 type='get'> 105 <query xmlns='%s'/> 106 </iq>""" % NS_DISCO_ITEMS 107 108 def cb(element): 109 self.assertEqual('query', element.name) 110 self.assertEqual(NS_DISCO_ITEMS, element.uri) 111 self.assertEqual(NS_DISCO_ITEMS, element.item.uri) 112 self.assertEqual('example.com', element.item['jid']) 113 self.assertEqual('test', element.item['node']) 114 self.assertEqual('Test node', element.item['name']) 115 116 def items(requestor, target, nodeIdentifier): 117 self.assertEqual(JID('test@example.com'), requestor) 118 self.assertEqual(JID('example.com'), target) 119 self.assertEqual('', nodeIdentifier) 120 121 return defer.succeed([ 122 disco.DiscoItem(JID('example.com'), 'test', 'Test node'), 123 ]) 124 125 self.service.items = items 126 d = self.handleRequest(xml) 127 d.addCallback(cb) 128 return d 129 130 def test_onDiscoItemsWithNode(self): 131 """ 132 An items request for a node should return it in the response. 133 """ 134 xml = """<iq from='test@example.com' to='example.com' 135 type='get'> 136 <query xmlns='%s' node='test'/> 137 </iq>""" % NS_DISCO_ITEMS 138 139 def cb(element): 140 self.assertTrue(element.hasAttribute('node')) 141 self.assertEqual('test', element['node']) 142 143 def items(requestor, target, nodeIdentifier): 144 self.assertEqual('test', nodeIdentifier) 145 146 return defer.succeed([ 147 disco.DiscoFeature('jabber:iq:version') 148 ]) 149 150 self.service.items = items 151 d = self.handleRequest(xml) 152 d.addCallback(cb) 153 return d 154 155 156 def test_info(self): 157 """ 158 C{info} should gather disco info from sibling handlers. 159 """ 160 discoItems = [disco.DiscoIdentity('dummy', 'generic', 161 'Generic Dummy Entity'), 162 disco.DiscoFeature('jabber:iq:version') 163 ] 164 165 class DiscoResponder(XMPPHandler): 166 implements(disco.IDisco) 167 168 def getDiscoInfo(self, requestor, target, nodeIdentifier): 169 if not nodeIdentifier: 170 return defer.succeed(discoItems) 171 else: 172 return defer.succeed([]) 173 174 def cb(result): 175 self.assertEquals(discoItems, result) 176 177 self.service.parent = [self.service, DiscoResponder()] 178 d = self.service.info(JID('test@example.com'), JID('example.com'), '') 179 d.addCallback(cb) 180 return d 181 182 183 def test_items(self): 184 """ 185 C{info} should gather disco items from sibling handlers. 186 """ 187 discoItems = [disco.DiscoItem(JID('example.com'), 'test', 'Test node')] 188 189 class DiscoResponder(XMPPHandler): 190 implements(disco.IDisco) 191 192 def getDiscoItems(self, requestor, target, nodeIdentifier): 193 if not nodeIdentifier: 194 return defer.succeed(discoItems) 195 else: 196 return defer.succeed([]) 197 198 def cb(result): 199 self.assertEquals(discoItems, result) 200 201 self.service.parent = [self.service, DiscoResponder()] 202 d = self.service.items(JID('test@example.com'), JID('example.com'), '') 203 d.addCallback(cb) 204 return d -
wokkel/test/test_pubsub.py
r43 r46 10 10 from twisted.trial import unittest 11 11 from twisted.internet import defer 12 from twisted.words.xish import domish , xpath12 from twisted.words.xish import domish 13 13 from twisted.words.protocols.jabber import error 14 14 from twisted.words.protocols.jabber.jid import JID 15 15 16 16 from wokkel import data_form, iwokkel, pubsub, shim 17 from wokkel.generic import parseXml 18 from wokkel.test.helpers import XmlStreamStub 17 from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub 19 18 20 19 try: … … 492 491 493 492 494 class PubSubServiceTest(unittest.TestCase ):493 class PubSubServiceTest(unittest.TestCase, TestableRequestHandlerMixin): 495 494 """ 496 495 Tests for L{pubsub.PubSubService}. … … 501 500 self.service = pubsub.PubSubService() 502 501 self.service.send = self.stub.xmlstream.send 503 504 def handleRequest(self, xml):505 """506 Find a handler and call it directly507 """508 handler = None509 iq = parseXml(xml)510 for queryString, method in self.service.iqHandlers.iteritems():511 if xpath.internQuery(queryString).matches(iq):512 handler = getattr(self.service, method)513 514 if handler:515 d = defer.maybeDeferred(handler, iq)516 else:517 d = defer.fail(NotImplementedError())518 519 return d520 521 502 522 503 def test_interface(self):
Note: See TracChangeset
for help on using the changeset viewer.