comparison idavoll/backend.py @ 222:698af5d720ad

Reshape Idavoll as a PubSubResource. PubSubResource is Wokkel's newer interface for building (parts of) XMPP publish-subscribe services and replaces the old interface of PubSubService. It is more flexible for adding new protocol, allows for node-as-code (providing a specific backend per node), and permits accepting requests for different entities (virtual hosts or PEP-like settings). This moves over the current backend to use the new interface, so new code for previously unsupported protocol can be added down the line.
author Ralph Meijer <ralphm@ik.nu>
date Sat, 16 Oct 2010 21:03:38 +0200
parents 53d2c0019458
children 0eafdced5f24
comparison
equal deleted inserted replaced
221:a430976f2977 222:698af5d720ad
19 19
20 from twisted.application import service 20 from twisted.application import service
21 from twisted.python import components, log 21 from twisted.python import components, log
22 from twisted.internet import defer, reactor 22 from twisted.internet import defer, reactor
23 from twisted.words.protocols.jabber.error import StanzaError 23 from twisted.words.protocols.jabber.error import StanzaError
24 from twisted.words.xish import domish, utility 24 from twisted.words.xish import utility
25 25
26 from wokkel.iwokkel import IDisco, IPubSubService 26 from wokkel import disco
27 from wokkel.pubsub import PubSubService, PubSubError 27 from wokkel.iwokkel import IPubSubResource
28 from wokkel.pubsub import PubSubResource, PubSubError
28 29
29 from idavoll import error, iidavoll 30 from idavoll import error, iidavoll
30 from idavoll.iidavoll import IBackendService, ILeafNode 31 from idavoll.iidavoll import IBackendService, ILeafNode
31 32
32 def _getAffiliation(node, entity): 33 def _getAffiliation(node, entity):
472 for d in dl: 473 for d in dl:
473 d.callback(None) 474 d.callback(None)
474 475
475 476
476 477
477 class PubSubServiceFromBackend(PubSubService): 478 class PubSubResourceFromBackend(PubSubResource):
478 """ 479 """
479 Adapts a backend to an xmpp publish-subscribe service. 480 Adapts a backend to an xmpp publish-subscribe service.
480 """ 481 """
481 482
482 implements(IDisco) 483 features = [
484 "config-node",
485 "create-nodes",
486 "delete-any",
487 "delete-nodes",
488 "item-ids",
489 "meta-data",
490 "publish",
491 "purge-nodes",
492 "retract-items",
493 "retrieve-affiliations",
494 "retrieve-default",
495 "retrieve-items",
496 "retrieve-subscriptions",
497 "subscribe",
498 ]
499
500 discoIdentity = disco.DiscoIdentity('pubsub',
501 'service',
502 'Idavoll publish-subscribe service')
503
504 pubsubService = None
483 505
484 _errorMap = { 506 _errorMap = {
485 error.NodeNotFound: ('item-not-found', None, None), 507 error.NodeNotFound: ('item-not-found', None, None),
486 error.NodeExists: ('conflict', None, None), 508 error.NodeExists: ('conflict', None, None),
487 error.Forbidden: ('forbidden', None, None), 509 error.Forbidden: ('forbidden', None, None),
504 'unsupported', 526 'unsupported',
505 'publish'), 527 'publish'),
506 } 528 }
507 529
508 def __init__(self, backend): 530 def __init__(self, backend):
509 PubSubService.__init__(self) 531 PubSubResource.__init__(self)
510 532
511 self.backend = backend 533 self.backend = backend
512 self.hideNodes = False 534 self.hideNodes = False
513 535
514 self.pubSubFeatures = self._getPubSubFeatures()
515
516 self.backend.registerNotifier(self._notify) 536 self.backend.registerNotifier(self._notify)
517 self.backend.registerPreDelete(self._preDelete) 537 self.backend.registerPreDelete(self._preDelete)
518 538
519
520 def _getPubSubFeatures(self):
521 features = [
522 "config-node",
523 "create-nodes",
524 "delete-any",
525 "delete-nodes",
526 "item-ids",
527 "meta-data",
528 "publish",
529 "purge-nodes",
530 "retract-items",
531 "retrieve-affiliations",
532 "retrieve-default",
533 "retrieve-items",
534 "retrieve-subscriptions",
535 "subscribe",
536 ]
537
538 if self.backend.supportsInstantNodes(): 539 if self.backend.supportsInstantNodes():
539 features.append("instant-nodes") 540 self.features.append("instant-nodes")
540 541
541 if self.backend.supportsOutcastAffiliation(): 542 if self.backend.supportsOutcastAffiliation():
542 features.append("outcast-affiliation") 543 self.features.append("outcast-affiliation")
543 544
544 if self.backend.supportsPersistentItems(): 545 if self.backend.supportsPersistentItems():
545 features.append("persistent-items") 546 self.features.append("persistent-items")
546 547
547 if self.backend.supportsPublisherAffiliation(): 548 if self.backend.supportsPublisherAffiliation():
548 features.append("publisher-affiliation") 549 self.features.append("publisher-affiliation")
549
550 return features
551 550
552 551
553 def _notify(self, data): 552 def _notify(self, data):
554 items = data['items'] 553 items = data['items']
555 nodeIdentifier = data['nodeIdentifier'] 554 nodeIdentifier = data['nodeIdentifier']
557 d = self.backend.getNotifications(nodeIdentifier, items) 556 d = self.backend.getNotifications(nodeIdentifier, items)
558 else: 557 else:
559 subscription = data['subscription'] 558 subscription = data['subscription']
560 d = defer.succeed([(subscription.subscriber, [subscription], 559 d = defer.succeed([(subscription.subscriber, [subscription],
561 items)]) 560 items)])
562 d.addCallback(lambda notifications: self.notifyPublish(self.serviceJID, 561 d.addCallback(lambda notifications: self.pubsubService.notifyPublish(
563 nodeIdentifier, 562 self.serviceJID,
564 notifications)) 563 nodeIdentifier,
564 notifications))
565 565
566 566
567 def _preDelete(self, data): 567 def _preDelete(self, data):
568 nodeIdentifier = data['nodeIdentifier'] 568 nodeIdentifier = data['nodeIdentifier']
569 redirectURI = data.get('redirectURI', None) 569 redirectURI = data.get('redirectURI', None)
570 d = self.backend.getSubscribers(nodeIdentifier) 570 d = self.backend.getSubscribers(nodeIdentifier)
571 d.addCallback(lambda subscribers: self.notifyDelete(self.serviceJID, 571 d.addCallback(lambda subscribers: self.pubsubService.notifyDelete(
572 nodeIdentifier, 572 self.serviceJID,
573 subscribers, 573 nodeIdentifier,
574 redirectURI)) 574 subscribers,
575 redirectURI))
575 return d 576 return d
576 577
577 578
578 def _mapErrors(self, failure): 579 def _mapErrors(self, failure):
579 e = failure.trap(*self._errorMap.keys()) 580 e = failure.trap(*self._errorMap.keys())
587 exc = StanzaError(condition, text=msg) 588 exc = StanzaError(condition, text=msg)
588 589
589 raise exc 590 raise exc
590 591
591 592
592 def getNodeInfo(self, requestor, service, nodeIdentifier): 593 def getInfo(self, requestor, service, nodeIdentifier):
593 info = {} 594 info = {}
594 595
595 def saveType(result): 596 def saveType(result):
596 info['type'] = result 597 info['type'] = result
597 return nodeIdentifier 598 return nodeIdentifier
612 d.addErrback(trapNotFound) 613 d.addErrback(trapNotFound)
613 d.addErrback(self._mapErrors) 614 d.addErrback(self._mapErrors)
614 return d 615 return d
615 616
616 617
617 def getNodes(self, requestor, service): 618 def getNodes(self, requestor, service, nodeIdentifier):
618 if service.resource: 619 if service.resource:
619 return defer.succeed([]) 620 return defer.succeed([])
620 d = self.backend.getNodes() 621 d = self.backend.getNodes()
621 return d.addErrback(self._mapErrors) 622 return d.addErrback(self._mapErrors)
622 623
623 624
624 def publish(self, requestor, service, nodeIdentifier, items):
625 d = self.backend.publish(nodeIdentifier, items, requestor)
626 return d.addErrback(self._mapErrors)
627
628
629 def subscribe(self, requestor, service, nodeIdentifier, subscriber):
630 d = self.backend.subscribe(nodeIdentifier, subscriber, requestor)
631 return d.addErrback(self._mapErrors)
632
633
634 def unsubscribe(self, requestor, service, nodeIdentifier, subscriber):
635 d = self.backend.unsubscribe(nodeIdentifier, subscriber, requestor)
636 return d.addErrback(self._mapErrors)
637
638
639 def subscriptions(self, requestor, service):
640 d = self.backend.getSubscriptions(requestor)
641 return d.addErrback(self._mapErrors)
642
643
644 def affiliations(self, requestor, service):
645 d = self.backend.getAffiliations(requestor)
646 return d.addErrback(self._mapErrors)
647
648
649 def create(self, requestor, service, nodeIdentifier):
650 d = self.backend.createNode(nodeIdentifier, requestor)
651 return d.addErrback(self._mapErrors)
652
653
654 def getConfigurationOptions(self): 625 def getConfigurationOptions(self):
655 return self.backend.nodeOptions 626 return self.backend.nodeOptions
656 627
657 628
658 def getDefaultConfiguration(self, requestor, service, nodeType): 629 def publish(self, request):
659 d = self.backend.getDefaultConfiguration(nodeType) 630 d = self.backend.publish(request.nodeIdentifier,
660 return d.addErrback(self._mapErrors) 631 request.items,
661 632 request.sender)
662 633 return d.addErrback(self._mapErrors)
663 def getConfiguration(self, requestor, service, nodeIdentifier): 634
664 d = self.backend.getNodeConfiguration(nodeIdentifier) 635
665 return d.addErrback(self._mapErrors) 636 def subscribe(self, request):
666 637 d = self.backend.subscribe(request.nodeIdentifier,
667 638 request.subscriber,
668 def setConfiguration(self, requestor, service, nodeIdentifier, options): 639 request.sender)
669 d = self.backend.setNodeConfiguration(nodeIdentifier, options, 640 return d.addErrback(self._mapErrors)
670 requestor) 641
671 return d.addErrback(self._mapErrors) 642
672 643 def unsubscribe(self, request):
673 644 d = self.backend.unsubscribe(request.nodeIdentifier,
674 def items(self, requestor, service, nodeIdentifier, maxItems, 645 request.subscriber,
675 itemIdentifiers): 646 request.sender)
676 d = self.backend.getItems(nodeIdentifier, requestor, maxItems, 647 return d.addErrback(self._mapErrors)
677 itemIdentifiers) 648
678 return d.addErrback(self._mapErrors) 649
679 650 def subscriptions(self, request):
680 651 d = self.backend.getSubscriptions(request.sender)
681 def retract(self, requestor, service, nodeIdentifier, itemIdentifiers): 652 return d.addErrback(self._mapErrors)
682 d = self.backend.retractItem(nodeIdentifier, itemIdentifiers, 653
683 requestor) 654
684 return d.addErrback(self._mapErrors) 655 def affiliations(self, request):
685 656 d = self.backend.getAffiliations(request.sender)
686 657 return d.addErrback(self._mapErrors)
687 def purge(self, requestor, service, nodeIdentifier): 658
688 d = self.backend.purgeNode(nodeIdentifier, requestor) 659
689 return d.addErrback(self._mapErrors) 660 def create(self, request):
690 661 d = self.backend.createNode(request.nodeIdentifier,
691 662 request.sender)
692 def delete(self, requestor, service, nodeIdentifier): 663 return d.addErrback(self._mapErrors)
693 d = self.backend.deleteNode(nodeIdentifier, requestor) 664
694 return d.addErrback(self._mapErrors) 665
695 666 def default(self, request):
696 components.registerAdapter(PubSubServiceFromBackend, 667 d = self.backend.getDefaultConfiguration(request.nodeType)
668 return d.addErrback(self._mapErrors)
669
670
671 def configureGet(self, request):
672 d = self.backend.getNodeConfiguration(request.nodeIdentifier)
673 return d.addErrback(self._mapErrors)
674
675
676 def configureSet(self, request):
677 d = self.backend.setNodeConfiguration(request.nodeIdentifier,
678 request.options,
679 request.sender)
680 return d.addErrback(self._mapErrors)
681
682
683 def items(self, request):
684 d = self.backend.getItems(request.nodeIdentifier,
685 request.sender,
686 request.maxItems,
687 request.itemIdentifiers)
688 return d.addErrback(self._mapErrors)
689
690
691 def retract(self, request):
692 d = self.backend.retractItem(request.nodeIdentifier,
693 request.itemIdentifiers,
694 request.sender)
695 return d.addErrback(self._mapErrors)
696
697
698 def purge(self, request):
699 d = self.backend.purgeNode(request.nodeIdentifier,
700 request.sender)
701 return d.addErrback(self._mapErrors)
702
703
704 def delete(self, request):
705 d = self.backend.deleteNode(request.nodeIdentifier,
706 request.sender)
707 return d.addErrback(self._mapErrors)
708
709 components.registerAdapter(PubSubResourceFromBackend,
697 IBackendService, 710 IBackendService,
698 IPubSubService) 711 IPubSubResource)