n8n-nodes-cakemail 1.0.2 → 1.0.3
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.
|
@@ -0,0 +1,1528 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
INodeExecutionData,
|
|
4
|
+
INodeType,
|
|
5
|
+
INodeTypeDescription,
|
|
6
|
+
NodeOperationError,
|
|
7
|
+
} from 'n8n-workflow';
|
|
8
|
+
|
|
9
|
+
import { CakemailClient, CakemailError } from 'cakemail-sdk';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cakemail n8n node
|
|
13
|
+
*/
|
|
14
|
+
export class Cakemail implements INodeType {
|
|
15
|
+
description: INodeTypeDescription = {
|
|
16
|
+
displayName: 'Cakemail',
|
|
17
|
+
name: 'cakemail',
|
|
18
|
+
icon: 'file:cakemail.svg',
|
|
19
|
+
group: ['transform'],
|
|
20
|
+
version: 1,
|
|
21
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
22
|
+
description: 'Interact with Cakemail API',
|
|
23
|
+
defaults: {
|
|
24
|
+
name: 'Cakemail',
|
|
25
|
+
},
|
|
26
|
+
inputs: ['main'],
|
|
27
|
+
outputs: ['main'],
|
|
28
|
+
credentials: [
|
|
29
|
+
{
|
|
30
|
+
name: 'cakemailApi',
|
|
31
|
+
required: true,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
properties: [
|
|
35
|
+
// Resource selector
|
|
36
|
+
{
|
|
37
|
+
displayName: 'Resource',
|
|
38
|
+
name: 'resource',
|
|
39
|
+
type: 'options',
|
|
40
|
+
noDataExpression: true,
|
|
41
|
+
options: [
|
|
42
|
+
{
|
|
43
|
+
name: 'Account',
|
|
44
|
+
value: 'account',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Contact',
|
|
48
|
+
value: 'contact',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'List',
|
|
52
|
+
value: 'list',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Campaign',
|
|
56
|
+
value: 'campaign',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
default: 'account',
|
|
60
|
+
required: true,
|
|
61
|
+
description: 'The resource to operate on',
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// Account operations
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Operation',
|
|
67
|
+
name: 'operation',
|
|
68
|
+
type: 'options',
|
|
69
|
+
noDataExpression: true,
|
|
70
|
+
displayOptions: {
|
|
71
|
+
show: {
|
|
72
|
+
resource: ['account'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
options: [
|
|
76
|
+
{
|
|
77
|
+
name: 'Get',
|
|
78
|
+
value: 'get',
|
|
79
|
+
description: 'Get current account information',
|
|
80
|
+
action: 'Get current account',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Get By ID',
|
|
84
|
+
value: 'getById',
|
|
85
|
+
description: 'Get account by ID',
|
|
86
|
+
action: 'Get account by ID',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'List',
|
|
90
|
+
value: 'list',
|
|
91
|
+
description: 'List accounts',
|
|
92
|
+
action: 'List accounts',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'Create',
|
|
96
|
+
value: 'create',
|
|
97
|
+
description: 'Create a new account',
|
|
98
|
+
action: 'Create account',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'Update',
|
|
102
|
+
value: 'update',
|
|
103
|
+
description: 'Update an account',
|
|
104
|
+
action: 'Update account',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Delete',
|
|
108
|
+
value: 'delete',
|
|
109
|
+
description: 'Delete an account',
|
|
110
|
+
action: 'Delete account',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'Get Children',
|
|
114
|
+
value: 'getChildren',
|
|
115
|
+
description: 'Get child accounts of a parent account',
|
|
116
|
+
action: 'Get child accounts',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
default: 'get',
|
|
120
|
+
required: true,
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// Contact operations
|
|
124
|
+
{
|
|
125
|
+
displayName: 'Operation',
|
|
126
|
+
name: 'operation',
|
|
127
|
+
type: 'options',
|
|
128
|
+
noDataExpression: true,
|
|
129
|
+
displayOptions: {
|
|
130
|
+
show: {
|
|
131
|
+
resource: ['contact'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
options: [
|
|
135
|
+
{
|
|
136
|
+
name: 'Get',
|
|
137
|
+
value: 'get',
|
|
138
|
+
description: 'Get a contact by ID',
|
|
139
|
+
action: 'Get contact',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'Get By Email',
|
|
143
|
+
value: 'getByEmail',
|
|
144
|
+
description: 'Get a contact by email',
|
|
145
|
+
action: 'Get contact by email',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'List',
|
|
149
|
+
value: 'list',
|
|
150
|
+
description: 'List contacts',
|
|
151
|
+
action: 'List contacts',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'Create',
|
|
155
|
+
value: 'create',
|
|
156
|
+
description: 'Create a new contact',
|
|
157
|
+
action: 'Create contact',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'Update',
|
|
161
|
+
value: 'update',
|
|
162
|
+
description: 'Update a contact',
|
|
163
|
+
action: 'Update contact',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'Delete',
|
|
167
|
+
value: 'delete',
|
|
168
|
+
description: 'Delete a contact',
|
|
169
|
+
action: 'Delete contact',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'Subscribe',
|
|
173
|
+
value: 'subscribe',
|
|
174
|
+
description: 'Subscribe a contact to a list',
|
|
175
|
+
action: 'Subscribe contact to list',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'Unsubscribe',
|
|
179
|
+
value: 'unsubscribe',
|
|
180
|
+
description: 'Unsubscribe a contact from a list',
|
|
181
|
+
action: 'Unsubscribe contact from list',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'Bulk Import',
|
|
185
|
+
value: 'bulkImport',
|
|
186
|
+
description: 'Import multiple contacts at once',
|
|
187
|
+
action: 'Bulk import contacts',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'Search',
|
|
191
|
+
value: 'search',
|
|
192
|
+
description: 'Advanced search with custom field filtering',
|
|
193
|
+
action: 'Search contacts',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'Add Tags',
|
|
197
|
+
value: 'addTags',
|
|
198
|
+
description: 'Add tags to a contact',
|
|
199
|
+
action: 'Add tags to contact',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'Remove Tags',
|
|
203
|
+
value: 'removeTags',
|
|
204
|
+
description: 'Remove tags from a contact',
|
|
205
|
+
action: 'Remove tags from contact',
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
default: 'get',
|
|
209
|
+
required: true,
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// List operations
|
|
213
|
+
{
|
|
214
|
+
displayName: 'Operation',
|
|
215
|
+
name: 'operation',
|
|
216
|
+
type: 'options',
|
|
217
|
+
noDataExpression: true,
|
|
218
|
+
displayOptions: {
|
|
219
|
+
show: {
|
|
220
|
+
resource: ['list'],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
options: [
|
|
224
|
+
{
|
|
225
|
+
name: 'Get',
|
|
226
|
+
value: 'get',
|
|
227
|
+
description: 'Get a list by ID',
|
|
228
|
+
action: 'Get list',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'List',
|
|
232
|
+
value: 'list',
|
|
233
|
+
description: 'List all lists',
|
|
234
|
+
action: 'List lists',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'Create',
|
|
238
|
+
value: 'create',
|
|
239
|
+
description: 'Create a new list',
|
|
240
|
+
action: 'Create list',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'Update',
|
|
244
|
+
value: 'update',
|
|
245
|
+
description: 'Update a list',
|
|
246
|
+
action: 'Update list',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'Delete',
|
|
250
|
+
value: 'delete',
|
|
251
|
+
description: 'Delete a list',
|
|
252
|
+
action: 'Delete list',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'Get Statistics',
|
|
256
|
+
value: 'getStatistics',
|
|
257
|
+
description: 'Get list statistics',
|
|
258
|
+
action: 'Get list statistics',
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
default: 'get',
|
|
262
|
+
required: true,
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
// Campaign operations
|
|
266
|
+
{
|
|
267
|
+
displayName: 'Operation',
|
|
268
|
+
name: 'operation',
|
|
269
|
+
type: 'options',
|
|
270
|
+
noDataExpression: true,
|
|
271
|
+
displayOptions: {
|
|
272
|
+
show: {
|
|
273
|
+
resource: ['campaign'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
options: [
|
|
277
|
+
{
|
|
278
|
+
name: 'Get',
|
|
279
|
+
value: 'get',
|
|
280
|
+
description: 'Get a campaign by ID',
|
|
281
|
+
action: 'Get campaign',
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'List',
|
|
285
|
+
value: 'list',
|
|
286
|
+
description: 'List campaigns',
|
|
287
|
+
action: 'List campaigns',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'Create',
|
|
291
|
+
value: 'create',
|
|
292
|
+
description: 'Create a new campaign',
|
|
293
|
+
action: 'Create campaign',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'Update',
|
|
297
|
+
value: 'update',
|
|
298
|
+
description: 'Update a campaign',
|
|
299
|
+
action: 'Update campaign',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'Delete',
|
|
303
|
+
value: 'delete',
|
|
304
|
+
description: 'Delete a campaign',
|
|
305
|
+
action: 'Delete campaign',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: 'Send',
|
|
309
|
+
value: 'send',
|
|
310
|
+
description: 'Send a campaign',
|
|
311
|
+
action: 'Send campaign',
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
default: 'get',
|
|
315
|
+
required: true,
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
// Multi-tenant account ID (optional for all operations)
|
|
319
|
+
{
|
|
320
|
+
displayName: 'Account ID',
|
|
321
|
+
name: 'accountId',
|
|
322
|
+
type: 'number',
|
|
323
|
+
default: undefined,
|
|
324
|
+
required: false,
|
|
325
|
+
description:
|
|
326
|
+
'Optional: Specify an account ID for multi-tenant operations. If not provided, uses the authenticated account.',
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
// ================== Account Operation Parameters ==================
|
|
330
|
+
|
|
331
|
+
// Target Account ID (for getById, update, delete, getChildren account operations)
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Target Account ID',
|
|
334
|
+
name: 'targetAccountId',
|
|
335
|
+
type: 'number',
|
|
336
|
+
required: true,
|
|
337
|
+
displayOptions: {
|
|
338
|
+
show: {
|
|
339
|
+
resource: ['account'],
|
|
340
|
+
operation: ['getById', 'update', 'delete', 'getChildren'],
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
default: 0,
|
|
344
|
+
description: 'The ID of the account to operate on',
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// Account name (for create/update)
|
|
348
|
+
{
|
|
349
|
+
displayName: 'Name',
|
|
350
|
+
name: 'accountName',
|
|
351
|
+
type: 'string',
|
|
352
|
+
required: true,
|
|
353
|
+
displayOptions: {
|
|
354
|
+
show: {
|
|
355
|
+
resource: ['account'],
|
|
356
|
+
operation: ['create'],
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
default: '',
|
|
360
|
+
description: 'Account name',
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// Account email (for create)
|
|
364
|
+
{
|
|
365
|
+
displayName: 'Email',
|
|
366
|
+
name: 'accountEmail',
|
|
367
|
+
type: 'string',
|
|
368
|
+
required: true,
|
|
369
|
+
displayOptions: {
|
|
370
|
+
show: {
|
|
371
|
+
resource: ['account'],
|
|
372
|
+
operation: ['create'],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
default: '',
|
|
376
|
+
description: 'Primary email for the account',
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
// Account additional fields (for create/update)
|
|
380
|
+
{
|
|
381
|
+
displayName: 'Additional Fields',
|
|
382
|
+
name: 'accountAdditionalFields',
|
|
383
|
+
type: 'collection',
|
|
384
|
+
placeholder: 'Add Field',
|
|
385
|
+
default: {},
|
|
386
|
+
displayOptions: {
|
|
387
|
+
show: {
|
|
388
|
+
resource: ['account'],
|
|
389
|
+
operation: ['create', 'update'],
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
options: [
|
|
393
|
+
{
|
|
394
|
+
displayName: 'Name',
|
|
395
|
+
name: 'name',
|
|
396
|
+
type: 'string',
|
|
397
|
+
default: '',
|
|
398
|
+
description: 'Account name (for update)',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
displayName: 'Primary Email',
|
|
402
|
+
name: 'primary_email',
|
|
403
|
+
type: 'string',
|
|
404
|
+
default: '',
|
|
405
|
+
description: 'Primary email (for update)',
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
displayName: 'Account Type',
|
|
409
|
+
name: 'account_type',
|
|
410
|
+
type: 'options',
|
|
411
|
+
options: [
|
|
412
|
+
{ name: 'Parent', value: 'parent' },
|
|
413
|
+
{ name: 'Child', value: 'child' },
|
|
414
|
+
{ name: 'Standalone', value: 'standalone' },
|
|
415
|
+
],
|
|
416
|
+
default: 'standalone',
|
|
417
|
+
description: 'Type of account',
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
displayName: 'Parent Account ID',
|
|
421
|
+
name: 'parent_account_id',
|
|
422
|
+
type: 'number',
|
|
423
|
+
default: 0,
|
|
424
|
+
description: 'Parent account ID (for child accounts)',
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
displayName: 'Status',
|
|
428
|
+
name: 'status',
|
|
429
|
+
type: 'options',
|
|
430
|
+
options: [
|
|
431
|
+
{ name: 'Active', value: 'active' },
|
|
432
|
+
{ name: 'Suspended', value: 'suspended' },
|
|
433
|
+
{ name: 'Deleted', value: 'deleted' },
|
|
434
|
+
],
|
|
435
|
+
default: 'active',
|
|
436
|
+
description: 'Account status (for update)',
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
displayName: 'Language',
|
|
440
|
+
name: 'language',
|
|
441
|
+
type: 'string',
|
|
442
|
+
default: 'en',
|
|
443
|
+
description: 'Account language (e.g., en, fr)',
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
displayName: 'Timezone',
|
|
447
|
+
name: 'timezone',
|
|
448
|
+
type: 'string',
|
|
449
|
+
default: 'America/New_York',
|
|
450
|
+
description: 'Account timezone',
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
displayName: 'Currency',
|
|
454
|
+
name: 'currency',
|
|
455
|
+
type: 'string',
|
|
456
|
+
default: 'USD',
|
|
457
|
+
description: 'Account currency',
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
displayName: 'Settings',
|
|
461
|
+
name: 'settings',
|
|
462
|
+
type: 'json',
|
|
463
|
+
default: '{}',
|
|
464
|
+
description: 'Account settings as JSON object',
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
// Get Children pagination options
|
|
470
|
+
{
|
|
471
|
+
displayName: 'Options',
|
|
472
|
+
name: 'getChildrenOptions',
|
|
473
|
+
type: 'collection',
|
|
474
|
+
placeholder: 'Add Option',
|
|
475
|
+
default: {},
|
|
476
|
+
displayOptions: {
|
|
477
|
+
show: {
|
|
478
|
+
resource: ['account'],
|
|
479
|
+
operation: ['getChildren'],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
options: [
|
|
483
|
+
{
|
|
484
|
+
displayName: 'Page',
|
|
485
|
+
name: 'page',
|
|
486
|
+
type: 'number',
|
|
487
|
+
default: 1,
|
|
488
|
+
description: 'Page number',
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
displayName: 'Per Page',
|
|
492
|
+
name: 'per_page',
|
|
493
|
+
type: 'number',
|
|
494
|
+
default: 20,
|
|
495
|
+
description: 'Results per page',
|
|
496
|
+
},
|
|
497
|
+
],
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
// Account list filters
|
|
501
|
+
{
|
|
502
|
+
displayName: 'Filters',
|
|
503
|
+
name: 'accountFilters',
|
|
504
|
+
type: 'collection',
|
|
505
|
+
placeholder: 'Add Filter',
|
|
506
|
+
default: {},
|
|
507
|
+
displayOptions: {
|
|
508
|
+
show: {
|
|
509
|
+
resource: ['account'],
|
|
510
|
+
operation: ['list'],
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
options: [
|
|
514
|
+
{
|
|
515
|
+
displayName: 'Status',
|
|
516
|
+
name: 'status',
|
|
517
|
+
type: 'options',
|
|
518
|
+
options: [
|
|
519
|
+
{ name: 'Active', value: 'active' },
|
|
520
|
+
{ name: 'Suspended', value: 'suspended' },
|
|
521
|
+
{ name: 'Deleted', value: 'deleted' },
|
|
522
|
+
],
|
|
523
|
+
default: 'active',
|
|
524
|
+
description: 'Filter by account status',
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
displayName: 'Account Type',
|
|
528
|
+
name: 'account_type',
|
|
529
|
+
type: 'options',
|
|
530
|
+
options: [
|
|
531
|
+
{ name: 'Parent', value: 'parent' },
|
|
532
|
+
{ name: 'Child', value: 'child' },
|
|
533
|
+
{ name: 'Standalone', value: 'standalone' },
|
|
534
|
+
],
|
|
535
|
+
default: 'standalone',
|
|
536
|
+
description: 'Filter by account type',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
displayName: 'Parent Account ID',
|
|
540
|
+
name: 'parent_account_id',
|
|
541
|
+
type: 'number',
|
|
542
|
+
default: 0,
|
|
543
|
+
description: 'Filter by parent account ID',
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
displayName: 'Search',
|
|
547
|
+
name: 'search',
|
|
548
|
+
type: 'string',
|
|
549
|
+
default: '',
|
|
550
|
+
description: 'Search by name or email',
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
displayName: 'Page',
|
|
554
|
+
name: 'page',
|
|
555
|
+
type: 'number',
|
|
556
|
+
default: 1,
|
|
557
|
+
description: 'Page number',
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
displayName: 'Per Page',
|
|
561
|
+
name: 'per_page',
|
|
562
|
+
type: 'number',
|
|
563
|
+
default: 20,
|
|
564
|
+
description: 'Results per page',
|
|
565
|
+
},
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
|
|
569
|
+
// ================== List Operation Parameters ==================
|
|
570
|
+
|
|
571
|
+
// List ID (for get, update, delete list operations)
|
|
572
|
+
{
|
|
573
|
+
displayName: 'List ID',
|
|
574
|
+
name: 'listIdParam',
|
|
575
|
+
type: 'number',
|
|
576
|
+
required: true,
|
|
577
|
+
displayOptions: {
|
|
578
|
+
show: {
|
|
579
|
+
resource: ['list'],
|
|
580
|
+
operation: ['get', 'update', 'delete', 'getStatistics'],
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
default: 0,
|
|
584
|
+
description: 'The ID of the list',
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
// List name (for create/update)
|
|
588
|
+
{
|
|
589
|
+
displayName: 'Name',
|
|
590
|
+
name: 'listName',
|
|
591
|
+
type: 'string',
|
|
592
|
+
required: true,
|
|
593
|
+
displayOptions: {
|
|
594
|
+
show: {
|
|
595
|
+
resource: ['list'],
|
|
596
|
+
operation: ['create'],
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
default: '',
|
|
600
|
+
description: 'The name of the list',
|
|
601
|
+
},
|
|
602
|
+
|
|
603
|
+
// List additional fields
|
|
604
|
+
{
|
|
605
|
+
displayName: 'Additional Fields',
|
|
606
|
+
name: 'listAdditionalFields',
|
|
607
|
+
type: 'collection',
|
|
608
|
+
placeholder: 'Add Field',
|
|
609
|
+
default: {},
|
|
610
|
+
displayOptions: {
|
|
611
|
+
show: {
|
|
612
|
+
resource: ['list'],
|
|
613
|
+
operation: ['create', 'update'],
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
options: [
|
|
617
|
+
{
|
|
618
|
+
displayName: 'Description',
|
|
619
|
+
name: 'description',
|
|
620
|
+
type: 'string',
|
|
621
|
+
default: '',
|
|
622
|
+
description: 'List description',
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
displayName: 'Sender Name',
|
|
626
|
+
name: 'sender_name',
|
|
627
|
+
type: 'string',
|
|
628
|
+
default: '',
|
|
629
|
+
description: 'Default sender name for campaigns',
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
displayName: 'Sender Email',
|
|
633
|
+
name: 'sender_email',
|
|
634
|
+
type: 'string',
|
|
635
|
+
default: '',
|
|
636
|
+
description: 'Default sender email for campaigns',
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
displayName: 'Language',
|
|
640
|
+
name: 'language',
|
|
641
|
+
type: 'string',
|
|
642
|
+
default: 'en',
|
|
643
|
+
description: 'Default language',
|
|
644
|
+
},
|
|
645
|
+
],
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
// List pagination
|
|
649
|
+
{
|
|
650
|
+
displayName: 'Limit',
|
|
651
|
+
name: 'listLimit',
|
|
652
|
+
type: 'number',
|
|
653
|
+
displayOptions: {
|
|
654
|
+
show: {
|
|
655
|
+
resource: ['list'],
|
|
656
|
+
operation: ['list'],
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
default: 20,
|
|
660
|
+
description: 'Number of results to return',
|
|
661
|
+
},
|
|
662
|
+
|
|
663
|
+
// ================== Contact Operation Parameters ==================
|
|
664
|
+
|
|
665
|
+
// Contact ID (for get, update, delete, subscribe, unsubscribe, addTags, removeTags operations)
|
|
666
|
+
{
|
|
667
|
+
displayName: 'Contact ID',
|
|
668
|
+
name: 'contactId',
|
|
669
|
+
type: 'number',
|
|
670
|
+
required: true,
|
|
671
|
+
displayOptions: {
|
|
672
|
+
show: {
|
|
673
|
+
resource: ['contact'],
|
|
674
|
+
operation: ['get', 'update', 'delete', 'subscribe', 'unsubscribe', 'addTags', 'removeTags'],
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
default: 0,
|
|
678
|
+
description: 'The ID of the contact',
|
|
679
|
+
},
|
|
680
|
+
|
|
681
|
+
// Tags (for addTags, removeTags operations)
|
|
682
|
+
{
|
|
683
|
+
displayName: 'Tags',
|
|
684
|
+
name: 'tagsToManage',
|
|
685
|
+
type: 'string',
|
|
686
|
+
required: true,
|
|
687
|
+
displayOptions: {
|
|
688
|
+
show: {
|
|
689
|
+
resource: ['contact'],
|
|
690
|
+
operation: ['addTags', 'removeTags'],
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
default: '',
|
|
694
|
+
placeholder: 'vip, newsletter, customer',
|
|
695
|
+
description: 'Comma-separated list of tags',
|
|
696
|
+
},
|
|
697
|
+
|
|
698
|
+
// Contact Email (for getByEmail operation)
|
|
699
|
+
{
|
|
700
|
+
displayName: 'Email',
|
|
701
|
+
name: 'email',
|
|
702
|
+
type: 'string',
|
|
703
|
+
required: true,
|
|
704
|
+
displayOptions: {
|
|
705
|
+
show: {
|
|
706
|
+
resource: ['contact'],
|
|
707
|
+
operation: ['getByEmail', 'create'],
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
default: '',
|
|
711
|
+
placeholder: 'user@example.com',
|
|
712
|
+
description: 'The email address of the contact',
|
|
713
|
+
},
|
|
714
|
+
|
|
715
|
+
// Contact fields for create/update
|
|
716
|
+
{
|
|
717
|
+
displayName: 'Additional Fields',
|
|
718
|
+
name: 'additionalFields',
|
|
719
|
+
type: 'collection',
|
|
720
|
+
placeholder: 'Add Field',
|
|
721
|
+
default: {},
|
|
722
|
+
displayOptions: {
|
|
723
|
+
show: {
|
|
724
|
+
resource: ['contact'],
|
|
725
|
+
operation: ['create', 'update'],
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
options: [
|
|
729
|
+
{
|
|
730
|
+
displayName: 'First Name',
|
|
731
|
+
name: 'first_name',
|
|
732
|
+
type: 'string',
|
|
733
|
+
default: '',
|
|
734
|
+
description: 'First name of the contact',
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
displayName: 'Last Name',
|
|
738
|
+
name: 'last_name',
|
|
739
|
+
type: 'string',
|
|
740
|
+
default: '',
|
|
741
|
+
description: 'Last name of the contact',
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
displayName: 'Company',
|
|
745
|
+
name: 'company',
|
|
746
|
+
type: 'string',
|
|
747
|
+
default: '',
|
|
748
|
+
description: 'Company name',
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
displayName: 'Phone',
|
|
752
|
+
name: 'phone',
|
|
753
|
+
type: 'string',
|
|
754
|
+
default: '',
|
|
755
|
+
description: 'Phone number',
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
displayName: 'Mobile',
|
|
759
|
+
name: 'mobile',
|
|
760
|
+
type: 'string',
|
|
761
|
+
default: '',
|
|
762
|
+
description: 'Mobile phone number',
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
displayName: 'Language',
|
|
766
|
+
name: 'language',
|
|
767
|
+
type: 'string',
|
|
768
|
+
default: 'en',
|
|
769
|
+
description: 'Preferred language (e.g., en, fr)',
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
displayName: 'Timezone',
|
|
773
|
+
name: 'timezone',
|
|
774
|
+
type: 'string',
|
|
775
|
+
default: 'UTC',
|
|
776
|
+
description: 'Timezone (e.g., America/New_York, Europe/Paris)',
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
displayName: 'Tags',
|
|
780
|
+
name: 'tags',
|
|
781
|
+
type: 'string',
|
|
782
|
+
default: '',
|
|
783
|
+
description: 'Comma-separated list of tags',
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
displayName: 'Custom Attributes',
|
|
787
|
+
name: 'custom_attributes',
|
|
788
|
+
type: 'json',
|
|
789
|
+
default: '{}',
|
|
790
|
+
description: 'JSON object with custom attribute key-value pairs',
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
displayName: 'List ID',
|
|
794
|
+
name: 'list_id',
|
|
795
|
+
type: 'number',
|
|
796
|
+
default: 0,
|
|
797
|
+
description: 'Subscribe contact to this list immediately (create only)',
|
|
798
|
+
},
|
|
799
|
+
],
|
|
800
|
+
},
|
|
801
|
+
|
|
802
|
+
// List ID for subscribe/unsubscribe operations
|
|
803
|
+
{
|
|
804
|
+
displayName: 'List ID',
|
|
805
|
+
name: 'listId',
|
|
806
|
+
type: 'number',
|
|
807
|
+
required: true,
|
|
808
|
+
displayOptions: {
|
|
809
|
+
show: {
|
|
810
|
+
resource: ['contact'],
|
|
811
|
+
operation: ['subscribe', 'unsubscribe'],
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
default: 0,
|
|
815
|
+
description: 'The ID of the list',
|
|
816
|
+
},
|
|
817
|
+
|
|
818
|
+
// Bulk import contacts
|
|
819
|
+
{
|
|
820
|
+
displayName: 'Contacts',
|
|
821
|
+
name: 'contacts',
|
|
822
|
+
type: 'json',
|
|
823
|
+
required: true,
|
|
824
|
+
displayOptions: {
|
|
825
|
+
show: {
|
|
826
|
+
resource: ['contact'],
|
|
827
|
+
operation: ['bulkImport'],
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
default: '[]',
|
|
831
|
+
description: 'Array of contact objects to import. Each contact should have at least an email field.',
|
|
832
|
+
placeholder: '[{"email": "user1@example.com", "first_name": "John"}, {"email": "user2@example.com"}]',
|
|
833
|
+
},
|
|
834
|
+
|
|
835
|
+
// Advanced search parameters
|
|
836
|
+
{
|
|
837
|
+
displayName: 'Search Query',
|
|
838
|
+
name: 'searchQuery',
|
|
839
|
+
type: 'json',
|
|
840
|
+
required: true,
|
|
841
|
+
displayOptions: {
|
|
842
|
+
show: {
|
|
843
|
+
resource: ['contact'],
|
|
844
|
+
operation: ['search'],
|
|
845
|
+
},
|
|
846
|
+
},
|
|
847
|
+
default: '{}',
|
|
848
|
+
description: 'Advanced search query with filters and operators',
|
|
849
|
+
placeholder: '{"custom_attributes.company": "Acme", "tags": ["vip"], "status": "active"}',
|
|
850
|
+
},
|
|
851
|
+
|
|
852
|
+
{
|
|
853
|
+
displayName: 'Search Options',
|
|
854
|
+
name: 'searchOptions',
|
|
855
|
+
type: 'collection',
|
|
856
|
+
placeholder: 'Add Option',
|
|
857
|
+
default: {},
|
|
858
|
+
displayOptions: {
|
|
859
|
+
show: {
|
|
860
|
+
resource: ['contact'],
|
|
861
|
+
operation: ['search'],
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
options: [
|
|
865
|
+
{
|
|
866
|
+
displayName: 'Page',
|
|
867
|
+
name: 'page',
|
|
868
|
+
type: 'number',
|
|
869
|
+
default: 1,
|
|
870
|
+
description: 'Page number',
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
displayName: 'Per Page',
|
|
874
|
+
name: 'per_page',
|
|
875
|
+
type: 'number',
|
|
876
|
+
default: 20,
|
|
877
|
+
description: 'Results per page',
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
displayName: 'Sort By',
|
|
881
|
+
name: 'sort',
|
|
882
|
+
type: 'string',
|
|
883
|
+
default: 'created_on',
|
|
884
|
+
description: 'Field to sort by (e.g., created_on, email, first_name)',
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
displayName: 'Sort Order',
|
|
888
|
+
name: 'sort_order',
|
|
889
|
+
type: 'options',
|
|
890
|
+
options: [
|
|
891
|
+
{ name: 'Ascending', value: 'asc' },
|
|
892
|
+
{ name: 'Descending', value: 'desc' },
|
|
893
|
+
],
|
|
894
|
+
default: 'desc',
|
|
895
|
+
description: 'Sort order',
|
|
896
|
+
},
|
|
897
|
+
],
|
|
898
|
+
},
|
|
899
|
+
|
|
900
|
+
// List contacts filters
|
|
901
|
+
{
|
|
902
|
+
displayName: 'Filters',
|
|
903
|
+
name: 'filters',
|
|
904
|
+
type: 'collection',
|
|
905
|
+
placeholder: 'Add Filter',
|
|
906
|
+
default: {},
|
|
907
|
+
displayOptions: {
|
|
908
|
+
show: {
|
|
909
|
+
resource: ['contact'],
|
|
910
|
+
operation: ['list'],
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
options: [
|
|
914
|
+
{
|
|
915
|
+
displayName: 'Status',
|
|
916
|
+
name: 'status',
|
|
917
|
+
type: 'options',
|
|
918
|
+
options: [
|
|
919
|
+
{
|
|
920
|
+
name: 'Active',
|
|
921
|
+
value: 'active',
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: 'Unsubscribed',
|
|
925
|
+
value: 'unsubscribed',
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
name: 'Bounced',
|
|
929
|
+
value: 'bounced',
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
name: 'Complained',
|
|
933
|
+
value: 'complained',
|
|
934
|
+
},
|
|
935
|
+
],
|
|
936
|
+
default: 'active',
|
|
937
|
+
description: 'Filter by contact status',
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
displayName: 'List ID',
|
|
941
|
+
name: 'list_id',
|
|
942
|
+
type: 'number',
|
|
943
|
+
default: 0,
|
|
944
|
+
description: 'Filter by list membership',
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
displayName: 'Search',
|
|
948
|
+
name: 'search',
|
|
949
|
+
type: 'string',
|
|
950
|
+
default: '',
|
|
951
|
+
description: 'Search by email, name, etc.',
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
displayName: 'Tags',
|
|
955
|
+
name: 'tags',
|
|
956
|
+
type: 'string',
|
|
957
|
+
default: '',
|
|
958
|
+
description: 'Comma-separated list of tags to filter by',
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
displayName: 'Page',
|
|
962
|
+
name: 'page',
|
|
963
|
+
type: 'number',
|
|
964
|
+
default: 1,
|
|
965
|
+
description: 'Page number for pagination',
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
displayName: 'Per Page',
|
|
969
|
+
name: 'per_page',
|
|
970
|
+
type: 'number',
|
|
971
|
+
default: 20,
|
|
972
|
+
description: 'Number of results per page',
|
|
973
|
+
},
|
|
974
|
+
],
|
|
975
|
+
},
|
|
976
|
+
],
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
980
|
+
const items = this.getInputData();
|
|
981
|
+
const returnData: INodeExecutionData[] = [];
|
|
982
|
+
|
|
983
|
+
// Get credentials
|
|
984
|
+
const credentials = await this.getCredentials('cakemailApi');
|
|
985
|
+
|
|
986
|
+
// Initialize Cakemail client
|
|
987
|
+
const client = new CakemailClient({
|
|
988
|
+
email: credentials.email as string,
|
|
989
|
+
password: credentials.password as string,
|
|
990
|
+
baseURL: (credentials.baseURL as string) || undefined,
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
// Process each item
|
|
994
|
+
for (let i = 0; i < items.length; i++) {
|
|
995
|
+
try {
|
|
996
|
+
const resource = this.getNodeParameter('resource', i) as string;
|
|
997
|
+
const operation = this.getNodeParameter('operation', i) as string;
|
|
998
|
+
const accountId = this.getNodeParameter('accountId', i, undefined) as
|
|
999
|
+
| number
|
|
1000
|
+
| undefined;
|
|
1001
|
+
|
|
1002
|
+
const options = accountId ? { accountId } : undefined;
|
|
1003
|
+
|
|
1004
|
+
let responseData: any;
|
|
1005
|
+
|
|
1006
|
+
// Route to appropriate resource handler
|
|
1007
|
+
const node = new Cakemail();
|
|
1008
|
+
if (resource === 'account') {
|
|
1009
|
+
responseData = await node.executeAccountOperation.call(
|
|
1010
|
+
this,
|
|
1011
|
+
client,
|
|
1012
|
+
operation,
|
|
1013
|
+
i,
|
|
1014
|
+
options
|
|
1015
|
+
);
|
|
1016
|
+
} else if (resource === 'contact') {
|
|
1017
|
+
responseData = await node.executeContactOperation.call(
|
|
1018
|
+
this,
|
|
1019
|
+
client,
|
|
1020
|
+
operation,
|
|
1021
|
+
i,
|
|
1022
|
+
options
|
|
1023
|
+
);
|
|
1024
|
+
} else if (resource === 'list') {
|
|
1025
|
+
responseData = await node.executeListOperation.call(
|
|
1026
|
+
this,
|
|
1027
|
+
client,
|
|
1028
|
+
operation,
|
|
1029
|
+
i,
|
|
1030
|
+
options
|
|
1031
|
+
);
|
|
1032
|
+
} else if (resource === 'campaign') {
|
|
1033
|
+
responseData = await node.executeCampaignOperation.call(
|
|
1034
|
+
this,
|
|
1035
|
+
client,
|
|
1036
|
+
operation,
|
|
1037
|
+
i,
|
|
1038
|
+
options
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
returnData.push({
|
|
1043
|
+
json: responseData,
|
|
1044
|
+
pairedItem: { item: i },
|
|
1045
|
+
});
|
|
1046
|
+
} catch (error: any) {
|
|
1047
|
+
if (this.continueOnFail()) {
|
|
1048
|
+
returnData.push({
|
|
1049
|
+
json: {
|
|
1050
|
+
error: error?.message || 'Unknown error',
|
|
1051
|
+
},
|
|
1052
|
+
pairedItem: { item: i },
|
|
1053
|
+
});
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// Convert Cakemail errors to n8n errors
|
|
1058
|
+
if (error instanceof CakemailError) {
|
|
1059
|
+
throw new NodeOperationError(this.getNode(), error.message, {
|
|
1060
|
+
itemIndex: i,
|
|
1061
|
+
description: error.cause?.message,
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
throw error;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return [returnData];
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Execute account operations
|
|
1074
|
+
*/
|
|
1075
|
+
private async executeAccountOperation(
|
|
1076
|
+
this: IExecuteFunctions,
|
|
1077
|
+
client: CakemailClient,
|
|
1078
|
+
operation: string,
|
|
1079
|
+
itemIndex: number,
|
|
1080
|
+
options?: any
|
|
1081
|
+
): Promise<any> {
|
|
1082
|
+
if (operation === 'get') {
|
|
1083
|
+
return await client.accounts.getSelf();
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if (operation === 'getById') {
|
|
1087
|
+
const targetAccountId = this.getNodeParameter(
|
|
1088
|
+
'targetAccountId',
|
|
1089
|
+
itemIndex
|
|
1090
|
+
) as number;
|
|
1091
|
+
return await client.accounts.get(targetAccountId, options);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (operation === 'list') {
|
|
1095
|
+
const filters = this.getNodeParameter(
|
|
1096
|
+
'accountFilters',
|
|
1097
|
+
itemIndex,
|
|
1098
|
+
{}
|
|
1099
|
+
) as any;
|
|
1100
|
+
return await client.accounts.list(filters, options);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
if (operation === 'create') {
|
|
1104
|
+
const name = this.getNodeParameter('accountName', itemIndex) as string;
|
|
1105
|
+
const email = this.getNodeParameter('accountEmail', itemIndex) as string;
|
|
1106
|
+
const additionalFields = this.getNodeParameter(
|
|
1107
|
+
'accountAdditionalFields',
|
|
1108
|
+
itemIndex,
|
|
1109
|
+
{}
|
|
1110
|
+
) as any;
|
|
1111
|
+
|
|
1112
|
+
// Parse settings if provided as JSON string
|
|
1113
|
+
if (additionalFields.settings) {
|
|
1114
|
+
try {
|
|
1115
|
+
additionalFields.settings =
|
|
1116
|
+
typeof additionalFields.settings === 'string'
|
|
1117
|
+
? JSON.parse(additionalFields.settings)
|
|
1118
|
+
: additionalFields.settings;
|
|
1119
|
+
} catch (error: any) {
|
|
1120
|
+
throw new NodeOperationError(
|
|
1121
|
+
this.getNode(),
|
|
1122
|
+
`Invalid settings JSON: ${error.message}`,
|
|
1123
|
+
{ itemIndex }
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const accountData = {
|
|
1129
|
+
name,
|
|
1130
|
+
primary_email: email,
|
|
1131
|
+
...additionalFields,
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
return await client.accounts.create(accountData, options);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (operation === 'update') {
|
|
1138
|
+
const targetAccountId = this.getNodeParameter(
|
|
1139
|
+
'targetAccountId',
|
|
1140
|
+
itemIndex
|
|
1141
|
+
) as number;
|
|
1142
|
+
const additionalFields = this.getNodeParameter(
|
|
1143
|
+
'accountAdditionalFields',
|
|
1144
|
+
itemIndex,
|
|
1145
|
+
{}
|
|
1146
|
+
) as any;
|
|
1147
|
+
|
|
1148
|
+
// Parse settings if provided as JSON string
|
|
1149
|
+
if (additionalFields.settings) {
|
|
1150
|
+
try {
|
|
1151
|
+
additionalFields.settings =
|
|
1152
|
+
typeof additionalFields.settings === 'string'
|
|
1153
|
+
? JSON.parse(additionalFields.settings)
|
|
1154
|
+
: additionalFields.settings;
|
|
1155
|
+
} catch (error: any) {
|
|
1156
|
+
throw new NodeOperationError(
|
|
1157
|
+
this.getNode(),
|
|
1158
|
+
`Invalid settings JSON: ${error.message}`,
|
|
1159
|
+
{ itemIndex }
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return await client.accounts.update(
|
|
1165
|
+
targetAccountId,
|
|
1166
|
+
additionalFields,
|
|
1167
|
+
options
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (operation === 'delete') {
|
|
1172
|
+
const targetAccountId = this.getNodeParameter(
|
|
1173
|
+
'targetAccountId',
|
|
1174
|
+
itemIndex
|
|
1175
|
+
) as number;
|
|
1176
|
+
await client.accounts.delete(targetAccountId, options);
|
|
1177
|
+
return { success: true, accountId: targetAccountId };
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
if (operation === 'getChildren') {
|
|
1181
|
+
const targetAccountId = this.getNodeParameter(
|
|
1182
|
+
'targetAccountId',
|
|
1183
|
+
itemIndex
|
|
1184
|
+
) as number;
|
|
1185
|
+
const childrenOptions = this.getNodeParameter(
|
|
1186
|
+
'getChildrenOptions',
|
|
1187
|
+
itemIndex,
|
|
1188
|
+
{}
|
|
1189
|
+
) as any;
|
|
1190
|
+
return await client.accounts.getChildren(
|
|
1191
|
+
targetAccountId,
|
|
1192
|
+
childrenOptions,
|
|
1193
|
+
options
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
throw new NodeOperationError(
|
|
1198
|
+
this.getNode(),
|
|
1199
|
+
`The operation "${operation}" is not yet implemented for account resource`,
|
|
1200
|
+
{ itemIndex }
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Execute contact operations
|
|
1206
|
+
*/
|
|
1207
|
+
private async executeContactOperation(
|
|
1208
|
+
this: IExecuteFunctions,
|
|
1209
|
+
client: CakemailClient,
|
|
1210
|
+
operation: string,
|
|
1211
|
+
itemIndex: number,
|
|
1212
|
+
options?: any
|
|
1213
|
+
): Promise<any> {
|
|
1214
|
+
if (operation === 'get') {
|
|
1215
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1216
|
+
return await client.contacts.get(contactId, options);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
if (operation === 'getByEmail') {
|
|
1220
|
+
const email = this.getNodeParameter('email', itemIndex) as string;
|
|
1221
|
+
return await client.contacts.getByEmail(email, options);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
if (operation === 'list') {
|
|
1225
|
+
const filters = this.getNodeParameter('filters', itemIndex, {}) as any;
|
|
1226
|
+
|
|
1227
|
+
// Convert comma-separated tags to array
|
|
1228
|
+
if (filters.tags && typeof filters.tags === 'string') {
|
|
1229
|
+
filters.tags = filters.tags.split(',').map((t: string) => t.trim());
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return await client.contacts.list(filters, options);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if (operation === 'create') {
|
|
1236
|
+
const email = this.getNodeParameter('email', itemIndex) as string;
|
|
1237
|
+
const additionalFields = this.getNodeParameter(
|
|
1238
|
+
'additionalFields',
|
|
1239
|
+
itemIndex,
|
|
1240
|
+
{}
|
|
1241
|
+
) as any;
|
|
1242
|
+
|
|
1243
|
+
// Build create request
|
|
1244
|
+
const createData: any = {
|
|
1245
|
+
email,
|
|
1246
|
+
...additionalFields,
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
// Convert comma-separated tags to array
|
|
1250
|
+
if (createData.tags && typeof createData.tags === 'string') {
|
|
1251
|
+
createData.tags = createData.tags.split(',').map((t: string) => t.trim());
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Parse custom attributes JSON
|
|
1255
|
+
if (createData.custom_attributes && typeof createData.custom_attributes === 'string') {
|
|
1256
|
+
try {
|
|
1257
|
+
createData.custom_attributes = JSON.parse(createData.custom_attributes);
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
throw new NodeOperationError(
|
|
1260
|
+
this.getNode(),
|
|
1261
|
+
'Invalid JSON in custom_attributes field',
|
|
1262
|
+
{ itemIndex }
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
return await client.contacts.create(createData, options);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if (operation === 'update') {
|
|
1271
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1272
|
+
const additionalFields = this.getNodeParameter(
|
|
1273
|
+
'additionalFields',
|
|
1274
|
+
itemIndex,
|
|
1275
|
+
{}
|
|
1276
|
+
) as any;
|
|
1277
|
+
|
|
1278
|
+
// Convert comma-separated tags to array
|
|
1279
|
+
if (additionalFields.tags && typeof additionalFields.tags === 'string') {
|
|
1280
|
+
additionalFields.tags = additionalFields.tags
|
|
1281
|
+
.split(',')
|
|
1282
|
+
.map((t: string) => t.trim());
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// Parse custom attributes JSON
|
|
1286
|
+
if (additionalFields.custom_attributes && typeof additionalFields.custom_attributes === 'string') {
|
|
1287
|
+
try {
|
|
1288
|
+
additionalFields.custom_attributes = JSON.parse(
|
|
1289
|
+
additionalFields.custom_attributes
|
|
1290
|
+
);
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
throw new NodeOperationError(
|
|
1293
|
+
this.getNode(),
|
|
1294
|
+
'Invalid JSON in custom_attributes field',
|
|
1295
|
+
{ itemIndex }
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
return await client.contacts.update(contactId, additionalFields, options);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (operation === 'delete') {
|
|
1304
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1305
|
+
await client.contacts.delete(contactId, options);
|
|
1306
|
+
return { success: true, contactId };
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
if (operation === 'subscribe') {
|
|
1310
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1311
|
+
const listId = this.getNodeParameter('listId', itemIndex) as number;
|
|
1312
|
+
return await client.contacts.subscribe(contactId, listId, options);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if (operation === 'unsubscribe') {
|
|
1316
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1317
|
+
const listId = this.getNodeParameter('listId', itemIndex) as number;
|
|
1318
|
+
return await client.contacts.unsubscribe(contactId, listId, options);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
if (operation === 'bulkImport') {
|
|
1322
|
+
const contactsParam = this.getNodeParameter('contacts', itemIndex) as string;
|
|
1323
|
+
|
|
1324
|
+
let contacts: any[];
|
|
1325
|
+
try {
|
|
1326
|
+
contacts = typeof contactsParam === 'string'
|
|
1327
|
+
? JSON.parse(contactsParam)
|
|
1328
|
+
: contactsParam;
|
|
1329
|
+
|
|
1330
|
+
if (!Array.isArray(contacts)) {
|
|
1331
|
+
throw new Error('Contacts must be an array');
|
|
1332
|
+
}
|
|
1333
|
+
} catch (error: any) {
|
|
1334
|
+
throw new NodeOperationError(
|
|
1335
|
+
this.getNode(),
|
|
1336
|
+
`Invalid contacts JSON: ${error.message}`,
|
|
1337
|
+
{ itemIndex }
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// Validate that all contacts have email
|
|
1342
|
+
const invalidContacts = contacts.filter((c, i) => !c.email);
|
|
1343
|
+
if (invalidContacts.length > 0) {
|
|
1344
|
+
throw new NodeOperationError(
|
|
1345
|
+
this.getNode(),
|
|
1346
|
+
`All contacts must have an email field. Found ${invalidContacts.length} contacts without email.`,
|
|
1347
|
+
{ itemIndex }
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// Import contacts one by one and collect results
|
|
1352
|
+
const results = {
|
|
1353
|
+
total: contacts.length,
|
|
1354
|
+
successful: 0,
|
|
1355
|
+
failed: 0,
|
|
1356
|
+
errors: [] as Array<{ index: number; email: string; error: string }>,
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
for (let i = 0; i < contacts.length; i++) {
|
|
1360
|
+
try {
|
|
1361
|
+
await client.contacts.create(contacts[i], options);
|
|
1362
|
+
results.successful++;
|
|
1363
|
+
} catch (error: any) {
|
|
1364
|
+
results.failed++;
|
|
1365
|
+
results.errors.push({
|
|
1366
|
+
index: i,
|
|
1367
|
+
email: contacts[i].email,
|
|
1368
|
+
error: error.message || 'Unknown error',
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
return results;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
if (operation === 'search') {
|
|
1377
|
+
const searchQueryParam = this.getNodeParameter('searchQuery', itemIndex) as string;
|
|
1378
|
+
const searchOptions = this.getNodeParameter('searchOptions', itemIndex, {}) as any;
|
|
1379
|
+
|
|
1380
|
+
let searchQuery: any;
|
|
1381
|
+
try {
|
|
1382
|
+
searchQuery = typeof searchQueryParam === 'string'
|
|
1383
|
+
? JSON.parse(searchQueryParam)
|
|
1384
|
+
: searchQueryParam;
|
|
1385
|
+
} catch (error: any) {
|
|
1386
|
+
throw new NodeOperationError(
|
|
1387
|
+
this.getNode(),
|
|
1388
|
+
`Invalid search query JSON: ${error.message}`,
|
|
1389
|
+
{ itemIndex }
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// Build search parameters
|
|
1394
|
+
const searchParams: any = {
|
|
1395
|
+
...searchOptions,
|
|
1396
|
+
filter: searchQuery,
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
// Handle sort order
|
|
1400
|
+
if (searchOptions.sort && searchOptions.sort_order) {
|
|
1401
|
+
searchParams.sort = `${searchOptions.sort}:${searchOptions.sort_order}`;
|
|
1402
|
+
delete searchParams.sort_order;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
return await client.contacts.list(searchParams, options);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
if (operation === 'addTags') {
|
|
1409
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1410
|
+
const tagsString = this.getNodeParameter('tagsToManage', itemIndex) as string;
|
|
1411
|
+
const tags = tagsString.split(',').map((t: string) => t.trim()).filter((t: string) => t);
|
|
1412
|
+
|
|
1413
|
+
if (tags.length === 0) {
|
|
1414
|
+
throw new NodeOperationError(
|
|
1415
|
+
this.getNode(),
|
|
1416
|
+
'At least one tag must be provided',
|
|
1417
|
+
{ itemIndex }
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
return await client.contacts.addTags(contactId, tags, options);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if (operation === 'removeTags') {
|
|
1425
|
+
const contactId = this.getNodeParameter('contactId', itemIndex) as number;
|
|
1426
|
+
const tagsString = this.getNodeParameter('tagsToManage', itemIndex) as string;
|
|
1427
|
+
const tags = tagsString.split(',').map((t: string) => t.trim()).filter((t: string) => t);
|
|
1428
|
+
|
|
1429
|
+
if (tags.length === 0) {
|
|
1430
|
+
throw new NodeOperationError(
|
|
1431
|
+
this.getNode(),
|
|
1432
|
+
'At least one tag must be provided',
|
|
1433
|
+
{ itemIndex }
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
return await client.contacts.removeTags(contactId, tags, options);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
throw new NodeOperationError(
|
|
1441
|
+
this.getNode(),
|
|
1442
|
+
`The operation "${operation}" is not yet implemented for contact resource`,
|
|
1443
|
+
{ itemIndex }
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Execute list operations
|
|
1449
|
+
*/
|
|
1450
|
+
private async executeListOperation(
|
|
1451
|
+
this: IExecuteFunctions,
|
|
1452
|
+
client: CakemailClient,
|
|
1453
|
+
operation: string,
|
|
1454
|
+
itemIndex: number,
|
|
1455
|
+
options?: any
|
|
1456
|
+
): Promise<any> {
|
|
1457
|
+
if (operation === 'get') {
|
|
1458
|
+
const listId = this.getNodeParameter('listIdParam', itemIndex) as number;
|
|
1459
|
+
return await client.lists.get(listId, options);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
if (operation === 'list') {
|
|
1463
|
+
const limit = this.getNodeParameter('listLimit', itemIndex, 20) as number;
|
|
1464
|
+
return await client.lists.list({ per_page: limit }, options);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
if (operation === 'create') {
|
|
1468
|
+
const name = this.getNodeParameter('listName', itemIndex) as string;
|
|
1469
|
+
const additionalFields = this.getNodeParameter(
|
|
1470
|
+
'listAdditionalFields',
|
|
1471
|
+
itemIndex,
|
|
1472
|
+
{}
|
|
1473
|
+
) as any;
|
|
1474
|
+
|
|
1475
|
+
const createData = {
|
|
1476
|
+
name,
|
|
1477
|
+
...additionalFields,
|
|
1478
|
+
};
|
|
1479
|
+
|
|
1480
|
+
return await client.lists.create(createData, options);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
if (operation === 'update') {
|
|
1484
|
+
const listId = this.getNodeParameter('listIdParam', itemIndex) as number;
|
|
1485
|
+
const additionalFields = this.getNodeParameter(
|
|
1486
|
+
'listAdditionalFields',
|
|
1487
|
+
itemIndex,
|
|
1488
|
+
{}
|
|
1489
|
+
) as any;
|
|
1490
|
+
|
|
1491
|
+
return await client.lists.update(listId, additionalFields, options);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
if (operation === 'delete') {
|
|
1495
|
+
const listId = this.getNodeParameter('listIdParam', itemIndex) as number;
|
|
1496
|
+
await client.lists.delete(listId, options);
|
|
1497
|
+
return { success: true, listId };
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (operation === 'getStatistics') {
|
|
1501
|
+
const listId = this.getNodeParameter('listIdParam', itemIndex) as number;
|
|
1502
|
+
return await client.lists.getStatistics(listId, options);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
throw new NodeOperationError(
|
|
1506
|
+
this.getNode(),
|
|
1507
|
+
`The operation "${operation}" is not yet implemented for list resource`,
|
|
1508
|
+
{ itemIndex }
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Execute campaign operations
|
|
1514
|
+
*/
|
|
1515
|
+
private async executeCampaignOperation(
|
|
1516
|
+
this: IExecuteFunctions,
|
|
1517
|
+
client: CakemailClient,
|
|
1518
|
+
operation: string,
|
|
1519
|
+
itemIndex: number,
|
|
1520
|
+
options?: any
|
|
1521
|
+
): Promise<any> {
|
|
1522
|
+
throw new NodeOperationError(
|
|
1523
|
+
this.getNode(),
|
|
1524
|
+
`Campaign operations are not yet implemented`,
|
|
1525
|
+
{ itemIndex }
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
}
|