appwrite-utils-cli 0.0.251 → 0.0.253

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/dist/main.js CHANGED
@@ -13,6 +13,7 @@ async function main() {
13
13
  let generateSchemas = false;
14
14
  let importData = false;
15
15
  let wipeDocuments = false;
16
+ let shouldWriteFile = false;
16
17
  if (args.includes("--prod")) {
17
18
  runProd = true;
18
19
  }
@@ -40,6 +41,9 @@ async function main() {
40
41
  if (args.includes("--wipe-users") || args.includes("--wipeUsers")) {
41
42
  wipeUsers = true;
42
43
  }
44
+ if (args.includes("--write-data") || args.includes("--writeData")) {
45
+ shouldWriteFile = true;
46
+ }
43
47
  if (args.includes("--init")) {
44
48
  await controller.run({
45
49
  runProd: runProd,
@@ -53,6 +57,7 @@ async function main() {
53
57
  generateMockData: false,
54
58
  importData: false,
55
59
  checkDuplicates: false,
60
+ shouldWriteFile: shouldWriteFile,
56
61
  });
57
62
  }
58
63
  else {
@@ -68,6 +73,7 @@ async function main() {
68
73
  wipeUsers: wipeUsers,
69
74
  importData: importData,
70
75
  checkDuplicates: false,
76
+ shouldWriteFile: shouldWriteFile,
71
77
  });
72
78
  }
73
79
  }
@@ -4,14 +4,6 @@ import { z } from "zod";
4
4
  import { type Databases } from "node-appwrite";
5
5
  export declare const CollectionImportDataSchema: z.ZodObject<{
6
6
  collection: z.ZodOptional<z.ZodObject<Omit<{
7
- /**
8
- * Prepares user data by checking for duplicates based on email or phone, adding to a duplicate map if found,
9
- * and then returning the transformed item without user-specific keys.
10
- *
11
- * @param item - The raw item to be processed.
12
- * @param attributeMappings - The attribute mappings for the item.
13
- * @returns The transformed item with user-specific keys removed.
14
- */
15
7
  $id: z.ZodDefault<z.ZodOptional<z.ZodString>>;
16
8
  $createdAt: z.ZodString;
17
9
  $updatedAt: z.ZodString;
@@ -1567,7 +1559,8 @@ export declare class DataLoader {
1567
1559
  private emailToUserIdMap;
1568
1560
  private phoneToUserIdMap;
1569
1561
  userExistsMap: Map<string, boolean>;
1570
- constructor(appwriteFolderPath: string, importDataActions: ImportDataActions, database: Databases, config: AppwriteConfig);
1562
+ private shouldWriteFile;
1563
+ constructor(appwriteFolderPath: string, importDataActions: ImportDataActions, database: Databases, config: AppwriteConfig, shouldWriteFile?: boolean);
1571
1564
  getCollectionKey(name: string): string;
1572
1565
  loadData(importDef: ImportDef): Promise<any[]>;
1573
1566
  checkMapValuesForId(newId: string, collectionName: string): string | false;
@@ -1586,6 +1579,7 @@ export declare class DataLoader {
1586
1579
  getAllUsers(): Promise<import("node-appwrite").Models.User<import("node-appwrite").Models.Preferences>[]>;
1587
1580
  start(dbId: string): Promise<void>;
1588
1581
  updateReferencesInRelatedCollections(): Promise<void>;
1582
+ private writeMapsToJsonFile;
1589
1583
  /**
1590
1584
  * Prepares user data by checking for duplicates based on email or phone, adding to a duplicate map if found,
1591
1585
  * and then returning the transformed item without user-specific keys.
@@ -45,13 +45,15 @@ export class DataLoader {
45
45
  emailToUserIdMap = new Map();
46
46
  phoneToUserIdMap = new Map();
47
47
  userExistsMap = new Map();
48
+ shouldWriteFile = false;
48
49
  // Constructor to initialize the DataLoader with necessary configurations
49
- constructor(appwriteFolderPath, importDataActions, database, config) {
50
+ constructor(appwriteFolderPath, importDataActions, database, config, shouldWriteFile) {
50
51
  this.appwriteFolderPath = appwriteFolderPath;
51
52
  this.importDataActions = importDataActions;
52
53
  this.database = database;
53
54
  this.usersController = new UsersController(config, database);
54
55
  this.config = config;
56
+ this.shouldWriteFile = shouldWriteFile || false;
55
57
  }
56
58
  // Helper method to generate a consistent key for collections
57
59
  getCollectionKey(name) {
@@ -156,7 +158,14 @@ export class DataLoader {
156
158
  async getAllUsers() {
157
159
  const users = new UsersController(this.config, this.database);
158
160
  const allUsers = await users.getAllUsers();
161
+ // Iterate over the users and setup our maps ahead of time for email and phone
159
162
  for (const user of allUsers) {
163
+ if (user.email) {
164
+ this.emailToUserIdMap.set(user.email, user.$id);
165
+ }
166
+ if (user.phone) {
167
+ this.phoneToUserIdMap.set(user.phone, user.$id);
168
+ }
160
169
  this.userExistsMap.set(user.$id, true);
161
170
  }
162
171
  return allUsers;
@@ -169,15 +178,6 @@ export class DataLoader {
169
178
  await this.setupMaps(dbId);
170
179
  const allUsers = await this.getAllUsers();
171
180
  console.log(`Fetched ${allUsers.length} users`);
172
- // Iterate over the users and setup our maps ahead of time for email and phone
173
- for (const user of allUsers) {
174
- if (user.email) {
175
- this.emailToUserIdMap.set(user.email, user.$id);
176
- }
177
- if (user.phone) {
178
- this.phoneToUserIdMap.set(user.phone, user.$id);
179
- }
180
- }
181
181
  // Iterate over the configured databases to find the matching one
182
182
  for (const db of this.config.databases) {
183
183
  if (db.$id !== dbId) {
@@ -222,7 +222,9 @@ export class DataLoader {
222
222
  console.log("---------------------------------");
223
223
  console.log(`Data setup for database: ${dbId} completed`);
224
224
  console.log("---------------------------------");
225
- // this.writeMapsToJsonFile();
225
+ if (this.shouldWriteFile) {
226
+ this.writeMapsToJsonFile();
227
+ }
226
228
  }
227
229
  async updateReferencesInRelatedCollections() {
228
230
  // Iterate over each collection configuration
@@ -350,43 +352,36 @@ export class DataLoader {
350
352
  // }
351
353
  // }
352
354
  // }
353
- // private writeMapsToJsonFile() {
354
- // const outputDir = path.resolve(process.cwd());
355
- // const outputFile = path.join(outputDir, "dataLoaderOutput.json");
356
- // const dataToWrite = {
357
- // dataFromCollections: Array.from(this.importMap.entries()).map(
358
- // ([key, value]) => {
359
- // return {
360
- // collection: key,
361
- // data: value.data.map((item: any) => item.finalData),
362
- // };
363
- // }
364
- // ),
365
- // // Convert Maps to arrays of entries for serialization
366
- // mergedUserMap: Array.from(this.mergedUserMap.entries()),
367
- // // emailToUserIdMap: Array.from(this.emailToUserIdMap.entries()),
368
- // // phoneToUserIdMap: Array.from(this.phoneToUserIdMap.entries()),
369
- // };
370
- // // Use JSON.stringify with a replacer function to handle Maps
371
- // const replacer = (key: any, value: any) => {
372
- // if (value instanceof Map) {
373
- // return Array.from(value.entries());
374
- // }
375
- // return value;
376
- // };
377
- // fs.writeFile(
378
- // outputFile,
379
- // JSON.stringify(dataToWrite, replacer, 2),
380
- // "utf8",
381
- // (err) => {
382
- // if (err) {
383
- // console.error("Error writing data to JSON file:", err);
384
- // return;
385
- // }
386
- // console.log(`Data successfully written to ${outputFile}`);
387
- // }
388
- // );
389
- // }
355
+ writeMapsToJsonFile() {
356
+ const outputDir = path.resolve(process.cwd());
357
+ const outputFile = path.join(outputDir, "dataLoaderOutput.json");
358
+ const dataToWrite = {
359
+ dataFromCollections: Array.from(this.importMap.entries()).map(([key, value]) => {
360
+ return {
361
+ collection: key,
362
+ data: value.data.map((item) => item.finalData),
363
+ };
364
+ }),
365
+ // Convert Maps to arrays of entries for serialization
366
+ mergedUserMap: Array.from(this.mergedUserMap.entries()),
367
+ // emailToUserIdMap: Array.from(this.emailToUserIdMap.entries()),
368
+ // phoneToUserIdMap: Array.from(this.phoneToUserIdMap.entries()),
369
+ };
370
+ // Use JSON.stringify with a replacer function to handle Maps
371
+ const replacer = (key, value) => {
372
+ if (value instanceof Map) {
373
+ return Array.from(value.entries());
374
+ }
375
+ return value;
376
+ };
377
+ fs.writeFile(outputFile, JSON.stringify(dataToWrite, replacer, 2), "utf8", (err) => {
378
+ if (err) {
379
+ console.error("Error writing data to JSON file:", err);
380
+ return;
381
+ }
382
+ console.log(`Data successfully written to ${outputFile}`);
383
+ });
384
+ }
390
385
  /**
391
386
  * Prepares user data by checking for duplicates based on email or phone, adding to a duplicate map if found,
392
387
  * and then returning the transformed item without user-specific keys.
@@ -58,7 +58,7 @@ export class ImportController {
58
58
  console.log(`Starting import data for database: ${db.name}`);
59
59
  console.log(`---------------------------------`);
60
60
  // await this.importCollections(db);
61
- const dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config);
61
+ const dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config, this.setupOptions.shouldWriteFile);
62
62
  await dataLoader.start(db.$id);
63
63
  await this.importCollections(db, dataLoader);
64
64
  await resolveAndUpdateRelationships(db.$id, this.database, this.config);
@@ -103,7 +103,6 @@ export class ImportDataActions {
103
103
  for (const mapping of attributeMap) {
104
104
  const { postImportActions } = mapping;
105
105
  if (!postImportActions || !Array.isArray(postImportActions)) {
106
- console.warn(`No post-import actions defined for attribute: ${mapping.targetKey}`, postImportActions);
107
106
  continue; // Skip to the next attribute if no actions are defined
108
107
  }
109
108
  for (const actionDef of postImportActions) {
@@ -13,6 +13,7 @@ export interface SetupOptions {
13
13
  generateMockData: boolean;
14
14
  importData: boolean;
15
15
  checkDuplicates: boolean;
16
+ shouldWriteFile: boolean;
16
17
  }
17
18
  export declare class UtilsController {
18
19
  private appwriteFolderPath;
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.251",
4
+ "version": "0.0.253",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
package/src/main.ts CHANGED
@@ -16,6 +16,7 @@ async function main() {
16
16
  let generateSchemas = false;
17
17
  let importData = false;
18
18
  let wipeDocuments = false;
19
+ let shouldWriteFile = false;
19
20
  if (args.includes("--prod")) {
20
21
  runProd = true;
21
22
  }
@@ -43,6 +44,9 @@ async function main() {
43
44
  if (args.includes("--wipe-users") || args.includes("--wipeUsers")) {
44
45
  wipeUsers = true;
45
46
  }
47
+ if (args.includes("--write-data") || args.includes("--writeData")) {
48
+ shouldWriteFile = true;
49
+ }
46
50
  if (args.includes("--init")) {
47
51
  await controller.run({
48
52
  runProd: runProd,
@@ -56,6 +60,7 @@ async function main() {
56
60
  generateMockData: false,
57
61
  importData: false,
58
62
  checkDuplicates: false,
63
+ shouldWriteFile: shouldWriteFile,
59
64
  });
60
65
  } else {
61
66
  await controller.run({
@@ -70,6 +75,7 @@ async function main() {
70
75
  wipeUsers: wipeUsers,
71
76
  importData: importData,
72
77
  checkDuplicates: false,
78
+ shouldWriteFile: shouldWriteFile,
73
79
  });
74
80
  }
75
81
  }
@@ -63,19 +63,22 @@ export class DataLoader {
63
63
  private emailToUserIdMap = new Map<string, string>();
64
64
  private phoneToUserIdMap = new Map<string, string>();
65
65
  userExistsMap = new Map<string, boolean>();
66
+ private shouldWriteFile = false;
66
67
 
67
68
  // Constructor to initialize the DataLoader with necessary configurations
68
69
  constructor(
69
70
  appwriteFolderPath: string,
70
71
  importDataActions: ImportDataActions,
71
72
  database: Databases,
72
- config: AppwriteConfig
73
+ config: AppwriteConfig,
74
+ shouldWriteFile?: boolean
73
75
  ) {
74
76
  this.appwriteFolderPath = appwriteFolderPath;
75
77
  this.importDataActions = importDataActions;
76
78
  this.database = database;
77
79
  this.usersController = new UsersController(config, database);
78
80
  this.config = config;
81
+ this.shouldWriteFile = shouldWriteFile || false;
79
82
  }
80
83
 
81
84
  // Helper method to generate a consistent key for collections
@@ -213,7 +216,14 @@ export class DataLoader {
213
216
  async getAllUsers() {
214
217
  const users = new UsersController(this.config, this.database);
215
218
  const allUsers = await users.getAllUsers();
219
+ // Iterate over the users and setup our maps ahead of time for email and phone
216
220
  for (const user of allUsers) {
221
+ if (user.email) {
222
+ this.emailToUserIdMap.set(user.email, user.$id);
223
+ }
224
+ if (user.phone) {
225
+ this.phoneToUserIdMap.set(user.phone, user.$id);
226
+ }
217
227
  this.userExistsMap.set(user.$id, true);
218
228
  }
219
229
  return allUsers;
@@ -227,15 +237,6 @@ export class DataLoader {
227
237
  await this.setupMaps(dbId);
228
238
  const allUsers = await this.getAllUsers();
229
239
  console.log(`Fetched ${allUsers.length} users`);
230
- // Iterate over the users and setup our maps ahead of time for email and phone
231
- for (const user of allUsers) {
232
- if (user.email) {
233
- this.emailToUserIdMap.set(user.email, user.$id);
234
- }
235
- if (user.phone) {
236
- this.phoneToUserIdMap.set(user.phone, user.$id);
237
- }
238
- }
239
240
  // Iterate over the configured databases to find the matching one
240
241
  for (const db of this.config.databases) {
241
242
  if (db.$id !== dbId) {
@@ -290,7 +291,9 @@ export class DataLoader {
290
291
  console.log("---------------------------------");
291
292
  console.log(`Data setup for database: ${dbId} completed`);
292
293
  console.log("---------------------------------");
293
- // this.writeMapsToJsonFile();
294
+ if (this.shouldWriteFile) {
295
+ this.writeMapsToJsonFile();
296
+ }
294
297
  }
295
298
 
296
299
  async updateReferencesInRelatedCollections() {
@@ -446,46 +449,46 @@ export class DataLoader {
446
449
  // }
447
450
  // }
448
451
 
449
- // private writeMapsToJsonFile() {
450
- // const outputDir = path.resolve(process.cwd());
451
- // const outputFile = path.join(outputDir, "dataLoaderOutput.json");
452
+ private writeMapsToJsonFile() {
453
+ const outputDir = path.resolve(process.cwd());
454
+ const outputFile = path.join(outputDir, "dataLoaderOutput.json");
452
455
 
453
- // const dataToWrite = {
454
- // dataFromCollections: Array.from(this.importMap.entries()).map(
455
- // ([key, value]) => {
456
- // return {
457
- // collection: key,
458
- // data: value.data.map((item: any) => item.finalData),
459
- // };
460
- // }
461
- // ),
462
- // // Convert Maps to arrays of entries for serialization
463
- // mergedUserMap: Array.from(this.mergedUserMap.entries()),
464
- // // emailToUserIdMap: Array.from(this.emailToUserIdMap.entries()),
465
- // // phoneToUserIdMap: Array.from(this.phoneToUserIdMap.entries()),
466
- // };
456
+ const dataToWrite = {
457
+ dataFromCollections: Array.from(this.importMap.entries()).map(
458
+ ([key, value]) => {
459
+ return {
460
+ collection: key,
461
+ data: value.data.map((item: any) => item.finalData),
462
+ };
463
+ }
464
+ ),
465
+ // Convert Maps to arrays of entries for serialization
466
+ mergedUserMap: Array.from(this.mergedUserMap.entries()),
467
+ // emailToUserIdMap: Array.from(this.emailToUserIdMap.entries()),
468
+ // phoneToUserIdMap: Array.from(this.phoneToUserIdMap.entries()),
469
+ };
467
470
 
468
- // // Use JSON.stringify with a replacer function to handle Maps
469
- // const replacer = (key: any, value: any) => {
470
- // if (value instanceof Map) {
471
- // return Array.from(value.entries());
472
- // }
473
- // return value;
474
- // };
471
+ // Use JSON.stringify with a replacer function to handle Maps
472
+ const replacer = (key: any, value: any) => {
473
+ if (value instanceof Map) {
474
+ return Array.from(value.entries());
475
+ }
476
+ return value;
477
+ };
475
478
 
476
- // fs.writeFile(
477
- // outputFile,
478
- // JSON.stringify(dataToWrite, replacer, 2),
479
- // "utf8",
480
- // (err) => {
481
- // if (err) {
482
- // console.error("Error writing data to JSON file:", err);
483
- // return;
484
- // }
485
- // console.log(`Data successfully written to ${outputFile}`);
486
- // }
487
- // );
488
- // }
479
+ fs.writeFile(
480
+ outputFile,
481
+ JSON.stringify(dataToWrite, replacer, 2),
482
+ "utf8",
483
+ (err) => {
484
+ if (err) {
485
+ console.error("Error writing data to JSON file:", err);
486
+ return;
487
+ }
488
+ console.log(`Data successfully written to ${outputFile}`);
489
+ }
490
+ );
491
+ }
489
492
 
490
493
  /**
491
494
  * Prepares user data by checking for duplicates based on email or phone, adding to a duplicate map if found,
@@ -107,7 +107,8 @@ export class ImportController {
107
107
  this.appwriteFolderPath,
108
108
  this.importDataActions,
109
109
  this.database,
110
- this.config
110
+ this.config,
111
+ this.setupOptions.shouldWriteFile
111
112
  );
112
113
  await dataLoader.start(db.$id);
113
114
  await this.importCollections(db, dataLoader);
@@ -161,10 +161,6 @@ export class ImportDataActions {
161
161
  for (const mapping of attributeMap) {
162
162
  const { postImportActions } = mapping;
163
163
  if (!postImportActions || !Array.isArray(postImportActions)) {
164
- console.warn(
165
- `No post-import actions defined for attribute: ${mapping.targetKey}`,
166
- postImportActions
167
- );
168
164
  continue; // Skip to the next attribute if no actions are defined
169
165
  }
170
166
  for (const actionDef of postImportActions) {
@@ -46,6 +46,7 @@ export interface SetupOptions {
46
46
  generateMockData: boolean;
47
47
  importData: boolean;
48
48
  checkDuplicates: boolean;
49
+ shouldWriteFile: boolean;
49
50
  }
50
51
 
51
52
  type CollectionConfig = AppwriteConfig["collections"];