n8n-nodes-confirm8 0.21.0 → 0.23.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.
@@ -1,9 +1,17 @@
1
- import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
2
2
  export declare class Confirm8AgentTool implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  /**
5
5
  * Parse natural date queries into filter JSON
6
6
  */
7
7
  private parseDateQuery;
8
+ /**
9
+ * Validate ISO date string yyyy-mm-dd
10
+ */
11
+ private isIsoDate;
12
+ /**
13
+ * Enforce date filters format when present
14
+ */
15
+ private validateDateFilters;
8
16
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
9
17
  }
@@ -5,107 +5,180 @@ const n8n_workflow_1 = require("n8n-workflow");
5
5
  class Confirm8AgentTool {
6
6
  constructor() {
7
7
  this.description = {
8
- displayName: 'Confirm8 AI Tool',
9
- name: 'confirm8AgentTool',
10
- icon: 'file:tool.svg',
11
- group: ['transform'],
8
+ displayName: "Confirm8 AI Tool",
9
+ name: "confirm8AgentTool",
10
+ icon: "file:tool.svg",
11
+ group: ["transform"],
12
12
  version: 1,
13
- description: 'AI tool for Confirm8. IMPORTANT: For "OS/ordem de serviço" use resource="order". For dates use filters with start_date/end_date. Example: {"start_date":{"gte":"2025-11-23"},"end_date":{"lte":"2025-11-25"}}',
13
+ description: `AI tool for Confirm8 (STRICT).
14
+
15
+ CRITICAL FOR AGENTS:
16
+ - "resource" MUST be exactly one of: user, client, item, itemType, task, service, product, order, modality, ticket, property
17
+ - "operation" MUST be exactly one of: getAll, get, create, update, activate, deactivate
18
+ - Use ONLY these tokens. Do NOT use labels like "OS", "Ordem de Serviço", "buscar".
19
+ - For OS/ordem de serviço => resource="order"
20
+ - To list/search => operation="getAll"
21
+ - Date filters MUST be ISO yyyy-mm-dd.`,
14
22
  defaults: {
15
- name: 'Confirm8 AI Tool',
23
+ name: "Confirm8 AI Tool",
16
24
  },
17
25
  usableAsTool: true,
18
- inputs: ['main'],
19
- outputs: ['main'],
26
+ inputs: ["main"],
27
+ outputs: ["main"],
20
28
  properties: [
21
29
  // API Configuration
22
30
  {
23
- displayName: 'Base URL',
24
- name: 'baseUrl',
25
- type: 'string',
26
- default: '',
27
- placeholder: 'https://api.confirm8.com',
31
+ displayName: "Base URL",
32
+ name: "baseUrl",
33
+ type: "string",
34
+ default: "",
35
+ placeholder: "https://api.confirm8.com",
28
36
  required: true,
29
37
  },
30
38
  {
31
- displayName: 'Bearer Token',
32
- name: 'bearerToken',
33
- type: 'string',
39
+ displayName: "Bearer Token",
40
+ name: "bearerToken",
41
+ type: "string",
34
42
  typeOptions: { password: true },
35
- default: '',
43
+ default: "",
36
44
  required: true,
37
45
  },
38
46
  {
39
- displayName: 'X-API-DOMAIN',
40
- name: 'apiDomain',
41
- type: 'string',
42
- default: '',
47
+ displayName: "X-API-DOMAIN",
48
+ name: "apiDomain",
49
+ type: "string",
50
+ default: "",
43
51
  required: true,
44
52
  },
45
53
  {
46
- displayName: 'X-APIKEY-TOKEN',
47
- name: 'apiKeyToken',
48
- type: 'string',
54
+ displayName: "X-APIKEY-TOKEN",
55
+ name: "apiKeyToken",
56
+ type: "string",
49
57
  typeOptions: { password: true },
50
- default: '',
58
+ default: "",
51
59
  required: true,
52
60
  },
53
61
  // Tool parameters
54
62
  {
55
- displayName: 'Resource',
56
- name: 'resource',
57
- type: 'options',
63
+ displayName: "Resource",
64
+ name: "resource",
65
+ type: "options",
58
66
  options: [
59
- { name: 'User', value: 'user', description: 'Users/Employees/Funcionários' },
60
- { name: 'Client', value: 'client', description: 'Clients/Customers/Clientes' },
61
- { name: 'Item', value: 'item', description: 'Items/Itens' },
62
- { name: 'Item Type', value: 'itemType', description: 'Item Types/Tipos' },
63
- { name: 'Task', value: 'task', description: 'Tasks/Tarefas/Checklists' },
64
- { name: 'Service', value: 'service', description: 'Services/Serviços' },
65
- { name: 'Product', value: 'product', description: 'Products/Produtos' },
66
- { name: 'Order (OS)', value: 'order', description: 'Work Orders/OS/Ordens de Serviço/WOS' },
67
- { name: 'Modality', value: 'modality', description: 'Modalities/Modalidades' },
68
- { name: 'Ticket', value: 'ticket', description: 'Tickets/Chamados' },
69
- { name: 'Property', value: 'property', description: 'Properties/Propriedades' },
67
+ {
68
+ name: "user",
69
+ value: "user",
70
+ description: "Users/Employees/Funcionários",
71
+ },
72
+ {
73
+ name: "client",
74
+ value: "client",
75
+ description: "Clients/Customers/Clientes",
76
+ },
77
+ { name: "item", value: "item", description: "Items/Itens" },
78
+ {
79
+ name: "itemType",
80
+ value: "itemType",
81
+ description: "Item Types/Tipos",
82
+ },
83
+ {
84
+ name: "task",
85
+ value: "task",
86
+ description: "Tasks/Tarefas/Checklists",
87
+ },
88
+ {
89
+ name: "service",
90
+ value: "service",
91
+ description: "Services/Serviços",
92
+ },
93
+ {
94
+ name: "product",
95
+ value: "product",
96
+ description: "Products/Produtos",
97
+ },
98
+ {
99
+ name: "order",
100
+ value: "order",
101
+ description: "Work Orders/OS/Ordens de Serviço/WOS",
102
+ },
103
+ {
104
+ name: "modality",
105
+ value: "modality",
106
+ description: "Modalities/Modalidades",
107
+ },
108
+ { name: "ticket", value: "ticket", description: "Tickets/Chamados" },
109
+ {
110
+ name: "property",
111
+ value: "property",
112
+ description: "Properties/Propriedades",
113
+ },
70
114
  ],
71
- default: 'user',
72
- description: 'CRITICAL: For "OS" or "ordem de serviço" ALWAYS use "order"',
115
+ default: "user",
116
+ description: `CRITICAL (Agents): Resource MUST be EXACTLY one of: user, client, item, itemType, task, service, product, order, modality, ticket, property.
117
+ Use ONLY the value token (example: order), never labels like OS.`,
73
118
  },
74
119
  {
75
- displayName: 'Operation',
76
- name: 'operation',
77
- type: 'options',
120
+ displayName: "Operation",
121
+ name: "operation",
122
+ type: "options",
78
123
  options: [
79
- { name: 'Get All (List)', value: 'getAll', description: 'List/Listar/Buscar todos/Mostrar todos' },
80
- { name: 'Get (Single)', value: 'get', description: 'Get one/Buscar um/Obter' },
81
- { name: 'Create', value: 'create', description: 'Create/Criar/Adicionar' },
82
- { name: 'Update', value: 'update', description: 'Update/Atualizar/Modificar' },
83
- { name: 'Activate', value: 'activate', description: 'Activate/Ativar' },
84
- { name: 'Deactivate', value: 'deactivate', description: 'Deactivate/Desativar' },
124
+ {
125
+ name: "getAll",
126
+ value: "getAll",
127
+ description: "List/Listar/Buscar todos/Mostrar todos",
128
+ },
129
+ {
130
+ name: "get",
131
+ value: "get",
132
+ description: "Get one/Buscar um/Obter",
133
+ },
134
+ {
135
+ name: "create",
136
+ value: "create",
137
+ description: "Create/Criar/Adicionar",
138
+ },
139
+ {
140
+ name: "update",
141
+ value: "update",
142
+ description: "Update/Atualizar/Modificar",
143
+ },
144
+ {
145
+ name: "activate",
146
+ value: "activate",
147
+ description: "Activate/Ativar",
148
+ },
149
+ {
150
+ name: "deactivate",
151
+ value: "deactivate",
152
+ description: "Deactivate/Desativar",
153
+ },
85
154
  ],
86
- default: 'getAll',
87
- description: 'CRITICAL: To list/search use "getAll"',
155
+ default: "getAll",
156
+ description: `CRITICAL (Agents): Operation MUST be EXACTLY one of: getAll, get, create, update, activate, deactivate.
157
+ Use ONLY the value token (example: getAll), never labels like buscar.`,
88
158
  },
89
159
  {
90
- displayName: 'Record ID',
91
- name: 'recordId',
92
- type: 'string',
93
- default: '',
94
- description: 'Record ID (only for get/update/activate/deactivate)',
160
+ displayName: "Record ID",
161
+ name: "recordId",
162
+ type: "string",
163
+ default: "",
164
+ description: "Record ID (only for get/update/activate/deactivate)",
95
165
  },
96
166
  {
97
- displayName: 'Data',
98
- name: 'data',
99
- type: 'string',
100
- default: '',
101
- description: 'JSON data (only for create/update)',
167
+ displayName: "Data",
168
+ name: "data",
169
+ type: "string",
170
+ default: "",
171
+ description: "JSON data (only for create/update)",
102
172
  },
103
173
  {
104
- displayName: 'Filters',
105
- name: 'filters',
106
- type: 'string',
107
- default: '',
108
- description: 'Filters as JSON. For dates: {"start_date":{"gte":"YYYY-MM-DD"},"end_date":{"lte":"YYYY-MM-DD"}}. For status: {"status":{"eq":"complete"}}',
174
+ displayName: "Filters",
175
+ name: "filters",
176
+ type: "string",
177
+ default: "",
178
+ description: `Filters as JSON ONLY.
179
+ Dates MUST be ISO yyyy-mm-dd.
180
+ Example (this week): {"start_date":{"gte":"2025-06-16"},"end_date":{"lte":"2025-06-22"}}
181
+ Do NOT send expressions like "data >= start_of_week".`,
109
182
  },
110
183
  ],
111
184
  };
@@ -119,19 +192,19 @@ class Confirm8AgentTool {
119
192
  query = query.toLowerCase().trim();
120
193
  // Helper to format date as YYYY-MM-DD
121
194
  const formatDate = (date) => {
122
- return date.toISOString().split('T')[0];
195
+ return date.toISOString().split("T")[0];
123
196
  };
124
197
  // Today
125
- if (query.includes('hoje') || query.includes('today')) {
198
+ if (query.includes("hoje") || query.includes("today")) {
126
199
  const endOfDay = new Date(today);
127
200
  endOfDay.setHours(23, 59, 59, 999);
128
201
  return {
129
202
  start_date: { gte: formatDate(today) },
130
- end_date: { lte: formatDate(endOfDay) }
203
+ end_date: { lte: formatDate(endOfDay) },
131
204
  };
132
205
  }
133
206
  // This week / semana atual
134
- if (query.includes('semana') || query.includes('week')) {
207
+ if (query.includes("semana") || query.includes("week")) {
135
208
  const dayOfWeek = today.getDay();
136
209
  const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Monday is start
137
210
  const weekStart = new Date(today);
@@ -140,16 +213,18 @@ class Confirm8AgentTool {
140
213
  weekEnd.setDate(weekStart.getDate() + 6);
141
214
  return {
142
215
  start_date: { gte: formatDate(weekStart) },
143
- end_date: { lte: formatDate(weekEnd) }
216
+ end_date: { lte: formatDate(weekEnd) },
144
217
  };
145
218
  }
146
219
  // This month / mês atual
147
- if (query.includes('mês') || query.includes('mes') || query.includes('month')) {
220
+ if (query.includes("mês") ||
221
+ query.includes("mes") ||
222
+ query.includes("month")) {
148
223
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
149
224
  const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
150
225
  return {
151
226
  start_date: { gte: formatDate(monthStart) },
152
- end_date: { lte: formatDate(monthEnd) }
227
+ end_date: { lte: formatDate(monthEnd) },
153
228
  };
154
229
  }
155
230
  // Last N days
@@ -160,7 +235,7 @@ class Confirm8AgentTool {
160
235
  startDate.setDate(today.getDate() - days);
161
236
  return {
162
237
  start_date: { gte: formatDate(startDate) },
163
- end_date: { lte: formatDate(today) }
238
+ end_date: { lte: formatDate(today) },
164
239
  };
165
240
  }
166
241
  // Next N days
@@ -171,26 +246,56 @@ class Confirm8AgentTool {
171
246
  endDate.setDate(today.getDate() + days);
172
247
  return {
173
248
  start_date: { gte: formatDate(today) },
174
- end_date: { lte: formatDate(endDate) }
249
+ end_date: { lte: formatDate(endDate) },
175
250
  };
176
251
  }
177
252
  return null;
178
253
  }
254
+ /**
255
+ * Validate ISO date string yyyy-mm-dd
256
+ */
257
+ isIsoDate(value) {
258
+ if (typeof value !== "string")
259
+ return false;
260
+ return /^\d{4}-\d{2}-\d{2}$/.test(value);
261
+ }
262
+ /**
263
+ * Enforce date filters format when present
264
+ */
265
+ validateDateFilters(filters) {
266
+ const dateFields = ["start_date", "end_date", "date", "data"];
267
+ for (const field of dateFields) {
268
+ if (!filters[field])
269
+ continue;
270
+ const ops = filters[field];
271
+ if (!ops || typeof ops !== "object")
272
+ continue;
273
+ for (const op of Object.keys(ops)) {
274
+ const v = ops[op];
275
+ if (v == null)
276
+ continue;
277
+ if (!this.isIsoDate(v)) {
278
+ throw new Error(`Invalid date format in filters["${field}"]["${op}"]="${String(v)}". ` +
279
+ `Dates MUST be yyyy-mm-dd (ISO), e.g. "2025-06-16".`);
280
+ }
281
+ }
282
+ }
283
+ }
179
284
  async execute() {
180
285
  const items = this.getInputData();
181
286
  const returnData = [];
182
287
  for (let i = 0; i < items.length; i++) {
183
288
  try {
184
- const baseUrl = this.getNodeParameter('baseUrl', i);
185
- const bearerToken = this.getNodeParameter('bearerToken', i);
186
- const apiDomain = this.getNodeParameter('apiDomain', i);
187
- const apiKeyToken = this.getNodeParameter('apiKeyToken', i);
188
- let resource = this.getNodeParameter('resource', i, '');
189
- let operation = this.getNodeParameter('operation', i, '');
190
- const recordId = this.getNodeParameter('recordId', i, '');
289
+ const baseUrl = this.getNodeParameter("baseUrl", i);
290
+ const bearerToken = this.getNodeParameter("bearerToken", i);
291
+ const apiDomain = this.getNodeParameter("apiDomain", i);
292
+ const apiKeyToken = this.getNodeParameter("apiKeyToken", i);
293
+ let resource = this.getNodeParameter("resource", i, "");
294
+ let operation = this.getNodeParameter("operation", i, "");
295
+ const recordId = this.getNodeParameter("recordId", i, "");
191
296
  // Parse data
192
297
  let data = {};
193
- const dataParam = this.getNodeParameter('data', i, '');
298
+ const dataParam = this.getNodeParameter("data", i, "");
194
299
  if (dataParam) {
195
300
  try {
196
301
  data = JSON.parse(dataParam);
@@ -201,7 +306,7 @@ class Confirm8AgentTool {
201
306
  }
202
307
  // Parse filters
203
308
  let filters = {};
204
- const filtersParam = this.getNodeParameter('filters', i, '');
309
+ const filtersParam = this.getNodeParameter("filters", i, "");
205
310
  if (filtersParam) {
206
311
  try {
207
312
  // Try to parse as JSON first
@@ -214,201 +319,309 @@ class Confirm8AgentTool {
214
319
  filters = parsedDate;
215
320
  }
216
321
  }
322
+ // Enforce ISO date filters when present
323
+ this.validateDateFilters(filters);
217
324
  }
218
325
  // Aggressive normalization - EXPAND THIS
219
326
  const resourceMap = {
220
327
  // Standard
221
- 'users': 'user', 'usuarios': 'user', 'usuários': 'user', 'employees': 'user', 'funcionários': 'user', 'funcionarios': 'user',
222
- 'clients': 'client', 'clientes': 'client', 'customers': 'client',
223
- 'items': 'item', 'itens': 'item',
224
- 'itemtypes': 'itemType', 'tipos': 'itemType',
225
- 'tasks': 'task', 'tarefas': 'task',
226
- 'services': 'service', 'serviços': 'service', 'servicos': 'service',
227
- 'products': 'product', 'produtos': 'product',
228
- 'modalities': 'modality', 'modalidades': 'modality',
229
- 'tickets': 'ticket', 'chamados': 'ticket',
230
- 'properties': 'property', 'propriedades': 'property',
328
+ users: "user",
329
+ usuarios: "user",
330
+ usuários: "user",
331
+ employees: "user",
332
+ funcionários: "user",
333
+ funcionarios: "user",
334
+ clients: "client",
335
+ clientes: "client",
336
+ customers: "client",
337
+ items: "item",
338
+ itens: "item",
339
+ itemtypes: "itemType",
340
+ tipos: "itemType",
341
+ tasks: "task",
342
+ tarefas: "task",
343
+ services: "service",
344
+ serviços: "service",
345
+ servicos: "service",
346
+ products: "product",
347
+ produtos: "product",
348
+ modalities: "modality",
349
+ modalidades: "modality",
350
+ tickets: "ticket",
351
+ chamados: "ticket",
352
+ properties: "property",
353
+ propriedades: "property",
231
354
  // CRITICAL: OS mappings
232
- 'os': 'order',
233
- 'o.s': 'order',
234
- 'o.s.': 'order',
235
- 'orders': 'order',
236
- 'ordens': 'order',
237
- 'ordem': 'order',
238
- 'ordem de serviço': 'order',
239
- 'ordem de servico': 'order',
240
- 'ordens de serviço': 'order',
241
- 'ordens de servico': 'order',
242
- 'wos': 'order',
243
- 'work order': 'order',
244
- 'work orders': 'order',
245
- 'pedidos': 'order',
246
- 'pedido': 'order',
355
+ os: "order",
356
+ "o.s": "order",
357
+ "o.s.": "order",
358
+ orders: "order",
359
+ ordens: "order",
360
+ ordem: "order",
361
+ "ordem de serviço": "order",
362
+ "ordem de servico": "order",
363
+ "ordens de serviço": "order",
364
+ "ordens de servico": "order",
365
+ wos: "order",
366
+ "work order": "order",
367
+ "work orders": "order",
368
+ pedidos: "order",
369
+ pedido: "order",
247
370
  };
248
371
  const operationMap = {
249
372
  // List/Get All
250
- 'list': 'getAll',
251
- 'listar': 'getAll',
252
- 'mostrar': 'getAll',
253
- 'buscar': 'getAll',
254
- 'busque': 'getAll',
255
- 'buscar todos': 'getAll',
256
- 'buscar todas': 'getAll',
257
- 'buscar todo': 'getAll',
258
- 'buscar toda': 'getAll',
259
- 'get all': 'getAll',
260
- 'show all': 'getAll',
261
- 'list all': 'getAll',
262
- 'todos': 'getAll',
263
- 'todas': 'getAll',
264
- 'obter todos': 'getAll',
265
- 'obter todas': 'getAll',
266
- 'pegar todos': 'getAll',
267
- 'pegar todas': 'getAll',
373
+ list: "getAll",
374
+ listar: "getAll",
375
+ mostrar: "getAll",
376
+ buscar: "getAll",
377
+ busque: "getAll",
378
+ "buscar todos": "getAll",
379
+ "buscar todas": "getAll",
380
+ "buscar todo": "getAll",
381
+ "buscar toda": "getAll",
382
+ "get all": "getAll",
383
+ "show all": "getAll",
384
+ "list all": "getAll",
385
+ todos: "getAll",
386
+ todas: "getAll",
387
+ "obter todos": "getAll",
388
+ "obter todas": "getAll",
389
+ "pegar todos": "getAll",
390
+ "pegar todas": "getAll",
268
391
  // Get Single
269
- 'obter': 'get',
270
- 'pegar': 'get',
271
- 'ver': 'get',
272
- 'visualizar': 'get',
392
+ obter: "get",
393
+ pegar: "get",
394
+ ver: "get",
395
+ visualizar: "get",
273
396
  // Create
274
- 'criar': 'create',
275
- 'adicionar': 'create',
276
- 'add': 'create',
277
- 'novo': 'create',
278
- 'nova': 'create',
279
- 'new': 'create',
397
+ criar: "create",
398
+ adicionar: "create",
399
+ add: "create",
400
+ novo: "create",
401
+ nova: "create",
402
+ new: "create",
280
403
  // Update
281
- 'atualizar': 'update',
282
- 'modificar': 'update',
283
- 'modify': 'update',
284
- 'editar': 'update',
285
- 'edit': 'update',
404
+ atualizar: "update",
405
+ modificar: "update",
406
+ modify: "update",
407
+ editar: "update",
408
+ edit: "update",
286
409
  // Activate/Deactivate
287
- 'ativar': 'activate',
288
- 'enable': 'activate',
289
- 'habilitar': 'activate',
290
- 'desativar': 'deactivate',
291
- 'disable': 'deactivate',
292
- 'desabilitar': 'deactivate',
410
+ ativar: "activate",
411
+ enable: "activate",
412
+ habilitar: "activate",
413
+ desativar: "deactivate",
414
+ disable: "deactivate",
415
+ desabilitar: "deactivate",
293
416
  };
294
- // Normalize - clean and map
295
- const cleanResource = resource.toLowerCase().trim().replace(/\s+/g, ' ');
296
- const cleanOperation = operation.toLowerCase().trim().replace(/\s+/g, ' ');
417
+ // Normalize - clean and map (remove accents/punctuation)
418
+ const normalizeKey = (value) => value
419
+ .toLowerCase()
420
+ .normalize("NFD")
421
+ .replace(/\p{Diacritic}/gu, "")
422
+ .replace(/[^a-z0-9\s.\-_/]/g, " ")
423
+ .trim()
424
+ .replace(/\s+/g, " ");
425
+ const cleanResource = normalizeKey(resource);
426
+ const cleanOperation = normalizeKey(operation);
297
427
  resource = resourceMap[cleanResource] || resource;
298
428
  operation = operationMap[cleanOperation] || operation;
299
429
  // If still not valid, throw clear error
300
- const validResources = ['user', 'client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality', 'ticket', 'property'];
301
- const validOperations = ['getAll', 'get', 'create', 'update', 'activate', 'deactivate'];
430
+ const validResources = [
431
+ "user",
432
+ "client",
433
+ "item",
434
+ "itemType",
435
+ "task",
436
+ "service",
437
+ "product",
438
+ "order",
439
+ "modality",
440
+ "ticket",
441
+ "property",
442
+ ];
443
+ const validOperations = [
444
+ "getAll",
445
+ "get",
446
+ "create",
447
+ "update",
448
+ "activate",
449
+ "deactivate",
450
+ ];
302
451
  if (!validResources.includes(resource)) {
303
- throw new Error(`Invalid resource: "${resource}". Must be one of: ${validResources.join(', ')}. ` +
304
- `For OS/Ordem de Serviço use "order".`);
452
+ throw new Error(`Invalid resource: "${resource}". Must be one of: ${validResources.join(", ")}. ` + `For OS/Ordem de Serviço use "order".`);
305
453
  }
306
454
  if (!validOperations.includes(operation)) {
307
- throw new Error(`Invalid operation: "${operation}". Must be one of: ${validOperations.join(', ')}. ` +
308
- `To list/search use "getAll".`);
455
+ throw new Error(`Invalid operation: "${operation}". Must be one of: ${validOperations.join(", ")}. ` + `To list/search use "getAll".`);
309
456
  }
310
457
  // Resource config with relations
311
458
  const resourceConfig = {
312
459
  user: {
313
- endpoint: 'users',
314
- relations: ['clients', 'attachments', 'permissions', 'device', 'employee']
460
+ endpoint: "users",
461
+ relations: [
462
+ "clients",
463
+ "attachments",
464
+ "permissions",
465
+ "device",
466
+ "employee",
467
+ ],
315
468
  },
316
469
  client: {
317
- endpoint: 'clients',
318
- relations: ['wos', 'items', 'employees', 'headquarter', 'files', 'userGroup', 'properties']
470
+ endpoint: "clients",
471
+ relations: [
472
+ "wos",
473
+ "items",
474
+ "employees",
475
+ "headquarter",
476
+ "files",
477
+ "userGroup",
478
+ "properties",
479
+ ],
319
480
  },
320
481
  item: {
321
- endpoint: 'items',
322
- relations: ['client', 'item_type', 'properties', 'parent', 'children', 'collects', 'wos']
482
+ endpoint: "items",
483
+ relations: [
484
+ "client",
485
+ "item_type",
486
+ "properties",
487
+ "parent",
488
+ "children",
489
+ "collects",
490
+ "wos",
491
+ ],
323
492
  },
324
493
  itemType: {
325
- endpoint: 'itemTypes',
326
- relations: ['properties']
494
+ endpoint: "itemTypes",
495
+ relations: ["properties"],
327
496
  },
328
497
  task: {
329
- endpoint: 'tasks',
330
- relations: ['itemType', 'activities', 'wos', 'modalities']
498
+ endpoint: "tasks",
499
+ relations: ["itemType", "activities", "wos", "modalities"],
331
500
  },
332
501
  service: {
333
- endpoint: 'services',
334
- relations: ['task']
502
+ endpoint: "services",
503
+ relations: ["task"],
335
504
  },
336
505
  product: {
337
- endpoint: 'products',
338
- relations: []
506
+ endpoint: "products",
507
+ relations: [],
339
508
  },
340
509
  order: {
341
- endpoint: 'wos',
342
- relations: ['client', 'modalities', 'tasks', 'pivot_tasks', 'products', 'users', 'items', 'tickets', 'services', 'collects', 'attachments']
510
+ endpoint: "wos",
511
+ relations: [
512
+ "client",
513
+ "modalities",
514
+ "tasks",
515
+ "pivot_tasks",
516
+ "products",
517
+ "users",
518
+ "items",
519
+ "tickets",
520
+ "services",
521
+ "collects",
522
+ "attachments",
523
+ ],
343
524
  },
344
525
  modality: {
345
- endpoint: 'modalities',
346
- relations: ['tasks']
526
+ endpoint: "modalities",
527
+ relations: ["tasks"],
347
528
  },
348
529
  ticket: {
349
- endpoint: 'tickets',
350
- relations: ['client', 'subject_category', 'category', 'status', 'attachments', 'item', 'owner', 'priority', 'users', 'orders', 'properties']
530
+ endpoint: "tickets",
531
+ relations: [
532
+ "client",
533
+ "subject_category",
534
+ "category",
535
+ "status",
536
+ "attachments",
537
+ "item",
538
+ "owner",
539
+ "priority",
540
+ "users",
541
+ "orders",
542
+ "properties",
543
+ ],
351
544
  },
352
545
  property: {
353
- endpoint: 'properties',
354
- relations: []
546
+ endpoint: "properties",
547
+ relations: [],
355
548
  },
356
549
  };
357
550
  const config = resourceConfig[resource];
358
551
  const baseEndpoint = config.endpoint;
359
- let endpoint = '';
360
- let method = 'GET';
552
+ let endpoint = "";
553
+ let method = "GET";
361
554
  let body = {};
362
555
  // Build endpoint
363
556
  switch (operation) {
364
- case 'getAll':
557
+ case "getAll":
365
558
  endpoint = `/v3/${baseEndpoint}`;
366
559
  break;
367
- case 'get':
560
+ case "get":
368
561
  if (!recordId)
369
- throw new Error('recordId required');
562
+ throw new Error("recordId required");
370
563
  endpoint = `/v3/${baseEndpoint}/${recordId}`;
371
564
  break;
372
- case 'create':
565
+ case "create":
373
566
  endpoint = `/v3/${baseEndpoint}`;
374
- method = 'POST';
567
+ method = "POST";
375
568
  body = data;
376
569
  break;
377
- case 'update':
570
+ case "update":
378
571
  if (!recordId)
379
- throw new Error('recordId required');
572
+ throw new Error("recordId required");
380
573
  endpoint = `/v3/${baseEndpoint}/${recordId}`;
381
- method = 'PUT';
574
+ method = "PUT";
382
575
  body = data;
383
576
  break;
384
- case 'activate':
577
+ case "activate":
385
578
  if (!recordId)
386
- throw new Error('recordId required');
579
+ throw new Error("recordId required");
387
580
  endpoint = `/v3/${baseEndpoint}/${recordId}/active`;
388
- method = ['client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality'].includes(resource)
389
- ? 'PUT' : 'PATCH';
581
+ method = [
582
+ "client",
583
+ "item",
584
+ "itemType",
585
+ "task",
586
+ "service",
587
+ "product",
588
+ "order",
589
+ "modality",
590
+ ].includes(resource)
591
+ ? "PUT"
592
+ : "PATCH";
390
593
  break;
391
- case 'deactivate':
594
+ case "deactivate":
392
595
  if (!recordId)
393
- throw new Error('recordId required');
596
+ throw new Error("recordId required");
394
597
  endpoint = `/v3/${baseEndpoint}/${recordId}/inactive`;
395
- method = ['client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality'].includes(resource)
396
- ? 'PUT' : 'PATCH';
598
+ method = [
599
+ "client",
600
+ "item",
601
+ "itemType",
602
+ "task",
603
+ "service",
604
+ "product",
605
+ "order",
606
+ "modality",
607
+ ].includes(resource)
608
+ ? "PUT"
609
+ : "PATCH";
397
610
  break;
398
611
  }
399
612
  // Build query string
400
613
  const queryParams = [];
401
614
  // Add relations (only GET)
402
- if (method === 'GET' && config.relations.length > 0) {
403
- config.relations.forEach(relation => {
615
+ if (method === "GET" && config.relations.length > 0) {
616
+ config.relations.forEach((relation) => {
404
617
  queryParams.push(`relations=${relation}`);
405
618
  });
406
619
  }
407
620
  // Add filters
408
621
  if (Object.keys(filters).length > 0) {
409
- Object.keys(filters).forEach(field => {
622
+ Object.keys(filters).forEach((field) => {
410
623
  const operators = filters[field];
411
- Object.keys(operators).forEach(operator => {
624
+ Object.keys(operators).forEach((operator) => {
412
625
  const value = operators[operator];
413
626
  queryParams.push(`filters[${field}][${operator}]=${encodeURIComponent(String(value))}`);
414
627
  });
@@ -417,21 +630,23 @@ class Confirm8AgentTool {
417
630
  // Full URL
418
631
  let fullUrl = `${baseUrl}${endpoint}`;
419
632
  if (queryParams.length > 0) {
420
- fullUrl += '?' + queryParams.join('&');
633
+ fullUrl += "?" + queryParams.join("&");
421
634
  }
422
635
  // Make request
423
636
  const options = {
424
637
  method,
425
638
  uri: fullUrl,
426
639
  headers: {
427
- 'Authorization': `Bearer ${bearerToken}`,
428
- 'X-API-DOMAIN': apiDomain,
429
- 'X-APIKEY-TOKEN': apiKeyToken,
430
- 'Content-Type': 'application/json',
640
+ Authorization: `Bearer ${bearerToken}`,
641
+ "X-API-DOMAIN": apiDomain,
642
+ "X-APIKEY-TOKEN": apiKeyToken,
643
+ "Content-Type": "application/json",
431
644
  },
432
645
  json: true,
433
646
  };
434
- if (method !== 'GET' && method !== 'DELETE' && Object.keys(body).length > 0) {
647
+ if (method !== "GET" &&
648
+ method !== "DELETE" &&
649
+ Object.keys(body).length > 0) {
435
650
  options.body = body;
436
651
  }
437
652
  const responseData = await this.helpers.request(options);
@@ -439,8 +654,8 @@ class Confirm8AgentTool {
439
654
  json: {
440
655
  success: true,
441
656
  normalized: {
442
- originalResource: this.getNodeParameter('resource', i, ''),
443
- originalOperation: this.getNodeParameter('operation', i, ''),
657
+ originalResource: this.getNodeParameter("resource", i, ""),
658
+ originalOperation: this.getNodeParameter("operation", i, ""),
444
659
  normalizedResource: resource,
445
660
  normalizedOperation: operation,
446
661
  },
@@ -461,7 +676,7 @@ class Confirm8AgentTool {
461
676
  returnData.push({
462
677
  json: {
463
678
  success: false,
464
- error: error.message || 'Unknown error',
679
+ error: error.message || "Unknown error",
465
680
  },
466
681
  pairedItem: { item: i },
467
682
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-confirm8",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Simple n8n node for Confirm8 API - no credentials needed",
5
5
  "license": "MIT",
6
6
  "author": "Bill Hebert",