n8n-nodes-data-mapper 0.4.0 → 0.4.2
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/README.md +90 -82
- package/dist/nodes/DataMapper/DataMapper.node.js +331 -116
- package/dist/nodes/DataMapper/DataMapper.node.js.map +1 -1
- package/dist/nodes/DataMapper/profile.d.ts +2 -0
- package/dist/nodes/DataMapper/profile.js +124 -24
- package/dist/nodes/DataMapper/profile.js.map +1 -1
- package/dist/nodes/DataMapper/visual.d.ts +4 -1
- package/dist/nodes/DataMapper/visual.js +20 -6
- package/dist/nodes/DataMapper/visual.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -7,80 +7,7 @@ const profile_1 = require("./profile");
|
|
|
7
7
|
const visual_1 = require("./visual");
|
|
8
8
|
const xml_1 = require("./xml");
|
|
9
9
|
const show = (values) => ({ show: values });
|
|
10
|
-
const
|
|
11
|
-
{
|
|
12
|
-
displayName: 'Source',
|
|
13
|
-
name: 'sourceType',
|
|
14
|
-
type: 'options',
|
|
15
|
-
options: [
|
|
16
|
-
{ name: 'Input Field', value: 'field', description: 'Read a value from the input by path' },
|
|
17
|
-
{
|
|
18
|
-
name: 'From Source Profile',
|
|
19
|
-
value: 'profile',
|
|
20
|
-
description: 'Pick a field from the source profile',
|
|
21
|
-
},
|
|
22
|
-
{ name: 'Fixed Value', value: 'value', description: 'Write a fixed value or the result of an expression' },
|
|
23
|
-
],
|
|
24
|
-
default: 'field',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
displayName: 'Source Field Name or ID',
|
|
28
|
-
name: 'sourceField',
|
|
29
|
-
type: 'options',
|
|
30
|
-
typeOptions: { loadOptionsMethod: 'getSourceFields' },
|
|
31
|
-
default: '',
|
|
32
|
-
description: 'Field from the source profile. Fill the Source Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
33
|
-
displayOptions: show({ sourceType: ['profile'] }),
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
displayName: 'Source Path',
|
|
37
|
-
name: 'source',
|
|
38
|
-
type: 'string',
|
|
39
|
-
default: '',
|
|
40
|
-
placeholder: 'customer.addresses[*].city',
|
|
41
|
-
description: 'Dot path into the input. Use [0] for one array element, [*] for every element, or leave empty for the whole input.',
|
|
42
|
-
displayOptions: show({ sourceType: ['field'] }),
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
displayName: 'Value',
|
|
46
|
-
name: 'value',
|
|
47
|
-
type: 'string',
|
|
48
|
-
default: '',
|
|
49
|
-
description: 'Fixed text, or an expression such as {{ $now.toISO() }}',
|
|
50
|
-
displayOptions: show({ sourceType: ['value'] }),
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
displayName: 'Target',
|
|
54
|
-
name: 'targetMode',
|
|
55
|
-
type: 'options',
|
|
56
|
-
options: [
|
|
57
|
-
{ name: 'Custom Path', value: 'custom', description: 'Type the target path yourself' },
|
|
58
|
-
{
|
|
59
|
-
name: 'From Target Profile',
|
|
60
|
-
value: 'profile',
|
|
61
|
-
description: 'Pick a field from the uploaded target profile',
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
default: 'custom',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
displayName: 'Target Field Name or ID',
|
|
68
|
-
name: 'targetField',
|
|
69
|
-
type: 'options',
|
|
70
|
-
typeOptions: { loadOptionsMethod: 'getTargetFields' },
|
|
71
|
-
default: '',
|
|
72
|
-
description: 'Field from the target profile. Fill the Target Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
73
|
-
displayOptions: show({ targetMode: ['profile'] }),
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
displayName: 'Target Path',
|
|
77
|
-
name: 'target',
|
|
78
|
-
type: 'string',
|
|
79
|
-
default: '',
|
|
80
|
-
placeholder: 'shipping.cities[*]',
|
|
81
|
-
description: 'Where to write the value in the output. Each [*] matches a [*] in the source path, in order.',
|
|
82
|
-
displayOptions: show({ targetMode: ['custom'] }),
|
|
83
|
-
},
|
|
10
|
+
const transformFields = [
|
|
84
11
|
{
|
|
85
12
|
displayName: 'Transform',
|
|
86
13
|
name: 'transform',
|
|
@@ -165,6 +92,82 @@ const mappingFields = [
|
|
|
165
92
|
default: '',
|
|
166
93
|
displayOptions: show({ transform: ['lookup'], lookupFallback: ['default'] }),
|
|
167
94
|
},
|
|
95
|
+
];
|
|
96
|
+
const mappingFields = [
|
|
97
|
+
{
|
|
98
|
+
displayName: 'Source',
|
|
99
|
+
name: 'sourceType',
|
|
100
|
+
type: 'options',
|
|
101
|
+
options: [
|
|
102
|
+
{ name: 'Input Field', value: 'field', description: 'Read a value from the input by path' },
|
|
103
|
+
{
|
|
104
|
+
name: 'From Source Profile',
|
|
105
|
+
value: 'profile',
|
|
106
|
+
description: 'Pick a field from the source profile',
|
|
107
|
+
},
|
|
108
|
+
{ name: 'Fixed Value', value: 'value', description: 'Write a fixed value or the result of an expression' },
|
|
109
|
+
],
|
|
110
|
+
default: 'field',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Source Field Name or ID',
|
|
114
|
+
name: 'sourceField',
|
|
115
|
+
type: 'options',
|
|
116
|
+
typeOptions: { loadOptionsMethod: 'getSourceFields' },
|
|
117
|
+
default: '',
|
|
118
|
+
description: 'Field from the source profile. Fill the Source Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
119
|
+
displayOptions: show({ sourceType: ['profile'] }),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'Source Path',
|
|
123
|
+
name: 'source',
|
|
124
|
+
type: 'string',
|
|
125
|
+
default: '',
|
|
126
|
+
placeholder: 'customer.addresses[*].city',
|
|
127
|
+
description: 'Dot path into the input. Use [0] for one array element, [*] for every element, or leave empty for the whole input.',
|
|
128
|
+
displayOptions: show({ sourceType: ['field'] }),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
displayName: 'Value',
|
|
132
|
+
name: 'value',
|
|
133
|
+
type: 'string',
|
|
134
|
+
default: '',
|
|
135
|
+
description: 'Fixed text, or an expression such as {{ $now.toISO() }}',
|
|
136
|
+
displayOptions: show({ sourceType: ['value'] }),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
displayName: 'Target',
|
|
140
|
+
name: 'targetMode',
|
|
141
|
+
type: 'options',
|
|
142
|
+
options: [
|
|
143
|
+
{ name: 'Custom Path', value: 'custom', description: 'Type the target path yourself' },
|
|
144
|
+
{
|
|
145
|
+
name: 'From Target Profile',
|
|
146
|
+
value: 'profile',
|
|
147
|
+
description: 'Pick a field from the target profile',
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
default: 'profile',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
displayName: 'Target Field Name or ID',
|
|
154
|
+
name: 'targetField',
|
|
155
|
+
type: 'options',
|
|
156
|
+
typeOptions: { loadOptionsMethod: 'getTargetFields' },
|
|
157
|
+
default: '',
|
|
158
|
+
description: 'Field from the target profile. Fill the Target Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
159
|
+
displayOptions: show({ targetMode: ['profile'] }),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
displayName: 'Target Path',
|
|
163
|
+
name: 'target',
|
|
164
|
+
type: 'string',
|
|
165
|
+
default: '',
|
|
166
|
+
placeholder: 'shipping.cities[*]',
|
|
167
|
+
description: 'Where to write the value in the output. Each [*] matches a [*] in the source path, in order.',
|
|
168
|
+
displayOptions: show({ targetMode: ['custom'] }),
|
|
169
|
+
},
|
|
170
|
+
...transformFields,
|
|
168
171
|
{
|
|
169
172
|
displayName: 'If Source Is Missing',
|
|
170
173
|
name: 'ifMissing',
|
|
@@ -239,26 +242,6 @@ const v1Properties = [
|
|
|
239
242
|
description: 'XML is read with attributes under "$" and text under "_", so a path looks like order.$.ID or order.total._',
|
|
240
243
|
displayOptions: show({ inputSource: ['field', 'binary'] }),
|
|
241
244
|
},
|
|
242
|
-
{
|
|
243
|
-
displayName: 'Define Mappings',
|
|
244
|
-
name: 'definitionMode',
|
|
245
|
-
type: 'options',
|
|
246
|
-
noDataExpression: true,
|
|
247
|
-
options: [
|
|
248
|
-
{ name: 'Using Fields Below', value: 'fields' },
|
|
249
|
-
{
|
|
250
|
-
name: 'Auto-Map From Profile',
|
|
251
|
-
value: 'auto',
|
|
252
|
-
description: 'Match source fields to a target profile automatically by name',
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
name: 'Using JSON',
|
|
256
|
-
value: 'json',
|
|
257
|
-
description: 'Paste or reference a mapping definition, so one mapping can be reused across workflows',
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
default: 'fields',
|
|
261
|
-
},
|
|
262
245
|
{
|
|
263
246
|
displayName: 'Source Profile',
|
|
264
247
|
name: 'sourceProfile',
|
|
@@ -348,6 +331,91 @@ const v1Properties = [
|
|
|
348
331
|
description: 'The kind of profile you provided. Detect Automatically works for most profiles.',
|
|
349
332
|
displayOptions: show({ definitionMode: ['fields', 'auto'] }),
|
|
350
333
|
},
|
|
334
|
+
{
|
|
335
|
+
displayName: 'Define Mappings',
|
|
336
|
+
name: 'definitionMode',
|
|
337
|
+
type: 'options',
|
|
338
|
+
noDataExpression: true,
|
|
339
|
+
options: [
|
|
340
|
+
{ name: 'Map Each Column Manually', value: 'fields', description: 'Add a mapping per target field' },
|
|
341
|
+
{
|
|
342
|
+
name: 'Auto-Map From Profile',
|
|
343
|
+
value: 'auto',
|
|
344
|
+
description: 'Match source fields to a target profile automatically by name',
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'Using JSON',
|
|
348
|
+
value: 'json',
|
|
349
|
+
description: 'Paste or reference a mapping definition, so one mapping can be reused across workflows',
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
default: 'fields',
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
displayName: 'Mapping Layout',
|
|
356
|
+
name: 'mappingLayout',
|
|
357
|
+
type: 'options',
|
|
358
|
+
noDataExpression: true,
|
|
359
|
+
options: [
|
|
360
|
+
{ name: 'One Mapping at a Time', value: 'list', description: 'Add a mapping row for each field' },
|
|
361
|
+
{
|
|
362
|
+
name: 'Fill Target Fields',
|
|
363
|
+
value: 'fields',
|
|
364
|
+
description: 'List every target-profile field and set the source for each, like a visual mapper',
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
default: 'list',
|
|
368
|
+
displayOptions: show({ definitionMode: ['fields'] }),
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
displayName: 'Target Fields',
|
|
372
|
+
name: 'columnMapping',
|
|
373
|
+
type: 'resourceMapper',
|
|
374
|
+
noDataExpression: true,
|
|
375
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
376
|
+
required: true,
|
|
377
|
+
typeOptions: {
|
|
378
|
+
loadOptionsDependsOn: ['targetProfile', 'targetProfileFormat', 'sourceProfile', 'sourceProfileFormat'],
|
|
379
|
+
resourceMapper: {
|
|
380
|
+
resourceMapperMethod: 'getProfileFields',
|
|
381
|
+
mode: 'add',
|
|
382
|
+
valuesLabel: 'Target Fields',
|
|
383
|
+
fieldWords: { singular: 'target field', plural: 'target fields' },
|
|
384
|
+
addAllFields: true,
|
|
385
|
+
multiKeyMatch: false,
|
|
386
|
+
supportAutoMap: false,
|
|
387
|
+
noFieldsError: 'No target fields found. Paste a target profile above.',
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
displayOptions: show({ definitionMode: ['fields'], mappingLayout: ['fields'] }),
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
displayName: 'Transform a Field',
|
|
394
|
+
name: 'fieldTransforms',
|
|
395
|
+
placeholder: 'Add Field Transform',
|
|
396
|
+
type: 'fixedCollection',
|
|
397
|
+
typeOptions: { multipleValues: true },
|
|
398
|
+
default: {},
|
|
399
|
+
description: 'Convert or reformat the target fields above (trim, number, date, lookup, split/join, ...). Pick a field and its transform. Number and boolean types from the profile already convert on their own.',
|
|
400
|
+
displayOptions: show({ definitionMode: ['fields'], mappingLayout: ['fields'] }),
|
|
401
|
+
options: [
|
|
402
|
+
{
|
|
403
|
+
displayName: 'Field',
|
|
404
|
+
name: 'item',
|
|
405
|
+
values: [
|
|
406
|
+
{
|
|
407
|
+
displayName: 'Target Field Name or ID',
|
|
408
|
+
name: 'targetField',
|
|
409
|
+
type: 'options',
|
|
410
|
+
typeOptions: { loadOptionsMethod: 'getTargetFields' },
|
|
411
|
+
default: '',
|
|
412
|
+
description: 'Which target field to transform. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
413
|
+
},
|
|
414
|
+
...transformFields,
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
},
|
|
351
419
|
{
|
|
352
420
|
displayName: 'Mappings',
|
|
353
421
|
name: 'mappings',
|
|
@@ -356,7 +424,7 @@ const v1Properties = [
|
|
|
356
424
|
typeOptions: { multipleValues: true, sortable: true },
|
|
357
425
|
default: {},
|
|
358
426
|
description: 'Applied in order; a later mapping to the same target overwrites an earlier one',
|
|
359
|
-
displayOptions: show({ definitionMode: ['fields'] }),
|
|
427
|
+
displayOptions: show({ definitionMode: ['fields'], mappingLayout: ['list'] }),
|
|
360
428
|
options: [{ displayName: 'Mapping', name: 'mapping', values: mappingFields }],
|
|
361
429
|
},
|
|
362
430
|
{
|
|
@@ -365,7 +433,7 @@ const v1Properties = [
|
|
|
365
433
|
type: 'boolean',
|
|
366
434
|
default: false,
|
|
367
435
|
description: 'Whether to also fill any target-profile fields you have not mapped above, by matching them to source fields by name',
|
|
368
|
-
displayOptions: show({ definitionMode: ['fields'] }),
|
|
436
|
+
displayOptions: show({ definitionMode: ['fields'], mappingLayout: ['list'] }),
|
|
369
437
|
},
|
|
370
438
|
{
|
|
371
439
|
displayName: 'Mapping Definition',
|
|
@@ -381,7 +449,7 @@ const v1Properties = [
|
|
|
381
449
|
type: 'collection',
|
|
382
450
|
placeholder: 'Add option',
|
|
383
451
|
default: {},
|
|
384
|
-
displayOptions: show({ definitionMode: ['auto'] }),
|
|
452
|
+
displayOptions: show({ definitionMode: ['fields', 'auto'] }),
|
|
385
453
|
options: [
|
|
386
454
|
{
|
|
387
455
|
displayName: 'Add Auto-Map Report',
|
|
@@ -397,6 +465,15 @@ const v1Properties = [
|
|
|
397
465
|
default: false,
|
|
398
466
|
description: 'Whether field names must match in the same letter case',
|
|
399
467
|
},
|
|
468
|
+
{
|
|
469
|
+
displayName: 'Custom Synonyms',
|
|
470
|
+
name: 'synonyms',
|
|
471
|
+
type: 'string',
|
|
472
|
+
typeOptions: { rows: 3 },
|
|
473
|
+
default: '',
|
|
474
|
+
placeholder: 'menge=quantity\nartikel=article',
|
|
475
|
+
description: 'Extra name matches for auto-map, one source=target per line (or a JSON object). Qty=quantity, Amt=amount and other common abbreviations are already built in.',
|
|
476
|
+
},
|
|
400
477
|
{
|
|
401
478
|
displayName: 'Match',
|
|
402
479
|
name: 'strategy',
|
|
@@ -454,6 +531,8 @@ const v1Properties = [
|
|
|
454
531
|
type: 'options',
|
|
455
532
|
noDataExpression: true,
|
|
456
533
|
options: [
|
|
534
|
+
{ name: 'CSV', value: 'csv', description: 'Output the mapped data as CSV text in a field' },
|
|
535
|
+
{ name: 'CSV File', value: 'csvFile', description: 'Output the mapped data as a CSV file' },
|
|
457
536
|
{ name: 'JSON', value: 'json', description: 'Output the mapped data as the item' },
|
|
458
537
|
{ name: 'JSON File', value: 'jsonFile', description: 'Output the mapped data as a JSON file' },
|
|
459
538
|
{ name: 'XML', value: 'xml', description: 'Output the mapped data as XML text in a field' },
|
|
@@ -465,9 +544,10 @@ const v1Properties = [
|
|
|
465
544
|
displayName: 'Put Output in Field',
|
|
466
545
|
name: 'outputField',
|
|
467
546
|
type: 'string',
|
|
468
|
-
default: '
|
|
469
|
-
|
|
470
|
-
|
|
547
|
+
default: '',
|
|
548
|
+
placeholder: 'xml',
|
|
549
|
+
description: 'Field for the XML or CSV text. Leave empty to use "xml" or "csv".',
|
|
550
|
+
displayOptions: show({ outputFormat: ['xml', 'csv'] }),
|
|
471
551
|
},
|
|
472
552
|
{
|
|
473
553
|
displayName: 'Put Output File in Field',
|
|
@@ -475,7 +555,7 @@ const v1Properties = [
|
|
|
475
555
|
type: 'string',
|
|
476
556
|
default: 'data',
|
|
477
557
|
required: true,
|
|
478
|
-
displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
|
|
558
|
+
displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile', 'csvFile'] }),
|
|
479
559
|
},
|
|
480
560
|
{
|
|
481
561
|
displayName: 'File Name',
|
|
@@ -484,7 +564,7 @@ const v1Properties = [
|
|
|
484
564
|
default: '',
|
|
485
565
|
placeholder: 'output.xml',
|
|
486
566
|
description: 'Leave empty to name the file output with the matching extension',
|
|
487
|
-
displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
|
|
567
|
+
displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile', 'csvFile'] }),
|
|
488
568
|
},
|
|
489
569
|
{
|
|
490
570
|
displayName: 'Root Element',
|
|
@@ -503,6 +583,72 @@ const v1Properties = [
|
|
|
503
583
|
description: 'Whether to start the XML with <?xml version="1.0" encoding="UTF-8"?>',
|
|
504
584
|
displayOptions: show({ outputFormat: ['xml', 'xmlFile'] }),
|
|
505
585
|
},
|
|
586
|
+
{
|
|
587
|
+
displayName: 'CSV Options',
|
|
588
|
+
name: 'csvOptions',
|
|
589
|
+
type: 'collection',
|
|
590
|
+
placeholder: 'Add option',
|
|
591
|
+
default: {},
|
|
592
|
+
displayOptions: show({ outputFormat: ['csv', 'csvFile'] }),
|
|
593
|
+
options: [
|
|
594
|
+
{ displayName: 'Delimiter', name: 'csvDelimiter', type: 'string', default: ',' },
|
|
595
|
+
{
|
|
596
|
+
displayName: 'Escape Formulas',
|
|
597
|
+
name: 'csvEscapeFormulas',
|
|
598
|
+
type: 'boolean',
|
|
599
|
+
default: false,
|
|
600
|
+
description: "Whether to put ' before values starting with =, +, -, @, tab or CR so spreadsheets don't run them as formulas (CSV injection)",
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
displayName: 'Header Row',
|
|
604
|
+
name: 'csvHeader',
|
|
605
|
+
type: 'boolean',
|
|
606
|
+
default: true,
|
|
607
|
+
description: 'Whether to start the CSV with a row of column names',
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
displayName: 'Line Ending',
|
|
611
|
+
name: 'csvLineEnding',
|
|
612
|
+
type: 'options',
|
|
613
|
+
options: [
|
|
614
|
+
{ name: 'LF', value: 'lf' },
|
|
615
|
+
{ name: 'CRLF', value: 'crlf' },
|
|
616
|
+
],
|
|
617
|
+
default: 'lf',
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
displayName: 'One CSV for All Items',
|
|
621
|
+
name: 'csvCombine',
|
|
622
|
+
type: 'boolean',
|
|
623
|
+
default: true,
|
|
624
|
+
description: 'Whether to put every item into one CSV document, instead of one CSV per item',
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
displayName: 'Options',
|
|
630
|
+
name: 'fieldOptions',
|
|
631
|
+
type: 'collection',
|
|
632
|
+
placeholder: 'Add option',
|
|
633
|
+
default: {},
|
|
634
|
+
displayOptions: show({ definitionMode: ['fields'], mappingLayout: ['fields'] }),
|
|
635
|
+
options: [
|
|
636
|
+
{
|
|
637
|
+
displayName: 'Convert Types From Profile',
|
|
638
|
+
name: 'convertTypes',
|
|
639
|
+
type: 'boolean',
|
|
640
|
+
default: true,
|
|
641
|
+
description: 'Whether to turn values into numbers or booleans where the profile says so. Values that cannot be converted are kept as they are.',
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
displayName: 'Keep Empty Values',
|
|
645
|
+
name: 'keepEmpty',
|
|
646
|
+
type: 'boolean',
|
|
647
|
+
default: false,
|
|
648
|
+
description: 'Whether to write target fields whose value is empty, instead of leaving them out',
|
|
649
|
+
},
|
|
650
|
+
],
|
|
651
|
+
},
|
|
506
652
|
];
|
|
507
653
|
function forVersions(properties, versions) {
|
|
508
654
|
return properties.map((property) => {
|
|
@@ -524,9 +670,9 @@ class DataMapper {
|
|
|
524
670
|
icon: { light: 'file:mapper.svg', dark: 'file:mapper.dark.svg' },
|
|
525
671
|
group: ['transform'],
|
|
526
672
|
version: [1, 2],
|
|
527
|
-
defaultVersion:
|
|
673
|
+
defaultVersion: 1,
|
|
528
674
|
subtitle: '={{ $parameter["targetFormat"] ? "Map to " + $parameter["targetFormat"].toUpperCase() : $parameter["definitionMode"] === "auto" ? "Auto-map from profile" : $parameter["definitionMode"] === "json" ? "JSON definition" : "Field mappings" }}',
|
|
529
|
-
description: '
|
|
675
|
+
description: 'Map and transform data between structures and output XML, JSON or CSV',
|
|
530
676
|
defaults: {
|
|
531
677
|
name: 'Data Mapper',
|
|
532
678
|
},
|
|
@@ -599,18 +745,30 @@ class DataMapper {
|
|
|
599
745
|
const items = this.getInputData();
|
|
600
746
|
const returnData = [];
|
|
601
747
|
const profileCache = new Map();
|
|
748
|
+
const format0 = this.getNodeParameter('outputFormat', 0, 'json');
|
|
749
|
+
const combineCsv = (format0 === 'csv' || format0 === 'csvFile') &&
|
|
750
|
+
this.getNodeParameter('csvOptions.csvCombine', 0, true) !== false;
|
|
751
|
+
const csvRecords = [];
|
|
752
|
+
let csvProfile;
|
|
602
753
|
for (let i = 0; i < items.length; i++) {
|
|
603
754
|
try {
|
|
604
755
|
const input = await readInput(this, items[i], i);
|
|
605
|
-
const { rules, report } = await buildRules(this, i, input, profileCache);
|
|
756
|
+
const { rules, report, profile, order } = await buildRules(this, i, input, profileCache);
|
|
606
757
|
const unmapped = this.getNodeParameter('unmapped', i);
|
|
607
758
|
const start = unmapped === 'keep' && input && typeof input === 'object' && !Array.isArray(input)
|
|
608
759
|
? structuredClone(input)
|
|
609
760
|
: {};
|
|
610
|
-
|
|
761
|
+
let mapped = (0, mapping_1.applyMappings)(input, rules, start);
|
|
762
|
+
if (order && profile)
|
|
763
|
+
mapped = (0, visual_1.orderByProfile)(mapped, profile);
|
|
611
764
|
if (report)
|
|
612
765
|
mapped._autoMap = report;
|
|
613
|
-
|
|
766
|
+
if (combineCsv) {
|
|
767
|
+
csvRecords.push({ mapped, index: i });
|
|
768
|
+
csvProfile !== null && csvProfile !== void 0 ? csvProfile : (csvProfile = profile);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
returnData.push(await writeOutput(this, mapped, i, profile));
|
|
614
772
|
}
|
|
615
773
|
catch (error) {
|
|
616
774
|
if (this.continueOnFail()) {
|
|
@@ -629,6 +787,10 @@ class DataMapper {
|
|
|
629
787
|
});
|
|
630
788
|
}
|
|
631
789
|
}
|
|
790
|
+
if (combineCsv && csvRecords.length > 0) {
|
|
791
|
+
const output = await csvOutput(this, csvRecords.map((r) => r.mapped), csvProfile, 0, csvRecords.map((r) => ({ item: r.index })));
|
|
792
|
+
returnData.push(output);
|
|
793
|
+
}
|
|
632
794
|
return [returnData];
|
|
633
795
|
}
|
|
634
796
|
}
|
|
@@ -713,6 +875,7 @@ function readAutoOptions(ctx, itemIndex) {
|
|
|
713
875
|
caseSensitive: o.caseSensitive === true,
|
|
714
876
|
reuseSources: o.reuseSources === true,
|
|
715
877
|
unmatched: (_c = o.unmatched) !== null && _c !== void 0 ? _c : 'skip',
|
|
878
|
+
synonyms: (0, profile_1.parseSynonyms)(o.synonyms),
|
|
716
879
|
};
|
|
717
880
|
}
|
|
718
881
|
function uiMappingToRule(m, ctx, itemIndex) {
|
|
@@ -750,7 +913,28 @@ function uiMappingToRule(m, ctx, itemIndex) {
|
|
|
750
913
|
default: m.defaultValue,
|
|
751
914
|
};
|
|
752
915
|
}
|
|
916
|
+
function applyFieldTransforms(ctx, itemIndex, rules) {
|
|
917
|
+
const items = ctx.getNodeParameter('fieldTransforms.item', itemIndex, []).filter(Boolean);
|
|
918
|
+
for (const it of items) {
|
|
919
|
+
if (!it.targetField || !it.transform || it.transform === 'none')
|
|
920
|
+
continue;
|
|
921
|
+
for (const rule of rules) {
|
|
922
|
+
if (rule.target !== it.targetField)
|
|
923
|
+
continue;
|
|
924
|
+
rule.transform = it.transform;
|
|
925
|
+
rule.separator = it.separator;
|
|
926
|
+
rule.decimals = it.decimals;
|
|
927
|
+
rule.dateInputFormat = it.dateInputFormat;
|
|
928
|
+
rule.dateOutputFormat = it.dateOutputFormat;
|
|
929
|
+
rule.lookup = it.lookupTable;
|
|
930
|
+
rule.lookupFallback = it.lookupFallback;
|
|
931
|
+
rule.lookupDefault = it.lookupDefault;
|
|
932
|
+
rule.lenient = true;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
753
936
|
async function buildRules(ctx, itemIndex, input, cache) {
|
|
937
|
+
var _a, _b;
|
|
754
938
|
const mode = ctx.getNodeParameter('definitionMode', itemIndex);
|
|
755
939
|
if (mode === 'json')
|
|
756
940
|
return { rules: (0, mapping_1.parseDefinition)(ctx.getNodeParameter('mappingJson', itemIndex)) };
|
|
@@ -770,27 +954,58 @@ async function buildRules(ctx, itemIndex, input, cache) {
|
|
|
770
954
|
}
|
|
771
955
|
return built;
|
|
772
956
|
}
|
|
957
|
+
if (ctx.getNodeParameter('mappingLayout', itemIndex, 'list') === 'fields') {
|
|
958
|
+
const raw = ((_a = ctx.getNodeParameter('columnMapping.value', itemIndex, {}, {
|
|
959
|
+
rawExpressions: true,
|
|
960
|
+
skipValidation: true,
|
|
961
|
+
})) !== null && _a !== void 0 ? _a : {});
|
|
962
|
+
const rm = ctx.getNodeParameter('columnMapping', itemIndex, undefined, { skipValidation: true });
|
|
963
|
+
const resolved = ((_b = rm === null || rm === void 0 ? void 0 : rm.value) !== null && _b !== void 0 ? _b : {});
|
|
964
|
+
const fo = ctx.getNodeParameter('fieldOptions', itemIndex, {});
|
|
965
|
+
const rules = (0, visual_1.buildDropRules)(raw, resolved, profile, {
|
|
966
|
+
convertTypes: fo.convertTypes !== false,
|
|
967
|
+
keepEmpty: fo.keepEmpty === true,
|
|
968
|
+
});
|
|
969
|
+
applyFieldTransforms(ctx, itemIndex, rules);
|
|
970
|
+
return { rules, profile, order: true };
|
|
971
|
+
}
|
|
773
972
|
const entries = ctx.getNodeParameter('mappings.mapping', itemIndex, []).filter(Boolean);
|
|
774
973
|
const rules = entries.map((m) => uiMappingToRule(m, ctx, itemIndex));
|
|
775
974
|
if (profile && ctx.getNodeParameter('autoMapRemaining', itemIndex, false) === true) {
|
|
776
975
|
const covered = new Set(rules.map((r) => r.target));
|
|
777
976
|
const remaining = { ...profile, fields: profile.fields.filter((f) => !covered.has(f.path)) };
|
|
778
|
-
const result = (0, profile_1.autoMap)((0, profile_1.flattenSource)(input), remaining,
|
|
779
|
-
threshold: 0.6,
|
|
780
|
-
strategy: 'fuzzy',
|
|
781
|
-
caseSensitive: false,
|
|
782
|
-
reuseSources: false,
|
|
783
|
-
unmatched: 'skip',
|
|
784
|
-
});
|
|
977
|
+
const result = (0, profile_1.autoMap)((0, profile_1.flattenSource)(input), remaining, readAutoOptions(ctx, itemIndex));
|
|
785
978
|
rules.push(...result.rules);
|
|
979
|
+
if (ctx.getNodeParameter('autoOptions.report', itemIndex, false) === true) {
|
|
980
|
+
return {
|
|
981
|
+
rules,
|
|
982
|
+
profile,
|
|
983
|
+
report: { matched: result.matched, unmatched: result.unmatched },
|
|
984
|
+
};
|
|
985
|
+
}
|
|
786
986
|
}
|
|
787
987
|
return { rules, profile };
|
|
788
988
|
}
|
|
789
|
-
async function
|
|
989
|
+
async function csvOutput(ctx, records, profile, itemIndex, pairedItem) {
|
|
990
|
+
const o = ctx.getNodeParameter('csvOptions', itemIndex, {});
|
|
991
|
+
const text = (0, visual_1.csvText)(records, profile, o);
|
|
992
|
+
if (ctx.getNodeParameter('outputFormat', itemIndex) === 'csvFile') {
|
|
993
|
+
const field = ctx.getNodeParameter('outputBinaryField', itemIndex);
|
|
994
|
+
const fileName = ctx.getNodeParameter('fileName', itemIndex, '') || 'output.csv';
|
|
995
|
+
const binary = await ctx.helpers.prepareBinaryData(Buffer.from(text, 'utf8'), fileName, 'text/csv');
|
|
996
|
+
return { json: {}, binary: { [field]: binary }, pairedItem };
|
|
997
|
+
}
|
|
998
|
+
const field = ctx.getNodeParameter('outputField', itemIndex, '') || 'csv';
|
|
999
|
+
return { json: { [field]: text }, pairedItem };
|
|
1000
|
+
}
|
|
1001
|
+
async function writeOutput(ctx, mapped, itemIndex, profile) {
|
|
790
1002
|
const format = ctx.getNodeParameter('outputFormat', itemIndex);
|
|
791
1003
|
const pairedItem = { item: itemIndex };
|
|
792
1004
|
if (format === 'json')
|
|
793
1005
|
return { json: mapped, pairedItem };
|
|
1006
|
+
if (format === 'csv' || format === 'csvFile') {
|
|
1007
|
+
return csvOutput(ctx, [mapped], profile, itemIndex, [pairedItem]);
|
|
1008
|
+
}
|
|
794
1009
|
if (format === 'jsonFile') {
|
|
795
1010
|
const field = ctx.getNodeParameter('outputBinaryField', itemIndex);
|
|
796
1011
|
const fileName = ctx.getNodeParameter('fileName', itemIndex, '') || 'output.json';
|
|
@@ -804,7 +1019,7 @@ async function writeOutput(ctx, mapped, itemIndex) {
|
|
|
804
1019
|
textKey: '_',
|
|
805
1020
|
});
|
|
806
1021
|
if (format === 'xml') {
|
|
807
|
-
const field = ctx.getNodeParameter('outputField', itemIndex);
|
|
1022
|
+
const field = ctx.getNodeParameter('outputField', itemIndex, '') || 'xml';
|
|
808
1023
|
return { json: { [field]: xml }, pairedItem };
|
|
809
1024
|
}
|
|
810
1025
|
const field = ctx.getNodeParameter('outputBinaryField', itemIndex);
|