appwrite-utils-cli 0.0.61 → 0.0.63

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.
@@ -26,7 +26,7 @@ export const afterImportActions = {
26
26
  updateCreatedDocument: async (config, dbId, collId, docId, data) => {
27
27
  try {
28
28
  const db = getDatabaseFromConfig(config);
29
- await db.updateDocument(dbId, collId, docId, data);
29
+ await tryAwaitWithRetry(async () => await db.updateDocument(dbId, collId, docId, data));
30
30
  }
31
31
  catch (error) {
32
32
  console.error("Error updating document: ", error);
@@ -35,11 +35,11 @@ export const afterImportActions = {
35
35
  checkAndUpdateFieldInDocument: async (config, dbId, collId, docId, fieldName, oldFieldValue, newFieldValue) => {
36
36
  try {
37
37
  const db = getDatabaseFromConfig(config);
38
- const doc = await db.getDocument(dbId, collId, docId);
38
+ const doc = await tryAwaitWithRetry(async () => await db.getDocument(dbId, collId, docId));
39
39
  if (doc[fieldName] == oldFieldValue) {
40
- await db.updateDocument(dbId, collId, docId, {
40
+ await tryAwaitWithRetry(async () => await db.updateDocument(dbId, collId, docId, {
41
41
  [fieldName]: newFieldValue,
42
- });
42
+ }));
43
43
  }
44
44
  }
45
45
  catch (error) {
@@ -50,10 +50,10 @@ export const afterImportActions = {
50
50
  const db = getDatabaseFromConfig(config);
51
51
  // Helper function to find a collection ID by name or return the ID if given
52
52
  const findCollectionId = async (collectionIdentifier) => {
53
- const collectionsPulled = await db.listCollections(dbId, [
53
+ const collectionsPulled = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [
54
54
  Query.limit(25),
55
55
  Query.equal("name", collectionIdentifier),
56
- ]);
56
+ ]));
57
57
  if (collectionsPulled.total > 0) {
58
58
  return collectionsPulled.collections[0].$id;
59
59
  }
@@ -71,9 +71,9 @@ export const afterImportActions = {
71
71
  const valueToSet = otherDoc[otherFieldName];
72
72
  if (valueToSet) {
73
73
  // Update the target document
74
- await db.updateDocument(dbId, targetCollectionId, docId, {
74
+ await tryAwaitWithRetry(async () => await db.updateDocument(dbId, targetCollectionId, docId, {
75
75
  [fieldName]: valueToSet,
76
- });
76
+ }));
77
77
  }
78
78
  console.log(`Field ${fieldName} updated successfully in document ${docId}.`);
79
79
  }
@@ -89,17 +89,17 @@ export const afterImportActions = {
89
89
  const db = getDatabaseFromConfig(config);
90
90
  // Helper function to find a collection ID by name or return the ID if given
91
91
  const findCollectionId = async (collectionIdentifier) => {
92
- const collections = await db.listCollections(dbId, [
92
+ const collections = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [
93
93
  Query.equal("name", collectionIdentifier),
94
94
  Query.limit(1),
95
- ]);
95
+ ]));
96
96
  return collections.total > 0
97
97
  ? collections.collections[0].$id
98
98
  : collectionIdentifier;
99
99
  };
100
100
  // Function to check if the target field is an array
101
101
  const isTargetFieldArray = async (collectionId, fieldName) => {
102
- const collection = await db.getCollection(dbId, collectionId);
102
+ const collection = await tryAwaitWithRetry(async () => await db.getCollection(dbId, collectionId));
103
103
  const attribute = collection.attributes.find((attr) => attr.key === fieldName);
104
104
  // @ts-ignore
105
105
  return attribute?.array === true;
@@ -119,7 +119,7 @@ export const afterImportActions = {
119
119
  queries.push(Query.cursorAfter(cursor));
120
120
  }
121
121
  queries.push(Query.limit(docLimit));
122
- const response = await db.listDocuments(dbId, otherCollectionId, queries);
122
+ const response = await tryAwaitWithRetry(async () => await db.listDocuments(dbId, otherCollectionId, queries));
123
123
  const documents = response.documents;
124
124
  if (documents.length === 0 || documents.length < docLimit) {
125
125
  return documents;
@@ -134,7 +134,7 @@ export const afterImportActions = {
134
134
  const updatePayload = targetFieldIsArray
135
135
  ? { [fieldName]: documentIds }
136
136
  : { [fieldName]: documentIds[0] };
137
- await db.updateDocument(dbId, targetCollectionId, docId, updatePayload);
137
+ await tryAwaitWithRetry(async () => await db.updateDocument(dbId, targetCollectionId, docId, updatePayload));
138
138
  console.log(`Field ${fieldName} updated successfully in document ${docId} with ${documentIds.length} document IDs.`);
139
139
  }
140
140
  }
@@ -145,10 +145,10 @@ export const afterImportActions = {
145
145
  setTargetFieldFromOtherCollectionDocumentsByMatchingField: async (config, dbId, collIdOrName, docId, fieldName, otherCollIdOrName, matchingFieldName, matchingFieldValue, targetField) => {
146
146
  const db = getDatabaseFromConfig(config);
147
147
  const findCollectionId = async (collectionIdentifier) => {
148
- const collections = await db.listCollections(dbId, [
148
+ const collections = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [
149
149
  Query.equal("name", collectionIdentifier),
150
150
  Query.limit(1),
151
- ]);
151
+ ]));
152
152
  return collections.total > 0
153
153
  ? collections.collections[0].$id
154
154
  : collectionIdentifier;
@@ -172,7 +172,7 @@ export const afterImportActions = {
172
172
  if (cursor) {
173
173
  queries.push(Query.cursorAfter(cursor));
174
174
  }
175
- const response = await db.listDocuments(dbId, otherCollectionId, queries);
175
+ const response = await tryAwaitWithRetry(async () => await db.listDocuments(dbId, otherCollectionId, queries));
176
176
  const documents = response.documents;
177
177
  if (documents.length === 0 || documents.length < docLimit) {
178
178
  return documents;
@@ -188,7 +188,7 @@ export const afterImportActions = {
188
188
  const updatePayload = targetFieldIsArray
189
189
  ? { [fieldName]: targetFieldValues }
190
190
  : { [fieldName]: targetFieldValues[0] };
191
- await db.updateDocument(dbId, targetCollectionId, docId, updatePayload);
191
+ await tryAwaitWithRetry(async () => await db.updateDocument(dbId, targetCollectionId, docId, updatePayload));
192
192
  console.log(`Field ${fieldName} updated successfully in document ${docId} with values from field ${targetField}.`);
193
193
  }
194
194
  }
@@ -199,22 +199,20 @@ export const afterImportActions = {
199
199
  createOrGetBucket: async (config, bucketName, bucketId, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus) => {
200
200
  try {
201
201
  const storage = getStorageFromConfig(config);
202
- const bucket = await storage.listBuckets([
203
- Query.equal("name", bucketName),
204
- ]);
202
+ const bucket = await tryAwaitWithRetry(async () => await storage.listBuckets([Query.equal("name", bucketName)]));
205
203
  if (bucket.buckets.length > 0) {
206
204
  return bucket.buckets[0];
207
205
  }
208
206
  else if (bucketId) {
209
207
  try {
210
- return await storage.getBucket(bucketId);
208
+ return await tryAwaitWithRetry(async () => await storage.getBucket(bucketId));
211
209
  }
212
210
  catch (error) {
213
- return await storage.createBucket(bucketId, bucketName, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus);
211
+ return await tryAwaitWithRetry(async () => await storage.createBucket(bucketId, bucketName, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus));
214
212
  }
215
213
  }
216
214
  else {
217
- return await storage.createBucket(bucketId || ID.unique(), bucketName, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus);
215
+ return await tryAwaitWithRetry(async () => await storage.createBucket(bucketId || ID.unique(), bucketName, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus));
218
216
  }
219
217
  }
220
218
  catch (error) {
@@ -225,7 +223,7 @@ export const afterImportActions = {
225
223
  try {
226
224
  const db = getDatabaseFromConfig(config);
227
225
  const storage = getStorageFromConfig(config);
228
- const collection = await db.getCollection(dbId, collId);
226
+ const collection = await tryAwaitWithRetry(async () => await db.getCollection(dbId, collId));
229
227
  const attributes = collection.attributes;
230
228
  const attribute = attributes.find((a) => a.key === fieldName);
231
229
  // console.log(
@@ -255,7 +253,7 @@ export const afterImportActions = {
255
253
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "appwrite_tmp"));
256
254
  const tempFilePath = path.join(tempDir, fileName);
257
255
  // Download the file using fetch
258
- const response = await fetch(filePath);
256
+ const response = await tryAwaitWithRetry(async () => await fetch(filePath));
259
257
  if (!response.ok)
260
258
  console.error(`Failed to fetch ${filePath}: ${response.statusText} for document ${docId} with field ${fieldName}`);
261
259
  // Use arrayBuffer if buffer is not available
@@ -291,7 +291,8 @@ export class DataLoader {
291
291
  console.log("---------------------------------");
292
292
  await this.setupMaps(dbId);
293
293
  const allUsers = await this.getAllUsers();
294
- console.log(`Fetched ${allUsers.length} users`);
294
+ console.log(`Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`);
295
+ await new Promise((resolve) => setTimeout(resolve, 5000));
295
296
  // Iterate over the configured databases to find the matching one
296
297
  for (const db of this.config.databases) {
297
298
  if (db.$id !== dbId) {
@@ -728,9 +729,18 @@ export class DataLoader {
728
729
  let { transformedItem, existingId, userData } = this.prepareUserData(item, importDef.attributeMappings, importDef.primaryKeyField, this.getTrueUniqueId(this.getCollectionKey("users")));
729
730
  logger.info(`In create user -- transformedItem: ${JSON.stringify(transformedItem, null, 2)}`);
730
731
  // Generate a new unique ID for the item or use existing ID
731
- if (!existingId) {
732
+ if (!existingId && !userData.finalData?.userId) {
732
733
  // No existing user ID, generate a new unique ID
733
- existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
734
+ existingId = this.getTrueUniqueId(this.getCollectionKey(collection.name));
735
+ transformedItem = {
736
+ ...transformedItem,
737
+ userId: existingId,
738
+ docId: existingId,
739
+ };
740
+ }
741
+ else if (!existingId && userData.finalData?.userId) {
742
+ // Existing user ID, use it as the new ID
743
+ existingId = userData.finalData.userId;
734
744
  transformedItem = {
735
745
  ...transformedItem,
736
746
  userId: existingId,
@@ -751,7 +761,9 @@ export class DataLoader {
751
761
  // Found a duplicate oldId, now decide how to merge or handle these duplicates
752
762
  for (const data of currentData.data) {
753
763
  if (data.finalData.docId === oldId ||
754
- data.finalData.userId === oldId) {
764
+ data.finalData.userId === oldId ||
765
+ data.context.docId === oldId ||
766
+ data.context.userId === oldId) {
755
767
  transformedItem = this.mergeObjects(data.finalData, transformedItem);
756
768
  }
757
769
  }
@@ -787,30 +799,30 @@ export class DataLoader {
787
799
  ...importDef,
788
800
  attributeMappings: mappingsWithActions,
789
801
  };
802
+ const updatedData = this.importMap.get(this.getCollectionKey(collection.name));
790
803
  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,
798
- };
799
- currentData.data[i].importDef = newImportDef;
800
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
804
+ for (let i = 0; i < updatedData.data.length; i++) {
805
+ if (updatedData.data[i].finalData.docId === existingId ||
806
+ updatedData.data[i].finalData.userId === existingId ||
807
+ updatedData.data[i].context.docId === existingId ||
808
+ updatedData.data[i].context.userId === existingId) {
809
+ updatedData.data[i].finalData = this.mergeObjects(updatedData.data[i].finalData, transformedItem);
810
+ updatedData.data[i].context = this.mergeObjects(updatedData.data[i].context, context);
811
+ updatedData.data[i].importDef = this.mergeObjects(updatedData.data[i].importDef, newImportDef);
812
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
801
813
  this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
802
814
  foundData = true;
803
815
  }
804
816
  }
805
817
  if (!foundData) {
806
818
  // Add new data to the associated collection
807
- currentData.data.push({
819
+ updatedData.data.push({
808
820
  rawData: item,
809
821
  context: context,
810
822
  importDef: newImportDef,
811
823
  finalData: transformedItem,
812
824
  });
813
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
825
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
814
826
  this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
815
827
  }
816
828
  }
@@ -854,13 +866,7 @@ export class DataLoader {
854
866
  // Create a context object for the item, including the new ID
855
867
  let context = this.createContext(db, collection, item, itemIdNew);
856
868
  // 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
- }
869
+ let transformedData = this.transformData(item, importDef.attributeMappings);
864
870
  // If a primary key field is defined, handle the ID mapping and check for duplicates
865
871
  if (importDef.primaryKeyField) {
866
872
  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.61",
4
+ "version": "0.0.63",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -47,7 +47,9 @@ export const afterImportActions = {
47
47
  ) => {
48
48
  try {
49
49
  const db = getDatabaseFromConfig(config);
50
- await db.updateDocument(dbId, collId, docId, data);
50
+ await tryAwaitWithRetry(
51
+ async () => await db.updateDocument(dbId, collId, docId, data)
52
+ );
51
53
  } catch (error) {
52
54
  console.error("Error updating document: ", error);
53
55
  }
@@ -63,11 +65,16 @@ export const afterImportActions = {
63
65
  ) => {
64
66
  try {
65
67
  const db = getDatabaseFromConfig(config);
66
- const doc = await db.getDocument(dbId, collId, docId);
68
+ const doc = await tryAwaitWithRetry(
69
+ async () => await db.getDocument(dbId, collId, docId)
70
+ );
67
71
  if (doc[fieldName as keyof typeof doc] == oldFieldValue) {
68
- await db.updateDocument(dbId, collId, docId, {
69
- [fieldName]: newFieldValue,
70
- });
72
+ await tryAwaitWithRetry(
73
+ async () =>
74
+ await db.updateDocument(dbId, collId, docId, {
75
+ [fieldName]: newFieldValue,
76
+ })
77
+ );
71
78
  }
72
79
  } catch (error) {
73
80
  console.error("Error updating document: ", error);
@@ -87,10 +94,13 @@ export const afterImportActions = {
87
94
 
88
95
  // Helper function to find a collection ID by name or return the ID if given
89
96
  const findCollectionId = async (collectionIdentifier: string) => {
90
- const collectionsPulled = await db.listCollections(dbId, [
91
- Query.limit(25),
92
- Query.equal("name", collectionIdentifier),
93
- ]);
97
+ const collectionsPulled = await tryAwaitWithRetry(
98
+ async () =>
99
+ await db.listCollections(dbId, [
100
+ Query.limit(25),
101
+ Query.equal("name", collectionIdentifier),
102
+ ])
103
+ );
94
104
  if (collectionsPulled.total > 0) {
95
105
  return collectionsPulled.collections[0].$id;
96
106
  } else {
@@ -114,9 +124,12 @@ export const afterImportActions = {
114
124
 
115
125
  if (valueToSet) {
116
126
  // Update the target document
117
- await db.updateDocument(dbId, targetCollectionId, docId, {
118
- [fieldName]: valueToSet,
119
- });
127
+ await tryAwaitWithRetry(
128
+ async () =>
129
+ await db.updateDocument(dbId, targetCollectionId, docId, {
130
+ [fieldName]: valueToSet,
131
+ })
132
+ );
120
133
  }
121
134
 
122
135
  console.log(
@@ -148,10 +161,13 @@ export const afterImportActions = {
148
161
 
149
162
  // Helper function to find a collection ID by name or return the ID if given
150
163
  const findCollectionId = async (collectionIdentifier: string) => {
151
- const collections = await db.listCollections(dbId, [
152
- Query.equal("name", collectionIdentifier),
153
- Query.limit(1),
154
- ]);
164
+ const collections = await tryAwaitWithRetry(
165
+ async () =>
166
+ await db.listCollections(dbId, [
167
+ Query.equal("name", collectionIdentifier),
168
+ Query.limit(1),
169
+ ])
170
+ );
155
171
  return collections.total > 0
156
172
  ? collections.collections[0].$id
157
173
  : collectionIdentifier;
@@ -162,7 +178,9 @@ export const afterImportActions = {
162
178
  collectionId: string,
163
179
  fieldName: string
164
180
  ) => {
165
- const collection = await db.getCollection(dbId, collectionId);
181
+ const collection = await tryAwaitWithRetry(
182
+ async () => await db.getCollection(dbId, collectionId)
183
+ );
166
184
  const attribute = collection.attributes.find(
167
185
  (attr: any) => attr.key === fieldName
168
186
  );
@@ -191,10 +209,8 @@ export const afterImportActions = {
191
209
  queries.push(Query.cursorAfter(cursor));
192
210
  }
193
211
  queries.push(Query.limit(docLimit));
194
- const response = await db.listDocuments(
195
- dbId,
196
- otherCollectionId,
197
- queries
212
+ const response = await tryAwaitWithRetry(
213
+ async () => await db.listDocuments(dbId, otherCollectionId, queries)
198
214
  );
199
215
  const documents = response.documents;
200
216
  if (documents.length === 0 || documents.length < docLimit) {
@@ -212,7 +228,15 @@ export const afterImportActions = {
212
228
  const updatePayload = targetFieldIsArray
213
229
  ? { [fieldName]: documentIds }
214
230
  : { [fieldName]: documentIds[0] };
215
- await db.updateDocument(dbId, targetCollectionId, docId, updatePayload);
231
+ await tryAwaitWithRetry(
232
+ async () =>
233
+ await db.updateDocument(
234
+ dbId,
235
+ targetCollectionId,
236
+ docId,
237
+ updatePayload
238
+ )
239
+ );
216
240
 
217
241
  console.log(
218
242
  `Field ${fieldName} updated successfully in document ${docId} with ${documentIds.length} document IDs.`
@@ -239,10 +263,13 @@ export const afterImportActions = {
239
263
  const db = getDatabaseFromConfig(config);
240
264
 
241
265
  const findCollectionId = async (collectionIdentifier: string) => {
242
- const collections = await db.listCollections(dbId, [
243
- Query.equal("name", collectionIdentifier),
244
- Query.limit(1),
245
- ]);
266
+ const collections = await tryAwaitWithRetry(
267
+ async () =>
268
+ await db.listCollections(dbId, [
269
+ Query.equal("name", collectionIdentifier),
270
+ Query.limit(1),
271
+ ])
272
+ );
246
273
  return collections.total > 0
247
274
  ? collections.collections[0].$id
248
275
  : collectionIdentifier;
@@ -279,10 +306,8 @@ export const afterImportActions = {
279
306
  if (cursor) {
280
307
  queries.push(Query.cursorAfter(cursor));
281
308
  }
282
- const response = await db.listDocuments(
283
- dbId,
284
- otherCollectionId,
285
- queries
309
+ const response = await tryAwaitWithRetry(
310
+ async () => await db.listDocuments(dbId, otherCollectionId, queries)
286
311
  );
287
312
  const documents = response.documents;
288
313
  if (documents.length === 0 || documents.length < docLimit) {
@@ -303,7 +328,15 @@ export const afterImportActions = {
303
328
  const updatePayload = targetFieldIsArray
304
329
  ? { [fieldName]: targetFieldValues }
305
330
  : { [fieldName]: targetFieldValues[0] };
306
- await db.updateDocument(dbId, targetCollectionId, docId, updatePayload);
331
+ await tryAwaitWithRetry(
332
+ async () =>
333
+ await db.updateDocument(
334
+ dbId,
335
+ targetCollectionId,
336
+ docId,
337
+ updatePayload
338
+ )
339
+ );
307
340
 
308
341
  console.log(
309
342
  `Field ${fieldName} updated successfully in document ${docId} with values from field ${targetField}.`
@@ -331,40 +364,48 @@ export const afterImportActions = {
331
364
  ) => {
332
365
  try {
333
366
  const storage = getStorageFromConfig(config);
334
- const bucket = await storage.listBuckets([
335
- Query.equal("name", bucketName),
336
- ]);
367
+ const bucket = await tryAwaitWithRetry(
368
+ async () => await storage.listBuckets([Query.equal("name", bucketName)])
369
+ );
337
370
  if (bucket.buckets.length > 0) {
338
371
  return bucket.buckets[0];
339
372
  } else if (bucketId) {
340
373
  try {
341
- return await storage.getBucket(bucketId);
374
+ return await tryAwaitWithRetry(
375
+ async () => await storage.getBucket(bucketId)
376
+ );
342
377
  } catch (error) {
343
- return await storage.createBucket(
344
- bucketId,
345
- bucketName,
346
- permissions,
347
- fileSecurity,
348
- enabled,
349
- maxFileSize,
350
- allowedExtensions,
351
- compression,
352
- encryption,
353
- antivirus
378
+ return await tryAwaitWithRetry(
379
+ async () =>
380
+ await storage.createBucket(
381
+ bucketId,
382
+ bucketName,
383
+ permissions,
384
+ fileSecurity,
385
+ enabled,
386
+ maxFileSize,
387
+ allowedExtensions,
388
+ compression,
389
+ encryption,
390
+ antivirus
391
+ )
354
392
  );
355
393
  }
356
394
  } else {
357
- return await storage.createBucket(
358
- bucketId || ID.unique(),
359
- bucketName,
360
- permissions,
361
- fileSecurity,
362
- enabled,
363
- maxFileSize,
364
- allowedExtensions,
365
- compression,
366
- encryption,
367
- antivirus
395
+ return await tryAwaitWithRetry(
396
+ async () =>
397
+ await storage.createBucket(
398
+ bucketId || ID.unique(),
399
+ bucketName,
400
+ permissions,
401
+ fileSecurity,
402
+ enabled,
403
+ maxFileSize,
404
+ allowedExtensions,
405
+ compression,
406
+ encryption,
407
+ antivirus
408
+ )
368
409
  );
369
410
  }
370
411
  } catch (error) {
@@ -384,7 +425,9 @@ export const afterImportActions = {
384
425
  try {
385
426
  const db = getDatabaseFromConfig(config);
386
427
  const storage = getStorageFromConfig(config);
387
- const collection = await db.getCollection(dbId, collId);
428
+ const collection = await tryAwaitWithRetry(
429
+ async () => await db.getCollection(dbId, collId)
430
+ );
388
431
  const attributes = collection.attributes as any[];
389
432
  const attribute = attributes.find((a) => a.key === fieldName);
390
433
  // console.log(
@@ -423,7 +466,9 @@ export const afterImportActions = {
423
466
  const tempFilePath = path.join(tempDir, fileName);
424
467
 
425
468
  // Download the file using fetch
426
- const response = await fetch(filePath);
469
+ const response = await tryAwaitWithRetry(
470
+ async () => await fetch(filePath)
471
+ );
427
472
  if (!response.ok)
428
473
  console.error(
429
474
  `Failed to fetch ${filePath}: ${response.statusText} for document ${docId} with field ${fieldName}`
@@ -362,7 +362,10 @@ export class DataLoader {
362
362
  console.log("---------------------------------");
363
363
  await this.setupMaps(dbId);
364
364
  const allUsers = await this.getAllUsers();
365
- console.log(`Fetched ${allUsers.length} users`);
365
+ console.log(
366
+ `Fetched ${allUsers.length} users, waiting a few seconds to let the program catch up...`
367
+ );
368
+ await new Promise((resolve) => setTimeout(resolve, 5000));
366
369
  // Iterate over the configured databases to find the matching one
367
370
  for (const db of this.config.databases) {
368
371
  if (db.$id !== dbId) {
@@ -940,9 +943,19 @@ export class DataLoader {
940
943
  );
941
944
 
942
945
  // Generate a new unique ID for the item or use existing ID
943
- if (!existingId) {
946
+ if (!existingId && !userData.finalData?.userId) {
944
947
  // No existing user ID, generate a new unique ID
945
- existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
948
+ existingId = this.getTrueUniqueId(
949
+ this.getCollectionKey(collection.name)
950
+ );
951
+ transformedItem = {
952
+ ...transformedItem,
953
+ userId: existingId,
954
+ docId: existingId,
955
+ };
956
+ } else if (!existingId && userData.finalData?.userId) {
957
+ // Existing user ID, use it as the new ID
958
+ existingId = userData.finalData.userId;
946
959
  transformedItem = {
947
960
  ...transformedItem,
948
961
  userId: existingId,
@@ -951,7 +964,7 @@ export class DataLoader {
951
964
  }
952
965
 
953
966
  // Create a context object for the item, including the new ID
954
- let context = this.createContext(db, collection, item, existingId);
967
+ let context = this.createContext(db, collection, item, existingId!);
955
968
 
956
969
  // Merge the transformed data into the context
957
970
  context = { ...context, ...transformedItem, ...userData.finalData };
@@ -970,7 +983,9 @@ export class DataLoader {
970
983
  for (const data of currentData.data) {
971
984
  if (
972
985
  data.finalData.docId === oldId ||
973
- data.finalData.userId === oldId
986
+ data.finalData.userId === oldId ||
987
+ data.context.docId === oldId ||
988
+ data.context.userId === oldId
974
989
  ) {
975
990
  transformedItem = this.mergeObjects(
976
991
  data.finalData,
@@ -1022,41 +1037,51 @@ export class DataLoader {
1022
1037
  attributeMappings: mappingsWithActions,
1023
1038
  };
1024
1039
 
1040
+ const updatedData = this.importMap.get(
1041
+ this.getCollectionKey(collection.name)
1042
+ )!;
1043
+
1025
1044
  let foundData = false;
1026
- for (let i = 0; i < currentData.data.length; i++) {
1045
+ for (let i = 0; i < updatedData.data.length; i++) {
1027
1046
  if (
1028
- currentData.data[i].finalData.docId === existingId ||
1029
- currentData.data[i].finalData.userId === existingId
1047
+ updatedData.data[i].finalData.docId === existingId ||
1048
+ updatedData.data[i].finalData.userId === existingId ||
1049
+ updatedData.data[i].context.docId === existingId ||
1050
+ updatedData.data[i].context.userId === existingId
1030
1051
  ) {
1031
- currentData.data[i].finalData = this.mergeObjects(
1032
- currentData.data[i].finalData,
1052
+ updatedData.data[i].finalData = this.mergeObjects(
1053
+ updatedData.data[i].finalData,
1033
1054
  transformedItem
1034
1055
  );
1035
- currentData.data[i].context = {
1036
- ...currentData.data[i].context,
1037
- ...context,
1038
- };
1039
- currentData.data[i].importDef = newImportDef;
1056
+ updatedData.data[i].context = this.mergeObjects(
1057
+ updatedData.data[i].context,
1058
+ context
1059
+ );
1060
+ updatedData.data[i].importDef = this.mergeObjects(
1061
+ updatedData.data[i].importDef,
1062
+ newImportDef
1063
+ );
1040
1064
  this.importMap.set(
1041
1065
  this.getCollectionKey(collection.name),
1042
- currentData
1066
+ updatedData
1043
1067
  );
1044
1068
  this.oldIdToNewIdPerCollectionMap.set(
1045
1069
  this.getCollectionKey(collection.name),
1046
1070
  collectionOldIdToNewIdMap!
1047
1071
  );
1072
+
1048
1073
  foundData = true;
1049
1074
  }
1050
1075
  }
1051
1076
  if (!foundData) {
1052
1077
  // Add new data to the associated collection
1053
- currentData.data.push({
1078
+ updatedData.data.push({
1054
1079
  rawData: item,
1055
1080
  context: context,
1056
1081
  importDef: newImportDef,
1057
1082
  finalData: transformedItem,
1058
1083
  });
1059
- this.importMap.set(this.getCollectionKey(collection.name), currentData);
1084
+ this.importMap.set(this.getCollectionKey(collection.name), updatedData);
1060
1085
  this.oldIdToNewIdPerCollectionMap.set(
1061
1086
  this.getCollectionKey(collection.name),
1062
1087
  collectionOldIdToNewIdMap!
@@ -1119,18 +1144,10 @@ export class DataLoader {
1119
1144
  // Create a context object for the item, including the new ID
1120
1145
  let context = this.createContext(db, collection, item, itemIdNew);
1121
1146
  // Transform the item data based on the attribute mappings
1122
- const transformedData = this.transformData(
1147
+ let transformedData = this.transformData(
1123
1148
  item,
1124
1149
  importDef.attributeMappings
1125
1150
  );
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
1151
  // If a primary key field is defined, handle the ID mapping and check for duplicates
1135
1152
  if (importDef.primaryKeyField) {
1136
1153
  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;