|
@@ -16,6 +16,7 @@ WorkflowController::WorkflowController(storage::StorageClient& storage,
|
|
|
runners::LoadBalancer& load_balancer,
|
|
runners::LoadBalancer& load_balancer,
|
|
|
WebSocketServer& ws_server,
|
|
WebSocketServer& ws_server,
|
|
|
WorkflowScheduler& scheduler,
|
|
WorkflowScheduler& scheduler,
|
|
|
|
|
+ DatabaseWatcher& db_watcher,
|
|
|
nodes::NodeStore& node_store)
|
|
nodes::NodeStore& node_store)
|
|
|
: storage_(storage)
|
|
: storage_(storage)
|
|
|
, middleware_(middleware)
|
|
, middleware_(middleware)
|
|
@@ -23,6 +24,7 @@ WorkflowController::WorkflowController(storage::StorageClient& storage,
|
|
|
, load_balancer_(load_balancer)
|
|
, load_balancer_(load_balancer)
|
|
|
, ws_server_(ws_server)
|
|
, ws_server_(ws_server)
|
|
|
, scheduler_(scheduler)
|
|
, scheduler_(scheduler)
|
|
|
|
|
+ , db_watcher_(db_watcher)
|
|
|
, node_store_(node_store) {}
|
|
, node_store_(node_store) {}
|
|
|
|
|
|
|
|
void WorkflowController::materializeNodeConfigDefaults(nlohmann::json& body) {
|
|
void WorkflowController::materializeNodeConfigDefaults(nlohmann::json& body) {
|
|
@@ -406,8 +408,10 @@ void WorkflowController::deleteWorkflow(const httplib::Request& req, httplib::Re
|
|
|
const auth::AuthContext& ctx) {
|
|
const auth::AuthContext& ctx) {
|
|
|
std::string id = req.matches[1];
|
|
std::string id = req.matches[1];
|
|
|
|
|
|
|
|
- // Unregister from scheduler before deleting
|
|
|
|
|
- scheduler_.unregisterWorkflow(id);
|
|
|
|
|
|
|
+ // Every trigger it had, not just the scheduled ones - a deleted workflow
|
|
|
|
|
+ // that kept its database watch would go on being started by changes, with
|
|
|
|
|
+ // nothing left to run.
|
|
|
|
|
+ updateScheduledTriggers(id, false);
|
|
|
|
|
|
|
|
auto result = storage_.remove("workflows", id);
|
|
auto result = storage_.remove("workflows", id);
|
|
|
if (result.failed()) {
|
|
if (result.failed()) {
|
|
@@ -677,8 +681,10 @@ void WorkflowController::deactivateWorkflow(const httplib::Request& req, httplib
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Unregister from scheduler
|
|
|
|
|
- scheduler_.unregisterWorkflow(id);
|
|
|
|
|
|
|
+ // Through the one function that owns trigger registration, not straight at
|
|
|
|
|
+ // the scheduler. Reaching past it meant a workflow switched off kept its
|
|
|
|
|
+ // database watch and went on running on every change.
|
|
|
|
|
+ updateScheduledTriggers(id, false);
|
|
|
|
|
|
|
|
ws_server_.broadcast("workflows.deactivated", {{"id", id}});
|
|
ws_server_.broadcast("workflows.deactivated", {{"id", id}});
|
|
|
sendJson(res, {{"success", true}, {"active", false}});
|
|
sendJson(res, {{"success", true}, {"active", false}});
|
|
@@ -687,9 +693,15 @@ void WorkflowController::deactivateWorkflow(const httplib::Request& req, httplib
|
|
|
void WorkflowController::updateScheduledTriggers(const std::string& workflow_id, bool activate) {
|
|
void WorkflowController::updateScheduledTriggers(const std::string& workflow_id, bool activate) {
|
|
|
if (!activate) {
|
|
if (!activate) {
|
|
|
scheduler_.unregisterWorkflow(workflow_id);
|
|
scheduler_.unregisterWorkflow(workflow_id);
|
|
|
|
|
+ db_watcher_.unwatch(workflow_id);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Re-registering from scratch. A workflow whose trigger was edited - a
|
|
|
|
|
+ // different collection, or the trigger removed - would otherwise keep
|
|
|
|
|
+ // listening to the old one.
|
|
|
|
|
+ db_watcher_.unwatch(workflow_id);
|
|
|
|
|
+
|
|
|
// Get workflow
|
|
// Get workflow
|
|
|
auto workflow_result = storage_.get("workflows", workflow_id);
|
|
auto workflow_result = storage_.get("workflows", workflow_id);
|
|
|
if (workflow_result.failed()) {
|
|
if (workflow_result.failed()) {
|
|
@@ -713,6 +725,27 @@ void WorkflowController::updateScheduledTriggers(const std::string& workflow_id,
|
|
|
|
|
|
|
|
auto& node_def = node_result.value();
|
|
auto& node_def = node_result.value();
|
|
|
|
|
|
|
|
|
|
+ // A database-change trigger is told rather than asked: the database
|
|
|
|
|
+ // reports the change, so there is no interval to register.
|
|
|
|
|
+ if (node_type == "database-change") {
|
|
|
|
|
+ auto config = common::applyConfigDefaults(
|
|
|
|
|
+ node.value("config", nlohmann::json::object()), node_def.config_schema);
|
|
|
|
|
+ const std::string collection = config.value("collection", "");
|
|
|
|
|
+ if (collection.empty()) {
|
|
|
|
|
+ LOG_WARN("Workflow {} has a database-change trigger with no collection set",
|
|
|
|
|
+ workflow_id);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ std::vector<std::string> event_types;
|
|
|
|
|
+ if (config.contains("eventTypes") && config["eventTypes"].is_array()) {
|
|
|
|
|
+ for (const auto& t : config["eventTypes"]) {
|
|
|
|
|
+ if (t.is_string()) event_types.push_back(t.get<std::string>());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ db_watcher_.watch(workflow_id, workflow_name, node_id, collection, event_types);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Check if node has scheduling capability (is_scheduled flag or pollInterval config)
|
|
// Check if node has scheduling capability (is_scheduled flag or pollInterval config)
|
|
|
bool is_scheduled = node_def.is_scheduled;
|
|
bool is_scheduled = node_def.is_scheduled;
|
|
|
if (!is_scheduled) {
|
|
if (!is_scheduled) {
|