@limetech/n8n-nodes-lime 2.6.1 → 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 +7 -2
- 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/nodes/lime-forms/LimeFormsTrigger.node.js +3 -3
- package/dist/nodes/lime-forms/LimeFormsTrigger.node.js.map +1 -1
- package/dist/nodes/lime-forms/lime-forms.svg +6 -0
- 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/nodes/lime-forms/LimeFormsTrigger.node.ts +3 -3
- package/nodes/lime-forms/lime-forms.svg +6 -0
- package/package.json +1 -1
- package/dist/nodes/lime-forms/assets/lime-crm.svg +0 -1
- package/nodes/lime-forms/assets/lime-crm.svg +0 -1
|
@@ -24,8 +24,11 @@ import {
|
|
|
24
24
|
getLimetypeProperties,
|
|
25
25
|
getLimetypes,
|
|
26
26
|
getNoHasManyProperties,
|
|
27
|
+
getRelationProperties,
|
|
27
28
|
getCreateMappingColumns,
|
|
28
29
|
getUpdateMappingColumns,
|
|
30
|
+
getRelationLookupMappingColumns,
|
|
31
|
+
getRelationPropertiesWithLookupField,
|
|
29
32
|
} from './methods';
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -148,10 +151,13 @@ export class LimeCrmNode implements INodeType {
|
|
|
148
151
|
getLimetypeProperties,
|
|
149
152
|
getFileProperties,
|
|
150
153
|
getNoHasManyProperties,
|
|
154
|
+
getRelationProperties,
|
|
155
|
+
getRelationPropertiesWithLookupField,
|
|
151
156
|
},
|
|
152
157
|
resourceMapping: {
|
|
153
158
|
getUpdateMappingColumns,
|
|
154
159
|
getCreateMappingColumns,
|
|
160
|
+
getRelationLookupMappingColumns,
|
|
155
161
|
},
|
|
156
162
|
};
|
|
157
163
|
|
|
@@ -110,3 +110,87 @@ export async function getNoHasManyProperties(
|
|
|
110
110
|
): Promise<INodePropertyOptions[]> {
|
|
111
111
|
return getFilteredLimetypeProperties(this, undefined, new Set(['hasmany']));
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get only relation properties (belongsto/hasone) for the current Limetype.
|
|
116
|
+
*
|
|
117
|
+
* @returns Array of relation property options
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
* @group Load Options Methods
|
|
121
|
+
*/
|
|
122
|
+
export async function getRelationProperties(
|
|
123
|
+
this: ILoadOptionsFunctions
|
|
124
|
+
): Promise<INodePropertyOptions[]> {
|
|
125
|
+
return getFilteredLimetypeProperties(
|
|
126
|
+
this,
|
|
127
|
+
new Set(['belongsto', 'hasone'])
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get properties from ALL related limetypes for relation fields.
|
|
133
|
+
*
|
|
134
|
+
* This method finds all relation properties (belongsto/hasone) on the current limetype,
|
|
135
|
+
* fetches properties from each related limetype, and returns them grouped by limetype.
|
|
136
|
+
* Each option is prefixed with the relation field name for clarity.
|
|
137
|
+
*
|
|
138
|
+
* @returns Array of property options from all related limetypes, grouped by relation
|
|
139
|
+
*
|
|
140
|
+
* @public
|
|
141
|
+
* @group Load Options Methods
|
|
142
|
+
*/
|
|
143
|
+
export async function getRelationPropertiesWithLookupField(
|
|
144
|
+
this: ILoadOptionsFunctions
|
|
145
|
+
): Promise<INodePropertyOptions[]> {
|
|
146
|
+
const limetype = this.getNodeParameter('limetype', '') as string;
|
|
147
|
+
|
|
148
|
+
if (!limetype) return [];
|
|
149
|
+
|
|
150
|
+
const propertiesResponse = await getProperties(this, limetype);
|
|
151
|
+
if (!propertiesResponse.success) return [];
|
|
152
|
+
|
|
153
|
+
const relationProperties = propertiesResponse.data.filter(
|
|
154
|
+
(p) => p.type === 'belongsto' || p.type === 'hasone'
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const allOptions: INodePropertyOptions[] = [];
|
|
158
|
+
for (const relationProp of relationProperties) {
|
|
159
|
+
const relatedLimetype = relationProp.relatedLimetype as string;
|
|
160
|
+
|
|
161
|
+
if (!relatedLimetype) {
|
|
162
|
+
Logger.warn(`No relatedLimetype found for ${relationProp.name}`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const relatedPropertiesResponse = await getProperties(
|
|
167
|
+
this,
|
|
168
|
+
relatedLimetype
|
|
169
|
+
);
|
|
170
|
+
if (!relatedPropertiesResponse.success) continue;
|
|
171
|
+
|
|
172
|
+
const relationDisplayName =
|
|
173
|
+
(relationProp.localname as string) || (relationProp.name as string);
|
|
174
|
+
|
|
175
|
+
const options = relatedPropertiesResponse.data
|
|
176
|
+
.filter((p) => p.type !== 'hasmany')
|
|
177
|
+
.map((p) => ({
|
|
178
|
+
name: `${relationDisplayName} → ${(p.localname as string) || (p.name as string)}`,
|
|
179
|
+
value: `${relationProp.name}.${p.name}`,
|
|
180
|
+
description: `Lookup ${relationDisplayName} by ${p.name} (${p.type})`,
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
allOptions.push(...options);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const emptyOption: INodePropertyOptions = {
|
|
187
|
+
name: '(None - not a relation field)',
|
|
188
|
+
value: '',
|
|
189
|
+
description: 'Leave empty if this field is not a relation',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return [
|
|
193
|
+
emptyOption,
|
|
194
|
+
...allOptions.sort((a, b) => a.name.localeCompare(b.name)),
|
|
195
|
+
];
|
|
196
|
+
}
|
|
@@ -4,9 +4,12 @@ export {
|
|
|
4
4
|
getFilteredLimetypeProperties,
|
|
5
5
|
getLimetypeProperties,
|
|
6
6
|
getNoHasManyProperties,
|
|
7
|
+
getRelationProperties,
|
|
8
|
+
getRelationPropertiesWithLookupField,
|
|
7
9
|
} from './getLimetypeProperties';
|
|
8
10
|
export {
|
|
9
11
|
getCreateMappingColumns,
|
|
10
12
|
getUpdateMappingColumns,
|
|
13
|
+
getRelationLookupMappingColumns,
|
|
11
14
|
parseResourceMapperFields,
|
|
12
15
|
} from './resourceMapping';
|
|
@@ -139,3 +139,79 @@ export async function getUpdateMappingColumns(
|
|
|
139
139
|
const limetype = this.getNodeParameter('limetype') as string;
|
|
140
140
|
return getMappingColumns(this, 'update', limetype);
|
|
141
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Method for getting relation lookup fields as a resource mapper.
|
|
145
|
+
* Shows one picker per relation (belongsto/hasone) with the related object's properties.
|
|
146
|
+
*/
|
|
147
|
+
export async function getRelationLookupMappingColumns(
|
|
148
|
+
this: ILoadOptionsFunctions
|
|
149
|
+
): Promise<ResourceMapperFields> {
|
|
150
|
+
const limetype = this.getNodeParameter('limetype') as string;
|
|
151
|
+
if (!limetype) {
|
|
152
|
+
return { fields: [] };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const propertiesResponse = await getProperties(this, limetype);
|
|
156
|
+
if (!propertiesResponse.success) {
|
|
157
|
+
Logger.error(
|
|
158
|
+
`There was an error with fetching properties: ${JSON.stringify(propertiesResponse.data)}`
|
|
159
|
+
);
|
|
160
|
+
return { fields: [] };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Find all relation properties (belongsto/hasone)
|
|
164
|
+
const relationProperties = propertiesResponse.data.filter(
|
|
165
|
+
(property) =>
|
|
166
|
+
property.type === 'belongsto' || property.type === 'hasone'
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const fields = await Promise.all(
|
|
170
|
+
relationProperties.map(async (relationProp) => {
|
|
171
|
+
const relatedLimetype = relationProp.relatedLimetype as string;
|
|
172
|
+
const relationDisplayName =
|
|
173
|
+
relationProp.localname || relationProp.name;
|
|
174
|
+
|
|
175
|
+
if (!relatedLimetype) {
|
|
176
|
+
Logger.warn(
|
|
177
|
+
`No relatedLimetype found for ${relationProp.name}`
|
|
178
|
+
);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Fetch properties from the related limetype
|
|
183
|
+
const relatedPropertiesResponse = await getProperties(
|
|
184
|
+
this,
|
|
185
|
+
relatedLimetype
|
|
186
|
+
);
|
|
187
|
+
if (!relatedPropertiesResponse.success) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Get available lookup properties (exclude hasmany relations)
|
|
192
|
+
const lookupOptions = relatedPropertiesResponse.data
|
|
193
|
+
.filter((p) => p.type !== 'hasmany')
|
|
194
|
+
.map((p) => ({
|
|
195
|
+
value: p.name,
|
|
196
|
+
name: `${p.localname || p.name} [${p.type}]`,
|
|
197
|
+
}))
|
|
198
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
id: relationProp.name,
|
|
202
|
+
displayName: `${relationDisplayName}`,
|
|
203
|
+
required: false,
|
|
204
|
+
defaultMatch: false,
|
|
205
|
+
display: true,
|
|
206
|
+
type: 'options' as FieldType,
|
|
207
|
+
options: lookupOptions,
|
|
208
|
+
};
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
fields: fields
|
|
214
|
+
.filter((f): f is NonNullable<typeof f> => f !== null)
|
|
215
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName)),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -21,6 +21,10 @@ const moduleHandler = new N8NOperationModuleHandler([
|
|
|
21
21
|
operations.deleteSingleObject,
|
|
22
22
|
operations.getManyObjects,
|
|
23
23
|
operations.getSingleFile,
|
|
24
|
+
operations.createManyObjects,
|
|
25
|
+
operations.updateManyObjects,
|
|
26
|
+
operations.createOrUpdateManyObjects,
|
|
27
|
+
operations.startBulkImport,
|
|
24
28
|
]);
|
|
25
29
|
|
|
26
30
|
/**
|
|
@@ -75,7 +79,7 @@ export const dataFields: INodeProperties[] = [
|
|
|
75
79
|
export async function dataOperations(
|
|
76
80
|
this: IExecuteFunctions,
|
|
77
81
|
{ operation, i }: { operation: string; i: number }
|
|
78
|
-
): Promise<INodeExecutionData | INodeExecutionData[]> {
|
|
82
|
+
): Promise<INodeExecutionData | INodeExecutionData[] | undefined> {
|
|
79
83
|
switch (operation) {
|
|
80
84
|
case 'createSingleObject': {
|
|
81
85
|
return {
|
|
@@ -115,6 +119,35 @@ export async function dataOperations(
|
|
|
115
119
|
case 'getSingleFile': {
|
|
116
120
|
return await operations.getSingleFile.execute.call(this, i);
|
|
117
121
|
}
|
|
122
|
+
case 'createManyObjects': {
|
|
123
|
+
const result = await operations.createManyObjects.execute.call(
|
|
124
|
+
this,
|
|
125
|
+
i
|
|
126
|
+
);
|
|
127
|
+
return result && { json: result };
|
|
128
|
+
}
|
|
129
|
+
case 'updateManyObjects': {
|
|
130
|
+
const result = await operations.updateManyObjects.execute.call(
|
|
131
|
+
this,
|
|
132
|
+
i
|
|
133
|
+
);
|
|
134
|
+
return result && { json: result };
|
|
135
|
+
}
|
|
136
|
+
case 'createOrUpdateManyObjects': {
|
|
137
|
+
const result =
|
|
138
|
+
await operations.createOrUpdateManyObjects.execute.call(
|
|
139
|
+
this,
|
|
140
|
+
i
|
|
141
|
+
);
|
|
142
|
+
return result && { json: result };
|
|
143
|
+
}
|
|
144
|
+
case 'startBulkImport': {
|
|
145
|
+
const result = await operations.startBulkImport.execute.call(
|
|
146
|
+
this,
|
|
147
|
+
i
|
|
148
|
+
);
|
|
149
|
+
return result && { json: result };
|
|
150
|
+
}
|
|
118
151
|
}
|
|
119
152
|
|
|
120
153
|
throw new NodeOperationError(
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IDataObject,
|
|
3
|
+
IExecuteFunctions,
|
|
4
|
+
INodeProperties,
|
|
5
|
+
LoggerProxy as Logger,
|
|
6
|
+
NodeOperationError,
|
|
7
|
+
} from 'n8n-workflow';
|
|
8
|
+
import { DATA_RESOURCE } from '../../../models';
|
|
9
|
+
import {
|
|
10
|
+
BulkImportJobPayload,
|
|
11
|
+
BulkImportMode,
|
|
12
|
+
BulkImportPayloadObject,
|
|
13
|
+
createBulkImportJob,
|
|
14
|
+
uploadBulkImportData,
|
|
15
|
+
waitForBulkImportJob,
|
|
16
|
+
} from '../../../transport/bulkimport';
|
|
17
|
+
import { parseResourceMapperFields } from '../../../methods';
|
|
18
|
+
|
|
19
|
+
export type BulkImportOperationName =
|
|
20
|
+
| 'createManyObjects'
|
|
21
|
+
| 'createOrUpdateManyObjects'
|
|
22
|
+
| 'updateManyObjects';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generate common properties for bulk import operations.
|
|
26
|
+
* @param operationName - The operation value to show these properties for
|
|
27
|
+
* @param matchingPropertyRequired - Whether the matching property field is required (true for update/create_or_update, false for create)
|
|
28
|
+
*/
|
|
29
|
+
export function getBulkImportProperties(
|
|
30
|
+
operationName: BulkImportOperationName,
|
|
31
|
+
matchingPropertyRequired: boolean
|
|
32
|
+
): INodeProperties[] {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
displayName:
|
|
36
|
+
'<h1>Alpha</h1>' +
|
|
37
|
+
'This feature is in alpha and will eventually change and require manual fixes to this node.<br>' +
|
|
38
|
+
'<br>' +
|
|
39
|
+
'The package <b>limepkg-mbeku-bulk-import >=1.4.1,<2.0.</b> must be installed in the solution.<br>' +
|
|
40
|
+
'<br>' +
|
|
41
|
+
'⚠️ Skips business logic such as automations, custom lime objects, webhooks and sql-on-update.',
|
|
42
|
+
name: 'alphaNotice',
|
|
43
|
+
type: 'notice',
|
|
44
|
+
displayOptions: {
|
|
45
|
+
show: {
|
|
46
|
+
resource: [DATA_RESOURCE],
|
|
47
|
+
operation: [operationName],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
default: '',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
displayName: 'Limetype',
|
|
54
|
+
name: 'limetype',
|
|
55
|
+
type: 'options',
|
|
56
|
+
typeOptions: {
|
|
57
|
+
loadOptionsMethod: 'getLimetypes',
|
|
58
|
+
},
|
|
59
|
+
required: true,
|
|
60
|
+
default: '',
|
|
61
|
+
description: 'The type of object to import',
|
|
62
|
+
displayOptions: {
|
|
63
|
+
show: {
|
|
64
|
+
resource: [DATA_RESOURCE],
|
|
65
|
+
operation: [operationName],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
displayName: 'Matching Property',
|
|
71
|
+
name: 'matchingProperty',
|
|
72
|
+
type: 'options',
|
|
73
|
+
typeOptions: {
|
|
74
|
+
loadOptionsMethod: 'getNoHasManyProperties',
|
|
75
|
+
loadOptionsDependsOn: ['limetype'],
|
|
76
|
+
},
|
|
77
|
+
required: matchingPropertyRequired,
|
|
78
|
+
default: '',
|
|
79
|
+
description: matchingPropertyRequired
|
|
80
|
+
? 'The property to use to match existing objects. Must be a unique property.'
|
|
81
|
+
: 'Optional: The property to use to match existing objects. If set, objects matching an existing record will be skipped.',
|
|
82
|
+
displayOptions: {
|
|
83
|
+
show: {
|
|
84
|
+
resource: [DATA_RESOURCE],
|
|
85
|
+
operation: [operationName],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
displayName: 'Input Method',
|
|
91
|
+
name: 'inputMethod',
|
|
92
|
+
type: 'options',
|
|
93
|
+
options: [
|
|
94
|
+
{
|
|
95
|
+
name: 'Form Fields',
|
|
96
|
+
value: 'fields',
|
|
97
|
+
description: 'Define fields using the UI',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'JSON',
|
|
101
|
+
value: 'json',
|
|
102
|
+
description: 'Define fields using JSON',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
default: 'fields',
|
|
106
|
+
description: 'How to input the data',
|
|
107
|
+
displayOptions: {
|
|
108
|
+
show: {
|
|
109
|
+
resource: [DATA_RESOURCE],
|
|
110
|
+
operation: [operationName],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Object (JSON)',
|
|
116
|
+
name: 'objectJson',
|
|
117
|
+
type: 'json',
|
|
118
|
+
default: '{\n "name": "Company Name",\n "phone": "+987654321"\n}',
|
|
119
|
+
description:
|
|
120
|
+
'Object data in JSON format. Property names must match the Lime CRM field names.',
|
|
121
|
+
typeOptions: {
|
|
122
|
+
alwaysOpenEditWindow: true,
|
|
123
|
+
},
|
|
124
|
+
displayOptions: {
|
|
125
|
+
show: {
|
|
126
|
+
resource: [DATA_RESOURCE],
|
|
127
|
+
operation: [operationName],
|
|
128
|
+
inputMethod: ['json'],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
displayName: 'Properties',
|
|
134
|
+
name: 'properties',
|
|
135
|
+
type: 'resourceMapper',
|
|
136
|
+
placeholder: 'Add Property',
|
|
137
|
+
typeOptions: {
|
|
138
|
+
resourceMapper: {
|
|
139
|
+
resourceMapperMethod: 'getCreateMappingColumns',
|
|
140
|
+
mode: 'add',
|
|
141
|
+
addAllFields: false,
|
|
142
|
+
supportAutoMap: false,
|
|
143
|
+
},
|
|
144
|
+
loadOptionsDependsOn: ['limetype'],
|
|
145
|
+
},
|
|
146
|
+
default: {
|
|
147
|
+
value: null,
|
|
148
|
+
},
|
|
149
|
+
displayOptions: {
|
|
150
|
+
show: {
|
|
151
|
+
resource: [DATA_RESOURCE],
|
|
152
|
+
operation: [operationName],
|
|
153
|
+
inputMethod: ['fields'],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Relation Lookups',
|
|
159
|
+
name: 'relationLookups',
|
|
160
|
+
type: 'resourceMapper',
|
|
161
|
+
placeholder: 'Add Relation Lookup',
|
|
162
|
+
description:
|
|
163
|
+
'For relation fields (belongsto/hasone), specify which property on the related object to use for matching.',
|
|
164
|
+
typeOptions: {
|
|
165
|
+
resourceMapper: {
|
|
166
|
+
resourceMapperMethod: 'getRelationLookupMappingColumns',
|
|
167
|
+
mode: 'add',
|
|
168
|
+
addAllFields: true,
|
|
169
|
+
supportAutoMap: false,
|
|
170
|
+
valuesLabel: 'Relation Lookups',
|
|
171
|
+
},
|
|
172
|
+
loadOptionsDependsOn: ['limetype'],
|
|
173
|
+
},
|
|
174
|
+
default: {
|
|
175
|
+
value: null,
|
|
176
|
+
},
|
|
177
|
+
displayOptions: {
|
|
178
|
+
show: {
|
|
179
|
+
resource: [DATA_RESOURCE],
|
|
180
|
+
operation: [operationName],
|
|
181
|
+
inputMethod: ['fields'],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Extract properties to import from JSON input.
|
|
190
|
+
* @param jsonData
|
|
191
|
+
*/
|
|
192
|
+
function getPropertiesFromJson(jsonData: string): string[] {
|
|
193
|
+
const parsed = JSON.parse(jsonData);
|
|
194
|
+
return Object.keys(parsed);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Execute a bulk import operation for Lime CRM.
|
|
199
|
+
*
|
|
200
|
+
* @param context - The n8n execution context
|
|
201
|
+
* @param i - The index of the current item in the workflow execution
|
|
202
|
+
* @param mode - The bulk import mode: 'create', 'update', or 'create_or_update'
|
|
203
|
+
*
|
|
204
|
+
* @returns The bulk import job status and summary
|
|
205
|
+
*/
|
|
206
|
+
export async function executeBulkImport(
|
|
207
|
+
context: IExecuteFunctions,
|
|
208
|
+
i: number,
|
|
209
|
+
mode: BulkImportMode
|
|
210
|
+
): Promise<IDataObject | undefined> {
|
|
211
|
+
if (i > 0) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const limetype = context.getNodeParameter('limetype', i) as string;
|
|
216
|
+
const inputMethod = context.getNodeParameter('inputMethod', i) as string;
|
|
217
|
+
const items = context.getInputData();
|
|
218
|
+
|
|
219
|
+
// Matching property is always available but optional for create mode
|
|
220
|
+
const matchingProperty = context.getNodeParameter(
|
|
221
|
+
'matchingProperty',
|
|
222
|
+
i,
|
|
223
|
+
''
|
|
224
|
+
) as string;
|
|
225
|
+
|
|
226
|
+
Logger.info(
|
|
227
|
+
`Preparing bulk import (${mode}) of ${items.length} objects for limetype: ${limetype}`
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Step 1: Compute the properties to import based on the first item
|
|
231
|
+
let propertiesToImport: string[];
|
|
232
|
+
if (inputMethod === 'json') {
|
|
233
|
+
propertiesToImport = getPropertiesFromJson(
|
|
234
|
+
context.getNodeParameter('objectJson', 0) as string
|
|
235
|
+
);
|
|
236
|
+
} else {
|
|
237
|
+
const firstItemData = parseResourceMapperFields(
|
|
238
|
+
context,
|
|
239
|
+
0,
|
|
240
|
+
'properties'
|
|
241
|
+
);
|
|
242
|
+
propertiesToImport = Object.keys(firstItemData);
|
|
243
|
+
|
|
244
|
+
// Apply relation lookups: replace property names with lookup paths (e.g., 'coworker' -> 'coworker.email')
|
|
245
|
+
// The relationLookups is a resource mapper where keys are relation property names
|
|
246
|
+
// and values are the lookup property names on the related limetype
|
|
247
|
+
const relationLookupsData = parseResourceMapperFields(
|
|
248
|
+
context,
|
|
249
|
+
0,
|
|
250
|
+
'relationLookups'
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const lookupMap = new Map<string, string>();
|
|
254
|
+
for (const [relationProp, lookupProp] of Object.entries(
|
|
255
|
+
relationLookupsData
|
|
256
|
+
)) {
|
|
257
|
+
if (lookupProp && typeof lookupProp === 'string') {
|
|
258
|
+
lookupMap.set(relationProp, `${relationProp}.${lookupProp}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
propertiesToImport = propertiesToImport.map(
|
|
263
|
+
(prop) => lookupMap.get(prop) || prop
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
Logger.info(`Properties to import: ${propertiesToImport.join(', ')}`);
|
|
268
|
+
|
|
269
|
+
// Step 2: Prepare the body for all items
|
|
270
|
+
const body: BulkImportPayloadObject[] = items.map((_, idx) => {
|
|
271
|
+
if (inputMethod === 'json') {
|
|
272
|
+
return JSON.parse(
|
|
273
|
+
context.getNodeParameter('objectJson', idx) as string
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
const data = parseResourceMapperFields(context, idx, 'properties');
|
|
277
|
+
return { values: data };
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Step 3: Create the bulk import job
|
|
281
|
+
const jobPayload: BulkImportJobPayload = {
|
|
282
|
+
mode,
|
|
283
|
+
limetype,
|
|
284
|
+
...(matchingProperty && { matchingProperty }),
|
|
285
|
+
properties: propertiesToImport,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const jobResponse = await createBulkImportJob(context, jobPayload);
|
|
289
|
+
const jobId = jobResponse.id;
|
|
290
|
+
|
|
291
|
+
// Step 4: Upload the data file
|
|
292
|
+
await uploadBulkImportData(context, jobId, body);
|
|
293
|
+
|
|
294
|
+
// Step 5: Poll for completion
|
|
295
|
+
const finalStatus = await waitForBulkImportJob(context, jobId, 2500);
|
|
296
|
+
|
|
297
|
+
if (finalStatus.status === 'failed') {
|
|
298
|
+
throw new NodeOperationError(
|
|
299
|
+
context.getNode(),
|
|
300
|
+
'The bulk import job failed',
|
|
301
|
+
{
|
|
302
|
+
message: 'The bulk import job failed due to a server error.',
|
|
303
|
+
description: `Bulk import job with ID ${jobId} has failed. Check the Lime CRM server for more details.`,
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Step 6: Return the results summary
|
|
309
|
+
return {
|
|
310
|
+
jobId,
|
|
311
|
+
status: finalStatus.status,
|
|
312
|
+
startedAt: finalStatus.startedAt,
|
|
313
|
+
finishedAt: finalStatus.endedAt,
|
|
314
|
+
summary: finalStatus.result || {
|
|
315
|
+
total: 0,
|
|
316
|
+
created: 0,
|
|
317
|
+
updated: 0,
|
|
318
|
+
skipped: 0,
|
|
319
|
+
failed: 0,
|
|
320
|
+
},
|
|
321
|
+
taskId: finalStatus.extras?.task_id || finalStatus.extras?.taskId,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
@@ -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 "Create many objects" operation in Lime CRM.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export const description = {
|
|
13
|
+
name: 'Create Many Objects (Alpha)',
|
|
14
|
+
value: 'createManyObjects',
|
|
15
|
+
description:
|
|
16
|
+
'Create multiple objects via bulk import. Skips business logic.',
|
|
17
|
+
action: 'Create many objects (alpha)',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Node properties for the "Create many objects" operation.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const properties: INodeProperties[] = getBulkImportProperties(
|
|
26
|
+
'createManyObjects',
|
|
27
|
+
false // Matching property is optional for create (can be used to skip duplicates)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Execute the "Create 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, 'create');
|
|
44
|
+
}
|
|
@@ -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 "Create or update many objects" operation in Lime CRM.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export const description = {
|
|
13
|
+
name: 'Create or Update Many Objects (Alpha)',
|
|
14
|
+
value: 'createOrUpdateManyObjects',
|
|
15
|
+
description:
|
|
16
|
+
'Create or update multiple objects via bulk import. Skips business logic.',
|
|
17
|
+
action: 'Create or update many objects (alpha)',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Node properties for the "Create or update many objects" operation.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const properties: INodeProperties[] = getBulkImportProperties(
|
|
26
|
+
'createOrUpdateManyObjects',
|
|
27
|
+
true // Matching property needed for create_or_update
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Execute the "Create or 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, 'create_or_update');
|
|
44
|
+
}
|