@ixiam/n8n-nodes-civicrm 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # n8n-nodes-civicrm
2
+ Community node for **CiviCRM API v4** and **Form Processor**.
3
+
4
+ Install: Settings → Community Nodes → Install → `n8n-nodes-civicrm`
5
+
6
+ Credentials:
7
+ - Base URL: `https://crm.example.org` (no trailing slash)
8
+ - API Token: header `X-Civi-Auth: Bearer <token>`
9
+ - Use the **Save** button to verify connectivity.
10
+
11
+ Entities: Contact, Event, Case, Contribution, Membership → `get|getMany|create|update|delete`
12
+ Form Processor: `execute` with `recordsJson`
@@ -0,0 +1,34 @@
1
+ import type { ICredentialType, INodeProperties, IHttpRequestMethods } from 'n8n-workflow';
2
+ export declare class CiviCrmApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ authenticate: {
7
+ type: "generic";
8
+ properties: {
9
+ headers: {
10
+ 'X-Civi-Auth': string;
11
+ 'Content-Type': string;
12
+ };
13
+ };
14
+ };
15
+ test: {
16
+ request: {
17
+ method: IHttpRequestMethods;
18
+ url: string;
19
+ headers: {
20
+ 'X-Civi-Auth': string;
21
+ 'Content-Type': string;
22
+ };
23
+ body: {
24
+ entity: string;
25
+ action: string;
26
+ params: {
27
+ limit: number;
28
+ };
29
+ };
30
+ json: boolean;
31
+ };
32
+ };
33
+ properties: INodeProperties[];
34
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CiviCrmApi = void 0;
4
+ class CiviCrmApi {
5
+ constructor() {
6
+ this.name = 'civiCrmApi';
7
+ this.displayName = 'CiviCRM API';
8
+ this.documentationUrl = 'https://docs.civicrm.org/dev/en/latest/api/v4/';
9
+ this.authenticate = {
10
+ type: 'generic',
11
+ properties: {
12
+ headers: {
13
+ 'X-Civi-Auth': '={{"Bearer " + $credentials.apiToken}}',
14
+ 'Content-Type': 'application/json',
15
+ },
16
+ },
17
+ };
18
+ // Button "Test" in credential UI
19
+ this.test = {
20
+ request: {
21
+ // IMPORTANT: method must be typed as IHttpRequestMethods
22
+ method: 'POST',
23
+ url: '={{$credentials.baseUrl.replace(/\\/$/, "")}}/civicrm/ajax/api4/Contact/get',
24
+ headers: {
25
+ 'X-Civi-Auth': '={{"Bearer " + $credentials.apiToken}}',
26
+ 'Content-Type': 'application/json',
27
+ },
28
+ body: {
29
+ entity: 'Contact',
30
+ action: 'get',
31
+ params: { limit: 1 },
32
+ },
33
+ json: true,
34
+ },
35
+ };
36
+ this.properties = [
37
+ {
38
+ displayName: 'Base URL',
39
+ name: 'baseUrl',
40
+ type: 'string',
41
+ default: '',
42
+ required: true,
43
+ placeholder: 'https://crm.example.org',
44
+ description: 'Sin la barra final',
45
+ },
46
+ {
47
+ displayName: 'API Token',
48
+ name: 'apiToken',
49
+ type: 'string',
50
+ typeOptions: { password: true },
51
+ default: '',
52
+ required: true,
53
+ description: 'Se envía como X-Civi-Auth: Bearer <token>',
54
+ },
55
+ ];
56
+ }
57
+ }
58
+ exports.CiviCrmApi = CiviCrmApi;
@@ -0,0 +1,13 @@
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ /**
3
+ * Nodo principal CiviCRM para n8n
4
+ */
5
+ export declare class CiviCrm implements INodeType {
6
+ description: INodeTypeDescription;
7
+ methods: {
8
+ loadOptions: {
9
+ getOptionValues(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
10
+ };
11
+ };
12
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
13
+ }
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CiviCrm = void 0;
4
+ const GenericFunctions_1 = require("../transport/GenericFunctions");
5
+ const resources_1 = require("./descriptions/resources");
6
+ const generic_1 = require("./descriptions/generic");
7
+ const ENTITY_MAP = {
8
+ contact: 'Contact',
9
+ event: 'Event',
10
+ case: 'Case',
11
+ contribution: 'Contribution',
12
+ membership: 'Membership',
13
+ };
14
+ /**
15
+ * Nodo principal CiviCRM para n8n
16
+ */
17
+ class CiviCrm {
18
+ constructor() {
19
+ this.description = {
20
+ displayName: 'CiviCRM',
21
+ name: 'civiCrm',
22
+ icon: 'file:civicrm.svg',
23
+ group: ['transform'],
24
+ version: 1,
25
+ description: 'Interact with CiviCRM API v4',
26
+ defaults: { name: 'CiviCRM' },
27
+ inputs: ['main'],
28
+ outputs: ['main'],
29
+ credentials: [{ name: 'civiCrmApi', required: true }],
30
+ properties: [
31
+ resources_1.resourceProp,
32
+ resources_1.operationProp,
33
+ {
34
+ displayName: 'ID',
35
+ name: 'id',
36
+ type: 'number',
37
+ default: 0,
38
+ required: true,
39
+ displayOptions: { show: { operation: ['get', 'update', 'delete'] } },
40
+ },
41
+ ...generic_1.genericFields,
42
+ ...generic_1.upsertFields,
43
+ {
44
+ displayName: 'Processor Name',
45
+ name: 'processor',
46
+ type: 'string',
47
+ default: '',
48
+ placeholder: 'fp_volunteer_registration',
49
+ description: 'Technical name after FormProcessor_',
50
+ displayOptions: { show: { resource: ['formProcessor'], operation: ['execute'] } },
51
+ },
52
+ {
53
+ displayName: 'Records (JSON)',
54
+ name: 'recordsJson',
55
+ type: 'string',
56
+ default: '',
57
+ placeholder: `[{"first_name":"Alice"}]`,
58
+ displayOptions: { show: { resource: ['formProcessor'], operation: ['execute'] } },
59
+ },
60
+ ],
61
+ };
62
+ this.methods = {
63
+ loadOptions: {
64
+ async getOptionValues() {
65
+ const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi'));
66
+ const url = `${baseUrl.replace(/\/$/, '')}/civicrm/ajax/api4/OptionValue/get`;
67
+ const res = await this.helpers.httpRequest({
68
+ method: 'POST',
69
+ url,
70
+ headers: {
71
+ 'X-Civi-Auth': `Bearer ${apiToken}`,
72
+ 'Content-Type': 'application/json',
73
+ },
74
+ body: (0, GenericFunctions_1.api4)('OptionValue', 'get', { limit: 5 }),
75
+ json: true,
76
+ });
77
+ const values = (res?.values || []);
78
+ return values.map((v) => ({
79
+ name: v.label,
80
+ value: v.id,
81
+ }));
82
+ },
83
+ },
84
+ };
85
+ }
86
+ async execute() {
87
+ const items = this.getInputData();
88
+ const out = [];
89
+ const resource = this.getNodeParameter('resource', 0);
90
+ const operation = this.getNodeParameter('operation', 0);
91
+ for (let i = 0; i < items.length; i++) {
92
+ // FORM PROCESSOR
93
+ if (resource === 'formProcessor' && operation === 'execute') {
94
+ const processor = this.getNodeParameter('processor', i);
95
+ const recordsJson = this.getNodeParameter('recordsJson', i, '');
96
+ const records = recordsJson ? JSON.parse(recordsJson) : [];
97
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/FormProcessor_${processor}/save`, { params: { records } });
98
+ out.push({ json: res });
99
+ continue;
100
+ }
101
+ const entity = ENTITY_MAP[resource];
102
+ // GET
103
+ if (operation === 'get') {
104
+ const id = this.getNodeParameter('id', i);
105
+ 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 }));
106
+ out.push({ json: (res?.values?.[0] ?? {}) });
107
+ }
108
+ // GET MANY
109
+ if (operation === 'getMany') {
110
+ const returnAll = this.getNodeParameter('returnAll', i, false);
111
+ const limit = this.getNodeParameter('limit', i, 100);
112
+ const whereJson = this.getNodeParameter('whereJson', i, '');
113
+ const where = whereJson ? JSON.parse(whereJson) : undefined;
114
+ if (returnAll) {
115
+ let offset = 0;
116
+ const page = 500;
117
+ while (true) {
118
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit: page, offset }));
119
+ const vals = (res?.values ?? []);
120
+ for (const v of vals)
121
+ out.push({ json: v });
122
+ if (vals.length < page)
123
+ break;
124
+ offset += page;
125
+ }
126
+ }
127
+ else {
128
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit }));
129
+ const vals = (res?.values ?? []);
130
+ for (const v of vals)
131
+ out.push({ json: v });
132
+ }
133
+ }
134
+ // CREATE
135
+ if (operation === 'create') {
136
+ const pairs = this.getNodeParameter('fields.field', i, []);
137
+ const params = Object.fromEntries(pairs
138
+ .filter((p) => p.fieldName)
139
+ .map((p) => [p.fieldName, convertValue(p.fieldValue)]));
140
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/create`, (0, GenericFunctions_1.api4)(entity, 'create', params));
141
+ out.push({ json: res });
142
+ }
143
+ // UPDATE
144
+ if (operation === 'update') {
145
+ const id = this.getNodeParameter('id', i);
146
+ const pairs = this.getNodeParameter('fields.field', i, []);
147
+ const params = {
148
+ id,
149
+ ...Object.fromEntries(pairs
150
+ .filter((p) => p.fieldName)
151
+ .map((p) => [p.fieldName, convertValue(p.fieldValue)])),
152
+ };
153
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`, (0, GenericFunctions_1.api4)(entity, 'update', params));
154
+ out.push({ json: res });
155
+ }
156
+ // DELETE
157
+ if (operation === 'delete') {
158
+ const id = this.getNodeParameter('id', i);
159
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/delete`, (0, GenericFunctions_1.api4)(entity, 'delete', { id }));
160
+ out.push({ json: res });
161
+ }
162
+ }
163
+ return [out];
164
+ }
165
+ }
166
+ exports.CiviCrm = CiviCrm;
167
+ /**
168
+ * Convierte string a número, boolean, objeto o deja string
169
+ */
170
+ function convertValue(val) {
171
+ const t = String(val ?? '').trim();
172
+ if (t === '')
173
+ return '';
174
+ if (t === 'true')
175
+ return true;
176
+ if (t === 'false')
177
+ return false;
178
+ if (/^-?\d+(\.\d+)?$/.test(t))
179
+ return Number(t);
180
+ try {
181
+ const j = JSON.parse(t);
182
+ if (typeof j === 'object')
183
+ return j;
184
+ }
185
+ catch { }
186
+ return val;
187
+ }
@@ -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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9; 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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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&#xA;&#9;&#9;&#9;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,3 @@
1
+ import type { INodeProperties } from 'n8n-workflow';
2
+ export declare const genericFields: INodeProperties[];
3
+ export declare const upsertFields: INodeProperties[];
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.upsertFields = exports.genericFields = void 0;
4
+ exports.genericFields = [
5
+ { displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, displayOptions: { show: { operation: ['getMany'] } } },
6
+ { displayName: 'Limit', name: 'limit', type: 'number', typeOptions: { minValue: 1, maxValue: 1000 }, default: 100, displayOptions: { show: { operation: ['getMany'], returnAll: [false] } } },
7
+ { displayName: 'Where (JSON)', name: 'whereJson', type: 'string', default: '', placeholder: `[["first_name","=","Alice"]]`, displayOptions: { show: { operation: ['getMany'] } } },
8
+ ];
9
+ exports.upsertFields = [
10
+ { 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'] } } }
11
+ ];
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const resourceProp: INodeProperties;
3
+ export declare const operationProp: INodeProperties;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ { name: 'Form Processor', value: 'formProcessor' },
11
+ ];
12
+ exports.resourceProp = {
13
+ displayName: 'Resource',
14
+ name: 'resource',
15
+ type: 'options',
16
+ default: 'contact',
17
+ options: RESOURCE_OPTIONS, // sin readonly/as const
18
+ };
19
+ exports.operationProp = {
20
+ displayName: 'Operation',
21
+ name: 'operation',
22
+ type: 'options',
23
+ displayOptions: {
24
+ show: { resource: ['contact', 'event', 'case', 'contribution', 'membership', 'formProcessor'] },
25
+ },
26
+ default: 'get',
27
+ options: [
28
+ { name: 'Get', value: 'get' },
29
+ { name: 'Get Many', value: 'getMany' },
30
+ { name: 'Create', value: 'create' },
31
+ { name: 'Update', value: 'update' },
32
+ { name: 'Delete', value: 'delete' },
33
+ // Solo para formProcessor
34
+ { name: 'Execute', value: 'execute', action: 'Execute FormProcessor', description: 'Run a FormProcessor', },
35
+ ], // tipo mutable
36
+ };
@@ -0,0 +1,13 @@
1
+ import { type IExecuteFunctions } from 'n8n-workflow';
2
+ /**
3
+ * Ejecuta una llamada a la API de CiviCRM v4
4
+ */
5
+ export declare function civicrmApiRequest(this: IExecuteFunctions, method: 'POST', path: string, body: Record<string, unknown>): Promise<any>;
6
+ /**
7
+ * Devuelve el formato estándar para una llamada API4
8
+ */
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
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.api4 = exports.civicrmApiRequest = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ /**
6
+ * Ejecuta una llamada a la API de CiviCRM v4
7
+ */
8
+ async function civicrmApiRequest(method, path, body) {
9
+ const { baseUrl, apiToken } = (await this.getCredentials('civiCrmApi'));
10
+ const options = {
11
+ method,
12
+ url: `${baseUrl}${path}`,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'X-Civi-Auth': `Bearer ${apiToken}`,
16
+ },
17
+ body,
18
+ json: true,
19
+ };
20
+ try {
21
+ const response = await this.helpers.httpRequest(options);
22
+ if (response?.is_error) {
23
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), response);
24
+ }
25
+ return response;
26
+ }
27
+ catch (error) {
28
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
29
+ }
30
+ }
31
+ exports.civicrmApiRequest = civicrmApiRequest;
32
+ /**
33
+ * Devuelve el formato estándar para una llamada API4
34
+ */
35
+ function api4(entity, action, params = {}) {
36
+ return { entity, action, params };
37
+ }
38
+ exports.api4 = api4;
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@ixiam/n8n-nodes-civicrm",
3
+ "version": "0.2.0",
4
+ "description": "CiviCRM API v4 for n8n",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "n8n-community-node-package",
8
+ "civicrm",
9
+ "api4"
10
+ ],
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "files": [
14
+ "dist",
15
+ "icons",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "n8n": {
20
+ "nodes": [
21
+ "dist/nodes/CiviCrm/CiviCrm.node.js"
22
+ ],
23
+ "credentials": [
24
+ "dist/credentials/CiviCrmApi.credentials.js"
25
+ ]
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://git.ixiam.com/dev/research.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://git.ixiam.com/dev/research/issues"
36
+ },
37
+ "homepage": "https://git.ixiam.com/dev/research#readme",
38
+ "devDependencies": {
39
+ "@n8n/node-cli": "0.11.0",
40
+ "@types/node": "^20.11.30",
41
+ "@typescript-eslint/eslint-plugin": "6.21.0",
42
+ "@typescript-eslint/parser": "6.21.0",
43
+ "eslint": "8.57.0",
44
+ "eslint-plugin-n8n-nodes-base": "^1.16.3",
45
+ "n8n-core": "^1",
46
+ "n8n-workflow": "^1",
47
+ "typescript": "5.3.3"
48
+ },
49
+ "scripts": {
50
+ "dev": "n8n-node dev",
51
+ "build": "n8n-node build",
52
+ "lint": "n8n-node lint",
53
+ "release": "n8n-node release"
54
+ }
55
+ }