diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/CustomTableTriggerRecordAutomationHandler.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/CustomTableTriggerRecordAutomationHandler.java
new file mode 100644
index 00000000..661ee3b8
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/CustomTableTriggerRecordAutomationHandler.java
@@ -0,0 +1,38 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2025. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.automation;
+
+
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
+
+
+/*******************************************************************************
+ ** interface to be implemented by one that wishes to execute custom table triggers
+ *******************************************************************************/
+public interface CustomTableTriggerRecordAutomationHandler extends RecordAutomationHandlerInterface
+{
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ boolean handlesThisInput(RecordAutomationInput recordAutomationInput) throws QException;
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandler.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandler.java
index 9358b58e..781161d9 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandler.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandler.java
@@ -22,19 +22,11 @@
package com.kingsrook.qqq.backend.core.actions.automation;
-import com.kingsrook.qqq.backend.core.exceptions.QException;
-import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
-
-
/*******************************************************************************
** Base class for custom-codes to run as an automation action
*******************************************************************************/
-public abstract class RecordAutomationHandler
+@Deprecated(since = "0.26.0 - when RecordAutomationHandlerInterface was introduced")
+public abstract class RecordAutomationHandler implements RecordAutomationHandlerInterface
{
- /*******************************************************************************
- **
- *******************************************************************************/
- public abstract void execute(RecordAutomationInput recordAutomationInput) throws QException;
-
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandlerInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandlerInterface.java
new file mode 100644
index 00000000..9713934c
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RecordAutomationHandlerInterface.java
@@ -0,0 +1,40 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2022. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.automation;
+
+
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
+
+
+/*******************************************************************************
+ ** Interface for custom-codes to run as an automation action
+ *******************************************************************************/
+public interface RecordAutomationHandlerInterface
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ void execute(RecordAutomationInput recordAutomationInput) throws QException;
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunCustomTableTriggerRecordAutomationHandler.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunCustomTableTriggerRecordAutomationHandler.java
new file mode 100644
index 00000000..94ebe620
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunCustomTableTriggerRecordAutomationHandler.java
@@ -0,0 +1,89 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2023. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.automation;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
+import com.kingsrook.qqq.backend.core.exceptions.QException;
+import com.kingsrook.qqq.backend.core.logging.QLogger;
+import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
+import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
+import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
+
+
+/*******************************************************************************
+ ** RecordAutomationHandler implementation that is called by automation runner
+ ** that doesn't know to deal with a TableTrigger record that it received.
+ **
+ ** e.g., if an app has altered that table (e.g., workflows-qbit).
+ *******************************************************************************/
+public class RunCustomTableTriggerRecordAutomationHandler implements RecordAutomationHandlerInterface
+{
+ private static final QLogger LOG = QLogger.getLogger(RunCustomTableTriggerRecordAutomationHandler.class);
+
+ private static Map handlers = new LinkedHashMap<>();
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ public static void registerHandler(String name, QCodeReference codeReference)
+ {
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // if there's already a value mapped for this name, warn about it (unless it's for the same code reference) //
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ if(handlers.containsKey(name))
+ {
+ if(handlers.get(name).getName().equals(codeReference.getName()))
+ {
+ LOG.warn("Registering a CustomTableTriggerRecordAutomationHandler for a name that is already registered", logPair("name", name));
+ }
+ }
+
+ handlers.put(name, codeReference);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public void execute(RecordAutomationInput recordAutomationInput) throws QException
+ {
+ for(QCodeReference codeReference : handlers.values())
+ {
+ CustomTableTriggerRecordAutomationHandler customHandler = QCodeLoader.getAdHoc(CustomTableTriggerRecordAutomationHandler.class, codeReference);
+ if(customHandler.handlesThisInput(recordAutomationInput))
+ {
+ customHandler.execute(recordAutomationInput);
+ return;
+ }
+ }
+
+ throw (new QException("No custom record automation handler was found for " + recordAutomationInput));
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java
index 2f77c623..c9576433 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/RunRecordScriptAutomationHandler.java
@@ -51,7 +51,7 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
**
*******************************************************************************/
-public class RunRecordScriptAutomationHandler extends RecordAutomationHandler
+public class RunRecordScriptAutomationHandler implements RecordAutomationHandlerInterface
{
private static final QLogger LOG = QLogger.getLogger(RunRecordScriptAutomationHandler.class);
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunner.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunner.java
index 5351b53a..64bc8211 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunner.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunner.java
@@ -33,8 +33,9 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
+import com.kingsrook.qqq.backend.core.actions.automation.RunCustomTableTriggerRecordAutomationHandler;
import com.kingsrook.qqq.backend.core.actions.automation.RunRecordScriptAutomationHandler;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallback;
@@ -442,15 +443,33 @@ public class PollingAutomationPerTableRunner implements Runnable
}
}
- rs.add(new TableAutomationAction()
- .withName("Script:" + tableTrigger.getScriptId())
+ TableAutomationAction tableAutomationAction = new TableAutomationAction()
.withFilter(filter)
.withTriggerEvent(triggerEvent)
.withPriority(tableTrigger.getPriority())
- .withCodeReference(new QCodeReference(RunRecordScriptAutomationHandler.class))
- .withValues(MapBuilder.of("scriptId", tableTrigger.getScriptId()))
- .withIncludeRecordAssociations(true)
- );
+ .withIncludeRecordAssociations(true);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////
+ // if the table trigger has a script id on it, then we know how to run that here in qqq-backend-core //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////
+ if(tableTrigger.getScriptId() != null)
+ {
+ rs.add(tableAutomationAction
+ .withName("Script:" + tableTrigger.getScriptId())
+ .withValues(MapBuilder.of("scriptId", tableTrigger.getScriptId()))
+ .withCodeReference(new QCodeReference(RunRecordScriptAutomationHandler.class)));
+ }
+ else
+ {
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // but - the app may have added an extension to the TableTrigger table (e.g., workflows qbit) //
+ // so, defer to RunCustomRecordAutomationHandler for unrecognized triggers //
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ rs.add(tableAutomationAction
+ .withName("Custom Trigger:" + tableTrigger.getScriptId())
+ .withValues(MapBuilder.of("tableTriggerId", tableTrigger.getId()))
+ .withCodeReference(new QCodeReference(RunCustomTableTriggerRecordAutomationHandler.class)));
+ }
}
catch(Exception e)
{
@@ -526,7 +545,7 @@ public class PollingAutomationPerTableRunner implements Runnable
// note - this method - will re-query the objects, so we should have confidence that their data is fresh... //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
List matchingQRecords = getRecordsMatchingActionFilter(table, records, action);
- LOG.debug("Of the [" + records.size() + "] records that were pending automations, [" + matchingQRecords.size() + "] of them match the filter on the action:" + action);
+ LOG.debug("Of the [" + records.size() + "] records that were pending automations, [" + matchingQRecords.size() + "] of them match the filter on the action:" + action);
if(CollectionUtils.nullSafeHasContents(matchingQRecords))
{
LOG.debug(" Processing " + matchingQRecords.size() + " records in " + table + " for action " + action);
@@ -649,7 +668,7 @@ public class PollingAutomationPerTableRunner implements Runnable
input.setRecordList(records);
input.setAction(action);
- RecordAutomationHandler recordAutomationHandler = QCodeLoader.getAdHoc(RecordAutomationHandler.class, action.getCodeReference());
+ RecordAutomationHandlerInterface recordAutomationHandler = QCodeLoader.getAdHoc(RecordAutomationHandlerInterface.class, action.getCodeReference());
recordAutomationHandler.execute(input);
}
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
index e578d570..b03d1526 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java
@@ -41,7 +41,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph;
@@ -1356,7 +1356,7 @@ public class QInstanceValidator
numberSet++;
if(preAssertionsForCodeReference(action.getCodeReference(), actionPrefix))
{
- validateSimpleCodeReference(actionPrefix + "code reference: ", action.getCodeReference(), RecordAutomationHandler.class);
+ validateSimpleCodeReference(actionPrefix + "code reference: ", action.getCodeReference(), RecordAutomationHandlerInterface.class);
}
}
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfiniteLoopTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfiniteLoopTest.java
index f8c4df67..358482c2 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfiniteLoopTest.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfiniteLoopTest.java
@@ -30,7 +30,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
@@ -163,7 +163,7 @@ public class PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfinite
/*******************************************************************************
**
*******************************************************************************/
- public static class OrderPostInsertAndUpdateAction extends RecordAutomationHandler
+ public static class OrderPostInsertAndUpdateAction implements RecordAutomationHandlerInterface
{
/*******************************************************************************
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerChildPostInsertCustomizerTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerChildPostInsertCustomizerTest.java
index 1e22966c..35915254 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerChildPostInsertCustomizerTest.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/PollingAutomationPerTableRunnerChildPostInsertCustomizerTest.java
@@ -33,7 +33,7 @@ import java.util.UUID;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCustomizer;
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
import com.kingsrook.qqq.backend.core.actions.tables.AggregateAction;
@@ -147,7 +147,7 @@ public class PollingAutomationPerTableRunnerChildPostInsertCustomizerTest extend
/*******************************************************************************
**
*******************************************************************************/
- public static class OrderPostInsertAction extends RecordAutomationHandler
+ public static class OrderPostInsertAction implements RecordAutomationHandlerInterface
{
/*******************************************************************************
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/StandardScheduledExecutorTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/StandardScheduledExecutorTest.java
index 84ed8ae9..dd26d531 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/StandardScheduledExecutorTest.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/automation/polling/StandardScheduledExecutorTest.java
@@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
@@ -162,7 +162,7 @@ class StandardScheduledExecutorTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
- public static class CaptureSessionIdAutomationHandler extends RecordAutomationHandler
+ public static class CaptureSessionIdAutomationHandler implements RecordAutomationHandlerInterface
{
static String sessionId;
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
index 7b81bc23..2add4346 100644
--- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java
@@ -29,7 +29,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
-import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
+import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandlerInterface;
import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarChart;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
@@ -1070,7 +1070,7 @@ public class TestUtils
/*******************************************************************************
**
*******************************************************************************/
- public static class CheckAge extends RecordAutomationHandler
+ public static class CheckAge implements RecordAutomationHandlerInterface
{
public static String SUFFIX_FOR_MINORS = " (a minor)";
@@ -1119,7 +1119,7 @@ public class TestUtils
/*******************************************************************************
**
*******************************************************************************/
- public static class FailAutomationForSith extends RecordAutomationHandler
+ public static class FailAutomationForSith implements RecordAutomationHandlerInterface
{
/*******************************************************************************
@@ -1142,7 +1142,7 @@ public class TestUtils
/*******************************************************************************
**
*******************************************************************************/
- public static class LogPersonUpdate extends RecordAutomationHandler
+ public static class LogPersonUpdate implements RecordAutomationHandlerInterface
{
public static List updatedIds = new ArrayList<>();