n8n
Version:
n8n Workflow Automation Tool
158 lines • 8.79 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkflowPublicationApplier = void 0;
const backend_common_1 = require("@n8n/backend-common");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const ensure_error_1 = require("@n8n/utils/errors/ensure-error");
const trigger_diff_1 = require("../../workflows/publication/trigger-diff");
const workflow_trigger_activator_1 = require("../../workflows/triggers/workflow-trigger-activator");
const workflow_published_data_service_1 = require("../../workflows/workflow-published-data.service");
let WorkflowPublicationApplier = class WorkflowPublicationApplier {
constructor(logger, workflowRepository, workflowHistoryRepository, workflowPublishedVersionRepository, workflowTriggerActivator, workflowPublishedDataService) {
this.logger = logger;
this.workflowRepository = workflowRepository;
this.workflowHistoryRepository = workflowHistoryRepository;
this.workflowPublishedVersionRepository = workflowPublishedVersionRepository;
this.workflowTriggerActivator = workflowTriggerActivator;
this.workflowPublishedDataService = workflowPublishedDataService;
this.logger = this.logger.scoped('workflow-publication');
}
async apply(record) {
const { workflow, oldVersion, newVersion } = await this.resolveVersions(record);
if (!workflow)
return { type: 'skipped', reason: 'workflow-not-found' };
if (workflow.activeVersionId === null) {
return await this.unpublish(workflow, oldVersion, record);
}
if (!newVersion)
return { type: 'version-missing' };
return await this.publish(workflow, oldVersion, newVersion, record);
}
async publish(workflow, oldVersion, newVersion, record) {
const oldTriggerNodes = this.workflowTriggerActivator.getEnabledTriggerNodes(oldVersion);
const desiredTriggerNodes = this.workflowTriggerActivator.getEnabledTriggerNodes(newVersion);
const triggerKinds = this.workflowTriggerActivator.getTriggerKinds(desiredTriggerNodes);
const { toAdd, toRemove } = (0, trigger_diff_1.computeTriggerDiff)(oldTriggerNodes, desiredTriggerNodes);
this.logger.debug(`Calculated trigger diff for workflow publication: ${toAdd.size} to add, ${toRemove.size} to remove`, {
workflowId: record.workflowId,
publishedVersionId: record.publishedVersionId,
toAdd: Array.from(toAdd),
toRemove: Array.from(toRemove),
});
this.workflowTriggerActivator
.getUnregisteredNonWebhookTriggerNodeIds(record.workflowId, desiredTriggerNodes)
.forEach((nodeId) => toAdd.add(nodeId));
const nodesWithUnregisteredWebhooks = await this.workflowTriggerActivator.getNodesWithUnregisteredWebhooks(workflow, newVersion);
nodesWithUnregisteredWebhooks.forEach((nodeId) => toAdd.add(nodeId));
if (toAdd.size === 0 && toRemove.size === 0) {
await this.advancePublishedVersion(record);
return {
type: 'completed',
triggerStatuses: this.buildTriggerStatuses(desiredTriggerNodes, triggerKinds, {
activated: [],
failures: [],
}),
};
}
if (toRemove.size > 0 && oldVersion) {
await this.workflowTriggerActivator.deactivate(workflow, oldVersion, toRemove);
}
await this.advancePublishedVersion(record);
try {
if (toAdd.size > 0) {
const outcome = await this.workflowTriggerActivator.activate(workflow, newVersion, toAdd);
return this.classifyActivationOutcome(outcome, desiredTriggerNodes, triggerKinds);
}
if (toRemove.size > 0) {
await this.workflowTriggerActivator.updateTriggerCount(workflow, newVersion);
}
}
catch (e) {
return { type: 'failed', error: (0, ensure_error_1.ensureError)(e) };
}
return {
type: 'completed',
triggerStatuses: this.buildTriggerStatuses(desiredTriggerNodes, triggerKinds, {
activated: [],
failures: [],
}),
};
}
async unpublish(workflow, oldVersion, record) {
const toRemove = new Set(this.workflowTriggerActivator.getEnabledTriggerNodes(oldVersion).map((node) => node.id));
if (oldVersion && toRemove.size > 0) {
await this.workflowTriggerActivator.deactivate(workflow, oldVersion, toRemove);
}
await this.workflowPublishedDataService.invalidateCache(record.workflowId);
await this.workflowPublishedVersionRepository.removePublishedVersion(record.workflowId);
return { type: 'unpublished' };
}
classifyActivationOutcome(outcome, desiredTriggerNodes, triggerKinds) {
const triggerStatuses = this.buildTriggerStatuses(desiredTriggerNodes, triggerKinds, outcome);
if (outcome.failures.length === 0)
return { type: 'completed', triggerStatuses };
const hasRunningTrigger = triggerStatuses.some((s) => s.status === 'activated');
if (!hasRunningTrigger) {
return { type: 'failed', error: this.toActivationError(outcome.failures), triggerStatuses };
}
return { type: 'partial', triggerStatuses };
}
buildTriggerStatuses(desiredTriggerNodes, triggerKinds, outcome) {
const failureByNodeId = new Map(outcome.failures.map((f) => [f.nodeId, f]));
return desiredTriggerNodes.map((node) => {
const triggerKind = triggerKinds.get(node.id) ?? 'persisted';
const failure = failureByNodeId.get(node.id);
return failure
? {
nodeId: node.id,
nodeName: node.name,
status: 'failed',
triggerKind,
errorMessage: failure.error.message,
}
: { nodeId: node.id, nodeName: node.name, status: 'activated', triggerKind };
});
}
toActivationError(failures) {
if (failures.length === 1)
return failures[0].error;
const detail = failures
.map((failure) => `"${failure.nodeName}": ${failure.error.message}`)
.join('; ');
return new Error(`Triggers failed to activate: ${detail}`);
}
async resolveVersions(record) {
const [workflow, currentlyPublishedVersion, newVersion] = await Promise.all([
this.workflowRepository.findOneBy({ id: record.workflowId }),
this.workflowPublishedVersionRepository.findOne({
where: { workflowId: record.workflowId },
relations: { publishedVersion: true },
loadEagerRelations: false,
}),
this.workflowHistoryRepository.findOneBy({ versionId: record.publishedVersionId }),
]);
const oldVersion = currentlyPublishedVersion?.publishedVersion ?? null;
return { workflow, oldVersion, newVersion };
}
async advancePublishedVersion(record) {
await this.workflowPublishedDataService.invalidateCache(record.workflowId);
await this.workflowPublishedVersionRepository.setPublishedVersion(record.workflowId, record.publishedVersionId);
await this.workflowPublishedDataService.refreshCache(record.workflowId);
}
};
exports.WorkflowPublicationApplier = WorkflowPublicationApplier;
exports.WorkflowPublicationApplier = WorkflowPublicationApplier = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger, db_1.WorkflowRepository, db_1.WorkflowHistoryRepository, db_1.WorkflowPublishedVersionRepository, workflow_trigger_activator_1.WorkflowTriggerActivator, workflow_published_data_service_1.WorkflowPublishedDataService])
], WorkflowPublicationApplier);
//# sourceMappingURL=workflow-publication-applier.js.map