n8n-nodes-datatable-manager-select 1.0.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.
@@ -0,0 +1,937 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataTableManager = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ /**
6
+ * Data Table Manager Node with Dropdown Selection
7
+ *
8
+ * This node uses a CREDENTIALS-based approach for authentication.
9
+ * This is the KEY FIX for the loadOptions problem:
10
+ *
11
+ * PROBLEM: getNodeParameter() and getCurrentNodeParameter() in loadOptions
12
+ * only work for SAVED parameter values. When a user enters credentials
13
+ * but hasn't executed the node yet, these methods return empty strings.
14
+ *
15
+ * SOLUTION: Use this.getCredentials() instead. n8n's credential system
16
+ * properly provides credential values to loadOptions even before the
17
+ * node is executed, because credentials are saved separately and
18
+ * immediately available once the user selects/creates them.
19
+ */
20
+ class DataTableManager {
21
+ description = {
22
+ displayName: 'Data Table Manager (Select)',
23
+ name: 'dataTableManagerSelect',
24
+ icon: 'file:datatable.svg',
25
+ group: ['transform'],
26
+ version: 1,
27
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
28
+ description: 'Manage n8n internal Data Tables with name-based selection (Create, List, Update, Delete)',
29
+ defaults: {
30
+ name: 'Data Table Manager',
31
+ },
32
+ inputs: ['main'],
33
+ outputs: ['main'],
34
+ // IMPORTANT: Link to credentials type
35
+ credentials: [
36
+ {
37
+ name: 'dataTableManagerApi',
38
+ required: true,
39
+ },
40
+ ],
41
+ properties: [
42
+ // ========== NOTICE ABOUT CREDENTIALS ==========
43
+ {
44
+ displayName: 'Connection settings are configured in the Credentials. Click the "Credential to connect with" dropdown above to create or select your n8n connection.',
45
+ name: 'credentialsNotice',
46
+ type: 'notice',
47
+ default: '',
48
+ },
49
+ {
50
+ displayName: 'Tipp: Nach Aenderungen (Create/Update/Delete) klicken Sie auf das ↻ Symbol neben dem Dropdown um die Liste neu zu laden.',
51
+ name: 'refreshNotice',
52
+ type: 'notice',
53
+ default: '',
54
+ displayOptions: {
55
+ show: {
56
+ operation: ['update', 'delete', 'add', 'rename'],
57
+ },
58
+ },
59
+ },
60
+ // Resource
61
+ {
62
+ displayName: 'Resource',
63
+ name: 'resource',
64
+ type: 'options',
65
+ noDataExpression: true,
66
+ options: [
67
+ {
68
+ name: 'Table',
69
+ value: 'table',
70
+ },
71
+ {
72
+ name: 'Column',
73
+ value: 'column',
74
+ },
75
+ ],
76
+ default: 'table',
77
+ },
78
+ // ========== TABLE OPERATIONS ==========
79
+ {
80
+ displayName: 'Operation',
81
+ name: 'operation',
82
+ type: 'options',
83
+ noDataExpression: true,
84
+ displayOptions: {
85
+ show: {
86
+ resource: ['table'],
87
+ },
88
+ },
89
+ options: [
90
+ {
91
+ name: 'Create',
92
+ value: 'create',
93
+ description: 'Create a new data table with columns',
94
+ action: 'Create a table',
95
+ },
96
+ {
97
+ name: 'Delete',
98
+ value: 'delete',
99
+ description: 'Delete a data table',
100
+ action: 'Delete a table',
101
+ },
102
+ {
103
+ name: 'Get All',
104
+ value: 'getAll',
105
+ description: 'Get all data tables',
106
+ action: 'Get all tables',
107
+ },
108
+ {
109
+ name: 'Update',
110
+ value: 'update',
111
+ description: 'Update/rename a data table',
112
+ action: 'Update a table',
113
+ },
114
+ ],
115
+ default: 'create',
116
+ },
117
+ // Table Create - Name
118
+ {
119
+ displayName: 'Table Name',
120
+ name: 'tableName',
121
+ type: 'string',
122
+ required: true,
123
+ default: '',
124
+ displayOptions: {
125
+ show: {
126
+ resource: ['table'],
127
+ operation: ['create'],
128
+ },
129
+ },
130
+ description: 'The name of the new data table',
131
+ },
132
+ // Table Create - Columns
133
+ {
134
+ displayName: 'Columns',
135
+ name: 'columns',
136
+ type: 'fixedCollection',
137
+ typeOptions: {
138
+ multipleValues: true,
139
+ },
140
+ default: {},
141
+ displayOptions: {
142
+ show: {
143
+ resource: ['table'],
144
+ operation: ['create'],
145
+ },
146
+ },
147
+ options: [
148
+ {
149
+ name: 'columnValues',
150
+ displayName: 'Column',
151
+ values: [
152
+ {
153
+ displayName: 'Column Name',
154
+ name: 'name',
155
+ type: 'string',
156
+ default: '',
157
+ description: 'Name of the column',
158
+ },
159
+ {
160
+ displayName: 'Column Type',
161
+ name: 'type',
162
+ type: 'options',
163
+ options: [
164
+ {
165
+ name: 'String',
166
+ value: 'string',
167
+ },
168
+ {
169
+ name: 'Number',
170
+ value: 'number',
171
+ },
172
+ {
173
+ name: 'Boolean',
174
+ value: 'boolean',
175
+ },
176
+ {
177
+ name: 'Date',
178
+ value: 'date',
179
+ },
180
+ ],
181
+ default: 'string',
182
+ description: 'Data type of the column',
183
+ },
184
+ ],
185
+ },
186
+ ],
187
+ description: 'Columns to create in the table',
188
+ },
189
+ // Table Selection (for update/delete) - DROPDOWN WITH NAMES
190
+ {
191
+ displayName: 'Table',
192
+ name: 'tableId',
193
+ type: 'options',
194
+ required: true,
195
+ default: '',
196
+ typeOptions: {
197
+ loadOptionsMethod: 'getTables',
198
+ },
199
+ displayOptions: {
200
+ show: {
201
+ resource: ['table'],
202
+ operation: ['update', 'delete'],
203
+ },
204
+ },
205
+ description: 'Select the table to modify',
206
+ },
207
+ // Table Update - New Name
208
+ {
209
+ displayName: 'New Table Name',
210
+ name: 'newTableName',
211
+ type: 'string',
212
+ required: true,
213
+ default: '',
214
+ displayOptions: {
215
+ show: {
216
+ resource: ['table'],
217
+ operation: ['update'],
218
+ },
219
+ },
220
+ description: 'The new name for the data table',
221
+ },
222
+ // ========== COLUMN OPERATIONS ==========
223
+ {
224
+ displayName: 'Operation',
225
+ name: 'operation',
226
+ type: 'options',
227
+ noDataExpression: true,
228
+ displayOptions: {
229
+ show: {
230
+ resource: ['column'],
231
+ },
232
+ },
233
+ options: [
234
+ {
235
+ name: 'Add',
236
+ value: 'add',
237
+ description: 'Add a column to a table',
238
+ action: 'Add a column',
239
+ },
240
+ {
241
+ name: 'Delete',
242
+ value: 'delete',
243
+ description: 'Delete a column from a table',
244
+ action: 'Delete a column',
245
+ },
246
+ {
247
+ name: 'Get All',
248
+ value: 'getAll',
249
+ description: 'Get all columns of a table',
250
+ action: 'Get all columns',
251
+ },
252
+ {
253
+ name: 'Rename',
254
+ value: 'rename',
255
+ description: 'Rename a column',
256
+ action: 'Rename a column',
257
+ },
258
+ ],
259
+ default: 'add',
260
+ },
261
+ // Column - Table Selection - DROPDOWN WITH NAMES
262
+ {
263
+ displayName: 'Table',
264
+ name: 'tableId',
265
+ type: 'options',
266
+ required: true,
267
+ default: '',
268
+ typeOptions: {
269
+ loadOptionsMethod: 'getTables',
270
+ },
271
+ displayOptions: {
272
+ show: {
273
+ resource: ['column'],
274
+ },
275
+ },
276
+ description: 'Select the table',
277
+ },
278
+ // Column Add - Multiple Columns
279
+ {
280
+ displayName: 'Columns to Add',
281
+ name: 'columnsToAdd',
282
+ type: 'fixedCollection',
283
+ typeOptions: {
284
+ multipleValues: true,
285
+ },
286
+ default: {},
287
+ displayOptions: {
288
+ show: {
289
+ resource: ['column'],
290
+ operation: ['add'],
291
+ },
292
+ },
293
+ options: [
294
+ {
295
+ name: 'columnValues',
296
+ displayName: 'Column',
297
+ values: [
298
+ {
299
+ displayName: 'Column Name',
300
+ name: 'name',
301
+ type: 'string',
302
+ default: '',
303
+ description: 'Name of the column',
304
+ required: true,
305
+ },
306
+ {
307
+ displayName: 'Column Type',
308
+ name: 'type',
309
+ type: 'options',
310
+ options: [
311
+ {
312
+ name: 'String',
313
+ value: 'string',
314
+ },
315
+ {
316
+ name: 'Number',
317
+ value: 'number',
318
+ },
319
+ {
320
+ name: 'Boolean',
321
+ value: 'boolean',
322
+ },
323
+ {
324
+ name: 'Date',
325
+ value: 'date',
326
+ },
327
+ ],
328
+ default: 'string',
329
+ description: 'Data type of the column',
330
+ },
331
+ ],
332
+ },
333
+ ],
334
+ description: 'Columns to add to the table (click "Add Column" to add more)',
335
+ },
336
+ // Column Selection for DELETE - MULTIPLE SELECTION
337
+ {
338
+ displayName: 'Columns to Delete',
339
+ name: 'columnIds',
340
+ type: 'multiOptions',
341
+ required: true,
342
+ default: [],
343
+ typeOptions: {
344
+ loadOptionsMethod: 'getColumns',
345
+ loadOptionsDependsOn: ['tableId'],
346
+ },
347
+ displayOptions: {
348
+ show: {
349
+ resource: ['column'],
350
+ operation: ['delete'],
351
+ },
352
+ },
353
+ description: 'Select the columns to delete (multiple selection possible)',
354
+ },
355
+ // Column Selection for RENAME - SINGLE SELECTION
356
+ {
357
+ displayName: 'Column',
358
+ name: 'columnId',
359
+ type: 'options',
360
+ required: true,
361
+ default: '',
362
+ typeOptions: {
363
+ loadOptionsMethod: 'getColumns',
364
+ loadOptionsDependsOn: ['tableId'],
365
+ },
366
+ displayOptions: {
367
+ show: {
368
+ resource: ['column'],
369
+ operation: ['rename'],
370
+ },
371
+ },
372
+ description: 'Select the column to rename',
373
+ },
374
+ // Column Rename - New Name
375
+ {
376
+ displayName: 'New Column Name',
377
+ name: 'newColumnName',
378
+ type: 'string',
379
+ required: true,
380
+ default: '',
381
+ displayOptions: {
382
+ show: {
383
+ resource: ['column'],
384
+ operation: ['rename'],
385
+ },
386
+ },
387
+ description: 'The new name for the column',
388
+ },
389
+ ],
390
+ };
391
+ methods = {
392
+ loadOptions: {
393
+ /**
394
+ * Load available tables as dropdown options.
395
+ *
396
+ * KEY FIX: Uses this.getCredentials() instead of this.getNodeParameter()
397
+ * This allows the dropdown to work IMMEDIATELY after credentials are selected,
398
+ * without needing to execute the node first.
399
+ */
400
+ async getTables() {
401
+ const returnData = [];
402
+ // Get currently selected tableId to check if it still exists
403
+ let currentlySelectedTableId = '';
404
+ try {
405
+ currentlySelectedTableId = this.getNodeParameter('tableId') || '';
406
+ }
407
+ catch (e) {
408
+ // Parameter not available yet
409
+ }
410
+ // Get credentials - this works even before node execution!
411
+ let credentials;
412
+ try {
413
+ credentials = await this.getCredentials('dataTableManagerApi');
414
+ }
415
+ catch (error) {
416
+ // Credentials not yet configured
417
+ return [{
418
+ name: '[Bitte zuerst Credentials konfigurieren]',
419
+ value: '__placeholder_no_credentials__',
420
+ description: 'Klicken Sie oben auf "Credential to connect with" um eine Verbindung einzurichten',
421
+ }];
422
+ }
423
+ const baseUrl = (credentials.baseUrl || 'http://localhost:5678').replace(/\/$/, '');
424
+ const email = credentials.email || '';
425
+ const password = credentials.password || '';
426
+ if (!baseUrl || !email || !password) {
427
+ return [{
428
+ name: '[Credentials unvollstaendig]',
429
+ value: '__placeholder_incomplete__',
430
+ description: 'Base URL, Email und Password muessen in den Credentials ausgefuellt sein',
431
+ }];
432
+ }
433
+ try {
434
+ // Login to n8n
435
+ const loginResponse = await this.helpers.httpRequest({
436
+ method: 'POST',
437
+ url: `${baseUrl}/rest/login`,
438
+ body: { emailOrLdapLoginId: email, password },
439
+ json: true,
440
+ skipSslCertificateValidation: true,
441
+ returnFullResponse: true,
442
+ });
443
+ // Extract session cookie
444
+ let sessionCookie = '';
445
+ const setCookieHeader = loginResponse.headers?.['set-cookie'];
446
+ if (setCookieHeader) {
447
+ const cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
448
+ sessionCookie = cookies.map((c) => c.split(';')[0]).join('; ');
449
+ }
450
+ // Get projects to find project ID
451
+ const projectsResponse = await this.helpers.httpRequest({
452
+ method: 'GET',
453
+ url: `${baseUrl}/rest/projects`,
454
+ json: true,
455
+ skipSslCertificateValidation: true,
456
+ headers: { Cookie: sessionCookie },
457
+ });
458
+ let projects = projectsResponse;
459
+ if (projectsResponse?.data) {
460
+ projects = projectsResponse.data;
461
+ }
462
+ let projectId = '';
463
+ if (Array.isArray(projects) && projects.length > 0) {
464
+ const personalProject = projects.find((p) => p.type === 'personal' || p.name?.toLowerCase().includes('personal'));
465
+ projectId = personalProject?.id || projects[0]?.id;
466
+ }
467
+ if (!projectId) {
468
+ return [{
469
+ name: '[Kein Projekt gefunden]',
470
+ value: '__placeholder_no_project__',
471
+ description: 'Bitte erstellen Sie zuerst ein Projekt in n8n',
472
+ }];
473
+ }
474
+ // Get tables
475
+ const tablesResponse = await this.helpers.httpRequest({
476
+ method: 'GET',
477
+ url: `${baseUrl}/rest/projects/${projectId}/data-tables`,
478
+ json: true,
479
+ skipSslCertificateValidation: true,
480
+ headers: { Cookie: sessionCookie },
481
+ });
482
+ // Parse response - handle various API response formats
483
+ let tables = [];
484
+ if (Array.isArray(tablesResponse)) {
485
+ tables = tablesResponse;
486
+ }
487
+ else if (tablesResponse && typeof tablesResponse === 'object') {
488
+ const possibleArrays = [
489
+ tablesResponse.data?.data,
490
+ tablesResponse.data?.items,
491
+ tablesResponse.data?.tables,
492
+ tablesResponse.data?.results,
493
+ tablesResponse.data,
494
+ tablesResponse.tables,
495
+ tablesResponse.items,
496
+ tablesResponse.results,
497
+ tablesResponse.dataTables,
498
+ tablesResponse.rows,
499
+ tablesResponse.records,
500
+ ];
501
+ for (const arr of possibleArrays) {
502
+ if (Array.isArray(arr) && arr.length > 0) {
503
+ tables = arr;
504
+ break;
505
+ }
506
+ }
507
+ }
508
+ tables = tables.filter((t) => t && typeof t === 'object');
509
+ if (tables.length === 0) {
510
+ return [{
511
+ name: '[Keine Tabellen vorhanden]',
512
+ value: '__placeholder_no_tables__',
513
+ description: 'Erstellen Sie zuerst eine Tabelle mit der "Create" Operation',
514
+ }];
515
+ }
516
+ for (const table of tables) {
517
+ const tableId = table.id || table.tableId || table._id || table.uuid;
518
+ const tableName = table.name || table.tableName || table.displayName || table.title;
519
+ if (tableId) {
520
+ returnData.push({
521
+ name: tableName || `Table ${tableId}`,
522
+ value: String(tableId),
523
+ description: `ID: ${tableId}`,
524
+ });
525
+ }
526
+ }
527
+ returnData.sort((a, b) => a.name.localeCompare(b.name));
528
+ if (returnData.length === 0) {
529
+ return [{
530
+ name: '[Tabellen ohne gueltige IDs]',
531
+ value: '__placeholder_invalid__',
532
+ description: 'API Response hat unerwartetes Format',
533
+ }];
534
+ }
535
+ // Check if currently selected table still exists
536
+ if (currentlySelectedTableId &&
537
+ !currentlySelectedTableId.startsWith('__placeholder_') &&
538
+ !returnData.some(item => item.value === currentlySelectedTableId)) {
539
+ // The previously selected table was deleted - add warning at top
540
+ returnData.unshift({
541
+ name: '⚠️ [Vorherige Auswahl wurde geloescht - bitte neu waehlen]',
542
+ value: '__placeholder_deleted__',
543
+ description: `Die Tabelle mit ID ${currentlySelectedTableId} existiert nicht mehr`,
544
+ });
545
+ }
546
+ }
547
+ catch (error) {
548
+ const errorMsg = error.message?.substring(0, 80) || 'Unbekannter Fehler';
549
+ return [{
550
+ name: `[Fehler: ${errorMsg}]`,
551
+ value: '__placeholder_error__',
552
+ description: 'Pruefen Sie die Credentials und n8n Logs',
553
+ }];
554
+ }
555
+ return returnData;
556
+ },
557
+ /**
558
+ * Load available columns for selected table as dropdown options.
559
+ */
560
+ async getColumns() {
561
+ const returnData = [];
562
+ // Get currently selected columnId to check if it still exists
563
+ let currentlySelectedColumnId = '';
564
+ try {
565
+ currentlySelectedColumnId = this.getNodeParameter('columnId') || '';
566
+ }
567
+ catch (e) {
568
+ // Parameter not available yet
569
+ }
570
+ // Get credentials
571
+ let credentials;
572
+ try {
573
+ credentials = await this.getCredentials('dataTableManagerApi');
574
+ }
575
+ catch (error) {
576
+ return [{
577
+ name: '[Bitte zuerst Credentials konfigurieren]',
578
+ value: '__placeholder_no_credentials__',
579
+ description: 'Klicken Sie oben auf "Credential to connect with"',
580
+ }];
581
+ }
582
+ const baseUrl = (credentials.baseUrl || 'http://localhost:5678').replace(/\/$/, '');
583
+ const email = credentials.email || '';
584
+ const password = credentials.password || '';
585
+ // Get selected table ID - this uses getNodeParameter which works for saved values
586
+ // Since tableId is selected from the dropdown (which works), this should be available
587
+ let tableId = '';
588
+ try {
589
+ tableId = this.getNodeParameter('tableId') || '';
590
+ }
591
+ catch (e) {
592
+ // Parameter not available
593
+ }
594
+ if (!baseUrl || !email || !password) {
595
+ return [{
596
+ name: '[Credentials unvollstaendig]',
597
+ value: '__placeholder_incomplete__',
598
+ description: 'Alle Felder in den Credentials muessen ausgefuellt sein',
599
+ }];
600
+ }
601
+ if (!tableId || tableId.startsWith('__placeholder_')) {
602
+ return [{
603
+ name: '[Bitte zuerst eine Tabelle auswaehlen]',
604
+ value: '__placeholder_no_table__',
605
+ description: 'Waehlen Sie zuerst eine Tabelle aus dem Dropdown',
606
+ }];
607
+ }
608
+ try {
609
+ // Login to n8n
610
+ const loginResponse = await this.helpers.httpRequest({
611
+ method: 'POST',
612
+ url: `${baseUrl}/rest/login`,
613
+ body: { emailOrLdapLoginId: email, password },
614
+ json: true,
615
+ skipSslCertificateValidation: true,
616
+ returnFullResponse: true,
617
+ });
618
+ // Extract session cookie
619
+ let sessionCookie = '';
620
+ const setCookieHeader = loginResponse.headers?.['set-cookie'];
621
+ if (setCookieHeader) {
622
+ const cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
623
+ sessionCookie = cookies.map((c) => c.split(';')[0]).join('; ');
624
+ }
625
+ // Get projects
626
+ const projectsResponse = await this.helpers.httpRequest({
627
+ method: 'GET',
628
+ url: `${baseUrl}/rest/projects`,
629
+ json: true,
630
+ skipSslCertificateValidation: true,
631
+ headers: { Cookie: sessionCookie },
632
+ });
633
+ let projects = projectsResponse;
634
+ if (projectsResponse?.data) {
635
+ projects = projectsResponse.data;
636
+ }
637
+ let projectId = '';
638
+ if (Array.isArray(projects) && projects.length > 0) {
639
+ const personalProject = projects.find((p) => p.type === 'personal' || p.name?.toLowerCase().includes('personal'));
640
+ projectId = personalProject?.id || projects[0]?.id;
641
+ }
642
+ if (!projectId) {
643
+ return [{
644
+ name: '[Kein Projekt gefunden]',
645
+ value: '__placeholder_no_project__',
646
+ description: 'Bitte erstellen Sie ein Projekt in n8n',
647
+ }];
648
+ }
649
+ // Get columns for the selected table
650
+ const columnsResponse = await this.helpers.httpRequest({
651
+ method: 'GET',
652
+ url: `${baseUrl}/rest/projects/${projectId}/data-tables/${tableId}/columns`,
653
+ json: true,
654
+ skipSslCertificateValidation: true,
655
+ headers: { Cookie: sessionCookie },
656
+ });
657
+ // Parse response
658
+ let columns = [];
659
+ if (Array.isArray(columnsResponse)) {
660
+ columns = columnsResponse;
661
+ }
662
+ else if (columnsResponse && typeof columnsResponse === 'object') {
663
+ const possibleArrays = [
664
+ columnsResponse.data?.data,
665
+ columnsResponse.data?.columns,
666
+ columnsResponse.data?.items,
667
+ columnsResponse.data?.results,
668
+ columnsResponse.data,
669
+ columnsResponse.columns,
670
+ columnsResponse.items,
671
+ columnsResponse.results,
672
+ columnsResponse.rows,
673
+ columnsResponse.records,
674
+ ];
675
+ for (const arr of possibleArrays) {
676
+ if (Array.isArray(arr) && arr.length > 0) {
677
+ columns = arr;
678
+ break;
679
+ }
680
+ }
681
+ }
682
+ columns = columns.filter((c) => c && typeof c === 'object');
683
+ if (columns.length === 0) {
684
+ return [{
685
+ name: '[Keine Spalten vorhanden]',
686
+ value: '__placeholder_no_columns__',
687
+ description: 'Diese Tabelle hat keine Spalten',
688
+ }];
689
+ }
690
+ for (const column of columns) {
691
+ const columnId = column.id || column.columnId || column._id || column.uuid;
692
+ const columnName = column.name || column.columnName || column.displayName || column.title;
693
+ const columnType = column.type || column.dataType || 'unknown';
694
+ if (columnId) {
695
+ returnData.push({
696
+ name: `${columnName || `Column ${columnId}`} (${columnType})`,
697
+ value: String(columnId),
698
+ description: `Type: ${columnType}, ID: ${columnId}`,
699
+ });
700
+ }
701
+ }
702
+ returnData.sort((a, b) => a.name.localeCompare(b.name));
703
+ if (returnData.length === 0) {
704
+ return [{
705
+ name: '[Spalten ohne gueltige IDs]',
706
+ value: '__placeholder_invalid__',
707
+ description: 'API Response hat unerwartetes Format',
708
+ }];
709
+ }
710
+ // Check if currently selected column still exists
711
+ if (currentlySelectedColumnId &&
712
+ !currentlySelectedColumnId.startsWith('__placeholder_') &&
713
+ !returnData.some(item => item.value === currentlySelectedColumnId)) {
714
+ // The previously selected column was deleted - add warning at top
715
+ returnData.unshift({
716
+ name: '⚠️ [Vorherige Auswahl wurde geloescht - bitte neu waehlen]',
717
+ value: '__placeholder_deleted__',
718
+ description: `Die Spalte mit ID ${currentlySelectedColumnId} existiert nicht mehr`,
719
+ });
720
+ }
721
+ }
722
+ catch (error) {
723
+ const errorMsg = error.message?.substring(0, 80) || 'Unbekannter Fehler';
724
+ return [{
725
+ name: `[Fehler: ${errorMsg}]`,
726
+ value: '__placeholder_error__',
727
+ description: 'Pruefen Sie die Verbindungsdaten',
728
+ }];
729
+ }
730
+ return returnData;
731
+ },
732
+ },
733
+ };
734
+ async execute() {
735
+ const items = this.getInputData();
736
+ const returnData = [];
737
+ const resource = this.getNodeParameter('resource', 0);
738
+ const operation = this.getNodeParameter('operation', 0);
739
+ // Get credentials
740
+ const credentials = await this.getCredentials('dataTableManagerApi');
741
+ const baseUrl = credentials.baseUrl.replace(/\/$/, '');
742
+ const email = credentials.email;
743
+ const password = credentials.password;
744
+ // Login to n8n and get session cookie
745
+ let sessionCookie = '';
746
+ try {
747
+ const loginResponse = await this.helpers.httpRequest({
748
+ method: 'POST',
749
+ url: `${baseUrl}/rest/login`,
750
+ body: { emailOrLdapLoginId: email, password },
751
+ json: true,
752
+ skipSslCertificateValidation: true,
753
+ returnFullResponse: true,
754
+ });
755
+ const setCookieHeader = loginResponse.headers?.['set-cookie'];
756
+ if (setCookieHeader) {
757
+ const cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
758
+ sessionCookie = cookies
759
+ .map((c) => c.split(';')[0])
760
+ .join('; ');
761
+ }
762
+ }
763
+ catch (error) {
764
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to login to n8n: ${error.message}. Please check your credentials.`);
765
+ }
766
+ // Get Project ID
767
+ let projectId = '';
768
+ try {
769
+ const projectsResponse = await this.helpers.httpRequest({
770
+ method: 'GET',
771
+ url: `${baseUrl}/rest/projects`,
772
+ json: true,
773
+ skipSslCertificateValidation: true,
774
+ headers: {
775
+ Cookie: sessionCookie,
776
+ },
777
+ });
778
+ let projects = projectsResponse;
779
+ if (projectsResponse?.data) {
780
+ projects = projectsResponse.data;
781
+ }
782
+ if (Array.isArray(projects) && projects.length > 0) {
783
+ const personalProject = projects.find((p) => p.type === 'personal' || p.name?.toLowerCase().includes('personal'));
784
+ projectId = personalProject?.id || projects[0]?.id;
785
+ }
786
+ }
787
+ catch (error) {
788
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to get projects: ${error.message}`);
789
+ }
790
+ if (!projectId) {
791
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No project found. Please create a project in n8n first.');
792
+ }
793
+ const apiBaseUrl = `${baseUrl}/rest/projects/${projectId}/data-tables`;
794
+ // Helper function for API requests
795
+ const makeInternalRequest = async (method, url, body) => {
796
+ const requestOptions = {
797
+ method,
798
+ url,
799
+ json: true,
800
+ skipSslCertificateValidation: true,
801
+ headers: {
802
+ 'Content-Type': 'application/json',
803
+ Cookie: sessionCookie,
804
+ },
805
+ };
806
+ if (body) {
807
+ requestOptions.body = body;
808
+ }
809
+ try {
810
+ return await this.helpers.httpRequest(requestOptions);
811
+ }
812
+ catch (error) {
813
+ const errorMessage = error?.message || String(error);
814
+ throw new Error(`Data Tables API request failed. URL: ${url}. Error: ${errorMessage}.`);
815
+ }
816
+ };
817
+ for (let i = 0; i < items.length; i++) {
818
+ try {
819
+ let responseData;
820
+ const validateNotPlaceholder = (value, fieldName) => {
821
+ if (!value || value.startsWith('__placeholder_')) {
822
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${fieldName} wurde nicht korrekt ausgewaehlt. Bitte waehlen Sie einen gueltigen Wert aus dem Dropdown.`, { itemIndex: i });
823
+ }
824
+ };
825
+ if (resource === 'table') {
826
+ if (operation === 'create') {
827
+ const tableName = this.getNodeParameter('tableName', i);
828
+ const columnsData = this.getNodeParameter('columns', i);
829
+ const columns = columnsData.columnValues || [];
830
+ responseData = await makeInternalRequest('POST', apiBaseUrl, {
831
+ name: tableName,
832
+ columns: columns.map((col, index) => ({
833
+ name: col.name,
834
+ type: col.type,
835
+ index,
836
+ })),
837
+ });
838
+ }
839
+ else if (operation === 'getAll') {
840
+ responseData = await makeInternalRequest('GET', apiBaseUrl);
841
+ }
842
+ else if (operation === 'update') {
843
+ const tableId = this.getNodeParameter('tableId', i);
844
+ validateNotPlaceholder(tableId, 'Table');
845
+ const newTableName = this.getNodeParameter('newTableName', i);
846
+ responseData = await makeInternalRequest('PATCH', `${apiBaseUrl}/${tableId}`, {
847
+ name: newTableName,
848
+ });
849
+ }
850
+ else if (operation === 'delete') {
851
+ const tableId = this.getNodeParameter('tableId', i);
852
+ validateNotPlaceholder(tableId, 'Table');
853
+ await makeInternalRequest('DELETE', `${apiBaseUrl}/${tableId}`);
854
+ responseData = { success: true, deletedTableId: tableId };
855
+ }
856
+ }
857
+ else if (resource === 'column') {
858
+ const tableId = this.getNodeParameter('tableId', i);
859
+ validateNotPlaceholder(tableId, 'Table');
860
+ if (operation === 'add') {
861
+ // Multiple columns support
862
+ const columnsToAdd = this.getNodeParameter('columnsToAdd', i);
863
+ const columns = columnsToAdd.columnValues || [];
864
+ if (columns.length === 0) {
865
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Bitte mindestens eine Spalte zum Hinzufuegen angeben.', { itemIndex: i });
866
+ }
867
+ const addedColumns = [];
868
+ for (const column of columns) {
869
+ const result = await makeInternalRequest('POST', `${apiBaseUrl}/${tableId}/columns`, {
870
+ name: column.name,
871
+ type: column.type,
872
+ });
873
+ addedColumns.push(result);
874
+ }
875
+ responseData = {
876
+ success: true,
877
+ addedColumns,
878
+ count: addedColumns.length,
879
+ };
880
+ }
881
+ else if (operation === 'getAll') {
882
+ responseData = await makeInternalRequest('GET', `${apiBaseUrl}/${tableId}/columns`);
883
+ }
884
+ else if (operation === 'delete') {
885
+ // Multiple columns support
886
+ const columnIds = this.getNodeParameter('columnIds', i);
887
+ if (!columnIds || columnIds.length === 0) {
888
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Bitte mindestens eine Spalte zum Loeschen auswaehlen.', { itemIndex: i });
889
+ }
890
+ // Validate all selected columns
891
+ for (const columnId of columnIds) {
892
+ validateNotPlaceholder(columnId, 'Column');
893
+ }
894
+ const deletedColumns = [];
895
+ for (const columnId of columnIds) {
896
+ await makeInternalRequest('DELETE', `${apiBaseUrl}/${tableId}/columns/${columnId}`);
897
+ deletedColumns.push(columnId);
898
+ }
899
+ responseData = {
900
+ success: true,
901
+ deletedColumnIds: deletedColumns,
902
+ count: deletedColumns.length,
903
+ };
904
+ }
905
+ else if (operation === 'rename') {
906
+ const columnId = this.getNodeParameter('columnId', i);
907
+ validateNotPlaceholder(columnId, 'Column');
908
+ const newColumnName = this.getNodeParameter('newColumnName', i);
909
+ responseData = await makeInternalRequest('PATCH', `${apiBaseUrl}/${tableId}/columns/${columnId}/rename`, {
910
+ name: newColumnName,
911
+ });
912
+ }
913
+ }
914
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
915
+ returnData.push(...executionData);
916
+ }
917
+ catch (error) {
918
+ const errorMessage = error instanceof Error ? error.message : String(error);
919
+ if (this.continueOnFail()) {
920
+ returnData.push({
921
+ json: {
922
+ error: errorMessage,
923
+ },
924
+ pairedItem: { item: i },
925
+ });
926
+ continue;
927
+ }
928
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, {
929
+ itemIndex: i,
930
+ description: `Error during ${resource} ${operation}: ${errorMessage}`,
931
+ });
932
+ }
933
+ }
934
+ return [returnData];
935
+ }
936
+ }
937
+ exports.DataTableManager = DataTableManager;