appwrite-utils-cli 0.9.982 → 0.9.984

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.
Files changed (45) hide show
  1. package/README.md +1 -0
  2. package/dist/main.js +56 -42
  3. package/dist/migrations/dataLoader.d.ts +2 -42
  4. package/dist/migrations/dataLoader.js +57 -12
  5. package/dist/migrations/importController.js +24 -9
  6. package/dist/migrations/migrationHelper.d.ts +2 -2
  7. package/dist/migrations/transfer.d.ts +2 -2
  8. package/dist/utilsController.js +39 -26
  9. package/package.json +1 -1
  10. package/src/appwrite.zip +0 -0
  11. package/src/main.ts +77 -52
  12. package/src/migrations/dataLoader.ts +74 -23
  13. package/src/migrations/importController.ts +37 -22
  14. package/src/migrations/transfer.ts +2 -2
  15. package/src/utilsController.ts +62 -53
  16. package/zlogs/album.json +0 -4
  17. package/zlogs/announcements.json +0 -397
  18. package/zlogs/announcementscomments.json +0 -36
  19. package/zlogs/articles.json +0 -138
  20. package/zlogs/articlescomments.json +0 -4
  21. package/zlogs/artist.json +0 -4
  22. package/zlogs/businesscategories.json +0 -7097
  23. package/zlogs/contacts.json +0 -517063
  24. package/zlogs/contactscouncils.json +0 -61905
  25. package/zlogs/contactssociallinks.json +0 -13776
  26. package/zlogs/councils.json +0 -5076
  27. package/zlogs/documents.json +0 -917
  28. package/zlogs/emails.json +0 -4
  29. package/zlogs/events.json +0 -132625
  30. package/zlogs/genre.json +0 -4
  31. package/zlogs/knowledgebase.json +0 -333
  32. package/zlogs/knowledgebasecomments.json +0 -4
  33. package/zlogs/linkcategories.json +0 -180
  34. package/zlogs/links.json +0 -4364
  35. package/zlogs/memberrequests.json +0 -83
  36. package/zlogs/memberrequestscomments.json +0 -65
  37. package/zlogs/mergedUserMap.json +0 -1
  38. package/zlogs/oldIdToNewIdPerCollectionMap.json +0 -1
  39. package/zlogs/playlist.json +0 -4
  40. package/zlogs/regions.json +0 -145
  41. package/zlogs/song.json +0 -4
  42. package/zlogs/testimonials.json +0 -335
  43. package/zlogs/useractivity.json +0 -4
  44. package/zlogs/userdata.json +0 -4
  45. package/zlogs/users.json +0 -4
@@ -1086,6 +1086,7 @@ export class DataLoader {
1086
1086
  }
1087
1087
  }
1088
1088
  // Update the attribute mappings with any actions that need to be performed post-import
1089
+ // We added the basePath to get the folder from the filePath
1089
1090
  const mappingsWithActions = this.getAttributeMappingsWithActions(
1090
1091
  importDef.attributeMappings,
1091
1092
  context,
@@ -1247,6 +1248,7 @@ export class DataLoader {
1247
1248
  continue;
1248
1249
  }
1249
1250
  // Update the attribute mappings with any actions that need to be performed post-import
1251
+ // We added the basePath to get the folder from the filePath
1250
1252
  const mappingsWithActions = this.getAttributeMappingsWithActions(
1251
1253
  importDef.attributeMappings,
1252
1254
  context,
@@ -1462,6 +1464,7 @@ export class DataLoader {
1462
1464
  }
1463
1465
 
1464
1466
  // Update the attribute mappings with any actions that need to be performed post-import
1467
+ // We added the basePath to get the folder from the filePath
1465
1468
  const mappingsWithActions = this.getAttributeMappingsWithActions(
1466
1469
  importDef.attributeMappings,
1467
1470
  context,
@@ -1480,26 +1483,42 @@ export class DataLoader {
1480
1483
  transformedData
1481
1484
  );
1482
1485
  itemDataToUpdate.context = context;
1483
- // Merge existing importDef with new importDef, focusing only on postImportActions
1486
+
1487
+ // Fix: Ensure we properly merge the attribute mappings and their actions
1488
+ const mergedAttributeMappings = newImportDef.attributeMappings.map(
1489
+ (newMapping) => {
1490
+ const existingMapping =
1491
+ itemDataToUpdate.importDef?.attributeMappings.find(
1492
+ (m) => m.targetKey === newMapping.targetKey
1493
+ );
1494
+
1495
+ return {
1496
+ ...newMapping,
1497
+ postImportActions: [
1498
+ ...(existingMapping?.postImportActions || []),
1499
+ ...(newMapping.postImportActions || []),
1500
+ ],
1501
+ };
1502
+ }
1503
+ );
1504
+
1484
1505
  itemDataToUpdate.importDef = {
1485
- ...itemDataToUpdate.importDef,
1486
- attributeMappings:
1487
- itemDataToUpdate.importDef?.attributeMappings.map(
1488
- (attrMapping, index) => ({
1489
- ...attrMapping,
1490
- postImportActions: [
1491
- ...(attrMapping.postImportActions || []),
1492
- ...(newImportDef.attributeMappings[index]
1493
- ?.postImportActions || []),
1494
- ],
1495
- })
1496
- ) || [],
1497
- } as ImportDef;
1498
-
1499
- if (collection.name.toLowerCase() === "councils") {
1500
- console.log(
1501
- `Mappings in update councils: ${JSON.stringify(
1502
- itemDataToUpdate.importDef.attributeMappings,
1506
+ ...newImportDef,
1507
+ attributeMappings: mergedAttributeMappings,
1508
+ };
1509
+
1510
+ // Debug logging
1511
+ if (
1512
+ mergedAttributeMappings.some((m) => m.postImportActions?.length > 0)
1513
+ ) {
1514
+ logger.info(
1515
+ `Post-import actions for ${collection.name}: ${JSON.stringify(
1516
+ mergedAttributeMappings
1517
+ .filter((m) => m.postImportActions?.length > 0)
1518
+ .map((m) => ({
1519
+ targetKey: m.targetKey,
1520
+ actions: m.postImportActions,
1521
+ })),
1503
1522
  null,
1504
1523
  2
1505
1524
  )}`
@@ -1603,10 +1622,42 @@ export class DataLoader {
1603
1622
  );
1604
1623
  // Ensure the file path is absolute if it doesn't start with "http"
1605
1624
  if (!mappingFilePath.toLowerCase().startsWith("http")) {
1606
- mappingFilePath = path.resolve(
1607
- this.appwriteFolderPath,
1608
- mappingFilePath
1609
- );
1625
+ // First try the direct path
1626
+ let fullPath = path.resolve(this.appwriteFolderPath, mappingFilePath);
1627
+
1628
+ // If file doesn't exist, search in subdirectories
1629
+ if (!fs.existsSync(fullPath)) {
1630
+ const findFileInDir = (dir: string): string | null => {
1631
+ const files = fs.readdirSync(dir);
1632
+
1633
+ for (const file of files) {
1634
+ const filePath = path.join(dir, file);
1635
+ const stat = fs.statSync(filePath);
1636
+
1637
+ if (stat.isDirectory()) {
1638
+ // Recursively search subdirectories
1639
+ const found = findFileInDir(filePath);
1640
+ if (found) return found;
1641
+ } else if (file === path.basename(mappingFilePath)) {
1642
+ return filePath;
1643
+ }
1644
+ }
1645
+ return null;
1646
+ };
1647
+
1648
+ const foundPath = findFileInDir(this.appwriteFolderPath);
1649
+ if (foundPath) {
1650
+ mappingFilePath = foundPath;
1651
+ } else {
1652
+ logger.warn(
1653
+ `File not found in any subdirectory: ${mappingFilePath}`
1654
+ );
1655
+ // Keep the original resolved path as fallback
1656
+ mappingFilePath = fullPath;
1657
+ }
1658
+ } else {
1659
+ mappingFilePath = fullPath;
1660
+ }
1610
1661
  }
1611
1662
  // Define the after-import action to create a file and update the field
1612
1663
  const afterImportAction = {
@@ -119,27 +119,38 @@ export class ImportController {
119
119
  updatedDb: Models.Database,
120
120
  targetDb: Models.Database
121
121
  ) {
122
- await transferDatabaseLocalToLocal(
123
- this.database,
124
- updatedDb.$id,
125
- targetDb.$id
126
- );
127
-
128
- // Find the corresponding database configs
129
- const updatedDbConfig = this.config.databases.find(
130
- (db) => db.$id === updatedDb.$id
131
- );
132
- const targetDbConfig = this.config.databases.find(
133
- (db) => db.$id === targetDb.$id
134
- );
122
+ if (this.database) {
123
+ await transferDatabaseLocalToLocal(
124
+ this.database,
125
+ updatedDb.$id,
126
+ targetDb.$id
127
+ );
128
+ }
135
129
 
136
- // Transfer database-specific bucket if both databases have a bucket defined
137
- if (updatedDbConfig?.bucket && targetDbConfig?.bucket) {
138
- await transferStorageLocalToLocal(
139
- this.storage,
140
- updatedDbConfig.bucket.$id,
141
- targetDbConfig.bucket.$id
130
+ if (this.storage) {
131
+ // Find the corresponding database configs
132
+ const updatedDbConfig = this.config.databases.find(
133
+ (db) => db.$id === updatedDb.$id
142
134
  );
135
+ const targetDbConfig = this.config.databases.find(
136
+ (db) => db.$id === targetDb.$id
137
+ );
138
+
139
+ const sourceBucketId = updatedDbConfig?.bucket?.$id ||
140
+ (this.config.documentBucketId &&
141
+ `${this.config.documentBucketId}_${updatedDb.$id.toLowerCase().trim().replace(" ", "")}`);
142
+
143
+ const targetBucketId = targetDbConfig?.bucket?.$id ||
144
+ (this.config.documentBucketId &&
145
+ `${this.config.documentBucketId}_${targetDb.$id.toLowerCase().trim().replace(" ", "")}`);
146
+
147
+ if (sourceBucketId && targetBucketId) {
148
+ await transferStorageLocalToLocal(
149
+ this.storage,
150
+ sourceBucketId,
151
+ targetBucketId
152
+ );
153
+ }
143
154
  }
144
155
  }
145
156
 
@@ -298,11 +309,13 @@ export class ImportController {
298
309
  }
299
310
 
300
311
  async executePostImportActions(dbId: string, dataLoader: DataLoader, specificCollections?: string[]) {
301
- const collectionsToProcess = specificCollections || Array.from(dataLoader.importMap.keys());
302
-
312
+ console.log("Executing post-import actions...");
313
+ const collectionsToProcess = specificCollections && specificCollections.length > 0 ? specificCollections : (this.config.collections ? this.config.collections.map(c => c.name) : Array.from(dataLoader.importMap.keys()));
314
+ console.log("Collections to process:", collectionsToProcess);
303
315
  // Iterate over each collection in the importMap
304
316
  for (const [collectionKey, collectionData] of dataLoader.importMap.entries()) {
305
- if (collectionsToProcess.includes(collectionKey)) {
317
+ const allCollectionKeys = collectionsToProcess.map(c => dataLoader.getCollectionKey(c));
318
+ if (allCollectionKeys.includes(collectionKey)) {
306
319
  console.log(
307
320
  `Processing post-import actions for collection: ${collectionKey}`
308
321
  );
@@ -330,6 +343,8 @@ export class ImportController {
330
343
  }
331
344
  }
332
345
  }
346
+ } else {
347
+ console.log(`Skipping collection: ${collectionKey} because it's not valid for post-import actions`);
333
348
  }
334
349
  }
335
350
  }
@@ -13,8 +13,8 @@ import { createOrUpdateAttribute } from "../collections/attributes.js";
13
13
  import { parseAttribute } from "appwrite-utils";
14
14
 
15
15
  export interface TransferOptions {
16
- fromDb: Models.Database;
17
- targetDb: Models.Database;
16
+ fromDb: Models.Database | undefined;
17
+ targetDb: Models.Database | undefined;
18
18
  isRemote: boolean;
19
19
  collections?: string[];
20
20
  transferEndpoint?: string;
@@ -145,6 +145,7 @@ export class UtilsController {
145
145
  async getDatabasesByIds(ids: string[]) {
146
146
  await this.init();
147
147
  if (!this.database) throw new Error("Database not initialized");
148
+ if (ids.length === 0) return [];
148
149
  const dbs = await this.database.list([
149
150
  Query.limit(500),
150
151
  Query.equal("$id", ids),
@@ -289,16 +290,11 @@ export class UtilsController {
289
290
  }
290
291
 
291
292
  async transferData(options: TransferOptions): Promise<void> {
292
- if (!this.database) {
293
- throw new Error(
294
- "Database is not initialized, is the config file correct & created?"
295
- );
296
- }
297
-
293
+ // Remove database requirement check
298
294
  let sourceClient = this.database;
299
- let targetClient: Databases;
300
- let sourceDatabases: Models.Database[];
301
- let targetDatabases: Models.Database[];
295
+ let targetClient: Databases | undefined;
296
+ let sourceDatabases: Models.Database[] = [];
297
+ let targetDatabases: Models.Database[] = [];
302
298
 
303
299
  if (options.isRemote) {
304
300
  if (
@@ -314,63 +310,76 @@ export class UtilsController {
314
310
  options.transferProject,
315
311
  options.transferKey
316
312
  );
317
- targetClient = new Databases(remoteClient);
318
-
319
- sourceDatabases = await fetchAllDatabases(sourceClient);
320
- targetDatabases = await fetchAllDatabases(targetClient);
321
- } else {
313
+
314
+ if (this.database) {
315
+ targetClient = new Databases(remoteClient);
316
+ sourceDatabases = await fetchAllDatabases(sourceClient!);
317
+ targetDatabases = await fetchAllDatabases(targetClient);
318
+ }
319
+ } else if (this.database) {
322
320
  targetClient = sourceClient;
323
- sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient);
324
- }
325
-
326
- // Validate that the provided databases exist in the fetched lists
327
- const fromDb = sourceDatabases.find((db) => db.$id === options.fromDb.$id);
328
- const targetDb = targetDatabases.find(
329
- (db) => db.$id === options.targetDb.$id
330
- );
331
-
332
- if (!fromDb || !targetDb) {
333
- throw new Error("Source or target database not found");
321
+ sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient!);
334
322
  }
335
323
 
336
- if (options.isRemote) {
337
- // Remote transfer
338
- await transferDatabaseLocalToRemote(
339
- sourceClient,
340
- options.transferEndpoint!,
341
- options.transferProject!,
342
- options.transferKey!,
343
- fromDb.$id,
344
- targetDb.$id
324
+ // Only validate databases if they're provided in options
325
+ if (options.fromDb && options.targetDb) {
326
+ const fromDb = sourceDatabases.find((db) => db.$id === options.fromDb!.$id);
327
+ const targetDb = targetDatabases.find(
328
+ (db) => db.$id === options.targetDb!.$id
345
329
  );
346
330
 
347
- if (this.storage && options.sourceBucket && options.targetBucket) {
348
- await transferStorageLocalToRemote(
349
- this.storage,
331
+ if (!fromDb || !targetDb) {
332
+ throw new Error("Source or target database not found");
333
+ }
334
+
335
+ if (options.isRemote && targetClient) {
336
+ await transferDatabaseLocalToRemote(
337
+ sourceClient!,
350
338
  options.transferEndpoint!,
351
339
  options.transferProject!,
352
340
  options.transferKey!,
353
- options.sourceBucket.$id,
354
- options.targetBucket.$id
341
+ fromDb.$id,
342
+ targetDb.$id
343
+ );
344
+ } else if (targetClient) {
345
+ await transferDatabaseLocalToLocal(
346
+ sourceClient!,
347
+ fromDb.$id,
348
+ targetDb.$id
355
349
  );
356
350
  }
357
- } else {
358
- // Local transfer
359
- await transferDatabaseLocalToLocal(
360
- sourceClient,
361
- fromDb.$id,
362
- targetDb.$id
363
- );
351
+ }
364
352
 
365
- if (this.storage && options.sourceBucket && options.targetBucket) {
366
- await transferStorageLocalToLocal(
367
- this.storage,
368
- options.sourceBucket.$id,
369
- options.targetBucket.$id
370
- );
353
+ // Handle storage transfer separately
354
+ if (this.storage && (options.sourceBucket || options.fromDb)) {
355
+ const sourceBucketId = options.sourceBucket?.$id ||
356
+ (options.fromDb && this.config?.documentBucketId &&
357
+ `${this.config.documentBucketId}_${options.fromDb.$id.toLowerCase().trim().replace(" ", "")}`);
358
+
359
+ const targetBucketId = options.targetBucket?.$id ||
360
+ (options.targetDb && this.config?.documentBucketId &&
361
+ `${this.config.documentBucketId}_${options.targetDb.$id.toLowerCase().trim().replace(" ", "")}`);
362
+
363
+ if (sourceBucketId && targetBucketId) {
364
+ if (options.isRemote) {
365
+ await transferStorageLocalToRemote(
366
+ this.storage,
367
+ options.transferEndpoint!,
368
+ options.transferProject!,
369
+ options.transferKey!,
370
+ sourceBucketId,
371
+ targetBucketId
372
+ );
373
+ } else {
374
+ await transferStorageLocalToLocal(
375
+ this.storage,
376
+ sourceBucketId,
377
+ targetBucketId
378
+ );
379
+ }
371
380
  }
372
381
  }
373
382
 
374
- console.log("Data transfer completed");
383
+ console.log("Transfer completed");
375
384
  }
376
385
  }
package/zlogs/album.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "collection": "album",
3
- "data": []
4
- }