@ixiam/n8n-nodes-civicrm 0.3.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.
@@ -62,10 +62,9 @@ class CiviCrm {
62
62
  body: {
63
63
  params: JSON.stringify({ limit: 5 }),
64
64
  },
65
- json: false,
65
+ json: true,
66
66
  });
67
- const parsed = JSON.parse(res);
68
- const values = (parsed?.values || []);
67
+ const values = (res?.values || []);
69
68
  return values.map((v) => ({
70
69
  name: v.label,
71
70
  value: v.id,
@@ -84,7 +83,29 @@ class CiviCrm {
84
83
  // === GET ===
85
84
  if (operation === 'get') {
86
85
  const id = this.getNodeParameter('id', i);
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 }));
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);
88
109
  out.push({ json: (res?.values?.[0] ?? {}) });
89
110
  }
90
111
  // === GET MANY ===
@@ -93,11 +114,32 @@ class CiviCrm {
93
114
  const limit = this.getNodeParameter('limit', i, 100);
94
115
  const whereJson = this.getNodeParameter('whereJson', i, '');
95
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
+ };
96
138
  if (returnAll) {
97
139
  let offset = 0;
98
140
  const page = 500;
99
141
  while (true) {
100
- const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit: page, offset }));
142
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, { ...baseParams, limit: page, offset });
101
143
  const vals = (res?.values ?? []);
102
144
  for (const v of vals)
103
145
  out.push({ json: v });
@@ -107,7 +149,7 @@ class CiviCrm {
107
149
  }
108
150
  }
109
151
  else {
110
- const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, (0, GenericFunctions_1.api4)(entity, 'get', { where, limit }));
152
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/get`, { ...baseParams, limit });
111
153
  const vals = (res?.values ?? []);
112
154
  for (const v of vals)
113
155
  out.push({ json: v });
@@ -116,29 +158,34 @@ class CiviCrm {
116
158
  // === CREATE ===
117
159
  if (operation === 'create') {
118
160
  const pairs = this.getNodeParameter('fields.field', i, []);
119
- const params = Object.fromEntries(pairs
161
+ const values = Object.fromEntries(pairs
120
162
  .filter((p) => p.fieldName)
121
163
  .map((p) => [p.fieldName, convertValue(p.fieldValue)]));
122
- const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/create`, (0, GenericFunctions_1.api4)(entity, 'create', params));
164
+ const params = { values };
165
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/create`, params);
123
166
  out.push({ json: res });
124
167
  }
125
168
  // === UPDATE ===
126
169
  if (operation === 'update') {
127
170
  const id = this.getNodeParameter('id', i);
128
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)]));
129
175
  const params = {
130
- id,
131
- ...Object.fromEntries(pairs
132
- .filter((p) => p.fieldName)
133
- .map((p) => [p.fieldName, convertValue(p.fieldValue)])),
176
+ values,
177
+ where: [['id', '=', id]],
134
178
  };
135
- const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`, (0, GenericFunctions_1.api4)(entity, 'update', params));
179
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/update`, params);
136
180
  out.push({ json: res });
137
181
  }
138
182
  // === DELETE ===
139
183
  if (operation === 'delete') {
140
184
  const id = this.getNodeParameter('id', i);
141
- const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/delete`, (0, GenericFunctions_1.api4)(entity, 'delete', { id }));
185
+ const params = {
186
+ where: [['id', '=', id]],
187
+ };
188
+ const res = await GenericFunctions_1.civicrmApiRequest.call(this, 'POST', `/civicrm/ajax/api4/${entity}/delete`, params);
142
189
  out.push({ json: res });
143
190
  }
144
191
  }
@@ -167,5 +214,5 @@ function convertValue(val) {
167
214
  catch { }
168
215
  return val;
169
216
  }
170
- // 👇 ESTA LÍNEA ES NECESARIA EN n8n >=1.110
217
+ // Requerido por n8n >=1.110
171
218
  exports.default = CiviCrm;
@@ -1,10 +1,10 @@
1
1
  import type { IExecuteFunctions } from 'n8n-workflow';
2
2
  /**
3
- * Ejecuta una llamada a la API de CiviCRM v4 (form-urlencoded, formato Civi-Go)
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 formato esperado por Civi-Go
8
- * Solo los parámetros planos: where, limit, select...
8
+ * Devuelve el cuerpo estándar para las llamadas API4 (plano)
9
9
  */
10
10
  export declare function api4(entity: string, action: string, params?: Record<string, unknown>): Record<string, unknown>;
@@ -3,7 +3,8 @@ 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 (form-urlencoded, formato Civi-Go)
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'));
@@ -14,11 +15,11 @@ async function civicrmApiRequest(method, path, body) {
14
15
  'Content-Type': 'application/x-www-form-urlencoded',
15
16
  'X-Civi-Auth': `Bearer ${apiToken}`,
16
17
  },
17
- // Civi-Go exige el campo 'params' con el JSON serializado
18
+ // cuerpo plano como espera Civi-Go
18
19
  body: {
19
- params: JSON.stringify(body),
20
+ params: JSON.stringify(body.params ?? body),
20
21
  },
21
- json: true, // importante: dejar que n8n maneje el parseo
22
+ json: true,
22
23
  };
23
24
  try {
24
25
  const response = await this.helpers.httpRequest(options);
@@ -30,10 +31,10 @@ async function civicrmApiRequest(method, path, body) {
30
31
  }
31
32
  exports.civicrmApiRequest = civicrmApiRequest;
32
33
  /**
33
- * Devuelve el formato esperado por Civi-Go
34
- * Solo los parámetros planos: where, limit, select...
34
+ * Devuelve el cuerpo estándar para las llamadas API4 (plano)
35
35
  */
36
36
  function api4(entity, action, params = {}) {
37
+ // devolvemos los parámetros planos, no anidados
37
38
  return params;
38
39
  }
39
40
  exports.api4 = api4;
@@ -8,7 +8,7 @@ import type {
8
8
  IDataObject,
9
9
  } from 'n8n-workflow';
10
10
 
11
- import { civicrmApiRequest, api4 } from '../transport/GenericFunctions';
11
+ import { civicrmApiRequest } from '../transport/GenericFunctions';
12
12
  import { resourceProp, operationProp } from './descriptions/resources';
13
13
  import { genericFields, upsertFields } from './descriptions/generic';
14
14
 
@@ -40,7 +40,6 @@ const ENTITY_MAP: Record<Resource, string> = {
40
40
  activity: 'Activity',
41
41
  };
42
42
 
43
-
44
43
  type Operation = 'get' | 'getMany' | 'create' | 'update' | 'delete';
45
44
 
46
45
  /**
@@ -94,11 +93,10 @@ export class CiviCrm implements INodeType {
94
93
  body: {
95
94
  params: JSON.stringify({ limit: 5 }),
96
95
  },
97
- json: false,
96
+ json: true,
98
97
  });
99
98
 
100
- const parsed = JSON.parse(res);
101
- const values = (parsed?.values || []) as Array<{ id: number; label: string }>;
99
+ const values = (res?.values || []) as Array<{ id: number; label: string }>;
102
100
  return values.map((v): INodePropertyOptions => ({
103
101
  name: v.label,
104
102
  value: v.id,
@@ -117,50 +115,98 @@ export class CiviCrm implements INodeType {
117
115
 
118
116
  for (let i = 0; i < items.length; i++) {
119
117
  // === 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
- }
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
+ }
130
209
 
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
210
 
165
211
  // === CREATE ===
166
212
  if (operation === 'create') {
@@ -168,16 +214,20 @@ export class CiviCrm implements INodeType {
168
214
  fieldName: string;
169
215
  fieldValue: string;
170
216
  }>;
171
- const params = Object.fromEntries(
217
+
218
+ const values = Object.fromEntries(
172
219
  pairs
173
220
  .filter((p) => p.fieldName)
174
221
  .map((p) => [p.fieldName, convertValue(p.fieldValue)]),
175
222
  );
223
+
224
+ const params = { values };
225
+
176
226
  const res = await civicrmApiRequest.call(
177
227
  this,
178
228
  'POST',
179
229
  `/civicrm/ajax/api4/${entity}/create`,
180
- api4(entity, 'create', params),
230
+ params,
181
231
  );
182
232
  out.push({ json: res as IDataObject });
183
233
  }
@@ -189,19 +239,23 @@ export class CiviCrm implements INodeType {
189
239
  fieldName: string;
190
240
  fieldValue: string;
191
241
  }>;
242
+
243
+ const values = Object.fromEntries(
244
+ pairs
245
+ .filter((p) => p.fieldName)
246
+ .map((p) => [p.fieldName, convertValue(p.fieldValue)]),
247
+ );
248
+
192
249
  const params = {
193
- id,
194
- ...Object.fromEntries(
195
- pairs
196
- .filter((p) => p.fieldName)
197
- .map((p) => [p.fieldName, convertValue(p.fieldValue)]),
198
- ),
250
+ values,
251
+ where: [['id', '=', id]],
199
252
  };
253
+
200
254
  const res = await civicrmApiRequest.call(
201
255
  this,
202
256
  'POST',
203
257
  `/civicrm/ajax/api4/${entity}/update`,
204
- api4(entity, 'update', params),
258
+ params,
205
259
  );
206
260
  out.push({ json: res as IDataObject });
207
261
  }
@@ -209,11 +263,16 @@ export class CiviCrm implements INodeType {
209
263
  // === DELETE ===
210
264
  if (operation === 'delete') {
211
265
  const id = this.getNodeParameter('id', i) as number;
266
+
267
+ const params = {
268
+ where: [['id', '=', id]],
269
+ };
270
+
212
271
  const res = await civicrmApiRequest.call(
213
272
  this,
214
273
  'POST',
215
274
  `/civicrm/ajax/api4/${entity}/delete`,
216
- api4(entity, 'delete', { id }),
275
+ params,
217
276
  );
218
277
  out.push({ json: res as IDataObject });
219
278
  }
@@ -239,5 +298,5 @@ function convertValue(val: string): unknown {
239
298
  return val;
240
299
  }
241
300
 
242
- // 👇 ESTA LÍNEA ES NECESARIA EN n8n >=1.110
301
+ // Requerido por n8n >=1.110
243
302
  export default CiviCrm;
@@ -2,7 +2,8 @@ import type { IExecuteFunctions, IHttpRequestOptions, JsonObject } from 'n8n-wor
2
2
  import { NodeApiError } from 'n8n-workflow';
3
3
 
4
4
  /**
5
- * Ejecuta una llamada a la API de CiviCRM v4 (form-urlencoded, formato Civi-Go)
5
+ * Ejecuta una llamada a la API de CiviCRM v4 (Civi-Go)
6
+ * Usa form-urlencoded con el campo "params" serializado
6
7
  */
7
8
  export async function civicrmApiRequest(
8
9
  this: IExecuteFunctions,
@@ -22,11 +23,11 @@ export async function civicrmApiRequest(
22
23
  'Content-Type': 'application/x-www-form-urlencoded',
23
24
  'X-Civi-Auth': `Bearer ${apiToken}`,
24
25
  },
25
- // Civi-Go exige el campo 'params' con el JSON serializado
26
+ // cuerpo plano como espera Civi-Go
26
27
  body: {
27
- params: JSON.stringify(body),
28
+ params: JSON.stringify(body.params ?? body),
28
29
  },
29
- json: true, // importante: dejar que n8n maneje el parseo
30
+ json: true,
30
31
  };
31
32
 
32
33
  try {
@@ -38,13 +39,13 @@ export async function civicrmApiRequest(
38
39
  }
39
40
 
40
41
  /**
41
- * Devuelve el formato esperado por Civi-Go
42
- * Solo los parámetros planos: where, limit, select...
42
+ * Devuelve el cuerpo estándar para las llamadas API4 (plano)
43
43
  */
44
44
  export function api4(
45
45
  entity: string,
46
46
  action: string,
47
47
  params: Record<string, unknown> = {},
48
48
  ) {
49
+ // devolvemos los parámetros planos, no anidados
49
50
  return params;
50
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ixiam/n8n-nodes-civicrm",
3
- "version": "0.3.4",
3
+ "version": "0.3.8",
4
4
  "description": "CiviCRM API v4",
5
5
  "license": "MIT",
6
6
  "keywords": [