appwrite-utils-cli 0.0.52 → 0.0.54
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/backup.d.ts +2 -0
- package/dist/migrations/collections.js +1 -1
- package/dist/migrations/converters.d.ts +1 -0
- package/dist/migrations/converters.js +4 -1
- package/dist/migrations/dataLoader.d.ts +32 -9
- package/dist/migrations/dataLoader.js +54 -78
- package/dist/migrations/migrationHelper.d.ts +5 -0
- package/dist/migrations/setupDatabase.js +3 -0
- package/dist/utils/helperFunctions.js +1 -0
- package/package.json +2 -2
- package/src/migrations/collections.ts +1 -3
- package/src/migrations/converters.ts +3 -1
- package/src/migrations/dataLoader.ts +57 -117
- package/src/migrations/setupDatabase.ts +3 -0
- package/src/utils/helperFunctions.ts +1 -0
package/README.md
CHANGED
|
@@ -132,6 +132,7 @@ This setup ensures that developers have robust tools at their fingertips to mana
|
|
|
132
132
|
|
|
133
133
|
### Changelog
|
|
134
134
|
|
|
135
|
+
- 0.0.54: Various fixes in here
|
|
135
136
|
- 0.0.50: Actually fixed the slight bug, it was really in the `mergeObjects`
|
|
136
137
|
- 0.0.49: Fixed a slight bug with `dataLoader` not mapping updates correctly with `updateMapping`
|
|
137
138
|
- 0.0.48: Added `--transfer`, `--fromdb <targetDatabaseId>`, `--targetdb <targetDatabaseId>`, `--transferendpoint <transferEndpoint>`, `--transferproject <transferProjectId>`, `--transferkey <transferApiKey>`. Additionally, I've added `--fromcoll <collectionId>` and `--targetcoll <collectionId>`. These allow you to do a few things. First, you can now transfer databases in the same project, and from local to a remote project. Second, you can now specify specific collections to transfer from one place to another, with all of their data. If `--fromcoll` and `--targetcoll` are ommitted, it will transfer the databases. During the database transfer, it will create any missing collections, attributes, and indices.
|
|
@@ -295,6 +295,7 @@ export declare const getMigrationCollectionSchemas: () => {
|
|
|
295
295
|
targetKey: string;
|
|
296
296
|
oldKey?: string | undefined;
|
|
297
297
|
oldKeys?: string[] | undefined;
|
|
298
|
+
valueToSet?: any;
|
|
298
299
|
fileData?: {
|
|
299
300
|
path: string;
|
|
300
301
|
name: string;
|
|
@@ -548,6 +549,7 @@ export declare const getMigrationCollectionSchemas: () => {
|
|
|
548
549
|
targetKey: string;
|
|
549
550
|
oldKey?: string | undefined;
|
|
550
551
|
oldKeys?: string[] | undefined;
|
|
552
|
+
valueToSet?: any;
|
|
551
553
|
fileData?: {
|
|
552
554
|
path: string;
|
|
553
555
|
name: string;
|
|
@@ -88,7 +88,7 @@ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) =>
|
|
|
88
88
|
};
|
|
89
89
|
export const wipeDatabase = async (database, databaseId) => {
|
|
90
90
|
console.log(`Wiping database: ${databaseId}`);
|
|
91
|
-
const
|
|
91
|
+
const existingCollections = await fetchAllCollections(databaseId, database);
|
|
92
92
|
let collectionsDeleted = [];
|
|
93
93
|
for (const { $id: collectionId, name: name } of existingCollections) {
|
|
94
94
|
console.log(`Deleting collection: ${collectionId}`);
|
|
@@ -89,7 +89,10 @@ export const convertObjectByAttributeMappings = (obj, attributeMappings) => {
|
|
|
89
89
|
return current;
|
|
90
90
|
};
|
|
91
91
|
for (const mapping of attributeMappings) {
|
|
92
|
-
if (
|
|
92
|
+
if (mapping.valueToSet !== undefined) {
|
|
93
|
+
result[mapping.targetKey] = mapping.valueToSet;
|
|
94
|
+
}
|
|
95
|
+
else if (Array.isArray(mapping.oldKeys)) {
|
|
93
96
|
// Collect and flatten values from multiple oldKeys
|
|
94
97
|
const values = mapping.oldKeys
|
|
95
98
|
.map((oldKey) => resolveValue(obj, oldKey))
|
|
@@ -192,14 +192,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
192
192
|
type: z.ZodLiteral<"ip">;
|
|
193
193
|
error: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
194
194
|
required: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
195
|
-
array: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
196
|
-
* Prepares the data for creating documents in a collection.
|
|
197
|
-
* This involves loading the data, transforming it, and handling ID mappings.
|
|
198
|
-
*
|
|
199
|
-
* @param db - The database configuration.
|
|
200
|
-
* @param collection - The collection configuration.
|
|
201
|
-
* @param importDef - The import definition containing the attribute mappings and other relevant info.
|
|
202
|
-
*/
|
|
195
|
+
array: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
203
196
|
xdefault: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
204
197
|
description: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
|
|
205
198
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -401,6 +394,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
401
394
|
oldKey: z.ZodOptional<z.ZodString>;
|
|
402
395
|
oldKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
403
396
|
targetKey: z.ZodString;
|
|
397
|
+
valueToSet: z.ZodOptional<z.ZodAny>;
|
|
404
398
|
fileData: z.ZodOptional<z.ZodObject<{
|
|
405
399
|
name: z.ZodString;
|
|
406
400
|
path: z.ZodString;
|
|
@@ -436,6 +430,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
436
430
|
targetKey: string;
|
|
437
431
|
oldKey?: string | undefined;
|
|
438
432
|
oldKeys?: string[] | undefined;
|
|
433
|
+
valueToSet?: any;
|
|
439
434
|
fileData?: {
|
|
440
435
|
path: string;
|
|
441
436
|
name: string;
|
|
@@ -453,6 +448,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
453
448
|
targetKey: string;
|
|
454
449
|
oldKey?: string | undefined;
|
|
455
450
|
oldKeys?: string[] | undefined;
|
|
451
|
+
valueToSet?: any;
|
|
456
452
|
fileData?: {
|
|
457
453
|
path: string;
|
|
458
454
|
name: string;
|
|
@@ -474,6 +470,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
474
470
|
targetKey: string;
|
|
475
471
|
oldKey?: string | undefined;
|
|
476
472
|
oldKeys?: string[] | undefined;
|
|
473
|
+
valueToSet?: any;
|
|
477
474
|
fileData?: {
|
|
478
475
|
path: string;
|
|
479
476
|
name: string;
|
|
@@ -507,6 +504,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
507
504
|
targetKey: string;
|
|
508
505
|
oldKey?: string | undefined;
|
|
509
506
|
oldKeys?: string[] | undefined;
|
|
507
|
+
valueToSet?: any;
|
|
510
508
|
fileData?: {
|
|
511
509
|
path: string;
|
|
512
510
|
name: string;
|
|
@@ -657,6 +655,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
657
655
|
targetKey: string;
|
|
658
656
|
oldKey?: string | undefined;
|
|
659
657
|
oldKeys?: string[] | undefined;
|
|
658
|
+
valueToSet?: any;
|
|
660
659
|
fileData?: {
|
|
661
660
|
path: string;
|
|
662
661
|
name: string;
|
|
@@ -811,6 +810,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
811
810
|
targetKey: string;
|
|
812
811
|
oldKey?: string | undefined;
|
|
813
812
|
oldKeys?: string[] | undefined;
|
|
813
|
+
valueToSet?: any;
|
|
814
814
|
fileData?: {
|
|
815
815
|
path: string;
|
|
816
816
|
name: string;
|
|
@@ -884,6 +884,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
884
884
|
oldKey: z.ZodOptional<z.ZodString>;
|
|
885
885
|
oldKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
886
886
|
targetKey: z.ZodString;
|
|
887
|
+
valueToSet: z.ZodOptional<z.ZodAny>;
|
|
887
888
|
fileData: z.ZodOptional<z.ZodObject<{
|
|
888
889
|
name: z.ZodString;
|
|
889
890
|
path: z.ZodString;
|
|
@@ -919,6 +920,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
919
920
|
targetKey: string;
|
|
920
921
|
oldKey?: string | undefined;
|
|
921
922
|
oldKeys?: string[] | undefined;
|
|
923
|
+
valueToSet?: any;
|
|
922
924
|
fileData?: {
|
|
923
925
|
path: string;
|
|
924
926
|
name: string;
|
|
@@ -936,6 +938,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
936
938
|
targetKey: string;
|
|
937
939
|
oldKey?: string | undefined;
|
|
938
940
|
oldKeys?: string[] | undefined;
|
|
941
|
+
valueToSet?: any;
|
|
939
942
|
fileData?: {
|
|
940
943
|
path: string;
|
|
941
944
|
name: string;
|
|
@@ -957,6 +960,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
957
960
|
targetKey: string;
|
|
958
961
|
oldKey?: string | undefined;
|
|
959
962
|
oldKeys?: string[] | undefined;
|
|
963
|
+
valueToSet?: any;
|
|
960
964
|
fileData?: {
|
|
961
965
|
path: string;
|
|
962
966
|
name: string;
|
|
@@ -990,6 +994,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
990
994
|
targetKey: string;
|
|
991
995
|
oldKey?: string | undefined;
|
|
992
996
|
oldKeys?: string[] | undefined;
|
|
997
|
+
valueToSet?: any;
|
|
993
998
|
fileData?: {
|
|
994
999
|
path: string;
|
|
995
1000
|
name: string;
|
|
@@ -1030,6 +1035,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1030
1035
|
targetKey: string;
|
|
1031
1036
|
oldKey?: string | undefined;
|
|
1032
1037
|
oldKeys?: string[] | undefined;
|
|
1038
|
+
valueToSet?: any;
|
|
1033
1039
|
fileData?: {
|
|
1034
1040
|
path: string;
|
|
1035
1041
|
name: string;
|
|
@@ -1068,6 +1074,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1068
1074
|
targetKey: string;
|
|
1069
1075
|
oldKey?: string | undefined;
|
|
1070
1076
|
oldKeys?: string[] | undefined;
|
|
1077
|
+
valueToSet?: any;
|
|
1071
1078
|
fileData?: {
|
|
1072
1079
|
path: string;
|
|
1073
1080
|
name: string;
|
|
@@ -1110,6 +1117,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1110
1117
|
targetKey: string;
|
|
1111
1118
|
oldKey?: string | undefined;
|
|
1112
1119
|
oldKeys?: string[] | undefined;
|
|
1120
|
+
valueToSet?: any;
|
|
1113
1121
|
fileData?: {
|
|
1114
1122
|
path: string;
|
|
1115
1123
|
name: string;
|
|
@@ -1259,6 +1267,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1259
1267
|
targetKey: string;
|
|
1260
1268
|
oldKey?: string | undefined;
|
|
1261
1269
|
oldKeys?: string[] | undefined;
|
|
1270
|
+
valueToSet?: any;
|
|
1262
1271
|
fileData?: {
|
|
1263
1272
|
path: string;
|
|
1264
1273
|
name: string;
|
|
@@ -1303,6 +1312,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1303
1312
|
targetKey: string;
|
|
1304
1313
|
oldKey?: string | undefined;
|
|
1305
1314
|
oldKeys?: string[] | undefined;
|
|
1315
|
+
valueToSet?: any;
|
|
1306
1316
|
fileData?: {
|
|
1307
1317
|
path: string;
|
|
1308
1318
|
name: string;
|
|
@@ -1455,6 +1465,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
|
1455
1465
|
targetKey: string;
|
|
1456
1466
|
oldKey?: string | undefined;
|
|
1457
1467
|
oldKeys?: string[] | undefined;
|
|
1468
|
+
valueToSet?: any;
|
|
1458
1469
|
fileData?: {
|
|
1459
1470
|
path: string;
|
|
1460
1471
|
name: string;
|
|
@@ -1506,6 +1517,7 @@ export declare class DataLoader {
|
|
|
1506
1517
|
targetKey: string;
|
|
1507
1518
|
oldKey?: string | undefined;
|
|
1508
1519
|
oldKeys?: string[] | undefined;
|
|
1520
|
+
valueToSet?: any;
|
|
1509
1521
|
fileData?: {
|
|
1510
1522
|
path: string;
|
|
1511
1523
|
name: string;
|
|
@@ -1655,6 +1667,7 @@ export declare class DataLoader {
|
|
|
1655
1667
|
targetKey: string;
|
|
1656
1668
|
oldKey?: string | undefined;
|
|
1657
1669
|
oldKeys?: string[] | undefined;
|
|
1670
|
+
valueToSet?: any;
|
|
1658
1671
|
fileData?: {
|
|
1659
1672
|
path: string;
|
|
1660
1673
|
name: string;
|
|
@@ -1726,7 +1739,15 @@ export declare class DataLoader {
|
|
|
1726
1739
|
setupMaps(dbId: string): Promise<void>;
|
|
1727
1740
|
getAllUsers(): Promise<import("node-appwrite").Models.User<import("node-appwrite").Models.Preferences>[]>;
|
|
1728
1741
|
start(dbId: string): Promise<void>;
|
|
1729
|
-
|
|
1742
|
+
/**
|
|
1743
|
+
* Deals with merged users by iterating through all collections in the configuration.
|
|
1744
|
+
* We have merged users if there are duplicate emails or phones in the import data.
|
|
1745
|
+
* This function will iterate through all collections that are the same name as the
|
|
1746
|
+
* users collection and pull out their primaryKeyField's. It will then loop through
|
|
1747
|
+
* each collection and find any documents that have a
|
|
1748
|
+
*
|
|
1749
|
+
* @return {void} This function does not return anything.
|
|
1750
|
+
*/
|
|
1730
1751
|
updateOldReferencesForNew(): void;
|
|
1731
1752
|
private writeMapsToJsonFile;
|
|
1732
1753
|
/**
|
|
@@ -1791,6 +1812,7 @@ export declare class DataLoader {
|
|
|
1791
1812
|
targetKey: string;
|
|
1792
1813
|
oldKey?: string | undefined;
|
|
1793
1814
|
oldKeys?: string[] | undefined;
|
|
1815
|
+
valueToSet?: any;
|
|
1794
1816
|
fileData?: {
|
|
1795
1817
|
path: string;
|
|
1796
1818
|
name: string;
|
|
@@ -1812,6 +1834,7 @@ export declare class DataLoader {
|
|
|
1812
1834
|
targetKey: string;
|
|
1813
1835
|
oldKey?: string | undefined;
|
|
1814
1836
|
oldKeys?: string[] | undefined;
|
|
1837
|
+
valueToSet?: any;
|
|
1815
1838
|
fileData?: {
|
|
1816
1839
|
path: string;
|
|
1817
1840
|
name: string;
|
|
@@ -348,83 +348,54 @@ export class DataLoader {
|
|
|
348
348
|
this.writeMapsToJsonFile();
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const oldId = item.finalData[idMapping.sourceField] ||
|
|
400
|
-
item.context[idMapping.sourceField];
|
|
401
|
-
if (oldId === undefined || oldId === null) {
|
|
402
|
-
console.log(`Skipping item with undefined or null oldId in collection ${collection.name}`);
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
const newId = this.mergedUserMap.get(`${oldId}`);
|
|
406
|
-
if (newId) {
|
|
407
|
-
needsUpdate = true;
|
|
408
|
-
numUpdates++;
|
|
409
|
-
console.log(`Updating old ID ${oldId} to new ID ${newId} in collection ${collection.name}`);
|
|
410
|
-
// Update context to use new user ID
|
|
411
|
-
item.finalData[fieldToSetKey] = newId;
|
|
412
|
-
item.context[fieldToSetKey] = newId;
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
console.log(`No new ID found for old ID ${oldId} in mergedUserMap.`);
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
});
|
|
422
|
-
if (needsUpdate) {
|
|
423
|
-
console.log(`Updated ${numUpdates} references for collection ${collection.name}`);
|
|
424
|
-
this.importMap.set(this.getCollectionKey(collection.name), collectionData);
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
}
|
|
351
|
+
/**
|
|
352
|
+
* Deals with merged users by iterating through all collections in the configuration.
|
|
353
|
+
* We have merged users if there are duplicate emails or phones in the import data.
|
|
354
|
+
* This function will iterate through all collections that are the same name as the
|
|
355
|
+
* users collection and pull out their primaryKeyField's. It will then loop through
|
|
356
|
+
* each collection and find any documents that have a
|
|
357
|
+
*
|
|
358
|
+
* @return {void} This function does not return anything.
|
|
359
|
+
*/
|
|
360
|
+
// dealWithMergedUsers() {
|
|
361
|
+
// const usersCollectionKey = this.getCollectionKey(
|
|
362
|
+
// this.config.usersCollectionName
|
|
363
|
+
// );
|
|
364
|
+
// const usersCollectionData = this.importMap.get(usersCollectionKey);
|
|
365
|
+
// if (!this.config.collections) {
|
|
366
|
+
// console.log("No collections found in configuration.");
|
|
367
|
+
// return;
|
|
368
|
+
// }
|
|
369
|
+
// let needsUpdate = false;
|
|
370
|
+
// let numUpdates = 0;
|
|
371
|
+
// for (const collectionConfig of this.config.collections) {
|
|
372
|
+
// const collectionKey = this.getCollectionKey(collectionConfig.name);
|
|
373
|
+
// const collectionData = this.importMap.get(collectionKey);
|
|
374
|
+
// const collectionImportDefs = collectionConfig.importDefs;
|
|
375
|
+
// const collectionIdMappings = collectionImportDefs
|
|
376
|
+
// .map((importDef) => importDef.idMappings)
|
|
377
|
+
// .flat()
|
|
378
|
+
// .filter((idMapping) => idMapping !== undefined && idMapping !== null);
|
|
379
|
+
// if (!collectionData || !collectionData.data) continue;
|
|
380
|
+
// for (const dataItem of collectionData.data) {
|
|
381
|
+
// for (const idMapping of collectionIdMappings) {
|
|
382
|
+
// // We know it's the users collection here
|
|
383
|
+
// if (this.getCollectionKey(idMapping.targetCollection) === usersCollectionKey) {
|
|
384
|
+
// const targetFieldKey = idMapping.targetFieldToMatch || idMapping.targetField;
|
|
385
|
+
// if (targetFieldKey === )
|
|
386
|
+
// const targetValue = dataItem.finalData[targetFieldKey];
|
|
387
|
+
// const targetCollectionData = this.importMap.get(this.getCollectionKey(idMapping.targetCollection));
|
|
388
|
+
// if (!targetCollectionData || !targetCollectionData.data) continue;
|
|
389
|
+
// const foundData = targetCollectionData.data.filter(({ context }) => {
|
|
390
|
+
// const targetValue = context[targetFieldKey];
|
|
391
|
+
// const isMatch = `${targetValue}` === `${valueToMatch}`;
|
|
392
|
+
// return isMatch && targetValue !== undefined && targetValue !== null;
|
|
393
|
+
// });
|
|
394
|
+
// }
|
|
395
|
+
// }
|
|
396
|
+
// }
|
|
397
|
+
// }
|
|
398
|
+
// }
|
|
428
399
|
updateOldReferencesForNew() {
|
|
429
400
|
if (!this.config.collections) {
|
|
430
401
|
return;
|
|
@@ -491,7 +462,12 @@ export class DataLoader {
|
|
|
491
462
|
else {
|
|
492
463
|
// Merge arrays if new data is non-empty array and filter for uniqueness
|
|
493
464
|
collectionData.data[i].finalData[fieldToSetKey] = [
|
|
494
|
-
...new Set([
|
|
465
|
+
...new Set([
|
|
466
|
+
...(Array.isArray(currentDataFiltered)
|
|
467
|
+
? currentDataFiltered
|
|
468
|
+
: [currentDataFiltered]),
|
|
469
|
+
...newData,
|
|
470
|
+
].filter((value) => `${value}` !== `${valueToMatch}`)),
|
|
495
471
|
];
|
|
496
472
|
}
|
|
497
473
|
}
|
|
@@ -19,6 +19,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
|
19
19
|
oldKey: z.ZodOptional<z.ZodString>;
|
|
20
20
|
oldKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
21
21
|
targetKey: z.ZodString;
|
|
22
|
+
valueToSet: z.ZodOptional<z.ZodAny>;
|
|
22
23
|
fileData: z.ZodOptional<z.ZodObject<{
|
|
23
24
|
name: z.ZodString;
|
|
24
25
|
path: z.ZodString;
|
|
@@ -54,6 +55,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
|
54
55
|
targetKey: string;
|
|
55
56
|
oldKey?: string | undefined;
|
|
56
57
|
oldKeys?: string[] | undefined;
|
|
58
|
+
valueToSet?: any;
|
|
57
59
|
fileData?: {
|
|
58
60
|
path: string;
|
|
59
61
|
name: string;
|
|
@@ -71,6 +73,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
|
71
73
|
targetKey: string;
|
|
72
74
|
oldKey?: string | undefined;
|
|
73
75
|
oldKeys?: string[] | undefined;
|
|
76
|
+
valueToSet?: any;
|
|
74
77
|
fileData?: {
|
|
75
78
|
path: string;
|
|
76
79
|
name: string;
|
|
@@ -93,6 +96,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
|
93
96
|
targetKey: string;
|
|
94
97
|
oldKey?: string | undefined;
|
|
95
98
|
oldKeys?: string[] | undefined;
|
|
99
|
+
valueToSet?: any;
|
|
96
100
|
fileData?: {
|
|
97
101
|
path: string;
|
|
98
102
|
name: string;
|
|
@@ -116,6 +120,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
|
116
120
|
targetKey: string;
|
|
117
121
|
oldKey?: string | undefined;
|
|
118
122
|
oldKeys?: string[] | undefined;
|
|
123
|
+
valueToSet?: any;
|
|
119
124
|
fileData?: {
|
|
120
125
|
path: string;
|
|
121
126
|
name: string;
|
|
@@ -136,6 +136,9 @@ export const startSetup = async (database, storage, config, setupOptions, appwri
|
|
|
136
136
|
await backupDatabase(database, db.$id, storage);
|
|
137
137
|
}
|
|
138
138
|
deletedCollections = await wipeDatabase(database, db.$id);
|
|
139
|
+
// Add a delay to ensure the deletion process completes
|
|
140
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
141
|
+
console.log(`Waited a few seconds to let the database wipe complete...`);
|
|
139
142
|
}
|
|
140
143
|
if (processDatabase) {
|
|
141
144
|
await createOrUpdateCollections(database, db.$id, config, deletedCollections);
|
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.
|
|
4
|
+
"version": "0.0.54",
|
|
5
5
|
"main": "src/main.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@types/inquirer": "^9.0.7",
|
|
36
|
-
"appwrite-utils": "^0.2.
|
|
36
|
+
"appwrite-utils": "^0.2.8",
|
|
37
37
|
"commander": "^12.0.0",
|
|
38
38
|
"inquirer": "^9.2.20",
|
|
39
39
|
"js-yaml": "^4.1.0",
|
|
@@ -129,9 +129,7 @@ export const wipeDatabase = async (
|
|
|
129
129
|
databaseId: string
|
|
130
130
|
): Promise<{ collectionId: string; collectionName: string }[]> => {
|
|
131
131
|
console.log(`Wiping database: ${databaseId}`);
|
|
132
|
-
const
|
|
133
|
-
async () => await database.listCollections(databaseId)
|
|
134
|
-
);
|
|
132
|
+
const existingCollections = await fetchAllCollections(databaseId, database);
|
|
135
133
|
let collectionsDeleted: { collectionId: string; collectionName: string }[] =
|
|
136
134
|
[];
|
|
137
135
|
for (const { $id: collectionId, name: name } of existingCollections) {
|
|
@@ -103,7 +103,9 @@ export const convertObjectByAttributeMappings = (
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
for (const mapping of attributeMappings) {
|
|
106
|
-
if (
|
|
106
|
+
if (mapping.valueToSet !== undefined) {
|
|
107
|
+
result[mapping.targetKey] = mapping.valueToSet;
|
|
108
|
+
} else if (Array.isArray(mapping.oldKeys)) {
|
|
107
109
|
// Collect and flatten values from multiple oldKeys
|
|
108
110
|
const values = mapping.oldKeys
|
|
109
111
|
.map((oldKey) => resolveValue(obj, oldKey))
|
|
@@ -428,122 +428,57 @@ export class DataLoader {
|
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
collectionImportDefs.forEach((importDef) => {
|
|
486
|
-
importDef.idMappings?.forEach((idMapping) => {
|
|
487
|
-
if (
|
|
488
|
-
this.getCollectionKey(idMapping.targetCollection) ===
|
|
489
|
-
usersCollectionKey
|
|
490
|
-
) {
|
|
491
|
-
const fieldToSetKey = idMapping.fieldToSet || idMapping.sourceField;
|
|
492
|
-
const targetFieldKey =
|
|
493
|
-
idMapping.targetFieldToMatch || idMapping.targetField;
|
|
494
|
-
|
|
495
|
-
if (usersCollectionPrimaryKeyFields.has(targetFieldKey)) {
|
|
496
|
-
console.log(
|
|
497
|
-
`Processing collection ${collection.name} with target field ${targetFieldKey}`
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
// Process each item in the collection
|
|
501
|
-
collectionData.data.forEach((item) => {
|
|
502
|
-
const oldId =
|
|
503
|
-
item.finalData[idMapping.sourceField] ||
|
|
504
|
-
item.context[idMapping.sourceField];
|
|
505
|
-
|
|
506
|
-
if (oldId === undefined || oldId === null) {
|
|
507
|
-
console.log(
|
|
508
|
-
`Skipping item with undefined or null oldId in collection ${collection.name}`
|
|
509
|
-
);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const newId = this.mergedUserMap.get(`${oldId}`);
|
|
514
|
-
|
|
515
|
-
if (newId) {
|
|
516
|
-
needsUpdate = true;
|
|
517
|
-
numUpdates++;
|
|
518
|
-
console.log(
|
|
519
|
-
`Updating old ID ${oldId} to new ID ${newId} in collection ${collection.name}`
|
|
520
|
-
);
|
|
521
|
-
|
|
522
|
-
// Update context to use new user ID
|
|
523
|
-
item.finalData[fieldToSetKey] = newId;
|
|
524
|
-
item.context[fieldToSetKey] = newId;
|
|
525
|
-
} else {
|
|
526
|
-
console.log(
|
|
527
|
-
`No new ID found for old ID ${oldId} in mergedUserMap.`
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
if (needsUpdate) {
|
|
537
|
-
console.log(
|
|
538
|
-
`Updated ${numUpdates} references for collection ${collection.name}`
|
|
539
|
-
);
|
|
540
|
-
this.importMap.set(
|
|
541
|
-
this.getCollectionKey(collection.name),
|
|
542
|
-
collectionData
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
}
|
|
431
|
+
/**
|
|
432
|
+
* Deals with merged users by iterating through all collections in the configuration.
|
|
433
|
+
* We have merged users if there are duplicate emails or phones in the import data.
|
|
434
|
+
* This function will iterate through all collections that are the same name as the
|
|
435
|
+
* users collection and pull out their primaryKeyField's. It will then loop through
|
|
436
|
+
* each collection and find any documents that have a
|
|
437
|
+
*
|
|
438
|
+
* @return {void} This function does not return anything.
|
|
439
|
+
*/
|
|
440
|
+
// dealWithMergedUsers() {
|
|
441
|
+
// const usersCollectionKey = this.getCollectionKey(
|
|
442
|
+
// this.config.usersCollectionName
|
|
443
|
+
// );
|
|
444
|
+
// const usersCollectionData = this.importMap.get(usersCollectionKey);
|
|
445
|
+
|
|
446
|
+
// if (!this.config.collections) {
|
|
447
|
+
// console.log("No collections found in configuration.");
|
|
448
|
+
// return;
|
|
449
|
+
// }
|
|
450
|
+
|
|
451
|
+
// let needsUpdate = false;
|
|
452
|
+
// let numUpdates = 0;
|
|
453
|
+
|
|
454
|
+
// for (const collectionConfig of this.config.collections) {
|
|
455
|
+
// const collectionKey = this.getCollectionKey(collectionConfig.name);
|
|
456
|
+
// const collectionData = this.importMap.get(collectionKey);
|
|
457
|
+
// const collectionImportDefs = collectionConfig.importDefs;
|
|
458
|
+
// const collectionIdMappings = collectionImportDefs
|
|
459
|
+
// .map((importDef) => importDef.idMappings)
|
|
460
|
+
// .flat()
|
|
461
|
+
// .filter((idMapping) => idMapping !== undefined && idMapping !== null);
|
|
462
|
+
// if (!collectionData || !collectionData.data) continue;
|
|
463
|
+
// for (const dataItem of collectionData.data) {
|
|
464
|
+
// for (const idMapping of collectionIdMappings) {
|
|
465
|
+
// // We know it's the users collection here
|
|
466
|
+
// if (this.getCollectionKey(idMapping.targetCollection) === usersCollectionKey) {
|
|
467
|
+
// const targetFieldKey = idMapping.targetFieldToMatch || idMapping.targetField;
|
|
468
|
+
// if (targetFieldKey === )
|
|
469
|
+
// const targetValue = dataItem.finalData[targetFieldKey];
|
|
470
|
+
// const targetCollectionData = this.importMap.get(this.getCollectionKey(idMapping.targetCollection));
|
|
471
|
+
// if (!targetCollectionData || !targetCollectionData.data) continue;
|
|
472
|
+
// const foundData = targetCollectionData.data.filter(({ context }) => {
|
|
473
|
+
// const targetValue = context[targetFieldKey];
|
|
474
|
+
// const isMatch = `${targetValue}` === `${valueToMatch}`;
|
|
475
|
+
// return isMatch && targetValue !== undefined && targetValue !== null;
|
|
476
|
+
// });
|
|
477
|
+
// }
|
|
478
|
+
// }
|
|
479
|
+
// }
|
|
480
|
+
// }
|
|
481
|
+
// }
|
|
547
482
|
|
|
548
483
|
updateOldReferencesForNew() {
|
|
549
484
|
if (!this.config.collections) {
|
|
@@ -655,7 +590,12 @@ export class DataLoader {
|
|
|
655
590
|
// Merge arrays if new data is non-empty array and filter for uniqueness
|
|
656
591
|
collectionData.data[i].finalData[fieldToSetKey] = [
|
|
657
592
|
...new Set(
|
|
658
|
-
[
|
|
593
|
+
[
|
|
594
|
+
...(Array.isArray(currentDataFiltered)
|
|
595
|
+
? currentDataFiltered
|
|
596
|
+
: [currentDataFiltered]),
|
|
597
|
+
...newData,
|
|
598
|
+
].filter(
|
|
659
599
|
(value: any) => `${value}` !== `${valueToMatch}`
|
|
660
600
|
)
|
|
661
601
|
),
|
|
@@ -216,6 +216,9 @@ export const startSetup = async (
|
|
|
216
216
|
await backupDatabase(database, db.$id, storage);
|
|
217
217
|
}
|
|
218
218
|
deletedCollections = await wipeDatabase(database, db.$id);
|
|
219
|
+
// Add a delay to ensure the deletion process completes
|
|
220
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
221
|
+
console.log(`Waited a few seconds to let the database wipe complete...`);
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
if (processDatabase) {
|