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