n8n
Version:
n8n Workflow Automation Tool
120 lines • 6.61 kB
JavaScript
;
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.BreakingChangeMigrationService = exports.WorkflowMigrationNodeError = void 0;
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const bad_request_error_1 = require("../../errors/response-errors/bad-request.error");
const not_found_error_1 = require("../../errors/response-errors/not-found.error");
const node_types_1 = require("../../node-types");
const workflow_finder_service_1 = require("../../workflows/workflow-finder.service");
const workflow_validation_service_1 = require("../../workflows/workflow-validation.service");
const workflow_service_1 = require("../../workflows/workflow.service");
const breaking_changes_migration_registry_service_1 = require("./breaking-changes.migration-registry.service");
const breaking_changes_rule_registry_service_1 = require("./breaking-changes.rule-registry.service");
const group_nodes_by_type_1 = require("./group-nodes-by-type");
class WorkflowMigrationNodeError extends bad_request_error_1.BadRequestError {
constructor(message, meta) {
super(message);
this.meta = meta;
this.name = 'WorkflowMigrationNodeError';
}
}
exports.WorkflowMigrationNodeError = WorkflowMigrationNodeError;
let BreakingChangeMigrationService = class BreakingChangeMigrationService {
constructor(ruleRegistry, migrationRegistry, workflowFinderService, workflowService, workflowValidationService, nodeTypes, logger) {
this.ruleRegistry = ruleRegistry;
this.migrationRegistry = migrationRegistry;
this.workflowFinderService = workflowFinderService;
this.workflowService = workflowService;
this.workflowValidationService = workflowValidationService;
this.nodeTypes = nodeTypes;
this.logger = logger;
this.logger = logger.scoped('breaking-changes');
}
async migrateWorkflow(ruleId, workflowId, user) {
const migration = this.migrationRegistry.get(ruleId);
if (!migration) {
throw new bad_request_error_1.BadRequestError(`No automated migration is available for rule '${ruleId}'.`);
}
const rule = this.ruleRegistry.getRule(ruleId);
if (!rule || !('detectWorkflow' in rule)) {
throw new bad_request_error_1.BadRequestError(`Rule '${ruleId}' does not support automated migration.`);
}
const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
'workflow:update',
]);
if (!workflow) {
throw new not_found_error_1.NotFoundError('You do not have permission to update this workflow. Ask the owner to share it with you.');
}
const wasPublishedVersion = !!workflow.activeVersionId && workflow.activeVersionId === workflow.versionId;
const detection = await rule.detectWorkflow(workflow, (0, group_nodes_by_type_1.groupNodesByType)(workflow.nodes));
const affectedNodeIds = new Set(detection.issues.map((issue) => issue.nodeId).filter((id) => Boolean(id)));
if (affectedNodeIds.size === 0) {
throw new bad_request_error_1.BadRequestError('This workflow has no nodes affected by the selected rule.');
}
const unmapped = [];
const notes = [];
const migratedNodeIds = [];
const nodes = workflow.nodes.map((node) => {
if (!affectedNodeIds.has(node.id))
return node;
let result;
try {
result = migration.migrate(node);
}
catch (error) {
throw new WorkflowMigrationNodeError(error instanceof Error ? error.message : 'Migration failed.', { nodeId: node.id, nodeName: node.name });
}
migratedNodeIds.push(node.id);
if (result.unmapped?.length)
unmapped.push(...result.unmapped);
if (result.notes?.length)
notes.push(...result.notes);
return {
...node,
type: result.node.type,
typeVersion: result.node.typeVersion,
parameters: result.node.parameters,
};
});
const republishable = unmapped.length === 0 &&
notes.length === 0 &&
wasPublishedVersion &&
this.workflowValidationService.validateForActivation(Object.fromEntries(nodes.map((node) => [node.name, node])), workflow.connections, this.nodeTypes).isValid;
const expectedChecksum = await (0, n8n_workflow_1.calculateWorkflowChecksum)(workflow);
workflow.nodes = nodes;
const updated = await this.workflowService.update(user, workflow, workflowId, {
versionName: 'Automated node migration',
expectedChecksum,
});
this.logger.info('Applied automated node migration', {
ruleId,
workflowId,
migratedNodeIds,
});
return {
workflowId,
newVersionId: updated.versionId,
migratedNodeIds,
unmapped,
notes,
republishable,
};
}
};
exports.BreakingChangeMigrationService = BreakingChangeMigrationService;
exports.BreakingChangeMigrationService = BreakingChangeMigrationService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [breaking_changes_rule_registry_service_1.RuleRegistry, breaking_changes_migration_registry_service_1.MigrationRegistry, workflow_finder_service_1.WorkflowFinderService, workflow_service_1.WorkflowService, workflow_validation_service_1.WorkflowValidationService, node_types_1.NodeTypes, backend_common_1.Logger])
], BreakingChangeMigrationService);
//# sourceMappingURL=breaking-changes.migration.service.js.map