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