UNPKG

n8n

Version:

n8n Workflow Automation Tool

166 lines • 9.57 kB
"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.AutoIncludedWorkflowResolver = void 0; const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const folder_finder_service_1 = require("../../../../services/folder-finder.service"); const project_service_ee_1 = require("../../../../services/project.service.ee"); const workflow_finder_service_1 = require("../../../../workflows/workflow-finder.service"); const package_export_errors_1 = require("../package-export.errors"); let AutoIncludedWorkflowResolver = class AutoIncludedWorkflowResolver { constructor(workflowFinder, folderFinder, projectService, sharedWorkflowRepository) { this.workflowFinder = workflowFinder; this.folderFinder = folderFinder; this.projectService = projectService; this.sharedWorkflowRepository = sharedWorkflowRepository; } async resolve(options) { const originsByWorkflowId = this.seedExportedOrigins({ topLevelWorkflowIds: options.topLevelWorkflowIds, folderWorkflowIds: options.folderWorkflowIds, projectWorkflowIds: options.projectWorkflowIds, }); const exportedWorkflowIds = new Set(originsByWorkflowId.keys()); this.propagateOrigins(originsByWorkflowId, options.requirements); const autoIncludedWorkflowIds = [...originsByWorkflowId.keys()].filter((workflowId) => !exportedWorkflowIds.has(workflowId)); const autoIncludedWorkflows = await this.buildAutoIncludedWorkflows({ user: options.user, workflowIds: autoIncludedWorkflowIds, originsByWorkflowId, includeTags: options.includeTags, }); return { autoIncludedWorkflows }; } seedExportedOrigins(options) { const originsByWorkflowId = new Map(); const add = (workflowIds, origin) => { for (const workflowId of workflowIds) { const origins = originsByWorkflowId.get(workflowId) ?? new Set(); origins.add(origin); originsByWorkflowId.set(workflowId, origins); } }; add(options.topLevelWorkflowIds, 'top-level'); add(options.folderWorkflowIds, 'folder'); add(options.projectWorkflowIds, 'project'); return originsByWorkflowId; } propagateOrigins(originsByWorkflowId, requirements) { const requirementsByWorkflowId = new Map(); for (const requirement of requirements) { const current = requirementsByWorkflowId.get(requirement.workflowId) ?? []; current.push(requirement); requirementsByWorkflowId.set(requirement.workflowId, current); } const pending = new Set(originsByWorkflowId.keys()); while (pending.size > 0) { const workflowId = pending.values().next().value; pending.delete(workflowId); const origins = originsByWorkflowId.get(workflowId); if (!origins) continue; for (const { referencedWorkflowId } of requirementsByWorkflowId.get(workflowId) ?? []) { const childOrigins = originsByWorkflowId.get(referencedWorkflowId) ?? new Set(); let changed = false; for (const origin of origins) { if (!childOrigins.has(origin)) { childOrigins.add(origin); changed = true; } } originsByWorkflowId.set(referencedWorkflowId, childOrigins); if (changed) { pending.add(referencedWorkflowId); } } } } async buildAutoIncludedWorkflows(options) { if (options.workflowIds.length === 0) return []; const workflows = await this.findExportableWorkflows(options.user, options.workflowIds, options.includeTags); const workflowsById = new Map(workflows.map((workflow) => [workflow.id, workflow])); const ownersByWorkflowId = await this.sharedWorkflowRepository.findOwnerProjectsByWorkflowIds(options.workflowIds); const placementsByWorkflowId = new Map(options.workflowIds.map((workflowId) => [ workflowId, this.choosePlacement(options.originsByWorkflowId.get(workflowId)), ])); const folderIds = options.workflowIds .map((workflowId) => workflowsById.get(workflowId)) .filter((workflow) => workflow !== undefined) .filter((workflow) => placementsByWorkflowId.get(workflow.id) !== 'top-level') .map((workflow) => workflow.parentFolder?.id) .filter((folderId) => folderId !== undefined); const folderChainsByFolderId = await this.findAccessibleFolderChains(options.user, folderIds); const projectIds = [ ...new Set(options.workflowIds .filter((workflowId) => placementsByWorkflowId.get(workflowId) === 'project') .map((workflowId) => ownersByWorkflowId.get(workflowId)?.id) .filter((projectId) => projectId !== undefined)), ]; const accessibleProjectsById = await this.findAccessibleProjects(options.user, projectIds); return options.workflowIds.map((workflowId) => { const workflow = workflowsById.get(workflowId); const ownerProject = ownersByWorkflowId.get(workflowId); if (!workflow || !ownerProject) { throw new package_export_errors_1.PackageExportBlockedError('Static sub-workflow dependency could not be resolved. Export aborted.'); } let placement = placementsByWorkflowId.get(workflowId) ?? 'top-level'; if (placement === 'folder' && !workflow.parentFolder) { placement = 'top-level'; } if (placement === 'project' && !accessibleProjectsById.has(ownerProject.id)) { throw new package_export_errors_1.PackageExportBlockedError('Static sub-workflow dependency project metadata is not accessible. Export aborted.'); } const folderChain = workflow.parentFolder ? (folderChainsByFolderId.get(workflow.parentFolder.id) ?? []) : []; if (placement !== 'top-level' && workflow.parentFolder && folderChain.length === 0) { throw new package_export_errors_1.PackageExportBlockedError('Static sub-workflow dependency folder metadata is not accessible. Export aborted.'); } return { workflow, placement, ownerProject, folderChain }; }); } choosePlacement(origins) { if (origins?.has('project')) return 'project'; if (origins?.has('folder')) return 'folder'; return 'top-level'; } async findExportableWorkflows(user, workflowIds, includeTags) { const workflows = await this.workflowFinder.findWorkflowsByIdsForUser(workflowIds, user, ['workflow:export'], { includeParentFolder: true, includeTags }); await (0, package_export_errors_1.assertEveryRequestedEntityAccessible)('workflow', workflowIds, workflows, async (ids) => await this.workflowFinder.findExistingWorkflowIds(ids)); return workflows; } async findAccessibleFolderChains(user, folderIds) { const uniqueFolderIds = [...new Set(folderIds)]; const folderChainsByFolderId = await this.folderFinder.findFolderAncestorChainsForUser(uniqueFolderIds, user, ['folder:read']); await (0, package_export_errors_1.assertEveryRequestedEntityAccessible)('folder', uniqueFolderIds, [...folderChainsByFolderId.values()] .map((chain) => chain.at(-1)) .filter((folder) => folder !== undefined), async (ids) => await this.folderFinder.findExistingFolderIds(ids)); return folderChainsByFolderId; } async findAccessibleProjects(user, projectIds) { const projects = await this.projectService.findProjectsByIdsForUser(user, projectIds, [ 'project:export', ]); await (0, package_export_errors_1.assertEveryRequestedEntityAccessible)('project', projectIds, projects, async (ids) => await this.projectService.findExistingProjectIds(ids)); return new Map(projects.map((project) => [project.id, project])); } }; exports.AutoIncludedWorkflowResolver = AutoIncludedWorkflowResolver; exports.AutoIncludedWorkflowResolver = AutoIncludedWorkflowResolver = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [workflow_finder_service_1.WorkflowFinderService, folder_finder_service_1.FolderFinderService, project_service_ee_1.ProjectService, db_1.SharedWorkflowRepository]) ], AutoIncludedWorkflowResolver); //# sourceMappingURL=auto-included-workflow-resolver.js.map