@n8n-plus/n8n-plus
Version:
n8n Workflow Automation Tool (plus edition)
194 lines • 9.56 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.ExecutionPersistence = void 0;
const config_1 = require("@n8n/config");
const constants_1 = require("@n8n/constants");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const flatted_1 = require("flatted");
const n8n_core_1 = require("n8n-core");
const db_store_1 = require("./execution-data/db-store");
const fs_store_1 = require("./execution-data/fs-store");
const missing_execution_data_error_1 = require("./execution-data/missing-execution-data.error");
const duplicate_execution_error_1 = require("../errors/duplicate-execution.error");
let ExecutionPersistence = class ExecutionPersistence {
constructor(executionRepository, binaryDataService, fsStore, dbStore, storageConfig, executionsConfig, databaseConfig) {
this.executionRepository = executionRepository;
this.binaryDataService = binaryDataService;
this.fsStore = fsStore;
this.dbStore = dbStore;
this.storageConfig = storageConfig;
this.executionsConfig = executionsConfig;
this.databaseConfig = databaseConfig;
}
async create(payload) {
const { data: rawData, workflowData, ...rest } = payload;
const { connections, nodes, name, settings, id } = workflowData;
const workflowSnapshot = { connections, nodes, name, settings, id };
const storedAt = this.storageConfig.modeTag;
const executionEntity = { ...rest, createdAt: new Date(), storedAt };
const data = (0, flatted_1.stringify)(rawData);
const workflowVersionId = workflowData.versionId ?? null;
try {
return await this.executionRepository.manager.transaction(async (tx) => {
const { identifiers } = await tx.insert(db_1.ExecutionEntity, executionEntity);
const executionId = String(identifiers[0].id);
const ref = { workflowId: id, executionId };
const bundle = { data, workflowData: workflowSnapshot, workflowVersionId };
await this.getStoreFor(storedAt).write(ref, bundle, tx);
return executionId;
});
}
catch (error) {
if (executionEntity.deduplicationKey && this.isDuplicateExecutionError(error)) {
throw new duplicate_execution_error_1.DuplicateExecutionError(executionEntity.deduplicationKey, error);
}
throw error;
}
}
async updateExistingExecution(executionId, execution, conditions) {
const hasDataField = execution.data !== undefined || execution.workflowData !== undefined;
if (!hasDataField) {
return await this.updateEntityOnly(executionId, execution, conditions);
}
const entity = await this.executionRepository.findOne({
where: this.buildEntityWhereCondition(executionId, conditions),
select: ['id', 'workflowId', 'storedAt'],
});
if (!entity)
return false;
const ref = { workflowId: entity.workflowId, executionId };
const store = this.getStoreFor(entity.storedAt);
return await this.applyDataUpdate(ref, store, execution, conditions);
}
async deleteInFlightExecution(target) {
if (this.executionsConfig.pruneData) {
const bufferMs = this.executionsConfig.pruneDataHardDeleteBuffer * constants_1.Time.hours.toMilliseconds;
const deletedAt = new Date(Date.now() - bufferMs);
await this.executionRepository.update(target.executionId, { deletedAt });
}
else {
await this.hardDelete(target);
}
}
async hardDelete(target) {
const targets = Array.isArray(target) ? target : [target];
if (targets.length === 0)
return;
const fsTargets = targets.filter((t) => t.storedAt === 'fs');
await Promise.all([
this.executionRepository.deleteByIds(targets.map((t) => t.executionId)),
this.binaryDataService.deleteMany(targets.map((t) => ({ type: 'execution', ...t }))),
fsTargets.length > 0 ? this.fsStore.delete(fsTargets) : Promise.resolve(),
]);
}
async hardDeleteBy(criteria) {
const refs = await this.executionRepository.deleteExecutionsByFilter(criteria);
const fsRefs = refs.filter((r) => r.storedAt === 'fs');
if (fsRefs.length > 0)
await this.fsStore.delete(fsRefs);
}
async updateEntityOnly(executionId, execution, conditions) {
const updatableColumns = this.pickUpdatableEntityColumns(execution);
if (Object.keys(updatableColumns).length === 0)
return true;
const whereCondition = this.buildEntityWhereCondition(executionId, conditions);
const result = await this.executionRepository.update(whereCondition, updatableColumns);
return (result.affected ?? 0) > 0;
}
async applyDataUpdate(ref, store, execution, conditions) {
const { data, workflowData } = execution;
const updatableColumns = this.pickUpdatableEntityColumns(execution);
return await this.executionRepository.manager.transaction(async (tx) => {
const whereCondition = this.buildEntityWhereCondition(ref.executionId, conditions);
if (Object.keys(updatableColumns).length > 0) {
const result = await tx.update(db_1.ExecutionEntity, whereCondition, updatableColumns);
if ((result.affected ?? 0) === 0)
return false;
}
else if (conditions) {
const matchingRows = await tx.count(db_1.ExecutionEntity, { where: whereCondition });
if (matchingRows === 0)
return false;
}
const existing = await store.read(ref, tx);
if (!existing)
throw new missing_execution_data_error_1.MissingExecutionDataError(ref);
await store.write(ref, {
data: data !== undefined ? (0, flatted_1.stringify)(data) : existing.data,
workflowData: workflowData
? this.toWorkflowSnapshot(workflowData)
: existing.workflowData,
workflowVersionId: existing.workflowVersionId,
}, tx);
return true;
});
}
pickUpdatableEntityColumns(execution) {
const { id: _id, data: _data, workflowId: _workflowId, workflowData: _workflowData, workflowVersionId: _workflowVersionId, createdAt: _createdAt, startedAt: _startedAt, customData: _customData, ...updatableColumns } = execution;
return updatableColumns;
}
buildEntityWhereCondition(executionId, conditions) {
const where = { id: executionId };
if (conditions?.requireStatus)
where.status = conditions.requireStatus;
if (conditions?.requireNotFinished)
where.finished = false;
if (conditions?.requireNotCanceled)
where.status = (0, db_1.Not)('canceled');
return where;
}
getStoreFor(location) {
switch (location) {
case 'db':
return this.dbStore;
case 'fs':
return this.fsStore;
}
const _exhaustive = location;
throw new Error(`Unknown storage location: ${String(_exhaustive)}`);
}
toWorkflowSnapshot(workflowData) {
const { id, name, nodes, connections, settings } = workflowData;
return { id, name, nodes, connections, settings };
}
isDuplicateExecutionError(error) {
if (!(error instanceof Error) || !('driverError' in error))
return false;
const { driverError } = error;
if (typeof driverError !== 'object' || driverError === null || !('code' in driverError)) {
return false;
}
const { code } = driverError;
if (typeof code !== 'string')
return false;
if (!error.message.includes('deduplicationKey'))
return false;
if (this.databaseConfig.type === 'postgresdb') {
return code === '23505';
}
return (code === 'SQLITE_CONSTRAINT_UNIQUE' ||
(code === 'SQLITE_CONSTRAINT' && error.message.includes('UNIQUE constraint failed')));
}
};
exports.ExecutionPersistence = ExecutionPersistence;
exports.ExecutionPersistence = ExecutionPersistence = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [db_1.ExecutionRepository,
n8n_core_1.BinaryDataService,
fs_store_1.FsStore,
db_store_1.DbStore,
n8n_core_1.StorageConfig,
config_1.ExecutionsConfig,
config_1.DatabaseConfig])
], ExecutionPersistence);
//# sourceMappingURL=execution-persistence.js.map