source: ralphm-patches/pubsub_manage_affiliations.patch @ 46:5ec77d2dcdb4

Last change on this file since 46:5ec77d2dcdb4 was 46:5ec77d2dcdb4, checked in by Ralph Meijer <ralphm@…>, 12 years ago

Raise exception on duplicate entities, always use bare JIDs.

File size: 11.3 KB
RevLine 
[45]1# HG changeset patch
2# Parent f69b08281becebb5483f3f2dfb0451ad3086a463
[46]3Add support for managing affiliations of Publish-Subscribe nodes.
[45]4
5diff -r f69b08281bec wokkel/iwokkel.py
6--- a/wokkel/iwokkel.py Sat Feb 12 00:08:30 2011 +0100
[46]7+++ b/wokkel/iwokkel.py Sat Feb 12 19:16:00 2011 +0100
8@@ -767,3 +767,36 @@
[45]9                  deleted.
10         @rtype: L{defer.Deferred}
11         """
12+
13+
14+    def affiliationsGet(request):
15+        """
16+        Called when a affiliations retrieval request (owner) has been received.
17+
18+        @param request: The publish-subscribe request.
19+        @type request: L{wokkel.pubsub.PubSubRequest}
20+        @return: A deferred that fires with a C{dict} of affiliations with the
21+            entity as key (L{JID}) and the affiliation state as value
22+            (C{unicode}).  The affiliation can be C{u'owner'}, C{u'publisher'},
23+            or C{u'outcast'}.
24+        @rtype: L{defer.Deferred}
[46]25+
26+        @note: Affiliations are always on the bare JID. An implementation of
27+        this method MUST NOT return JIDs with a resource part.
28+        """
29+
30+
31+    def affiliationsSet(request):
32+        """
33+        Called when a affiliations modify request has been received.
34+
35+        @param request: The publish-subscribe request.
36+        @type request: L{wokkel.pubsub.PubSubRequest}
37+        @return: A deferred that fires with C{None} when the affiliation
38+            changes were succesfully processed..
39+        @rtype: L{defer.Deferred}
40+
41+        @note: Affiliations are always on the bare JID. The JIDs in
42+        L{wokkel.pubsub.PubSubRequest.affiliations} are already stripped of
43+        any resource.
[45]44+        """
45diff -r f69b08281bec wokkel/pubsub.py
46--- a/wokkel/pubsub.py  Sat Feb 12 00:08:30 2011 +0100
[46]47+++ b/wokkel/pubsub.py  Sat Feb 12 19:16:00 2011 +0100
[45]48@@ -220,6 +220,9 @@
49     @ivar subscriptions: Subscriptions to be modified, as a set of
50                          L{Subscription}.
51     @type subscriptions: C{set}
52+    @ivar affiliations: Affiliations to be modified, as a dictionary of entity
53+                        (L{JID} to affiliation (C{unicode}).
54+    @type affiliations: C{dict}
55     """
56 
57     verb = None
58@@ -234,6 +237,7 @@
59     subscriber = None
60     subscriptionIdentifier = None
61     subscriptions = None
62+    affiliations = None
63 
64     # Map request iq type and subelement name to request verb
65     _requestVerbMap = {
66@@ -279,7 +283,7 @@
67         'purge': ['node'],
68         'delete': ['node'],
69         'affiliationsGet': ['nodeOrEmpty'],
70-        'affiliationsSet': [],
71+        'affiliationsSet': ['nodeOrEmpty', 'affiliations'],
72         'subscriptionsGet': ['nodeOrEmpty'],
73         'subscriptionsSet': [],
74     }
[46]75@@ -526,6 +530,27 @@
[45]76             self._render_options(optionsElement)
77 
78 
79+    def _parse_affiliations(self, verbElement):
80+        self.affiliations = {}
81+        for element in verbElement.elements():
82+            if (element.uri == NS_PUBSUB_OWNER and
83+                element.name == 'affiliation'):
84+                try:
[46]85+                    entity = jid.internJID(element['jid']).userhostJID()
[45]86+                except KeyError:
87+                    raise BadRequest(text='Missing jid attribute')
[46]88+
89+                if entity in self.affiliations:
90+                    raise BadRequest(text='Multiple affiliations for an entity')
91+
[45]92+                try:
93+                    affiliation = element['affiliation']
94+                except KeyError:
95+                    raise BadRequest(text='Missing affiliation attribute')
[46]96+
[45]97+                self.affiliations[entity] = affiliation
98+
99+
100     def parseElement(self, element):
101         """
102         Parse the publish-subscribe verb and parameters out of a request.
[46]103@@ -1303,6 +1328,22 @@
[45]104 
105         return message
106 
107+
108+    def _toResponse_affiliationsGet(self, result, resource, request):
109+        response = domish.Element((NS_PUBSUB_OWNER, 'pubsub'))
110+        affiliations = response.addElement('affiliations')
111+
112+        if request.nodeIdentifier:
113+            affiliations['node'] = request.nodeIdentifier
114+
115+        for entity, affiliation in result.iteritems():
116+            item = affiliations.addElement('affiliation')
117+            item['jid'] = entity.full()
118+            item['affiliation'] = affiliation
119+
120+        return response
121+
122+
123     # public methods
124 
125     def notifyPublish(self, service, nodeIdentifier, notifications):
126diff -r f69b08281bec wokkel/test/test_pubsub.py
127--- a/wokkel/test/test_pubsub.py        Sat Feb 12 00:08:30 2011 +0100
[46]128+++ b/wokkel/test/test_pubsub.py        Sat Feb 12 19:16:00 2011 +0100
[45]129@@ -3192,7 +3192,45 @@
130 
131     def test_on_affiliationsGet(self):
132         """
133-        Getting subscription options is not supported.
134+        Getting node affiliations should have.
135+        """
136+
137+        xml = """
138+        <iq type='get' to='pubsub.example.org'
139+                       from='user@example.org'>
140+          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
141+            <affiliations node='test'/>
142+          </pubsub>
143+        </iq>
144+        """
145+
146+        def affiliationsGet(request):
147+            self.assertEquals('test', request.nodeIdentifier)
148+            return defer.succeed({JID('user@example.org'): 'owner'})
149+
150+        def cb(element):
151+            self.assertEquals(u'pubsub', element.name)
152+            self.assertEquals(NS_PUBSUB_OWNER, element.uri)
153+            self.assertEquals(NS_PUBSUB_OWNER, element.affiliations.uri)
154+            self.assertEquals(u'test', element.affiliations[u'node'])
155+            children = list(element.affiliations.elements())
156+            self.assertEquals(1, len(children))
157+            affiliation = children[0]
158+            self.assertEquals(u'affiliation', affiliation.name)
159+            self.assertEquals(NS_PUBSUB_OWNER, affiliation.uri)
160+            self.assertEquals(u'user@example.org', affiliation[u'jid'])
161+            self.assertEquals(u'owner', affiliation[u'affiliation'])
162+
163+        self.resource.affiliationsGet = affiliationsGet
164+        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
165+        d = self.handleRequest(xml)
166+        d.addCallback(cb)
167+        return d
168+
169+
170+    def test_on_affiliationsGetEmptyNode(self):
171+        """
172+        Getting node affiliations without node should assume empty node.
173         """
174 
175         xml = """
[46]176@@ -3204,39 +3242,90 @@
[45]177         </iq>
178         """
179 
180-        def cb(result):
181-            self.assertEquals('feature-not-implemented', result.condition)
182-            self.assertEquals('unsupported', result.appCondition.name)
183-            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
184-            self.assertEquals('modify-affiliations',
185-                              result.appCondition['feature'])
186-
187+        def affiliationsGet(request):
188+            self.assertIdentical('', request.nodeIdentifier)
189+            return defer.succeed({})
190+
191+        def cb(element):
192+            self.assertFalse(element.affiliations.hasAttribute(u'node'))
193+
194+        self.resource.affiliationsGet = affiliationsGet
195+        verify.verifyObject(iwokkel.IPubSubResource, self.resource)
196         d = self.handleRequest(xml)
197-        self.assertFailure(d, error.StanzaError)
198         d.addCallback(cb)
199         return d
200 
201 
202     def test_on_affiliationsSet(self):
203         """
204-        Setting subscription options is not supported.
205+        Setting node affiliations has the affiliations to be modified.
206         """
207 
208         xml = """
209         <iq type='set' to='pubsub.example.org'
210                        from='user@example.org'>
211           <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
212-            <affiliations/>
213+            <affiliations node='test'>
214+              <affiliation jid='other@example.org' affiliation='publisher'/>
215+            </affiliations>
216           </pubsub>
217         </iq>
218         """
219 
220+        def affiliationsSet(request):
221+            self.assertEquals(u'test', request.nodeIdentifier)
222+            otherJID = JID(u'other@example.org')
223+            self.assertIn(otherJID, request.affiliations)
224+            self.assertEquals(u'publisher', request.affiliations[otherJID])
225+
226+        self.resource.affiliationsSet = affiliationsSet
227+        return self.handleRequest(xml)
[46]228+
229+
230+    def test_on_affiliationsSetBareJID(self):
231+        """
232+        Affiliations are always on the bare JID.
233+        """
234+
235+        xml = """
236+        <iq type='set' to='pubsub.example.org'
237+                       from='user@example.org'>
238+          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
239+            <affiliations node='test'>
240+              <affiliation jid='other@example.org/Home'
241+                           affiliation='publisher'/>
242+            </affiliations>
243+          </pubsub>
244+        </iq>
245+        """
246+
247+        def affiliationsSet(request):
248+            otherJID = JID(u'other@example.org')
249+            self.assertIn(otherJID, request.affiliations)
250+
251+        self.resource.affiliationsSet = affiliationsSet
252+        return self.handleRequest(xml)
253+
254+
255+    def test_on_affiliationsSetMultipleForSameEntity(self):
256+        """
257+        Setting node affiliations can only have one item per entity.
258+        """
259+
260+        xml = """
261+        <iq type='set' to='pubsub.example.org'
262+                       from='user@example.org'>
263+          <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
264+            <affiliations node='test'>
265+              <affiliation jid='other@example.org' affiliation='publisher'/>
266+              <affiliation jid='other@example.org' affiliation='owner'/>
267+            </affiliations>
268+          </pubsub>
269+        </iq>
270+        """
271+
272         def cb(result):
273-            self.assertEquals('feature-not-implemented', result.condition)
274-            self.assertEquals('unsupported', result.appCondition.name)
275-            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
276-            self.assertEquals('modify-affiliations',
277-                              result.appCondition['feature'])
278+            self.assertEquals('bad-request', result.condition)
[45]279 
[46]280         d = self.handleRequest(xml)
281         self.assertFailure(d, error.StanzaError)
282@@ -3914,3 +4003,39 @@
[45]283         self.assertFailure(d, error.StanzaError)
284         d.addCallback(cb)
285         return d
286+
287+
288+    def test_affiliationsGet(self):
289+        """
290+        Non-overridden owner affiliations get yields unsupported error.
291+        """
292+
293+        def cb(result):
294+            self.assertEquals('feature-not-implemented', result.condition)
295+            self.assertEquals('unsupported', result.appCondition.name)
296+            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
297+            self.assertEquals('modify-affiliations',
298+                              result.appCondition['feature'])
299+
300+        d = self.resource.affiliationsGet(pubsub.PubSubRequest())
301+        self.assertFailure(d, error.StanzaError)
302+        d.addCallback(cb)
303+        return d
304+
305+
306+    def test_affiliationsSet(self):
307+        """
308+        Non-overridden owner affiliations set yields unsupported error.
309+        """
310+
311+        def cb(result):
312+            self.assertEquals('feature-not-implemented', result.condition)
313+            self.assertEquals('unsupported', result.appCondition.name)
314+            self.assertEquals(NS_PUBSUB_ERRORS, result.appCondition.uri)
315+            self.assertEquals('modify-affiliations',
316+                              result.appCondition['feature'])
317+
318+        d = self.resource.affiliationsGet(pubsub.PubSubRequest())
319+        self.assertFailure(d, error.StanzaError)
320+        d.addCallback(cb)
321+        return d
Note: See TracBrowser for help on using the repository browser.