From 8f7f7eaed75e0d178266d9889f23d3a505c1c0bc Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Sun, 10 May 2026 08:45:52 +1000 Subject: [PATCH] BE: Trigger failing if object non-existent + emoji removal in upgrade message Signed-off-by: jokob-sk --- server/initialise.py | 4 +- server/workflows/actions.py | 128 +++++++++++++++++++++-------------- server/workflows/triggers.py | 6 +- 3 files changed, 83 insertions(+), 55 deletions(-) diff --git a/server/initialise.py b/server/initialise.py index 2414aa08..9509b06b 100755 --- a/server/initialise.py +++ b/server/initialise.py @@ -724,13 +724,13 @@ def importConfigs(pm, db, all_plugins): write_notification( f"""[Upgrade]: App upgraded from {prev_version} to \ - {new_version} 🚀 Please clear the cache: \ + {new_version} Please clear the cache: \
  1. Click OK below
  2. \
  3. Clear the browser cache (shift + browser refresh button)
  4. \
  5. Clear app cache with the (reload) button in the header
  6. \
  7. Go to Settings and click Save
\ Check out new features and what has changed in the \ - 📓 release notes.""", + release notes.""", 'interrupt', timeNowUTC() ) diff --git a/server/workflows/actions.py b/server/workflows/actions.py index 429da0f5..38edb295 100755 --- a/server/workflows/actions.py +++ b/server/workflows/actions.py @@ -14,8 +14,16 @@ class Action: def __init__(self, trigger): self.trigger = trigger - def execute(self, obj): - """Executes the action on the given object.""" + def get_object(self): + """Safely get and normalize the trigger object.""" + obj = getattr(self.trigger, "object", None) + + if isinstance(obj, sqlite3.Row): + obj = dict(obj) + + return obj + + def execute(self): raise NotImplementedError("Subclasses must implement execute()") @@ -23,7 +31,7 @@ class UpdateFieldAction(Action): """Action to update a specific field of an object.""" def __init__(self, db, field, value, trigger): - super().__init__(trigger) # Call the base class constructor + super().__init__(trigger) self.field = field self.value = value self.db = db @@ -31,29 +39,35 @@ class UpdateFieldAction(Action): def execute(self): mylog("verbose", f"[WF] Updating field '{self.field}' to '{self.value}' for event object {self.trigger.object_type}") - obj = self.trigger.object + obj = self.get_object() - # convert to dict for easeir handling - if isinstance(obj, sqlite3.Row): - obj = dict(obj) # Convert Row object to a standard dictionary + if obj is None: + mylog("none", "[WF] Object no longer exists") + return None - processed = False - - # currently unused if isinstance(obj, dict) and "objectGuid" in obj: - mylog("debug", f"[WF] Updating Object '{obj}' ") - plugin_instance = PluginObjectInstance() - plugin_instance.updateField(obj["objectGuid"], self.field, self.value) - processed = True + mylog("debug", f"[WF] Updating Object '{obj}'") - elif isinstance(obj, dict) and "devGUID" in obj: - mylog("debug", f"[WF] Updating Device '{obj}' ") - device_instance = DeviceInstance() - device_instance.updateField(obj["devGUID"], self.field, self.value) - processed = True + PluginObjectInstance().updateField( + obj["objectGuid"], + self.field, + self.value, + ) - if not processed: - mylog("none", f"[WF] Could not process action for object: {obj}") + return obj + + if isinstance(obj, dict) and "devGUID" in obj: + mylog("debug", f"[WF] Updating Device '{obj}'") + + DeviceInstance().updateField( + obj["devGUID"], + self.field, + self.value, + ) + + return obj + + mylog("none", f"[WF] Unsupported object format: {obj}") return obj @@ -62,35 +76,33 @@ class DeleteObjectAction(Action): """Action to delete an object.""" def __init__(self, db, trigger): - super().__init__(trigger) # Call the base class constructor + super().__init__(trigger) self.db = db def execute(self): mylog("verbose", f"[WF] Deleting event object {self.trigger.object_type}") - obj = self.trigger.object + obj = self.get_object() - # convert to dict for easeir handling - if isinstance(obj, sqlite3.Row): - obj = dict(obj) # Convert Row object to a standard dictionary + if obj is None: + mylog("none", "[WF] Object no longer exists") + return None - processed = False - - # currently unused if isinstance(obj, dict) and "objectGuid" in obj: - mylog("debug", f"[WF] Updating Object '{obj}' ") - plugin_instance = PluginObjectInstance() - plugin_instance.delete(obj["objectGuid"]) - processed = True + mylog("debug", f"[WF] Deleting Object '{obj}'") - elif isinstance(obj, dict) and "devGUID" in obj: - mylog("debug", f"[WF] Updating Device '{obj}' ") - device_instance = DeviceInstance() - device_instance.delete(obj["devGUID"]) - processed = True + PluginObjectInstance().delete(obj["objectGuid"]) - if not processed: - mylog("none", f"[WF] Could not process action for object: {obj}") + return obj + + if isinstance(obj, dict) and "devGUID" in obj: + mylog("debug", f"[WF] Deleting Device '{obj}'") + + DeviceInstance().delete(obj["devGUID"]) + + return obj + + mylog("none", f"[WF] Unsupported object format: {obj}") return obj @@ -98,16 +110,22 @@ class DeleteObjectAction(Action): class RunPluginAction(Action): """Action to run a specific plugin.""" - def __init__(self, plugin_name, params, trigger): # Add trigger - super().__init__(trigger) # Call parent constructor + def __init__(self, plugin_name, params, trigger): + super().__init__(trigger) self.plugin_name = plugin_name self.params = params def execute(self): - obj = self.trigger.object + obj = self.get_object() + + if obj is None: + mylog("none", "[WF] Object no longer exists") + return None + + mylog("verbose", f"[WF] Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}") + + # PluginManager.run(self.plugin_name, self.params) - mylog("verbose", f"Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}") - # PluginManager.run(self.plugin_name, self.parameters) return obj @@ -115,14 +133,21 @@ class SendNotificationAction(Action): """Action to send a notification.""" def __init__(self, method, message, trigger): - super().__init__(trigger) # Call parent constructor - self.method = method # Fix attribute name + super().__init__(trigger) + self.method = method self.message = message def execute(self): - obj = self.trigger.object - mylog("verbose", f"Sending notification via '{self.method}': {self.message} for object {obj}") + obj = self.get_object() + + if obj is None: + mylog("none", "[WF] Object no longer exists") + return None + + mylog("verbose", f"[WF] Sending notification via '{self.method}': {self.message} for object {obj}") + # NotificationManager.send(self.method, self.message) + return obj @@ -132,7 +157,6 @@ class ActionGroup: def __init__(self, actions): self.actions = actions - def execute(self, obj): + def execute(self): for action in self.actions: - action.execute(obj) - return obj + action.execute() \ No newline at end of file diff --git a/server/workflows/triggers.py b/server/workflows/triggers.py index ed8ec4b9..86e305ec 100755 --- a/server/workflows/triggers.py +++ b/server/workflows/triggers.py @@ -48,7 +48,11 @@ class Trigger: mylog("debug", [query]) result = db.sql.execute(query).fetchall() - self.object = result[0] + + if len(result) > 0: + self.object = result[0] + else: + self.object = None else: self.object = None