"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkflowService = void 0;
const backend_common_1 = require("@n8n/backend-common");
const config_1 = require("@n8n/config");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const permissions_1 = require("@n8n/permissions");
const typeorm_1 = require("@n8n/typeorm");
const isEqual_1 = __importDefault(require("lodash/isEqual"));
const pick_1 = __importDefault(require("lodash/pick"));
const n8n_core_1 = require("n8n-core");
const n8n_workflow_1 = require("n8n-workflow");
const uuid_1 = require("uuid");
const workflow_finder_service_1 = require("./workflow-finder.service");
const workflow_history_service_1 = require("./workflow-history/workflow-history.service");
const active_workflow_manager_1 = require("../active-workflow-manager");
const folder_not_found_error_1 = require("../errors/folder-not-found.error");
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 workflow_validation_error_1 = require("../errors/response-errors/workflow-validation.error");
const workflow_history_version_not_found_error_1 = require("../errors/workflow-history-version-not-found.error");
const event_service_1 = require("../events/event.service");
const check_access_1 = require("../permissions.ee/check-access");
const external_hooks_1 = require("../external-hooks");
const generic_helpers_1 = require("../generic-helpers");
const node_types_1 = require("../node-types");
const requests_1 = require("../requests");
const ownership_service_1 = require("../services/ownership.service");
const project_service_ee_1 = require("../services/project.service.ee");
const role_service_1 = require("../services/role.service");
const tag_service_1 = require("../services/tag.service");
const WorkflowHelpers = __importStar(require("../workflow-helpers"));
const workflow_execute_additional_data_1 = require("../workflow-execute-additional-data");
const workflow_validation_service_1 = require("./workflow-validation.service");
const webhook_service_1 = require("../webhooks/webhook.service");
const conflict_error_1 = require("../errors/response-errors/conflict.error");
let WorkflowService = class WorkflowService {
    constructor(logger, sharedWorkflowRepository, workflowRepository, workflowTagMappingRepository, binaryDataService, ownershipService, tagService, workflowHistoryService, externalHooks, activeWorkflowManager, roleService, projectService, executionRepository, eventService, globalConfig, folderRepository, workflowFinderService, workflowPublishedVersionRepository, workflowPublishHistoryRepository, workflowValidationService, nodeTypes, webhookService, licenseState, projectRepository) {
        this.logger = logger;
        this.sharedWorkflowRepository = sharedWorkflowRepository;
        this.workflowRepository = workflowRepository;
        this.workflowTagMappingRepository = workflowTagMappingRepository;
        this.binaryDataService = binaryDataService;
        this.ownershipService = ownershipService;
        this.tagService = tagService;
        this.workflowHistoryService = workflowHistoryService;
        this.externalHooks = externalHooks;
        this.activeWorkflowManager = activeWorkflowManager;
        this.roleService = roleService;
        this.projectService = projectService;
        this.executionRepository = executionRepository;
        this.eventService = eventService;
        this.globalConfig = globalConfig;
        this.folderRepository = folderRepository;
        this.workflowFinderService = workflowFinderService;
        this.workflowPublishedVersionRepository = workflowPublishedVersionRepository;
        this.workflowPublishHistoryRepository = workflowPublishHistoryRepository;
        this.workflowValidationService = workflowValidationService;
        this.nodeTypes = nodeTypes;
        this.webhookService = webhookService;
        this.licenseState = licenseState;
        this.projectRepository = projectRepository;
    }
    async getMany(user, options, includeScopes, includeFolders, onlySharedWithMe, requiredScopes = ['workflow:read']) {
        let count;
        let workflows;
        let workflowsAndFolders = [];
        let isPersonalProject = false;
        let personalProjectOwnerId = null;
        if (options?.filter?.projectId) {
            const project = await this.projectRepository.findOneBy({
                id: options.filter.projectId,
            });
            if (!project) {
                return { workflows: [], count: 0 };
            }
            isPersonalProject = project.type === 'personal';
            personalProjectOwnerId = project.creatorId;
        }
        const sharingOptions = {};
        if (isPersonalProject && personalProjectOwnerId) {
            if (personalProjectOwnerId !== user.id && !(0, permissions_1.hasGlobalScope)(user, 'workflow:read')) {
                return { workflows: [], count: 0 };
            }
            sharingOptions.isPersonalProject = true;
            sharingOptions.personalProjectOwnerId = personalProjectOwnerId;
        }
        else if (onlySharedWithMe) {
            sharingOptions.onlySharedWithMe = true;
        }
        else {
            const projectRoles = await this.roleService.rolesWithScope('project', requiredScopes);
            const workflowRoles = await this.roleService.rolesWithScope('workflow', requiredScopes);
            sharingOptions.scopes = requiredScopes;
            sharingOptions.projectRoles = projectRoles;
            sharingOptions.workflowRoles = workflowRoles;
        }
        if (includeFolders) {
            [workflowsAndFolders, count] =
                await this.workflowRepository.getWorkflowsAndFoldersWithCountWithSharingSubquery(user, sharingOptions, options);
            workflows = workflowsAndFolders.filter((wf) => wf.resource === 'workflow');
        }
        else {
            ({ workflows, count } = await this.workflowRepository.getManyAndCountWithSharingSubquery(user, sharingOptions, options));
        }
        if ((0, requests_1.hasSharing)(workflows)) {
            workflows = await this.processSharedWorkflows(workflows, options);
        }
        if (includeScopes) {
            workflows = await this.addUserScopes(workflows, user);
        }
        this.cleanupSharedField(workflows);
        if (includeFolders) {
            workflows = this.mergeProcessedWorkflows(workflowsAndFolders, workflows);
        }
        if (this.licenseState.isDynamicCredentialsLicensed()) {
            return {
                workflows: await this.addResolvableCredentialsFlag(workflows),
                count,
            };
        }
        return {
            workflows,
            count,
        };
    }
    async addResolvableCredentialsFlag(workflows) {
        const { EnterpriseWorkflowService } = await Promise.resolve().then(() => __importStar(require('./workflow.service.ee')));
        const enterpriseWorkflowService = di_1.Container.get(EnterpriseWorkflowService);
        const workflowIds = workflows.map((w) => w.id);
        const workflowIdsWithResolvable = await enterpriseWorkflowService.getWorkflowIdsWithResolvableCredentials(workflowIds);
        return workflows.map((workflow) => ({
            ...workflow,
            hasResolvableCredentials: workflowIdsWithResolvable.has(workflow.id),
        }));
    }
    async processSharedWorkflows(workflows, options) {
        const projectId = options?.filter?.projectId;
        const shouldAddProjectRelations = typeof projectId === 'string' && projectId !== '';
        if (shouldAddProjectRelations) {
            await this.addSharedRelation(workflows);
        }
        return workflows.map((workflow) => this.ownershipService.addOwnedByAndSharedWith(workflow));
    }
    async addSharedRelation(workflows) {
        const workflowIds = workflows.map((workflow) => workflow.id);
        const relations = await this.sharedWorkflowRepository.getAllRelationsForWorkflows(workflowIds);
        workflows.forEach((workflow) => {
            workflow.shared = relations.filter((relation) => relation.workflowId === workflow.id);
        });
    }
    async addUserScopes(workflows, user) {
        const projectRelations = await this.projectService.getProjectRelationsForUser(user);
        return workflows.map((workflow) => this.roleService.addScopes(workflow, user, projectRelations));
    }
    cleanupSharedField(workflows) {
        workflows.forEach((workflow) => {
            delete workflow.shared;
        });
    }
    mergeProcessedWorkflows(workflowsAndFolders, processedWorkflows) {
        const workflowMap = new Map(processedWorkflows.map((workflow) => [workflow.id, workflow]));
        return workflowsAndFolders.map((item) => item.resource === 'workflow' ? (workflowMap.get(item.id) ?? item) : item);
    }
    async update(user, workflowUpdateData, workflowId, options = {}) {
        const { expectedChecksum, tagIds, parentFolderId, forceSave = false, publicApi = false, publishIfActive = false, aiBuilderAssisted = false, autosaved = false, } = options;
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:update',
        ]);
        if (!workflow) {
            this.logger.warn('User attempted to update a workflow without permissions', {
                workflowId,
                userId: user.id,
            });
            throw new not_found_error_1.NotFoundError('You do not have permission to update this workflow. Ask the owner to share it with you.');
        }
        if (workflow.isArchived) {
            throw new bad_request_error_1.BadRequestError('Cannot update an archived workflow.');
        }
        if (!forceSave && expectedChecksum) {
            await this._detectConflicts(workflow, expectedChecksum);
        }
        const hasNodesKey = 'nodes' in workflowUpdateData;
        const hasConnectionsKey = 'connections' in workflowUpdateData;
        const nodesChanged = hasNodesKey && !(0, isEqual_1.default)(workflowUpdateData.nodes, workflow.nodes);
        const connectionsChanged = hasConnectionsKey && !(0, isEqual_1.default)(workflowUpdateData.connections, workflow.connections);
        const saveNewVersion = nodesChanged || connectionsChanged;
        if (saveNewVersion) {
            workflowUpdateData.versionId = (0, uuid_1.v4)();
            this.logger.debug(`Updating versionId for workflow ${workflowId} for user ${user.id} after saving`, {
                previousVersionId: workflow.versionId,
                newVersionId: workflowUpdateData.versionId,
            });
            workflowUpdateData.nodes = workflowUpdateData.nodes ?? workflow.nodes;
            workflowUpdateData.connections = workflowUpdateData.connections ?? workflow.connections;
        }
        else {
            workflowUpdateData.versionId = workflow.versionId;
        }
        const ownerProject = await this.ownershipService.getWorkflowProjectCached(workflowId);
        await WorkflowHelpers.replaceInvalidCredentials(workflowUpdateData, ownerProject.id);
        WorkflowHelpers.addNodeIds(workflowUpdateData);
        WorkflowHelpers.resolveNodeWebhookIds(workflowUpdateData, this.nodeTypes);
        if (workflowUpdateData.settings?.redactionPolicy !== undefined &&
            workflowUpdateData.settings.redactionPolicy !== workflow.settings?.redactionPolicy &&
            !this.licenseState.isDataRedactionLicensed()) {
            delete workflowUpdateData.settings.redactionPolicy;
        }
        if (workflowUpdateData.settings?.redactionPolicy !== undefined &&
            workflowUpdateData.settings.redactionPolicy !== workflow.settings?.redactionPolicy) {
            const canUpdate = await (0, check_access_1.userHasScopes)(user, ['workflow:updateRedactionSetting'], false, {
                workflowId,
            });
            if (!canUpdate) {
                delete workflowUpdateData.settings.redactionPolicy;
            }
        }
        if (workflowUpdateData.settings && workflow.settings) {
            workflowUpdateData.settings = {
                ...workflow.settings,
                ...workflowUpdateData.settings,
            };
        }
        if (workflowUpdateData.settings) {
            workflowUpdateData.settings = WorkflowHelpers.removeDefaultValues(workflowUpdateData.settings, this.globalConfig.executions.timeout);
        }
        const settingsChanged = workflowUpdateData.settings !== undefined &&
            !(0, isEqual_1.default)(workflow.settings, workflowUpdateData.settings);
        workflowUpdateData.updatedAt = new Date();
        if (workflowUpdateData.name) {
            await (0, generic_helpers_1.validateEntity)(workflowUpdateData);
        }
        if ('pinData' in workflowUpdateData) {
            WorkflowHelpers.validatePinDataSize({ ...workflow, ...workflowUpdateData });
        }
        await this.externalHooks.run('workflow.update', [workflowUpdateData]);
        const fieldsToUpdate = [
            'name',
            'nodes',
            'connections',
            'meta',
            'settings',
            'staticData',
            'pinData',
            'versionId',
            'description',
            'updatedAt',
        ];
        const updatePayload = (0, pick_1.default)(workflowUpdateData, fieldsToUpdate);
        if (saveNewVersion) {
            await this.workflowHistoryService.saveVersion(user, workflowUpdateData, workflowId, autosaved);
        }
        const publishCurrent = workflow.activeVersionId && publishIfActive;
        if (publishCurrent) {
            updatePayload.active = true;
            updatePayload.activeVersionId = workflowUpdateData.versionId;
        }
        if (parentFolderId) {
            const project = await this.sharedWorkflowRepository.getWorkflowOwningProject(workflow.id);
            if (parentFolderId !== n8n_workflow_1.PROJECT_ROOT) {
                try {
                    await this.folderRepository.findOneOrFailFolderInProject(parentFolderId, project?.id ?? '');
                }
                catch (e) {
                    throw new folder_not_found_error_1.FolderNotFoundError(parentFolderId);
                }
            }
            updatePayload.parentFolder = parentFolderId === n8n_workflow_1.PROJECT_ROOT ? null : { id: parentFolderId };
        }
        await this.workflowRepository.update(workflowId, updatePayload);
        const tagsDisabled = this.globalConfig.tags.disabled;
        if (tagIds && !tagsDisabled) {
            await this.workflowTagMappingRepository.overwriteTaggings(workflowId, tagIds);
        }
        const relations = tagsDisabled ? ['activeVersion'] : ['tags', 'activeVersion'];
        const updatedWorkflow = await this.workflowRepository.findOne({
            where: { id: workflowId },
            relations,
        });
        if (updatedWorkflow === null) {
            throw new bad_request_error_1.BadRequestError(`Workflow with ID "${workflowId}" could not be found to be updated.`);
        }
        if (updatedWorkflow.tags?.length && tagIds?.length) {
            updatedWorkflow.tags = this.tagService.sortByRequestOrder(updatedWorkflow.tags, {
                requestOrder: tagIds,
            });
        }
        await this.externalHooks.run('workflow.afterUpdate', [updatedWorkflow]);
        const settingsChangesDetail = this.calculateSettingsChanges(workflow.settings, updatedWorkflow.settings);
        this.eventService.emit('workflow-saved', {
            user,
            workflow: updatedWorkflow,
            publicApi,
            previousWorkflow: workflow,
            aiBuilderAssisted,
            ...(settingsChangesDetail && { settingsChanged: settingsChangesDetail }),
        });
        if (updatedWorkflow.activeVersionId && (publishCurrent || settingsChanged)) {
            await this.activateWorkflow(user, workflowId, { versionId: updatedWorkflow.activeVersionId }, publicApi);
        }
        return updatedWorkflow;
    }
    async _addToActiveWorkflowManager(user, workflowId, workflow, mode, publicApi = false) {
        let didPublish = false;
        try {
            await this.externalHooks.run('workflow.activate', [workflow]);
            await this.activeWorkflowManager.add(workflowId, mode);
            didPublish = true;
        }
        catch (error) {
            const rollbackPayload = {
                active: false,
                activeVersionId: null,
                activeVersion: null,
            };
            await this.workflowRepository.update(workflowId, rollbackPayload);
            workflow.active = rollbackPayload.active;
            workflow.activeVersionId = rollbackPayload.activeVersionId;
            workflow.activeVersion = rollbackPayload.activeVersion;
            const message = error instanceof n8n_workflow_1.NodeApiError
                ? (error.description ?? error.message)
                : error.message;
            throw new bad_request_error_1.BadRequestError(message);
        }
        finally {
            if (didPublish) {
                (0, n8n_workflow_1.assert)(workflow.activeVersionId !== null);
                if (this.globalConfig.workflows.useWorkflowPublicationService) {
                    await this.workflowPublishedVersionRepository.setPublishedVersion(workflowId, workflow.activeVersionId);
                }
                await this.workflowPublishHistoryRepository.addRecord({
                    workflowId,
                    versionId: workflow.activeVersionId,
                    event: 'activated',
                    userId: user.id,
                });
                this.eventService.emit('workflow-activated', {
                    user,
                    workflowId,
                    workflow,
                    publicApi,
                });
            }
        }
    }
    async _findConflictingWebhooks(workflowEntity, versionToActivate) {
        const workflow = new n8n_workflow_1.Workflow({
            id: workflowEntity.id,
            nodes: versionToActivate.nodes,
            connections: versionToActivate.connections,
            active: !!workflowEntity.activeVersion,
            settings: workflowEntity.settings,
            nodeTypes: this.nodeTypes,
        });
        const additionalData = await (0, workflow_execute_additional_data_1.getBase)({
            workflowId: workflow.id,
        });
        await workflow.expression.acquireIsolate();
        try {
            return await this.webhookService.findWebhookConflicts(workflow, additionalData);
        }
        finally {
            await workflow.expression.releaseIsolate();
        }
    }
    async _detectWebhookConflicts(workflowEntity, versionToActivate) {
        const conflicts = await this._findConflictingWebhooks(workflowEntity, versionToActivate);
        if (conflicts.length > 0) {
            throw new conflict_error_1.ConflictError('There is a conflict with one of the webhooks.', JSON.stringify(conflicts.map(({ trigger, conflict }) => ({
                trigger,
                conflict,
            }))));
        }
    }
    async activateWorkflow(user, workflowId, options, publicApi = false) {
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:publish',
        ]);
        if (!workflow) {
            this.logger.warn('User attempted to activate a workflow without permissions', {
                workflowId,
                userId: user.id,
            });
            throw new not_found_error_1.NotFoundError('You do not have permission to activate this workflow. Ask the owner to share it with you.');
        }
        if (workflow.isArchived) {
            throw new bad_request_error_1.BadRequestError('Cannot activate an archived workflow.');
        }
        const versionIdToActivate = options?.versionId ?? workflow.versionId;
        const previousActiveVersionId = workflow.activeVersionId;
        let versionToActivate;
        try {
            versionToActivate = await this.workflowHistoryService.getVersion(user, workflow.id, versionIdToActivate, {
                includePublishHistory: false,
            });
        }
        catch (error) {
            if (error instanceof workflow_history_version_not_found_error_1.WorkflowHistoryVersionNotFoundError) {
                throw new not_found_error_1.NotFoundError('Version not found');
            }
            throw error;
        }
        if (options?.expectedChecksum) {
            await this._detectConflicts(workflow, options.expectedChecksum);
        }
        await this._detectWebhookConflicts(workflow, versionToActivate);
        this._validateNodes(workflowId, versionToActivate.nodes, versionToActivate.connections);
        await this._validateDynamicCredentials(workflowId, versionToActivate.nodes, workflow.settings);
        await this._validateSubWorkflowReferences(workflowId, versionToActivate.nodes);
        if (previousActiveVersionId) {
            await this.activeWorkflowManager.remove(workflowId);
            this.eventService.emit('workflow-deactivated', {
                user,
                workflowId,
                workflow,
                publicApi,
                deactivatedVersionId: previousActiveVersionId,
            });
            await this.workflowPublishHistoryRepository.addRecord({
                workflowId,
                versionId: previousActiveVersionId,
                event: 'deactivated',
                userId: user.id,
            });
        }
        const activationMode = previousActiveVersionId ? 'update' : 'activate';
        await this.workflowRepository.update(workflowId, {
            activeVersionId: versionIdToActivate,
            active: true,
            updatedAt: workflow.updatedAt,
        });
        const workflowForActivation = await this.workflowRepository.findOne({
            where: { id: workflowId },
            relations: ['activeVersion'],
        });
        if (!workflowForActivation) {
            throw new not_found_error_1.NotFoundError(`Workflow with ID "${workflowId}" could not be found.`);
        }
        await this._addToActiveWorkflowManager(user, workflowId, workflowForActivation, activationMode, publicApi);
        if (options?.name !== undefined || options?.description !== undefined) {
            const updateFields = {};
            if (options.name !== undefined)
                updateFields.name = options.name;
            if (options.description !== undefined)
                updateFields.description = options.description;
            await this.workflowHistoryService.updateVersion(workflowId, versionIdToActivate, updateFields);
        }
        const updatedWorkflow = await this.workflowRepository.findOne({
            where: { id: workflowId },
            relations: {
                activeVersion: {
                    workflowPublishHistory: true,
                },
            },
        });
        if (!updatedWorkflow) {
            throw new not_found_error_1.NotFoundError(`Workflow with ID "${workflowId}" could not be found.`);
        }
        return updatedWorkflow;
    }
    async deactivateWorkflow(user, workflowId, options) {
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:unpublish',
        ]);
        if (!workflow) {
            this.logger.warn('User attempted to deactivate a workflow without permissions', {
                workflowId,
                userId: user.id,
            });
            throw new not_found_error_1.NotFoundError('You do not have permission to deactivate this workflow. Ask the owner to share it with you.');
        }
        if (workflow.activeVersionId === null) {
            return workflow;
        }
        if (options?.expectedChecksum) {
            await this._detectConflicts(workflow, options.expectedChecksum);
        }
        await this.activeWorkflowManager.remove(workflowId);
        await this.workflowRepository.update(workflowId, {
            active: false,
            activeVersionId: null,
            updatedAt: workflow.updatedAt,
        });
        const deactivatedVersionId = workflow.activeVersionId;
        if (this.globalConfig.workflows.useWorkflowPublicationService) {
            await this.workflowPublishedVersionRepository.removePublishedVersion(workflowId);
        }
        await this.workflowPublishHistoryRepository.addRecord({
            workflowId,
            versionId: deactivatedVersionId,
            event: 'deactivated',
            userId: user.id,
        });
        workflow.active = false;
        workflow.activeVersionId = null;
        workflow.activeVersion = null;
        this.eventService.emit('workflow-deactivated', {
            user,
            workflowId,
            workflow,
            publicApi: options?.publicApi ?? false,
            deactivatedVersionId,
        });
        return workflow;
    }
    async delete(user, workflowId, force = false) {
        await this.externalHooks.run('workflow.delete', [workflowId]);
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:delete',
        ]);
        if (!workflow) {
            return;
        }
        if (!workflow.isArchived && !force) {
            throw new bad_request_error_1.BadRequestError('Workflow must be archived before it can be deleted.');
        }
        if (workflow.active) {
            await this.activeWorkflowManager.remove(workflowId);
        }
        const idsForDeletion = await this.executionRepository
            .find({
            select: ['id'],
            where: { workflowId },
        })
            .then((rows) => rows.map(({ id: executionId }) => n8n_core_1.FileLocation.ofExecution(workflowId, executionId)));
        await this.workflowRepository.delete(workflowId);
        await this.binaryDataService.deleteMany(idsForDeletion);
        this.eventService.emit('workflow-deleted', { user, workflowId, publicApi: false });
        await this.externalHooks.run('workflow.afterDelete', [workflowId]);
        return workflow;
    }
    async archive(user, workflowId, options) {
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:delete',
        ]);
        if (!workflow) {
            return;
        }
        if (workflow.isArchived) {
            if (options?.skipArchived) {
                return workflow;
            }
            throw new bad_request_error_1.BadRequestError('Workflow is already archived.');
        }
        if (options?.expectedChecksum) {
            await this._detectConflicts(workflow, options.expectedChecksum);
        }
        if (workflow.activeVersionId !== null) {
            await this.activeWorkflowManager.remove(workflowId);
            if (this.globalConfig.workflows.useWorkflowPublicationService) {
                await this.workflowPublishedVersionRepository.removePublishedVersion(workflowId);
            }
            await this.workflowPublishHistoryRepository.addRecord({
                workflowId,
                versionId: workflow.activeVersionId,
                event: 'deactivated',
                userId: user.id,
            });
        }
        const versionId = (0, uuid_1.v4)();
        workflow.versionId = versionId;
        workflow.isArchived = true;
        workflow.active = false;
        workflow.activeVersionId = null;
        workflow.activeVersion = null;
        await this.workflowRepository.update(workflowId, {
            isArchived: true,
            active: false,
            activeVersion: null,
            versionId,
        });
        await this.workflowHistoryService.saveVersion(user, workflow, workflowId);
        this.eventService.emit('workflow-archived', {
            user,
            workflowId,
            publicApi: options?.publicApi ?? false,
        });
        await this.externalHooks.run('workflow.afterArchive', [workflowId]);
        return workflow;
    }
    async unarchive(user, workflowId, options) {
        const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
            'workflow:delete',
        ]);
        if (!workflow) {
            return;
        }
        if (!workflow.isArchived) {
            throw new bad_request_error_1.BadRequestError('Workflow is not archived.');
        }
        const versionId = (0, uuid_1.v4)();
        workflow.versionId = versionId;
        workflow.isArchived = false;
        await this.workflowRepository.update(workflowId, { isArchived: false, versionId });
        await this.workflowHistoryService.saveVersion(user, workflow, workflowId);
        this.eventService.emit('workflow-unarchived', {
            user,
            workflowId,
            publicApi: options?.publicApi ?? false,
        });
        await this.externalHooks.run('workflow.afterUnarchive', [workflowId]);
        return workflow;
    }
    async archiveForPublicApi(user, workflowId) {
        return await this.archive(user, workflowId, { skipArchived: true, publicApi: true });
    }
    async unarchiveForPublicApi(user, workflowId) {
        return await this.unarchive(user, workflowId, { publicApi: true });
    }
    async getWorkflowScopes(user, workflowId) {
        const userProjectRelations = await this.projectService.getProjectRelationsForUser(user);
        const shared = await this.sharedWorkflowRepository.find({
            where: {
                projectId: (0, typeorm_1.In)([...new Set(userProjectRelations.map((pr) => pr.projectId))]),
                workflowId,
            },
        });
        return this.roleService.combineResourceScopes('workflow', user, shared, userProjectRelations);
    }
    async transferAll(fromProjectId, toProjectId, trx) {
        trx = trx ?? this.workflowRepository.manager;
        const allSharedWorkflows = await trx.findBy(db_1.SharedWorkflow, {
            projectId: (0, typeorm_1.In)([fromProjectId, toProjectId]),
        });
        const sharedWorkflowsOfFromProject = allSharedWorkflows.filter((sw) => sw.projectId === fromProjectId);
        const ownedWorkflowIds = sharedWorkflowsOfFromProject
            .filter((sw) => sw.role === 'workflow:owner')
            .map((sw) => sw.workflowId);
        await this.sharedWorkflowRepository.makeOwner(ownedWorkflowIds, toProjectId, trx);
        await this.sharedWorkflowRepository.deleteByIds(ownedWorkflowIds, fromProjectId, trx);
        const sharedWorkflowIdsOfTransferee = allSharedWorkflows
            .filter((sw) => sw.projectId === toProjectId)
            .map((sw) => sw.workflowId);
        const sharedWorkflowsToTransfer = sharedWorkflowsOfFromProject.filter((sw) => sw.role !== 'workflow:owner' && !sharedWorkflowIdsOfTransferee.includes(sw.workflowId));
        await trx.insert(db_1.SharedWorkflow, sharedWorkflowsToTransfer.map((sw) => ({
            workflowId: sw.workflowId,
            projectId: toProjectId,
            role: sw.role,
        })));
    }
    async getWorkflowsWithNodesIncluded(user, nodeTypes, includeNodes = false) {
        const foundWorkflows = await this.workflowRepository.findWorkflowsWithNodeType(nodeTypes, includeNodes);
        let { workflows } = await this.workflowRepository.getManyAndCount(foundWorkflows.map((w) => w.id));
        if ((0, requests_1.hasSharing)(workflows)) {
            workflows = await this.processSharedWorkflows(workflows);
        }
        const withScopes = await this.addUserScopes(workflows, user);
        this.cleanupSharedField(withScopes);
        return withScopes.map((workflow) => {
            const nodes = includeNodes
                ? (foundWorkflows.find((w) => w.id === workflow.id)?.nodes ?? [])
                : undefined;
            return { resourceType: 'workflow', ...workflow, ...(includeNodes ? { nodes } : {}) };
        });
    }
    async _detectConflicts(dbWorkflow, expectedChecksum) {
        const currentChecksum = await (0, n8n_workflow_1.calculateWorkflowChecksum)(dbWorkflow);
        if (expectedChecksum !== currentChecksum) {
            throw new conflict_error_1.ConflictError('Your most recent changes may be lost, because someone else just updated this workflow. Open this workflow in a new tab to see those new updates.');
        }
    }
    _validateNodes(workflowId, nodes, connections) {
        const nodesToValidate = nodes.reduce((acc, node) => {
            acc[node.name] = node;
            return acc;
        }, {});
        const validation = this.workflowValidationService.validateForActivation(nodesToValidate, connections, this.nodeTypes);
        if (!validation.isValid) {
            this.logger.warn('Workflow activation failed validation', {
                workflowId,
                error: validation.error,
            });
            throw new workflow_validation_error_1.WorkflowValidationError(validation.error ?? 'Workflow validation failed');
        }
    }
    async _validateDynamicCredentials(workflowId, nodes, workflowSettings) {
        const validation = await this.workflowValidationService.validateDynamicCredentials(nodes, this.nodeTypes, workflowSettings);
        if (!validation.isValid) {
            this.logger.warn('Workflow activation failed dynamic credentials validation', {
                workflowId,
                error: validation.error,
            });
            throw new workflow_validation_error_1.WorkflowValidationError(validation.error ?? 'Dynamic credentials validation failed');
        }
    }
    calculateSettingsChanges(previousSettings, newSettings) {
        const changes = {};
        const prev = previousSettings ?? {};
        const next = newSettings ?? {};
        const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
        for (const key of allKeys) {
            const prevValue = prev[key];
            const nextValue = next[key];
            if (!(0, isEqual_1.default)(prevValue, nextValue)) {
                const from = prevValue ?? null;
                const to = nextValue ?? null;
                changes[key] = { from, to };
            }
        }
        return Object.keys(changes).length > 0 ? changes : undefined;
    }
    async _validateSubWorkflowReferences(workflowId, nodes) {
        const validation = await this.workflowValidationService.validateSubWorkflowReferences(workflowId, nodes);
        if (!validation.isValid) {
            this.logger.warn('Workflow activation failed sub-workflow validation', {
                workflowId,
                error: validation.error,
                invalidReferences: validation.invalidReferences,
            });
            throw new workflow_validation_error_1.WorkflowValidationError(validation.error ?? 'Sub-workflow validation failed');
        }
    }
};
exports.WorkflowService = WorkflowService;
exports.WorkflowService = WorkflowService = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [backend_common_1.Logger,
        db_1.SharedWorkflowRepository,
        db_1.WorkflowRepository,
        db_1.WorkflowTagMappingRepository,
        n8n_core_1.BinaryDataService,
        ownership_service_1.OwnershipService,
        tag_service_1.TagService,
        workflow_history_service_1.WorkflowHistoryService,
        external_hooks_1.ExternalHooks,
        active_workflow_manager_1.ActiveWorkflowManager,
        role_service_1.RoleService,
        project_service_ee_1.ProjectService,
        db_1.ExecutionRepository,
        event_service_1.EventService,
        config_1.GlobalConfig,
        db_1.FolderRepository,
        workflow_finder_service_1.WorkflowFinderService,
        db_1.WorkflowPublishedVersionRepository,
        db_1.WorkflowPublishHistoryRepository,
        workflow_validation_service_1.WorkflowValidationService,
        node_types_1.NodeTypes,
        webhook_service_1.WebhookService,
        backend_common_1.LicenseState,
        db_1.ProjectRepository])
], WorkflowService);
//# sourceMappingURL=workflow.service.js.map