UNPKG

n8n-nodes-sevdesk-v2

Version:

n8n community node for SevDesk API v2 integration with 24 production-ready workflow templates. Direct API access without external dependencies - simplified, secure, and optimized for German accounting automation (n8n 1.101.1 compatible).

294 lines (293 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResourceDependencyResolver = void 0; class ResourceDependencyResolver { static initialize() { if (this.initialized) return; this.registerDependencies(); this.initialized = true; } static registerDependencies() { this.registerDependency({ resource: 'invoice', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'contact', required: true } ], provides: [ { field: 'id', type: 'id' } ] }); this.registerDependency({ resource: 'order', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'contact', required: true } ], provides: [ { field: 'id', type: 'id' } ] }); this.registerDependency({ resource: 'voucher', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'supplier', required: false } ], provides: [ { field: 'id', type: 'id' } ] }); this.registerDependency({ resource: 'creditNote', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'contact', required: true }, { resource: 'invoice', operation: 'create', field: 'invoice', required: false } ], provides: [ { field: 'id', type: 'id' } ] }); this.registerDependency({ resource: 'contactAddress', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'contact', required: true } ] }); this.registerDependency({ resource: 'communicationWay', operation: 'create', dependsOn: [ { resource: 'contact', operation: 'create', field: 'contact', required: true } ] }); } static registerDependency(dependency) { const key = `${dependency.resource}:${dependency.operation}`; this.dependencies.set(key, dependency); } static getDependency(resource, operation) { const key = `${resource}:${operation}`; return this.dependencies.get(key) || null; } static resolveDependencies(operations) { this.initialize(); const result = { executionOrder: [], dependencyMap: new Map(), isValid: true, errors: [] }; const dependencyGraph = this.buildDependencyGraph(operations); const circularDeps = this.detectCircularDependencies(dependencyGraph); if (circularDeps.length > 0) { result.isValid = false; result.errors.push(`Circular dependencies detected: ${circularDeps.join(', ')}`); return result; } const sortedOperations = this.topologicalSort(operations, dependencyGraph); if (sortedOperations.length !== operations.length) { result.isValid = false; result.errors.push('Unable to resolve all dependencies'); return result; } result.executionOrder = sortedOperations.map((op, index) => ({ resource: op.resource, operation: op.operation, index: op.index, dependencies: dependencyGraph.get(`${op.resource}:${op.operation}:${op.index}`) || [] })); result.dependencyMap = dependencyGraph; return result; } static buildDependencyGraph(operations) { const graph = new Map(); operations.forEach((operation, index) => { const opKey = `${operation.resource}:${operation.operation}:${index}`; const dependencies = []; const dependency = this.getDependency(operation.resource, operation.operation); if (dependency) { dependency.dependsOn.forEach(dep => { const satisfyingOps = operations.filter((op, opIndex) => op.resource === dep.resource && op.operation === dep.operation && opIndex !== index); satisfyingOps.forEach(satisfyingOp => { const depKey = `${satisfyingOp.resource}:${satisfyingOp.operation}:${satisfyingOp.index}`; dependencies.push(depKey); }); if (dep.required && satisfyingOps.length === 0) { const existingResource = this.findExistingResource(operation, dep); if (!existingResource) { dependencies.push(`MISSING:${dep.resource}:${dep.operation}`); } } }); } graph.set(opKey, dependencies); }); return graph; } static findExistingResource(operation, dependency) { if (dependency.field && operation.data[dependency.field]) { return true; } return false; } static detectCircularDependencies(graph) { const visited = new Set(); const recursionStack = new Set(); const circularDeps = []; const dfs = (node) => { if (recursionStack.has(node)) { circularDeps.push(node); return true; } if (visited.has(node)) { return false; } visited.add(node); recursionStack.add(node); const dependencies = graph.get(node) || []; for (const dep of dependencies) { if (!dep.startsWith('MISSING:') && dfs(dep)) { return true; } } recursionStack.delete(node); return false; }; for (const node of graph.keys()) { if (!visited.has(node)) { dfs(node); } } return circularDeps; } static topologicalSort(operations, graph) { const inDegree = new Map(); const result = []; const queue = []; operations.forEach((op, index) => { const key = `${op.resource}:${op.operation}:${index}`; inDegree.set(key, 0); }); graph.forEach((dependencies, node) => { dependencies.forEach(dep => { if (!dep.startsWith('MISSING:')) { inDegree.set(dep, (inDegree.get(dep) || 0) + 1); } }); }); inDegree.forEach((degree, node) => { if (degree === 0) { queue.push(node); } }); while (queue.length > 0) { const current = queue.shift(); const [resource, operation, indexStr] = current.split(':'); const index = parseInt(indexStr); const currentOp = operations.find((op, i) => op.resource === resource && op.operation === operation && i === index); if (currentOp) { result.push(currentOp); } const dependencies = graph.get(current) || []; dependencies.forEach(dep => { if (!dep.startsWith('MISSING:')) { const newDegree = (inDegree.get(dep) || 0) - 1; inDegree.set(dep, newDegree); if (newDegree === 0) { queue.push(dep); } } }); } return result; } static validateDependencies(operations) { this.initialize(); const errors = []; operations.forEach((operation, index) => { const dependency = this.getDependency(operation.resource, operation.operation); if (dependency) { dependency.dependsOn.forEach(dep => { if (dep.required) { const isSatisfied = operations.some((op, opIndex) => op.resource === dep.resource && op.operation === dep.operation && opIndex !== index) || this.findExistingResource(operation, dep); if (!isSatisfied) { errors.push(`Operation ${operation.resource}:${operation.operation} requires ${dep.resource}:${dep.operation} but it's not provided`); } } }); } }); return { isValid: errors.length === 0, errors }; } static getAllDependencies() { this.initialize(); return new Map(this.dependencies); } static clearDependencies() { this.dependencies.clear(); this.initialized = false; } } exports.ResourceDependencyResolver = ResourceDependencyResolver; ResourceDependencyResolver.dependencies = new Map(); ResourceDependencyResolver.initialized = false;