@ixiam/n8n-nodes-civicrm 0.2.4 → 0.3.8
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/.eslintignore +1 -0
- package/.eslintrc.cjs +10 -0
- package/credentials/CiviCrmApi.credentials.ts +61 -0
- package/dist/nodes/CiviCrm/CiviCrm.node.d.ts +1 -0
- package/dist/nodes/CiviCrm/CiviCrm.node.js +79 -42
- package/dist/nodes/CiviCrm/descriptions/resources.d.ts +1 -1
- package/dist/nodes/CiviCrm/descriptions/resources.js +17 -15
- package/dist/nodes/transport/GenericFunctions.d.ts +5 -8
- package/dist/nodes/transport/GenericFunctions.js +11 -9
- package/nodes/CiviCrm/CiviCrm.node.ts +302 -0
- package/nodes/CiviCrm/civicrm.svg +28 -0
- package/nodes/CiviCrm/descriptions/generic.ts +9 -0
- package/nodes/CiviCrm/descriptions/resources.ts +35 -0
- package/nodes/transport/GenericFunctions.ts +51 -0
- package/package.json +2 -19
- package/tsconfig.json +16 -0
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dist/
|
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ICredentialType,
|
|
3
|
+
INodeProperties,
|
|
4
|
+
IHttpRequestMethods,
|
|
5
|
+
} from 'n8n-workflow';
|
|
6
|
+
|
|
7
|
+
export class CiviCrmApi implements ICredentialType {
|
|
8
|
+
name = 'civiCrmApi';
|
|
9
|
+
displayName = 'CiviCRM API';
|
|
10
|
+
documentationUrl = 'https://docs.civicrm.org/dev/en/latest/api/v4/';
|
|
11
|
+
|
|
12
|
+
authenticate = {
|
|
13
|
+
type: 'generic' as const,
|
|
14
|
+
properties: {
|
|
15
|
+
headers: {
|
|
16
|
+
'X-Civi-Auth': '={{"Bearer " + $credentials.apiToken}}',
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Button "Test" in credential UI
|
|
23
|
+
test = {
|
|
24
|
+
request: {
|
|
25
|
+
// IMPORTANT: method must be typed as IHttpRequestMethods
|
|
26
|
+
method: 'POST' as IHttpRequestMethods,
|
|
27
|
+
url: '={{$credentials.baseUrl.replace(/\\/$/, "")}}/civicrm/ajax/api4/Contact/get',
|
|
28
|
+
headers: {
|
|
29
|
+
'X-Civi-Auth': '={{"Bearer " + $credentials.apiToken}}',
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
body: {
|
|
33
|
+
entity: 'Contact',
|
|
34
|
+
action: 'get',
|
|
35
|
+
params: { limit: 1 },
|
|
36
|
+
},
|
|
37
|
+
json: true,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
properties: INodeProperties[] = [
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Base URL',
|
|
44
|
+
name: 'baseUrl',
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: '',
|
|
47
|
+
required: true,
|
|
48
|
+
placeholder: 'https://crm.example.org',
|
|
49
|
+
description: 'Sin la barra final',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
displayName: 'API Token',
|
|
53
|
+
name: 'apiToken',
|
|
54
|
+
type: 'string',
|
|
55
|
+
typeOptions: { password: true },
|
|
56
|
+
default: '',
|
|
57
|
+
required: true,
|
|
58
|
+
description: 'Se envía como X-Civi-Auth: Bearer <token>',
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
}
|
|
@@ -10,6 +10,11 @@ const ENTITY_MAP = {
|
|
|
10
10
|
case: 'Case',
|
|
11
11
|
contribution: 'Contribution',
|
|
12
12
|
membership: 'Membership',
|
|
13
|
+
participant: 'Participant',
|
|
14
|
+
group: 'Group',
|
|
15
|
+
relationship: 'Relationship',
|
|
16
|
+
email: 'Email',
|
|
17
|
+
activity: 'Activity',
|
|
13
18
|
};
|
|
14
19
|
/**
|
|
15
20
|
* Nodo principal CiviCRM para n8n
|
|
@@ -22,7 +27,7 @@ class CiviCrm {
|
|
|
22
27
|
icon: 'file:civicrm.svg',
|
|
23
28
|
group: ['transform'],
|
|
24
29
|
version: 1,
|
|
25
|
-
description: 'Interact with CiviCRM API v4',
|
|
30
|
+
description: 'Interact with CiviCRM API v4 (Civi-Go compatible)',
|
|
26
31
|
defaults: { name: 'CiviCRM' },
|
|
27
32
|
inputs: ['main'],
|
|
28
33
|
outputs: ['main'],
|
|
@@ -52,9 +57,11 @@ class CiviCrm {
|
|
|
52
57
|
url,
|
|
53
58
|
headers: {
|
|
54
59
|
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
55
|
-
'Content-Type': 'application/
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
},
|
|
62
|
+
body: {
|
|
63
|
+
params: JSON.stringify({ limit: 5 }),
|
|
56
64
|
},
|
|
57
|
-
body: (0, GenericFunctions_1.api4)('OptionValue', 'get', { limit: 5 }),
|
|
58
65
|
json: true,
|
|
59
66
|
});
|
|
60
67
|
const values = (res?.values || []);
|
|
@@ -71,42 +78,68 @@ class CiviCrm {
|
|
|
71
78
|
const out = [];
|
|
72
79
|
const resource = this.getNodeParameter('resource', 0);
|
|
73
80
|
const operation = this.getNodeParameter('operation', 0);
|
|
81
|
+
const entity = ENTITY_MAP[resource];
|
|
74
82
|
for (let i = 0; i < items.length; i++) {
|
|
75
|
-
|
|
76
|
-
// GET
|
|
83
|
+
// === GET ===
|
|
77
84
|
if (operation === 'get') {
|
|
78
85
|
const id = this.getNodeParameter('id', i);
|
|
79
|
-
const
|
|
86
|
+
const params = {
|
|
87
|
+
where: [['id', '=', id]],
|
|
88
|
+
limit: 1,
|
|
89
|
+
select: [
|
|
90
|
+
'id',
|
|
91
|
+
'display_name',
|
|
92
|
+
'first_name',
|
|
93
|
+
'last_name',
|
|
94
|
+
'contact_type',
|
|
95
|
+
'email.email',
|
|
96
|
+
'phone.phone',
|
|
97
|
+
'address.city',
|
|
98
|
+
'address.country_id:label',
|
|
99
|
+
'address.postal_code',
|
|
100
|
+
'address.street_address',
|
|
101
|
+
],
|
|
102
|
+
join: [
|
|
103
|
+
['Email AS email', 'LEFT'],
|
|
104
|
+
['Phone AS phone', 'LEFT'],
|
|
105
|
+
['Address AS address', 'LEFT'],
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, params);
|
|
80
109
|
out.push({ json: (res?.values?.[0] ?? {}) });
|
|
81
110
|
}
|
|
82
|
-
// GET MANY
|
|
111
|
+
// === GET MANY ===
|
|
83
112
|
if (operation === 'getMany') {
|
|
84
113
|
const returnAll = this.getNodeParameter('returnAll', i, false);
|
|
85
114
|
const limit = this.getNodeParameter('limit', i, 100);
|
|
86
115
|
const whereJson = this.getNodeParameter('whereJson', i, '');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
116
|
+
const where = whereJson ? JSON.parse(whereJson) : undefined;
|
|
117
|
+
const baseParams = {
|
|
118
|
+
where,
|
|
119
|
+
select: [
|
|
120
|
+
'id',
|
|
121
|
+
'display_name',
|
|
122
|
+
'first_name',
|
|
123
|
+
'last_name',
|
|
124
|
+
'contact_type',
|
|
125
|
+
'email.email',
|
|
126
|
+
'phone.phone',
|
|
127
|
+
'address.city',
|
|
128
|
+
'address.country_id:label',
|
|
129
|
+
'address.postal_code',
|
|
130
|
+
'address.street_address',
|
|
131
|
+
],
|
|
132
|
+
join: [
|
|
133
|
+
['Email AS email', 'LEFT'],
|
|
134
|
+
['Phone AS phone', 'LEFT'],
|
|
135
|
+
['Address AS address', 'LEFT'],
|
|
136
|
+
],
|
|
137
|
+
};
|
|
105
138
|
if (returnAll) {
|
|
106
139
|
let offset = 0;
|
|
107
140
|
const page = 500;
|
|
108
141
|
while (true) {
|
|
109
|
-
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`,
|
|
142
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, { ...baseParams, limit: page, offset });
|
|
110
143
|
const vals = (res?.values ?? []);
|
|
111
144
|
for (const v of vals)
|
|
112
145
|
out.push({ json: v });
|
|
@@ -116,41 +149,43 @@ class CiviCrm {
|
|
|
116
149
|
}
|
|
117
150
|
}
|
|
118
151
|
else {
|
|
119
|
-
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`,
|
|
120
|
-
|
|
121
|
-
if (vals.length > cappedLimit) {
|
|
122
|
-
vals = vals.slice(0, cappedLimit);
|
|
123
|
-
}
|
|
152
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, { ...baseParams, limit });
|
|
153
|
+
const vals = (res?.values ?? []);
|
|
124
154
|
for (const v of vals)
|
|
125
155
|
out.push({ json: v });
|
|
126
156
|
}
|
|
127
157
|
}
|
|
128
|
-
// CREATE
|
|
158
|
+
// === CREATE ===
|
|
129
159
|
if (operation === 'create') {
|
|
130
160
|
const pairs = this.getNodeParameter('fields.field', i, []);
|
|
131
|
-
const
|
|
161
|
+
const values = Object.fromEntries(pairs
|
|
132
162
|
.filter((p) => p.fieldName)
|
|
133
163
|
.map((p) => [p.fieldName, convertValue(p.fieldValue)]));
|
|
134
|
-
const
|
|
164
|
+
const params = { values };
|
|
165
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/create`, params);
|
|
135
166
|
out.push({ json: res });
|
|
136
167
|
}
|
|
137
|
-
// UPDATE
|
|
168
|
+
// === UPDATE ===
|
|
138
169
|
if (operation === 'update') {
|
|
139
170
|
const id = this.getNodeParameter('id', i);
|
|
140
171
|
const pairs = this.getNodeParameter('fields.field', i, []);
|
|
172
|
+
const values = Object.fromEntries(pairs
|
|
173
|
+
.filter((p) => p.fieldName)
|
|
174
|
+
.map((p) => [p.fieldName, convertValue(p.fieldValue)]));
|
|
141
175
|
const params = {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.filter((p) => p.fieldName)
|
|
145
|
-
.map((p) => [p.fieldName, convertValue(p.fieldValue)])),
|
|
176
|
+
values,
|
|
177
|
+
where: [['id', '=', id]],
|
|
146
178
|
};
|
|
147
|
-
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`,
|
|
179
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`, params);
|
|
148
180
|
out.push({ json: res });
|
|
149
181
|
}
|
|
150
|
-
// DELETE
|
|
182
|
+
// === DELETE ===
|
|
151
183
|
if (operation === 'delete') {
|
|
152
184
|
const id = this.getNodeParameter('id', i);
|
|
153
|
-
const
|
|
185
|
+
const params = {
|
|
186
|
+
where: [['id', '=', id]],
|
|
187
|
+
};
|
|
188
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/delete`, params);
|
|
154
189
|
out.push({ json: res });
|
|
155
190
|
}
|
|
156
191
|
}
|
|
@@ -179,3 +214,5 @@ function convertValue(val) {
|
|
|
179
214
|
catch { }
|
|
180
215
|
return val;
|
|
181
216
|
}
|
|
217
|
+
// Requerido por n8n >=1.110
|
|
218
|
+
exports.default = CiviCrm;
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.operationProp = exports.resourceProp = void 0;
|
|
4
|
-
const RESOURCE_OPTIONS = [
|
|
5
|
-
{ name: 'Contact', value: 'contact' },
|
|
6
|
-
{ name: 'Event', value: 'event' },
|
|
7
|
-
{ name: 'Case', value: 'case' },
|
|
8
|
-
{ name: 'Contribution', value: 'contribution' },
|
|
9
|
-
{ name: 'Membership', value: 'membership' },
|
|
10
|
-
];
|
|
11
4
|
exports.resourceProp = {
|
|
12
5
|
displayName: 'Resource',
|
|
13
6
|
name: 'resource',
|
|
14
7
|
type: 'options',
|
|
15
8
|
default: 'contact',
|
|
16
|
-
options:
|
|
9
|
+
options: [
|
|
10
|
+
{ name: 'Contact', value: 'contact' },
|
|
11
|
+
{ name: 'Event', value: 'event' },
|
|
12
|
+
{ name: 'Case', value: 'case' },
|
|
13
|
+
{ name: 'Contribution', value: 'contribution' },
|
|
14
|
+
{ name: 'Membership', value: 'membership' },
|
|
15
|
+
{ name: 'Participant', value: 'participant' },
|
|
16
|
+
{ name: 'Group', value: 'group' },
|
|
17
|
+
{ name: 'Relationship', value: 'relationship' },
|
|
18
|
+
{ name: 'Email', value: 'email' },
|
|
19
|
+
{ name: 'Activity', value: 'activity' },
|
|
20
|
+
{ name: 'Custom API Call', value: 'customApi' },
|
|
21
|
+
],
|
|
17
22
|
};
|
|
18
23
|
exports.operationProp = {
|
|
19
24
|
displayName: 'Operation',
|
|
20
25
|
name: 'operation',
|
|
21
26
|
type: 'options',
|
|
22
|
-
|
|
23
|
-
show: { resource: ['contact', 'event', 'case', 'contribution', 'membership'] },
|
|
24
|
-
},
|
|
25
|
-
default: 'get',
|
|
27
|
+
default: 'getMany',
|
|
26
28
|
options: [
|
|
29
|
+
{ name: 'Create', value: 'create' },
|
|
30
|
+
{ name: 'Delete', value: 'delete' },
|
|
27
31
|
{ name: 'Get', value: 'get' },
|
|
28
32
|
{ name: 'Get Many', value: 'getMany' },
|
|
29
|
-
{ name: 'Create', value: 'create' },
|
|
30
33
|
{ name: 'Update', value: 'update' },
|
|
31
|
-
|
|
32
|
-
], // tipo mutable
|
|
34
|
+
],
|
|
33
35
|
};
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { IExecuteFunctions } from 'n8n-workflow';
|
|
2
2
|
/**
|
|
3
|
-
* Ejecuta una llamada a la API de CiviCRM v4
|
|
3
|
+
* Ejecuta una llamada a la API de CiviCRM v4 (Civi-Go)
|
|
4
|
+
* Usa form-urlencoded con el campo "params" serializado
|
|
4
5
|
*/
|
|
5
6
|
export declare function civicrmApiRequest(this: IExecuteFunctions, method: 'POST', path: string, body: Record<string, unknown>): Promise<any>;
|
|
6
7
|
/**
|
|
7
|
-
* Devuelve el
|
|
8
|
+
* Devuelve el cuerpo estándar para las llamadas API4 (plano)
|
|
8
9
|
*/
|
|
9
|
-
export declare function api4(entity: string, action: string, params?: Record<string, unknown>):
|
|
10
|
-
entity: string;
|
|
11
|
-
action: string;
|
|
12
|
-
params: Record<string, unknown>;
|
|
13
|
-
};
|
|
10
|
+
export declare function api4(entity: string, action: string, params?: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -3,25 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.api4 = exports.civicrmApiRequest = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
/**
|
|
6
|
-
* Ejecuta una llamada a la API de CiviCRM v4
|
|
6
|
+
* Ejecuta una llamada a la API de CiviCRM v4 (Civi-Go)
|
|
7
|
+
* Usa form-urlencoded con el campo "params" serializado
|
|
7
8
|
*/
|
|
8
9
|
async function civicrmApiRequest(method, path, body) {
|
|
9
10
|
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi'));
|
|
10
11
|
const options = {
|
|
11
12
|
method,
|
|
12
|
-
url: `${baseUrl}${path}`,
|
|
13
|
+
url: `${baseUrl.replace(/\/$/, '')}${path}`,
|
|
13
14
|
headers: {
|
|
14
|
-
'Content-Type': 'application/
|
|
15
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
15
16
|
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
16
17
|
},
|
|
17
|
-
|
|
18
|
+
// cuerpo plano como espera Civi-Go
|
|
19
|
+
body: {
|
|
20
|
+
params: JSON.stringify(body.params ?? body),
|
|
21
|
+
},
|
|
18
22
|
json: true,
|
|
19
23
|
};
|
|
20
24
|
try {
|
|
21
25
|
const response = await this.helpers.httpRequest(options);
|
|
22
|
-
if (response?.is_error) {
|
|
23
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), response);
|
|
24
|
-
}
|
|
25
26
|
return response;
|
|
26
27
|
}
|
|
27
28
|
catch (error) {
|
|
@@ -30,9 +31,10 @@ async function civicrmApiRequest(method, path, body) {
|
|
|
30
31
|
}
|
|
31
32
|
exports.civicrmApiRequest = civicrmApiRequest;
|
|
32
33
|
/**
|
|
33
|
-
* Devuelve el
|
|
34
|
+
* Devuelve el cuerpo estándar para las llamadas API4 (plano)
|
|
34
35
|
*/
|
|
35
36
|
function api4(entity, action, params = {}) {
|
|
36
|
-
|
|
37
|
+
// devolvemos los parámetros planos, no anidados
|
|
38
|
+
return params;
|
|
37
39
|
}
|
|
38
40
|
exports.api4 = api4;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IExecuteFunctions,
|
|
3
|
+
ILoadOptionsFunctions,
|
|
4
|
+
INodeExecutionData,
|
|
5
|
+
INodePropertyOptions,
|
|
6
|
+
INodeType,
|
|
7
|
+
INodeTypeDescription,
|
|
8
|
+
IDataObject,
|
|
9
|
+
} from 'n8n-workflow';
|
|
10
|
+
|
|
11
|
+
import { civicrmApiRequest } from '../transport/GenericFunctions';
|
|
12
|
+
import { resourceProp, operationProp } from './descriptions/resources';
|
|
13
|
+
import { genericFields, upsertFields } from './descriptions/generic';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Recursos soportados
|
|
17
|
+
*/
|
|
18
|
+
type Resource =
|
|
19
|
+
| 'contact'
|
|
20
|
+
| 'event'
|
|
21
|
+
| 'case'
|
|
22
|
+
| 'contribution'
|
|
23
|
+
| 'membership'
|
|
24
|
+
| 'participant'
|
|
25
|
+
| 'group'
|
|
26
|
+
| 'relationship'
|
|
27
|
+
| 'email'
|
|
28
|
+
| 'activity';
|
|
29
|
+
|
|
30
|
+
const ENTITY_MAP: Record<Resource, string> = {
|
|
31
|
+
contact: 'Contact',
|
|
32
|
+
event: 'Event',
|
|
33
|
+
case: 'Case',
|
|
34
|
+
contribution: 'Contribution',
|
|
35
|
+
membership: 'Membership',
|
|
36
|
+
participant: 'Participant',
|
|
37
|
+
group: 'Group',
|
|
38
|
+
relationship: 'Relationship',
|
|
39
|
+
email: 'Email',
|
|
40
|
+
activity: 'Activity',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type Operation = 'get' | 'getMany' | 'create' | 'update' | 'delete';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Nodo principal CiviCRM para n8n
|
|
47
|
+
*/
|
|
48
|
+
export class CiviCrm implements INodeType {
|
|
49
|
+
description: INodeTypeDescription = {
|
|
50
|
+
displayName: 'CiviCRM',
|
|
51
|
+
name: 'civiCrm',
|
|
52
|
+
icon: 'file:civicrm.svg',
|
|
53
|
+
group: ['transform'],
|
|
54
|
+
version: 1,
|
|
55
|
+
description: 'Interact with CiviCRM API v4 (Civi-Go compatible)',
|
|
56
|
+
defaults: { name: 'CiviCRM' },
|
|
57
|
+
inputs: ['main'],
|
|
58
|
+
outputs: ['main'],
|
|
59
|
+
credentials: [{ name: 'civiCrmApi', required: true }],
|
|
60
|
+
properties: [
|
|
61
|
+
resourceProp,
|
|
62
|
+
operationProp,
|
|
63
|
+
{
|
|
64
|
+
displayName: 'ID',
|
|
65
|
+
name: 'id',
|
|
66
|
+
type: 'number',
|
|
67
|
+
default: 0,
|
|
68
|
+
required: true,
|
|
69
|
+
displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
|
|
70
|
+
},
|
|
71
|
+
...genericFields,
|
|
72
|
+
...upsertFields,
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
methods = {
|
|
77
|
+
loadOptions: {
|
|
78
|
+
async getOptionValues(this: ILoadOptionsFunctions) {
|
|
79
|
+
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi')) as {
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
apiToken: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const url = `${baseUrl.replace(/\/$/, '')}/civicrm/ajax/api4/OptionValue/get`;
|
|
85
|
+
|
|
86
|
+
const res = await this.helpers.httpRequest({
|
|
87
|
+
method: 'POST',
|
|
88
|
+
url,
|
|
89
|
+
headers: {
|
|
90
|
+
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
91
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
92
|
+
},
|
|
93
|
+
body: {
|
|
94
|
+
params: JSON.stringify({ limit: 5 }),
|
|
95
|
+
},
|
|
96
|
+
json: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const values = (res?.values || []) as Array<{ id: number; label: string }>;
|
|
100
|
+
return values.map((v): INodePropertyOptions => ({
|
|
101
|
+
name: v.label,
|
|
102
|
+
value: v.id,
|
|
103
|
+
}));
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
async execute(this: IExecuteFunctions) {
|
|
109
|
+
const items = this.getInputData();
|
|
110
|
+
const out: INodeExecutionData[] = [];
|
|
111
|
+
|
|
112
|
+
const resource = this.getNodeParameter('resource', 0) as Resource;
|
|
113
|
+
const operation = this.getNodeParameter('operation', 0) as Operation;
|
|
114
|
+
const entity = ENTITY_MAP[resource];
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < items.length; i++) {
|
|
117
|
+
// === GET ===
|
|
118
|
+
if (operation === 'get') {
|
|
119
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
120
|
+
|
|
121
|
+
const params = {
|
|
122
|
+
where: [['id', '=', id]],
|
|
123
|
+
limit: 1,
|
|
124
|
+
select: [
|
|
125
|
+
'id',
|
|
126
|
+
'display_name',
|
|
127
|
+
'first_name',
|
|
128
|
+
'last_name',
|
|
129
|
+
'contact_type',
|
|
130
|
+
'email.email',
|
|
131
|
+
'phone.phone',
|
|
132
|
+
'address.city',
|
|
133
|
+
'address.country_id:label',
|
|
134
|
+
'address.postal_code',
|
|
135
|
+
'address.street_address',
|
|
136
|
+
],
|
|
137
|
+
join: [
|
|
138
|
+
['Email AS email', 'LEFT'],
|
|
139
|
+
['Phone AS phone', 'LEFT'],
|
|
140
|
+
['Address AS address', 'LEFT'],
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const res = await civicrmApiRequest.call(
|
|
145
|
+
this,
|
|
146
|
+
'POST',
|
|
147
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
148
|
+
params,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
out.push({ json: (res?.values?.[0] ?? {}) as IDataObject });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// === GET MANY ===
|
|
155
|
+
if (operation === 'getMany') {
|
|
156
|
+
const returnAll = this.getNodeParameter('returnAll', i, false) as boolean;
|
|
157
|
+
const limit = this.getNodeParameter('limit', i, 100) as number;
|
|
158
|
+
const whereJson = this.getNodeParameter('whereJson', i, '') as string;
|
|
159
|
+
const where = whereJson ? JSON.parse(whereJson) : undefined;
|
|
160
|
+
|
|
161
|
+
const baseParams = {
|
|
162
|
+
where,
|
|
163
|
+
select: [
|
|
164
|
+
'id',
|
|
165
|
+
'display_name',
|
|
166
|
+
'first_name',
|
|
167
|
+
'last_name',
|
|
168
|
+
'contact_type',
|
|
169
|
+
'email.email',
|
|
170
|
+
'phone.phone',
|
|
171
|
+
'address.city',
|
|
172
|
+
'address.country_id:label',
|
|
173
|
+
'address.postal_code',
|
|
174
|
+
'address.street_address',
|
|
175
|
+
],
|
|
176
|
+
join: [
|
|
177
|
+
['Email AS email', 'LEFT'],
|
|
178
|
+
['Phone AS phone', 'LEFT'],
|
|
179
|
+
['Address AS address', 'LEFT'],
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
if (returnAll) {
|
|
184
|
+
let offset = 0;
|
|
185
|
+
const page = 500;
|
|
186
|
+
while (true) {
|
|
187
|
+
const res = await civicrmApiRequest.call(
|
|
188
|
+
this,
|
|
189
|
+
'POST',
|
|
190
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
191
|
+
{ ...baseParams, limit: page, offset },
|
|
192
|
+
);
|
|
193
|
+
const vals = (res?.values ?? []) as IDataObject[];
|
|
194
|
+
for (const v of vals) out.push({ json: v });
|
|
195
|
+
if (vals.length < page) break;
|
|
196
|
+
offset += page;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
const res = await civicrmApiRequest.call(
|
|
200
|
+
this,
|
|
201
|
+
'POST',
|
|
202
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
203
|
+
{ ...baseParams, limit },
|
|
204
|
+
);
|
|
205
|
+
const vals = (res?.values ?? []) as IDataObject[];
|
|
206
|
+
for (const v of vals) out.push({ json: v });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
// === CREATE ===
|
|
212
|
+
if (operation === 'create') {
|
|
213
|
+
const pairs = this.getNodeParameter('fields.field', i, []) as Array<{
|
|
214
|
+
fieldName: string;
|
|
215
|
+
fieldValue: string;
|
|
216
|
+
}>;
|
|
217
|
+
|
|
218
|
+
const values = Object.fromEntries(
|
|
219
|
+
pairs
|
|
220
|
+
.filter((p) => p.fieldName)
|
|
221
|
+
.map((p) => [p.fieldName, convertValue(p.fieldValue)]),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const params = { values };
|
|
225
|
+
|
|
226
|
+
const res = await civicrmApiRequest.call(
|
|
227
|
+
this,
|
|
228
|
+
'POST',
|
|
229
|
+
`/civicrm/ajax/api4/${entity}/create`,
|
|
230
|
+
params,
|
|
231
|
+
);
|
|
232
|
+
out.push({ json: res as IDataObject });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// === UPDATE ===
|
|
236
|
+
if (operation === 'update') {
|
|
237
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
238
|
+
const pairs = this.getNodeParameter('fields.field', i, []) as Array<{
|
|
239
|
+
fieldName: string;
|
|
240
|
+
fieldValue: string;
|
|
241
|
+
}>;
|
|
242
|
+
|
|
243
|
+
const values = Object.fromEntries(
|
|
244
|
+
pairs
|
|
245
|
+
.filter((p) => p.fieldName)
|
|
246
|
+
.map((p) => [p.fieldName, convertValue(p.fieldValue)]),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const params = {
|
|
250
|
+
values,
|
|
251
|
+
where: [['id', '=', id]],
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const res = await civicrmApiRequest.call(
|
|
255
|
+
this,
|
|
256
|
+
'POST',
|
|
257
|
+
`/civicrm/ajax/api4/${entity}/update`,
|
|
258
|
+
params,
|
|
259
|
+
);
|
|
260
|
+
out.push({ json: res as IDataObject });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// === DELETE ===
|
|
264
|
+
if (operation === 'delete') {
|
|
265
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
266
|
+
|
|
267
|
+
const params = {
|
|
268
|
+
where: [['id', '=', id]],
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const res = await civicrmApiRequest.call(
|
|
272
|
+
this,
|
|
273
|
+
'POST',
|
|
274
|
+
`/civicrm/ajax/api4/${entity}/delete`,
|
|
275
|
+
params,
|
|
276
|
+
);
|
|
277
|
+
out.push({ json: res as IDataObject });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return [out];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Convierte string a número, boolean, objeto o deja string
|
|
287
|
+
*/
|
|
288
|
+
function convertValue(val: string): unknown {
|
|
289
|
+
const t = String(val ?? '').trim();
|
|
290
|
+
if (t === '') return '';
|
|
291
|
+
if (t === 'true') return true;
|
|
292
|
+
if (t === 'false') return false;
|
|
293
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
|
|
294
|
+
try {
|
|
295
|
+
const j = JSON.parse(t);
|
|
296
|
+
if (typeof j === 'object') return j;
|
|
297
|
+
} catch {}
|
|
298
|
+
return val;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Requerido por n8n >=1.110
|
|
302
|
+
export default CiviCrm;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<svg xml:space="preserve" style="max-height: 500px" viewBox="190.4982463465553 39.1683 70.07818371607516 70.33139999999999" y="0px" x="0px" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" id="Layer_1" version="1.1" width="70.07818371607516" height="70.33139999999999">
|
|
2
|
+
<style type="text/css">
|
|
3
|
+
.st0{fill:#086287;}
|
|
4
|
+
.st1{fill:#81C459;}
|
|
5
|
+
</style>
|
|
6
|
+
<g>
|
|
7
|
+
<g>
|
|
8
|
+
<g>
|
|
9
|
+
<path d="M216.26,104.26C216.26,104.26,216.26,104.26,216.26,104.26c-3.77-0.07-5.2-4.43-5.35-4.92l-12.85-42.11
				c-0.76-2.47-0.58-4.46,0.51-5.92c0.71-0.94,2.12-2.04,4.83-1.99c0.99,0.02,1.77,0.19,1.85,0.21l42.78,9.97
				c2.75,0.64,4.44,1.94,5.02,3.85c0.93,3.04-1.65,5.92-1.95,6.24l-29.94,32.12C219.57,103.43,217.91,104.29,216.26,104.26z
				 M203.19,53.73c-0.77-0.01-1.02,0.19-1.06,0.26c-0.22,0.31-0.26,0.44,0.19,1.93l12.85,42.11c0.29,0.9,0.76,1.74,1.18,1.77l0,0
				c0.01,0,0.52,0,1.57-1.13l29.94-32.12c0.55-0.61,1.06-1.6,0.95-1.9c-0.08-0.22-0.43-0.5-1.77-0.81l-42.78-9.97l0,0
				C204.24,53.87,203.7,53.74,203.19,53.73z" class="st0"/>
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
<path d="M211.45,100.83c-1.38,0.51-2.83,0.61-3.96,0.24c0,0,0,0,0,0c-3.57-1.17-3.68-5.74-3.68-6.26l0-41l4.21,0.94
				l0.02,40.15c0.01,0.94,0.25,1.84,0.83,1.94l0,0c0,0,0.3,0.09,1.15-0.3L211.45,100.83z M214.02,94.31l34.62-20.03
				c0.7-0.42,1.53-1.04,1.46-1.54c-0.01-0.07-0.17-0.52-0.92-1.05l2.9-3.3c1.64,1.25,2.47,2.71,2.47,4.35c0,3.17-3.3,5.17-3.68,5.39
				l-35.51,20.4L214.02,94.31z M245.14,69.42l-34.02-19.79c0,0-0.85-0.54-1.34-0.7c-0.73-0.24-1.08,0-1.15,0.05
				c-0.2,0.14-0.27,0.24-0.35,1.24l-4.38-0.82c0.24-1.82,0.95-3.17,2.12-4.03c0.95-0.69,2.62-1.33,5.19-0.5
				c0.94,0.31,1.63,0.7,1.71,0.74l35.39,20.42L245.14,69.42z" class="st1"/>
|
|
13
|
+
</g>
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
<path d="M183.02,66.82c-0.48-0.28-0.86-0.67-1.15-1.15c-0.28-0.48-0.42-1-0.42-1.57c0-0.56,0.14-1.09,0.42-1.57
			s0.66-0.86,1.15-1.15s1-0.42,1.57-0.42s1.09,0.14,1.57,0.42s0.86,0.66,1.15,1.15s0.42,1,0.42,1.57c0,0.57-0.14,1.09-0.42,1.57
			c-0.28,0.48-0.66,0.86-1.15,1.15s-1,0.42-1.57,0.42S183.5,67.1,183.02,66.82z M185.93,66.42c0.41-0.24,0.74-0.57,0.98-0.98
			c0.24-0.41,0.36-0.86,0.36-1.35c0-0.48-0.12-0.93-0.36-1.34c-0.24-0.41-0.57-0.73-0.98-0.98c-0.41-0.24-0.86-0.36-1.35-0.36
			c-0.48,0-0.93,0.12-1.34,0.36c-0.41,0.24-0.73,0.57-0.98,0.98c-0.24,0.41-0.36,0.85-0.36,1.34c0,0.49,0.12,0.94,0.36,1.35
			c0.24,0.41,0.57,0.74,0.98,0.98s0.85,0.36,1.34,0.36C185.07,66.79,185.52,66.66,185.93,66.42z M183.37,65.8
			c-0.06-0.05-0.08-0.12-0.08-0.21v-2.83c0-0.11,0.04-0.21,0.11-0.28s0.17-0.12,0.28-0.12h1.05c0.46,0,0.8,0.1,1.03,0.29
			s0.34,0.45,0.34,0.76c0,0.21-0.06,0.39-0.18,0.53c-0.12,0.14-0.28,0.25-0.46,0.32c0.16,0.06,0.3,0.18,0.42,0.35
			c0.12,0.18,0.18,0.42,0.18,0.74v0.24c0,0.08-0.03,0.15-0.09,0.21c-0.06,0.06-0.13,0.09-0.21,0.09s-0.15-0.03-0.21-0.09
			c-0.06-0.06-0.09-0.13-0.09-0.21v-0.16c0-0.25-0.04-0.46-0.11-0.63c-0.07-0.17-0.25-0.26-0.53-0.26h-0.95v1.06
			c0,0.08-0.03,0.15-0.09,0.21c-0.06,0.06-0.13,0.08-0.21,0.08C183.49,65.88,183.42,65.86,183.37,65.8z M184.7,64.02
			c0.25,0,0.45-0.05,0.6-0.15c0.15-0.1,0.22-0.25,0.22-0.44c0-0.18-0.06-0.32-0.19-0.42c-0.13-0.1-0.34-0.14-0.65-0.14h-0.81v1.15
			H184.7z" class="st0"/>
|
|
17
|
+
</g>
|
|
18
|
+
<g>
|
|
19
|
+
<path d="M31.72,88.55c-2.15-1.18-3.85-2.87-5.08-5.08c-1.23-2.21-1.85-4.79-1.85-7.74s0.62-5.53,1.85-7.74
			c1.23-2.21,2.93-3.9,5.08-5.08c2.15-1.18,4.58-1.77,7.29-1.77c1.84,0,3.49,0.22,4.94,0.65c1.45,0.43,2.89,1.06,4.33,1.87
			c0.6,0.32,0.89,0.84,0.89,1.54c0,0.41-0.15,0.77-0.45,1.08c-0.3,0.31-0.69,0.47-1.18,0.47c-0.27,0-0.51-0.05-0.73-0.16
			c-1.19-0.62-2.36-1.1-3.49-1.42c-1.14-0.32-2.45-0.49-3.94-0.49c-2.33,0-4.31,0.48-5.95,1.44c-1.64,0.96-2.87,2.28-3.7,3.94
			c-0.83,1.67-1.24,3.55-1.24,5.67s0.41,4,1.24,5.67c0.83,1.67,2.06,2.98,3.7,3.94c1.64,0.96,3.62,1.44,5.95,1.44
			c1.49,0,2.8-0.16,3.94-0.49c1.14-0.32,2.3-0.8,3.49-1.42c0.22-0.11,0.46-0.16,0.73-0.16c0.49,0,0.88,0.15,1.18,0.45
			c0.3,0.3,0.45,0.66,0.45,1.1c0,0.7-0.3,1.22-0.89,1.54c-1.44,0.81-2.88,1.44-4.33,1.87c-1.45,0.43-3.09,0.65-4.94,0.65
			C36.3,90.32,33.87,89.73,31.72,88.55z" class="st0"/>
|
|
20
|
+
<path d="M53.47,64.35c-0.41-0.41-0.61-0.89-0.61-1.46v-0.16c0-0.57,0.2-1.06,0.61-1.46c0.41-0.41,0.89-0.61,1.46-0.61
			h0.24c0.57,0,1.06,0.2,1.46,0.61s0.61,0.89,0.61,1.46v0.16c0,0.57-0.2,1.06-0.61,1.46c-0.41,0.41-0.89,0.61-1.46,0.61h-0.24
			C54.37,64.96,53.88,64.76,53.47,64.35z M53.72,89.58c-0.35-0.35-0.53-0.79-0.53-1.3V70.86c0-0.51,0.18-0.96,0.53-1.32
			c0.35-0.37,0.79-0.55,1.3-0.55c0.54,0,0.99,0.18,1.34,0.53c0.35,0.35,0.53,0.8,0.53,1.34v17.43c0,0.51-0.18,0.95-0.55,1.3
			c-0.37,0.35-0.81,0.53-1.32,0.53C54.5,90.11,54.07,89.94,53.72,89.58z" class="st0"/>
|
|
21
|
+
<path d="M68.5,89.69c-0.41-0.28-0.7-0.64-0.89-1.08l-7.07-16.9c-0.14-0.32-0.2-0.61-0.2-0.85
			c0-0.51,0.18-0.96,0.53-1.32c0.35-0.37,0.79-0.55,1.3-0.55c0.35,0,0.68,0.1,1,0.3c0.31,0.2,0.53,0.45,0.67,0.75l6.22,15.76
			l6.22-15.76c0.14-0.3,0.36-0.55,0.67-0.75c0.31-0.2,0.64-0.3,1-0.3c0.51,0,0.95,0.18,1.3,0.55c0.35,0.37,0.53,0.81,0.53,1.32
			c0,0.24-0.07,0.53-0.2,0.85l-7.07,16.9c-0.19,0.43-0.49,0.79-0.89,1.08s-0.85,0.43-1.34,0.43h-0.41
			C69.36,90.11,68.91,89.97,68.5,89.69z" class="st0"/>
|
|
22
|
+
<path d="M83.5,64.35c-0.41-0.41-0.61-0.89-0.61-1.46v-0.16c0-0.57,0.2-1.06,0.61-1.46c0.41-0.41,0.89-0.61,1.46-0.61
			h0.24c0.57,0,1.06,0.2,1.46,0.61s0.61,0.89,0.61,1.46v0.16c0,0.57-0.2,1.06-0.61,1.46c-0.41,0.41-0.89,0.61-1.46,0.61h-0.24
			C84.39,64.96,83.9,64.76,83.5,64.35z M83.74,89.58c-0.35-0.35-0.53-0.79-0.53-1.3V70.86c0-0.51,0.18-0.96,0.53-1.32
			c0.35-0.37,0.79-0.55,1.3-0.55c0.54,0,0.99,0.18,1.34,0.53c0.35,0.35,0.53,0.8,0.53,1.34v17.43c0,0.51-0.18,0.95-0.55,1.3
			c-0.37,0.35-0.81,0.53-1.32,0.53C84.53,90.11,84.09,89.94,83.74,89.58z" class="st0"/>
|
|
23
|
+
<path d="M97.65,88.55c-2.15-1.18-3.85-2.87-5.08-5.08c-1.23-2.21-1.85-4.79-1.85-7.74s0.62-5.53,1.85-7.74
			c1.23-2.21,2.93-3.9,5.08-5.08c2.15-1.18,4.58-1.77,7.29-1.77c1.84,0,3.49,0.22,4.94,0.65c1.45,0.43,2.89,1.06,4.33,1.87
			c0.6,0.32,0.89,0.84,0.89,1.54c0,0.41-0.15,0.77-0.45,1.08c-0.3,0.31-0.69,0.47-1.18,0.47c-0.27,0-0.51-0.05-0.73-0.16
			c-1.19-0.62-2.36-1.1-3.49-1.42c-1.14-0.32-2.45-0.49-3.94-0.49c-2.33,0-4.31,0.48-5.95,1.44c-1.64,0.96-2.87,2.28-3.7,3.94
			c-0.83,1.67-1.24,3.55-1.24,5.67s0.41,4,1.24,5.67c0.83,1.67,2.06,2.98,3.7,3.94c1.64,0.96,3.62,1.44,5.95,1.44
			c1.49,0,2.8-0.16,3.94-0.49c1.14-0.32,2.3-0.8,3.49-1.42c0.22-0.11,0.46-0.16,0.73-0.16c0.49,0,0.88,0.15,1.18,0.45
			c0.3,0.3,0.45,0.66,0.45,1.1c0,0.7-0.3,1.22-0.89,1.54c-1.44,0.81-2.88,1.44-4.33,1.87c-1.45,0.43-3.09,0.65-4.94,0.65
			C102.24,90.32,99.81,89.73,97.65,88.55z" class="st0"/>
|
|
24
|
+
<path d="M119.9,89.58c-0.35-0.35-0.53-0.79-0.53-1.3V63.42c0-0.51,0.18-0.96,0.53-1.32s0.79-0.55,1.3-0.55h9.47
			c3.68,0,6.45,0.76,8.29,2.28c1.84,1.52,2.76,3.51,2.76,5.97c0,1.62-0.45,3.11-1.36,4.45c-0.91,1.34-2.23,2.31-3.96,2.9
			c1.95,0.51,3.28,1.73,4,3.64c0.72,1.91,1.08,3.79,1.08,5.63v1.87c0,0.51-0.18,0.95-0.53,1.3c-0.35,0.35-0.79,0.53-1.3,0.53
			c-0.57,0-1.02-0.17-1.36-0.51c-0.34-0.34-0.51-0.78-0.51-1.32v-1.22c0-1.35-0.12-2.61-0.37-3.76s-0.83-2.18-1.75-3.09
			c-0.92-0.91-2.32-1.36-4.18-1.36h-8.41v9.43c0,0.51-0.18,0.95-0.55,1.3s-0.81,0.53-1.32,0.53
			C120.68,90.11,120.25,89.94,119.9,89.58z M130.42,75.37c2.33,0,4.18-0.46,5.55-1.38c1.37-0.92,2.05-2.22,2.05-3.9
			c0-1.62-0.59-2.87-1.77-3.74c-1.18-0.87-3.19-1.3-6.03-1.3h-7.15v10.32H130.42z" class="st0"/>
|
|
25
|
+
<path d="M146.71,89.58c-0.35-0.35-0.53-0.79-0.53-1.3V64.03c0-0.73,0.26-1.36,0.77-1.89
			c0.51-0.53,1.14-0.79,1.87-0.79h1.34c0.54,0,1.04,0.16,1.48,0.49c0.45,0.33,0.78,0.73,1,1.22l9.22,22.99l9.22-22.99
			c0.22-0.49,0.55-0.89,1-1.22c0.45-0.32,0.94-0.49,1.48-0.49h1.34c0.73,0,1.35,0.26,1.87,0.79c0.51,0.53,0.77,1.16,0.77,1.89v24.25
			c0,0.51-0.18,0.95-0.53,1.3c-0.35,0.35-0.79,0.53-1.3,0.53c-0.52,0-0.96-0.18-1.32-0.53c-0.37-0.35-0.55-0.79-0.55-1.3V65.21
			l-9.38,23.2c-0.22,0.51-0.56,0.93-1.04,1.24c-0.47,0.31-1,0.47-1.56,0.47s-1.09-0.16-1.56-0.47c-0.47-0.31-0.82-0.72-1.04-1.24
			l-9.38-23.2v23.08c0,0.51-0.18,0.95-0.55,1.3s-0.81,0.53-1.32,0.53C147.5,90.11,147.06,89.94,146.71,89.58z" class="st0"/>
|
|
26
|
+
</g>
|
|
27
|
+
</g>
|
|
28
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export const genericFields: INodeProperties[] = [
|
|
3
|
+
{ displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, displayOptions: { show: { operation: ['getMany'] } } },
|
|
4
|
+
{ displayName: 'Limit', name: 'limit', type: 'number', typeOptions: { minValue: 1, maxValue: 1000 }, default: 100, displayOptions: { show: { operation: ['getMany'], returnAll: [false] } } },
|
|
5
|
+
{ displayName: 'Where (JSON)', name: 'whereJson', type: 'string', default: '', placeholder: `[["first_name","=","Alice"]]`, displayOptions: { show: { operation: ['getMany'] } } },
|
|
6
|
+
];
|
|
7
|
+
export const upsertFields: INodeProperties[] = [
|
|
8
|
+
{ displayName: 'Fields', name: 'fields', type: 'fixedCollection', typeOptions: { multipleValues: true }, default: {}, options: [{ name:'field', displayName:'Field', values:[{displayName:'Name',name:'fieldName',type:'string',default:''},{displayName:'Value',name:'fieldValue',type:'string',default:''}]}], displayOptions: { show: { operation: ['create','update'] } } }
|
|
9
|
+
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
|
|
3
|
+
export const resourceProp: INodeProperties = {
|
|
4
|
+
displayName: 'Resource',
|
|
5
|
+
name: 'resource',
|
|
6
|
+
type: 'options',
|
|
7
|
+
default: 'contact',
|
|
8
|
+
options: [
|
|
9
|
+
{ name: 'Contact', value: 'contact' },
|
|
10
|
+
{ name: 'Event', value: 'event' },
|
|
11
|
+
{ name: 'Case', value: 'case' },
|
|
12
|
+
{ name: 'Contribution', value: 'contribution' },
|
|
13
|
+
{ name: 'Membership', value: 'membership' },
|
|
14
|
+
{ name: 'Participant', value: 'participant' },
|
|
15
|
+
{ name: 'Group', value: 'group' },
|
|
16
|
+
{ name: 'Relationship', value: 'relationship' },
|
|
17
|
+
{ name: 'Email', value: 'email' },
|
|
18
|
+
{ name: 'Activity', value: 'activity' },
|
|
19
|
+
{ name: 'Custom API Call', value: 'customApi' },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const operationProp: INodeProperties = {
|
|
24
|
+
displayName: 'Operation',
|
|
25
|
+
name: 'operation',
|
|
26
|
+
type: 'options',
|
|
27
|
+
default: 'getMany',
|
|
28
|
+
options: [
|
|
29
|
+
{ name: 'Create', value: 'create' },
|
|
30
|
+
{ name: 'Delete', value: 'delete' },
|
|
31
|
+
{ name: 'Get', value: 'get' },
|
|
32
|
+
{ name: 'Get Many', value: 'getMany' },
|
|
33
|
+
{ name: 'Update', value: 'update' },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IExecuteFunctions, IHttpRequestOptions, JsonObject } from 'n8n-workflow';
|
|
2
|
+
import { NodeApiError } from 'n8n-workflow';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ejecuta una llamada a la API de CiviCRM v4 (Civi-Go)
|
|
6
|
+
* Usa form-urlencoded con el campo "params" serializado
|
|
7
|
+
*/
|
|
8
|
+
export async function civicrmApiRequest(
|
|
9
|
+
this: IExecuteFunctions,
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: string,
|
|
12
|
+
body: Record<string, unknown>,
|
|
13
|
+
) {
|
|
14
|
+
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi')) as {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
apiToken: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const options: IHttpRequestOptions = {
|
|
20
|
+
method,
|
|
21
|
+
url: `${baseUrl.replace(/\/$/, '')}${path}`,
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
24
|
+
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
25
|
+
},
|
|
26
|
+
// cuerpo plano como espera Civi-Go
|
|
27
|
+
body: {
|
|
28
|
+
params: JSON.stringify(body.params ?? body),
|
|
29
|
+
},
|
|
30
|
+
json: true,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const response = await this.helpers.httpRequest(options);
|
|
35
|
+
return response;
|
|
36
|
+
} catch (error: unknown) {
|
|
37
|
+
throw new NodeApiError(this.getNode(), error as JsonObject);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Devuelve el cuerpo estándar para las llamadas API4 (plano)
|
|
43
|
+
*/
|
|
44
|
+
export function api4(
|
|
45
|
+
entity: string,
|
|
46
|
+
action: string,
|
|
47
|
+
params: Record<string, unknown> = {},
|
|
48
|
+
) {
|
|
49
|
+
// devolvemos los parámetros planos, no anidados
|
|
50
|
+
return params;
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixiam/n8n-nodes-civicrm",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CiviCRM API v4
|
|
3
|
+
"version": "0.3.8",
|
|
4
|
+
"description": "CiviCRM API v4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"n8n-community-node-package",
|
|
@@ -10,12 +10,6 @@
|
|
|
10
10
|
],
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"types": "dist/index.d.ts",
|
|
13
|
-
"files": [
|
|
14
|
-
"dist",
|
|
15
|
-
"icons",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
19
13
|
"scripts": {
|
|
20
14
|
"dev": "n8n-node dev",
|
|
21
15
|
"build": "n8n-node build",
|
|
@@ -30,17 +24,6 @@
|
|
|
30
24
|
"dist/credentials/CiviCrmApi.credentials.js"
|
|
31
25
|
]
|
|
32
26
|
},
|
|
33
|
-
"publishConfig": {
|
|
34
|
-
"access": "public"
|
|
35
|
-
},
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "git+https://git.ixiam.com/dev/research.git"
|
|
39
|
-
},
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://git.ixiam.com/dev/research/issues"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://git.ixiam.com/dev/research#readme",
|
|
44
27
|
"devDependencies": {
|
|
45
28
|
"@n8n/node-cli": "0.11.0",
|
|
46
29
|
"@types/node": "^20.11.30",
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["es2020"],
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"target": "es2020",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"skipLibCheck": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["nodes/**/*.ts", "credentials/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|