@limetech/n8n-nodes-lime 2.6.0 → 2.7.0-dev.1
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/CHANGELOG.md +12 -0
- package/dist/nodes/lime-crm/LimeCrmNode.node.d.ts +4 -1
- package/dist/nodes/lime-crm/LimeCrmNode.node.js +3 -0
- package/dist/nodes/lime-crm/LimeCrmNode.node.js.map +1 -1
- package/dist/nodes/lime-crm/methods/getLimetypeProperties.d.ts +2 -0
- package/dist/nodes/lime-crm/methods/getLimetypeProperties.js +43 -0
- package/dist/nodes/lime-crm/methods/getLimetypeProperties.js.map +1 -1
- package/dist/nodes/lime-crm/methods/index.d.ts +2 -2
- package/dist/nodes/lime-crm/methods/index.js +4 -1
- package/dist/nodes/lime-crm/methods/index.js.map +1 -1
- package/dist/nodes/lime-crm/methods/resourceMapping.d.ts +1 -0
- package/dist/nodes/lime-crm/methods/resourceMapping.js +46 -0
- package/dist/nodes/lime-crm/methods/resourceMapping.js.map +1 -1
- package/dist/nodes/lime-crm/models/limetype.d.ts +1 -0
- package/dist/nodes/lime-crm/resources/data/index.d.ts +1 -1
- package/dist/nodes/lime-crm/resources/data/index.js +20 -0
- package/dist/nodes/lime-crm/resources/data/index.js.map +1 -1
- package/dist/nodes/lime-crm/resources/data/operations/bulkImportCommons.d.ts +5 -0
- package/dist/nodes/lime-crm/resources/data/operations/bulkImportCommons.js +230 -0
- package/dist/nodes/lime-crm/resources/data/operations/bulkImportCommons.js.map +1 -0
- package/dist/nodes/lime-crm/resources/data/operations/createManyObjects.operation.d.ts +9 -0
- package/dist/nodes/lime-crm/resources/data/operations/createManyObjects.operation.js +16 -0
- package/dist/nodes/lime-crm/resources/data/operations/createManyObjects.operation.js.map +1 -0
- package/dist/nodes/lime-crm/resources/data/operations/createOrUpdateManyObjects.operation.d.ts +9 -0
- package/dist/nodes/lime-crm/resources/data/operations/createOrUpdateManyObjects.operation.js +16 -0
- package/dist/nodes/lime-crm/resources/data/operations/createOrUpdateManyObjects.operation.js.map +1 -0
- package/dist/nodes/lime-crm/resources/data/operations/deprecated-startBulkImport.operation.d.ts +9 -0
- package/dist/nodes/lime-crm/resources/data/operations/deprecated-startBulkImport.operation.js +241 -0
- package/dist/nodes/lime-crm/resources/data/operations/deprecated-startBulkImport.operation.js.map +1 -0
- package/dist/nodes/lime-crm/resources/data/operations/index.d.ts +4 -0
- package/dist/nodes/lime-crm/resources/data/operations/index.js +5 -1
- package/dist/nodes/lime-crm/resources/data/operations/index.js.map +1 -1
- package/dist/nodes/lime-crm/resources/data/operations/updateManyObjects.operation.d.ts +9 -0
- package/dist/nodes/lime-crm/resources/data/operations/updateManyObjects.operation.js +16 -0
- package/dist/nodes/lime-crm/resources/data/operations/updateManyObjects.operation.js.map +1 -0
- package/dist/nodes/lime-crm/transport/bulkimport.d.ts +41 -0
- package/dist/nodes/lime-crm/transport/bulkimport.js +86 -0
- package/dist/nodes/lime-crm/transport/bulkimport.js.map +1 -0
- package/dist/nodes/lime-crm/transport/index.d.ts +1 -0
- package/dist/nodes/lime-crm/transport/index.js +6 -1
- package/dist/nodes/lime-crm/transport/index.js.map +1 -1
- package/dist/nodes/lime-crm/transport/limetypes.js +15 -4
- package/dist/nodes/lime-crm/transport/limetypes.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/nodes/lime-crm/LimeCrmNode.node.ts +6 -0
- package/nodes/lime-crm/methods/getLimetypeProperties.ts +84 -0
- package/nodes/lime-crm/methods/index.ts +3 -0
- package/nodes/lime-crm/methods/resourceMapping.ts +76 -0
- package/nodes/lime-crm/models/limetype.ts +1 -0
- package/nodes/lime-crm/resources/data/index.ts +34 -1
- package/nodes/lime-crm/resources/data/operations/bulkImportCommons.ts +323 -0
- package/nodes/lime-crm/resources/data/operations/createManyObjects.operation.ts +44 -0
- package/nodes/lime-crm/resources/data/operations/createOrUpdateManyObjects.operation.ts +44 -0
- package/nodes/lime-crm/resources/data/operations/deprecated-startBulkImport.operation.ts +364 -0
- package/nodes/lime-crm/resources/data/operations/index.ts +17 -0
- package/nodes/lime-crm/resources/data/operations/updateManyObjects.operation.ts +44 -0
- package/nodes/lime-crm/transport/bulkimport.ts +271 -0
- package/nodes/lime-crm/transport/index.ts +8 -0
- package/nodes/lime-crm/transport/limetypes.ts +26 -8
- package/package.json +1 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IDataObject,
|
|
3
|
+
IExecuteFunctions,
|
|
4
|
+
INode,
|
|
5
|
+
INodeProperties,
|
|
6
|
+
LoggerProxy as Logger,
|
|
7
|
+
NodeOperationError,
|
|
8
|
+
} from 'n8n-workflow';
|
|
9
|
+
import { DATA_RESOURCE } from '../../../models';
|
|
10
|
+
import {
|
|
11
|
+
BulkImportJobPayload,
|
|
12
|
+
createBulkImportJob,
|
|
13
|
+
uploadBulkImportData,
|
|
14
|
+
} from '../../../transport';
|
|
15
|
+
import {
|
|
16
|
+
BulkImportPayloadObject,
|
|
17
|
+
waitForBulkImportJob,
|
|
18
|
+
} from '../../../transport/bulkimport';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Description and metadata for the "Bulk import objects" operation in Lime CRM.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const description = {
|
|
26
|
+
name: 'Create or update many objects (deprecated)',
|
|
27
|
+
value: 'startBulkImport',
|
|
28
|
+
description:
|
|
29
|
+
'Create or update multiple objects via a bulk import. Skipping business logic.',
|
|
30
|
+
action: 'Create or update many objects (deprecated)',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Node properties for the "Bulk import objects" operation.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} limetype - The type of entity to update. Loaded from available Limetypes
|
|
37
|
+
* @param {string} id - The ID of the object to update
|
|
38
|
+
* @param {'simple' | 'json'} inputType - How the object data is provided: 'simple' for form inputs, 'json' for raw JSON
|
|
39
|
+
* @param {IDataObject} simpleFields - Used if `inputType` is 'simple'. List of field name/value pairs to update
|
|
40
|
+
* @param {string} jsonData - Used if `inputType` is 'json'. Full object data in JSON format
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export const properties: INodeProperties[] = [
|
|
45
|
+
{
|
|
46
|
+
displayName:
|
|
47
|
+
'<h1>Deprecated</h1>' +
|
|
48
|
+
'This action is deprecated and will be removed very soon. ' +
|
|
49
|
+
'Please use the new "Create or Update Many Objects (Alpha)" operation instead.',
|
|
50
|
+
name: 'deprecatedNotice',
|
|
51
|
+
type: 'callout',
|
|
52
|
+
displayOptions: {
|
|
53
|
+
show: {
|
|
54
|
+
resource: [DATA_RESOURCE],
|
|
55
|
+
operation: ['startBulkImport'],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
default: undefined,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
displayName: 'Limetype',
|
|
62
|
+
name: 'limetype',
|
|
63
|
+
type: 'options',
|
|
64
|
+
typeOptions: {
|
|
65
|
+
loadOptionsMethod: 'getLimetypes',
|
|
66
|
+
},
|
|
67
|
+
required: true,
|
|
68
|
+
default: '',
|
|
69
|
+
description: 'The type of object to update',
|
|
70
|
+
displayOptions: {
|
|
71
|
+
show: {
|
|
72
|
+
resource: [DATA_RESOURCE],
|
|
73
|
+
operation: ['startBulkImport'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
displayName: 'Matching Property',
|
|
79
|
+
name: 'matchingProperty',
|
|
80
|
+
type: 'options',
|
|
81
|
+
typeOptions: {
|
|
82
|
+
loadOptionsMethod: 'getNoHasManyProperties',
|
|
83
|
+
loadOptionsDependsOn: ['limetype'],
|
|
84
|
+
},
|
|
85
|
+
required: true,
|
|
86
|
+
default: '',
|
|
87
|
+
description:
|
|
88
|
+
'The property to use to match existing objects. Must be a unique property.',
|
|
89
|
+
displayOptions: {
|
|
90
|
+
show: {
|
|
91
|
+
resource: [DATA_RESOURCE],
|
|
92
|
+
operation: ['startBulkImport'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
displayName: 'Input Type',
|
|
98
|
+
name: 'inputType',
|
|
99
|
+
type: 'options',
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
name: 'Simple Fields',
|
|
103
|
+
value: 'simple',
|
|
104
|
+
description: 'Define fields using the UI',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'JSON Object',
|
|
108
|
+
value: 'json',
|
|
109
|
+
description: 'Define fields using JSON',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
default: 'simple',
|
|
113
|
+
description: 'How to input the data',
|
|
114
|
+
displayOptions: {
|
|
115
|
+
show: {
|
|
116
|
+
resource: [DATA_RESOURCE],
|
|
117
|
+
operation: ['startBulkImport'],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
displayName: 'Data',
|
|
123
|
+
name: 'jsonData',
|
|
124
|
+
type: 'json',
|
|
125
|
+
default:
|
|
126
|
+
'{\n "name": "Updated Company Name",\n "phone": "+987654321"\n}',
|
|
127
|
+
description:
|
|
128
|
+
'Key-value pairs for fields to update. Property names must match the Lime CRM field names.',
|
|
129
|
+
typeOptions: {
|
|
130
|
+
alwaysOpenEditWindow: true,
|
|
131
|
+
},
|
|
132
|
+
displayOptions: {
|
|
133
|
+
show: {
|
|
134
|
+
resource: [DATA_RESOURCE],
|
|
135
|
+
operation: ['startBulkImport'],
|
|
136
|
+
inputType: ['json'],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
displayName: 'Fields',
|
|
142
|
+
name: 'simpleFields',
|
|
143
|
+
placeholder: 'Add Field',
|
|
144
|
+
type: 'fixedCollection',
|
|
145
|
+
typeOptions: {
|
|
146
|
+
multipleValues: true,
|
|
147
|
+
sortable: true,
|
|
148
|
+
},
|
|
149
|
+
default: {},
|
|
150
|
+
displayOptions: {
|
|
151
|
+
show: {
|
|
152
|
+
resource: [DATA_RESOURCE],
|
|
153
|
+
operation: ['startBulkImport'],
|
|
154
|
+
inputType: ['simple'],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
options: [
|
|
158
|
+
{
|
|
159
|
+
name: 'field',
|
|
160
|
+
displayName: 'Field',
|
|
161
|
+
values: [
|
|
162
|
+
{
|
|
163
|
+
displayName: 'Field Name',
|
|
164
|
+
name: 'fieldName',
|
|
165
|
+
type: 'options',
|
|
166
|
+
typeOptions: {
|
|
167
|
+
loadOptionsMethod: 'getNoHasManyProperties',
|
|
168
|
+
loadOptionsDependsOn: ['limetype'],
|
|
169
|
+
},
|
|
170
|
+
default: '',
|
|
171
|
+
description: 'The name of the field',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
displayName: 'Field Value',
|
|
175
|
+
name: 'fieldValue',
|
|
176
|
+
type: 'string',
|
|
177
|
+
default: '',
|
|
178
|
+
description: 'The value of the field',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
displayName: 'Relation Lookup',
|
|
182
|
+
name: 'fieldNameDotLookup',
|
|
183
|
+
type: 'options',
|
|
184
|
+
typeOptions: {
|
|
185
|
+
loadOptionsMethod:
|
|
186
|
+
'getRelationPropertiesWithLookupField',
|
|
187
|
+
loadOptionsDependsOn: ['limetype'],
|
|
188
|
+
},
|
|
189
|
+
default: '',
|
|
190
|
+
description:
|
|
191
|
+
'For relation fields: select which property on the related object to use for matching. Leave empty for non-relation fields.',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Extract properties to import from JSON input.
|
|
201
|
+
* @param jsonData
|
|
202
|
+
*/
|
|
203
|
+
function getPropertiesFromJson(jsonData: string): string[] {
|
|
204
|
+
const parsed = JSON.parse(jsonData);
|
|
205
|
+
return Object.keys(parsed);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Extract properties to import from simple fields input.
|
|
210
|
+
* Validates that relation lookups match their field names.
|
|
211
|
+
* @param simpleFields
|
|
212
|
+
* @param getNode
|
|
213
|
+
*/
|
|
214
|
+
function getPropertiesFromSimpleFields(
|
|
215
|
+
simpleFields: IDataObject[],
|
|
216
|
+
getNode: () => INode
|
|
217
|
+
): string[] {
|
|
218
|
+
const propertiesToImport: string[] = [];
|
|
219
|
+
|
|
220
|
+
for (const field of simpleFields) {
|
|
221
|
+
const fieldName = field.fieldName as string;
|
|
222
|
+
const fieldNameDotLookup = field.fieldNameDotLookup as string;
|
|
223
|
+
|
|
224
|
+
if (fieldNameDotLookup) {
|
|
225
|
+
const [relationField] = fieldNameDotLookup.split('.');
|
|
226
|
+
|
|
227
|
+
if (relationField !== fieldName) {
|
|
228
|
+
throw new NodeOperationError(
|
|
229
|
+
getNode(),
|
|
230
|
+
`Relation lookup "${fieldNameDotLookup}" does not match field name "${fieldName}".`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
propertiesToImport.push(fieldNameDotLookup);
|
|
234
|
+
} else {
|
|
235
|
+
propertiesToImport.push(fieldName);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return propertiesToImport;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Build a payload object from simple fields.
|
|
244
|
+
* @param simpleFields
|
|
245
|
+
*/
|
|
246
|
+
function buildPayloadFromSimpleFields(
|
|
247
|
+
simpleFields: IDataObject[]
|
|
248
|
+
): BulkImportPayloadObject {
|
|
249
|
+
const obj: BulkImportPayloadObject = { values: {} };
|
|
250
|
+
|
|
251
|
+
for (const field of simpleFields) {
|
|
252
|
+
const fieldName = field.fieldName as string;
|
|
253
|
+
const fieldValue = field.fieldValue as string;
|
|
254
|
+
|
|
255
|
+
obj.values[fieldName] = fieldValue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return obj;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Execute the "Bulk import objects" operation for Lime CRM.
|
|
263
|
+
*
|
|
264
|
+
* @remarks
|
|
265
|
+
* This method creates a bulk import job and uploads data.
|
|
266
|
+
*
|
|
267
|
+
* @param i - The index of the current item in the workflow execution
|
|
268
|
+
*
|
|
269
|
+
* @returns The bulk import job status and summary
|
|
270
|
+
*
|
|
271
|
+
* @public
|
|
272
|
+
*/
|
|
273
|
+
export async function execute(
|
|
274
|
+
this: IExecuteFunctions,
|
|
275
|
+
i: number
|
|
276
|
+
): Promise<IDataObject | undefined> {
|
|
277
|
+
if (i > 0) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const limetype = this.getNodeParameter('limetype', i) as string;
|
|
282
|
+
const matchingProperty = this.getNodeParameter(
|
|
283
|
+
'matchingProperty',
|
|
284
|
+
i
|
|
285
|
+
) as string;
|
|
286
|
+
const inputType = this.getNodeParameter('inputType', i) as string;
|
|
287
|
+
const items = this.getInputData();
|
|
288
|
+
|
|
289
|
+
Logger.info(
|
|
290
|
+
`Preparing bulk import of ${items.length} objects for limetype: ${limetype}`
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Step 1: Compute the properties to import based on the first item
|
|
294
|
+
const propertiesToImport =
|
|
295
|
+
inputType === 'json'
|
|
296
|
+
? getPropertiesFromJson(
|
|
297
|
+
this.getNodeParameter('jsonData', 0) as string
|
|
298
|
+
)
|
|
299
|
+
: getPropertiesFromSimpleFields(
|
|
300
|
+
this.getNodeParameter(
|
|
301
|
+
'simpleFields.field',
|
|
302
|
+
0,
|
|
303
|
+
[]
|
|
304
|
+
) as IDataObject[],
|
|
305
|
+
this.getNode.bind(this)
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
Logger.info(`Properties to import: ${propertiesToImport.join(', ')}`);
|
|
309
|
+
|
|
310
|
+
// Step 2: Prepare the body for all items
|
|
311
|
+
const body: BulkImportPayloadObject[] = items.map((_, idx) => {
|
|
312
|
+
if (inputType === 'json') {
|
|
313
|
+
return JSON.parse(this.getNodeParameter('jsonData', idx) as string);
|
|
314
|
+
}
|
|
315
|
+
return buildPayloadFromSimpleFields(
|
|
316
|
+
this.getNodeParameter(
|
|
317
|
+
'simpleFields.field',
|
|
318
|
+
idx,
|
|
319
|
+
[]
|
|
320
|
+
) as IDataObject[]
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Step 3: Create the bulk import job
|
|
325
|
+
const jobPayload: BulkImportJobPayload = {
|
|
326
|
+
mode: 'create_or_update',
|
|
327
|
+
limetype,
|
|
328
|
+
matchingProperty,
|
|
329
|
+
properties: propertiesToImport,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const jobResponse = await createBulkImportJob(this, jobPayload);
|
|
333
|
+
const jobId = jobResponse.id;
|
|
334
|
+
|
|
335
|
+
// Step 4: Upload the data file
|
|
336
|
+
await uploadBulkImportData(this, jobId, body);
|
|
337
|
+
|
|
338
|
+
// Step 5: Poll for completion
|
|
339
|
+
const finalStatus = await waitForBulkImportJob(this, jobId, 2500);
|
|
340
|
+
|
|
341
|
+
if (finalStatus.status === 'failed') {
|
|
342
|
+
throw new NodeOperationError(
|
|
343
|
+
this.getNode(),
|
|
344
|
+
'The bulk import job failed',
|
|
345
|
+
{
|
|
346
|
+
message: 'The bulk import job failed due to a server error.',
|
|
347
|
+
description: `Bulk import job with ID ${jobId} has failed. Check the Lime CRM server for more details.`,
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Step 6: Return the results summary
|
|
353
|
+
return {
|
|
354
|
+
jobId,
|
|
355
|
+
status: finalStatus.status,
|
|
356
|
+
summary: finalStatus.result || {
|
|
357
|
+
total: 0,
|
|
358
|
+
created: 0,
|
|
359
|
+
updated: 0,
|
|
360
|
+
skipped: 0,
|
|
361
|
+
failed: 0,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
@@ -22,3 +22,20 @@ export * as getManyObjects from './getManyObjects.operation';
|
|
|
22
22
|
* @group Resources
|
|
23
23
|
*/
|
|
24
24
|
export * as getSingleFile from './getSingleFile.operation';
|
|
25
|
+
/**
|
|
26
|
+
* @group Resources
|
|
27
|
+
*/
|
|
28
|
+
export * as createManyObjects from './createManyObjects.operation';
|
|
29
|
+
/**
|
|
30
|
+
* @group Resources
|
|
31
|
+
*/
|
|
32
|
+
export * as updateManyObjects from './updateManyObjects.operation';
|
|
33
|
+
/**
|
|
34
|
+
* @group Resources
|
|
35
|
+
*/
|
|
36
|
+
export * as createOrUpdateManyObjects from './createOrUpdateManyObjects.operation';
|
|
37
|
+
/**
|
|
38
|
+
* @group Resources
|
|
39
|
+
* @deprecated Use createManyObjects, updateManyObjects, or createOrUpdateManyObjects instead.
|
|
40
|
+
*/
|
|
41
|
+
export * as startBulkImport from './deprecated-startBulkImport.operation';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
import {
|
|
3
|
+
getBulkImportProperties,
|
|
4
|
+
executeBulkImport,
|
|
5
|
+
} from './bulkImportCommons';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Description and metadata for the "Update many objects" operation in Lime CRM.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export const description = {
|
|
13
|
+
name: 'Update Many Objects (Alpha)',
|
|
14
|
+
value: 'updateManyObjects',
|
|
15
|
+
description:
|
|
16
|
+
'Update multiple existing objects via bulk import. Skips business logic.',
|
|
17
|
+
action: 'Update many objects (alpha)',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Node properties for the "Update many objects" operation.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const properties: INodeProperties[] = getBulkImportProperties(
|
|
26
|
+
'updateManyObjects',
|
|
27
|
+
true // Matching property needed for update
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Execute the "Update many objects" operation for Lime CRM.
|
|
32
|
+
*
|
|
33
|
+
* @param i - The index of the current item in the workflow execution
|
|
34
|
+
*
|
|
35
|
+
* @returns The bulk import job status and summary
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export async function execute(
|
|
40
|
+
this: IExecuteFunctions,
|
|
41
|
+
i: number
|
|
42
|
+
): Promise<IDataObject | undefined> {
|
|
43
|
+
return executeBulkImport(this, i, 'update');
|
|
44
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
LoggerProxy as Logger,
|
|
4
|
+
NodeApiError,
|
|
5
|
+
} from 'n8n-workflow';
|
|
6
|
+
import { callLimeApi } from './commons';
|
|
7
|
+
import { LIME_CRM_API_CREDENTIAL_KEY } from '../models';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Endpoint path for Lime CRM bulk import API.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
* @group Transport
|
|
14
|
+
*/
|
|
15
|
+
const BULK_IMPORT_URL = 'limepkg-mbeku-bulk-import/bulk-imports/';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Response from creating a bulk import job.
|
|
19
|
+
*
|
|
20
|
+
* @property id - The unique ID of the bulk import job
|
|
21
|
+
* @property createdBy - The ID of the user who created the job
|
|
22
|
+
* @property fileId - The ID of the uploaded file (if any)
|
|
23
|
+
* @property status - The current status of the job (`waiting_for_data`, `ready`, `failed`, `succeeded`)
|
|
24
|
+
* @property startedAt - Timestamp when the job started (if any)
|
|
25
|
+
* @property endedAt - Timestamp when the job ended (if any)
|
|
26
|
+
* @property metadata - Metadata about the job configuration
|
|
27
|
+
* @property metadata.limetype - The Lime type being imported
|
|
28
|
+
* @property metadata.properties - Array of property names being imported
|
|
29
|
+
* @property metadata.mode - Import mode
|
|
30
|
+
* @property metadata.matchingProperty - Property used for matching existing records
|
|
31
|
+
* @property result - Summary of the job results (if finished)
|
|
32
|
+
* @property result.total - Total number of records processed
|
|
33
|
+
* @property result.created - Number of records created
|
|
34
|
+
* @property result.updated - Number of records updated
|
|
35
|
+
* @property result.skipped - Number of records skipped
|
|
36
|
+
* @property result.failed - Number of records failed
|
|
37
|
+
* @property extras - Optional extra metadata from the server/job runner
|
|
38
|
+
* @property extras.task_id - Optional background task id (snake_case)
|
|
39
|
+
* @property extras.taskId - Optional background task id (camelCase)
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
* @group Transport
|
|
43
|
+
*/
|
|
44
|
+
export interface BulkImportJobResponse {
|
|
45
|
+
id: string;
|
|
46
|
+
createdBy: number;
|
|
47
|
+
fileId: number | null;
|
|
48
|
+
status: 'waiting_for_data' | 'ready' | 'failed' | 'succeeded';
|
|
49
|
+
startedAt: string | null;
|
|
50
|
+
endedAt: string | null;
|
|
51
|
+
metadata: {
|
|
52
|
+
limetype: string;
|
|
53
|
+
properties: string[];
|
|
54
|
+
mode: BulkImportMode;
|
|
55
|
+
matchingProperty: string;
|
|
56
|
+
};
|
|
57
|
+
result: null | {
|
|
58
|
+
total: number;
|
|
59
|
+
created: number;
|
|
60
|
+
updated: number;
|
|
61
|
+
skipped: number;
|
|
62
|
+
failed: number;
|
|
63
|
+
};
|
|
64
|
+
extras: null | {
|
|
65
|
+
task_id?: string;
|
|
66
|
+
taskId?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The mode for a bulk import operation.
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
* @group Transport
|
|
75
|
+
*/
|
|
76
|
+
export type BulkImportMode = 'create' | 'update' | 'create_or_update';
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Payload for creating a bulk import job.
|
|
80
|
+
*
|
|
81
|
+
* @property limetype - The Limetype to import objects into
|
|
82
|
+
* @property properties - List of property names to import
|
|
83
|
+
* @property mode - The import mode
|
|
84
|
+
* @property matchingProperty - Property to use for matching existing records (required for 'update' and 'create_or_update' modes)
|
|
85
|
+
*
|
|
86
|
+
* @public
|
|
87
|
+
* @group Transport
|
|
88
|
+
*/
|
|
89
|
+
export interface BulkImportJobPayload {
|
|
90
|
+
limetype: string;
|
|
91
|
+
properties: string[];
|
|
92
|
+
mode: BulkImportMode;
|
|
93
|
+
matchingProperty?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Object representing a single object to import.
|
|
98
|
+
*
|
|
99
|
+
* @property values - Key-value pairs of property names and their values
|
|
100
|
+
* @property extras - (Optional) Additional informational data for the import
|
|
101
|
+
*
|
|
102
|
+
* @public
|
|
103
|
+
* @group Transport
|
|
104
|
+
*/
|
|
105
|
+
export interface BulkImportPayloadObject {
|
|
106
|
+
values: Record<string, unknown>;
|
|
107
|
+
extras?: Record<string, unknown>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a new bulk import job in Lime CRM.
|
|
112
|
+
*
|
|
113
|
+
* @param context - The n8n execution context
|
|
114
|
+
* @param payload - The bulk import job configuration
|
|
115
|
+
*
|
|
116
|
+
* @returns Promise resolving to the created job response with id and status
|
|
117
|
+
*
|
|
118
|
+
* @public
|
|
119
|
+
* @group Transport
|
|
120
|
+
*/
|
|
121
|
+
export async function createBulkImportJob(
|
|
122
|
+
context: IExecuteFunctions,
|
|
123
|
+
payload: BulkImportJobPayload
|
|
124
|
+
): Promise<BulkImportJobResponse> {
|
|
125
|
+
Logger.info(
|
|
126
|
+
`Creating bulk import job at ${BULK_IMPORT_URL} with payload: ${JSON.stringify(payload)}`
|
|
127
|
+
);
|
|
128
|
+
try {
|
|
129
|
+
const response = await callLimeApi<BulkImportJobResponse>(context, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
url: BULK_IMPORT_URL,
|
|
132
|
+
requestOptions: {
|
|
133
|
+
body: payload,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
if (!response.success) {
|
|
137
|
+
throw new NodeApiError(context.getNode(), {
|
|
138
|
+
message: 'The bulk import job was rejected by the server.',
|
|
139
|
+
description: `${JSON.stringify(response.data)}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
Logger.info(
|
|
143
|
+
`Created bulk import job at ${BULK_IMPORT_URL} with payload: ${JSON.stringify(payload)}`
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return response.data;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
Logger.error(
|
|
149
|
+
`Failed to create bulk import job at ${BULK_IMPORT_URL} with payload: ${JSON.stringify(payload)}`
|
|
150
|
+
);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Upload data to an existing bulk import job.
|
|
157
|
+
*
|
|
158
|
+
* @param context - The n8n execution context
|
|
159
|
+
* @param jobId - The ID of the bulk import job
|
|
160
|
+
* @param data - Array of objects to import
|
|
161
|
+
*
|
|
162
|
+
* @returns Promise resolving when upload is complete
|
|
163
|
+
*
|
|
164
|
+
* @public
|
|
165
|
+
* @group Transport
|
|
166
|
+
*/
|
|
167
|
+
export async function uploadBulkImportData(
|
|
168
|
+
context: IExecuteFunctions,
|
|
169
|
+
jobId: string,
|
|
170
|
+
data: unknown
|
|
171
|
+
): Promise<void> {
|
|
172
|
+
// Get the base URL for constructing the full endpoint
|
|
173
|
+
const credentials = await context.getCredentials(
|
|
174
|
+
LIME_CRM_API_CREDENTIAL_KEY
|
|
175
|
+
);
|
|
176
|
+
const baseUrl = credentials.url as string;
|
|
177
|
+
const fullUrl = `${baseUrl}/${BULK_IMPORT_URL}${jobId}`;
|
|
178
|
+
|
|
179
|
+
const message = `Uploading data to: ${fullUrl} for job ID: ${jobId}`;
|
|
180
|
+
Logger.info(message);
|
|
181
|
+
|
|
182
|
+
const jsonData = JSON.stringify(data);
|
|
183
|
+
const messageData = `First element of uploaded data: ${jsonData[0]}`;
|
|
184
|
+
Logger.info(messageData);
|
|
185
|
+
const formData = {
|
|
186
|
+
file: {
|
|
187
|
+
value: Buffer.from(jsonData, 'utf8'),
|
|
188
|
+
options: {
|
|
189
|
+
filename: 'import-data.json',
|
|
190
|
+
contentType: 'application/json',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
Logger.info(`Uploading bulk import data to ${fullUrl}`);
|
|
196
|
+
|
|
197
|
+
await context.helpers.requestWithAuthentication.call(
|
|
198
|
+
context,
|
|
199
|
+
LIME_CRM_API_CREDENTIAL_KEY,
|
|
200
|
+
{
|
|
201
|
+
method: 'POST',
|
|
202
|
+
url: fullUrl,
|
|
203
|
+
formData,
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
Logger.info(`Successfully uploaded data for job ID: ${jobId}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get the status of a bulk import job.
|
|
212
|
+
*
|
|
213
|
+
* @param context - The n8n execution context
|
|
214
|
+
* @param jobId - The ID of the bulk import job
|
|
215
|
+
*
|
|
216
|
+
* @returns Promise resolving to the job status response
|
|
217
|
+
*
|
|
218
|
+
* @public
|
|
219
|
+
* @group Transport
|
|
220
|
+
*/
|
|
221
|
+
export async function getBulkImportJobStatus(
|
|
222
|
+
context: IExecuteFunctions,
|
|
223
|
+
jobId: string
|
|
224
|
+
): Promise<BulkImportJobResponse> {
|
|
225
|
+
Logger.info(`Fetching status for bulk import job ID: ${jobId}`);
|
|
226
|
+
const response = await callLimeApi<BulkImportJobResponse>(context, {
|
|
227
|
+
method: 'GET',
|
|
228
|
+
url: `${BULK_IMPORT_URL}${jobId}`,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!response.success) {
|
|
232
|
+
throw new NodeApiError(context.getNode(), {
|
|
233
|
+
message: 'The bulk import job status could not be retrieved.',
|
|
234
|
+
description: `${JSON.stringify(response)}`,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return response.data;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Poll a bulk import job until it completes.
|
|
243
|
+
*
|
|
244
|
+
* @param context - The n8n execution context
|
|
245
|
+
* @param jobId - The ID of the bulk import job
|
|
246
|
+
* @param pollIntervalMs - Milliseconds to wait between polls (default: 2500)
|
|
247
|
+
*
|
|
248
|
+
* @returns Promise resolving to the final job status response
|
|
249
|
+
*
|
|
250
|
+
* @public
|
|
251
|
+
* @group Transport
|
|
252
|
+
*/
|
|
253
|
+
export async function waitForBulkImportJob(
|
|
254
|
+
context: IExecuteFunctions,
|
|
255
|
+
jobId: string,
|
|
256
|
+
pollIntervalMs: number = 2500
|
|
257
|
+
): Promise<BulkImportJobResponse> {
|
|
258
|
+
let status = 'running';
|
|
259
|
+
let response: BulkImportJobResponse;
|
|
260
|
+
|
|
261
|
+
while (['waiting_for_data', 'ready', 'running'].includes(status)) {
|
|
262
|
+
// Wait before polling
|
|
263
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
264
|
+
|
|
265
|
+
Logger.info(`Polling bulk import job ID: ${jobId}`);
|
|
266
|
+
response = await getBulkImportJobStatus(context, jobId);
|
|
267
|
+
status = response.status;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return response!;
|
|
271
|
+
}
|
|
@@ -41,3 +41,11 @@ export {
|
|
|
41
41
|
fetchSingleUserById,
|
|
42
42
|
fetchSingleUserByLimeobjectId,
|
|
43
43
|
} from './users';
|
|
44
|
+
export {
|
|
45
|
+
BulkImportJobResponse,
|
|
46
|
+
BulkImportJobPayload,
|
|
47
|
+
createBulkImportJob,
|
|
48
|
+
uploadBulkImportData,
|
|
49
|
+
getBulkImportJobStatus,
|
|
50
|
+
waitForBulkImportJob,
|
|
51
|
+
} from './bulkimport';
|