Mercurial > libervia-pubsub
annotate idavoll/generic_backend.py @ 153:753b8432460f
Work towards JEP-0060 1.8
- Remove subscription information from <affiliations/> result.
- Add handling of <subscriptions/> entity use case.
- Make <subscribe/> return <subscription/> instead of <entity/>.
- Move <purge/> and <delete/> to owner namespace.
- Don't use 'self' in interfaces.
author | Ralph Meijer <ralphm@ik.nu> |
---|---|
date | Sat, 06 May 2006 19:47:53 +0000 |
parents | ea8b4189ae3b |
children | 5191ba7c4df8 |
rev | line source |
---|---|
107 | 1 import sha |
2 import time | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
3 import uuid |
107 | 4 from twisted.words.protocols.jabber import jid |
152 | 5 from twisted.words.xish import utility |
107 | 6 from twisted.application import service |
7 from twisted.internet import defer | |
8 from zope.interface import implements | |
129 | 9 import backend, storage |
107 | 10 |
11 def _get_affiliation(node, entity): | |
12 d = node.get_affiliation(entity) | |
13 d.addCallback(lambda affiliation: (node, affiliation)) | |
14 return d | |
15 | |
16 class BackendService(service.MultiService, utility.EventDispatcher): | |
17 | |
18 implements(backend.IBackendService) | |
19 | |
20 options = {"pubsub#persist_items": | |
21 {"type": "boolean", | |
22 "label": "Persist items to storage"}, | |
23 "pubsub#deliver_payloads": | |
24 {"type": "boolean", | |
25 "label": "Deliver payloads with event notifications"}, | |
26 } | |
27 | |
28 default_config = {"pubsub#persist_items": True, | |
29 "pubsub#deliver_payloads": True, | |
30 } | |
31 | |
32 def __init__(self, storage): | |
33 service.MultiService.__init__(self) | |
34 utility.EventDispatcher.__init__(self) | |
35 self.storage = storage | |
36 | |
37 def supports_publisher_affiliation(self): | |
38 return True | |
39 | |
40 def supports_outcast_affiliation(self): | |
41 return True | |
42 | |
43 def supports_persistent_items(self): | |
44 return True | |
45 | |
46 def get_node_type(self, node_id): | |
47 d = self.storage.get_node(node_id) | |
48 d.addCallback(lambda node: node.get_type()) | |
49 return d | |
50 | |
51 def get_nodes(self): | |
52 return self.storage.get_node_ids() | |
53 | |
54 def get_node_meta_data(self, node_id): | |
55 d = self.storage.get_node(node_id) | |
56 d.addCallback(lambda node: node.get_meta_data()) | |
57 d.addCallback(self._make_meta_data) | |
58 return d | |
59 | |
60 def _make_meta_data(self, meta_data): | |
61 options = [] | |
62 for key, value in meta_data.iteritems(): | |
63 if self.options.has_key(key): | |
64 option = {"var": key} | |
65 option.update(self.options[key]) | |
66 option["value"] = value | |
67 options.append(option) | |
68 | |
69 return options | |
70 | |
71 class PublishService(service.Service): | |
72 | |
73 implements(backend.IPublishService) | |
74 | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
75 def _check_auth(self, node, requestor): |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
76 def check(affiliation, node): |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
77 if affiliation not in ['owner', 'publisher']: |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
78 raise backend.NotAuthorized |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
79 return node |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
80 |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
81 d = node.get_affiliation(requestor) |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
82 d.addCallback(check, node) |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
83 return d |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
84 |
107 | 85 def publish(self, node_id, items, requestor): |
86 d = self.parent.storage.get_node(node_id) | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
87 d.addCallback(self._check_auth, requestor) |
107 | 88 d.addCallback(self._do_publish, items, requestor) |
89 return d | |
90 | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
91 def _do_publish(self, node, items, requestor): |
107 | 92 configuration = node.get_configuration() |
93 persist_items = configuration["pubsub#persist_items"] | |
94 deliver_payloads = configuration["pubsub#deliver_payloads"] | |
95 | |
96 if items and not persist_items and not deliver_payloads: | |
97 raise backend.NoPayloadAllowed | |
98 elif not items and (persist_items or deliver_payloads): | |
99 raise backend.PayloadExpected | |
100 | |
101 if persist_items or deliver_payloads: | |
102 for item in items: | |
103 if not item.getAttribute("id"): | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
104 item["id"] = uuid.generate() |
107 | 105 |
106 if persist_items: | |
107 d = node.store_items(items, requestor) | |
108 else: | |
109 d = defer.succeed(None) | |
110 | |
111 d.addCallback(self._do_notify, node.id, items, deliver_payloads) | |
129 | 112 return d |
107 | 113 |
114 def _do_notify(self, result, node_id, items, deliver_payloads): | |
115 if items and not deliver_payloads: | |
116 for item in items: | |
117 item.children = [] | |
118 | |
119 self.parent.dispatch({ 'items': items, 'node_id': node_id }, | |
120 '//event/pubsub/notify') | |
121 | |
122 class NotificationService(service.Service): | |
123 | |
124 implements(backend.INotificationService) | |
125 | |
126 def get_notification_list(self, node_id, items): | |
127 d = self.parent.storage.get_node(node_id) | |
128 d.addCallback(lambda node: node.get_subscribers()) | |
129 d.addCallback(self._magic_filter, node_id, items) | |
130 return d | |
131 | |
132 def _magic_filter(self, subscribers, node_id, items): | |
133 list = [] | |
134 for subscriber in subscribers: | |
135 list.append((subscriber, items)) | |
136 return list | |
137 | |
138 def register_notifier(self, observerfn, *args, **kwargs): | |
139 self.parent.addObserver('//event/pubsub/notify', observerfn, | |
140 *args, **kwargs) | |
141 | |
142 class SubscriptionService(service.Service): | |
143 | |
144 implements(backend.ISubscriptionService) | |
145 | |
146 def subscribe(self, node_id, subscriber, requestor): | |
147 subscriber_entity = subscriber.userhostJID() | |
148 if subscriber_entity != requestor: | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
149 return defer.fail(backend.NotAuthorized()) |
107 | 150 |
151 d = self.parent.storage.get_node(node_id) | |
152 d.addCallback(_get_affiliation, subscriber_entity) | |
153 d.addCallback(self._do_subscribe, subscriber) | |
154 return d | |
155 | |
156 def _do_subscribe(self, result, subscriber): | |
157 node, affiliation = result | |
158 | |
159 if affiliation == 'outcast': | |
160 raise backend.NotAuthorized | |
161 | |
162 d = node.add_subscription(subscriber, 'subscribed') | |
118
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
163 d.addCallback(lambda _: 'subscribed') |
129 | 164 d.addErrback(self._get_subscription, node, subscriber) |
153 | 165 d.addCallback(self._return_subscription, node.id) |
107 | 166 return d |
167 | |
129 | 168 def _get_subscription(self, failure, node, subscriber): |
169 failure.trap(storage.SubscriptionExists) | |
118
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
170 return node.get_subscription(subscriber) |
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
171 |
153 | 172 def _return_subscription(self, result, node_id): |
173 return node_id, result | |
107 | 174 |
175 def unsubscribe(self, node_id, subscriber, requestor): | |
176 if subscriber.userhostJID() != requestor: | |
177 raise backend.NotAuthorized | |
178 | |
179 d = self.parent.storage.get_node(node_id) | |
180 d.addCallback(lambda node: node.remove_subscription(subscriber)) | |
181 return d | |
182 | |
153 | 183 def get_subscriptions(self, entity): |
184 return self.parent.storage.get_subscriptions(entity) | |
185 | |
107 | 186 class NodeCreationService(service.Service): |
187 | |
188 implements(backend.INodeCreationService) | |
189 | |
190 def supports_instant_nodes(self): | |
191 return True | |
192 | |
193 def create_node(self, node_id, requestor): | |
194 if not node_id: | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
195 node_id = 'generic/%s' % uuid.generate() |
107 | 196 d = self.parent.storage.create_node(node_id, requestor) |
197 d.addCallback(lambda _: node_id) | |
198 return d | |
199 | |
200 def get_node_configuration(self, node_id): | |
201 if node_id: | |
202 d = self.parent.storage.get_node(node_id) | |
203 d.addCallback(lambda node: node.get_configuration()) | |
204 else: | |
205 # XXX: this is disabled in pubsub.py | |
206 d = defer.succeed(self.parent.default_config) | |
207 | |
208 d.addCallback(self._make_config) | |
209 return d | |
210 | |
211 def _make_config(self, config): | |
212 options = [] | |
213 for key, value in self.parent.options.iteritems(): | |
214 option = {"var": key} | |
215 option.update(value) | |
216 if config.has_key(key): | |
217 option["value"] = config[key] | |
218 options.append(option) | |
219 | |
220 return options | |
221 | |
222 def set_node_configuration(self, node_id, options, requestor): | |
223 for key, value in options.iteritems(): | |
224 if not self.parent.options.has_key(key): | |
225 raise backend.InvalidConfigurationOption | |
226 if self.parent.options[key]["type"] == 'boolean': | |
227 try: | |
228 options[key] = bool(int(value)) | |
229 except ValueError: | |
230 raise backend.InvalidConfigurationValue | |
231 | |
232 d = self.parent.storage.get_node(node_id) | |
233 d.addCallback(_get_affiliation, requestor) | |
234 d.addCallback(self._do_set_node_configuration, options) | |
235 return d | |
236 | |
237 def _do_set_node_configuration(self, result, options): | |
238 node, affiliation = result | |
239 | |
240 if affiliation != 'owner': | |
241 raise backend.NotAuthorized | |
242 | |
243 return node.set_configuration(options) | |
244 | |
245 class AffiliationsService(service.Service): | |
246 | |
247 implements(backend.IAffiliationsService) | |
248 | |
249 def get_affiliations(self, entity): | |
153 | 250 return self.parent.storage.get_affiliations(entity) |
107 | 251 |
252 class ItemRetrievalService(service.Service): | |
253 | |
254 implements(backend.IItemRetrievalService) | |
255 | |
256 def get_items(self, node_id, requestor, max_items=None, item_ids=[]): | |
257 d = self.parent.storage.get_node(node_id) | |
258 d.addCallback(self._is_subscribed, requestor) | |
259 d.addCallback(self._do_get_items, max_items, item_ids) | |
260 return d | |
261 | |
262 def _is_subscribed(self, node, subscriber): | |
263 d = node.is_subscribed(subscriber) | |
264 d.addCallback(lambda subscribed: (node, subscribed)) | |
265 return d | |
266 | |
267 def _do_get_items(self, result, max_items, item_ids): | |
268 node, subscribed = result | |
269 | |
270 if not subscribed: | |
271 raise backend.NotAuthorized | |
272 | |
273 if item_ids: | |
274 return node.get_items_by_id(item_ids) | |
275 else: | |
276 return node.get_items(max_items) | |
277 | |
278 class RetractionService(service.Service): | |
279 | |
280 implements(backend.IRetractionService) | |
281 | |
282 def retract_item(self, node_id, item_ids, requestor): | |
283 d = self.parent.storage.get_node(node_id) | |
284 d.addCallback(_get_affiliation, requestor) | |
285 d.addCallback(self._do_retract, item_ids) | |
286 return d | |
287 | |
288 def _do_retract(self, result, item_ids): | |
289 node, affiliation = result | |
290 persist_items = node.get_configuration()["pubsub#persist_items"] | |
291 | |
292 if affiliation not in ['owner', 'publisher']: | |
293 raise backend.NotAuthorized | |
294 | |
295 if not persist_items: | |
296 raise backend.NodeNotPersistent | |
297 | |
298 d = node.remove_items(item_ids) | |
142
812300cdbc22
Changed behaviour of retraction of items so that only the actually deleted
Ralph Meijer <ralphm@ik.nu>
parents:
129
diff
changeset
|
299 d.addCallback(self._do_notify_retraction, node.id) |
107 | 300 return d |
301 | |
142
812300cdbc22
Changed behaviour of retraction of items so that only the actually deleted
Ralph Meijer <ralphm@ik.nu>
parents:
129
diff
changeset
|
302 def _do_notify_retraction(self, item_ids, node_id): |
129 | 303 self.parent.dispatch({ 'item_ids': item_ids, 'node_id': node_id }, |
107 | 304 '//event/pubsub/retract') |
305 | |
306 def purge_node(self, node_id, requestor): | |
307 d = self.parent.storage.get_node(node_id) | |
308 d.addCallback(_get_affiliation, requestor) | |
309 d.addCallback(self._do_purge) | |
310 return d | |
311 | |
312 def _do_purge(self, result): | |
313 node, affiliation = result | |
314 persist_items = node.get_configuration()["pubsub#persist_items"] | |
315 | |
316 if affiliation != 'owner': | |
317 raise backend.NotAuthorized | |
318 | |
319 if not persist_items: | |
320 raise backend.NodeNotPersistent | |
321 | |
322 d = node.purge() | |
323 d.addCallback(self._do_notify_purge, node.id) | |
324 return d | |
325 | |
326 def _do_notify_purge(self, result, node_id): | |
327 self.parent.dispatch(node_id, '//event/pubsub/purge') | |
328 | |
329 class NodeDeletionService(service.Service): | |
330 | |
331 implements(backend.INodeDeletionService) | |
332 | |
333 def __init__(self): | |
334 self._callback_list = [] | |
335 | |
336 def register_pre_delete(self, pre_delete_fn): | |
337 self._callback_list.append(pre_delete_fn) | |
338 | |
339 def get_subscribers(self, node_id): | |
340 d = self.parent.storage.get_node(node_id) | |
341 d.addCallback(lambda node: node.get_subscribers()) | |
342 return d | |
343 | |
344 def delete_node(self, node_id, requestor): | |
345 d = self.parent.storage.get_node(node_id) | |
346 d.addCallback(_get_affiliation, requestor) | |
347 d.addCallback(self._do_pre_delete) | |
348 return d | |
349 | |
350 def _do_pre_delete(self, result): | |
351 node, affiliation = result | |
352 | |
353 if affiliation != 'owner': | |
354 raise backend.NotAuthorized | |
355 | |
144
5b0b3f013ccc
Fixed typo. Reported by Herman Li.
Ralph Meijer <ralphm@ik.nu>
parents:
142
diff
changeset
|
356 d = defer.DeferredList([cb(node.id) for cb in self._callback_list], |
107 | 357 consumeErrors=1) |
358 d.addCallback(self._do_delete, node.id) | |
359 | |
360 def _do_delete(self, result, node_id): | |
361 dl = [] | |
362 for succeeded, r in result: | |
363 if succeeded and r: | |
364 dl.extend(r) | |
365 | |
366 d = self.parent.storage.delete_node(node_id) | |
367 d.addCallback(self._do_notify_delete, dl) | |
368 | |
369 return d | |
370 | |
371 def _do_notify_delete(self, result, dl): | |
372 for d in dl: | |
373 d.callback(None) |