appwrite-utils-cli 0.0.37 → 0.0.38
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/README.md +1 -0
- package/dist/migrations/attributes.js +21 -20
- package/dist/migrations/collections.js +10 -13
- package/dist/migrations/dataLoader.d.ts +14 -23
- package/dist/migrations/dataLoader.js +248 -170
- package/dist/migrations/databases.js +2 -1
- package/dist/migrations/importController.js +6 -24
- package/dist/migrations/importDataActions.d.ts +1 -1
- package/dist/migrations/importDataActions.js +3 -2
- package/dist/migrations/indexes.js +2 -1
- package/dist/migrations/migrationHelper.js +2 -1
- package/dist/migrations/queue.js +3 -2
- package/dist/migrations/setupDatabase.js +10 -12
- package/dist/migrations/storage.js +14 -13
- package/dist/migrations/users.js +14 -36
- package/dist/utils/helperFunctions.d.ts +10 -1
- package/dist/utils/helperFunctions.js +27 -0
- package/dist/utilsController.js +2 -1
- package/package.json +2 -2
- package/src/migrations/attributes.ts +204 -143
- package/src/migrations/collections.ts +49 -33
- package/src/migrations/dataLoader.ts +377 -240
- package/src/migrations/databases.ts +4 -1
- package/src/migrations/importController.ts +13 -27
- package/src/migrations/importDataActions.ts +6 -3
- package/src/migrations/indexes.ts +4 -1
- package/src/migrations/migrationHelper.ts +9 -5
- package/src/migrations/queue.ts +5 -6
- package/src/migrations/setupDatabase.ts +35 -18
- package/src/migrations/storage.ts +50 -36
- package/src/migrations/users.ts +28 -38
- package/src/utils/helperFunctions.ts +33 -1
- package/src/utilsController.ts +3 -1
|
@@ -65,6 +65,7 @@ export class DataLoader {
|
|
|
65
65
|
* It iterates through the target object's keys and updates the source object if:
|
|
66
66
|
* - The source object has the key.
|
|
67
67
|
* - The target object's value for that key is not null, undefined, or an empty string.
|
|
68
|
+
* - If the target object has an array value, it concatenates the values and removes duplicates.
|
|
68
69
|
*
|
|
69
70
|
* @param source - The source object to be updated.
|
|
70
71
|
* @param target - The target object with values to update the source object.
|
|
@@ -125,8 +126,19 @@ export class DataLoader {
|
|
|
125
126
|
// Method to generate a unique ID that doesn't conflict with existing IDs
|
|
126
127
|
getTrueUniqueId(collectionName) {
|
|
127
128
|
let newId = ID.unique();
|
|
128
|
-
|
|
129
|
+
let condition = this.checkMapValuesForId(newId, collectionName) ||
|
|
130
|
+
this.userExistsMap.has(newId) ||
|
|
131
|
+
this.importMap
|
|
132
|
+
.get(this.getCollectionKey("users"))
|
|
133
|
+
?.data.some((user) => user.finalData.docId === newId || user.finalData.userId === newId);
|
|
134
|
+
while (condition) {
|
|
129
135
|
newId = ID.unique();
|
|
136
|
+
condition =
|
|
137
|
+
this.checkMapValuesForId(newId, collectionName) ||
|
|
138
|
+
this.userExistsMap.has(newId) ||
|
|
139
|
+
this.importMap
|
|
140
|
+
.get(this.getCollectionKey("users"))
|
|
141
|
+
?.data.some((user) => user.finalData.docId === newId || user.finalData.userId === newId);
|
|
130
142
|
}
|
|
131
143
|
return newId;
|
|
132
144
|
}
|
|
@@ -209,6 +221,21 @@ export class DataLoader {
|
|
|
209
221
|
this.phoneToUserIdMap.set(user.phone, user.$id);
|
|
210
222
|
}
|
|
211
223
|
this.userExistsMap.set(user.$id, true);
|
|
224
|
+
let importData = this.importMap.get(this.getCollectionKey("users"));
|
|
225
|
+
if (!importData) {
|
|
226
|
+
importData = {
|
|
227
|
+
data: [],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
importData.data.push({
|
|
231
|
+
finalData: {
|
|
232
|
+
...user,
|
|
233
|
+
userId: user.$id,
|
|
234
|
+
docId: user.$id,
|
|
235
|
+
},
|
|
236
|
+
rawData: user,
|
|
237
|
+
});
|
|
238
|
+
this.importMap.set(this.getCollectionKey("users"), importData);
|
|
212
239
|
}
|
|
213
240
|
return allUsers;
|
|
214
241
|
}
|
|
@@ -244,12 +271,12 @@ export class DataLoader {
|
|
|
244
271
|
for (const createDef of createDefs) {
|
|
245
272
|
if (!isUsersCollection) {
|
|
246
273
|
console.log(`${collection.name} is not users collection`);
|
|
247
|
-
|
|
274
|
+
this.prepareCreateData(db, collection, createDef);
|
|
248
275
|
}
|
|
249
276
|
else {
|
|
250
277
|
// Special handling for users collection if needed
|
|
251
278
|
console.log(`${collection.name} is users collection`);
|
|
252
|
-
|
|
279
|
+
this.prepareUserCollectionCreateData(db, collection, createDef);
|
|
253
280
|
}
|
|
254
281
|
}
|
|
255
282
|
for (const updateDef of updateDefs) {
|
|
@@ -258,11 +285,11 @@ export class DataLoader {
|
|
|
258
285
|
continue;
|
|
259
286
|
}
|
|
260
287
|
// Prepare the update data for the collection
|
|
261
|
-
|
|
288
|
+
this.prepareUpdateData(db, collection, updateDef);
|
|
262
289
|
}
|
|
263
290
|
}
|
|
264
291
|
console.log("Running update references");
|
|
265
|
-
this.dealWithMergedUsers();
|
|
292
|
+
// this.dealWithMergedUsers();
|
|
266
293
|
this.updateOldReferencesForNew();
|
|
267
294
|
console.log("Done running update references");
|
|
268
295
|
}
|
|
@@ -280,8 +307,11 @@ export class DataLoader {
|
|
|
280
307
|
const usersCollectionKey = this.getCollectionKey(this.config.usersCollectionName);
|
|
281
308
|
const usersCollectionPrimaryKeyFields = new Set();
|
|
282
309
|
if (!this.config.collections) {
|
|
310
|
+
console.log("No collections found in configuration.");
|
|
283
311
|
return;
|
|
284
312
|
}
|
|
313
|
+
let needsUpdate = false;
|
|
314
|
+
let numUpdates = 0;
|
|
285
315
|
// Collect primary key fields from the users collection definitions
|
|
286
316
|
this.config.collections.forEach((collection) => {
|
|
287
317
|
if (this.getCollectionKey(collection.name) === usersCollectionKey) {
|
|
@@ -296,37 +326,61 @@ export class DataLoader {
|
|
|
296
326
|
});
|
|
297
327
|
}
|
|
298
328
|
});
|
|
329
|
+
console.log(`Primary key fields collected for users collection: ${[
|
|
330
|
+
...usersCollectionPrimaryKeyFields,
|
|
331
|
+
]}`);
|
|
299
332
|
// Iterate over all collections to update references based on merged users
|
|
300
333
|
this.config.collections.forEach((collection) => {
|
|
301
334
|
const collectionData = this.importMap.get(this.getCollectionKey(collection.name));
|
|
302
|
-
if (!collectionData || !collectionData.data)
|
|
335
|
+
if (!collectionData || !collectionData.data) {
|
|
336
|
+
console.log(`No data found for collection ${collection.name}`);
|
|
303
337
|
return;
|
|
338
|
+
}
|
|
304
339
|
const collectionImportDefs = collection.importDefs;
|
|
305
340
|
if (!collectionImportDefs || !collectionImportDefs.length) {
|
|
341
|
+
console.log(`No import definitions found for collection ${collection.name}`);
|
|
306
342
|
return;
|
|
307
343
|
}
|
|
308
344
|
collectionImportDefs.forEach((importDef) => {
|
|
309
345
|
importDef.idMappings?.forEach((idMapping) => {
|
|
310
346
|
if (this.getCollectionKey(idMapping.targetCollection) ===
|
|
311
347
|
usersCollectionKey) {
|
|
348
|
+
const fieldToSetKey = idMapping.fieldToSet || idMapping.sourceField;
|
|
312
349
|
const targetFieldKey = idMapping.targetFieldToMatch || idMapping.targetField;
|
|
313
350
|
if (usersCollectionPrimaryKeyFields.has(targetFieldKey)) {
|
|
351
|
+
console.log(`Processing collection ${collection.name} with target field ${targetFieldKey}`);
|
|
314
352
|
// Process each item in the collection
|
|
315
353
|
collectionData.data.forEach((item) => {
|
|
316
|
-
const oldId = item.
|
|
354
|
+
const oldId = item.finalData[idMapping.sourceField] ||
|
|
355
|
+
item.context[idMapping.sourceField];
|
|
356
|
+
if (oldId === undefined || oldId === null) {
|
|
357
|
+
console.log(`Skipping item with undefined or null oldId in collection ${collection.name}`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
317
360
|
const newId = this.mergedUserMap.get(`${oldId}`);
|
|
318
361
|
if (newId) {
|
|
362
|
+
needsUpdate = true;
|
|
363
|
+
numUpdates++;
|
|
364
|
+
console.log(`Updating old ID ${oldId} to new ID ${newId} in collection ${collection.name}`);
|
|
319
365
|
// Update context to use new user ID
|
|
320
|
-
item.finalData[
|
|
366
|
+
item.finalData[fieldToSetKey] = newId;
|
|
367
|
+
item.context[fieldToSetKey] = newId;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
console.log(`No new ID found for old ID ${oldId} in mergedUserMap.`);
|
|
321
371
|
}
|
|
322
372
|
});
|
|
323
373
|
}
|
|
324
374
|
}
|
|
325
375
|
});
|
|
326
376
|
});
|
|
377
|
+
if (needsUpdate) {
|
|
378
|
+
console.log(`Updated ${numUpdates} references for collection ${collection.name}`);
|
|
379
|
+
this.importMap.set(this.getCollectionKey(collection.name), collectionData);
|
|
380
|
+
}
|
|
327
381
|
});
|
|
328
382
|
}
|
|
329
|
-
|
|
383
|
+
updateOldReferencesForNew() {
|
|
330
384
|
if (!this.config.collections) {
|
|
331
385
|
return;
|
|
332
386
|
}
|
|
@@ -467,7 +521,7 @@ export class DataLoader {
|
|
|
467
521
|
* @param attributeMappings - The attribute mappings for the item.
|
|
468
522
|
* @returns The transformed item with user-specific keys removed.
|
|
469
523
|
*/
|
|
470
|
-
|
|
524
|
+
prepareUserData(item, attributeMappings, primaryKeyField, newId) {
|
|
471
525
|
let transformedItem = this.transformData(item, attributeMappings);
|
|
472
526
|
const userData = AuthUserCreateSchema.safeParse(transformedItem);
|
|
473
527
|
if (!userData.success) {
|
|
@@ -515,15 +569,23 @@ export class DataLoader {
|
|
|
515
569
|
});
|
|
516
570
|
if (userFound) {
|
|
517
571
|
userFound.finalData.userId = existingId;
|
|
572
|
+
userFound.finalData.docId = existingId;
|
|
573
|
+
this.userExistsMap.set(existingId, true);
|
|
518
574
|
}
|
|
519
|
-
|
|
575
|
+
const userKeys = ["email", "phone", "name", "labels", "prefs"];
|
|
576
|
+
userKeys.forEach((key) => {
|
|
577
|
+
if (transformedItem.hasOwnProperty(key)) {
|
|
578
|
+
delete transformedItem[key];
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
return {
|
|
520
582
|
transformedItem,
|
|
521
583
|
existingId,
|
|
522
|
-
{
|
|
584
|
+
userData: {
|
|
523
585
|
rawData: userFound?.rawData,
|
|
524
586
|
finalData: userFound?.finalData,
|
|
525
587
|
},
|
|
526
|
-
|
|
588
|
+
};
|
|
527
589
|
}
|
|
528
590
|
else {
|
|
529
591
|
existingId = newId;
|
|
@@ -543,7 +605,11 @@ export class DataLoader {
|
|
|
543
605
|
this.importMap.set(this.getCollectionKey("users"), {
|
|
544
606
|
data: [...(usersMap?.data || []), userDataToAdd],
|
|
545
607
|
});
|
|
546
|
-
return
|
|
608
|
+
return {
|
|
609
|
+
transformedItem,
|
|
610
|
+
existingId,
|
|
611
|
+
userData: userDataToAdd,
|
|
612
|
+
};
|
|
547
613
|
}
|
|
548
614
|
/**
|
|
549
615
|
* Prepares the data for creating user collection documents.
|
|
@@ -554,7 +620,7 @@ export class DataLoader {
|
|
|
554
620
|
* @param collection - The collection configuration.
|
|
555
621
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
556
622
|
*/
|
|
557
|
-
|
|
623
|
+
prepareUserCollectionCreateData(db, collection, importDef) {
|
|
558
624
|
// Load the raw data based on the import definition
|
|
559
625
|
const rawData = this.loadData(importDef);
|
|
560
626
|
const operationId = this.collectionImportOperations.get(this.getCollectionKey(collection.name));
|
|
@@ -569,100 +635,108 @@ export class DataLoader {
|
|
|
569
635
|
if (!operationId) {
|
|
570
636
|
throw new Error(`No import operation found for collection ${collection.name}`);
|
|
571
637
|
}
|
|
572
|
-
|
|
638
|
+
updateOperation(this.database, operationId, {
|
|
573
639
|
status: "ready",
|
|
574
640
|
total: rawData.length,
|
|
575
|
-
})
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
641
|
+
}).then(() => {
|
|
642
|
+
// Retrieve the current user data and the current collection data from the import map
|
|
643
|
+
const currentUserData = this.importMap.get(this.getCollectionKey("users"));
|
|
644
|
+
const currentData = this.importMap.get(this.getCollectionKey(collection.name));
|
|
645
|
+
// Log errors if the necessary data is not found in the import map
|
|
646
|
+
if (!currentUserData) {
|
|
647
|
+
logger.error(`No data found for collection ${"users"} for createDef but it says it's supposed to have one...`);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
else if (!currentData) {
|
|
651
|
+
logger.error(`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
// Iterate through each item in the raw data
|
|
655
|
+
for (const item of rawData) {
|
|
656
|
+
// Prepare user data, check for duplicates, and remove user-specific keys
|
|
657
|
+
let { transformedItem, existingId, userData } = this.prepareUserData(item, importDef.attributeMappings, importDef.primaryKeyField, this.getTrueUniqueId(this.getCollectionKey("users")));
|
|
658
|
+
logger.info(`In create user -- transformedItem: ${JSON.stringify(transformedItem, null, 2)}`);
|
|
659
|
+
// Generate a new unique ID for the item or use existing ID
|
|
660
|
+
if (!existingId) {
|
|
661
|
+
// No existing user ID, generate a new unique ID
|
|
662
|
+
existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
|
|
663
|
+
transformedItem.docId = existingId; // Assign the new ID to the transformed data's docId field
|
|
664
|
+
}
|
|
665
|
+
// Create a context object for the item, including the new ID
|
|
666
|
+
let context = this.createContext(db, collection, item, existingId);
|
|
667
|
+
// Merge the transformed data into the context
|
|
668
|
+
context = { ...context, ...transformedItem, ...userData.finalData };
|
|
669
|
+
// If a primary key field is defined, handle the ID mapping and check for duplicates
|
|
670
|
+
if (importDef.primaryKeyField) {
|
|
671
|
+
const oldId = item[importDef.primaryKeyField];
|
|
672
|
+
// Check if the oldId already exists to handle potential duplicates
|
|
673
|
+
if (this.oldIdToNewIdPerCollectionMap
|
|
674
|
+
.get(this.getCollectionKey(collection.name))
|
|
675
|
+
?.has(`${oldId}`)) {
|
|
676
|
+
// Found a duplicate oldId, now decide how to merge or handle these duplicates
|
|
677
|
+
for (const data of currentData.data) {
|
|
678
|
+
if (data.finalData.docId === oldId ||
|
|
679
|
+
data.finalData.userId === oldId) {
|
|
680
|
+
transformedItem = this.mergeObjects(data.finalData, transformedItem);
|
|
681
|
+
}
|
|
615
682
|
}
|
|
616
683
|
}
|
|
684
|
+
else {
|
|
685
|
+
// No duplicate found, simply map the oldId to the new itemId
|
|
686
|
+
collectionOldIdToNewIdMap?.set(`${oldId}`, `${existingId}`);
|
|
687
|
+
}
|
|
617
688
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
689
|
+
// Handle merging for currentUserData
|
|
690
|
+
for (let i = 0; i < currentUserData.data.length; i++) {
|
|
691
|
+
const currentUserDataItem = currentUserData.data[i];
|
|
692
|
+
const samePhones = currentUserDataItem.finalData.phone &&
|
|
693
|
+
transformedItem.phone &&
|
|
694
|
+
currentUserDataItem.finalData.phone === transformedItem.phone;
|
|
695
|
+
const sameEmails = currentUserDataItem.finalData.email &&
|
|
696
|
+
transformedItem.email &&
|
|
697
|
+
currentUserDataItem.finalData.email === transformedItem.email;
|
|
698
|
+
if ((currentUserDataItem.finalData.docId === existingId ||
|
|
699
|
+
currentUserDataItem.finalData.userId === existingId) &&
|
|
700
|
+
(samePhones || sameEmails) &&
|
|
701
|
+
currentUserDataItem.finalData &&
|
|
702
|
+
userData.finalData) {
|
|
703
|
+
const userDataMerged = this.mergeObjects(currentUserData.data[i].finalData, userData.finalData);
|
|
704
|
+
currentUserData.data[i].finalData = userDataMerged;
|
|
705
|
+
this.importMap.set(this.getCollectionKey("users"), currentUserData);
|
|
706
|
+
}
|
|
621
707
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
708
|
+
// Update the attribute mappings with any actions that need to be performed post-import
|
|
709
|
+
const mappingsWithActions = this.getAttributeMappingsWithActions(importDef.attributeMappings, context, transformedItem);
|
|
710
|
+
// Update the import definition with the new attribute mappings
|
|
711
|
+
const newImportDef = {
|
|
712
|
+
...importDef,
|
|
713
|
+
attributeMappings: mappingsWithActions,
|
|
714
|
+
};
|
|
715
|
+
let foundData = false;
|
|
716
|
+
for (let i = 0; i < currentData.data.length; i++) {
|
|
717
|
+
if (currentData.data[i].finalData.docId === existingId ||
|
|
718
|
+
currentData.data[i].finalData.userId === existingId) {
|
|
719
|
+
currentData.data[i].finalData = this.mergeObjects(currentData.data[i].finalData, transformedItem);
|
|
720
|
+
currentData.data[i].context = context;
|
|
721
|
+
currentData.data[i].importDef = newImportDef;
|
|
722
|
+
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
723
|
+
this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
|
|
724
|
+
foundData = true;
|
|
725
|
+
}
|
|
633
726
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
let foundData = false;
|
|
643
|
-
for (let i = 0; i < currentData.data.length; i++) {
|
|
644
|
-
if (currentData.data[i].finalData.docId === existingId ||
|
|
645
|
-
currentData.data[i].finalData.userId === existingId) {
|
|
646
|
-
currentData.data[i].finalData = this.mergeObjects(currentData.data[i].finalData, transformedItem);
|
|
647
|
-
currentData.data[i].context = context;
|
|
648
|
-
currentData.data[i].importDef = newImportDef;
|
|
727
|
+
if (!foundData) {
|
|
728
|
+
// Add new data to the associated collection
|
|
729
|
+
currentData.data.push({
|
|
730
|
+
rawData: item,
|
|
731
|
+
context: context,
|
|
732
|
+
importDef: newImportDef,
|
|
733
|
+
finalData: transformedItem,
|
|
734
|
+
});
|
|
649
735
|
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
650
736
|
this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
|
|
651
|
-
foundData = true;
|
|
652
737
|
}
|
|
653
738
|
}
|
|
654
|
-
|
|
655
|
-
// Add new data to the associated collection
|
|
656
|
-
currentData.data.push({
|
|
657
|
-
rawData: item,
|
|
658
|
-
context: context,
|
|
659
|
-
importDef: newImportDef,
|
|
660
|
-
finalData: transformedItem,
|
|
661
|
-
});
|
|
662
|
-
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
663
|
-
this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
739
|
+
});
|
|
666
740
|
}
|
|
667
741
|
/**
|
|
668
742
|
* Prepares the data for creating documents in a collection.
|
|
@@ -672,74 +746,75 @@ export class DataLoader {
|
|
|
672
746
|
* @param collection - The collection configuration.
|
|
673
747
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
674
748
|
*/
|
|
675
|
-
|
|
749
|
+
prepareCreateData(db, collection, importDef) {
|
|
676
750
|
// Load the raw data based on the import definition
|
|
677
751
|
const rawData = this.loadData(importDef);
|
|
678
752
|
const operationId = this.collectionImportOperations.get(this.getCollectionKey(collection.name));
|
|
679
753
|
if (!operationId) {
|
|
680
754
|
throw new Error(`No import operation found for collection ${collection.name}`);
|
|
681
755
|
}
|
|
682
|
-
|
|
756
|
+
updateOperation(this.database, operationId, {
|
|
683
757
|
status: "ready",
|
|
684
758
|
total: rawData.length,
|
|
685
|
-
})
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
759
|
+
}).then(() => {
|
|
760
|
+
// Initialize a new map for old ID to new ID mappings
|
|
761
|
+
const oldIdToNewIdMapNew = new Map();
|
|
762
|
+
// Retrieve or initialize the collection-specific old ID to new ID map
|
|
763
|
+
const collectionOldIdToNewIdMap = this.oldIdToNewIdPerCollectionMap.get(this.getCollectionKey(collection.name)) ||
|
|
764
|
+
this.oldIdToNewIdPerCollectionMap
|
|
765
|
+
.set(this.getCollectionKey(collection.name), oldIdToNewIdMapNew)
|
|
766
|
+
.get(this.getCollectionKey(collection.name));
|
|
767
|
+
console.log(`${collection.name} -- collectionOldIdToNewIdMap: ${collectionOldIdToNewIdMap}`);
|
|
768
|
+
// Iterate through each item in the raw data
|
|
769
|
+
for (const item of rawData) {
|
|
770
|
+
// Generate a new unique ID for the item
|
|
771
|
+
const itemIdNew = this.getTrueUniqueId(this.getCollectionKey(collection.name));
|
|
772
|
+
// Retrieve the current collection data from the import map
|
|
773
|
+
const currentData = this.importMap.get(this.getCollectionKey(collection.name));
|
|
774
|
+
// Create a context object for the item, including the new ID
|
|
775
|
+
let context = this.createContext(db, collection, item, itemIdNew);
|
|
776
|
+
// Transform the item data based on the attribute mappings
|
|
777
|
+
const transformedData = this.transformData(item, importDef.attributeMappings);
|
|
778
|
+
// If a primary key field is defined, handle the ID mapping and check for duplicates
|
|
779
|
+
if (importDef.primaryKeyField) {
|
|
780
|
+
const oldId = item[importDef.primaryKeyField];
|
|
781
|
+
if (collectionOldIdToNewIdMap?.has(`${oldId}`)) {
|
|
782
|
+
logger.error(`Collection ${collection.name} has multiple documents with the same primary key ${oldId}`);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
collectionOldIdToNewIdMap?.set(`${oldId}`, `${itemIdNew}`);
|
|
786
|
+
}
|
|
787
|
+
// Merge the transformed data into the context
|
|
788
|
+
context = { ...context, ...transformedData };
|
|
789
|
+
// Validate the item before proceeding
|
|
790
|
+
const isValid = this.importDataActions.validateItem(transformedData, importDef.attributeMappings, context);
|
|
791
|
+
if (!isValid) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
// Update the attribute mappings with any actions that need to be performed post-import
|
|
795
|
+
const mappingsWithActions = this.getAttributeMappingsWithActions(importDef.attributeMappings, context, transformedData);
|
|
796
|
+
// Update the import definition with the new attribute mappings
|
|
797
|
+
const newImportDef = {
|
|
798
|
+
...importDef,
|
|
799
|
+
attributeMappings: mappingsWithActions,
|
|
800
|
+
};
|
|
801
|
+
// If the current collection data exists, add the item with its context and final data
|
|
802
|
+
if (currentData && currentData.data) {
|
|
803
|
+
currentData.data.push({
|
|
804
|
+
rawData: item,
|
|
805
|
+
context: context,
|
|
806
|
+
importDef: newImportDef,
|
|
807
|
+
finalData: transformedData,
|
|
808
|
+
});
|
|
809
|
+
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
810
|
+
this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
logger.error(`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`);
|
|
709
814
|
continue;
|
|
710
815
|
}
|
|
711
|
-
collectionOldIdToNewIdMap?.set(`${oldId}`, `${itemIdNew}`);
|
|
712
|
-
}
|
|
713
|
-
// Merge the transformed data into the context
|
|
714
|
-
context = { ...context, ...transformedData };
|
|
715
|
-
// Validate the item before proceeding
|
|
716
|
-
const isValid = await this.importDataActions.validateItem(transformedData, importDef.attributeMappings, context);
|
|
717
|
-
if (!isValid) {
|
|
718
|
-
continue;
|
|
719
|
-
}
|
|
720
|
-
// Update the attribute mappings with any actions that need to be performed post-import
|
|
721
|
-
const mappingsWithActions = this.getAttributeMappingsWithActions(importDef.attributeMappings, context, transformedData);
|
|
722
|
-
// Update the import definition with the new attribute mappings
|
|
723
|
-
const newImportDef = {
|
|
724
|
-
...importDef,
|
|
725
|
-
attributeMappings: mappingsWithActions,
|
|
726
|
-
};
|
|
727
|
-
// If the current collection data exists, add the item with its context and final data
|
|
728
|
-
if (currentData && currentData.data) {
|
|
729
|
-
currentData.data.push({
|
|
730
|
-
rawData: item,
|
|
731
|
-
context: context,
|
|
732
|
-
importDef: newImportDef,
|
|
733
|
-
finalData: transformedData,
|
|
734
|
-
});
|
|
735
|
-
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
736
|
-
this.oldIdToNewIdPerCollectionMap.set(this.getCollectionKey(collection.name), collectionOldIdToNewIdMap);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
logger.error(`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`);
|
|
740
|
-
continue;
|
|
741
816
|
}
|
|
742
|
-
}
|
|
817
|
+
});
|
|
743
818
|
}
|
|
744
819
|
/**
|
|
745
820
|
* Prepares the data for updating documents within a collection.
|
|
@@ -751,7 +826,7 @@ export class DataLoader {
|
|
|
751
826
|
* @param collection - The collection configuration.
|
|
752
827
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
753
828
|
*/
|
|
754
|
-
|
|
829
|
+
prepareUpdateData(db, collection, importDef) {
|
|
755
830
|
// Retrieve the current collection data and old-to-new ID map from the import map
|
|
756
831
|
const currentData = this.importMap.get(this.getCollectionKey(collection.name));
|
|
757
832
|
const oldIdToNewIdMap = this.oldIdToNewIdPerCollectionMap.get(this.getCollectionKey(collection.name));
|
|
@@ -773,12 +848,7 @@ export class DataLoader {
|
|
|
773
848
|
let newId;
|
|
774
849
|
let oldId;
|
|
775
850
|
// Determine the new ID for the item based on the primary key field or update mapping
|
|
776
|
-
|
|
777
|
-
oldId = item[importDef.primaryKeyField];
|
|
778
|
-
}
|
|
779
|
-
else if (importDef.updateMapping) {
|
|
780
|
-
oldId = item[importDef.updateMapping.originalIdField];
|
|
781
|
-
}
|
|
851
|
+
oldId = item[importDef.primaryKeyField];
|
|
782
852
|
if (oldId) {
|
|
783
853
|
newId = oldIdToNewIdMap?.get(`${oldId}`);
|
|
784
854
|
if (!newId &&
|
|
@@ -796,24 +866,31 @@ export class DataLoader {
|
|
|
796
866
|
logger.error(`No old ID found (to update another document with) in prepareUpdateData for ${collection.name}, ${JSON.stringify(item, null, 2)}`);
|
|
797
867
|
continue;
|
|
798
868
|
}
|
|
869
|
+
const itemDataToUpdate = this.importMap
|
|
870
|
+
.get(this.getCollectionKey(collection.name))
|
|
871
|
+
?.data.find((data) => `${data.context[importDef.primaryKeyField]}` === `${oldId}`);
|
|
799
872
|
// Log an error and continue to the next item if no new ID is found
|
|
800
|
-
if (!newId) {
|
|
873
|
+
if (!newId && !itemDataToUpdate) {
|
|
801
874
|
logger.error(`No new id found for collection ${collection.name} for updateDef ${JSON.stringify(item, null, 2)} but it says it's supposed to have one...`);
|
|
802
875
|
continue;
|
|
803
876
|
}
|
|
804
|
-
|
|
805
|
-
.
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
877
|
+
else if (itemDataToUpdate) {
|
|
878
|
+
newId = itemDataToUpdate.finalData.docId;
|
|
879
|
+
if (!newId) {
|
|
880
|
+
logger.error(`No new id found for collection ${collection.name} for updateDef ${JSON.stringify(item, null, 2)} but it says it's supposed to have one...`);
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (!itemDataToUpdate || !newId) {
|
|
885
|
+
logger.error(`No data or ID (docId) found for collection ${collection.name} for updateDef ${JSON.stringify(item, null, 2)} but it says it's supposed to have one...`);
|
|
809
886
|
continue;
|
|
810
887
|
}
|
|
811
888
|
transformedData = this.mergeObjects(itemDataToUpdate.finalData, transformedData);
|
|
812
889
|
// Create a context object for the item, including the new ID and transformed data
|
|
813
890
|
let context = this.createContext(db, collection, item, newId);
|
|
814
|
-
context =
|
|
891
|
+
context = { ...context, ...transformedData };
|
|
815
892
|
// Validate the item before proceeding
|
|
816
|
-
const isValid =
|
|
893
|
+
const isValid = this.importDataActions.validateItem(item, importDef.attributeMappings, context);
|
|
817
894
|
// Log info and continue to the next item if it's invalid
|
|
818
895
|
if (!isValid) {
|
|
819
896
|
logger.info(`Skipping item: ${JSON.stringify(item, null, 2)} because it's invalid`);
|
|
@@ -832,6 +909,7 @@ export class DataLoader {
|
|
|
832
909
|
itemDataToUpdate.finalData = this.mergeObjects(itemDataToUpdate.finalData, transformedData);
|
|
833
910
|
itemDataToUpdate.context = context;
|
|
834
911
|
itemDataToUpdate.importDef = newImportDef;
|
|
912
|
+
currentData.data.push(itemDataToUpdate);
|
|
835
913
|
}
|
|
836
914
|
else {
|
|
837
915
|
// If no existing item matches, then add the new item
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Databases, Query } from "node-appwrite";
|
|
2
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
|
2
3
|
export const fetchAllDatabases = async (database) => {
|
|
3
|
-
const databases = await database.list([Query.limit(25)]);
|
|
4
|
+
const databases = await tryAwaitWithRetry(async () => await database.list([Query.limit(25)]));
|
|
4
5
|
const allDatabases = databases.databases;
|
|
5
6
|
let lastDatabaseId = allDatabases[allDatabases.length - 1].$id;
|
|
6
7
|
if (databases.databases.length < 25) {
|