appwrite-utils-cli 0.0.33 → 0.0.34

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 CHANGED
@@ -55,17 +55,21 @@ Replace `--args` with the appropriate options:
55
55
  - `--staging`: Run tasks in the staging environment.
56
56
  - `--dev`: Run tasks in the development environment.
57
57
  - `--wipe`: Wipe all databases.
58
- - `--wipe-docs` or `--wipeDocs`: Wipe all documents in the databases.
58
+ - `--wipe-docs`: Wipe all documents in the databases.
59
59
  - `--generate`: Generate TypeScript schemas from database schemas.
60
60
  - `--import`: Import data into your databases.
61
61
  - `--backup`: Perform a backup of your databases.
62
- - `--wipe-users` or `--wipeUsers`: Wipe all user data.
63
- - `--write-data` or `--writeData`: Write converted imported data to file
62
+ - `--wipe-users`: Wipe all user data.
63
+ - `--write-data`: Write converted imported data to file
64
64
  - `--sync`: Synchronize your project's config and generate schema for your database
65
65
  - `--endpoint`: Set a different endpoint for the migration target
66
66
  - `--project`: Set a different project ID for the migration target
67
67
  - `--key`: Set a different API key for the migration target
68
68
 
69
+ ## If you run out of RAM
70
+
71
+ This happens because Node only allocates 4 GB, it'll only happen if you're importing a ton of data, but you can use `export NODE_OPTIONS="--max-old-space-size=16384"` or the relevant command for your system if that doesn't work, and it'll set the env var to whatever. That's 16 GB, I would recommend `8192` for most.
72
+
69
73
  ### OpenAPI Generation (almost done, in progress)
70
74
 
71
75
  Recently, I have also added an optional OpenAPI generation for each attribute in the schema. This is because I needed it and because I felt it would be nice to have. This is done using [this package](https://github.com/asteasolutions/zod-to-openapi), many thanks to them.
@@ -82,6 +86,7 @@ This setup ensures that developers have robust tools at their fingertips to mana
82
86
 
83
87
  ### Changelog
84
88
 
89
+ - 0.0.34: Fixed the `bin` section of the package.json, apparently you can't use `node` to run it
85
90
  - 0.0.33: Fixed `idMappings`, if you are importing data and use the `idMappings` functionality, you can set a `fieldToSet` based on the value of a `sourceField` in the current imported items data (whether it's in the final data or the original), in order to match another field in another collection. So if you had a store, and it had items and the items have a Region ID for instance. You can then, in your regionId of the items, setup an `idMapping` that will allow you to map the value of the `targetField` based on the value of the `targetFieldToMatch` in the `targetCollection`. Sounds complex, but it's very useful. Like psuedo-relationship resolution, without the relationships.
86
91
  - 0.0.29: If you use the `description` variable in an attribute and collection, it'll add that description to the generated schemas. This assumes you have `zod-to-openpi`
87
92
  - 0.0.275: THINGS ARE NOW IN TYPESCRIPT WOOHOO. No but for reaal, super happy to report that everything has been converted to TypeScript, just way too many changes, I hope you enjoy it!
@@ -61,6 +61,14 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
61
61
  required: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
62
62
  array: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
63
63
  min: z.ZodOptional<z.ZodNumber>;
64
+ /**
65
+ * Prepares the data for creating documents in a collection.
66
+ * This involves loading the data, transforming it, and handling ID mappings.
67
+ *
68
+ * @param db - The database configuration.
69
+ * @param collection - The collection configuration.
70
+ * @param importDef - The import definition containing the attribute mappings and other relevant info.
71
+ */
64
72
  max: z.ZodOptional<z.ZodNumber>;
65
73
  xdefault: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
66
74
  description: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
@@ -359,6 +367,16 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
359
367
  type: z.ZodOptional<z.ZodDefault<z.ZodEnum<["create", "update"]>>>;
360
368
  filePath: z.ZodString;
361
369
  basePath: z.ZodOptional<z.ZodString>;
370
+ /**
371
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
372
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
373
+ * and update the field with the file's ID if necessary.
374
+ *
375
+ * @param attributeMappings - The attribute mappings from the import definition.
376
+ * @param context - The context object containing information about the database, collection, and document.
377
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
378
+ * @returns The attribute mappings updated with any necessary post-import actions.
379
+ */
362
380
  primaryKeyField: z.ZodDefault<z.ZodString>;
363
381
  idMappings: z.ZodOptional<z.ZodArray<z.ZodObject<{
364
382
  sourceField: z.ZodString;
@@ -92,7 +92,7 @@ export class DataLoader {
92
92
  result[key] = updateValue;
93
93
  }
94
94
  // If the update value is nullish, keep the original value unless it doesn't exist
95
- else if (sourceValue === undefined) {
95
+ else if (sourceValue === undefined || sourceValue === null) {
96
96
  result[key] = updateValue;
97
97
  }
98
98
  });
@@ -314,10 +314,10 @@ export class DataLoader {
314
314
  // Process each item in the collection
315
315
  collectionData.data.forEach((item) => {
316
316
  const oldId = item.context[idMapping.sourceField];
317
- const newId = this.mergedUserMap.get(oldId);
317
+ const newId = this.mergedUserMap.get(`${oldId}`);
318
318
  if (newId) {
319
319
  // Update context to use new user ID
320
- item.context[idMapping.fieldToSet || targetFieldKey] = newId;
320
+ item.finalData[idMapping.fieldToSet || idMapping.sourceField] = newId;
321
321
  }
322
322
  });
323
323
  }
@@ -398,13 +398,14 @@ export class DataLoader {
398
398
  if (!currentDataFiltered) {
399
399
  // Set new data if current data is undefined
400
400
  collectionData.data[i].finalData[fieldToSetKey] =
401
- Array.isArray(newData) ? newData : [newData];
401
+ Array.isArray(newData) ? newData[0] : newData;
402
402
  }
403
403
  else if (Array.isArray(newData) && newData.length > 0) {
404
404
  // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
405
+ // and take the first value, because it's an array and the attribute is not an array
405
406
  collectionData.data[i].finalData[fieldToSetKey] = [
406
407
  ...new Set([currentDataFiltered, ...newData].filter((value) => `${value}` !== `${valueToMatch}`)),
407
- ];
408
+ ].slice(0, 1)[0];
408
409
  }
409
410
  else if (!Array.isArray(newData) && newData !== undefined) {
410
411
  // Simply update the field if new data is not an array and defined
@@ -467,7 +468,6 @@ export class DataLoader {
467
468
  * @returns The transformed item with user-specific keys removed.
468
469
  */
469
470
  async prepareUserData(item, attributeMappings, primaryKeyField, newId) {
470
- // Transform the item data based on the attribute mappings
471
471
  let transformedItem = this.transformData(item, attributeMappings);
472
472
  const userData = AuthUserCreateSchema.safeParse(transformedItem);
473
473
  if (!userData.success) {
@@ -477,35 +477,58 @@ export class DataLoader {
477
477
  const email = userData.data.email;
478
478
  const phone = userData.data.phone;
479
479
  let existingId;
480
- // Check for duplicate email and add to emailToUserIdMap if not found
481
- if (email && email.length > 0) {
482
- if (this.emailToUserIdMap.has(email)) {
483
- existingId = this.emailToUserIdMap.get(email);
484
- }
485
- else {
486
- this.emailToUserIdMap.set(email, newId);
487
- }
480
+ // Check for duplicate email and phone
481
+ if (email && this.emailToUserIdMap.has(email)) {
482
+ existingId = this.emailToUserIdMap.get(email);
488
483
  }
489
- // Check for duplicate phone and add to phoneToUserIdMap if not found
490
- if (phone && phone.length > 0) {
491
- if (this.phoneToUserIdMap.has(phone)) {
492
- existingId = this.phoneToUserIdMap.get(phone);
493
- }
494
- else {
495
- this.phoneToUserIdMap.set(phone, newId);
496
- }
484
+ else if (phone && this.phoneToUserIdMap.has(phone)) {
485
+ existingId = this.phoneToUserIdMap.get(phone);
497
486
  }
498
- if (!existingId) {
499
- existingId = newId;
487
+ else {
488
+ if (email)
489
+ this.emailToUserIdMap.set(email, newId);
490
+ if (phone)
491
+ this.phoneToUserIdMap.set(phone, newId);
500
492
  }
501
- // If existingId is found, add to mergedUserMap
502
493
  if (existingId) {
503
494
  userData.data.userId = existingId;
504
495
  const mergedUsers = this.mergedUserMap.get(existingId) || [];
505
496
  mergedUsers.push(`${item[primaryKeyField]}`);
506
497
  this.mergedUserMap.set(existingId, mergedUsers);
498
+ const userFound = this.importMap
499
+ .get(this.getCollectionKey("users"))
500
+ ?.data.find((userDataExisting) => {
501
+ let userIdToMatch;
502
+ if (userDataExisting?.finalData?.userId) {
503
+ userIdToMatch = userDataExisting?.finalData?.userId;
504
+ }
505
+ else if (userDataExisting?.finalData?.docId) {
506
+ userIdToMatch = userDataExisting?.finalData?.docId;
507
+ }
508
+ else if (userDataExisting?.context?.userId) {
509
+ userIdToMatch = userDataExisting.context.userId;
510
+ }
511
+ else if (userDataExisting?.context?.docId) {
512
+ userIdToMatch = userDataExisting.context.docId;
513
+ }
514
+ return userIdToMatch === existingId;
515
+ });
516
+ if (userFound) {
517
+ userFound.finalData.userId = existingId;
518
+ }
519
+ return [
520
+ transformedItem,
521
+ existingId,
522
+ {
523
+ rawData: userFound?.rawData,
524
+ finalData: userFound?.finalData,
525
+ },
526
+ ];
527
+ }
528
+ else {
529
+ existingId = newId;
530
+ userData.data.userId = existingId;
507
531
  }
508
- // Remove user-specific keys from the transformed item
509
532
  const userKeys = ["email", "phone", "name", "labels", "prefs"];
510
533
  userKeys.forEach((key) => {
511
534
  if (transformedItem.hasOwnProperty(key)) {
@@ -517,7 +540,6 @@ export class DataLoader {
517
540
  rawData: item,
518
541
  finalData: userData.data,
519
542
  };
520
- // Directly update the importMap with the new user data, without pushing to usersMap.data first
521
543
  this.importMap.set(this.getCollectionKey("users"), {
522
544
  data: [...(usersMap?.data || []), userDataToAdd],
523
545
  });
@@ -86,44 +86,58 @@ export class ImportController {
86
86
  const usersData = usersDataMap?.data;
87
87
  const usersController = new UsersController(this.config, this.database);
88
88
  if (usersData) {
89
- console.log("Found users data");
90
- const userBatchesAll = createBatches(usersData);
91
- console.log(`${userBatchesAll.length} user batches`);
92
- for (let i = 0; i < userBatchesAll.length; i++) {
93
- const userBatches = userBatchesAll[i];
94
- console.log(`Processing user batch ${i + 1} of ${userBatchesAll.length}`);
95
- const userBatchPromises = userBatches
96
- .map((userBatch) => {
97
- if (userBatch.finalData && userBatch.finalData.length > 0) {
98
- const userId = userBatch.finalData.userId;
99
- if (dataLoader.userExistsMap.has(userId)) {
100
- // We only are storing the existing user ID's as true, so we need to check for that
101
- if (!(dataLoader.userExistsMap.get(userId) === true)) {
102
- const userId = userBatch.finalData.userId ||
103
- userBatch.context.userId ||
104
- userBatch.context.docId;
105
- if (!userBatch.finalData.userId) {
106
- userBatch.finalData.userId = userId;
107
- }
108
- return usersController
109
- .createUserAndReturn(userBatch.finalData)
110
- .then(() => console.log("Created user"))
111
- .catch((error) => {
112
- logger.error("Error creating user:", error, "\nUser data is ", userBatch.finalData);
113
- });
114
- }
115
- else {
116
- console.log("Skipped existing user: ", userId);
117
- return Promise.resolve();
118
- }
119
- }
89
+ console.log("Found users data", usersData.length);
90
+ const userDataBatches = createBatches(usersData);
91
+ for (const batch of userDataBatches) {
92
+ console.log("Importing users batch", batch.length);
93
+ const userBatchPromises = batch
94
+ .filter((item) => {
95
+ let itemId;
96
+ if (item.finalData.userId) {
97
+ itemId = item.finalData.userId;
120
98
  }
99
+ else if (item.finalData.docId) {
100
+ itemId = item.finalData.docId;
101
+ }
102
+ if (!itemId) {
103
+ return false;
104
+ }
105
+ return (item &&
106
+ item.finalData &&
107
+ !dataLoader.userExistsMap.has(itemId));
121
108
  })
122
- .flat();
123
- // Wait for all promises in the current user batch to resolve
124
- await Promise.allSettled(userBatchPromises);
125
- console.log(`Completed user batch ${i + 1} of ${userBatchesAll.length}`);
109
+ .map((item) => {
110
+ return usersController.createUserAndReturn(item.finalData);
111
+ });
112
+ await Promise.all(userBatchPromises);
113
+ for (const item of batch) {
114
+ if (item && item.finalData) {
115
+ dataLoader.userExistsMap.set(item.finalData.userId ||
116
+ item.finalData.docId ||
117
+ item.context.userId ||
118
+ item.context.docId, true);
119
+ }
120
+ }
121
+ console.log("Finished importing users batch", batch.length);
126
122
  }
123
+ // for (let i = 0; i < usersData.length; i++) {
124
+ // const user = usersData[i];
125
+ // if (user.finalData) {
126
+ // const userId =
127
+ // user.finalData.userId ||
128
+ // user.context.userId ||
129
+ // user.context.docId;
130
+ // if (!dataLoader.userExistsMap.has(userId)) {
131
+ // if (!user.finalData.userId) {
132
+ // user.finalData.userId = userId;
133
+ // }
134
+ // await usersController.createUserAndReturn(user.finalData);
135
+ // dataLoader.userExistsMap.set(userId, true);
136
+ // } else {
137
+ // console.log("Skipped existing user: ", userId);
138
+ // }
139
+ // }
140
+ // }
127
141
  console.log("Finished importing users");
128
142
  }
129
143
  }
@@ -146,10 +160,10 @@ export class ImportController {
146
160
  const batches = dataSplit[i];
147
161
  console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
148
162
  const batchPromises = batches.map((item) => {
149
- const id = item.context.docId ||
150
- item.context.userId ||
151
- item.finalData.docId ||
152
- item.finalData.userId;
163
+ const id = item.finalData.docId ||
164
+ item.finalData.userId ||
165
+ item.context.docId ||
166
+ item.context.userId;
153
167
  if (item.finalData.hasOwnProperty("userId")) {
154
168
  delete item.finalData.userId;
155
169
  }
@@ -159,17 +173,7 @@ export class ImportController {
159
173
  if (!item.finalData) {
160
174
  return Promise.resolve();
161
175
  }
162
- return this.database
163
- .createDocument(db.$id, collection.$id, id, item.finalData)
164
- .then(() => {
165
- processedItems++;
166
- console.log("Created item");
167
- })
168
- .catch((error) => {
169
- console.error(`Error creating item in ${collection.name}:`, error, "\nItem data is ", item.finalData);
170
- throw error;
171
- // Optionally, log the failed item for retry or review
172
- });
176
+ return this.database.createDocument(db.$id, collection.$id, id, item.finalData);
173
177
  });
174
178
  // Wait for all promises in the current batch to resolve
175
179
  await Promise.allSettled(batchPromises);
@@ -115,6 +115,7 @@ export class UsersController {
115
115
  if (e instanceof Error) {
116
116
  logger.error("FAILED CREATING USER: ", e.message);
117
117
  }
118
+ console.log("FAILED CREATING USER: ", e);
118
119
  throw e;
119
120
  }
120
121
  }
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.33",
4
+ "version": "0.0.34",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "bin": {
24
24
  "appwrite-init": "./dist/init.js",
25
- "appwrite-migrate": "node --max-old-space-size=16384 ./dist/main.js"
25
+ "appwrite-migrate": "./dist/main.js"
26
26
  },
27
27
  "scripts": {
28
28
  "build": "bun run tsc",
@@ -124,7 +124,7 @@ export class DataLoader {
124
124
  result[key] = updateValue;
125
125
  }
126
126
  // If the update value is nullish, keep the original value unless it doesn't exist
127
- else if (sourceValue === undefined) {
127
+ else if (sourceValue === undefined || sourceValue === null) {
128
128
  result[key] = updateValue;
129
129
  }
130
130
  });
@@ -395,11 +395,13 @@ export class DataLoader {
395
395
  // Process each item in the collection
396
396
  collectionData.data.forEach((item) => {
397
397
  const oldId = item.context[idMapping.sourceField];
398
- const newId = this.mergedUserMap.get(oldId);
398
+ const newId = this.mergedUserMap.get(`${oldId}`);
399
399
 
400
400
  if (newId) {
401
401
  // Update context to use new user ID
402
- item.context[idMapping.fieldToSet || targetFieldKey] = newId;
402
+ item.finalData[
403
+ idMapping.fieldToSet || idMapping.sourceField
404
+ ] = newId;
403
405
  }
404
406
  });
405
407
  }
@@ -524,16 +526,17 @@ export class DataLoader {
524
526
  if (!currentDataFiltered) {
525
527
  // Set new data if current data is undefined
526
528
  collectionData.data[i].finalData[fieldToSetKey] =
527
- Array.isArray(newData) ? newData : [newData];
529
+ Array.isArray(newData) ? newData[0] : newData;
528
530
  } else if (Array.isArray(newData) && newData.length > 0) {
529
531
  // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
532
+ // and take the first value, because it's an array and the attribute is not an array
530
533
  collectionData.data[i].finalData[fieldToSetKey] = [
531
534
  ...new Set(
532
535
  [currentDataFiltered, ...newData].filter(
533
536
  (value: any) => `${value}` !== `${valueToMatch}`
534
537
  )
535
538
  ),
536
- ];
539
+ ].slice(0, 1)[0];
537
540
  } else if (!Array.isArray(newData) && newData !== undefined) {
538
541
  // Simply update the field if new data is not an array and defined
539
542
  collectionData.data[i].finalData[fieldToSetKey] = newData;
@@ -615,7 +618,6 @@ export class DataLoader {
615
618
  primaryKeyField: string,
616
619
  newId: string
617
620
  ): Promise<any> {
618
- // Transform the item data based on the attribute mappings
619
621
  let transformedItem = this.transformData(item, attributeMappings);
620
622
  const userData = AuthUserCreateSchema.safeParse(transformedItem);
621
623
  if (!userData.success) {
@@ -632,48 +634,64 @@ export class DataLoader {
632
634
  const phone = userData.data.phone;
633
635
  let existingId: string | undefined;
634
636
 
635
- // Check for duplicate email and add to emailToUserIdMap if not found
636
- if (email && email.length > 0) {
637
- if (this.emailToUserIdMap.has(email)) {
638
- existingId = this.emailToUserIdMap.get(email);
639
- } else {
640
- this.emailToUserIdMap.set(email, newId);
641
- }
642
- }
643
-
644
- // Check for duplicate phone and add to phoneToUserIdMap if not found
645
- if (phone && phone.length > 0) {
646
- if (this.phoneToUserIdMap.has(phone)) {
647
- existingId = this.phoneToUserIdMap.get(phone);
648
- } else {
649
- this.phoneToUserIdMap.set(phone, newId);
650
- }
651
- }
652
- if (!existingId) {
653
- existingId = newId;
637
+ // Check for duplicate email and phone
638
+ if (email && this.emailToUserIdMap.has(email)) {
639
+ existingId = this.emailToUserIdMap.get(email);
640
+ } else if (phone && this.phoneToUserIdMap.has(phone)) {
641
+ existingId = this.phoneToUserIdMap.get(phone);
642
+ } else {
643
+ if (email) this.emailToUserIdMap.set(email, newId);
644
+ if (phone) this.phoneToUserIdMap.set(phone, newId);
654
645
  }
655
646
 
656
- // If existingId is found, add to mergedUserMap
657
647
  if (existingId) {
658
648
  userData.data.userId = existingId;
659
649
  const mergedUsers = this.mergedUserMap.get(existingId) || [];
660
650
  mergedUsers.push(`${item[primaryKeyField]}`);
661
651
  this.mergedUserMap.set(existingId, mergedUsers);
652
+ const userFound = this.importMap
653
+ .get(this.getCollectionKey("users"))
654
+ ?.data.find((userDataExisting) => {
655
+ let userIdToMatch: string | undefined;
656
+ if (userDataExisting?.finalData?.userId) {
657
+ userIdToMatch = userDataExisting?.finalData?.userId;
658
+ } else if (userDataExisting?.finalData?.docId) {
659
+ userIdToMatch = userDataExisting?.finalData?.docId;
660
+ } else if (userDataExisting?.context?.userId) {
661
+ userIdToMatch = userDataExisting.context.userId;
662
+ } else if (userDataExisting?.context?.docId) {
663
+ userIdToMatch = userDataExisting.context.docId;
664
+ }
665
+ return userIdToMatch === existingId;
666
+ });
667
+ if (userFound) {
668
+ userFound.finalData.userId = existingId;
669
+ }
670
+ return [
671
+ transformedItem,
672
+ existingId,
673
+ {
674
+ rawData: userFound?.rawData,
675
+ finalData: userFound?.finalData,
676
+ },
677
+ ];
678
+ } else {
679
+ existingId = newId;
680
+ userData.data.userId = existingId;
662
681
  }
663
682
 
664
- // Remove user-specific keys from the transformed item
665
683
  const userKeys = ["email", "phone", "name", "labels", "prefs"];
666
684
  userKeys.forEach((key) => {
667
685
  if (transformedItem.hasOwnProperty(key)) {
668
686
  delete transformedItem[key];
669
687
  }
670
688
  });
689
+
671
690
  const usersMap = this.importMap.get(this.getCollectionKey("users"));
672
691
  const userDataToAdd = {
673
692
  rawData: item,
674
693
  finalData: userData.data,
675
694
  };
676
- // Directly update the importMap with the new user data, without pushing to usersMap.data first
677
695
  this.importMap.set(this.getCollectionKey("users"), {
678
696
  data: [...(usersMap?.data || []), userDataToAdd],
679
697
  });
@@ -137,53 +137,62 @@ export class ImportController {
137
137
  const usersData = usersDataMap?.data;
138
138
  const usersController = new UsersController(this.config, this.database);
139
139
  if (usersData) {
140
- console.log("Found users data");
141
- const userBatchesAll = createBatches(usersData);
142
- console.log(`${userBatchesAll.length} user batches`);
143
- for (let i = 0; i < userBatchesAll.length; i++) {
144
- const userBatches = userBatchesAll[i];
145
- console.log(
146
- `Processing user batch ${i + 1} of ${userBatchesAll.length}`
147
- );
148
- const userBatchPromises = userBatches
149
- .map((userBatch) => {
150
- if (userBatch.finalData && userBatch.finalData.length > 0) {
151
- const userId = userBatch.finalData.userId;
152
- if (dataLoader.userExistsMap.has(userId)) {
153
- // We only are storing the existing user ID's as true, so we need to check for that
154
- if (!(dataLoader.userExistsMap.get(userId) === true)) {
155
- const userId =
156
- userBatch.finalData.userId ||
157
- userBatch.context.userId ||
158
- userBatch.context.docId;
159
- if (!userBatch.finalData.userId) {
160
- userBatch.finalData.userId = userId;
161
- }
162
- return usersController
163
- .createUserAndReturn(userBatch.finalData)
164
- .then(() => console.log("Created user"))
165
- .catch((error) => {
166
- logger.error(
167
- "Error creating user:",
168
- error,
169
- "\nUser data is ",
170
- userBatch.finalData
171
- );
172
- });
173
- } else {
174
- console.log("Skipped existing user: ", userId);
175
- return Promise.resolve();
176
- }
177
- }
140
+ console.log("Found users data", usersData.length);
141
+ const userDataBatches = createBatches(usersData);
142
+ for (const batch of userDataBatches) {
143
+ console.log("Importing users batch", batch.length);
144
+ const userBatchPromises = batch
145
+ .filter((item) => {
146
+ let itemId: string | undefined;
147
+ if (item.finalData.userId) {
148
+ itemId = item.finalData.userId;
149
+ } else if (item.finalData.docId) {
150
+ itemId = item.finalData.docId;
151
+ }
152
+ if (!itemId) {
153
+ return false;
178
154
  }
155
+ return (
156
+ item &&
157
+ item.finalData &&
158
+ !dataLoader.userExistsMap.has(itemId)
159
+ );
179
160
  })
180
- .flat();
181
- // Wait for all promises in the current user batch to resolve
182
- await Promise.allSettled(userBatchPromises);
183
- console.log(
184
- `Completed user batch ${i + 1} of ${userBatchesAll.length}`
185
- );
161
+ .map((item) => {
162
+ return usersController.createUserAndReturn(item.finalData);
163
+ });
164
+ await Promise.all(userBatchPromises);
165
+ for (const item of batch) {
166
+ if (item && item.finalData) {
167
+ dataLoader.userExistsMap.set(
168
+ item.finalData.userId ||
169
+ item.finalData.docId ||
170
+ item.context.userId ||
171
+ item.context.docId,
172
+ true
173
+ );
174
+ }
175
+ }
176
+ console.log("Finished importing users batch", batch.length);
186
177
  }
178
+ // for (let i = 0; i < usersData.length; i++) {
179
+ // const user = usersData[i];
180
+ // if (user.finalData) {
181
+ // const userId =
182
+ // user.finalData.userId ||
183
+ // user.context.userId ||
184
+ // user.context.docId;
185
+ // if (!dataLoader.userExistsMap.has(userId)) {
186
+ // if (!user.finalData.userId) {
187
+ // user.finalData.userId = userId;
188
+ // }
189
+ // await usersController.createUserAndReturn(user.finalData);
190
+ // dataLoader.userExistsMap.set(userId, true);
191
+ // } else {
192
+ // console.log("Skipped existing user: ", userId);
193
+ // }
194
+ // }
195
+ // }
187
196
  console.log("Finished importing users");
188
197
  }
189
198
  }
@@ -216,10 +225,10 @@ export class ImportController {
216
225
  console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
217
226
  const batchPromises = batches.map((item) => {
218
227
  const id =
219
- item.context.docId ||
220
- item.context.userId ||
221
228
  item.finalData.docId ||
222
- item.finalData.userId;
229
+ item.finalData.userId ||
230
+ item.context.docId ||
231
+ item.context.userId;
223
232
  if (item.finalData.hasOwnProperty("userId")) {
224
233
  delete item.finalData.userId;
225
234
  }
@@ -229,22 +238,12 @@ export class ImportController {
229
238
  if (!item.finalData) {
230
239
  return Promise.resolve();
231
240
  }
232
- return this.database
233
- .createDocument(db.$id, collection.$id, id, item.finalData)
234
- .then(() => {
235
- processedItems++;
236
- console.log("Created item");
237
- })
238
- .catch((error) => {
239
- console.error(
240
- `Error creating item in ${collection.name}:`,
241
- error,
242
- "\nItem data is ",
243
- item.finalData
244
- );
245
- throw error;
246
- // Optionally, log the failed item for retry or review
247
- });
241
+ return this.database.createDocument(
242
+ db.$id,
243
+ collection.$id,
244
+ id,
245
+ item.finalData
246
+ );
248
247
  });
249
248
  // Wait for all promises in the current batch to resolve
250
249
  await Promise.allSettled(batchPromises);
@@ -132,6 +132,7 @@ export class UsersController {
132
132
  if (e instanceof Error) {
133
133
  logger.error("FAILED CREATING USER: ", e.message);
134
134
  }
135
+ console.log("FAILED CREATING USER: ", e);
135
136
  throw e;
136
137
  }
137
138
  }