Mercurial > libervia-pubsub
annotate idavoll/generic_backend.py @ 158:b2149e448465 idavoll-0.5.0
update tags
author | convert-repo |
---|---|
date | Thu, 18 Jun 2009 11:52:01 +0000 |
parents | 5191ba7c4df8 |
children | 6fe78048baf9 |
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']: |
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 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
|
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: | |
100 raise backend.NoPayloadAllowed | |
101 elif not items and (persist_items or deliver_payloads): | |
102 raise backend.PayloadExpected | |
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: | |
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
|
152 return defer.fail(backend.NotAuthorized()) |
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': | |
163 raise backend.NotAuthorized | |
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: | |
180 raise backend.NotAuthorized | |
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 | |
203 def get_node_configuration(self, node_id): | |
204 if node_id: | |
205 d = self.parent.storage.get_node(node_id) | |
206 d.addCallback(lambda node: node.get_configuration()) | |
207 else: | |
208 # XXX: this is disabled in pubsub.py | |
209 d = defer.succeed(self.parent.default_config) | |
210 | |
211 d.addCallback(self._make_config) | |
212 return d | |
213 | |
214 def _make_config(self, config): | |
215 options = [] | |
216 for key, value in self.parent.options.iteritems(): | |
217 option = {"var": key} | |
218 option.update(value) | |
219 if config.has_key(key): | |
220 option["value"] = config[key] | |
221 options.append(option) | |
222 | |
223 return options | |
224 | |
225 def set_node_configuration(self, node_id, options, requestor): | |
226 for key, value in options.iteritems(): | |
227 if not self.parent.options.has_key(key): | |
228 raise backend.InvalidConfigurationOption | |
229 if self.parent.options[key]["type"] == 'boolean': | |
230 try: | |
231 options[key] = bool(int(value)) | |
232 except ValueError: | |
233 raise backend.InvalidConfigurationValue | |
234 | |
235 d = self.parent.storage.get_node(node_id) | |
236 d.addCallback(_get_affiliation, requestor) | |
237 d.addCallback(self._do_set_node_configuration, options) | |
238 return d | |
239 | |
240 def _do_set_node_configuration(self, result, options): | |
241 node, affiliation = result | |
242 | |
243 if affiliation != 'owner': | |
244 raise backend.NotAuthorized | |
245 | |
246 return node.set_configuration(options) | |
247 | |
248 class AffiliationsService(service.Service): | |
249 | |
250 implements(backend.IAffiliationsService) | |
251 | |
252 def get_affiliations(self, entity): | |
153 | 253 return self.parent.storage.get_affiliations(entity) |
107 | 254 |
255 class ItemRetrievalService(service.Service): | |
256 | |
257 implements(backend.IItemRetrievalService) | |
258 | |
259 def get_items(self, node_id, requestor, max_items=None, item_ids=[]): | |
260 d = self.parent.storage.get_node(node_id) | |
261 d.addCallback(self._is_subscribed, requestor) | |
262 d.addCallback(self._do_get_items, max_items, item_ids) | |
263 return d | |
264 | |
265 def _is_subscribed(self, node, subscriber): | |
266 d = node.is_subscribed(subscriber) | |
267 d.addCallback(lambda subscribed: (node, subscribed)) | |
268 return d | |
269 | |
270 def _do_get_items(self, result, max_items, item_ids): | |
271 node, subscribed = result | |
272 | |
273 if not subscribed: | |
274 raise backend.NotAuthorized | |
275 | |
276 if item_ids: | |
277 return node.get_items_by_id(item_ids) | |
278 else: | |
279 return node.get_items(max_items) | |
280 | |
281 class RetractionService(service.Service): | |
282 | |
283 implements(backend.IRetractionService) | |
284 | |
285 def retract_item(self, node_id, item_ids, requestor): | |
286 d = self.parent.storage.get_node(node_id) | |
287 d.addCallback(_get_affiliation, requestor) | |
288 d.addCallback(self._do_retract, item_ids) | |
289 return d | |
290 | |
291 def _do_retract(self, result, item_ids): | |
292 node, affiliation = result | |
293 persist_items = node.get_configuration()["pubsub#persist_items"] | |
294 | |
295 if affiliation not in ['owner', 'publisher']: | |
296 raise backend.NotAuthorized | |
297 | |
298 if not persist_items: | |
299 raise backend.NodeNotPersistent | |
300 | |
301 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
|
302 d.addCallback(self._do_notify_retraction, node.id) |
107 | 303 return d |
304 | |
142
812300cdbc22
Changed behaviour of retraction of items so that only the actually deleted
Ralph Meijer <ralphm@ik.nu>
parents:
129
diff
changeset
|
305 def _do_notify_retraction(self, item_ids, node_id): |
129 | 306 self.parent.dispatch({ 'item_ids': item_ids, 'node_id': node_id }, |
107 | 307 '//event/pubsub/retract') |
308 | |
309 def purge_node(self, node_id, requestor): | |
310 d = self.parent.storage.get_node(node_id) | |
311 d.addCallback(_get_affiliation, requestor) | |
312 d.addCallback(self._do_purge) | |
313 return d | |
314 | |
315 def _do_purge(self, result): | |
316 node, affiliation = result | |
317 persist_items = node.get_configuration()["pubsub#persist_items"] | |
318 | |
319 if affiliation != 'owner': | |
320 raise backend.NotAuthorized | |
321 | |
322 if not persist_items: | |
323 raise backend.NodeNotPersistent | |
324 | |
325 d = node.purge() | |
326 d.addCallback(self._do_notify_purge, node.id) | |
327 return d | |
328 | |
329 def _do_notify_purge(self, result, node_id): | |
330 self.parent.dispatch(node_id, '//event/pubsub/purge') | |
331 | |
332 class NodeDeletionService(service.Service): | |
333 | |
334 implements(backend.INodeDeletionService) | |
335 | |
336 def __init__(self): | |
337 self._callback_list = [] | |
338 | |
339 def register_pre_delete(self, pre_delete_fn): | |
340 self._callback_list.append(pre_delete_fn) | |
341 | |
342 def get_subscribers(self, node_id): | |
343 d = self.parent.storage.get_node(node_id) | |
344 d.addCallback(lambda node: node.get_subscribers()) | |
345 return d | |
346 | |
347 def delete_node(self, node_id, requestor): | |
348 d = self.parent.storage.get_node(node_id) | |
349 d.addCallback(_get_affiliation, requestor) | |
350 d.addCallback(self._do_pre_delete) | |
351 return d | |
352 | |
353 def _do_pre_delete(self, result): | |
354 node, affiliation = result | |
355 | |
356 if affiliation != 'owner': | |
357 raise backend.NotAuthorized | |
358 | |
144
5b0b3f013ccc
Fixed typo. Reported by Herman Li.
Ralph Meijer <ralphm@ik.nu>
parents:
142
diff
changeset
|
359 d = defer.DeferredList([cb(node.id) for cb in self._callback_list], |
107 | 360 consumeErrors=1) |
361 d.addCallback(self._do_delete, node.id) | |
362 | |
363 def _do_delete(self, result, node_id): | |
364 dl = [] | |
365 for succeeded, r in result: | |
366 if succeeded and r: | |
367 dl.extend(r) | |
368 | |
369 d = self.parent.storage.delete_node(node_id) | |
370 d.addCallback(self._do_notify_delete, dl) | |
371 | |
372 return d | |
373 | |
374 def _do_notify_delete(self, result, dl): | |
375 for d in dl: | |
376 d.callback(None) |