# HG changeset patch # User Goffi # Date 1684160445 -7200 # Node ID 2594e1951cf7165d4e705d8b65044fe6f221bc6c # Parent 1f967f85fc235106a87573e2aeb062812834b24f core (bridge): `action_new` now use serialised dict for extra data. diff -r 1f967f85fc23 -r 2594e1951cf7 sat/bridge/bridge_constructor/bridge_template.ini --- a/sat/bridge/bridge_constructor/bridge_template.ini Mon May 15 16:20:38 2023 +0200 +++ b/sat/bridge/bridge_constructor/bridge_template.ini Mon May 15 16:20:45 2023 +0200 @@ -120,10 +120,10 @@ [action_new] type=signal category=core -sig_in=a{ss}sis +sig_in=ssis doc=A frontend action is requested -doc_param_0=action_data: a dict where key can be: - - xmlui: a XMLUI need to be displayed +doc_param_0=action_data: a serialised dict where key can be: + - xmlui: a XMLUI describing the action - progress: a progress id - meta_*: meta information on the action, used to make automation more easy, some are defined below @@ -761,8 +761,8 @@ async= type=method category=core -sig_in=sa{ss}s -sig_out=a{ss} +sig_in=sss +sig_out=s param_2_default="@DEFAULT@" doc=Launch a registred action doc_param_0=callback_id: id of the registred callback @@ -775,7 +775,7 @@ type=method category=core sig_in=s -sig_out=a(a{ss}si) +sig_out=a(ssi) param_0_default="@DEFAULT@" doc=Get all not yet answered actions doc_param_0=%(doc_profile_key)s diff -r 1f967f85fc23 -r 2594e1951cf7 sat/bridge/dbus_bridge.py --- a/sat/bridge/dbus_bridge.py Mon May 15 16:20:38 2023 +0200 +++ b/sat/bridge/dbus_bridge.py Mon May 15 16:20:45 2023 +0200 @@ -88,8 +88,8 @@ core_iface = DBusInterface( const_INT_PREFIX + const_CORE_SUFFIX, - Method('action_launch', arguments='sa{ss}s', returns='a{ss}'), - Method('actions_get', arguments='s', returns='a(a{ss}si)'), + Method('action_launch', arguments='sss', returns='s'), + Method('actions_get', arguments='s', returns='a(ssi)'), Method('config_get', arguments='ss', returns='s'), Method('connect', arguments='ssa{ss}', returns='b'), Method('contact_add', arguments='ss', returns=''), @@ -155,7 +155,7 @@ Method('subscription', arguments='sss', returns=''), Method('version_get', arguments='', returns='s'), Signal('_debug', 'sa{ss}s'), - Signal('action_new', 'a{ss}sis'), + Signal('action_new', 'ssis'), Signal('connected', 'ss'), Signal('contact_deleted', 'ss'), Signal('contact_new', 'sa{ss}ass'), diff -r 1f967f85fc23 -r 2594e1951cf7 sat/core/sat_main.py --- a/sat/core/sat_main.py Mon May 15 16:20:38 2023 +0200 +++ b/sat/core/sat_main.py Mon May 15 16:20:45 2023 +0200 @@ -36,6 +36,7 @@ from wokkel.xmppim import RosterItem from sat.core import xmpp from sat.core import exceptions +from sat.core.core_types import SatXMPPEntity from sat.core.log import getLogger from sat.core.constants import Const as C @@ -201,7 +202,7 @@ self.bridge.register_method("contact_del", self._del_contact) self.bridge.register_method("roster_resync", self._roster_resync) self.bridge.register_method("is_connected", self.is_connected) - self.bridge.register_method("action_launch", self.launch_callback) + self.bridge.register_method("action_launch", self._action_launch) self.bridge.register_method("actions_get", self.actions_get) self.bridge.register_method("progress_get", self._progress_get) self.bridge.register_method("progress_get_all", self._progress_get_all) @@ -885,7 +886,7 @@ def get_local_path( self, - client: Optional[xmpp.SatXMPPEntity], + client: Optional[SatXMPPEntity], dir_name: str, *extra_path: str, component: bool = False, @@ -1119,7 +1120,7 @@ async def find_by_features( self, - client: xmpp.SatXMPPEntity, + client: SatXMPPEntity, namespaces: List[str], identities: Optional[List[Tuple[str, str]]]=None, bare_jids: bool=False, @@ -1277,7 +1278,9 @@ action_timer = reactor.callLater(60 * 30, self._kill_action, keep_id, client) client.actions[keep_id] = (action_data, id_, security_limit, action_timer) - self.bridge.action_new(action_data, id_, security_limit, profile) + self.bridge.action_new( + data_format.serialise(action_data), id_, security_limit, profile + ) def actions_get(self, profile): """Return current non answered actions @@ -1285,7 +1288,10 @@ @param profile: %(doc_profile)s """ client = self.get_client(profile) - return [action_tuple[:-1] for action_tuple in client.actions.values()] + return [ + (data_format.serialise(action_tuple[0]), *action_tuple[1:-1]) + for action_tuple in client.actions.values() + ] def register_progress_cb( self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE @@ -1401,7 +1407,7 @@ def purge_callback(): try: - self.removeCallback(callback_id) + self.remove_callback(callback_id) except KeyError: pass @@ -1409,13 +1415,32 @@ return callback_id - def removeCallback(self, callback_id): + def remove_callback(self, callback_id): """ Remove a previously registered callback @param callback_id: id returned by [register_callback] """ log.debug("Removing callback [%s]" % callback_id) del self._cb_map[callback_id] - def launch_callback(self, callback_id, data=None, profile_key=C.PROF_KEY_NONE): + def _action_launch( + self, + callback_id: str, + data_s: str, + profile_key: str + ) -> defer.Deferred: + d = self.launch_callback( + callback_id, + data_format.deserialise(data_s), + profile_key + ) + d.addCallback(data_format.serialise) + return d + + def launch_callback( + self, + callback_id: str, + data: Optional[dict] = None, + profile_key: str = C.PROF_KEY_NONE + ) -> defer.Deferred: """Launch a specific callback @param callback_id: id of the action (callback) to launch @@ -1428,7 +1453,9 @@ - C.BOOL_TRUE - C.BOOL_FALSE """ - #  FIXME: security limit need to be checked here + # FIXME: is it possible to use this method without profile connected? If not, + # client must be used instead of profile_key + # FIXME: security limit need to be checked here try: client = self.get_client(profile_key) except exceptions.NotFound: @@ -1466,7 +1493,7 @@ del kwargs["with_data"] if kwargs.pop("one_shot", False): - self.removeCallback(callback_id) + self.remove_callback(callback_id) return utils.as_deferred(callback, *args, **kwargs) diff -r 1f967f85fc23 -r 2594e1951cf7 sat/tools/xml_tools.py --- a/sat/tools/xml_tools.py Mon May 15 16:20:38 2023 +0200 +++ b/sat/tools/xml_tools.py Mon May 15 16:20:45 2023 +0200 @@ -1755,23 +1755,31 @@ return xmlui_d -def defer_dialog(host, message, title="Please confirm", type_=C.XMLUI_DIALOG_CONFIRM, - options=None, action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False, - profile=C.PROF_KEY_NONE): +def defer_dialog( + host, + message: str, + title: str = "Please confirm", + type_: str = C.XMLUI_DIALOG_CONFIRM, + options: Optional[dict] = None, + action_extra: Optional[dict] = None, + security_limit: int = C.NO_SECURITY_LIMIT, + chained: bool = False, + profile: str = C.PROF_KEY_NONE +) -> defer.Deferred: """Create a submitable dialog and manage it with a deferred - @param message(unicode): message to display - @param title(unicode): title of the dialog - @param type(unicode): dialog type (C.XMLUI_DIALOG_*) - @param options(None, dict): if not None, will be used to update (extend) dialog_opt - arguments of XMLUI - @param action_extra(None, dict): extra action to merge with xmlui + @param message: message to display + @param title: title of the dialog + @param type: dialog type (C.XMLUI_DIALOG_* or plugin specific string) + @param options: if not None, will be used to update (extend) dialog_opt arguments of + XMLUI + @param action_extra: extra action to merge with xmlui mainly used to add meta informations (see action_new doc) @param security_limit: %(doc_security_limit)s - @param chained(bool): True if the Deferred result must be returned to the frontend + @param chained: True if the Deferred result must be returned to the frontend useful when backend is in a series of dialogs with an ui @param profile: %(doc_profile)s - @return (dict): Deferred dict + @return: answer dict """ assert profile is not None dialog_opt = {"type": type_, "message": message} diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/bridge/dbus_bridge.py --- a/sat_frontends/bridge/dbus_bridge.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/bridge/dbus_bridge.py Mon May 15 16:20:45 2023 +0200 @@ -165,7 +165,7 @@ if errback is None: errback = log.error error_handler = lambda err:errback(dbus_to_bridge_exception(err)) - return self.db_core_iface.action_launch(callback_id, data, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler) + return str(self.db_core_iface.action_launch(callback_id, data, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)) def actions_get(self, profile_key="@DEFAULT@", callback=None, errback=None): if callback is None: diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/jp/base.py --- a/sat_frontends/jp/base.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/jp/base.py Mon May 15 16:20:45 2023 +0200 @@ -1387,9 +1387,16 @@ def __init__(self, *args, **kwargs): super(CommandAnswering, self).__init__(*args, **kwargs) - async def on_action_new(self, action_data, action_id, security_limit, profile): + async def on_action_new( + self, + action_data_s: str, + action_id: str, + security_limit: int, + profile: str + ) -> None: if profile != self.profile: return + action_data = data_format.deserialise(action_data_s) try: action_type = action_data['meta_type'] except KeyError: @@ -1424,5 +1431,5 @@ """Auto reply to confirmation requests""" self.host.bridge.register_signal("action_new", self.on_action_new) actions = await self.host.bridge.actions_get(self.profile) - for action_data, action_id, security_limit in actions: - await self.on_action_new(action_data, action_id, security_limit, self.profile) + for action_data_s, action_id, security_limit in actions: + await self.on_action_new(action_data_s, action_id, security_limit, self.profile) diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/jp/cmd_file.py --- a/sat_frontends/jp/cmd_file.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/jp/cmd_file.py Mon May 15 16:20:45 2023 +0200 @@ -387,12 +387,15 @@ if self._overwrite_refused: self.disp(_("File refused because overwrite is needed"), error=True) await self.host.bridge.action_launch( - xmlui_id, {"cancelled": C.BOOL_TRUE}, profile_key=profile + xmlui_id, data_format.serialise({"cancelled": C.BOOL_TRUE}), + profile_key=profile ) return self.host.quit_from_signal(2) await self.set_progress_id(progress_id) xmlui_data = {"path": self.path} - await self.host.bridge.action_launch(xmlui_id, xmlui_data, profile_key=profile) + await self.host.bridge.action_launch( + xmlui_id, data_format.serialise(xmlui_data), profile_key=profile + ) async def on_overwrite_action(self, action_data, action_id, security_limit, profile): xmlui_id = self.get_xmlui_id(action_data) @@ -413,7 +416,9 @@ self._overwrite_refused = True xmlui_data = {"answer": C.bool_const(self.args.force)} - await self.host.bridge.action_launch(xmlui_id, xmlui_data, profile_key=profile) + await self.host.bridge.action_launch( + xmlui_id, data_format.serialise(xmlui_data), profile_key=profile + ) async def on_not_in_roster_action(self, action_data, action_id, security_limit, profile): xmlui_id = self.get_xmlui_id(action_data) @@ -443,7 +448,9 @@ confirmed = await self.host.confirm(xmlui.dlg.message) xmlui_data = {"answer": C.bool_const(confirmed)} - await self.host.bridge.action_launch(xmlui_id, xmlui_data, profile_key=profile) + await self.host.bridge.action_launch( + xmlui_id, data_format.serialise(xmlui_data), profile_key=profile + ) if not confirmed and not self.args.multiple: self.disp(_("Session refused for {from_jid}").format(from_jid=from_jid)) self.host.quit_from_signal(0) diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/jp/cmd_pipe.py --- a/sat_frontends/jp/cmd_pipe.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/jp/cmd_pipe.py Mon May 15 16:20:45 2023 +0200 @@ -17,15 +17,17 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import socket import asyncio import errno from functools import partial +import socket +import sys + +from sat.core.i18n import _ +from sat.tools.common import data_format from sat_frontends.jp import base -from sat_frontends.jp.constants import Const as C from sat_frontends.jp import xmlui_manager -import sys -from sat.core.i18n import _ +from sat_frontends.jp.constants import Const as C from sat_frontends.tools import jid __commands__ = ["Pipe"] @@ -141,7 +143,8 @@ break xmlui_data = {"answer": C.BOOL_TRUE, "port": str(port)} await self.host.bridge.action_launch( - xmlui_id, xmlui_data, profile_key=profile) + xmlui_id, data_format.serialise(xmlui_data), profile_key=profile + ) async with server: await server.serve_forever() self.host.quit_from_signal() diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/jp/xmlui_manager.py --- a/sat_frontends/jp/xmlui_manager.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/jp/xmlui_manager.py Mon May 15 16:20:45 2023 +0200 @@ -23,6 +23,7 @@ from sat_frontends.jp.constants import Const as C from sat.tools.common.ansi import ANSI as A from sat.core.i18n import _ +from sat.tools.common import data_format log = getLogger(__name__) @@ -619,10 +620,12 @@ async def _xmlui_launch_action(self, action_id, data): XMLUIPanel._actions += 1 try: - data = await self.host.bridge.action_launch( - action_id, - data, - self.profile, + data = data_format.deserialise( + await self.host.bridge.action_launch( + action_id, + data_format.serialise(data), + self.profile, + ) ) except Exception as e: self.disp(f"can't launch XMLUI action: {e}", error=True) diff -r 1f967f85fc23 -r 2594e1951cf7 sat_frontends/quick_frontend/quick_app.py --- a/sat_frontends/quick_frontend/quick_app.py Mon May 15 16:20:38 2023 +0200 +++ b/sat_frontends/quick_frontend/quick_app.py Mon May 15 16:20:45 2023 +0200 @@ -742,8 +742,10 @@ self.sync = False self.set_presence_status(C.PRESENCE_UNAVAILABLE, "", profile=profile) - def action_new_handler(self, action_data, id_, security_limit, profile): - self.action_manager(action_data, user_action=False, profile=profile) + def action_new_handler(self, action_data_s, id_, security_limit, profile): + self.action_manager( + data_format.deserialise(action_data_s), user_action=False, profile=profile + ) def contact_new_handler(self, jid_s, attributes, groups, profile): entity = jid.JID(jid_s) @@ -1298,18 +1300,6 @@ self.register_progress_cbs(progress_id, progress_cb, progress_eb) self.progress_id_handler(progress_id, profile) - # we ignore metadata - action_data = { - k: v for k, v in action_data.items() if not k.startswith("meta_") - } - - if action_data: - raise exceptions.DataError( - "Not all keys in action_data are managed ({keys})".format( - keys=", ".join(list(action_data.keys())) - ) - ) - def _action_cb(self, data, callback, callback_id, profile): if callback is None: self.action_manager(data, profile=profile) @@ -1334,9 +1324,12 @@ """ if data is None: data = dict() - action_cb = lambda data: self._action_cb(data, callback, callback_id, profile) + action_cb = lambda data: self._action_cb( + data_format.deserialise(data), callback, callback_id, profile + ) self.bridge.action_launch( - callback_id, data, profile, callback=action_cb, errback=self.dialog_failure + callback_id, data_format.serialise(data), profile, callback=action_cb, + errback=self.dialog_failure ) def launch_menu(