appwrite-utils-cli 0.0.261 → 0.0.263

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.
Files changed (37) hide show
  1. package/README.md +24 -5
  2. package/dist/main.js +6 -0
  3. package/dist/migrations/afterImportActions.d.ts +3 -1
  4. package/dist/migrations/afterImportActions.js +2 -2
  5. package/dist/migrations/appwriteToX.d.ts +109 -0
  6. package/dist/migrations/appwriteToX.js +88 -0
  7. package/dist/migrations/backup.d.ts +4 -4
  8. package/dist/migrations/converters.js +1 -1
  9. package/dist/migrations/dataLoader.d.ts +19 -18
  10. package/dist/migrations/dataLoader.js +65 -84
  11. package/dist/migrations/databases.d.ts +2 -0
  12. package/dist/migrations/databases.js +23 -0
  13. package/dist/migrations/importController.js +3 -0
  14. package/dist/migrations/importDataActions.d.ts +1 -1
  15. package/dist/migrations/importDataActions.js +1 -1
  16. package/dist/migrations/schema.d.ts +363 -32
  17. package/dist/migrations/schema.js +48 -28
  18. package/dist/migrations/schemaStrings.d.ts +1 -0
  19. package/dist/migrations/schemaStrings.js +10 -0
  20. package/dist/setup.js +0 -0
  21. package/dist/utils/helperFunctions.d.ts +3 -0
  22. package/dist/utils/helperFunctions.js +8 -0
  23. package/dist/utilsController.d.ts +1 -0
  24. package/dist/utilsController.js +7 -0
  25. package/package.json +1 -1
  26. package/src/main.ts +6 -0
  27. package/src/migrations/afterImportActions.ts +2 -2
  28. package/src/migrations/appwriteToX.ts +122 -0
  29. package/src/migrations/converters.ts +2 -2
  30. package/src/migrations/dataLoader.ts +87 -95
  31. package/src/migrations/databases.ts +25 -0
  32. package/src/migrations/importController.ts +3 -0
  33. package/src/migrations/importDataActions.ts +1 -1
  34. package/src/migrations/schema.ts +65 -36
  35. package/src/migrations/schemaStrings.ts +11 -0
  36. package/src/utils/helperFunctions.ts +17 -0
  37. package/src/utilsController.ts +9 -0
@@ -10,6 +10,7 @@ import { findOrCreateOperation, updateOperation } from "./migrationHelper.js";
10
10
  import { AuthUserCreateSchema } from "../schemas/authUser.js";
11
11
  import _ from "lodash";
12
12
  import { UsersController } from "./users.js";
13
+ import { finalizeByAttributeMap } from "../utils/helperFunctions.js";
13
14
  // Define a schema for the structure of collection import data using Zod for validation
14
15
  export const CollectionImportDataSchema = z.object({
15
16
  // Optional collection creation schema
@@ -69,17 +70,33 @@ export class DataLoader {
69
70
  * @param target - The target object with values to update the source object.
70
71
  * @returns The updated source object.
71
72
  */
72
- mergeObjects(source, target) {
73
- Object.keys(target).forEach((key) => {
74
- const targetValue = target[key];
75
- if (source.hasOwnProperty(key) &&
76
- targetValue !== null &&
77
- targetValue !== undefined &&
78
- targetValue !== "") {
79
- source[key] = targetValue;
73
+ mergeObjects(source, update) {
74
+ // Create a new object to hold the merged result
75
+ const result = { ...source };
76
+ Object.keys(update).forEach((key) => {
77
+ const sourceValue = source[key];
78
+ const updateValue = update[key];
79
+ // If the update value is an array, concatenate and remove duplicates
80
+ if (Array.isArray(updateValue)) {
81
+ const sourceArray = Array.isArray(sourceValue) ? sourceValue : [];
82
+ result[key] = [...new Set([...sourceArray, ...updateValue])];
83
+ }
84
+ // If the update value is an object, recursively merge
85
+ else if (updateValue !== null &&
86
+ typeof updateValue === "object" &&
87
+ !(updateValue instanceof Date)) {
88
+ result[key] = this.mergeObjects(sourceValue, updateValue);
89
+ }
90
+ // If the update value is not nullish, overwrite the source value
91
+ else if (updateValue !== null && updateValue !== undefined) {
92
+ result[key] = updateValue;
93
+ }
94
+ // If the update value is nullish, keep the original value unless it doesn't exist
95
+ else if (sourceValue === undefined) {
96
+ result[key] = updateValue;
80
97
  }
81
98
  });
82
- return source;
99
+ return result;
83
100
  }
84
101
  // Method to load data from a file specified in the import definition
85
102
  async loadData(importDef) {
@@ -255,6 +272,7 @@ export class DataLoader {
255
272
  const collectionData = this.importMap.get(collectionKey);
256
273
  if (!collectionData || !collectionData.data)
257
274
  continue;
275
+ console.log(`Updating references for collection: ${collectionConfig.name}`);
258
276
  // Iterate over each data item in the current collection
259
277
  for (const item of collectionData.data) {
260
278
  let needsUpdate = false;
@@ -264,91 +282,56 @@ export class DataLoader {
264
282
  if (importDef.idMappings) {
265
283
  // Iterate over each idMapping defined for the current import definition
266
284
  for (const idMapping of importDef.idMappings) {
267
- const oldId = item.context[idMapping.sourceField];
268
- if (oldId) {
269
- let newIdForOldId;
270
- // Your existing logic to resolve newIdForOldId here...
271
- if (newIdForOldId) {
272
- // Determine the field to update, handling arrays appropriately
273
- const fieldToUpdate = idMapping.fieldToSet || idMapping.targetField;
274
- if (Array.isArray(item.finalData[fieldToUpdate])) {
275
- // If the target field is an array, append the new ID
276
- item.finalData[fieldToUpdate].push(newIdForOldId);
277
- }
278
- else {
279
- // Otherwise, directly set the new ID
280
- item.finalData[fieldToUpdate] = newIdForOldId;
281
- }
282
- console.log(`Updated ${oldId} to ${newIdForOldId}`);
283
- needsUpdate = true;
285
+ const oldIds = Array.isArray(item.context[idMapping.sourceField])
286
+ ? item.context[idMapping.sourceField]
287
+ : [item.context[idMapping.sourceField]];
288
+ const resolvedNewIds = [];
289
+ oldIds.forEach((oldId) => {
290
+ // Attempt to find a new ID for the old ID
291
+ let newIdForOldId = this.findNewIdForOldId(oldId, idMapping);
292
+ // Check if a new ID was found and it's not already included
293
+ if (newIdForOldId &&
294
+ !resolvedNewIds.includes(newIdForOldId)) {
295
+ resolvedNewIds.push(newIdForOldId);
284
296
  }
297
+ });
298
+ if (resolvedNewIds.length) {
299
+ const targetField = idMapping.fieldToSet || idMapping.targetField;
300
+ const isArray = collectionConfig.attributes.some((attribute) => attribute.key === targetField && attribute.array);
301
+ // Set the target field based on whether it's an array or single value
302
+ if (isArray) {
303
+ item.finalData[targetField] = resolvedNewIds;
304
+ }
305
+ else {
306
+ // In case of a single value, use the first resolved ID
307
+ item.finalData[targetField] = resolvedNewIds[0];
308
+ }
309
+ needsUpdate = true;
285
310
  }
286
311
  }
287
312
  }
288
313
  }
289
314
  }
315
+ // Update the importMap if changes were made to the item
290
316
  if (needsUpdate) {
291
- // Re-transform the item's finalData using its attribute mappings
292
- const importDef = item.importDef; // Assuming importDef is available in the item
293
- if (importDef && importDef.attributeMappings) {
294
- item.finalData = await this.transformData(item.finalData, importDef.attributeMappings);
295
- }
296
317
  this.importMap.set(collectionKey, collectionData);
318
+ logger.info(`Updated item: ${JSON.stringify(item.finalData, undefined, 2)}`);
297
319
  }
298
320
  }
299
321
  }
300
322
  }
301
- // async updateReferencesInRelatedCollections() {
302
- // // Process each collection defined in the config
303
- // for (const collection of this.config.collections) {
304
- // // Ensure we handle only those definitions with idMappings and a primaryKeyField defined
305
- // for (const importDef of collection.importDefs) {
306
- // if (!importDef.idMappings || !importDef.primaryKeyField) continue;
307
- // // Load data for this particular import definition
308
- // const collectionData = this.importMap.get(
309
- // this.getCollectionKey(collection.name)
310
- // )?.data;
311
- // if (!collectionData) continue;
312
- // // Iterate over each item in the collection data
313
- // for (const item of collectionData) {
314
- // let needsUpdate = false;
315
- // // Go through each idMapping defined for the collection
316
- // for (const mapping of importDef.idMappings) {
317
- // const oldReferenceId =
318
- // item[mapping.targetField as keyof typeof item]; // Get the current reference ID from the item
319
- // const referenceCollectionMap =
320
- // this.oldIdToNewIdPerCollectionMap.get(
321
- // this.getCollectionKey(mapping.targetCollection)
322
- // );
323
- // if (
324
- // referenceCollectionMap &&
325
- // referenceCollectionMap.has(oldReferenceId)
326
- // ) {
327
- // // Update the target field with the new reference ID from the mapped collection
328
- // item[mapping.sourceField as keyof typeof item] =
329
- // referenceCollectionMap.get(oldReferenceId);
330
- // needsUpdate = true;
331
- // console.log(
332
- // `Updated item with ${mapping.sourceField} = ${JSON.stringify(
333
- // item,
334
- // null,
335
- // undefined
336
- // )}.`
337
- // );
338
- // }
339
- // }
340
- // // Save changes if any reference was updated
341
- // if (needsUpdate) {
342
- // await this.saveUpdatedItem(
343
- // collection.name,
344
- // item,
345
- // importDef.primaryKeyField
346
- // );
347
- // }
348
- // }
349
- // }
350
- // }
351
- // }
323
+ findNewIdForOldId(oldId, idMapping) {
324
+ // First, check if the old ID has been merged into a new one
325
+ for (const [newUserId, oldIds] of this.mergedUserMap.entries()) {
326
+ if (oldIds.includes(oldId)) {
327
+ return newUserId;
328
+ }
329
+ }
330
+ // If not merged, look for a direct mapping from old to new ID
331
+ const targetCollectionKey = this.getCollectionKey(idMapping.targetCollection);
332
+ const targetOldIdToNewIdMap = this.oldIdToNewIdPerCollectionMap.get(targetCollectionKey);
333
+ return targetOldIdToNewIdMap?.get(oldId);
334
+ }
352
335
  writeMapsToJsonFile() {
353
336
  const outputDir = path.resolve(process.cwd());
354
337
  const outputFile = path.join(outputDir, "dataLoaderOutput.json");
@@ -401,7 +384,6 @@ export class DataLoader {
401
384
  // Check for duplicate email and add to emailToUserIdMap if not found
402
385
  if (email && email.length > 0) {
403
386
  if (this.emailToUserIdMap.has(email)) {
404
- logger.error(`Duplicate email found or user exists already: ${email}`);
405
387
  existingId = this.emailToUserIdMap.get(email);
406
388
  }
407
389
  else {
@@ -411,7 +393,6 @@ export class DataLoader {
411
393
  // Check for duplicate phone and add to phoneToUserIdMap if not found
412
394
  if (phone && phone.length > 0) {
413
395
  if (this.phoneToUserIdMap.has(phone)) {
414
- logger.error(`Duplicate phone found: ${phone}`);
415
396
  existingId = this.phoneToUserIdMap.get(phone);
416
397
  }
417
398
  else {
@@ -0,0 +1,2 @@
1
+ import { Databases, type Models } from "node-appwrite";
2
+ export declare const fetchAllDatabases: (database: Databases) => Promise<Models.Database[]>;
@@ -0,0 +1,23 @@
1
+ import { Databases, Query } from "node-appwrite";
2
+ export const fetchAllDatabases = async (database) => {
3
+ const databases = await database.list([Query.limit(25)]);
4
+ const allDatabases = databases.databases;
5
+ let lastDatabaseId = allDatabases[allDatabases.length - 1].$id;
6
+ if (databases.databases.length < 25) {
7
+ return allDatabases;
8
+ }
9
+ else {
10
+ while (lastDatabaseId) {
11
+ const databases = await database.list([
12
+ Query.limit(25),
13
+ Query.cursorAfter(lastDatabaseId),
14
+ ]);
15
+ allDatabases.push(...databases.databases);
16
+ if (databases.databases.length < 25) {
17
+ break;
18
+ }
19
+ lastDatabaseId = databases.databases[databases.databases.length - 1].$id;
20
+ }
21
+ }
22
+ return allDatabases;
23
+ };
@@ -159,6 +159,9 @@ export class ImportController {
159
159
  if (item.finalData.hasOwnProperty("docId")) {
160
160
  delete item.finalData.docId;
161
161
  }
162
+ if (!item.finalData) {
163
+ return Promise.resolve();
164
+ }
162
165
  return this.database
163
166
  .createDocument(db.$id, collection.$id, id, item.finalData)
164
167
  .then(() => {
@@ -12,7 +12,7 @@ export declare class ImportDataActions {
12
12
  private validityRuleDefinitions;
13
13
  private afterImportActionsDefinitions;
14
14
  constructor(db: Databases, storage: Storage, config: AppwriteConfig, converterDefinitions: ConverterFunctions, validityRuleDefinitions: ValidationRules, afterImportActionsDefinitions: AfterImportActions);
15
- runConverterFunctions(item: any, attributeMappings: AttributeMappings): Promise<any>;
15
+ runConverterFunctions(item: any, attributeMappings: AttributeMappings): any;
16
16
  /**
17
17
  * Validates a single data item based on defined validation rules.
18
18
  * @param item The data item to validate.
@@ -18,7 +18,7 @@ export class ImportDataActions {
18
18
  this.validityRuleDefinitions = validityRuleDefinitions;
19
19
  this.afterImportActionsDefinitions = afterImportActionsDefinitions;
20
20
  }
21
- async runConverterFunctions(item, attributeMappings) {
21
+ runConverterFunctions(item, attributeMappings) {
22
22
  const conversionSchema = attributeMappings.reduce((schema, mapping) => {
23
23
  schema[mapping.targetKey] = (originalValue) => {
24
24
  return mapping.converters.reduce((value, converterName) => {