comparison sat/plugins/plugin_xep_0060.py @ 2807:0b7ce5daee9b

plugin XEP-0277: blog items data are now entirely serialised before going to bridge: So far, and for historical reasons, blog items data where serialised using a unicode: unicode dict, which was causing trouble for many types of values (timestamps, booleans, lists). This patch changes it by serialising the whole items before going to bridge, and deserialising it when going back. This way, complex data can be used easily in items. This impact psEvent and serItemsData* methods which are renamed transItemsData* because there are not always serialising anymore (a new argument "serialise" allows to specify it). When editing a blog post in jp, metadata are now more easy to manipulate, specially lists like tags.
author Goffi <goffi@goffi.org>
date Sat, 23 Feb 2019 18:59:00 +0100
parents ae127e14b040
children cd391ea847cb
comparison
equal deleted inserted replaced
2806:2400cad2dace 2807:0b7ce5daee9b
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23 23
24 log = getLogger(__name__) 24 log = getLogger(__name__)
25 from sat.core import exceptions 25 from sat.core import exceptions
26 from sat.tools import sat_defer 26 from sat.tools import sat_defer
27 from sat.tools.common import data_format
27 28
28 from twisted.words.protocols.jabber import jid, error 29 from twisted.words.protocols.jabber import jid, error
29 from twisted.internet import reactor, defer 30 from twisted.internet import reactor, defer
30 from wokkel import disco 31 from wokkel import disco
31 from wokkel import data_form 32 from wokkel import data_form
278 async=True, 279 async=True,
279 ) 280 )
280 281
281 #  high level observer method 282 #  high level observer method
282 host.bridge.addSignal( 283 host.bridge.addSignal(
283 "psEvent", ".plugin", signature="ssssa{ss}s" 284 "psEvent", ".plugin", signature="ssssss"
284 ) # args: category, service(jid), node, type (C.PS_ITEMS, C.PS_DELETE), data, profile 285 ) # args: category, service(jid), node, type (C.PS_ITEMS, C.PS_DELETE), data, profile
285 286
286 # low level observer method, used if service/node is in watching list (see psNodeWatch* methods) 287 # low level observer method, used if service/node is in watching list (see psNodeWatch* methods)
287 host.bridge.addSignal( 288 host.bridge.addSignal(
288 "psEventRaw", ".plugin", signature="sssass" 289 "psEventRaw", ".plugin", signature="sssass"
566 item_ids, 567 item_ids,
567 sub_id or None, 568 sub_id or None,
568 extra.rsm_request, 569 extra.rsm_request,
569 extra.extra, 570 extra.extra,
570 ) 571 )
571 d.addCallback(self.serItemsData) 572 d.addCallback(self.transItemsData)
572 return d 573 return d
573 574
574 def getItems(self, client, service, node, max_items=None, item_ids=None, sub_id=None, 575 def getItems(self, client, service, node, max_items=None, item_ids=None, sub_id=None,
575 rsm_request=None, extra=None): 576 rsm_request=None, extra=None):
576 """Retrieve pubsub items from a node. 577 """Retrieve pubsub items from a node.
646 node, unicode(service), unicode(failure.value) 647 node, unicode(service), unicode(failure.value)
647 ) 648 )
648 ) 649 )
649 650
650 def doSubscribe(data): 651 def doSubscribe(data):
651 self.subscribe(service, node, profile_key=client.profile).addErrback( 652 self.subscribe(client, service, node).addErrback(
652 subscribeEb, service, node 653 subscribeEb, service, node
653 ) 654 )
654 return data 655 return data
655 656
656 if subscribe: 657 if subscribe:
1073 def getRTResults( 1074 def getRTResults(
1074 self, session_id, on_success=None, on_error=None, profile=C.PROF_KEY_NONE 1075 self, session_id, on_success=None, on_error=None, profile=C.PROF_KEY_NONE
1075 ): 1076 ):
1076 return self.rt_sessions.getResults(session_id, on_success, on_error, profile) 1077 return self.rt_sessions.getResults(session_id, on_success, on_error, profile)
1077 1078
1078 def serItemsData(self, items_data, item_cb=lambda item: item.toXml()): 1079 def transItemsData(self, items_data, item_cb=lambda item: item.toXml(),
1079 """Helper method to serialise result from [getItems] 1080 serialise=False):
1081 """Helper method to transform result from [getItems]
1080 1082
1081 the items_data must be a tuple(list[domish.Element], dict[unicode, unicode]) 1083 the items_data must be a tuple(list[domish.Element], dict[unicode, unicode])
1082 as returned by [getItems]. metadata values are then casted to unicode and 1084 as returned by [getItems]. metadata values are then casted to unicode and
1083 each item is passed to items_cb 1085 each item is passed to items_cb then optionally serialised with
1086 data_format.serialise.
1084 @param items_data(tuple): tuple returned by [getItems] 1087 @param items_data(tuple): tuple returned by [getItems]
1085 @param item_cb(callable): method to transform each item 1088 @param item_cb(callable): method to transform each item
1089 @param serialise(bool): if True do a data_format.serialise
1090 after applying item_cb
1086 @return (tuple): a serialised form ready to go throught bridge 1091 @return (tuple): a serialised form ready to go throught bridge
1087 """ 1092 """
1088 items, metadata = items_data 1093 items, metadata = items_data
1094 if serialise:
1095 items = [data_format.serialise(item_cb(item)) for item in items]
1096 else:
1097 items = [item_cb(item) for item in items]
1098
1089 return ( 1099 return (
1090 [item_cb(item) for item in items], 1100 items,
1091 {key: unicode(value) for key, value in metadata.iteritems()}, 1101 {key: unicode(value) for key, value in metadata.iteritems()},
1092 ) 1102 )
1093 1103
1094 def serItemsDataD(self, items_data, item_cb): 1104 def transItemsDataD(self, items_data, item_cb, serialise=False):
1095 """Helper method to serialise result from [getItems], deferred version 1105 """Helper method to transform result from [getItems], deferred version
1096 1106
1097 the items_data must be a tuple(list[domish.Element], dict[unicode, unicode]) 1107 the items_data must be a tuple(list[domish.Element], dict[unicode, unicode])
1098 as returned by [getItems]. metadata values are then casted to unicode and 1108 as returned by [getItems]. metadata values are then casted to unicode and
1099 each item is passed to items_cb 1109 each item is passed to items_cb then optionally serialised with
1100 An errback is added to item_cb, and when it is fired the value is filtered from final items 1110 data_format.serialise.
1111 An errback is added to item_cb, and when it is fired the value is filtered from
1112 final items
1101 @param items_data(tuple): tuple returned by [getItems] 1113 @param items_data(tuple): tuple returned by [getItems]
1102 @param item_cb(callable): method to transform each item (must return a deferred) 1114 @param item_cb(callable): method to transform each item (must return a deferred)
1103 @return (tuple): a deferred which fire a serialised form ready to go throught bridge 1115 @param serialise(bool): if True do a data_format.serialise
1116 after applying item_cb
1117 @return (tuple): a deferred which fire a serialised form ready to go throught
1118 bridge
1104 """ 1119 """
1105 items, metadata = items_data 1120 items, metadata = items_data
1106 1121
1107 def eb(failure): 1122 def eb(failure):
1108 log.warning( 1123 log.warning(
1109 "Error while serialising/parsing item: {}".format(unicode(failure.value)) 1124 "Error while serialising/parsing item: {}".format(unicode(failure.value))
1110 ) 1125 )
1111 1126
1112 d = defer.gatherResults([item_cb(item).addErrback(eb) for item in items]) 1127 d = defer.gatherResults([item_cb(item).addErrback(eb) for item in items])
1113 1128
1114 def finishSerialisation(serialised_items): 1129 def finishSerialisation(parsed_items):
1130 if serialise:
1131 items = [data_format.serialise(i) for i in parsed_items if i is not None]
1132 else:
1133 items = [i for i in parsed_items if i is not None]
1134
1115 return ( 1135 return (
1116 [item for item in serialised_items if item is not None], 1136 items,
1117 {key: unicode(value) for key, value in metadata.iteritems()}, 1137 {key: unicode(value) for key, value in metadata.iteritems()},
1118 ) 1138 )
1119 1139
1120 d.addCallback(finishSerialisation) 1140 d.addCallback(finishSerialisation)
1121 return d 1141 return d
1313 - metadata(dict): serialised metadata 1333 - metadata(dict): serialised metadata
1314 """ 1334 """
1315 profile = self.host.getClient(profile_key).profile 1335 profile = self.host.getClient(profile_key).profile
1316 d = self.rt_sessions.getResults( 1336 d = self.rt_sessions.getResults(
1317 session_id, 1337 session_id,
1318 on_success=lambda result: ("", self.serItemsData(result)), 1338 on_success=lambda result: ("", self.transItemsData(result)),
1319 on_error=lambda failure: (unicode(failure.value) or UNSPECIFIED, ([], {})), 1339 on_error=lambda failure: (unicode(failure.value) or UNSPECIFIED, ([], {})),
1320 profile=profile, 1340 profile=profile,
1321 ) 1341 )
1322 d.addCallback( 1342 d.addCallback(
1323 lambda ret: ( 1343 lambda ret: (
1346 extra.rsm_request, 1366 extra.rsm_request,
1347 extra.extra, 1367 extra.extra,
1348 profile_key, 1368 profile_key,
1349 ) 1369 )
1350 1370
1351 def getFromMany( 1371 def getFromMany(self, node_data, max_item=None, rsm_request=None, extra=None,
1352 self, 1372 profile_key=C.PROF_KEY_NONE):
1353 node_data,
1354 max_item=None,
1355 rsm_request=None,
1356 extra=None,
1357 profile_key=C.PROF_KEY_NONE,
1358 ):
1359 """Get items from many nodes at once 1373 """Get items from many nodes at once
1360 1374
1361 @param node_data (iterable[tuple]): iterable of tuple (service, node) where: 1375 @param node_data (iterable[tuple]): iterable of tuple (service, node) where:
1362 - service (jid.JID) is the pubsub service 1376 - service (jid.JID) is the pubsub service
1363 - node (unicode) is the node to get items from 1377 - node (unicode) is the node to get items from