appwrite-utils-cli 0.0.62 → 0.0.64

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.
@@ -623,6 +623,16 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
623
623
  relatedCollection: string;
624
624
  relationType: "oneToMany" | "manyToOne" | "oneToOne" | "manyToMany";
625
625
  twoWay: boolean;
626
+ /**
627
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
628
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
629
+ * and update the field with the file's ID if necessary.
630
+ *
631
+ * @param attributeMappings - The attribute mappings from the import definition.
632
+ * @param context - The context object containing information about the database, collection, and document.
633
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
634
+ * @returns The attribute mappings updated with any necessary post-import actions.
635
+ */
626
636
  twoWayKey: string;
627
637
  onDelete: "setNull" | "cascade" | "restrict";
628
638
  side: "parent" | "child";
@@ -1235,6 +1245,16 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
1235
1245
  relatedCollection: string;
1236
1246
  relationType: "oneToMany" | "manyToOne" | "oneToOne" | "manyToMany";
1237
1247
  twoWay: boolean;
1248
+ /**
1249
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
1250
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
1251
+ * and update the field with the file's ID if necessary.
1252
+ *
1253
+ * @param attributeMappings - The attribute mappings from the import definition.
1254
+ * @param context - The context object containing information about the database, collection, and document.
1255
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
1256
+ * @returns The attribute mappings updated with any necessary post-import actions.
1257
+ */
1238
1258
  twoWayKey: string;
1239
1259
  onDelete: "setNull" | "cascade" | "restrict";
1240
1260
  side: "parent" | "child";
@@ -1635,6 +1655,16 @@ export declare class DataLoader {
1635
1655
  relatedCollection: string;
1636
1656
  relationType: "oneToMany" | "manyToOne" | "oneToOne" | "manyToMany";
1637
1657
  twoWay: boolean;
1658
+ /**
1659
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
1660
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
1661
+ * and update the field with the file's ID if necessary.
1662
+ *
1663
+ * @param attributeMappings - The attribute mappings from the import definition.
1664
+ * @param context - The context object containing information about the database, collection, and document.
1665
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
1666
+ * @returns The attribute mappings updated with any necessary post-import actions.
1667
+ */
1638
1668
  twoWayKey: string;
1639
1669
  onDelete: "setNull" | "cascade" | "restrict";
1640
1670
  side: "parent" | "child";
@@ -278,6 +278,11 @@ export class DataLoader {
278
278
  userId: user.$id,
279
279
  docId: user.$id,
280
280
  },
281
+ context: {
282
+ ...user,
283
+ userId: user.$id,
284
+ docId: user.$id,
285
+ },
281
286
  rawData: user,
282
287
  });
283
288
  this.importMap.set(this.getCollectionKey("users"), importData);
@@ -291,7 +296,8 @@ export class DataLoader {
291
296
  console.log("---------------------------------");
292
297
  await this.setupMaps(dbId);
293
298
  const allUsers = await this.getAllUsers();
294
- console.log(`Fetched ${allUsers.length} users`);
299
+ console.log(`Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`);
300
+ await new Promise((resolve) => setTimeout(resolve, 5000));
295
301
  // Iterate over the configured databases to find the matching one
296
302
  for (const db of this.config.databases) {
297
303
  if (db.$id !== dbId) {
@@ -448,13 +454,6 @@ export class DataLoader {
448
454
  targetValue !== undefined &&
449
455
  targetValue !== null);
450
456
  });
451
- // Log and skip if no matching data found
452
- if (!foundData.length) {
453
- console.log(`No data found for collection ${collectionConfig.name}:\nTarget collection: ${targetCollectionKey}\nValue to match: ${valueToMatch}\nField to set: ${fieldToSetKey}\nTarget field to match: ${targetFieldKey}\nTarget field value: ${idMapping.targetField}`);
454
- logger.info(`No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(idMapping, null, 2)}`);
455
- continue;
456
- }
457
- needsUpdate = true;
458
457
  const getCurrentDataFiltered = (currentData) => {
459
458
  if (Array.isArray(currentData.finalData[fieldToSetKey])) {
460
459
  return currentData.finalData[fieldToSetKey].filter((data) => `${data}` !== `${valueToMatch}`);
@@ -463,6 +462,13 @@ export class DataLoader {
463
462
  };
464
463
  // Get the current data to be updated
465
464
  const currentDataFiltered = getCurrentDataFiltered(collectionData.data[i]);
465
+ // Log and skip if no matching data found
466
+ if (!foundData.length) {
467
+ console.log(`No data found for collection ${collectionConfig.name}:\nTarget collection: ${targetCollectionKey}\nValue to match: ${valueToMatch}\nField to set: ${fieldToSetKey}\nTarget field to match: ${targetFieldKey}\nTarget field value: ${idMapping.targetField}`);
468
+ logger.info(`No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(idMapping, null, 2)}`);
469
+ continue;
470
+ }
471
+ needsUpdate = true;
466
472
  // Extract the new data to set
467
473
  const newData = foundData.map((data) => {
468
474
  const valueFound = this.getValueFromData(data.finalData, data.context, idMapping.targetField);
@@ -575,6 +581,11 @@ export class DataLoader {
575
581
  * @returns The transformed item with user-specific keys removed.
576
582
  */
577
583
  prepareUserData(item, attributeMappings, primaryKeyField, newId) {
584
+ if (this.userExistsMap.has(newId) ||
585
+ Array.from(this.emailToUserIdMap.values()).includes(newId) ||
586
+ Array.from(this.phoneToUserIdMap.values()).includes(newId)) {
587
+ newId = this.getTrueUniqueId(this.getCollectionKey("users"));
588
+ }
578
589
  let transformedItem = this.transformData(item, attributeMappings);
579
590
  const userData = AuthUserCreateSchema.safeParse(transformedItem);
580
591
  if (!userData.success || !(userData.data.email && userData.data.phone)) {
@@ -728,9 +739,18 @@ export class DataLoader {
728
739
  let { transformedItem, existingId, userData } = this.prepareUserData(item, importDef.attributeMappings, importDef.primaryKeyField, this.getTrueUniqueId(this.getCollectionKey("users")));
729
740
  logger.info(`In create user -- transformedItem: ${JSON.stringify(transformedItem, null, 2)}`);
730
741
  // Generate a new unique ID for the item or use existing ID
731
- if (!existingId) {
742
+ if (!existingId && !userData.finalData?.userId) {
732
743
  // No existing user ID, generate a new unique ID
733
- existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
744
+ existingId = this.getTrueUniqueId(this.getCollectionKey(collection.name));
745
+ transformedItem = {
746
+ ...transformedItem,
747
+ userId: existingId,
748
+ docId: existingId,
749
+ };
750
+ }
751
+ else if (!existingId && userData.finalData?.userId) {
752
+ // Existing user ID, use it as the new ID
753
+ existingId = userData.finalData.userId;
734
754
  transformedItem = {
735
755
  ...transformedItem,
736
756
  userId: existingId,
@@ -751,7 +771,9 @@ export class DataLoader {
751
771
  // Found a duplicate oldId, now decide how to merge or handle these duplicates
752
772
  for (const data of currentData.data) {
753
773
  if (data.finalData.docId === oldId ||
754
- data.finalData.userId === oldId) {
774
+ data.finalData.userId === oldId ||
775
+ data.context.docId === oldId ||
776
+ data.context.userId === oldId) {
755
777
  transformedItem = this.mergeObjects(data.finalData, transformedItem);
756
778
  }
757
779
  }
@@ -787,30 +809,41 @@ export class DataLoader {
787
809
  ...importDef,
788
810
  attributeMappings: mappingsWithActions,
789
811
  };
812
+ const updatedData = this.importMap.get(this.getCollectionKey(collection.name));
790
813
  let foundData = false;
791
- for (let i = 0; i < currentData.data.length; i++) {
792
- if (currentData.data[i].finalData.docId === existingId ||
793
- currentData.data[i].finalData.userId === existingId) {
794
- currentData.data[i].finalData = this.mergeObjects(currentData.data[i].finalData, transformedItem);
795
- currentData.data[i].context = {
796
- ...currentData.data[i].context,
797
- ...context,
814
+ for (let i = 0; i < updatedData.data.length; i++) {
815
+ if (updatedData.data[i].finalData.docId === existingId ||
816
+ updatedData.data[i].finalData.userId === existingId ||
817
+ updatedData.data[i].context.docId === existingId ||
818
+ updatedData.data[i].context.userId === existingId) {
819
+ updatedData.data[i].finalData = this.mergeObjects(updatedData.data[i].finalData, transformedItem);
820
+ updatedData.data[i].context = this.mergeObjects(updatedData.data[i].context, context);
821
+ const mergedImportDef = {
822
+ ...updatedData.data[i].importDef,
823
+ idMappings: [
824
+ ...(updatedData.data[i].importDef?.idMappings || []),
825
+ ...(newImportDef.idMappings || []),
826
+ ],
827
+ attributeMappings: [
828
+ ...(updatedData.data[i].importDef?.attributeMappings || []),
829
+ ...(newImportDef.attributeMappings || []),
830
+ ],
798
831
  };
799
- currentData.data[i].importDef = newImportDef;
800
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
832
+ updatedData.data[i].importDef = mergedImportDef;
833
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
801
834
  this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
802
835
  foundData = true;
803
836
  }
804
837
  }
805
838
  if (!foundData) {
806
839
  // Add new data to the associated collection
807
- currentData.data.push({
840
+ updatedData.data.push({
808
841
  rawData: item,
809
842
  context: context,
810
843
  importDef: newImportDef,
811
844
  finalData: transformedItem,
812
845
  });
813
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
846
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
814
847
  this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
815
848
  }
816
849
  }
@@ -854,13 +887,7 @@ export class DataLoader {
854
887
  // Create a context object for the item, including the new ID
855
888
  let context = this.createContext(db, collection, item, itemIdNew);
856
889
  // Transform the item data based on the attribute mappings
857
- const transformedData = this.transformData(item, importDef.attributeMappings);
858
- if (collection.name.toLowerCase() === "councils") {
859
- console.log("Transformed Council: ", transformedData);
860
- }
861
- if (isRegions) {
862
- logger.info(`Transformed region: ${JSON.stringify(transformedData, null, 2)}`);
863
- }
890
+ let transformedData = this.transformData(item, importDef.attributeMappings);
864
891
  // If a primary key field is defined, handle the ID mapping and check for duplicates
865
892
  if (importDef.primaryKeyField) {
866
893
  const oldId = item[importDef.primaryKeyField];
@@ -157,6 +157,7 @@ export class ImportController {
157
157
  status: "in_progress",
158
158
  });
159
159
  const collectionData = dataLoader.importMap.get(dataLoader.getCollectionKey(collection.name));
160
+ console.log(`Processing collection: ${collection.name}...`);
160
161
  if (!collectionData) {
161
162
  console.log("No collection data for ", collection.name);
162
163
  continue;
@@ -253,7 +253,7 @@ export const transferStorageLocalToLocal = async (storage, fromBucketId, toBucke
253
253
  let numberOfFiles = 0;
254
254
  if (fromFiles.files.length < 100) {
255
255
  for (const file of allFromFiles) {
256
- const fileData = await storage.getFileDownload(file.bucketId, file.$id);
256
+ const fileData = await tryAwaitWithRetry(async () => await storage.getFileDownload(file.bucketId, file.$id));
257
257
  const fileToCreate = InputFile.fromBuffer(Buffer.from(fileData), file.name);
258
258
  console.log(`Creating file: ${file.name}`);
259
259
  tryAwaitWithRetry(async () => await storage.createFile(toBucketId, file.$id, fileToCreate, file.$permissions));
@@ -263,10 +263,10 @@ export const transferStorageLocalToLocal = async (storage, fromBucketId, toBucke
263
263
  else {
264
264
  lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
265
265
  while (lastFileId) {
266
- const files = await storage.listFiles(fromBucketId, [
266
+ const files = await tryAwaitWithRetry(async () => await storage.listFiles(fromBucketId, [
267
267
  Query.limit(100),
268
268
  Query.cursorAfter(lastFileId),
269
- ]);
269
+ ]));
270
270
  allFromFiles.push(...files.files);
271
271
  if (files.files.length < 100) {
272
272
  lastFileId = undefined;
@@ -276,7 +276,7 @@ export const transferStorageLocalToLocal = async (storage, fromBucketId, toBucke
276
276
  }
277
277
  }
278
278
  for (const file of allFromFiles) {
279
- const fileData = await storage.getFileDownload(file.bucketId, file.$id);
279
+ const fileData = await tryAwaitWithRetry(async () => await storage.getFileDownload(file.bucketId, file.$id));
280
280
  const fileToCreate = InputFile.fromBuffer(Buffer.from(fileData), file.name);
281
281
  await tryAwaitWithRetry(async () => await storage.createFile(toBucketId, file.$id, fileToCreate, file.$permissions));
282
282
  numberOfFiles++;
@@ -295,10 +295,10 @@ export const transferStorageLocalToRemote = async (localStorage, endpoint, proje
295
295
  if (fromFiles.files.length === 100) {
296
296
  lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
297
297
  while (lastFileId) {
298
- const files = await localStorage.listFiles(fromBucketId, [
298
+ const files = await tryAwaitWithRetry(async () => await localStorage.listFiles(fromBucketId, [
299
299
  Query.limit(100),
300
300
  Query.cursorAfter(lastFileId),
301
- ]);
301
+ ]));
302
302
  allFromFiles.push(...files.files);
303
303
  if (files.files.length < 100) {
304
304
  break;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
3
  "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
- "version": "0.0.62",
4
+ "version": "0.0.64",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -348,6 +348,11 @@ export class DataLoader {
348
348
  userId: user.$id,
349
349
  docId: user.$id,
350
350
  },
351
+ context: {
352
+ ...user,
353
+ userId: user.$id,
354
+ docId: user.$id,
355
+ },
351
356
  rawData: user,
352
357
  });
353
358
  this.importMap.set(this.getCollectionKey("users"), importData);
@@ -362,7 +367,10 @@ export class DataLoader {
362
367
  console.log("---------------------------------");
363
368
  await this.setupMaps(dbId);
364
369
  const allUsers = await this.getAllUsers();
365
- console.log(`Fetched ${allUsers.length} users`);
370
+ console.log(
371
+ `Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`
372
+ );
373
+ await new Promise((resolve) => setTimeout(resolve, 5000));
366
374
  // Iterate over the configured databases to find the matching one
367
375
  for (const db of this.config.databases) {
368
376
  if (db.$id !== dbId) {
@@ -568,6 +576,20 @@ export class DataLoader {
568
576
  );
569
577
  }
570
578
  );
579
+
580
+ const getCurrentDataFiltered = (currentData: any) => {
581
+ if (Array.isArray(currentData.finalData[fieldToSetKey])) {
582
+ return currentData.finalData[fieldToSetKey].filter(
583
+ (data: any) => `${data}` !== `${valueToMatch}`
584
+ );
585
+ }
586
+ return currentData.finalData[fieldToSetKey];
587
+ };
588
+
589
+ // Get the current data to be updated
590
+ const currentDataFiltered = getCurrentDataFiltered(
591
+ collectionData.data[i]
592
+ );
571
593
  // Log and skip if no matching data found
572
594
  if (!foundData.length) {
573
595
  console.log(
@@ -585,20 +607,6 @@ export class DataLoader {
585
607
 
586
608
  needsUpdate = true;
587
609
 
588
- const getCurrentDataFiltered = (currentData: any) => {
589
- if (Array.isArray(currentData.finalData[fieldToSetKey])) {
590
- return currentData.finalData[fieldToSetKey].filter(
591
- (data: any) => `${data}` !== `${valueToMatch}`
592
- );
593
- }
594
- return currentData.finalData[fieldToSetKey];
595
- };
596
-
597
- // Get the current data to be updated
598
- const currentDataFiltered = getCurrentDataFiltered(
599
- collectionData.data[i]
600
- );
601
-
602
610
  // Extract the new data to set
603
611
  const newData = foundData.map((data) => {
604
612
  const valueFound = this.getValueFromData(
@@ -752,6 +760,13 @@ export class DataLoader {
752
760
  finalData: z.infer<typeof AuthUserCreateSchema>;
753
761
  };
754
762
  } {
763
+ if (
764
+ this.userExistsMap.has(newId) ||
765
+ Array.from(this.emailToUserIdMap.values()).includes(newId) ||
766
+ Array.from(this.phoneToUserIdMap.values()).includes(newId)
767
+ ) {
768
+ newId = this.getTrueUniqueId(this.getCollectionKey("users"));
769
+ }
755
770
  let transformedItem = this.transformData(item, attributeMappings);
756
771
  const userData = AuthUserCreateSchema.safeParse(transformedItem);
757
772
  if (!userData.success || !(userData.data.email && userData.data.phone)) {
@@ -940,9 +955,19 @@ export class DataLoader {
940
955
  );
941
956
 
942
957
  // Generate a new unique ID for the item or use existing ID
943
- if (!existingId) {
958
+ if (!existingId && !userData.finalData?.userId) {
944
959
  // No existing user ID, generate a new unique ID
945
- existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
960
+ existingId = this.getTrueUniqueId(
961
+ this.getCollectionKey(collection.name)
962
+ );
963
+ transformedItem = {
964
+ ...transformedItem,
965
+ userId: existingId,
966
+ docId: existingId,
967
+ };
968
+ } else if (!existingId && userData.finalData?.userId) {
969
+ // Existing user ID, use it as the new ID
970
+ existingId = userData.finalData.userId;
946
971
  transformedItem = {
947
972
  ...transformedItem,
948
973
  userId: existingId,
@@ -951,7 +976,7 @@ export class DataLoader {
951
976
  }
952
977
 
953
978
  // Create a context object for the item, including the new ID
954
- let context = this.createContext(db, collection, item, existingId);
979
+ let context = this.createContext(db, collection, item, existingId!);
955
980
 
956
981
  // Merge the transformed data into the context
957
982
  context = { ...context, ...transformedItem, ...userData.finalData };
@@ -970,7 +995,9 @@ export class DataLoader {
970
995
  for (const data of currentData.data) {
971
996
  if (
972
997
  data.finalData.docId === oldId ||
973
- data.finalData.userId === oldId
998
+ data.finalData.userId === oldId ||
999
+ data.context.docId === oldId ||
1000
+ data.context.userId === oldId
974
1001
  ) {
975
1002
  transformedItem = this.mergeObjects(
976
1003
  data.finalData,
@@ -1022,41 +1049,59 @@ export class DataLoader {
1022
1049
  attributeMappings: mappingsWithActions,
1023
1050
  };
1024
1051
 
1052
+ const updatedData = this.importMap.get(
1053
+ this.getCollectionKey(collection.name)
1054
+ )!;
1055
+
1025
1056
  let foundData = false;
1026
- for (let i = 0; i < currentData.data.length; i++) {
1057
+ for (let i = 0; i < updatedData.data.length; i++) {
1027
1058
  if (
1028
- currentData.data[i].finalData.docId === existingId ||
1029
- currentData.data[i].finalData.userId === existingId
1059
+ updatedData.data[i].finalData.docId === existingId ||
1060
+ updatedData.data[i].finalData.userId === existingId ||
1061
+ updatedData.data[i].context.docId === existingId ||
1062
+ updatedData.data[i].context.userId === existingId
1030
1063
  ) {
1031
- currentData.data[i].finalData = this.mergeObjects(
1032
- currentData.data[i].finalData,
1064
+ updatedData.data[i].finalData = this.mergeObjects(
1065
+ updatedData.data[i].finalData,
1033
1066
  transformedItem
1034
1067
  );
1035
- currentData.data[i].context = {
1036
- ...currentData.data[i].context,
1037
- ...context,
1068
+ updatedData.data[i].context = this.mergeObjects(
1069
+ updatedData.data[i].context,
1070
+ context
1071
+ );
1072
+ const mergedImportDef = {
1073
+ ...updatedData.data[i].importDef,
1074
+ idMappings: [
1075
+ ...(updatedData.data[i].importDef?.idMappings || []),
1076
+ ...(newImportDef.idMappings || []),
1077
+ ],
1078
+ attributeMappings: [
1079
+ ...(updatedData.data[i].importDef?.attributeMappings || []),
1080
+ ...(newImportDef.attributeMappings || []),
1081
+ ],
1038
1082
  };
1039
- currentData.data[i].importDef = newImportDef;
1083
+ updatedData.data[i].importDef = mergedImportDef as ImportDef;
1040
1084
  this.importMap.set(
1041
1085
  this.getCollectionKey(collection.name),
1042
- currentData
1086
+ updatedData
1043
1087
  );
1044
1088
  this.oldIdToNewIdPerCollectionMap.set(
1045
1089
  this.getCollectionKey(collection.name),
1046
1090
  collectionOldIdToNewIdMap!
1047
1091
  );
1092
+
1048
1093
  foundData = true;
1049
1094
  }
1050
1095
  }
1051
1096
  if (!foundData) {
1052
1097
  // Add new data to the associated collection
1053
- currentData.data.push({
1098
+ updatedData.data.push({
1054
1099
  rawData: item,
1055
1100
  context: context,
1056
1101
  importDef: newImportDef,
1057
1102
  finalData: transformedItem,
1058
1103
  });
1059
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
1104
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
1060
1105
  this.oldIdToNewIdPerCollectionMap.set(
1061
1106
  this.getCollectionKey(collection.name),
1062
1107
  collectionOldIdToNewIdMap!
@@ -1119,18 +1164,10 @@ export class DataLoader {
1119
1164
  // Create a context object for the item, including the new ID
1120
1165
  let context = this.createContext(db, collection, item, itemIdNew);
1121
1166
  // Transform the item data based on the attribute mappings
1122
- const transformedData = this.transformData(
1167
+ let transformedData = this.transformData(
1123
1168
  item,
1124
1169
  importDef.attributeMappings
1125
1170
  );
1126
- if (collection.name.toLowerCase() === "councils") {
1127
- console.log("Transformed Council: ", transformedData);
1128
- }
1129
- if (isRegions) {
1130
- logger.info(
1131
- `Transformed region: ${JSON.stringify(transformedData, null, 2)}`
1132
- );
1133
- }
1134
1171
  // If a primary key field is defined, handle the ID mapping and check for duplicates
1135
1172
  if (importDef.primaryKeyField) {
1136
1173
  const oldId = item[importDef.primaryKeyField];
@@ -237,6 +237,7 @@ export class ImportController {
237
237
  const collectionData = dataLoader.importMap.get(
238
238
  dataLoader.getCollectionKey(collection.name)
239
239
  );
240
+ console.log(`Processing collection: ${collection.name}...`);
240
241
  if (!collectionData) {
241
242
  console.log("No collection data for ", collection.name);
242
243
  continue;
@@ -390,7 +390,9 @@ export const transferStorageLocalToLocal = async (
390
390
  let numberOfFiles = 0;
391
391
  if (fromFiles.files.length < 100) {
392
392
  for (const file of allFromFiles) {
393
- const fileData = await storage.getFileDownload(file.bucketId, file.$id);
393
+ const fileData = await tryAwaitWithRetry(
394
+ async () => await storage.getFileDownload(file.bucketId, file.$id)
395
+ );
394
396
  const fileToCreate = InputFile.fromBuffer(
395
397
  Buffer.from(fileData),
396
398
  file.name
@@ -410,10 +412,13 @@ export const transferStorageLocalToLocal = async (
410
412
  } else {
411
413
  lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
412
414
  while (lastFileId) {
413
- const files = await storage.listFiles(fromBucketId, [
414
- Query.limit(100),
415
- Query.cursorAfter(lastFileId),
416
- ]);
415
+ const files = await tryAwaitWithRetry(
416
+ async () =>
417
+ await storage.listFiles(fromBucketId, [
418
+ Query.limit(100),
419
+ Query.cursorAfter(lastFileId!),
420
+ ])
421
+ );
417
422
  allFromFiles.push(...files.files);
418
423
  if (files.files.length < 100) {
419
424
  lastFileId = undefined;
@@ -422,7 +427,9 @@ export const transferStorageLocalToLocal = async (
422
427
  }
423
428
  }
424
429
  for (const file of allFromFiles) {
425
- const fileData = await storage.getFileDownload(file.bucketId, file.$id);
430
+ const fileData = await tryAwaitWithRetry(
431
+ async () => await storage.getFileDownload(file.bucketId, file.$id)
432
+ );
426
433
  const fileToCreate = InputFile.fromBuffer(
427
434
  Buffer.from(fileData),
428
435
  file.name
@@ -467,10 +474,13 @@ export const transferStorageLocalToRemote = async (
467
474
  if (fromFiles.files.length === 100) {
468
475
  lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
469
476
  while (lastFileId) {
470
- const files = await localStorage.listFiles(fromBucketId, [
471
- Query.limit(100),
472
- Query.cursorAfter(lastFileId),
473
- ]);
477
+ const files = await tryAwaitWithRetry(
478
+ async () =>
479
+ await localStorage.listFiles(fromBucketId, [
480
+ Query.limit(100),
481
+ Query.cursorAfter(lastFileId!),
482
+ ])
483
+ );
474
484
  allFromFiles.push(...files.files);
475
485
  if (files.files.length < 100) {
476
486
  break;