@ixiam/n8n-nodes-civicrm 0.2.4 → 0.3.4
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 +24 -34
- 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 -10
- package/nodes/CiviCrm/CiviCrm.node.ts +243 -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 +50 -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,12 +57,15 @@ 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',
|
|
56
61
|
},
|
|
57
|
-
body:
|
|
58
|
-
|
|
62
|
+
body: {
|
|
63
|
+
params: JSON.stringify({ limit: 5 }),
|
|
64
|
+
},
|
|
65
|
+
json: false,
|
|
59
66
|
});
|
|
60
|
-
const
|
|
67
|
+
const parsed = JSON.parse(res);
|
|
68
|
+
const values = (parsed?.values || []);
|
|
61
69
|
return values.map((v) => ({
|
|
62
70
|
name: v.label,
|
|
63
71
|
value: v.id,
|
|
@@ -71,37 +79,20 @@ class CiviCrm {
|
|
|
71
79
|
const out = [];
|
|
72
80
|
const resource = this.getNodeParameter('resource', 0);
|
|
73
81
|
const operation = this.getNodeParameter('operation', 0);
|
|
82
|
+
const entity = ENTITY_MAP[resource];
|
|
74
83
|
for (let i = 0; i < items.length; i++) {
|
|
75
|
-
|
|
76
|
-
// GET
|
|
84
|
+
// === GET ===
|
|
77
85
|
if (operation === 'get') {
|
|
78
86
|
const id = this.getNodeParameter('id', i);
|
|
79
87
|
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where: [['id', '=', id]], limit: 1 }));
|
|
80
88
|
out.push({ json: (res?.values?.[0] ?? {}) });
|
|
81
89
|
}
|
|
82
|
-
// GET MANY
|
|
90
|
+
// === GET MANY ===
|
|
83
91
|
if (operation === 'getMany') {
|
|
84
92
|
const returnAll = this.getNodeParameter('returnAll', i, false);
|
|
85
93
|
const limit = this.getNodeParameter('limit', i, 100);
|
|
86
94
|
const whereJson = this.getNodeParameter('whereJson', i, '');
|
|
87
|
-
|
|
88
|
-
if (whereJson) {
|
|
89
|
-
try {
|
|
90
|
-
where = JSON.parse(whereJson);
|
|
91
|
-
}
|
|
92
|
-
catch (e) {
|
|
93
|
-
throw new Error('Where (JSON) debe ser JSON válido: usa un objeto o un array de condiciones.');
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
// Normalize where: allow object or array; API4 accepts both
|
|
97
|
-
if (where && typeof where === 'object' && !Array.isArray(where)) {
|
|
98
|
-
where = where;
|
|
99
|
-
}
|
|
100
|
-
if (Array.isArray(where)) {
|
|
101
|
-
// leave as-is; expected [[field, op, value], ...]
|
|
102
|
-
}
|
|
103
|
-
// Cap limit to a reasonable page size
|
|
104
|
-
const cappedLimit = Math.max(1, Math.min(limit, 500));
|
|
95
|
+
const where = whereJson ? JSON.parse(whereJson) : undefined;
|
|
105
96
|
if (returnAll) {
|
|
106
97
|
let offset = 0;
|
|
107
98
|
const page = 500;
|
|
@@ -116,16 +107,13 @@ class CiviCrm {
|
|
|
116
107
|
}
|
|
117
108
|
}
|
|
118
109
|
else {
|
|
119
|
-
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit
|
|
120
|
-
|
|
121
|
-
if (vals.length > cappedLimit) {
|
|
122
|
-
vals = vals.slice(0, cappedLimit);
|
|
123
|
-
}
|
|
110
|
+
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit }));
|
|
111
|
+
const vals = (res?.values ?? []);
|
|
124
112
|
for (const v of vals)
|
|
125
113
|
out.push({ json: v });
|
|
126
114
|
}
|
|
127
115
|
}
|
|
128
|
-
// CREATE
|
|
116
|
+
// === CREATE ===
|
|
129
117
|
if (operation === 'create') {
|
|
130
118
|
const pairs = this.getNodeParameter('fields.field', i, []);
|
|
131
119
|
const params = Object.fromEntries(pairs
|
|
@@ -134,7 +122,7 @@ class CiviCrm {
|
|
|
134
122
|
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/create`, (0, GenericFunctions_1.api4)(entity, 'create', params));
|
|
135
123
|
out.push({ json: res });
|
|
136
124
|
}
|
|
137
|
-
// UPDATE
|
|
125
|
+
// === UPDATE ===
|
|
138
126
|
if (operation === 'update') {
|
|
139
127
|
const id = this.getNodeParameter('id', i);
|
|
140
128
|
const pairs = this.getNodeParameter('fields.field', i, []);
|
|
@@ -147,7 +135,7 @@ class CiviCrm {
|
|
|
147
135
|
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`, (0, GenericFunctions_1.api4)(entity, 'update', params));
|
|
148
136
|
out.push({ json: res });
|
|
149
137
|
}
|
|
150
|
-
// DELETE
|
|
138
|
+
// === DELETE ===
|
|
151
139
|
if (operation === 'delete') {
|
|
152
140
|
const id = this.getNodeParameter('id', i);
|
|
153
141
|
const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/delete`, (0, GenericFunctions_1.api4)(entity, 'delete', { id }));
|
|
@@ -179,3 +167,5 @@ function convertValue(val) {
|
|
|
179
167
|
catch { }
|
|
180
168
|
return val;
|
|
181
169
|
}
|
|
170
|
+
// 👇 ESTA LÍNEA ES NECESARIA EN n8n >=1.110
|
|
171
|
+
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 (form-urlencoded, formato Civi-Go)
|
|
4
4
|
*/
|
|
5
5
|
export declare function civicrmApiRequest(this: IExecuteFunctions, method: 'POST', path: string, body: Record<string, unknown>): Promise<any>;
|
|
6
6
|
/**
|
|
7
|
-
* Devuelve el formato
|
|
7
|
+
* Devuelve el formato esperado por Civi-Go
|
|
8
|
+
* Solo los parámetros planos: where, limit, select...
|
|
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,25 @@ 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 (form-urlencoded, formato Civi-Go)
|
|
7
7
|
*/
|
|
8
8
|
async function civicrmApiRequest(method, path, body) {
|
|
9
9
|
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi'));
|
|
10
10
|
const options = {
|
|
11
11
|
method,
|
|
12
|
-
url: `${baseUrl}${path}`,
|
|
12
|
+
url: `${baseUrl.replace(/\/$/, '')}${path}`,
|
|
13
13
|
headers: {
|
|
14
|
-
'Content-Type': 'application/
|
|
14
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
15
15
|
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
16
16
|
},
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Civi-Go exige el campo 'params' con el JSON serializado
|
|
18
|
+
body: {
|
|
19
|
+
params: JSON.stringify(body),
|
|
20
|
+
},
|
|
21
|
+
json: true, // importante: dejar que n8n maneje el parseo
|
|
19
22
|
};
|
|
20
23
|
try {
|
|
21
24
|
const response = await this.helpers.httpRequest(options);
|
|
22
|
-
if (response?.is_error) {
|
|
23
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), response);
|
|
24
|
-
}
|
|
25
25
|
return response;
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
@@ -30,9 +30,10 @@ async function civicrmApiRequest(method, path, body) {
|
|
|
30
30
|
}
|
|
31
31
|
exports.civicrmApiRequest = civicrmApiRequest;
|
|
32
32
|
/**
|
|
33
|
-
* Devuelve el formato
|
|
33
|
+
* Devuelve el formato esperado por Civi-Go
|
|
34
|
+
* Solo los parámetros planos: where, limit, select...
|
|
34
35
|
*/
|
|
35
36
|
function api4(entity, action, params = {}) {
|
|
36
|
-
return
|
|
37
|
+
return params;
|
|
37
38
|
}
|
|
38
39
|
exports.api4 = api4;
|
|
@@ -0,0 +1,243 @@
|
|
|
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, api4 } 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
|
+
|
|
44
|
+
type Operation = 'get' | 'getMany' | 'create' | 'update' | 'delete';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Nodo principal CiviCRM para n8n
|
|
48
|
+
*/
|
|
49
|
+
export class CiviCrm implements INodeType {
|
|
50
|
+
description: INodeTypeDescription = {
|
|
51
|
+
displayName: 'CiviCRM',
|
|
52
|
+
name: 'civiCrm',
|
|
53
|
+
icon: 'file:civicrm.svg',
|
|
54
|
+
group: ['transform'],
|
|
55
|
+
version: 1,
|
|
56
|
+
description: 'Interact with CiviCRM API v4 (Civi-Go compatible)',
|
|
57
|
+
defaults: { name: 'CiviCRM' },
|
|
58
|
+
inputs: ['main'],
|
|
59
|
+
outputs: ['main'],
|
|
60
|
+
credentials: [{ name: 'civiCrmApi', required: true }],
|
|
61
|
+
properties: [
|
|
62
|
+
resourceProp,
|
|
63
|
+
operationProp,
|
|
64
|
+
{
|
|
65
|
+
displayName: 'ID',
|
|
66
|
+
name: 'id',
|
|
67
|
+
type: 'number',
|
|
68
|
+
default: 0,
|
|
69
|
+
required: true,
|
|
70
|
+
displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
|
|
71
|
+
},
|
|
72
|
+
...genericFields,
|
|
73
|
+
...upsertFields,
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
methods = {
|
|
78
|
+
loadOptions: {
|
|
79
|
+
async getOptionValues(this: ILoadOptionsFunctions) {
|
|
80
|
+
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi')) as {
|
|
81
|
+
baseUrl: string;
|
|
82
|
+
apiToken: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const url = `${baseUrl.replace(/\/$/, '')}/civicrm/ajax/api4/OptionValue/get`;
|
|
86
|
+
|
|
87
|
+
const res = await this.helpers.httpRequest({
|
|
88
|
+
method: 'POST',
|
|
89
|
+
url,
|
|
90
|
+
headers: {
|
|
91
|
+
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
92
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
93
|
+
},
|
|
94
|
+
body: {
|
|
95
|
+
params: JSON.stringify({ limit: 5 }),
|
|
96
|
+
},
|
|
97
|
+
json: false,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const parsed = JSON.parse(res);
|
|
101
|
+
const values = (parsed?.values || []) as Array<{ id: number; label: string }>;
|
|
102
|
+
return values.map((v): INodePropertyOptions => ({
|
|
103
|
+
name: v.label,
|
|
104
|
+
value: v.id,
|
|
105
|
+
}));
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
async execute(this: IExecuteFunctions) {
|
|
111
|
+
const items = this.getInputData();
|
|
112
|
+
const out: INodeExecutionData[] = [];
|
|
113
|
+
|
|
114
|
+
const resource = this.getNodeParameter('resource', 0) as Resource;
|
|
115
|
+
const operation = this.getNodeParameter('operation', 0) as Operation;
|
|
116
|
+
const entity = ENTITY_MAP[resource];
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < items.length; i++) {
|
|
119
|
+
// === GET ===
|
|
120
|
+
if (operation === 'get') {
|
|
121
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
122
|
+
const res = await civicrmApiRequest.call(
|
|
123
|
+
this,
|
|
124
|
+
'POST',
|
|
125
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
126
|
+
api4(entity, 'get', { where: [['id', '=', id]], limit: 1 }),
|
|
127
|
+
);
|
|
128
|
+
out.push({ json: (res?.values?.[0] ?? {}) as IDataObject });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// === GET MANY ===
|
|
132
|
+
if (operation === 'getMany') {
|
|
133
|
+
const returnAll = this.getNodeParameter('returnAll', i, false) as boolean;
|
|
134
|
+
const limit = this.getNodeParameter('limit', i, 100) as number;
|
|
135
|
+
const whereJson = this.getNodeParameter('whereJson', i, '') as string;
|
|
136
|
+
const where = whereJson ? JSON.parse(whereJson) : undefined;
|
|
137
|
+
|
|
138
|
+
if (returnAll) {
|
|
139
|
+
let offset = 0;
|
|
140
|
+
const page = 500;
|
|
141
|
+
while (true) {
|
|
142
|
+
const res = await civicrmApiRequest.call(
|
|
143
|
+
this,
|
|
144
|
+
'POST',
|
|
145
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
146
|
+
api4(entity, 'get', { where, limit: page, offset }),
|
|
147
|
+
);
|
|
148
|
+
const vals = (res?.values ?? []) as IDataObject[];
|
|
149
|
+
for (const v of vals) out.push({ json: v });
|
|
150
|
+
if (vals.length < page) break;
|
|
151
|
+
offset += page;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
const res = await civicrmApiRequest.call(
|
|
155
|
+
this,
|
|
156
|
+
'POST',
|
|
157
|
+
`/civicrm/ajax/api4/${entity}/get`,
|
|
158
|
+
api4(entity, 'get', { where, limit }),
|
|
159
|
+
);
|
|
160
|
+
const vals = (res?.values ?? []) as IDataObject[];
|
|
161
|
+
for (const v of vals) out.push({ json: v });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// === CREATE ===
|
|
166
|
+
if (operation === 'create') {
|
|
167
|
+
const pairs = this.getNodeParameter('fields.field', i, []) as Array<{
|
|
168
|
+
fieldName: string;
|
|
169
|
+
fieldValue: string;
|
|
170
|
+
}>;
|
|
171
|
+
const params = Object.fromEntries(
|
|
172
|
+
pairs
|
|
173
|
+
.filter((p) => p.fieldName)
|
|
174
|
+
.map((p) => [p.fieldName, convertValue(p.fieldValue)]),
|
|
175
|
+
);
|
|
176
|
+
const res = await civicrmApiRequest.call(
|
|
177
|
+
this,
|
|
178
|
+
'POST',
|
|
179
|
+
`/civicrm/ajax/api4/${entity}/create`,
|
|
180
|
+
api4(entity, 'create', params),
|
|
181
|
+
);
|
|
182
|
+
out.push({ json: res as IDataObject });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// === UPDATE ===
|
|
186
|
+
if (operation === 'update') {
|
|
187
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
188
|
+
const pairs = this.getNodeParameter('fields.field', i, []) as Array<{
|
|
189
|
+
fieldName: string;
|
|
190
|
+
fieldValue: string;
|
|
191
|
+
}>;
|
|
192
|
+
const params = {
|
|
193
|
+
id,
|
|
194
|
+
...Object.fromEntries(
|
|
195
|
+
pairs
|
|
196
|
+
.filter((p) => p.fieldName)
|
|
197
|
+
.map((p) => [p.fieldName, convertValue(p.fieldValue)]),
|
|
198
|
+
),
|
|
199
|
+
};
|
|
200
|
+
const res = await civicrmApiRequest.call(
|
|
201
|
+
this,
|
|
202
|
+
'POST',
|
|
203
|
+
`/civicrm/ajax/api4/${entity}/update`,
|
|
204
|
+
api4(entity, 'update', params),
|
|
205
|
+
);
|
|
206
|
+
out.push({ json: res as IDataObject });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// === DELETE ===
|
|
210
|
+
if (operation === 'delete') {
|
|
211
|
+
const id = this.getNodeParameter('id', i) as number;
|
|
212
|
+
const res = await civicrmApiRequest.call(
|
|
213
|
+
this,
|
|
214
|
+
'POST',
|
|
215
|
+
`/civicrm/ajax/api4/${entity}/delete`,
|
|
216
|
+
api4(entity, 'delete', { id }),
|
|
217
|
+
);
|
|
218
|
+
out.push({ json: res as IDataObject });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return [out];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Convierte string a número, boolean, objeto o deja string
|
|
228
|
+
*/
|
|
229
|
+
function convertValue(val: string): unknown {
|
|
230
|
+
const t = String(val ?? '').trim();
|
|
231
|
+
if (t === '') return '';
|
|
232
|
+
if (t === 'true') return true;
|
|
233
|
+
if (t === 'false') return false;
|
|
234
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
|
|
235
|
+
try {
|
|
236
|
+
const j = JSON.parse(t);
|
|
237
|
+
if (typeof j === 'object') return j;
|
|
238
|
+
} catch {}
|
|
239
|
+
return val;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 👇 ESTA LÍNEA ES NECESARIA EN n8n >=1.110
|
|
243
|
+
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,50 @@
|
|
|
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 (form-urlencoded, formato Civi-Go)
|
|
6
|
+
*/
|
|
7
|
+
export async function civicrmApiRequest(
|
|
8
|
+
this: IExecuteFunctions,
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: string,
|
|
11
|
+
body: Record<string, unknown>,
|
|
12
|
+
) {
|
|
13
|
+
const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi')) as {
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
apiToken: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const options: IHttpRequestOptions = {
|
|
19
|
+
method,
|
|
20
|
+
url: `${baseUrl.replace(/\/$/, '')}${path}`,
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
23
|
+
'X-Civi-Auth': `Bearer ${apiToken}`,
|
|
24
|
+
},
|
|
25
|
+
// Civi-Go exige el campo 'params' con el JSON serializado
|
|
26
|
+
body: {
|
|
27
|
+
params: JSON.stringify(body),
|
|
28
|
+
},
|
|
29
|
+
json: true, // importante: dejar que n8n maneje el parseo
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const response = await this.helpers.httpRequest(options);
|
|
34
|
+
return response;
|
|
35
|
+
} catch (error: unknown) {
|
|
36
|
+
throw new NodeApiError(this.getNode(), error as JsonObject);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Devuelve el formato esperado por Civi-Go
|
|
42
|
+
* Solo los parámetros planos: where, limit, select...
|
|
43
|
+
*/
|
|
44
|
+
export function api4(
|
|
45
|
+
entity: string,
|
|
46
|
+
action: string,
|
|
47
|
+
params: Record<string, unknown> = {},
|
|
48
|
+
) {
|
|
49
|
+
return params;
|
|
50
|
+
}
|
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.4",
|
|
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
|
+
}
|