Переглянути джерело

feat: a notification can be skipped and retried, so it cannot end a run

Wiring 35photo2anime's generation failure to a Nextcloud message instead of
a stop-and-error moves the failure from "ends the whole scheduled pass" to
"says so and takes the next photo". That only holds if the message itself
cannot fail the run - otherwise a chat outage puts back the very failure it
replaced, in the middle of reporting a different one.

So the node gains skipOnError, returning success false with the reason, and
retries, defaulting to 1 - posting a message is safe to repeat, and it now
costs nothing to say so because the retry lives in the HTTP helper.

Written as a plain function called from execute rather than a method reached
through this, because how the runner invokes a node's execute is not
something a node should be betting on.

The workflow change is in the database, not here: the false branch of "Did
the image get made" now goes to a Say It Failed notification and back to the
Loop, and the stop-and-error node is gone. All four ways an iteration can end
now lead back to the loop.

Verified by standing in for a job that ran and came back unsuccessful: the
gate went false, the message was posted, and the run completed rather than
failing. 62/62.
fszontagh 1 місяць тому
батько
коміт
633e940f74
1 змінених файлів з 40 додано та 9 видалено
  1. 40 9
      nodes/integration/nextcloud-talk.js

+ 40 - 9
nodes/integration/nextcloud-talk.js

@@ -60,6 +60,14 @@ const configSchema = {
             minimum: 1,
             maximum: 200
         },
+        retries: {
+            type: 'number', title: 'Retries', default: 1,
+            description: 'Extra attempts when Nextcloud times out or answers 429, 500, 502, 503 or 504. Posting a message is safe to repeat'
+        },
+        skipOnError: {
+            type: 'boolean', title: 'Skip On Error', default: false,
+            description: 'Return success false instead of failing the workflow. A notification that could not be sent is rarely worth ending a run over - least of all when the run is already reporting a problem'
+        },
         timeoutMs: {
             type: 'number',
             title: 'Timeout (ms)',
@@ -80,6 +88,7 @@ const inputSchema = {
 const outputSchema = {
     type: 'object',
     properties: {
+        error: { type: 'string', description: 'Why the send failed, when Skip On Error let the run carry on' },
         success: { type: 'boolean' },
         messageId: { type: 'integer', description: 'The id Talk gave the posted message' },
         token: { type: 'string', description: 'The conversation it was posted to' },
@@ -185,13 +194,8 @@ function unwrapOcs(response, what) {
     return ocs.data;
 }
 
-module.exports = {
-    configSchema,
-    inputSchema,
-    outputSchema,
-    outputs,
-
-    async execute(config, input, context) {
+async function talk(config, input, context) {
+    {
         const from = incoming(input);
 
         const base = String(config.baseUrl || '').replace(/\/+$/, '');
@@ -218,7 +222,9 @@ module.exports = {
                 method: 'GET',
                 url: chatUrl + '?format=json&lookIntoFuture=0&limit=' + limit,
                 headers: headers,
-                timeout: timeout
+                timeout: timeout,
+                retries: Number(config.retries) >= 0 ? Number(config.retries) : 1,
+                retryDelayMs: 2000
             });
 
             const data = unwrapOcs(response, 'read') || [];
@@ -258,7 +264,9 @@ module.exports = {
             url: chatUrl + '?format=json',
             headers: headers,
             body: JSON.stringify(payload),
-            timeout: timeout
+            timeout: timeout,
+            retries: Number(config.retries) >= 0 ? Number(config.retries) : 1,
+            retryDelayMs: 2000
         });
 
         const data = unwrapOcs(response, 'send') || {};
@@ -270,4 +278,27 @@ module.exports = {
             token: token
         };
     }
+}
+
+module.exports = {
+    configSchema,
+    inputSchema,
+    outputSchema,
+    outputs,
+
+    async execute(config, input, context) {
+        try {
+            return await talk(config, input, context);
+        } catch (err) {
+            if (config.skipOnError !== true) {
+                throw err;
+            }
+            // A notification that could not be sent is rarely worth ending a run
+            // over - least of all when the run is already reporting a problem,
+            // where failing here would put back the very failure it replaced.
+            const message = (err && err.message) ? err.message : String(err);
+            smartbotic.log.warn('Nextcloud Talk: skipping after ' + message);
+            return { success: false, error: message, messageId: null, token: config.token || '' };
+        }
+    }
 };