appwrite-utils-cli 0.0.36 → 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 +2 -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/utils/setupFiles.js +4 -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/utils/setupFiles.ts +4 -0
- package/src/utilsController.ts +3 -1
|
@@ -93,6 +93,7 @@ export class DataLoader {
|
|
|
93
93
|
* It iterates through the target object's keys and updates the source object if:
|
|
94
94
|
* - The source object has the key.
|
|
95
95
|
* - The target object's value for that key is not null, undefined, or an empty string.
|
|
96
|
+
* - If the target object has an array value, it concatenates the values and removes duplicates.
|
|
96
97
|
*
|
|
97
98
|
* @param source - The source object to be updated.
|
|
98
99
|
* @param target - The target object with values to update the source object.
|
|
@@ -162,8 +163,26 @@ export class DataLoader {
|
|
|
162
163
|
// Method to generate a unique ID that doesn't conflict with existing IDs
|
|
163
164
|
getTrueUniqueId(collectionName: string) {
|
|
164
165
|
let newId = ID.unique();
|
|
165
|
-
|
|
166
|
+
let condition =
|
|
167
|
+
this.checkMapValuesForId(newId, collectionName) ||
|
|
168
|
+
this.userExistsMap.has(newId) ||
|
|
169
|
+
this.importMap
|
|
170
|
+
.get(this.getCollectionKey("users"))
|
|
171
|
+
?.data.some(
|
|
172
|
+
(user) =>
|
|
173
|
+
user.finalData.docId === newId || user.finalData.userId === newId
|
|
174
|
+
);
|
|
175
|
+
while (condition) {
|
|
166
176
|
newId = ID.unique();
|
|
177
|
+
condition =
|
|
178
|
+
this.checkMapValuesForId(newId, collectionName) ||
|
|
179
|
+
this.userExistsMap.has(newId) ||
|
|
180
|
+
this.importMap
|
|
181
|
+
.get(this.getCollectionKey("users"))
|
|
182
|
+
?.data.some(
|
|
183
|
+
(user) =>
|
|
184
|
+
user.finalData.docId === newId || user.finalData.userId === newId
|
|
185
|
+
);
|
|
167
186
|
}
|
|
168
187
|
return newId;
|
|
169
188
|
}
|
|
@@ -271,6 +290,21 @@ export class DataLoader {
|
|
|
271
290
|
this.phoneToUserIdMap.set(user.phone, user.$id);
|
|
272
291
|
}
|
|
273
292
|
this.userExistsMap.set(user.$id, true);
|
|
293
|
+
let importData = this.importMap.get(this.getCollectionKey("users"));
|
|
294
|
+
if (!importData) {
|
|
295
|
+
importData = {
|
|
296
|
+
data: [],
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
importData.data.push({
|
|
300
|
+
finalData: {
|
|
301
|
+
...user,
|
|
302
|
+
userId: user.$id,
|
|
303
|
+
docId: user.$id,
|
|
304
|
+
},
|
|
305
|
+
rawData: user,
|
|
306
|
+
});
|
|
307
|
+
this.importMap.set(this.getCollectionKey("users"), importData);
|
|
274
308
|
}
|
|
275
309
|
return allUsers;
|
|
276
310
|
}
|
|
@@ -312,15 +346,11 @@ export class DataLoader {
|
|
|
312
346
|
for (const createDef of createDefs) {
|
|
313
347
|
if (!isUsersCollection) {
|
|
314
348
|
console.log(`${collection.name} is not users collection`);
|
|
315
|
-
|
|
349
|
+
this.prepareCreateData(db, collection, createDef);
|
|
316
350
|
} else {
|
|
317
351
|
// Special handling for users collection if needed
|
|
318
352
|
console.log(`${collection.name} is users collection`);
|
|
319
|
-
|
|
320
|
-
db,
|
|
321
|
-
collection,
|
|
322
|
-
createDef
|
|
323
|
-
);
|
|
353
|
+
this.prepareUserCollectionCreateData(db, collection, createDef);
|
|
324
354
|
}
|
|
325
355
|
}
|
|
326
356
|
for (const updateDef of updateDefs) {
|
|
@@ -331,11 +361,11 @@ export class DataLoader {
|
|
|
331
361
|
continue;
|
|
332
362
|
}
|
|
333
363
|
// Prepare the update data for the collection
|
|
334
|
-
|
|
364
|
+
this.prepareUpdateData(db, collection, updateDef);
|
|
335
365
|
}
|
|
336
366
|
}
|
|
337
367
|
console.log("Running update references");
|
|
338
|
-
this.dealWithMergedUsers();
|
|
368
|
+
// this.dealWithMergedUsers();
|
|
339
369
|
this.updateOldReferencesForNew();
|
|
340
370
|
console.log("Done running update references");
|
|
341
371
|
}
|
|
@@ -355,9 +385,15 @@ export class DataLoader {
|
|
|
355
385
|
this.config.usersCollectionName
|
|
356
386
|
);
|
|
357
387
|
const usersCollectionPrimaryKeyFields = new Set();
|
|
388
|
+
|
|
358
389
|
if (!this.config.collections) {
|
|
390
|
+
console.log("No collections found in configuration.");
|
|
359
391
|
return;
|
|
360
392
|
}
|
|
393
|
+
|
|
394
|
+
let needsUpdate = false;
|
|
395
|
+
let numUpdates = 0;
|
|
396
|
+
|
|
361
397
|
// Collect primary key fields from the users collection definitions
|
|
362
398
|
this.config.collections.forEach((collection) => {
|
|
363
399
|
if (this.getCollectionKey(collection.name) === usersCollectionKey) {
|
|
@@ -373,45 +409,95 @@ export class DataLoader {
|
|
|
373
409
|
}
|
|
374
410
|
});
|
|
375
411
|
|
|
412
|
+
console.log(
|
|
413
|
+
`Primary key fields collected for users collection: ${[
|
|
414
|
+
...usersCollectionPrimaryKeyFields,
|
|
415
|
+
]}`
|
|
416
|
+
);
|
|
417
|
+
|
|
376
418
|
// Iterate over all collections to update references based on merged users
|
|
377
419
|
this.config.collections.forEach((collection) => {
|
|
378
420
|
const collectionData = this.importMap.get(
|
|
379
421
|
this.getCollectionKey(collection.name)
|
|
380
422
|
);
|
|
381
|
-
|
|
423
|
+
|
|
424
|
+
if (!collectionData || !collectionData.data) {
|
|
425
|
+
console.log(`No data found for collection ${collection.name}`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
382
429
|
const collectionImportDefs = collection.importDefs;
|
|
383
430
|
if (!collectionImportDefs || !collectionImportDefs.length) {
|
|
431
|
+
console.log(
|
|
432
|
+
`No import definitions found for collection ${collection.name}`
|
|
433
|
+
);
|
|
384
434
|
return;
|
|
385
435
|
}
|
|
436
|
+
|
|
386
437
|
collectionImportDefs.forEach((importDef) => {
|
|
387
438
|
importDef.idMappings?.forEach((idMapping) => {
|
|
388
439
|
if (
|
|
389
440
|
this.getCollectionKey(idMapping.targetCollection) ===
|
|
390
441
|
usersCollectionKey
|
|
391
442
|
) {
|
|
443
|
+
const fieldToSetKey = idMapping.fieldToSet || idMapping.sourceField;
|
|
392
444
|
const targetFieldKey =
|
|
393
445
|
idMapping.targetFieldToMatch || idMapping.targetField;
|
|
446
|
+
|
|
394
447
|
if (usersCollectionPrimaryKeyFields.has(targetFieldKey)) {
|
|
448
|
+
console.log(
|
|
449
|
+
`Processing collection ${collection.name} with target field ${targetFieldKey}`
|
|
450
|
+
);
|
|
451
|
+
|
|
395
452
|
// Process each item in the collection
|
|
396
453
|
collectionData.data.forEach((item) => {
|
|
397
|
-
const oldId =
|
|
454
|
+
const oldId =
|
|
455
|
+
item.finalData[idMapping.sourceField] ||
|
|
456
|
+
item.context[idMapping.sourceField];
|
|
457
|
+
|
|
458
|
+
if (oldId === undefined || oldId === null) {
|
|
459
|
+
console.log(
|
|
460
|
+
`Skipping item with undefined or null oldId in collection ${collection.name}`
|
|
461
|
+
);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
398
465
|
const newId = this.mergedUserMap.get(`${oldId}`);
|
|
399
466
|
|
|
400
467
|
if (newId) {
|
|
468
|
+
needsUpdate = true;
|
|
469
|
+
numUpdates++;
|
|
470
|
+
console.log(
|
|
471
|
+
`Updating old ID ${oldId} to new ID ${newId} in collection ${collection.name}`
|
|
472
|
+
);
|
|
473
|
+
|
|
401
474
|
// Update context to use new user ID
|
|
402
|
-
item.finalData[
|
|
403
|
-
|
|
404
|
-
|
|
475
|
+
item.finalData[fieldToSetKey] = newId;
|
|
476
|
+
item.context[fieldToSetKey] = newId;
|
|
477
|
+
} else {
|
|
478
|
+
console.log(
|
|
479
|
+
`No new ID found for old ID ${oldId} in mergedUserMap.`
|
|
480
|
+
);
|
|
405
481
|
}
|
|
406
482
|
});
|
|
407
483
|
}
|
|
408
484
|
}
|
|
409
485
|
});
|
|
410
486
|
});
|
|
487
|
+
|
|
488
|
+
if (needsUpdate) {
|
|
489
|
+
console.log(
|
|
490
|
+
`Updated ${numUpdates} references for collection ${collection.name}`
|
|
491
|
+
);
|
|
492
|
+
this.importMap.set(
|
|
493
|
+
this.getCollectionKey(collection.name),
|
|
494
|
+
collectionData
|
|
495
|
+
);
|
|
496
|
+
}
|
|
411
497
|
});
|
|
412
498
|
}
|
|
413
499
|
|
|
414
|
-
|
|
500
|
+
updateOldReferencesForNew() {
|
|
415
501
|
if (!this.config.collections) {
|
|
416
502
|
return;
|
|
417
503
|
}
|
|
@@ -612,12 +698,19 @@ export class DataLoader {
|
|
|
612
698
|
* @param attributeMappings - The attribute mappings for the item.
|
|
613
699
|
* @returns The transformed item with user-specific keys removed.
|
|
614
700
|
*/
|
|
615
|
-
|
|
701
|
+
prepareUserData(
|
|
616
702
|
item: any,
|
|
617
703
|
attributeMappings: AttributeMappings,
|
|
618
704
|
primaryKeyField: string,
|
|
619
705
|
newId: string
|
|
620
|
-
):
|
|
706
|
+
): {
|
|
707
|
+
transformedItem: any;
|
|
708
|
+
existingId: string | undefined;
|
|
709
|
+
userData: {
|
|
710
|
+
rawData: any;
|
|
711
|
+
finalData: z.infer<typeof AuthUserCreateSchema>;
|
|
712
|
+
};
|
|
713
|
+
} {
|
|
621
714
|
let transformedItem = this.transformData(item, attributeMappings);
|
|
622
715
|
const userData = AuthUserCreateSchema.safeParse(transformedItem);
|
|
623
716
|
if (!userData.success) {
|
|
@@ -666,15 +759,24 @@ export class DataLoader {
|
|
|
666
759
|
});
|
|
667
760
|
if (userFound) {
|
|
668
761
|
userFound.finalData.userId = existingId;
|
|
762
|
+
userFound.finalData.docId = existingId;
|
|
763
|
+
this.userExistsMap.set(existingId, true);
|
|
669
764
|
}
|
|
670
|
-
|
|
765
|
+
|
|
766
|
+
const userKeys = ["email", "phone", "name", "labels", "prefs"];
|
|
767
|
+
userKeys.forEach((key) => {
|
|
768
|
+
if (transformedItem.hasOwnProperty(key)) {
|
|
769
|
+
delete transformedItem[key];
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
return {
|
|
671
773
|
transformedItem,
|
|
672
774
|
existingId,
|
|
673
|
-
{
|
|
775
|
+
userData: {
|
|
674
776
|
rawData: userFound?.rawData,
|
|
675
777
|
finalData: userFound?.finalData,
|
|
676
778
|
},
|
|
677
|
-
|
|
779
|
+
};
|
|
678
780
|
} else {
|
|
679
781
|
existingId = newId;
|
|
680
782
|
userData.data.userId = existingId;
|
|
@@ -696,7 +798,11 @@ export class DataLoader {
|
|
|
696
798
|
data: [...(usersMap?.data || []), userDataToAdd],
|
|
697
799
|
});
|
|
698
800
|
|
|
699
|
-
return
|
|
801
|
+
return {
|
|
802
|
+
transformedItem,
|
|
803
|
+
existingId,
|
|
804
|
+
userData: userDataToAdd,
|
|
805
|
+
};
|
|
700
806
|
}
|
|
701
807
|
|
|
702
808
|
/**
|
|
@@ -708,11 +814,11 @@ export class DataLoader {
|
|
|
708
814
|
* @param collection - The collection configuration.
|
|
709
815
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
710
816
|
*/
|
|
711
|
-
|
|
817
|
+
prepareUserCollectionCreateData(
|
|
712
818
|
db: ConfigDatabase,
|
|
713
819
|
collection: CollectionCreate,
|
|
714
820
|
importDef: ImportDef
|
|
715
|
-
):
|
|
821
|
+
): void {
|
|
716
822
|
// Load the raw data based on the import definition
|
|
717
823
|
const rawData = this.loadData(importDef);
|
|
718
824
|
const operationId = this.collectionImportOperations.get(
|
|
@@ -736,127 +842,157 @@ export class DataLoader {
|
|
|
736
842
|
`No import operation found for collection ${collection.name}`
|
|
737
843
|
);
|
|
738
844
|
}
|
|
739
|
-
|
|
845
|
+
updateOperation(this.database, operationId, {
|
|
740
846
|
status: "ready",
|
|
741
847
|
total: rawData.length,
|
|
742
|
-
})
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
this.getCollectionKey(collection.name)
|
|
747
|
-
);
|
|
748
|
-
// Log errors if the necessary data is not found in the import map
|
|
749
|
-
if (!currentUserData) {
|
|
750
|
-
logger.error(
|
|
751
|
-
`No data found for collection ${"users"} for createDef but it says it's supposed to have one...`
|
|
848
|
+
}).then(() => {
|
|
849
|
+
// Retrieve the current user data and the current collection data from the import map
|
|
850
|
+
const currentUserData = this.importMap.get(
|
|
851
|
+
this.getCollectionKey("users")
|
|
752
852
|
);
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
logger.error(
|
|
756
|
-
`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`
|
|
757
|
-
);
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
// Iterate through each item in the raw data
|
|
761
|
-
for (const item of rawData) {
|
|
762
|
-
// Prepare user data, check for duplicates, and remove user-specific keys
|
|
763
|
-
let [transformedItem, existingId, userData] = await this.prepareUserData(
|
|
764
|
-
item,
|
|
765
|
-
importDef.attributeMappings,
|
|
766
|
-
importDef.primaryKeyField,
|
|
767
|
-
this.getTrueUniqueId(this.getCollectionKey("users"))
|
|
853
|
+
const currentData = this.importMap.get(
|
|
854
|
+
this.getCollectionKey(collection.name)
|
|
768
855
|
);
|
|
856
|
+
// Log errors if the necessary data is not found in the import map
|
|
857
|
+
if (!currentUserData) {
|
|
858
|
+
logger.error(
|
|
859
|
+
`No data found for collection ${"users"} for createDef but it says it's supposed to have one...`
|
|
860
|
+
);
|
|
861
|
+
return;
|
|
862
|
+
} else if (!currentData) {
|
|
863
|
+
logger.error(
|
|
864
|
+
`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`
|
|
865
|
+
);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
// Iterate through each item in the raw data
|
|
869
|
+
for (const item of rawData) {
|
|
870
|
+
// Prepare user data, check for duplicates, and remove user-specific keys
|
|
871
|
+
let { transformedItem, existingId, userData } = this.prepareUserData(
|
|
872
|
+
item,
|
|
873
|
+
importDef.attributeMappings,
|
|
874
|
+
importDef.primaryKeyField,
|
|
875
|
+
this.getTrueUniqueId(this.getCollectionKey("users"))
|
|
876
|
+
);
|
|
769
877
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
878
|
+
logger.info(
|
|
879
|
+
`In create user -- transformedItem: ${JSON.stringify(
|
|
880
|
+
transformedItem,
|
|
881
|
+
null,
|
|
882
|
+
2
|
|
883
|
+
)}`
|
|
884
|
+
);
|
|
777
885
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
886
|
+
// Generate a new unique ID for the item or use existing ID
|
|
887
|
+
if (!existingId) {
|
|
888
|
+
// No existing user ID, generate a new unique ID
|
|
889
|
+
existingId = this.getTrueUniqueId(this.getCollectionKey("users"));
|
|
890
|
+
transformedItem.docId = existingId; // Assign the new ID to the transformed data's docId field
|
|
891
|
+
}
|
|
784
892
|
|
|
785
|
-
|
|
786
|
-
|
|
893
|
+
// Create a context object for the item, including the new ID
|
|
894
|
+
let context = this.createContext(db, collection, item, existingId);
|
|
787
895
|
|
|
788
|
-
|
|
789
|
-
|
|
896
|
+
// Merge the transformed data into the context
|
|
897
|
+
context = { ...context, ...transformedItem, ...userData.finalData };
|
|
790
898
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
899
|
+
// If a primary key field is defined, handle the ID mapping and check for duplicates
|
|
900
|
+
if (importDef.primaryKeyField) {
|
|
901
|
+
const oldId = item[importDef.primaryKeyField];
|
|
794
902
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
903
|
+
// Check if the oldId already exists to handle potential duplicates
|
|
904
|
+
if (
|
|
905
|
+
this.oldIdToNewIdPerCollectionMap
|
|
906
|
+
.get(this.getCollectionKey(collection.name))
|
|
907
|
+
?.has(`${oldId}`)
|
|
908
|
+
) {
|
|
909
|
+
// Found a duplicate oldId, now decide how to merge or handle these duplicates
|
|
910
|
+
for (const data of currentData.data) {
|
|
911
|
+
if (
|
|
912
|
+
data.finalData.docId === oldId ||
|
|
913
|
+
data.finalData.userId === oldId
|
|
914
|
+
) {
|
|
915
|
+
transformedItem = this.mergeObjects(
|
|
916
|
+
data.finalData,
|
|
917
|
+
transformedItem
|
|
918
|
+
);
|
|
919
|
+
}
|
|
811
920
|
}
|
|
921
|
+
} else {
|
|
922
|
+
// No duplicate found, simply map the oldId to the new itemId
|
|
923
|
+
collectionOldIdToNewIdMap?.set(`${oldId}`, `${existingId}`);
|
|
812
924
|
}
|
|
813
|
-
} else {
|
|
814
|
-
// No duplicate found, simply map the oldId to the new itemId
|
|
815
|
-
collectionOldIdToNewIdMap?.set(`${oldId}`, `${existingId}`);
|
|
816
925
|
}
|
|
817
|
-
}
|
|
818
|
-
// Merge the final user data into the context
|
|
819
|
-
context = { ...context, ...userData.finalData };
|
|
820
926
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
927
|
+
// Handle merging for currentUserData
|
|
928
|
+
for (let i = 0; i < currentUserData.data.length; i++) {
|
|
929
|
+
const currentUserDataItem = currentUserData.data[i];
|
|
930
|
+
const samePhones =
|
|
931
|
+
currentUserDataItem.finalData.phone &&
|
|
932
|
+
transformedItem.phone &&
|
|
933
|
+
currentUserDataItem.finalData.phone === transformedItem.phone;
|
|
934
|
+
const sameEmails =
|
|
935
|
+
currentUserDataItem.finalData.email &&
|
|
936
|
+
transformedItem.email &&
|
|
937
|
+
currentUserDataItem.finalData.email === transformedItem.email;
|
|
938
|
+
if (
|
|
939
|
+
(currentUserDataItem.finalData.docId === existingId ||
|
|
940
|
+
currentUserDataItem.finalData.userId === existingId) &&
|
|
941
|
+
(samePhones || sameEmails) &&
|
|
942
|
+
currentUserDataItem.finalData &&
|
|
830
943
|
userData.finalData
|
|
831
|
-
)
|
|
832
|
-
|
|
833
|
-
|
|
944
|
+
) {
|
|
945
|
+
const userDataMerged = this.mergeObjects(
|
|
946
|
+
currentUserData.data[i].finalData,
|
|
947
|
+
userData.finalData
|
|
948
|
+
);
|
|
949
|
+
currentUserData.data[i].finalData = userDataMerged;
|
|
950
|
+
this.importMap.set(this.getCollectionKey("users"), currentUserData);
|
|
951
|
+
}
|
|
834
952
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
};
|
|
953
|
+
// Update the attribute mappings with any actions that need to be performed post-import
|
|
954
|
+
const mappingsWithActions = this.getAttributeMappingsWithActions(
|
|
955
|
+
importDef.attributeMappings,
|
|
956
|
+
context,
|
|
957
|
+
transformedItem
|
|
958
|
+
);
|
|
959
|
+
// Update the import definition with the new attribute mappings
|
|
960
|
+
const newImportDef = {
|
|
961
|
+
...importDef,
|
|
962
|
+
attributeMappings: mappingsWithActions,
|
|
963
|
+
};
|
|
847
964
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
965
|
+
let foundData = false;
|
|
966
|
+
for (let i = 0; i < currentData.data.length; i++) {
|
|
967
|
+
if (
|
|
968
|
+
currentData.data[i].finalData.docId === existingId ||
|
|
969
|
+
currentData.data[i].finalData.userId === existingId
|
|
970
|
+
) {
|
|
971
|
+
currentData.data[i].finalData = this.mergeObjects(
|
|
972
|
+
currentData.data[i].finalData,
|
|
973
|
+
transformedItem
|
|
974
|
+
);
|
|
975
|
+
currentData.data[i].context = context;
|
|
976
|
+
currentData.data[i].importDef = newImportDef;
|
|
977
|
+
this.importMap.set(
|
|
978
|
+
this.getCollectionKey(collection.name),
|
|
979
|
+
currentData
|
|
980
|
+
);
|
|
981
|
+
this.oldIdToNewIdPerCollectionMap.set(
|
|
982
|
+
this.getCollectionKey(collection.name),
|
|
983
|
+
collectionOldIdToNewIdMap!
|
|
984
|
+
);
|
|
985
|
+
foundData = true;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (!foundData) {
|
|
989
|
+
// Add new data to the associated collection
|
|
990
|
+
currentData.data.push({
|
|
991
|
+
rawData: item,
|
|
992
|
+
context: context,
|
|
993
|
+
importDef: newImportDef,
|
|
994
|
+
finalData: transformedItem,
|
|
995
|
+
});
|
|
860
996
|
this.importMap.set(
|
|
861
997
|
this.getCollectionKey(collection.name),
|
|
862
998
|
currentData
|
|
@@ -865,24 +1001,9 @@ export class DataLoader {
|
|
|
865
1001
|
this.getCollectionKey(collection.name),
|
|
866
1002
|
collectionOldIdToNewIdMap!
|
|
867
1003
|
);
|
|
868
|
-
foundData = true;
|
|
869
1004
|
}
|
|
870
1005
|
}
|
|
871
|
-
|
|
872
|
-
// Add new data to the associated collection
|
|
873
|
-
currentData.data.push({
|
|
874
|
-
rawData: item,
|
|
875
|
-
context: context,
|
|
876
|
-
importDef: newImportDef,
|
|
877
|
-
finalData: transformedItem,
|
|
878
|
-
});
|
|
879
|
-
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
880
|
-
this.oldIdToNewIdPerCollectionMap.set(
|
|
881
|
-
this.getCollectionKey(collection.name),
|
|
882
|
-
collectionOldIdToNewIdMap!
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
1006
|
+
});
|
|
886
1007
|
}
|
|
887
1008
|
|
|
888
1009
|
/**
|
|
@@ -893,11 +1014,11 @@ export class DataLoader {
|
|
|
893
1014
|
* @param collection - The collection configuration.
|
|
894
1015
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
895
1016
|
*/
|
|
896
|
-
|
|
1017
|
+
prepareCreateData(
|
|
897
1018
|
db: ConfigDatabase,
|
|
898
1019
|
collection: CollectionCreate,
|
|
899
1020
|
importDef: ImportDef
|
|
900
|
-
):
|
|
1021
|
+
): void {
|
|
901
1022
|
// Load the raw data based on the import definition
|
|
902
1023
|
const rawData = this.loadData(importDef);
|
|
903
1024
|
const operationId = this.collectionImportOperations.get(
|
|
@@ -908,94 +1029,99 @@ export class DataLoader {
|
|
|
908
1029
|
`No import operation found for collection ${collection.name}`
|
|
909
1030
|
);
|
|
910
1031
|
}
|
|
911
|
-
|
|
1032
|
+
updateOperation(this.database, operationId, {
|
|
912
1033
|
status: "ready",
|
|
913
1034
|
total: rawData.length,
|
|
914
|
-
})
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
);
|
|
928
|
-
// Iterate through each item in the raw data
|
|
929
|
-
for (const item of rawData) {
|
|
930
|
-
// Generate a new unique ID for the item
|
|
931
|
-
const itemIdNew = this.getTrueUniqueId(
|
|
932
|
-
this.getCollectionKey(collection.name)
|
|
933
|
-
);
|
|
934
|
-
// Retrieve the current collection data from the import map
|
|
935
|
-
const currentData = this.importMap.get(
|
|
936
|
-
this.getCollectionKey(collection.name)
|
|
937
|
-
);
|
|
938
|
-
// Create a context object for the item, including the new ID
|
|
939
|
-
let context = this.createContext(db, collection, item, itemIdNew);
|
|
940
|
-
// Transform the item data based on the attribute mappings
|
|
941
|
-
const transformedData = this.transformData(
|
|
942
|
-
item,
|
|
943
|
-
importDef.attributeMappings
|
|
1035
|
+
}).then(() => {
|
|
1036
|
+
// Initialize a new map for old ID to new ID mappings
|
|
1037
|
+
const oldIdToNewIdMapNew = new Map<string, string>();
|
|
1038
|
+
// Retrieve or initialize the collection-specific old ID to new ID map
|
|
1039
|
+
const collectionOldIdToNewIdMap =
|
|
1040
|
+
this.oldIdToNewIdPerCollectionMap.get(
|
|
1041
|
+
this.getCollectionKey(collection.name)
|
|
1042
|
+
) ||
|
|
1043
|
+
this.oldIdToNewIdPerCollectionMap
|
|
1044
|
+
.set(this.getCollectionKey(collection.name), oldIdToNewIdMapNew)
|
|
1045
|
+
.get(this.getCollectionKey(collection.name));
|
|
1046
|
+
console.log(
|
|
1047
|
+
`${collection.name} -- collectionOldIdToNewIdMap: ${collectionOldIdToNewIdMap}`
|
|
944
1048
|
);
|
|
945
|
-
//
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1049
|
+
// Iterate through each item in the raw data
|
|
1050
|
+
for (const item of rawData) {
|
|
1051
|
+
// Generate a new unique ID for the item
|
|
1052
|
+
const itemIdNew = this.getTrueUniqueId(
|
|
1053
|
+
this.getCollectionKey(collection.name)
|
|
1054
|
+
);
|
|
1055
|
+
// Retrieve the current collection data from the import map
|
|
1056
|
+
const currentData = this.importMap.get(
|
|
1057
|
+
this.getCollectionKey(collection.name)
|
|
1058
|
+
);
|
|
1059
|
+
// Create a context object for the item, including the new ID
|
|
1060
|
+
let context = this.createContext(db, collection, item, itemIdNew);
|
|
1061
|
+
// Transform the item data based on the attribute mappings
|
|
1062
|
+
const transformedData = this.transformData(
|
|
1063
|
+
item,
|
|
1064
|
+
importDef.attributeMappings
|
|
1065
|
+
);
|
|
1066
|
+
// If a primary key field is defined, handle the ID mapping and check for duplicates
|
|
1067
|
+
if (importDef.primaryKeyField) {
|
|
1068
|
+
const oldId = item[importDef.primaryKeyField];
|
|
1069
|
+
if (collectionOldIdToNewIdMap?.has(`${oldId}`)) {
|
|
1070
|
+
logger.error(
|
|
1071
|
+
`Collection ${collection.name} has multiple documents with the same primary key ${oldId}`
|
|
1072
|
+
);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
collectionOldIdToNewIdMap?.set(`${oldId}`, `${itemIdNew}`);
|
|
1076
|
+
}
|
|
1077
|
+
// Merge the transformed data into the context
|
|
1078
|
+
context = { ...context, ...transformedData };
|
|
1079
|
+
// Validate the item before proceeding
|
|
1080
|
+
const isValid = this.importDataActions.validateItem(
|
|
1081
|
+
transformedData,
|
|
1082
|
+
importDef.attributeMappings,
|
|
1083
|
+
context
|
|
1084
|
+
);
|
|
1085
|
+
if (!isValid) {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
// Update the attribute mappings with any actions that need to be performed post-import
|
|
1089
|
+
const mappingsWithActions = this.getAttributeMappingsWithActions(
|
|
1090
|
+
importDef.attributeMappings,
|
|
1091
|
+
context,
|
|
1092
|
+
transformedData
|
|
1093
|
+
);
|
|
1094
|
+
// Update the import definition with the new attribute mappings
|
|
1095
|
+
const newImportDef = {
|
|
1096
|
+
...importDef,
|
|
1097
|
+
attributeMappings: mappingsWithActions,
|
|
1098
|
+
};
|
|
1099
|
+
// If the current collection data exists, add the item with its context and final data
|
|
1100
|
+
if (currentData && currentData.data) {
|
|
1101
|
+
currentData.data.push({
|
|
1102
|
+
rawData: item,
|
|
1103
|
+
context: context,
|
|
1104
|
+
importDef: newImportDef,
|
|
1105
|
+
finalData: transformedData,
|
|
1106
|
+
});
|
|
1107
|
+
this.importMap.set(
|
|
1108
|
+
this.getCollectionKey(collection.name),
|
|
1109
|
+
currentData
|
|
1110
|
+
);
|
|
1111
|
+
this.oldIdToNewIdPerCollectionMap.set(
|
|
1112
|
+
this.getCollectionKey(collection.name),
|
|
1113
|
+
collectionOldIdToNewIdMap!
|
|
1114
|
+
);
|
|
1115
|
+
} else {
|
|
949
1116
|
logger.error(
|
|
950
|
-
`
|
|
1117
|
+
`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`
|
|
951
1118
|
);
|
|
952
1119
|
continue;
|
|
953
1120
|
}
|
|
954
|
-
collectionOldIdToNewIdMap?.set(`${oldId}`, `${itemIdNew}`);
|
|
955
1121
|
}
|
|
956
|
-
|
|
957
|
-
context = { ...context, ...transformedData };
|
|
958
|
-
// Validate the item before proceeding
|
|
959
|
-
const isValid = await this.importDataActions.validateItem(
|
|
960
|
-
transformedData,
|
|
961
|
-
importDef.attributeMappings,
|
|
962
|
-
context
|
|
963
|
-
);
|
|
964
|
-
if (!isValid) {
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
// Update the attribute mappings with any actions that need to be performed post-import
|
|
968
|
-
const mappingsWithActions = this.getAttributeMappingsWithActions(
|
|
969
|
-
importDef.attributeMappings,
|
|
970
|
-
context,
|
|
971
|
-
transformedData
|
|
972
|
-
);
|
|
973
|
-
// Update the import definition with the new attribute mappings
|
|
974
|
-
const newImportDef = {
|
|
975
|
-
...importDef,
|
|
976
|
-
attributeMappings: mappingsWithActions,
|
|
977
|
-
};
|
|
978
|
-
// If the current collection data exists, add the item with its context and final data
|
|
979
|
-
if (currentData && currentData.data) {
|
|
980
|
-
currentData.data.push({
|
|
981
|
-
rawData: item,
|
|
982
|
-
context: context,
|
|
983
|
-
importDef: newImportDef,
|
|
984
|
-
finalData: transformedData,
|
|
985
|
-
});
|
|
986
|
-
this.importMap.set(this.getCollectionKey(collection.name), currentData);
|
|
987
|
-
this.oldIdToNewIdPerCollectionMap.set(
|
|
988
|
-
this.getCollectionKey(collection.name),
|
|
989
|
-
collectionOldIdToNewIdMap!
|
|
990
|
-
);
|
|
991
|
-
} else {
|
|
992
|
-
logger.error(
|
|
993
|
-
`No data found for collection ${collection.name} for createDef but it says it's supposed to have one...`
|
|
994
|
-
);
|
|
995
|
-
continue;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
1122
|
+
});
|
|
998
1123
|
}
|
|
1124
|
+
|
|
999
1125
|
/**
|
|
1000
1126
|
* Prepares the data for updating documents within a collection.
|
|
1001
1127
|
* This method loads the raw data based on the import definition, transforms it according to the attribute mappings,
|
|
@@ -1006,7 +1132,7 @@ export class DataLoader {
|
|
|
1006
1132
|
* @param collection - The collection configuration.
|
|
1007
1133
|
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
1008
1134
|
*/
|
|
1009
|
-
|
|
1135
|
+
prepareUpdateData(
|
|
1010
1136
|
db: ConfigDatabase,
|
|
1011
1137
|
collection: CollectionCreate,
|
|
1012
1138
|
importDef: ImportDef
|
|
@@ -1047,11 +1173,7 @@ export class DataLoader {
|
|
|
1047
1173
|
let newId: string | undefined;
|
|
1048
1174
|
let oldId: string | undefined;
|
|
1049
1175
|
// Determine the new ID for the item based on the primary key field or update mapping
|
|
1050
|
-
|
|
1051
|
-
oldId = item[importDef.primaryKeyField];
|
|
1052
|
-
} else if (importDef.updateMapping) {
|
|
1053
|
-
oldId = item[importDef.updateMapping.originalIdField];
|
|
1054
|
-
}
|
|
1176
|
+
oldId = item[importDef.primaryKeyField];
|
|
1055
1177
|
if (oldId) {
|
|
1056
1178
|
newId = oldIdToNewIdMap?.get(`${oldId}`);
|
|
1057
1179
|
if (
|
|
@@ -1074,8 +1196,13 @@ export class DataLoader {
|
|
|
1074
1196
|
);
|
|
1075
1197
|
continue;
|
|
1076
1198
|
}
|
|
1199
|
+
const itemDataToUpdate = this.importMap
|
|
1200
|
+
.get(this.getCollectionKey(collection.name))
|
|
1201
|
+
?.data.find(
|
|
1202
|
+
(data) => `${data.context[importDef.primaryKeyField]}` === `${oldId}`
|
|
1203
|
+
);
|
|
1077
1204
|
// Log an error and continue to the next item if no new ID is found
|
|
1078
|
-
if (!newId) {
|
|
1205
|
+
if (!newId && !itemDataToUpdate) {
|
|
1079
1206
|
logger.error(
|
|
1080
1207
|
`No new id found for collection ${
|
|
1081
1208
|
collection.name
|
|
@@ -1086,15 +1213,24 @@ export class DataLoader {
|
|
|
1086
1213
|
)} but it says it's supposed to have one...`
|
|
1087
1214
|
);
|
|
1088
1215
|
continue;
|
|
1216
|
+
} else if (itemDataToUpdate) {
|
|
1217
|
+
newId = itemDataToUpdate.finalData.docId;
|
|
1218
|
+
if (!newId) {
|
|
1219
|
+
logger.error(
|
|
1220
|
+
`No new id found for collection ${
|
|
1221
|
+
collection.name
|
|
1222
|
+
} for updateDef ${JSON.stringify(
|
|
1223
|
+
item,
|
|
1224
|
+
null,
|
|
1225
|
+
2
|
|
1226
|
+
)} but it says it's supposed to have one...`
|
|
1227
|
+
);
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1089
1230
|
}
|
|
1090
|
-
|
|
1091
|
-
.get(this.getCollectionKey(collection.name))
|
|
1092
|
-
?.data.find(
|
|
1093
|
-
(data) => data.rawData[importDef.primaryKeyField] === oldId
|
|
1094
|
-
);
|
|
1095
|
-
if (!itemDataToUpdate) {
|
|
1231
|
+
if (!itemDataToUpdate || !newId) {
|
|
1096
1232
|
logger.error(
|
|
1097
|
-
`No data found for collection ${
|
|
1233
|
+
`No data or ID (docId) found for collection ${
|
|
1098
1234
|
collection.name
|
|
1099
1235
|
} for updateDef ${JSON.stringify(
|
|
1100
1236
|
item,
|
|
@@ -1110,9 +1246,9 @@ export class DataLoader {
|
|
|
1110
1246
|
);
|
|
1111
1247
|
// Create a context object for the item, including the new ID and transformed data
|
|
1112
1248
|
let context = this.createContext(db, collection, item, newId);
|
|
1113
|
-
context =
|
|
1249
|
+
context = { ...context, ...transformedData };
|
|
1114
1250
|
// Validate the item before proceeding
|
|
1115
|
-
const isValid =
|
|
1251
|
+
const isValid = this.importDataActions.validateItem(
|
|
1116
1252
|
item,
|
|
1117
1253
|
importDef.attributeMappings,
|
|
1118
1254
|
context
|
|
@@ -1144,6 +1280,7 @@ export class DataLoader {
|
|
|
1144
1280
|
);
|
|
1145
1281
|
itemDataToUpdate.context = context;
|
|
1146
1282
|
itemDataToUpdate.importDef = newImportDef;
|
|
1283
|
+
currentData!.data.push(itemDataToUpdate);
|
|
1147
1284
|
} else {
|
|
1148
1285
|
// If no existing item matches, then add the new item
|
|
1149
1286
|
currentData!.data.push({
|