appwrite-utils-cli 1.7.9 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -199
- package/README.md +87 -30
- package/dist/adapters/AdapterFactory.js +5 -25
- package/dist/adapters/DatabaseAdapter.d.ts +17 -2
- package/dist/adapters/LegacyAdapter.d.ts +2 -1
- package/dist/adapters/LegacyAdapter.js +212 -16
- package/dist/adapters/TablesDBAdapter.d.ts +2 -12
- package/dist/adapters/TablesDBAdapter.js +261 -57
- package/dist/cli/commands/databaseCommands.js +4 -3
- package/dist/cli/commands/functionCommands.js +17 -8
- package/dist/collections/attributes.js +447 -125
- package/dist/collections/methods.js +197 -186
- package/dist/collections/tableOperations.d.ts +86 -0
- package/dist/collections/tableOperations.js +434 -0
- package/dist/collections/transferOperations.d.ts +3 -2
- package/dist/collections/transferOperations.js +93 -12
- package/dist/config/yamlConfig.d.ts +221 -88
- package/dist/examples/yamlTerminologyExample.d.ts +1 -1
- package/dist/examples/yamlTerminologyExample.js +6 -3
- package/dist/functions/fnConfigDiscovery.d.ts +3 -0
- package/dist/functions/fnConfigDiscovery.js +108 -0
- package/dist/interactiveCLI.js +18 -15
- package/dist/main.js +211 -73
- package/dist/migrations/appwriteToX.d.ts +88 -23
- package/dist/migrations/comprehensiveTransfer.d.ts +2 -0
- package/dist/migrations/comprehensiveTransfer.js +83 -6
- package/dist/migrations/dataLoader.d.ts +227 -69
- package/dist/migrations/dataLoader.js +3 -3
- package/dist/migrations/importController.js +3 -3
- package/dist/migrations/relationships.d.ts +8 -2
- package/dist/migrations/services/ImportOrchestrator.js +3 -3
- package/dist/migrations/transfer.js +159 -37
- package/dist/shared/attributeMapper.d.ts +20 -0
- package/dist/shared/attributeMapper.js +203 -0
- package/dist/shared/selectionDialogs.js +8 -4
- package/dist/storage/schemas.d.ts +354 -92
- package/dist/utils/configDiscovery.js +4 -3
- package/dist/utils/versionDetection.d.ts +0 -4
- package/dist/utils/versionDetection.js +41 -173
- package/dist/utils/yamlConverter.js +89 -16
- package/dist/utils/yamlLoader.d.ts +1 -1
- package/dist/utils/yamlLoader.js +6 -2
- package/dist/utilsController.js +56 -19
- package/package.json +4 -4
- package/src/adapters/AdapterFactory.ts +119 -143
- package/src/adapters/DatabaseAdapter.ts +18 -3
- package/src/adapters/LegacyAdapter.ts +236 -105
- package/src/adapters/TablesDBAdapter.ts +773 -643
- package/src/cli/commands/databaseCommands.ts +13 -12
- package/src/cli/commands/functionCommands.ts +23 -14
- package/src/collections/attributes.ts +2054 -1611
- package/src/collections/methods.ts +208 -293
- package/src/collections/tableOperations.ts +506 -0
- package/src/collections/transferOperations.ts +218 -144
- package/src/examples/yamlTerminologyExample.ts +10 -5
- package/src/functions/fnConfigDiscovery.ts +103 -0
- package/src/interactiveCLI.ts +25 -20
- package/src/main.ts +549 -194
- package/src/migrations/comprehensiveTransfer.ts +126 -50
- package/src/migrations/dataLoader.ts +3 -3
- package/src/migrations/importController.ts +3 -3
- package/src/migrations/services/ImportOrchestrator.ts +3 -3
- package/src/migrations/transfer.ts +148 -131
- package/src/shared/attributeMapper.ts +229 -0
- package/src/shared/selectionDialogs.ts +29 -25
- package/src/utils/configDiscovery.ts +9 -3
- package/src/utils/versionDetection.ts +74 -228
- package/src/utils/yamlConverter.ts +94 -17
- package/src/utils/yamlLoader.ts +11 -4
- package/src/utilsController.ts +80 -30
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* without any translation layer. It uses object notation parameters
|
|
6
6
|
* and returns Models.Row instead of Models.Document.
|
|
7
7
|
*/
|
|
8
|
-
import { Query } from "node-appwrite";
|
|
8
|
+
import { IndexType, Query, RelationMutate, RelationshipType } from "node-appwrite";
|
|
9
9
|
import { chunk } from "es-toolkit";
|
|
10
10
|
import { BaseAdapter, AdapterError } from './DatabaseAdapter.js';
|
|
11
|
+
import { TablesDB, Client } from "node-appwrite";
|
|
11
12
|
/**
|
|
12
13
|
* TablesDBAdapter implementation for native TablesDB API
|
|
13
14
|
*/
|
|
@@ -16,12 +17,16 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
16
17
|
constructor(client) {
|
|
17
18
|
super(client, 'tablesdb');
|
|
18
19
|
// Assuming TablesDB service is available on the client
|
|
19
|
-
this.tablesDB =
|
|
20
|
+
this.tablesDB = new TablesDB(client);
|
|
20
21
|
}
|
|
21
22
|
// Row (Document) Operations
|
|
22
23
|
async listRows(params) {
|
|
23
24
|
try {
|
|
24
|
-
const result = await this.tablesDB.listRows(
|
|
25
|
+
const result = await this.tablesDB.listRows({
|
|
26
|
+
tableId: params.tableId,
|
|
27
|
+
databaseId: params.databaseId,
|
|
28
|
+
queries: params.queries || [],
|
|
29
|
+
});
|
|
25
30
|
return {
|
|
26
31
|
data: result.rows,
|
|
27
32
|
rows: result.rows,
|
|
@@ -100,8 +105,9 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
100
105
|
}
|
|
101
106
|
async createTable(params) {
|
|
102
107
|
try {
|
|
108
|
+
const rowSecurity = params.rowSecurity ?? params.documentSecurity ?? false;
|
|
103
109
|
const result = await this.tablesDB.createTable(params.databaseId, params.id, // tableId
|
|
104
|
-
params.name, params.permissions || [],
|
|
110
|
+
params.name, params.permissions || [], rowSecurity, params.enabled ?? true);
|
|
105
111
|
return {
|
|
106
112
|
data: result,
|
|
107
113
|
tables: [result]
|
|
@@ -113,8 +119,9 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
113
119
|
}
|
|
114
120
|
async updateTable(params) {
|
|
115
121
|
try {
|
|
122
|
+
const rowSecurity = params.rowSecurity ?? params.documentSecurity;
|
|
116
123
|
const result = await this.tablesDB.updateTable(params.databaseId, params.id, // tableId
|
|
117
|
-
params.name, params.permissions,
|
|
124
|
+
params.name, params.permissions, rowSecurity, params.enabled);
|
|
118
125
|
return {
|
|
119
126
|
data: result,
|
|
120
127
|
tables: [result]
|
|
@@ -179,39 +186,133 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
179
186
|
// Attribute Operations
|
|
180
187
|
async createAttribute(params) {
|
|
181
188
|
try {
|
|
182
|
-
// TablesDB
|
|
189
|
+
// TablesDB exposes type-specific column methods
|
|
190
|
+
// TablesDB uses type-specific column methods
|
|
183
191
|
let result;
|
|
184
|
-
|
|
192
|
+
const type = (params.type || "").toLowerCase();
|
|
193
|
+
const required = params.required ?? false;
|
|
194
|
+
const array = params.array ?? false;
|
|
195
|
+
const encrypt = params.encrypt ?? params.encrypted ?? false;
|
|
196
|
+
const normalizedDefault = params.default === null || params.default === undefined
|
|
197
|
+
? undefined
|
|
198
|
+
: params.default;
|
|
199
|
+
const numberDefault = typeof normalizedDefault === "number" ? normalizedDefault : undefined;
|
|
200
|
+
const stringDefault = typeof normalizedDefault === "string" ? normalizedDefault : undefined;
|
|
201
|
+
const booleanDefault = typeof normalizedDefault === "boolean" ? normalizedDefault : undefined;
|
|
202
|
+
switch (type) {
|
|
185
203
|
case 'string':
|
|
186
|
-
result = await this.tablesDB.
|
|
204
|
+
result = await this.tablesDB.createStringColumn({
|
|
205
|
+
databaseId: params.databaseId,
|
|
206
|
+
tableId: params.tableId,
|
|
207
|
+
key: params.key,
|
|
208
|
+
size: params.size || 255,
|
|
209
|
+
required: params.required ?? false,
|
|
210
|
+
xdefault: params.default,
|
|
211
|
+
array: params.array ?? false,
|
|
212
|
+
encrypt: params.encrypt ?? false
|
|
213
|
+
});
|
|
187
214
|
break;
|
|
188
215
|
case 'integer':
|
|
189
|
-
result = await this.tablesDB.
|
|
216
|
+
result = await this.tablesDB.createIntegerColumn({
|
|
217
|
+
databaseId: params.databaseId,
|
|
218
|
+
tableId: params.tableId,
|
|
219
|
+
key: params.key,
|
|
220
|
+
required: params.required ?? false,
|
|
221
|
+
min: params.min,
|
|
222
|
+
max: params.max,
|
|
223
|
+
xdefault: params.default,
|
|
224
|
+
array: params.array ?? false
|
|
225
|
+
});
|
|
190
226
|
break;
|
|
191
227
|
case 'float':
|
|
192
228
|
case 'double':
|
|
193
|
-
result = await this.tablesDB.
|
|
229
|
+
result = await this.tablesDB.createFloatColumn({
|
|
230
|
+
databaseId: params.databaseId,
|
|
231
|
+
tableId: params.tableId,
|
|
232
|
+
key: params.key,
|
|
233
|
+
required: params.required ?? false,
|
|
234
|
+
min: params.min,
|
|
235
|
+
max: params.max,
|
|
236
|
+
xdefault: params.default,
|
|
237
|
+
array: params.array ?? false
|
|
238
|
+
});
|
|
194
239
|
break;
|
|
195
240
|
case 'boolean':
|
|
196
|
-
result = await this.tablesDB.
|
|
241
|
+
result = await this.tablesDB.createBooleanColumn({
|
|
242
|
+
databaseId: params.databaseId,
|
|
243
|
+
tableId: params.tableId,
|
|
244
|
+
key: params.key,
|
|
245
|
+
required: params.required ?? false,
|
|
246
|
+
xdefault: params.default,
|
|
247
|
+
array: params.array ?? false
|
|
248
|
+
});
|
|
197
249
|
break;
|
|
198
250
|
case 'datetime':
|
|
199
|
-
result = await this.tablesDB.
|
|
251
|
+
result = await this.tablesDB.createDatetimeColumn({
|
|
252
|
+
databaseId: params.databaseId,
|
|
253
|
+
tableId: params.tableId,
|
|
254
|
+
key: params.key,
|
|
255
|
+
required: params.required ?? false,
|
|
256
|
+
xdefault: params.default,
|
|
257
|
+
array: params.array ?? false
|
|
258
|
+
});
|
|
200
259
|
break;
|
|
201
260
|
case 'email':
|
|
202
|
-
result = await this.tablesDB.
|
|
261
|
+
result = await this.tablesDB.createEmailColumn({
|
|
262
|
+
databaseId: params.databaseId,
|
|
263
|
+
tableId: params.tableId,
|
|
264
|
+
key: params.key,
|
|
265
|
+
required: params.required ?? false,
|
|
266
|
+
xdefault: params.default,
|
|
267
|
+
array: params.array ?? false
|
|
268
|
+
});
|
|
203
269
|
break;
|
|
204
270
|
case 'enum':
|
|
205
|
-
|
|
271
|
+
// Defensive: require non-empty elements
|
|
272
|
+
if (!Array.isArray(params.elements) || params.elements.length === 0) {
|
|
273
|
+
throw new AdapterError(`Enum '${params.key}' requires a non-empty 'elements' array`, 'CREATE_ENUM_ELEMENTS_REQUIRED');
|
|
274
|
+
}
|
|
275
|
+
result = await this.tablesDB.createEnumColumn({
|
|
276
|
+
databaseId: params.databaseId,
|
|
277
|
+
tableId: params.tableId,
|
|
278
|
+
key: params.key,
|
|
279
|
+
elements: params.elements,
|
|
280
|
+
required: params.required ?? false,
|
|
281
|
+
xdefault: params.default,
|
|
282
|
+
array: params.array ?? false
|
|
283
|
+
});
|
|
206
284
|
break;
|
|
207
285
|
case 'ip':
|
|
208
|
-
result = await this.tablesDB.
|
|
286
|
+
result = await this.tablesDB.createIpColumn({
|
|
287
|
+
databaseId: params.databaseId,
|
|
288
|
+
tableId: params.tableId,
|
|
289
|
+
key: params.key,
|
|
290
|
+
required: params.required ?? false,
|
|
291
|
+
xdefault: params.default,
|
|
292
|
+
array: params.array ?? false
|
|
293
|
+
});
|
|
209
294
|
break;
|
|
210
295
|
case 'url':
|
|
211
|
-
result = await this.tablesDB.
|
|
296
|
+
result = await this.tablesDB.createUrlColumn({
|
|
297
|
+
databaseId: params.databaseId,
|
|
298
|
+
tableId: params.tableId,
|
|
299
|
+
key: params.key,
|
|
300
|
+
required: params.required ?? false,
|
|
301
|
+
xdefault: params.default,
|
|
302
|
+
array: params.array ?? false
|
|
303
|
+
});
|
|
212
304
|
break;
|
|
213
305
|
case 'relationship':
|
|
214
|
-
result = await this.tablesDB.
|
|
306
|
+
result = await this.tablesDB.createRelationshipColumn({
|
|
307
|
+
databaseId: params.databaseId,
|
|
308
|
+
tableId: params.tableId,
|
|
309
|
+
key: params.key,
|
|
310
|
+
relatedTableId: params.relatedCollection || '',
|
|
311
|
+
type: (params.type || 'oneToOne'),
|
|
312
|
+
twoWay: params.twoWay ?? false,
|
|
313
|
+
twoWayKey: params.twoWayKey,
|
|
314
|
+
onDelete: params.onDelete || 'restrict'
|
|
315
|
+
});
|
|
215
316
|
break;
|
|
216
317
|
default:
|
|
217
318
|
throw new AdapterError(`Unsupported attribute type: ${params.type}`, 'UNSUPPORTED_ATTRIBUTE_TYPE');
|
|
@@ -224,9 +325,135 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
224
325
|
}
|
|
225
326
|
async updateAttribute(params) {
|
|
226
327
|
try {
|
|
227
|
-
// TablesDB uses type-specific update methods
|
|
228
|
-
//
|
|
229
|
-
|
|
328
|
+
// TablesDB uses type-specific update methods with object notation
|
|
329
|
+
// We need to detect the existing column type to use the correct update method
|
|
330
|
+
// For now, we'll need to get the column info first, then use the appropriate update method
|
|
331
|
+
// Get the current table schema to determine the column type
|
|
332
|
+
const tableInfo = await this.tablesDB.getTable(params.databaseId, params.tableId);
|
|
333
|
+
// Find the column to determine its type
|
|
334
|
+
const column = tableInfo.columns?.find(col => col.key === params.key);
|
|
335
|
+
if (!column) {
|
|
336
|
+
throw new AdapterError(`Column '${params.key}' not found in table`, 'COLUMN_NOT_FOUND');
|
|
337
|
+
}
|
|
338
|
+
let result;
|
|
339
|
+
// Use the appropriate updateXColumn method based on the column type
|
|
340
|
+
// Cast column to proper Models type to access its specific properties
|
|
341
|
+
const columnType = column.type;
|
|
342
|
+
switch (columnType) {
|
|
343
|
+
case 'string':
|
|
344
|
+
const stringColumn = column;
|
|
345
|
+
result = await this.tablesDB.updateStringColumn({
|
|
346
|
+
databaseId: params.databaseId,
|
|
347
|
+
tableId: params.tableId,
|
|
348
|
+
key: params.key,
|
|
349
|
+
required: params.required !== undefined ? params.required : stringColumn.required,
|
|
350
|
+
xdefault: params.default !== undefined ? params.default : stringColumn.default,
|
|
351
|
+
size: params.size !== undefined ? params.size : stringColumn.size,
|
|
352
|
+
});
|
|
353
|
+
break;
|
|
354
|
+
case 'integer':
|
|
355
|
+
const integerColumn = column;
|
|
356
|
+
result = await this.tablesDB.updateIntegerColumn({
|
|
357
|
+
databaseId: params.databaseId,
|
|
358
|
+
tableId: params.tableId,
|
|
359
|
+
key: params.key,
|
|
360
|
+
required: params.required !== undefined ? params.required : integerColumn.required,
|
|
361
|
+
xdefault: params.default !== undefined ? params.default : integerColumn.default,
|
|
362
|
+
// Only send min/max when explicitly provided to avoid resubmitting extreme values
|
|
363
|
+
...(params.min !== undefined ? { min: params.min } : {}),
|
|
364
|
+
...(params.max !== undefined ? { max: params.max } : {}),
|
|
365
|
+
});
|
|
366
|
+
break;
|
|
367
|
+
case 'float':
|
|
368
|
+
case 'double':
|
|
369
|
+
const floatColumn = column;
|
|
370
|
+
result = await this.tablesDB.updateFloatColumn({
|
|
371
|
+
databaseId: params.databaseId,
|
|
372
|
+
tableId: params.tableId,
|
|
373
|
+
key: params.key,
|
|
374
|
+
required: params.required !== undefined ? params.required : floatColumn.required,
|
|
375
|
+
xdefault: params.default !== undefined ? params.default : floatColumn.default,
|
|
376
|
+
...(params.min !== undefined ? { min: params.min } : {}),
|
|
377
|
+
...(params.max !== undefined ? { max: params.max } : {}),
|
|
378
|
+
});
|
|
379
|
+
break;
|
|
380
|
+
case 'boolean':
|
|
381
|
+
const booleanColumn = column;
|
|
382
|
+
result = await this.tablesDB.updateBooleanColumn({
|
|
383
|
+
databaseId: params.databaseId,
|
|
384
|
+
tableId: params.tableId,
|
|
385
|
+
key: params.key,
|
|
386
|
+
required: params.required !== undefined ? params.required : booleanColumn.required,
|
|
387
|
+
xdefault: params.default !== undefined ? params.default : booleanColumn.default
|
|
388
|
+
});
|
|
389
|
+
break;
|
|
390
|
+
case 'datetime':
|
|
391
|
+
const datetimeColumn = column;
|
|
392
|
+
result = await this.tablesDB.updateDatetimeColumn({
|
|
393
|
+
databaseId: params.databaseId,
|
|
394
|
+
tableId: params.tableId,
|
|
395
|
+
key: params.key,
|
|
396
|
+
required: params.required !== undefined ? params.required : datetimeColumn.required,
|
|
397
|
+
xdefault: params.default !== undefined ? params.default : datetimeColumn.default
|
|
398
|
+
});
|
|
399
|
+
break;
|
|
400
|
+
case 'email':
|
|
401
|
+
const emailColumn = column;
|
|
402
|
+
result = await this.tablesDB.updateEmailColumn({
|
|
403
|
+
databaseId: params.databaseId,
|
|
404
|
+
tableId: params.tableId,
|
|
405
|
+
key: params.key,
|
|
406
|
+
required: params.required !== undefined ? params.required : emailColumn.required,
|
|
407
|
+
xdefault: params.default !== undefined ? params.default : emailColumn.default
|
|
408
|
+
});
|
|
409
|
+
break;
|
|
410
|
+
case 'enum':
|
|
411
|
+
const enumColumn = column;
|
|
412
|
+
// Choose elements to send only when provided, otherwise preserve existing
|
|
413
|
+
const provided = params.elements;
|
|
414
|
+
const existing = enumColumn?.elements;
|
|
415
|
+
const nextElements = (Array.isArray(provided) && provided.length > 0) ? provided : existing;
|
|
416
|
+
result = await this.tablesDB.updateEnumColumn({
|
|
417
|
+
databaseId: params.databaseId,
|
|
418
|
+
tableId: params.tableId,
|
|
419
|
+
key: params.key,
|
|
420
|
+
required: params.required !== undefined ? params.required : enumColumn.required,
|
|
421
|
+
xdefault: params.default !== undefined ? params.default : enumColumn.default,
|
|
422
|
+
elements: nextElements
|
|
423
|
+
});
|
|
424
|
+
break;
|
|
425
|
+
case 'ip':
|
|
426
|
+
const ipColumn = column;
|
|
427
|
+
result = await this.tablesDB.updateIpColumn({
|
|
428
|
+
databaseId: params.databaseId,
|
|
429
|
+
tableId: params.tableId,
|
|
430
|
+
key: params.key,
|
|
431
|
+
required: params.required !== undefined ? params.required : ipColumn.required,
|
|
432
|
+
xdefault: params.default !== undefined ? params.default : ipColumn.default
|
|
433
|
+
});
|
|
434
|
+
break;
|
|
435
|
+
case 'url':
|
|
436
|
+
const urlColumn = column;
|
|
437
|
+
result = await this.tablesDB.updateUrlColumn({
|
|
438
|
+
databaseId: params.databaseId,
|
|
439
|
+
tableId: params.tableId,
|
|
440
|
+
key: params.key,
|
|
441
|
+
required: params.required !== undefined ? params.required : urlColumn.required,
|
|
442
|
+
xdefault: params.default !== undefined ? params.default : urlColumn.default
|
|
443
|
+
});
|
|
444
|
+
break;
|
|
445
|
+
case 'relationship':
|
|
446
|
+
const relationshipColumn = column;
|
|
447
|
+
result = await this.tablesDB.updateRelationshipColumn({
|
|
448
|
+
databaseId: params.databaseId,
|
|
449
|
+
tableId: params.tableId,
|
|
450
|
+
key: params.key,
|
|
451
|
+
onDelete: (params.onDelete !== undefined ? params.onDelete : relationshipColumn.onDelete),
|
|
452
|
+
});
|
|
453
|
+
break;
|
|
454
|
+
default:
|
|
455
|
+
throw new AdapterError(`Unsupported column type for update: ${columnType}`, 'UNSUPPORTED_COLUMN_TYPE');
|
|
456
|
+
}
|
|
230
457
|
return { data: result };
|
|
231
458
|
}
|
|
232
459
|
catch (error) {
|
|
@@ -235,7 +462,11 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
235
462
|
}
|
|
236
463
|
async deleteAttribute(params) {
|
|
237
464
|
try {
|
|
238
|
-
const result = await this.tablesDB.
|
|
465
|
+
const result = await this.tablesDB.deleteColumn({
|
|
466
|
+
databaseId: params.databaseId,
|
|
467
|
+
tableId: params.tableId,
|
|
468
|
+
key: params.key
|
|
469
|
+
});
|
|
239
470
|
return { data: result };
|
|
240
471
|
}
|
|
241
472
|
catch (error) {
|
|
@@ -245,7 +476,7 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
245
476
|
// Bulk Operations (Native TablesDB Support)
|
|
246
477
|
async bulkCreateRows(params) {
|
|
247
478
|
try {
|
|
248
|
-
const result = await this.tablesDB.
|
|
479
|
+
const result = await this.tablesDB.createRows(params);
|
|
249
480
|
return {
|
|
250
481
|
data: result.rows,
|
|
251
482
|
rows: result.rows,
|
|
@@ -258,7 +489,7 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
258
489
|
}
|
|
259
490
|
async bulkUpsertRows(params) {
|
|
260
491
|
try {
|
|
261
|
-
const result = await this.tablesDB.
|
|
492
|
+
const result = await this.tablesDB.upsertRows(params);
|
|
262
493
|
return {
|
|
263
494
|
data: result.rows,
|
|
264
495
|
rows: result.rows,
|
|
@@ -323,45 +554,18 @@ export class TablesDBAdapter extends BaseAdapter {
|
|
|
323
554
|
* Execute a transaction (if supported by TablesDB)
|
|
324
555
|
*/
|
|
325
556
|
async executeTransaction(operations) {
|
|
326
|
-
if (!this.tablesDB.transaction) {
|
|
327
|
-
throw new AdapterError('Transactions are not supported in this TablesDB version', 'TRANSACTIONS_NOT_SUPPORTED');
|
|
328
|
-
}
|
|
329
557
|
try {
|
|
330
|
-
|
|
558
|
+
// Create a new transaction first
|
|
559
|
+
const transaction = await this.tablesDB.createTransaction();
|
|
560
|
+
// Add operations to the transaction
|
|
561
|
+
const result = await this.tablesDB.createOperations({
|
|
562
|
+
transactionId: transaction.$id,
|
|
563
|
+
operations: operations.map(op => ({ operation: 'execute', fn: op }))
|
|
564
|
+
});
|
|
331
565
|
return { data: result };
|
|
332
566
|
}
|
|
333
567
|
catch (error) {
|
|
334
568
|
throw new AdapterError(`Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'TRANSACTION_FAILED', error instanceof Error ? error : undefined);
|
|
335
569
|
}
|
|
336
570
|
}
|
|
337
|
-
/**
|
|
338
|
-
* Subscribe to real-time updates (if supported)
|
|
339
|
-
*/
|
|
340
|
-
subscribeToTable(params, callback) {
|
|
341
|
-
if (!this.tablesDB.subscribe) {
|
|
342
|
-
throw new AdapterError('Real-time subscriptions are not supported', 'REALTIME_NOT_SUPPORTED');
|
|
343
|
-
}
|
|
344
|
-
try {
|
|
345
|
-
return this.tablesDB.subscribe(`databases.${params.databaseId}.tables.${params.tableId}.rows`, callback);
|
|
346
|
-
}
|
|
347
|
-
catch (error) {
|
|
348
|
-
throw new AdapterError(`Failed to subscribe to table: ${error instanceof Error ? error.message : 'Unknown error'}`, 'SUBSCRIPTION_FAILED', error instanceof Error ? error : undefined);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Get table statistics (if available in TablesDB)
|
|
353
|
-
*/
|
|
354
|
-
async getTableStats(params) {
|
|
355
|
-
try {
|
|
356
|
-
if (!this.tablesDB.getTableStats) {
|
|
357
|
-
// Fallback to basic table info
|
|
358
|
-
return this.getTable(params);
|
|
359
|
-
}
|
|
360
|
-
const result = await this.tablesDB.getTableStats(params);
|
|
361
|
-
return { data: result };
|
|
362
|
-
}
|
|
363
|
-
catch (error) {
|
|
364
|
-
throw new AdapterError(`Failed to get table stats: ${error instanceof Error ? error.message : 'Unknown error'}`, 'GET_TABLE_STATS_FAILED', error instanceof Error ? error : undefined);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
571
|
}
|
|
@@ -21,7 +21,7 @@ export const databaseCommands = {
|
|
|
21
21
|
const localCollections = cli.getLocalCollections();
|
|
22
22
|
// Push operations always use local configuration as source of truth
|
|
23
23
|
// Select databases
|
|
24
|
-
const selectedDatabaseIds = await SelectionDialogs.selectDatabases(availableDatabases, configuredDatabases, { showSelectAll:
|
|
24
|
+
const selectedDatabaseIds = await SelectionDialogs.selectDatabases(availableDatabases, configuredDatabases, { showSelectAll: false, allowNewOnly: false, defaultSelected: [] });
|
|
25
25
|
if (selectedDatabaseIds.length === 0) {
|
|
26
26
|
MessageFormatter.warning("No databases selected. Skipping database sync.", { prefix: "Database" });
|
|
27
27
|
return;
|
|
@@ -33,7 +33,8 @@ export const databaseCommands = {
|
|
|
33
33
|
const database = availableDatabases.find(db => db.$id === databaseId);
|
|
34
34
|
// Use the existing selectCollectionsAndTables method
|
|
35
35
|
const selectedCollections = await cli.selectCollectionsAndTables(database, cli.controller.database, chalk.blue(`Select collections/tables to push to "${database.name}":`), true, // multiSelect
|
|
36
|
-
true // prefer local
|
|
36
|
+
true, // prefer local
|
|
37
|
+
true // shouldFilterByDatabase
|
|
37
38
|
);
|
|
38
39
|
// Map selected collections to table IDs
|
|
39
40
|
const selectedTableIds = selectedCollections.map((c) => c.$id || c.id);
|
|
@@ -66,7 +67,7 @@ export const databaseCommands = {
|
|
|
66
67
|
}
|
|
67
68
|
else {
|
|
68
69
|
// Select buckets using SelectionDialogs
|
|
69
|
-
const selectedBucketIds = await SelectionDialogs.selectBucketsForDatabases(selectedDatabaseIds, availableBuckets, configuredBuckets, { showSelectAll:
|
|
70
|
+
const selectedBucketIds = await SelectionDialogs.selectBucketsForDatabases(selectedDatabaseIds, availableBuckets, configuredBuckets, { showSelectAll: false, groupByDatabase: true, defaultSelected: [] });
|
|
70
71
|
if (selectedBucketIds.length > 0) {
|
|
71
72
|
// Create BucketSelection objects
|
|
72
73
|
bucketSelections = SelectionDialogs.createBucketSelection(selectedBucketIds, availableBuckets, configuredBuckets, availableDatabases);
|
|
@@ -8,6 +8,7 @@ import { Query } from "node-appwrite";
|
|
|
8
8
|
import { MessageFormatter } from "../../shared/messageFormatter.js";
|
|
9
9
|
import { createFunctionTemplate, deleteFunction, downloadLatestFunctionDeployment, listFunctions, listSpecifications, } from "../../functions/methods.js";
|
|
10
10
|
import { deployLocalFunction } from "../../functions/deployments.js";
|
|
11
|
+
import { discoverFnConfigs, mergeDiscoveredFunctions } from "../../functions/fnConfigDiscovery.js";
|
|
11
12
|
import { addFunctionToYamlConfig, findYamlConfig } from "../../config/yamlConfig.js";
|
|
12
13
|
import { RuntimeSchema } from "appwrite-utils";
|
|
13
14
|
export const functionCommands = {
|
|
@@ -110,6 +111,13 @@ export const functionCommands = {
|
|
|
110
111
|
MessageFormatter.error("Failed to initialize controller or load config", undefined, { prefix: "Functions" });
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
114
|
+
// Discover local .fnconfig.yaml functions and merge into controller config
|
|
115
|
+
try {
|
|
116
|
+
const discovered = discoverFnConfigs(cli.currentDir);
|
|
117
|
+
const merged = mergeDiscoveredFunctions(cli.controller.config.functions || [], discovered);
|
|
118
|
+
cli.controller.config.functions = merged;
|
|
119
|
+
}
|
|
120
|
+
catch { }
|
|
113
121
|
const functions = await cli.selectFunctions("Select function(s) to deploy:", true, true);
|
|
114
122
|
if (!functions?.length) {
|
|
115
123
|
MessageFormatter.error("No function selected", undefined, { prefix: "Functions" });
|
|
@@ -140,17 +148,18 @@ export const functionCommands = {
|
|
|
140
148
|
}
|
|
141
149
|
MessageFormatter.info(` Appwrite folder: ${cli.controller.getAppwriteFolderPath()}`, { prefix: "Functions" });
|
|
142
150
|
MessageFormatter.info(` Current working dir: ${process.cwd()}`, { prefix: "Functions" });
|
|
143
|
-
//
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
return path;
|
|
149
|
-
};
|
|
151
|
+
// Resolve config dirPath relative to central YAML if it's relative
|
|
152
|
+
const yamlConfigPath = findYamlConfig(cli.currentDir);
|
|
153
|
+
const yamlBaseDir = yamlConfigPath ? require('node:path').dirname(yamlConfigPath) : process.cwd();
|
|
154
|
+
const expandTildePath = (p) => (p?.startsWith('~/') ? p.replace('~', os.homedir()) : p);
|
|
150
155
|
// Check locations in priority order:
|
|
151
156
|
const priorityLocations = [
|
|
152
157
|
// 1. Config dirPath if specified (with tilde expansion)
|
|
153
|
-
functionConfig.dirPath
|
|
158
|
+
functionConfig.dirPath
|
|
159
|
+
? (require('node:path').isAbsolute(expandTildePath(functionConfig.dirPath))
|
|
160
|
+
? expandTildePath(functionConfig.dirPath)
|
|
161
|
+
: require('node:path').resolve(yamlBaseDir, expandTildePath(functionConfig.dirPath)))
|
|
162
|
+
: undefined,
|
|
154
163
|
// 2. Appwrite config folder/functions/name
|
|
155
164
|
join(cli.controller.getAppwriteFolderPath(), "functions", functionNameLower),
|
|
156
165
|
// 3. Current working directory/functions/name
|