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).

304 lines (303 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReadOnlyResourceHandler = exports.CrudResourceHandler = exports.BaseResourceHandler = void 0; const n8n_workflow_1 = require("n8n-workflow"); const ResourceValidator_1 = require("../validation/ResourceValidator"); const InputSanitizer_1 = require("../security/InputSanitizer"); class BaseResourceHandler { constructor(executeFunctions, config) { this.executeFunctions = executeFunctions; this.config = config; } async execute(operation, itemIndex) { try { this.validateOperation(operation); await this.validateInputData(operation, itemIndex); const requestOptions = await this.buildRequestOptions(operation, itemIndex); const response = await this.makeRequest(requestOptions); return this.formatResponse(response, itemIndex); } catch (error) { throw this.handleError(error, operation); } } validateOperation(operation) { const supportedOps = this.config.supportedOperations; switch (operation) { case "create": if (!supportedOps.create) { throw new Error(`Create operation not supported for ${this.config.resourceName}`); } break; case "get": if (!supportedOps.read) { throw new Error(`Read operation not supported for ${this.config.resourceName}`); } break; case "getMany": case "list": if (!supportedOps.list) { throw new Error(`List operation not supported for ${this.config.resourceName}`); } break; case "update": if (!supportedOps.update) { throw new Error(`Update operation not supported for ${this.config.resourceName}`); } break; case "delete": if (!supportedOps.delete) { throw new Error(`Delete operation not supported for ${this.config.resourceName}`); } break; default: break; } } getSanitizedNodeParameter(parameterName, itemIndex, defaultValue) { const rawValue = this.executeFunctions.getNodeParameter(parameterName, itemIndex, defaultValue); if (rawValue === null || rawValue === undefined || rawValue === defaultValue) { return rawValue; } const sanitizationResult = InputSanitizer_1.defaultSanitizer.sanitizeForSevDesk(rawValue, this.config.resourceName.toLowerCase()); if (sanitizationResult.warnings.length > 0) { console.warn(`Parameter '${parameterName}' sanitization warnings:`, sanitizationResult.warnings); } return sanitizationResult.data; } async validateInputData(operation, itemIndex) { let inputData = {}; switch (operation) { case "create": case "update": inputData = this.getSanitizedNodeParameter("additionalFields", itemIndex, {}); break; case "get": case "delete": const idParamName = this.config.idParameterName || `${this.config.resourceName.toLowerCase()}Id`; const resourceId = this.getSanitizedNodeParameter(idParamName, itemIndex); inputData = { [idParamName]: resourceId }; break; case "getMany": case "list": inputData = this.getSanitizedNodeParameter("additionalFields", itemIndex, {}); break; default: try { inputData = this.getSanitizedNodeParameter("additionalFields", itemIndex, {}); const operationParams = this.getOperationSpecificParameters(operation, itemIndex); inputData = { ...inputData, ...operationParams }; } catch (error) { return; } break; } const sanitizationResult = InputSanitizer_1.defaultSanitizer.sanitizeForSevDesk(inputData, this.config.resourceName.toLowerCase()); if (sanitizationResult.warnings.length > 0) { console.warn(`Input sanitization warnings for ${this.config.resourceName} ${operation}:`, sanitizationResult.warnings); } const sanitizedInputData = sanitizationResult.data; const validationResult = ResourceValidator_1.ResourceValidator.validate(sanitizedInputData, this.config.resourceName.toLowerCase(), operation); if (!validationResult.isValid) { const errorMessages = validationResult.errors .map((error) => error.message) .join("; "); throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), { message: `Validation failed for ${this.config.resourceName} ${operation}: ${errorMessages}`, description: "Please check your input parameters and try again.", }); } } getOperationSpecificParameters(operation, itemIndex) { const params = {}; try { if (operation.includes("Id") || operation.includes("get") || operation.includes("update") || operation.includes("delete")) { const idParamName = this.config.idParameterName || `${this.config.resourceName.toLowerCase()}Id`; const resourceId = this.executeFunctions.getNodeParameter(idParamName, itemIndex, null); if (resourceId) { params[idParamName] = resourceId; } } if (operation.includes("Email")) { const sendType = this.executeFunctions.getNodeParameter("sendType", itemIndex, null); if (sendType) { params.sendType = sendType; } } if (operation.includes("book") || operation.includes("Book")) { const amount = this.executeFunctions.getNodeParameter("amount", itemIndex, null); const date = this.executeFunctions.getNodeParameter("date", itemIndex, null); if (amount) params.amount = amount; if (date) params.date = date; } } catch (error) { } return params; } async buildRequestOptions(operation, itemIndex) { const credentials = await this.executeFunctions.getCredentials("sevDeskApi"); const baseURL = `https://my.sevdesk.de/api/${credentials.apiVersion}`; const baseOptions = { headers: { Authorization: credentials.apiKey, Accept: "application/json", "Content-Type": "application/json", }, url: "", method: "GET", }; switch (operation) { case "create": return this.buildCreateRequest(baseOptions, baseURL, itemIndex); case "get": return this.buildGetRequest(baseOptions, baseURL, itemIndex); case "getMany": case "list": return this.buildListRequest(baseOptions, baseURL, itemIndex); case "update": return this.buildUpdateRequest(baseOptions, baseURL, itemIndex); case "delete": return this.buildDeleteRequest(baseOptions, baseURL, itemIndex); default: return this.buildCustomRequest(baseOptions, baseURL, operation, itemIndex); } } buildCreateRequest(baseOptions, baseURL, itemIndex) { const createData = this.getSanitizedNodeParameter("additionalFields", itemIndex, {}); return { ...baseOptions, method: "POST", url: `${baseURL}/${this.config.apiEndpoint}`, body: this.transformCreateData(createData), }; } buildGetRequest(baseOptions, baseURL, itemIndex) { const idParamName = this.config.idParameterName || `${this.config.resourceName.toLowerCase()}Id`; const resourceId = this.getSanitizedNodeParameter(idParamName, itemIndex); return { ...baseOptions, method: "GET", url: `${baseURL}/${this.config.apiEndpoint}/${resourceId}`, }; } buildListRequest(baseOptions, baseURL, itemIndex) { const queryParams = this.getSanitizedNodeParameter("additionalFields", itemIndex, {}); return { ...baseOptions, method: "GET", url: `${baseURL}/${this.config.apiEndpoint}`, qs: this.transformQueryParams(queryParams), }; } buildUpdateRequest(baseOptions, baseURL, itemIndex) { const idParamName = this.config.idParameterName || `${this.config.resourceName.toLowerCase()}Id`; const resourceId = this.getSanitizedNodeParameter(idParamName, itemIndex); const updateData = this.getSanitizedNodeParameter("updateFields", itemIndex, {}); return { ...baseOptions, method: "PUT", url: `${baseURL}/${this.config.apiEndpoint}/${resourceId}`, body: this.transformUpdateData(updateData), }; } buildDeleteRequest(baseOptions, baseURL, itemIndex) { const idParamName = this.config.idParameterName || `${this.config.resourceName.toLowerCase()}Id`; const resourceId = this.getSanitizedNodeParameter(idParamName, itemIndex); return { ...baseOptions, method: "DELETE", url: `${baseURL}/${this.config.apiEndpoint}/${resourceId}`, }; } buildCustomRequest(baseOptions, baseURL, operation, itemIndex) { throw new Error(`Custom operation '${operation}' not implemented for ${this.config.resourceName}`); } async makeRequest(requestOptions) { return (await this.executeFunctions.helpers.httpRequest(requestOptions)); } formatResponse(response, itemIndex) { let responseData; if (response.objects) { if (Array.isArray(response.objects) && response.objects.length === 1) { responseData = response.objects[0]; } else { responseData = response.objects; } } else { responseData = response; } return { json: responseData, pairedItem: { item: itemIndex, }, }; } handleError(error, operation) { const errorMessage = error.message || "Unknown error occurred"; return new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), { message: `${this.config.resourceName} operation failed: ${errorMessage}`, description: `Failed to execute ${operation} operation on ${this.config.resourceName} resource.`, }); } transformCreateData(data) { return data; } transformUpdateData(data) { return data; } transformQueryParams(params) { return params; } } exports.BaseResourceHandler = BaseResourceHandler; class CrudResourceHandler extends BaseResourceHandler { constructor(executeFunctions, resourceName, apiEndpoint) { const config = { resourceName, apiEndpoint: apiEndpoint || resourceName, supportedOperations: { create: true, read: true, update: true, delete: true, list: true, }, }; super(executeFunctions, config); } } exports.CrudResourceHandler = CrudResourceHandler; class ReadOnlyResourceHandler extends BaseResourceHandler { constructor(executeFunctions, resourceName, apiEndpoint) { const config = { resourceName, apiEndpoint: apiEndpoint || resourceName, supportedOperations: { create: false, read: true, update: false, delete: false, list: true, }, }; super(executeFunctions, config); } } exports.ReadOnlyResourceHandler = ReadOnlyResourceHandler;