comparison sat_pubsub/backend.py @ 318:d13526c0eb32

RSM improvments/refactoring: - a warning message is displayed if maxItems == 0 in getItems, and an empty list is returned in this case - use the new container.ItemData instead of doing tuple (un)packing - the list of ItemData => list of domish.Element conversion is done at the end of the workflow - rsm request is checked in self._items_rsm directly - better handling of Response.index in _items_rsm - itemsIdentifiers can't be used with RSM (the later will be ignored if this happen) - don't do approximative unpacking anymore in _items_rsm - countItems and getIndex have been refactored and renamed getItemsCount and getItemsIndex, don't use query duplications anymore - cleaned query handling in getItems - /!\ mam module is temporarly broken
author Goffi <goffi@goffi.org>
date Sun, 03 Jan 2016 18:33:22 +0100
parents 34adc4a8aa64
children 29c2553ef863
comparison
equal deleted inserted replaced
317:34adc4a8aa64 318:d13526c0eb32
595 return {} 595 return {}
596 596
597 def _doGetItems(self, result, requestor, maxItems, itemIdentifiers, 597 def _doGetItems(self, result, requestor, maxItems, itemIdentifiers,
598 ext_data): 598 ext_data):
599 node, affiliation = result 599 node, affiliation = result
600 if maxItems == 0:
601 log.msg("WARNING: maxItems=0 on items retrieval")
602 return []
600 603
601 def append_item_config(items_data): 604 def append_item_config(items_data):
602 ret = [] 605 """Add item config data form to items with roster access model"""
603 for item_data in items_data: 606 for item_data in items_data:
604 item, access_model, access_list = item_data.item, item_data.access_model, item_data.config 607 if item_data.access_model == const.VAL_AMODEL_OPEN:
605 if access_model == const.VAL_AMODEL_OPEN:
606 pass 608 pass
607 elif access_model == const.VAL_AMODEL_ROSTER: 609 elif item_data.access_model == const.VAL_AMODEL_ROSTER:
608 form = data_form.Form('submit', formNamespace=const.NS_ITEM_CONFIG) 610 form = data_form.Form('submit', formNamespace=const.NS_ITEM_CONFIG)
609 access = data_form.Field(None, const.OPT_ACCESS_MODEL, value=const.VAL_AMODEL_ROSTER) 611 access = data_form.Field(None, const.OPT_ACCESS_MODEL, value=const.VAL_AMODEL_ROSTER)
610 allowed = data_form.Field(None, const.OPT_ROSTER_GROUPS_ALLOWED, values=access_list[const.OPT_ROSTER_GROUPS_ALLOWED]) 612 allowed = data_form.Field(None, const.OPT_ROSTER_GROUPS_ALLOWED, values=item_data.config[const.OPT_ROSTER_GROUPS_ALLOWED])
611 form.addField(access) 613 form.addField(access)
612 form.addField(allowed) 614 form.addField(allowed)
613 item.addChild(form.toElement()) 615 item_data.item.addChild(form.toElement())
614 elif access_model == const.VAL_AMODEL_JID: 616 elif access_model == const.VAL_AMODEL_JID:
615 #FIXME: manage jid 617 #FIXME: manage jid
616 raise NotImplementedError 618 raise NotImplementedError
617 else: 619 else:
618 raise error.BadAccessTypeError(access_model) 620 raise error.BadAccessTypeError(access_model)
619 621 return items_data
620 ret.append(item)
621 return ret
622 622
623 def access_checked(access_data): 623 def access_checked(access_data):
624 authorized, roster = access_data 624 authorized, roster = access_data
625 if not authorized: 625 if not authorized:
626 raise error.NotAuthorized() 626 raise error.NotAuthorized()
627 627
628 roster_item = roster.get(requestor.userhostJID()) 628 roster_item = roster.get(requestor.userhostJID())
629 authorized_groups = tuple(roster_item.groups) if roster_item else tuple() 629 authorized_groups = tuple(roster_item.groups) if roster_item else tuple()
630 unrestricted = affiliation == 'owner' 630 owner = affiliation == 'owner'
631 631
632 if itemIdentifiers: 632 if itemIdentifiers:
633 d = node.getItemsById(authorized_groups, unrestricted, itemIdentifiers) 633 d = node.getItemsById(authorized_groups, owner, itemIdentifiers)
634 else: 634 else:
635 d = node.getItems(authorized_groups, unrestricted, maxItems, ext_data) 635 d = node.getItems(authorized_groups, owner, maxItems, ext_data)
636 if unrestricted: 636 if owner:
637 d.addCallback(append_item_config) 637 d.addCallback(append_item_config)
638 638
639 try: 639 d.addCallback(self._items_rsm, node, authorized_groups,
640 rsm_data = ext_data['rsm'] 640 owner, itemIdentifiers,
641 except KeyError: 641 ext_data)
642 pass
643 else:
644 if rsm_data is not None:
645 d.addCallback(self._items_rsm, node, authorized_groups,
646 unrestricted, maxItems, itemIdentifiers,
647 rsm_data)
648 return d 642 return d
649 643
650 if not iidavoll.ILeafNode.providedBy(node): 644 if not iidavoll.ILeafNode.providedBy(node):
651 return [] 645 return []
652 646
669 elif access_model == const.VAL_AMODEL_ROSTER: 663 elif access_model == const.VAL_AMODEL_ROSTER:
670 d.addCallback(self._getNodeGroups, node.nodeIdentifier, ext_data.get('pep', False)) 664 d.addCallback(self._getNodeGroups, node.nodeIdentifier, ext_data.get('pep', False))
671 d.addCallback(self.checkGroup, requestor) 665 d.addCallback(self.checkGroup, requestor)
672 d.addCallback(access_checked) 666 d.addCallback(access_checked)
673 667
668 d.addCallback(lambda items_data: [item_data.item for item_data in items_data])
669
674 return d 670 return d
675 671
676 def _setCount(self, value, response): 672 def _setCount(self, value, response):
677 response.count = value 673 response.count = value
678 674
679 def _setIndex(self, value, response): 675 def _setIndex(self, value, response, adjust):
680 response.index = value 676 """Set index in RSM response
681 677
682 def _items_rsm(self, elts, node, authorized_groups, unrestricted, maxItems, 678 @param value(int): value of the reference index (i.e. before or after item)
683 itemIdentifiers, request): 679 @param response(RSMResponse): response instance to fill
684 # FIXME: move this to a separate module ? 680 @param adjust(int): adjustement term (i.e. difference between reference index and first item of the result)
681 """
682 response.index = value + adjust
683
684 def _items_rsm(self, items_data, node, authorized_groups, owner,
685 itemIdentifiers, ext_data):
686 # FIXME: move this to a separate module
687 # TODO: Index can be optimized by keeping a cache of the last RSM request
688 # An other optimisation would be to look for index first and use it as offset
689 try:
690 rsm_request = ext_data['rsm']
691 except KeyError:
692 # No RSM in this request, nothing to do
693 return items_data
694
695 if itemIdentifiers:
696 log.msg("WARNING, itemIdentifiers used with RSM, ignoring the RSM part")
697 return items_data
698
685 response = rsm.RSMResponse() 699 response = rsm.RSMResponse()
686 700
687 d_count = node.countItems(authorized_groups, unrestricted) 701 d_count = node.getItemsCount(authorized_groups, owner, ext_data)
688 d_count.addCallback(self._setCount, response) 702 d_count.addCallback(self._setCount, response)
689 d_list = [d_count] 703 d_list = [d_count]
690 704
691 if request.index is not None: 705 if items_data:
692 response.index = request.index 706 response.first = items_data[0].item['id']
693 elif request.before is not None: 707 response.last = items_data[-1].item['id']
694 if request.before != '': 708
695 # XXX: getIndex starts with index 1, RSM starts with 0 709 # index handling
696 d_index = node.getIndex(authorized_groups, unrestricted, request.before) 710 if rsm_request.index is not None:
697 d_index.addCallback(lambda index: max(index - request.max - 1, 0)) 711 response.index = rsm_request.index
698 d_index.addCallback(self._setIndex, response) 712 elif rsm_request.before:
713 # The last page case (before == '') is managed in render method
714 d_index = node.getItemsIndex(rsm_request.before, authorized_groups, owner, ext_data)
715 d_index.addCallback(self._setIndex, response, -len(items_data))
699 d_list.append(d_index) 716 d_list.append(d_index)
700 elif request.after is not None: 717 elif rsm_request.after is not None:
701 d_index = node.getIndex(authorized_groups, unrestricted, request.after) 718 d_index = node.getItemsIndex(rsm_request.after, authorized_groups, owner, ext_data)
702 d_index.addCallback(self._setIndex, response) 719 d_index.addCallback(self._setIndex, response, 1)
703 d_list.append(d_index) 720 d_list.append(d_index)
704 elif itemIdentifiers: 721 else:
705 d_index = node.getIndex(authorized_groups, unrestricted, itemIdentifiers[0]) 722 # the first page was requested
706 d_index.addCallback(lambda index: index - 1) 723 response.index = 0
707 d_index.addCallback(self._setIndex, response)
708 d_list.append(d_index)
709
710 724
711 def render(result): 725 def render(result):
712 try: 726 if rsm_request.before == '':
713 items = [elt for elt in elts if elt.name == 'item'] 727 # the last page was requested
714 except AttributeError: 728 response.index = response.count - len(items_data)
715 # XXX: see sat_pubsub.pgsql_storage.LeafNode.getItemsById return value 729 items_data.append(container.ItemData(response.toElement()))
716 items = [elt[0] for elt in elts if elt[0].name == 'item'] 730 return items_data
717 if len(items) > 0:
718 if response.index is None:
719 if request.before == '': # last page
720 response.index = response.count - request.max
721 else: # first page
722 response.index = 0
723 response.first = items[0]['id']
724 response.last = items[len(items) - 1]['id']
725 if request.before is not None:
726 response.first, response.last = response.last, response.first
727 else:
728 response.index = None
729 elts.append(response.toElement())
730 return elts
731 731
732 return defer.DeferredList(d_list).addCallback(render) 732 return defer.DeferredList(d_list).addCallback(render)
733 733
734 def retractItem(self, nodeIdentifier, itemIdentifiers, requestor, notify, pep, recipient): 734 def retractItem(self, nodeIdentifier, itemIdentifiers, requestor, notify, pep, recipient):
735 d = self.storage.getNode(nodeIdentifier, pep, recipient) 735 d = self.storage.getNode(nodeIdentifier, pep, recipient)