n8n-nodes-mautic-advanced 0.3.7 → 0.3.9
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.
- package/dist/nodes/MauticAdvanced/CampaignDescription.js +27 -0
- package/dist/nodes/MauticAdvanced/ContactDescription.js +12 -0
- package/dist/nodes/MauticAdvanced/GenericFunctions.js +9 -1
- package/dist/nodes/MauticAdvanced/operations/CampaignOperations.js +10 -6
- package/dist/nodes/MauticAdvanced/operations/CompanyOperations.js +2 -2
- package/dist/nodes/MauticAdvanced/operations/ContactOperations.js +76 -14
- package/dist/nodes/MauticAdvanced/operations/FieldOperations.js +5 -3
- package/dist/nodes/MauticAdvanced/operations/MiscellaneousOperations.js +13 -6
- package/dist/nodes/MauticAdvanced/operations/NotificationOperations.js +5 -3
- package/dist/nodes/MauticAdvanced/operations/SegmentOperations.js +5 -3
- package/dist/nodes/MauticAdvanced/utils/ApiHelpers.js +63 -1
- package/dist/nodes/MauticAdvanced/utils/DataHelpers.js +31 -1
- package/package.json +1 -1
|
@@ -118,6 +118,13 @@ exports.campaignFields = [
|
|
|
118
118
|
default: '',
|
|
119
119
|
description: 'Date/time when the campaign should be published',
|
|
120
120
|
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'Alias',
|
|
123
|
+
name: 'alias',
|
|
124
|
+
type: 'string',
|
|
125
|
+
default: '',
|
|
126
|
+
description: 'Used to generate the URL for the campaign',
|
|
127
|
+
},
|
|
121
128
|
],
|
|
122
129
|
},
|
|
123
130
|
/* -------------------------------------------------------------------------- */
|
|
@@ -136,6 +143,19 @@ exports.campaignFields = [
|
|
|
136
143
|
},
|
|
137
144
|
default: '',
|
|
138
145
|
},
|
|
146
|
+
{
|
|
147
|
+
displayName: 'Create If Not Found',
|
|
148
|
+
name: 'createIfNotFound',
|
|
149
|
+
type: 'boolean',
|
|
150
|
+
displayOptions: {
|
|
151
|
+
show: {
|
|
152
|
+
resource: ['campaign'],
|
|
153
|
+
operation: ['update'],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
default: false,
|
|
157
|
+
description: 'Create a new campaign if the given ID does not exist (uses PUT instead of PATCH)',
|
|
158
|
+
},
|
|
139
159
|
{
|
|
140
160
|
displayName: 'Update Fields',
|
|
141
161
|
name: 'updateFields',
|
|
@@ -184,6 +204,13 @@ exports.campaignFields = [
|
|
|
184
204
|
default: '',
|
|
185
205
|
description: 'Date/time when the campaign should be published',
|
|
186
206
|
},
|
|
207
|
+
{
|
|
208
|
+
displayName: 'Alias',
|
|
209
|
+
name: 'alias',
|
|
210
|
+
type: 'string',
|
|
211
|
+
default: '',
|
|
212
|
+
description: 'Used to generate the URL for the campaign',
|
|
213
|
+
},
|
|
187
214
|
],
|
|
188
215
|
},
|
|
189
216
|
/* -------------------------------------------------------------------------- */
|
|
@@ -139,6 +139,18 @@ exports.contactOperations = [
|
|
|
139
139
|
description: 'Get activity events for all contacts',
|
|
140
140
|
action: 'Get activity events for all contacts',
|
|
141
141
|
},
|
|
142
|
+
{
|
|
143
|
+
name: 'Get Owners',
|
|
144
|
+
value: 'getOwners',
|
|
145
|
+
description: 'Get list of available owners',
|
|
146
|
+
action: 'Get list of available owners',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'Get Fields',
|
|
150
|
+
value: 'getFields',
|
|
151
|
+
description: 'Get list of available contact fields',
|
|
152
|
+
action: 'Get list of available contact fields',
|
|
153
|
+
},
|
|
142
154
|
],
|
|
143
155
|
default: 'create',
|
|
144
156
|
},
|
|
@@ -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
|
-
|
|
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
|
}
|
|
@@ -48,9 +48,11 @@ async function createCampaign(context, itemIndex) {
|
|
|
48
48
|
}
|
|
49
49
|
async function updateCampaign(context, itemIndex) {
|
|
50
50
|
const campaignId = (0, ApiHelpers_1.getRequiredParam)(context, 'campaignId', itemIndex);
|
|
51
|
+
const createIfNotFound = (0, ApiHelpers_1.getOptionalParam)(context, 'createIfNotFound', itemIndex, false);
|
|
51
52
|
const updateFields = (0, ApiHelpers_1.getOptionalParam)(context, 'updateFields', itemIndex, {});
|
|
52
53
|
const body = { ...updateFields };
|
|
53
|
-
const
|
|
54
|
+
const method = createIfNotFound ? 'PUT' : 'PATCH';
|
|
55
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, method, `/campaigns/${campaignId}/edit`, body);
|
|
54
56
|
return response.campaign;
|
|
55
57
|
}
|
|
56
58
|
async function cloneCampaign(context, itemIndex) {
|
|
@@ -61,7 +63,7 @@ async function cloneCampaign(context, itemIndex) {
|
|
|
61
63
|
async function getCampaign(context, itemIndex) {
|
|
62
64
|
const campaignId = (0, ApiHelpers_1.getRequiredParam)(context, 'campaignId', itemIndex);
|
|
63
65
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/campaigns/${campaignId}`);
|
|
64
|
-
return response.campaign;
|
|
66
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.campaign);
|
|
65
67
|
}
|
|
66
68
|
async function getAllCampaigns(context, itemIndex) {
|
|
67
69
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -72,12 +74,13 @@ async function getAllCampaigns(context, itemIndex) {
|
|
|
72
74
|
if (!qs.orderByDir)
|
|
73
75
|
qs.orderByDir = 'asc';
|
|
74
76
|
if (returnAll) {
|
|
75
|
-
|
|
77
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'campaigns', 'GET', '/campaigns', {}, qs);
|
|
78
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
76
79
|
}
|
|
77
80
|
else {
|
|
78
81
|
qs.limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, 30);
|
|
79
82
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/campaigns', {}, qs);
|
|
80
|
-
return response.campaigns;
|
|
83
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.campaigns);
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
async function getCampaignContacts(context, itemIndex) {
|
|
@@ -86,12 +89,13 @@ async function getCampaignContacts(context, itemIndex) {
|
|
|
86
89
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
87
90
|
const qs = (0, DataHelpers_1.buildQueryFromOptions)(options);
|
|
88
91
|
if (returnAll) {
|
|
89
|
-
|
|
92
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'contacts', 'GET', `/campaigns/${campaignId}/contacts`, {}, qs);
|
|
93
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
90
94
|
}
|
|
91
95
|
else {
|
|
92
96
|
qs.limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, 30);
|
|
93
97
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/campaigns/${campaignId}/contacts`, {}, qs);
|
|
94
|
-
return response.contacts;
|
|
98
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.contacts);
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
async function deleteCampaign(context, itemIndex) {
|
|
@@ -131,7 +131,7 @@ async function getCompany(context, itemIndex) {
|
|
|
131
131
|
if (simple) {
|
|
132
132
|
result = result.fields.all;
|
|
133
133
|
}
|
|
134
|
-
return result;
|
|
134
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
135
135
|
}
|
|
136
136
|
async function getAllCompanies(context, itemIndex) {
|
|
137
137
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -156,7 +156,7 @@ async function getAllCompanies(context, itemIndex) {
|
|
|
156
156
|
if (simple) {
|
|
157
157
|
responseData = responseData.map((item) => item.fields.all);
|
|
158
158
|
}
|
|
159
|
-
return responseData;
|
|
159
|
+
return (0, DataHelpers_1.convertNumericStrings)(responseData);
|
|
160
160
|
}
|
|
161
161
|
async function deleteCompany(context, itemIndex) {
|
|
162
162
|
const simple = (0, ApiHelpers_1.getOptionalParam)(context, 'simple', itemIndex, false);
|
|
@@ -72,6 +72,12 @@ async function executeContactOperation(context, operation, i) {
|
|
|
72
72
|
case 'getAllActivity':
|
|
73
73
|
responseData = await getAllContactActivity(context, i);
|
|
74
74
|
break;
|
|
75
|
+
case 'getOwners':
|
|
76
|
+
responseData = await getContactOwners(context);
|
|
77
|
+
break;
|
|
78
|
+
case 'getFields':
|
|
79
|
+
responseData = await getContactFields(context);
|
|
80
|
+
break;
|
|
75
81
|
default:
|
|
76
82
|
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Operation '${operation}' is not supported for Contact resource.`, { itemIndex: i });
|
|
77
83
|
}
|
|
@@ -99,7 +105,26 @@ async function createContact(context, itemIndex) {
|
|
|
99
105
|
body = (0, DataHelpers_1.validateJsonParameter)(context, 'bodyJson', itemIndex);
|
|
100
106
|
}
|
|
101
107
|
addContactFields(body, additionalFields);
|
|
102
|
-
|
|
108
|
+
// Data sanitization: Remove empty string values and validate email format
|
|
109
|
+
const sanitizedBody = {};
|
|
110
|
+
Object.entries(body).forEach(([key, value]) => {
|
|
111
|
+
// Skip empty strings as Mautic sometimes rejects them
|
|
112
|
+
if (value !== '' && value !== null && value !== undefined) {
|
|
113
|
+
sanitizedBody[key] = value;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// Basic email validation if email is provided
|
|
117
|
+
if (sanitizedBody.email && typeof sanitizedBody.email === 'string') {
|
|
118
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
119
|
+
if (!emailRegex.test(sanitizedBody.email)) {
|
|
120
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid email format: ${sanitizedBody.email}`, { itemIndex });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Log the sanitized body for debugging (only in development)
|
|
124
|
+
if (process.env.NODE_ENV === 'development') {
|
|
125
|
+
console.log('Mautic Contact Creation - Sanitized Body:', JSON.stringify(sanitizedBody, null, 2));
|
|
126
|
+
}
|
|
127
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'POST', '/contacts/new', sanitizedBody);
|
|
103
128
|
const contactData = [response.contact];
|
|
104
129
|
return (0, DataHelpers_1.processContactFields)(contactData, options);
|
|
105
130
|
}
|
|
@@ -133,7 +158,8 @@ async function getContact(context, itemIndex) {
|
|
|
133
158
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
134
159
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/contacts/${contactId}`);
|
|
135
160
|
const contactData = [response.contact];
|
|
136
|
-
|
|
161
|
+
const processedData = (0, DataHelpers_1.processContactFields)(contactData, options, options.fieldsToReturn);
|
|
162
|
+
return (0, DataHelpers_1.convertNumericStrings)(processedData);
|
|
137
163
|
}
|
|
138
164
|
async function getAllContacts(context, itemIndex) {
|
|
139
165
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -171,7 +197,8 @@ async function getAllContacts(context, itemIndex) {
|
|
|
171
197
|
responseData = response.contacts ? Object.values(response.contacts) : [];
|
|
172
198
|
}
|
|
173
199
|
}
|
|
174
|
-
|
|
200
|
+
const processedData = (0, DataHelpers_1.processContactFields)(responseData, options, options.fieldsToReturn);
|
|
201
|
+
return (0, DataHelpers_1.convertNumericStrings)(processedData);
|
|
175
202
|
}
|
|
176
203
|
async function deleteContact(context, itemIndex) {
|
|
177
204
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
@@ -201,8 +228,16 @@ async function editContactPoints(context, itemIndex) {
|
|
|
201
228
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
202
229
|
const action = (0, ApiHelpers_1.getRequiredParam)(context, 'action', itemIndex);
|
|
203
230
|
const points = (0, ApiHelpers_1.getRequiredParam)(context, 'points', itemIndex);
|
|
204
|
-
const
|
|
205
|
-
const
|
|
231
|
+
const eventName = (0, ApiHelpers_1.getOptionalParam)(context, 'eventName', itemIndex, '');
|
|
232
|
+
const actionName = (0, ApiHelpers_1.getOptionalParam)(context, 'actionName', itemIndex, '');
|
|
233
|
+
const body = {};
|
|
234
|
+
if (eventName)
|
|
235
|
+
body.eventName = eventName;
|
|
236
|
+
if (actionName)
|
|
237
|
+
body.actionName = actionName;
|
|
238
|
+
const endpoint = action === 'add'
|
|
239
|
+
? `/contacts/${contactId}/points/plus/${points}`
|
|
240
|
+
: `/contacts/${contactId}/points/minus/${points}`;
|
|
206
241
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'POST', endpoint, body);
|
|
207
242
|
return response.contact;
|
|
208
243
|
}
|
|
@@ -210,8 +245,20 @@ async function editDoNotContactList(context, itemIndex) {
|
|
|
210
245
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
211
246
|
const action = (0, ApiHelpers_1.getRequiredParam)(context, 'action', itemIndex);
|
|
212
247
|
const channel = (0, ApiHelpers_1.getRequiredParam)(context, 'channel', itemIndex);
|
|
213
|
-
const
|
|
214
|
-
const
|
|
248
|
+
const reason = (0, ApiHelpers_1.getOptionalParam)(context, 'reason', itemIndex, 3); // Default to Manual (3)
|
|
249
|
+
const channelId = (0, ApiHelpers_1.getOptionalParam)(context, 'channelId', itemIndex, '');
|
|
250
|
+
const comments = (0, ApiHelpers_1.getOptionalParam)(context, 'comments', itemIndex, '');
|
|
251
|
+
const body = {};
|
|
252
|
+
if (reason !== undefined)
|
|
253
|
+
body.reason = reason;
|
|
254
|
+
if (channelId)
|
|
255
|
+
body.channelId = channelId;
|
|
256
|
+
if (comments)
|
|
257
|
+
body.comments = comments;
|
|
258
|
+
const endpoint = action === 'add'
|
|
259
|
+
? `/contacts/${contactId}/dnc/${channel}/add`
|
|
260
|
+
: `/contacts/${contactId}/dnc/${channel}/remove`;
|
|
261
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'POST', endpoint, body);
|
|
215
262
|
return response.contact;
|
|
216
263
|
}
|
|
217
264
|
async function addUtmTags(context, itemIndex) {
|
|
@@ -251,7 +298,8 @@ async function removeUtmTags(context, itemIndex) {
|
|
|
251
298
|
}
|
|
252
299
|
async function getContactDevices(context, itemIndex) {
|
|
253
300
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
254
|
-
|
|
301
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'devices', 'GET', `/contacts/${contactId}/devices`);
|
|
302
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
255
303
|
}
|
|
256
304
|
async function getContactActivity(context, itemIndex) {
|
|
257
305
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
@@ -273,25 +321,30 @@ async function getContactActivity(context, itemIndex) {
|
|
|
273
321
|
qs.order = [options.orderBy, options.orderByDir ?? 'asc'];
|
|
274
322
|
if (options.limit)
|
|
275
323
|
qs.limit = options.limit;
|
|
276
|
-
|
|
324
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'events', 'GET', `/contacts/${contactId}/activity`, {}, qs);
|
|
325
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
277
326
|
}
|
|
278
327
|
async function getContactNotes(context, itemIndex) {
|
|
279
328
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
280
329
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
281
330
|
const qs = options;
|
|
282
|
-
|
|
331
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'notes', 'GET', `/contacts/${contactId}/notes`, {}, qs);
|
|
332
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
283
333
|
}
|
|
284
334
|
async function getContactCompanies(context, itemIndex) {
|
|
285
335
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
286
|
-
|
|
336
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'companies', 'GET', `/contacts/${contactId}/companies`);
|
|
337
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
287
338
|
}
|
|
288
339
|
async function getContactCampaigns(context, itemIndex) {
|
|
289
340
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
290
|
-
|
|
341
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'campaigns', 'GET', `/contacts/${contactId}/campaigns`);
|
|
342
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
291
343
|
}
|
|
292
344
|
async function getContactSegments(context, itemIndex) {
|
|
293
345
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
294
|
-
|
|
346
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'segments', 'GET', `/contacts/${contactId}/segments`);
|
|
347
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
295
348
|
}
|
|
296
349
|
async function addContactToSegments(context, itemIndex) {
|
|
297
350
|
const contactId = (0, ApiHelpers_1.getRequiredParam)(context, 'contactId', itemIndex);
|
|
@@ -340,7 +393,8 @@ async function getAllContactActivity(context, itemIndex) {
|
|
|
340
393
|
qs.order = [options.orderBy, options.orderByDir ?? 'asc'];
|
|
341
394
|
if (options.limit)
|
|
342
395
|
qs.limit = options.limit;
|
|
343
|
-
|
|
396
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'events', 'GET', `/contacts/activity`, {}, qs);
|
|
397
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
344
398
|
}
|
|
345
399
|
function normalizeTagsInput(tagsInput) {
|
|
346
400
|
// Handle different input formats for tags
|
|
@@ -459,3 +513,11 @@ async function getContactsWithDncFilter(context, qs, options, limit) {
|
|
|
459
513
|
}
|
|
460
514
|
return contacts;
|
|
461
515
|
}
|
|
516
|
+
async function getContactOwners(context) {
|
|
517
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/contacts/list/owners');
|
|
518
|
+
return (0, DataHelpers_1.convertNumericStrings)(response);
|
|
519
|
+
}
|
|
520
|
+
async function getContactFields(context) {
|
|
521
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/contacts/list/fields');
|
|
522
|
+
return (0, DataHelpers_1.convertNumericStrings)(response);
|
|
523
|
+
}
|
|
@@ -142,7 +142,7 @@ async function getField(context, itemIndex) {
|
|
|
142
142
|
const fieldId = (0, ApiHelpers_1.getRequiredParam)(context, 'fieldId', itemIndex);
|
|
143
143
|
const endpoint = `/fields/${fieldObject}/${fieldId}`;
|
|
144
144
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', endpoint);
|
|
145
|
-
return response.field;
|
|
145
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.field);
|
|
146
146
|
}
|
|
147
147
|
async function getAllFields(context, itemIndex) {
|
|
148
148
|
const fieldObject = (0, ApiHelpers_1.getRequiredParam)(context, 'fieldObject', itemIndex);
|
|
@@ -151,12 +151,14 @@ async function getAllFields(context, itemIndex) {
|
|
|
151
151
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
152
152
|
const query = (0, DataHelpers_1.buildQueryFromOptions)(options);
|
|
153
153
|
if (returnAll) {
|
|
154
|
-
|
|
154
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'fields', 'GET', `/fields/${fieldObject}`, {}, query);
|
|
155
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
155
156
|
}
|
|
156
157
|
else {
|
|
157
158
|
query.limit = limit;
|
|
158
159
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/fields/${fieldObject}`, {}, query);
|
|
159
|
-
|
|
160
|
+
const data = Object.values(response.fields || {});
|
|
161
|
+
return (0, DataHelpers_1.convertNumericStrings)(data);
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
164
|
async function deleteField(context, itemIndex) {
|
|
@@ -161,7 +161,7 @@ async function updateTag(context, itemIndex) {
|
|
|
161
161
|
async function getTag(context, itemIndex) {
|
|
162
162
|
const tagId = (0, ApiHelpers_1.getRequiredParam)(context, 'tagId', itemIndex);
|
|
163
163
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/tags/${tagId}`);
|
|
164
|
-
return response.tag;
|
|
164
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.tag);
|
|
165
165
|
}
|
|
166
166
|
async function getAllTags(context, itemIndex) {
|
|
167
167
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -173,12 +173,14 @@ async function getAllTags(context, itemIndex) {
|
|
|
173
173
|
qs.orderByDir = 'asc';
|
|
174
174
|
if (returnAll) {
|
|
175
175
|
const limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, undefined);
|
|
176
|
-
|
|
176
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'tags', 'GET', '/tags', {}, qs, limit);
|
|
177
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
177
178
|
}
|
|
178
179
|
else {
|
|
179
180
|
qs.limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, 30);
|
|
180
181
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/tags', {}, qs);
|
|
181
|
-
|
|
182
|
+
const data = response.tags ? Object.values(response.tags) : [];
|
|
183
|
+
return (0, DataHelpers_1.convertNumericStrings)(data);
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
async function deleteTag(context, itemIndex) {
|
|
@@ -203,6 +205,7 @@ async function createCategory(context, itemIndex) {
|
|
|
203
205
|
}
|
|
204
206
|
async function updateCategory(context, itemIndex) {
|
|
205
207
|
const categoryId = (0, ApiHelpers_1.getRequiredParam)(context, 'categoryId', itemIndex);
|
|
208
|
+
const createIfNotFound = (0, ApiHelpers_1.getOptionalParam)(context, 'createIfNotFound', itemIndex, false);
|
|
206
209
|
const updateFields = (0, ApiHelpers_1.getOptionalParam)(context, 'updateFields', itemIndex, {});
|
|
207
210
|
const body = {};
|
|
208
211
|
if (updateFields.title)
|
|
@@ -211,13 +214,16 @@ async function updateCategory(context, itemIndex) {
|
|
|
211
214
|
body.description = updateFields.description;
|
|
212
215
|
if (updateFields.color)
|
|
213
216
|
body.color = updateFields.color;
|
|
214
|
-
|
|
217
|
+
if (updateFields.bundle)
|
|
218
|
+
body.bundle = updateFields.bundle;
|
|
219
|
+
const method = createIfNotFound ? 'PUT' : 'PATCH';
|
|
220
|
+
const response = await (0, ApiHelpers_1.makeApiRequest)(context, method, `/categories/${categoryId}/edit`, body);
|
|
215
221
|
return response.category;
|
|
216
222
|
}
|
|
217
223
|
async function getCategory(context, itemIndex) {
|
|
218
224
|
const categoryId = (0, ApiHelpers_1.getRequiredParam)(context, 'categoryId', itemIndex);
|
|
219
225
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/categories/${categoryId}`);
|
|
220
|
-
return response.category;
|
|
226
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.category);
|
|
221
227
|
}
|
|
222
228
|
async function getAllCategories(context, itemIndex) {
|
|
223
229
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -231,7 +237,8 @@ async function getAllCategories(context, itemIndex) {
|
|
|
231
237
|
if (!returnAll) {
|
|
232
238
|
limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, 30);
|
|
233
239
|
}
|
|
234
|
-
|
|
240
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'categories', 'GET', '/categories', {}, qs, limit);
|
|
241
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
235
242
|
}
|
|
236
243
|
async function deleteCategory(context, itemIndex) {
|
|
237
244
|
const categoryId = (0, ApiHelpers_1.getRequiredParam)(context, 'categoryId', itemIndex);
|
|
@@ -111,7 +111,7 @@ async function getNotification(context, itemIndex) {
|
|
|
111
111
|
const notificationId = (0, ApiHelpers_1.getRequiredParam)(context, 'notificationId', itemIndex);
|
|
112
112
|
const endpoint = `/notifications/${notificationId}`;
|
|
113
113
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', endpoint);
|
|
114
|
-
return response.notification;
|
|
114
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.notification);
|
|
115
115
|
}
|
|
116
116
|
async function getAllNotifications(context, itemIndex) {
|
|
117
117
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
@@ -119,12 +119,14 @@ async function getAllNotifications(context, itemIndex) {
|
|
|
119
119
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
120
120
|
const query = (0, DataHelpers_1.buildQueryFromOptions)(options);
|
|
121
121
|
if (returnAll) {
|
|
122
|
-
|
|
122
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'notifications', 'GET', '/notifications', {}, query);
|
|
123
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
123
124
|
}
|
|
124
125
|
else {
|
|
125
126
|
query.limit = limit;
|
|
126
127
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/notifications', {}, query);
|
|
127
|
-
|
|
128
|
+
const data = Object.values(response.notifications || {});
|
|
129
|
+
return (0, DataHelpers_1.convertNumericStrings)(data);
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
async function deleteNotification(context, itemIndex) {
|
|
@@ -62,20 +62,22 @@ async function updateSegment(context, itemIndex) {
|
|
|
62
62
|
async function getSegment(context, itemIndex) {
|
|
63
63
|
const segmentId = (0, ApiHelpers_1.getRequiredParam)(context, 'segmentId', itemIndex);
|
|
64
64
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', `/segments/${segmentId}`);
|
|
65
|
-
return response.list;
|
|
65
|
+
return (0, DataHelpers_1.convertNumericStrings)(response.list);
|
|
66
66
|
}
|
|
67
67
|
async function getAllSegments(context, itemIndex) {
|
|
68
68
|
const returnAll = (0, ApiHelpers_1.getOptionalParam)(context, 'returnAll', itemIndex, false);
|
|
69
69
|
const options = (0, ApiHelpers_1.getOptionalParam)(context, 'options', itemIndex, {});
|
|
70
70
|
const qs = (0, DataHelpers_1.buildQueryFromOptions)(options);
|
|
71
71
|
if (returnAll) {
|
|
72
|
-
|
|
72
|
+
const result = await (0, ApiHelpers_1.makePaginatedRequest)(context, 'lists', 'GET', '/segments', {}, qs);
|
|
73
|
+
return (0, DataHelpers_1.convertNumericStrings)(result);
|
|
73
74
|
}
|
|
74
75
|
else {
|
|
75
76
|
const limit = (0, ApiHelpers_1.getOptionalParam)(context, 'limit', itemIndex, 30);
|
|
76
77
|
qs.limit = limit;
|
|
77
78
|
const response = await (0, ApiHelpers_1.makeApiRequest)(context, 'GET', '/segments', {}, qs);
|
|
78
|
-
|
|
79
|
+
const data = response.lists ? Object.values(response.lists) : [];
|
|
80
|
+
return (0, DataHelpers_1.convertNumericStrings)(data);
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
async function deleteSegment(context, itemIndex) {
|
|
@@ -39,7 +39,69 @@ 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
|
-
|
|
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 &&
|
|
62
|
+
Array.isArray(errorData.errors) &&
|
|
63
|
+
errorData.errors.length > 0) {
|
|
64
|
+
const validationErrors = [];
|
|
65
|
+
errorData.errors.forEach((err) => {
|
|
66
|
+
if (err.details && typeof err.details === 'object') {
|
|
67
|
+
// Extract field-specific validation messages
|
|
68
|
+
Object.entries(err.details).forEach(([field, messages]) => {
|
|
69
|
+
if (Array.isArray(messages)) {
|
|
70
|
+
messages.forEach((msg) => {
|
|
71
|
+
validationErrors.push(`- ${field}: ${msg}`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (err.message) {
|
|
77
|
+
// Fallback to error message if no details
|
|
78
|
+
validationErrors.push(`- ${err.message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (validationErrors.length > 0) {
|
|
82
|
+
errorMessage += ` Validation errors:\n${validationErrors.join('\n')}\n`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Check for deprecated error format (Mautic < 2.6.0)
|
|
86
|
+
else if (errorData?.error?.details && typeof errorData.error.details === 'object') {
|
|
87
|
+
const validationErrors = [];
|
|
88
|
+
Object.entries(errorData.error.details).forEach(([field, messages]) => {
|
|
89
|
+
if (Array.isArray(messages)) {
|
|
90
|
+
messages.forEach((msg) => {
|
|
91
|
+
validationErrors.push(`- ${field}: ${msg}`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (validationErrors.length > 0) {
|
|
96
|
+
errorMessage += ` Validation errors:\n${validationErrors.join('\n')}\n`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (parseError) {
|
|
101
|
+
// If we can't parse the error details, just use the generic message
|
|
102
|
+
}
|
|
103
|
+
errorMessage += ' Please check your input parameters.';
|
|
104
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), errorMessage, { itemIndex: 0 });
|
|
43
105
|
}
|
|
44
106
|
throw error;
|
|
45
107
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createBatchSuccessResponse = exports.processBatchIds = exports.buildQueryFromOptions = exports.validateJsonParameter = exports.processContactFields = exports.wrapSingleItem = exports.processSimpleResponse = void 0;
|
|
3
|
+
exports.convertNumericStrings = exports.createBatchSuccessResponse = exports.processBatchIds = exports.buildQueryFromOptions = exports.validateJsonParameter = exports.processContactFields = exports.wrapSingleItem = exports.processSimpleResponse = void 0;
|
|
4
4
|
const GenericFunctions_1 = require("../GenericFunctions");
|
|
5
5
|
// Process simple response data extraction
|
|
6
6
|
function processSimpleResponse(responseData, simple, dataPath) {
|
|
@@ -102,3 +102,33 @@ function createBatchSuccessResponse(operation, ids, customMessage) {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
exports.createBatchSuccessResponse = createBatchSuccessResponse;
|
|
105
|
+
// Convert numeric strings to numbers recursively
|
|
106
|
+
function convertNumericStrings(data) {
|
|
107
|
+
if (data === null || data === undefined) {
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
if (typeof data === 'string') {
|
|
111
|
+
// Check if string is a valid number (including negative numbers and decimals)
|
|
112
|
+
const numericRegex = /^-?\d+(\.\d+)?$/;
|
|
113
|
+
if (numericRegex.test(data)) {
|
|
114
|
+
const num = parseFloat(data);
|
|
115
|
+
// Only convert if parseFloat doesn't lose precision and result is finite
|
|
116
|
+
if (!isNaN(num) && isFinite(num) && num.toString() === data) {
|
|
117
|
+
return num;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return data;
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(data)) {
|
|
123
|
+
return data.map(convertNumericStrings);
|
|
124
|
+
}
|
|
125
|
+
if (typeof data === 'object') {
|
|
126
|
+
const converted = {};
|
|
127
|
+
for (const [key, value] of Object.entries(data)) {
|
|
128
|
+
converted[key] = convertNumericStrings(value);
|
|
129
|
+
}
|
|
130
|
+
return converted;
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
}
|
|
134
|
+
exports.convertNumericStrings = convertNumericStrings;
|
package/package.json
CHANGED