n8n-nodes-mautic-advanced 0.3.7 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,11 +30,19 @@ async function mauticApiRequest(method, endpoint, body = {}, query, uri) {
30
30
  }
31
31
  if (returnData.errors) {
32
32
  // They seem to sometimes return 200 status but still error.
33
- throw new n8n_workflow_1.NodeApiError(this.getNode(), returnData);
33
+ // Preserve the full error object including details for better error handling
34
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), returnData, {
35
+ httpCode: '400',
36
+ description: returnData,
37
+ });
34
38
  }
35
39
  return returnData;
36
40
  }
37
41
  catch (error) {
42
+ // Preserve error details when available for better error handling
43
+ if (error instanceof n8n_workflow_1.NodeApiError) {
44
+ throw error;
45
+ }
38
46
  throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
39
47
  }
40
48
  }
@@ -99,7 +99,26 @@ async function createContact(context, itemIndex) {
99
99
  body = (0, DataHelpers_1.validateJsonParameter)(context, 'bodyJson', itemIndex);
100
100
  }
101
101
  addContactFields(body, additionalFields);
102
- const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'POST', '/contacts/new', body);
102
+ // Data sanitization: Remove empty string values and validate email format
103
+ const sanitizedBody = {};
104
+ Object.entries(body).forEach(([key, value]) => {
105
+ // Skip empty strings as Mautic sometimes rejects them
106
+ if (value !== '' && value !== null && value !== undefined) {
107
+ sanitizedBody[key] = value;
108
+ }
109
+ });
110
+ // Basic email validation if email is provided
111
+ if (sanitizedBody.email && typeof sanitizedBody.email === 'string') {
112
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
113
+ if (!emailRegex.test(sanitizedBody.email)) {
114
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid email format: ${sanitizedBody.email}`, { itemIndex });
115
+ }
116
+ }
117
+ // Log the sanitized body for debugging (only in development)
118
+ if (process.env.NODE_ENV === 'development') {
119
+ console.log('Mautic Contact Creation - Sanitized Body:', JSON.stringify(sanitizedBody, null, 2));
120
+ }
121
+ const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'POST', '/contacts/new', sanitizedBody);
103
122
  const contactData = [response.contact];
104
123
  return (0, DataHelpers_1.processContactFields)(contactData, options);
105
124
  }
@@ -39,7 +39,67 @@ function handleApiError(context, error, operation, resource) {
39
39
  throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Permission denied for ${operation} ${resource}. Please check your API credentials.`, { itemIndex: 0 });
40
40
  }
41
41
  if (error.httpCode === '400') {
42
- throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid data provided for ${operation} ${resource}. Please check your input parameters.`, { itemIndex: 0 });
42
+ // Extract detailed validation errors from Mautic API response
43
+ let errorMessage = `Invalid data provided for ${operation} ${resource}.`;
44
+ try {
45
+ const errorData = error.description;
46
+ // Check if we have a direct error message from Mautic API
47
+ if (typeof errorData === 'string' && errorData.trim()) {
48
+ // Parse field-specific error messages like "field_name: error message"
49
+ const fieldErrorMatch = errorData.match(/^([^:]+):\s*(.+)$/);
50
+ if (fieldErrorMatch) {
51
+ const fieldName = fieldErrorMatch[1].trim();
52
+ const errorMsg = fieldErrorMatch[2].trim();
53
+ errorMessage += ` Validation error:\n- ${fieldName}: ${errorMsg}\n`;
54
+ }
55
+ else {
56
+ // If it doesn't match the field:message pattern, show the raw message
57
+ errorMessage += ` Error: ${errorData}\n`;
58
+ }
59
+ }
60
+ // Check for errors array (current Mautic API format)
61
+ else if (errorData?.errors && Array.isArray(errorData.errors) && errorData.errors.length > 0) {
62
+ const validationErrors = [];
63
+ errorData.errors.forEach((err) => {
64
+ if (err.details && typeof err.details === 'object') {
65
+ // Extract field-specific validation messages
66
+ Object.entries(err.details).forEach(([field, messages]) => {
67
+ if (Array.isArray(messages)) {
68
+ messages.forEach((msg) => {
69
+ validationErrors.push(`- ${field}: ${msg}`);
70
+ });
71
+ }
72
+ });
73
+ }
74
+ else if (err.message) {
75
+ // Fallback to error message if no details
76
+ validationErrors.push(`- ${err.message}`);
77
+ }
78
+ });
79
+ if (validationErrors.length > 0) {
80
+ errorMessage += ` Validation errors:\n${validationErrors.join('\n')}\n`;
81
+ }
82
+ }
83
+ // Check for deprecated error format (Mautic < 2.6.0)
84
+ else if (errorData?.error?.details && typeof errorData.error.details === 'object') {
85
+ const validationErrors = [];
86
+ Object.entries(errorData.error.details).forEach(([field, messages]) => {
87
+ if (Array.isArray(messages)) {
88
+ messages.forEach((msg) => {
89
+ validationErrors.push(`- ${field}: ${msg}`);
90
+ });
91
+ }
92
+ });
93
+ if (validationErrors.length > 0) {
94
+ errorMessage += ` Validation errors:\n${validationErrors.join('\n')}\n`;
95
+ }
96
+ }
97
+ }
98
+ catch (parseError) {
99
+ // If we can't parse the error details, just use the generic message
100
+ }
101
+ errorMessage += ' Please check your input parameters.';
102
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), errorMessage, { itemIndex: 0 });
43
103
  }
44
104
  throw error;
45
105
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-mautic-advanced",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Enhanced n8n node for Mautic with comprehensive API coverage including tags, campaigns, categories, and advanced contact management",
5
5
  "keywords": [
6
6
  "n8n",