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
JavaScript
"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;