appwrite-utils-cli 0.0.285 → 0.9.0

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 (109) hide show
  1. package/README.md +122 -96
  2. package/dist/collections/attributes.d.ts +4 -0
  3. package/dist/collections/attributes.js +224 -0
  4. package/dist/collections/indexes.d.ts +4 -0
  5. package/dist/collections/indexes.js +27 -0
  6. package/dist/collections/methods.d.ts +16 -0
  7. package/dist/collections/methods.js +216 -0
  8. package/dist/databases/methods.d.ts +6 -0
  9. package/dist/databases/methods.js +33 -0
  10. package/dist/interactiveCLI.d.ts +19 -0
  11. package/dist/interactiveCLI.js +555 -0
  12. package/dist/main.js +227 -62
  13. package/dist/migrations/afterImportActions.js +37 -40
  14. package/dist/migrations/appwriteToX.d.ts +26 -25
  15. package/dist/migrations/appwriteToX.js +42 -6
  16. package/dist/migrations/attributes.js +21 -20
  17. package/dist/migrations/backup.d.ts +93 -87
  18. package/dist/migrations/collections.d.ts +6 -0
  19. package/dist/migrations/collections.js +149 -20
  20. package/dist/migrations/converters.d.ts +2 -18
  21. package/dist/migrations/converters.js +13 -2
  22. package/dist/migrations/dataLoader.d.ts +276 -161
  23. package/dist/migrations/dataLoader.js +535 -292
  24. package/dist/migrations/databases.js +8 -2
  25. package/dist/migrations/helper.d.ts +3 -0
  26. package/dist/migrations/helper.js +21 -0
  27. package/dist/migrations/importController.d.ts +5 -2
  28. package/dist/migrations/importController.js +125 -88
  29. package/dist/migrations/importDataActions.d.ts +9 -1
  30. package/dist/migrations/importDataActions.js +15 -3
  31. package/dist/migrations/indexes.js +3 -2
  32. package/dist/migrations/logging.js +20 -8
  33. package/dist/migrations/migrationHelper.d.ts +9 -4
  34. package/dist/migrations/migrationHelper.js +6 -5
  35. package/dist/migrations/openapi.d.ts +1 -1
  36. package/dist/migrations/openapi.js +33 -18
  37. package/dist/migrations/queue.js +3 -2
  38. package/dist/migrations/relationships.d.ts +2 -2
  39. package/dist/migrations/schemaStrings.js +53 -41
  40. package/dist/migrations/setupDatabase.d.ts +2 -4
  41. package/dist/migrations/setupDatabase.js +24 -105
  42. package/dist/migrations/storage.d.ts +3 -1
  43. package/dist/migrations/storage.js +110 -16
  44. package/dist/migrations/transfer.d.ts +30 -0
  45. package/dist/migrations/transfer.js +337 -0
  46. package/dist/migrations/users.d.ts +2 -1
  47. package/dist/migrations/users.js +78 -43
  48. package/dist/schemas/authUser.d.ts +2 -2
  49. package/dist/storage/methods.d.ts +15 -0
  50. package/dist/storage/methods.js +207 -0
  51. package/dist/storage/schemas.d.ts +687 -0
  52. package/dist/storage/schemas.js +175 -0
  53. package/dist/utils/getClientFromConfig.d.ts +4 -0
  54. package/dist/utils/getClientFromConfig.js +16 -0
  55. package/dist/utils/helperFunctions.d.ts +11 -1
  56. package/dist/utils/helperFunctions.js +38 -0
  57. package/dist/utils/retryFailedPromises.d.ts +2 -0
  58. package/dist/utils/retryFailedPromises.js +21 -0
  59. package/dist/utils/schemaStrings.d.ts +13 -0
  60. package/dist/utils/schemaStrings.js +403 -0
  61. package/dist/utils/setupFiles.js +110 -61
  62. package/dist/utilsController.d.ts +40 -22
  63. package/dist/utilsController.js +164 -84
  64. package/package.json +13 -15
  65. package/src/collections/attributes.ts +483 -0
  66. package/src/collections/indexes.ts +53 -0
  67. package/src/collections/methods.ts +331 -0
  68. package/src/databases/methods.ts +47 -0
  69. package/src/init.ts +64 -64
  70. package/src/interactiveCLI.ts +767 -0
  71. package/src/main.ts +292 -83
  72. package/src/migrations/afterImportActions.ts +553 -490
  73. package/src/migrations/appwriteToX.ts +237 -174
  74. package/src/migrations/attributes.ts +483 -422
  75. package/src/migrations/backup.ts +205 -205
  76. package/src/migrations/collections.ts +545 -300
  77. package/src/migrations/converters.ts +161 -150
  78. package/src/migrations/dataLoader.ts +1615 -1304
  79. package/src/migrations/databases.ts +44 -25
  80. package/src/migrations/dbHelpers.ts +92 -92
  81. package/src/migrations/helper.ts +40 -0
  82. package/src/migrations/importController.ts +448 -384
  83. package/src/migrations/importDataActions.ts +315 -307
  84. package/src/migrations/indexes.ts +40 -37
  85. package/src/migrations/logging.ts +29 -16
  86. package/src/migrations/migrationHelper.ts +207 -201
  87. package/src/migrations/openapi.ts +83 -70
  88. package/src/migrations/queue.ts +118 -119
  89. package/src/migrations/relationships.ts +324 -324
  90. package/src/migrations/schemaStrings.ts +472 -460
  91. package/src/migrations/setupDatabase.ts +118 -219
  92. package/src/migrations/storage.ts +538 -358
  93. package/src/migrations/transfer.ts +608 -0
  94. package/src/migrations/users.ts +362 -285
  95. package/src/migrations/validationRules.ts +63 -63
  96. package/src/schemas/authUser.ts +23 -23
  97. package/src/setup.ts +8 -8
  98. package/src/storage/methods.ts +371 -0
  99. package/src/storage/schemas.ts +205 -0
  100. package/src/types.ts +9 -9
  101. package/src/utils/getClientFromConfig.ts +17 -0
  102. package/src/utils/helperFunctions.ts +181 -127
  103. package/src/utils/index.ts +2 -2
  104. package/src/utils/loadConfigs.ts +59 -59
  105. package/src/utils/retryFailedPromises.ts +27 -0
  106. package/src/utils/schemaStrings.ts +473 -0
  107. package/src/utils/setupFiles.ts +228 -182
  108. package/src/utilsController.ts +325 -194
  109. package/tsconfig.json +37 -37
@@ -1,7 +1,13 @@
1
- import { Databases, Query } from "node-appwrite";
1
+ import { Client, Databases, IndexType, Query, } from "node-appwrite";
2
+ import { getAppwriteClient, tryAwaitWithRetry, } from "../utils/helperFunctions.js";
3
+ import { transferDocumentsBetweenDbsLocalToLocal, transferDocumentsBetweenDbsLocalToRemote, } from "./collections.js";
4
+ import { createOrUpdateAttribute } from "./attributes.js";
5
+ import { parseAttribute } from "appwrite-utils";
2
6
  export const fetchAllDatabases = async (database) => {
3
- const databases = await database.list([Query.limit(25)]);
7
+ const databases = await tryAwaitWithRetry(async () => await database.list([Query.limit(25)]));
4
8
  const allDatabases = databases.databases;
9
+ if (allDatabases.length === 0)
10
+ return [];
5
11
  let lastDatabaseId = allDatabases[allDatabases.length - 1].$id;
6
12
  if (databases.databases.length < 25) {
7
13
  return allDatabases;
@@ -0,0 +1,3 @@
1
+ import type { Databases, Models } from "node-appwrite";
2
+ import type { OperationCreate } from "../storage/schemas.js";
3
+ export declare const logOperation: (db: Databases, dbId: string, operationDetails: OperationCreate, operationId?: string) => Promise<Models.Document>;
@@ -0,0 +1,21 @@
1
+ import { tryAwaitWithRetry } from "appwrite-utils";
2
+ import { ulid } from "ulidx";
3
+ export const logOperation = async (db, dbId, operationDetails, operationId) => {
4
+ try {
5
+ let operation;
6
+ if (operationId) {
7
+ // Update existing operation log
8
+ operation = await tryAwaitWithRetry(async () => await db.updateDocument("migrations", "currentOperations", operationId, operationDetails));
9
+ }
10
+ else {
11
+ // Create new operation log
12
+ operation = await db.createDocument("migrations", "currentOperations", ulid(), operationDetails);
13
+ }
14
+ console.log(`Operation logged: ${operation.$id}`);
15
+ return operation;
16
+ }
17
+ catch (error) {
18
+ console.error(`Error logging operation: ${error}`);
19
+ throw error;
20
+ }
21
+ };
@@ -1,4 +1,4 @@
1
- import { type Databases, type Storage } from "node-appwrite";
1
+ import { type Databases, type Models, type Storage } from "node-appwrite";
2
2
  import type { AppwriteConfig, ConfigDatabase } from "appwrite-utils";
3
3
  import type { ImportDataActions } from "./importDataActions.js";
4
4
  import type { SetupOptions } from "../utilsController.js";
@@ -12,9 +12,12 @@ export declare class ImportController {
12
12
  private setupOptions;
13
13
  private documentCache;
14
14
  private batchLimit;
15
+ private hasImportedUsers;
15
16
  private postImportActionsQueue;
16
- constructor(config: AppwriteConfig, database: Databases, storage: Storage, appwriteFolderPath: string, importDataActions: ImportDataActions, setupOptions: SetupOptions);
17
+ private databasesToRun;
18
+ constructor(config: AppwriteConfig, database: Databases, storage: Storage, appwriteFolderPath: string, importDataActions: ImportDataActions, setupOptions: SetupOptions, databasesToRun?: Models.Database[]);
17
19
  run(): Promise<void>;
20
+ updateOthersToFinalData(updatedDb: Models.Database, targetDb: Models.Database): Promise<void>;
18
21
  importCollections(db: ConfigDatabase, dataLoader: DataLoader): Promise<void>;
19
22
  executePostImportActions(dbId: string, dataLoader: DataLoader): Promise<void>;
20
23
  }
@@ -1,12 +1,13 @@
1
- import { ID, Query, } from "node-appwrite";
1
+ import { AppwriteException, ID, Query, } from "node-appwrite";
2
2
  import _ from "lodash";
3
- import { areCollectionNamesSame } from "../utils/index.js";
3
+ import { areCollectionNamesSame, tryAwaitWithRetry } from "../utils/index.js";
4
4
  import { resolveAndUpdateRelationships } from "./relationships.js";
5
5
  import { UsersController } from "./users.js";
6
6
  import { logger } from "./logging.js";
7
7
  import { updateOperation } from "./migrationHelper.js";
8
8
  import { BatchSchema, OperationCreateSchema, OperationSchema, } from "./backup.js";
9
9
  import { DataLoader } from "./dataLoader.js";
10
+ import { transferDatabaseLocalToLocal, transferStorageLocalToLocal, } from "./transfer.js";
10
11
  export class ImportController {
11
12
  config;
12
13
  database;
@@ -16,8 +17,10 @@ export class ImportController {
16
17
  setupOptions;
17
18
  documentCache;
18
19
  batchLimit = 25; // Define batch size limit
20
+ hasImportedUsers = false;
19
21
  postImportActionsQueue = [];
20
- constructor(config, database, storage, appwriteFolderPath, importDataActions, setupOptions) {
22
+ databasesToRun;
23
+ constructor(config, database, storage, appwriteFolderPath, importDataActions, setupOptions, databasesToRun) {
21
24
  this.config = config;
22
25
  this.database = database;
23
26
  this.storage = storage;
@@ -25,43 +28,54 @@ export class ImportController {
25
28
  this.importDataActions = importDataActions;
26
29
  this.setupOptions = setupOptions;
27
30
  this.documentCache = new Map();
31
+ this.databasesToRun = databasesToRun || [];
28
32
  }
29
33
  async run() {
30
- const databasesToRun = this.config.databases
31
- .filter((db) => (areCollectionNamesSame(db.name, this.config.databases[0].name) &&
32
- this.setupOptions.runProd) ||
33
- (areCollectionNamesSame(db.name, this.config.databases[1].name) &&
34
- this.setupOptions.runStaging) ||
35
- (areCollectionNamesSame(db.name, this.config.databases[2].name) &&
36
- this.setupOptions.runDev))
37
- .map((db) => db.name);
38
- for (let db of this.config.databases) {
39
- if (db.name.toLowerCase().trim().replace(" ", "") === "migrations" ||
40
- !databasesToRun.includes(db.name)) {
34
+ let databasesToProcess;
35
+ if (this.databasesToRun.length > 0) {
36
+ // Use the provided databases
37
+ databasesToProcess = this.databasesToRun;
38
+ }
39
+ else {
40
+ // If no databases are specified, fetch all databases
41
+ const allDatabases = await this.database.list();
42
+ databasesToProcess = allDatabases.databases;
43
+ }
44
+ let dataLoader;
45
+ let databaseRan;
46
+ for (let db of databasesToProcess) {
47
+ if (db.name.toLowerCase().trim().replace(" ", "") === "migrations") {
41
48
  continue;
42
49
  }
43
- if (!db.$id) {
44
- const databases = await this.database.list([
45
- Query.equal("name", db.name),
46
- ]);
47
- if (databases.databases.length > 0) {
48
- db.$id = databases.databases[0].$id;
49
- }
50
- }
51
50
  console.log(`---------------------------------`);
52
51
  console.log(`Starting import data for database: ${db.name}`);
53
52
  console.log(`---------------------------------`);
54
- // await this.importCollections(db);
55
- const dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config, this.setupOptions.shouldWriteFile);
56
- await dataLoader.start(db.$id);
57
- await this.importCollections(db, dataLoader);
58
- await resolveAndUpdateRelationships(db.$id, this.database, this.config);
59
- await this.executePostImportActions(db.$id, dataLoader);
53
+ if (!databaseRan) {
54
+ databaseRan = db;
55
+ dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config, this.setupOptions.shouldWriteFile);
56
+ await dataLoader.start(db.$id);
57
+ await this.importCollections(db, dataLoader);
58
+ await resolveAndUpdateRelationships(db.$id, this.database, this.config);
59
+ await this.executePostImportActions(db.$id, dataLoader);
60
+ }
61
+ else if (databaseRan.$id !== db.$id) {
62
+ await this.updateOthersToFinalData(databaseRan, db);
63
+ }
60
64
  console.log(`---------------------------------`);
61
65
  console.log(`Finished import data for database: ${db.name}`);
62
66
  console.log(`---------------------------------`);
63
67
  }
64
68
  }
69
+ async updateOthersToFinalData(updatedDb, targetDb) {
70
+ await transferDatabaseLocalToLocal(this.database, updatedDb.$id, targetDb.$id);
71
+ // Find the corresponding database configs
72
+ const updatedDbConfig = this.config.databases.find((db) => db.$id === updatedDb.$id);
73
+ const targetDbConfig = this.config.databases.find((db) => db.$id === targetDb.$id);
74
+ // Transfer database-specific bucket if both databases have a bucket defined
75
+ if (updatedDbConfig?.bucket && targetDbConfig?.bucket) {
76
+ await transferStorageLocalToLocal(this.storage, updatedDbConfig.bucket.$id, targetDbConfig.bucket.$id);
77
+ }
78
+ }
65
79
  async importCollections(db, dataLoader) {
66
80
  if (!this.config.collections) {
67
81
  return;
@@ -71,7 +85,7 @@ export class ImportController {
71
85
  dataLoader.getCollectionKey(collection.name);
72
86
  const importOperationId = dataLoader.collectionImportOperations.get(dataLoader.getCollectionKey(collection.name));
73
87
  const createBatches = (finalData) => {
74
- let maxBatchLength = 50;
88
+ let maxBatchLength = 100;
75
89
  const finalBatches = [];
76
90
  for (let i = 0; i < finalData.length; i++) {
77
91
  if (i % maxBatchLength === 0) {
@@ -81,49 +95,50 @@ export class ImportController {
81
95
  }
82
96
  return finalBatches;
83
97
  };
84
- if (isUsersCollection) {
98
+ if (isUsersCollection && !this.hasImportedUsers) {
85
99
  const usersDataMap = dataLoader.importMap.get(dataLoader.getCollectionKey("users"));
86
100
  const usersData = usersDataMap?.data;
87
101
  const usersController = new UsersController(this.config, this.database);
88
102
  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
- }
103
+ console.log("Found users data", usersData.length);
104
+ const userDataBatches = createBatches(usersData);
105
+ for (const batch of userDataBatches) {
106
+ console.log("Importing users batch", batch.length);
107
+ const userBatchPromises = batch
108
+ .filter((item) => {
109
+ let itemId;
110
+ if (item.finalData.userId) {
111
+ itemId = item.finalData.userId;
112
+ }
113
+ else if (item.finalData.docId) {
114
+ itemId = item.finalData.docId;
120
115
  }
116
+ if (!itemId) {
117
+ return false;
118
+ }
119
+ return (item &&
120
+ item.finalData &&
121
+ !dataLoader.userExistsMap.has(itemId));
121
122
  })
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}`);
123
+ .map((item) => {
124
+ dataLoader.userExistsMap.set(item.finalData.userId ||
125
+ item.finalData.docId ||
126
+ item.context.userId ||
127
+ item.context.docId, true);
128
+ return usersController.createUserAndReturn(item.finalData);
129
+ });
130
+ const promiseResults = await Promise.allSettled(userBatchPromises);
131
+ for (const item of batch) {
132
+ if (item && item.finalData) {
133
+ dataLoader.userExistsMap.set(item.finalData.userId ||
134
+ item.finalData.docId ||
135
+ item.context.userId ||
136
+ item.context.docId, true);
137
+ }
138
+ }
139
+ console.log("Finished importing users batch");
126
140
  }
141
+ this.hasImportedUsers = true;
127
142
  console.log("Finished importing users");
128
143
  }
129
144
  }
@@ -136,6 +151,7 @@ export class ImportController {
136
151
  status: "in_progress",
137
152
  });
138
153
  const collectionData = dataLoader.importMap.get(dataLoader.getCollectionKey(collection.name));
154
+ console.log(`Processing collection: ${collection.name}...`);
139
155
  if (!collectionData) {
140
156
  console.log("No collection data for ", collection.name);
141
157
  continue;
@@ -145,34 +161,55 @@ export class ImportController {
145
161
  for (let i = 0; i < dataSplit.length; i++) {
146
162
  const batches = dataSplit[i];
147
163
  console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
148
- const batchPromises = batches.map((item) => {
149
- const id = item.context.docId ||
150
- item.context.userId ||
151
- item.finalData.docId ||
152
- item.finalData.userId;
153
- if (item.finalData.hasOwnProperty("userId")) {
154
- delete item.finalData.userId;
155
- }
156
- if (item.finalData.hasOwnProperty("docId")) {
157
- delete item.finalData.docId;
164
+ // const documentExistsPromises = batches.map(async (item) => {
165
+ // try {
166
+ // const id =
167
+ // item.finalData.docId ||
168
+ // item.finalData.userId ||
169
+ // item.context.docId ||
170
+ // item.context.userId;
171
+ // if (!item.finalData) {
172
+ // return Promise.resolve(null);
173
+ // }
174
+ // return tryAwaitWithRetry(
175
+ // async () =>
176
+ // await documentExists(
177
+ // this.database,
178
+ // db.$id,
179
+ // collection.$id,
180
+ // item.finalData
181
+ // )
182
+ // );
183
+ // } catch (error) {
184
+ // console.error(error);
185
+ // return Promise.resolve(null);
186
+ // }
187
+ // });
188
+ // const documentExistsResults = await Promise.all(documentExistsPromises);
189
+ const batchPromises = batches.map((item, index) => {
190
+ try {
191
+ const id = item.finalData.docId ||
192
+ item.finalData.userId ||
193
+ item.context.docId ||
194
+ item.context.userId;
195
+ if (item.finalData.hasOwnProperty("userId")) {
196
+ delete item.finalData.userId;
197
+ }
198
+ if (item.finalData.hasOwnProperty("docId")) {
199
+ delete item.finalData.docId;
200
+ }
201
+ if (!item.finalData) {
202
+ return Promise.resolve();
203
+ }
204
+ return tryAwaitWithRetry(async () => await this.database.createDocument(db.$id, collection.$id, id, item.finalData));
158
205
  }
159
- if (!item.finalData) {
206
+ catch (error) {
207
+ console.error(error);
160
208
  return Promise.resolve();
161
209
  }
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
- });
173
210
  });
174
211
  // Wait for all promises in the current batch to resolve
175
- await Promise.allSettled(batchPromises);
212
+ await Promise.all(batchPromises);
176
213
  console.log(`Completed batch ${i + 1} of ${dataSplit.length}`);
177
214
  await updateOperation(this.database, importOperation.$id, {
178
215
  progress: processedItems,
@@ -11,16 +11,24 @@ export declare class ImportDataActions {
11
11
  private validityRuleDefinitions;
12
12
  private afterImportActionsDefinitions;
13
13
  constructor(db: Databases, storage: Storage, config: AppwriteConfig, converterDefinitions: ConverterFunctions, validityRuleDefinitions: ValidationRules, afterImportActionsDefinitions: AfterImportActions);
14
+ /**
15
+ * Runs converter functions on the item based on the provided attribute mappings.
16
+ *
17
+ * @param item - The item to be transformed.
18
+ * @param attributeMappings - The mappings that define how each attribute should be transformed.
19
+ * @returns The transformed item.
20
+ */
14
21
  runConverterFunctions(item: any, attributeMappings: AttributeMappings): any;
15
22
  /**
16
23
  * Validates a single data item based on defined validation rules.
17
24
  * @param item The data item to validate.
25
+ * @param attributeMap The attribute mappings for the data item.
18
26
  * @param context The context for resolving templated parameters in validation rules.
19
27
  * @returns A promise that resolves to true if the item is valid, false otherwise.
20
28
  */
21
29
  validateItem(item: any, attributeMap: AttributeMappings, context: {
22
30
  [key: string]: any;
23
- }): Promise<boolean>;
31
+ }): boolean;
24
32
  executeAfterImportActions(item: any, attributeMap: AttributeMappings, context: {
25
33
  [key: string]: any;
26
34
  }): Promise<void>;
@@ -1,10 +1,11 @@
1
- import { ID, InputFile, Query, } from "node-appwrite";
1
+ import {} from "node-appwrite";
2
2
  import { validationRules, } from "appwrite-utils";
3
3
  import { converterFunctions } from "appwrite-utils";
4
4
  import { convertObjectBySchema } from "./converters.js";
5
5
  import {} from "appwrite-utils";
6
6
  import { afterImportActions } from "./afterImportActions.js";
7
7
  import { logger } from "./logging.js";
8
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
8
9
  export class ImportDataActions {
9
10
  db;
10
11
  storage;
@@ -20,9 +21,19 @@ export class ImportDataActions {
20
21
  this.validityRuleDefinitions = validityRuleDefinitions;
21
22
  this.afterImportActionsDefinitions = afterImportActionsDefinitions;
22
23
  }
24
+ /**
25
+ * Runs converter functions on the item based on the provided attribute mappings.
26
+ *
27
+ * @param item - The item to be transformed.
28
+ * @param attributeMappings - The mappings that define how each attribute should be transformed.
29
+ * @returns The transformed item.
30
+ */
23
31
  runConverterFunctions(item, attributeMappings) {
24
32
  const conversionSchema = attributeMappings.reduce((schema, mapping) => {
25
33
  schema[mapping.targetKey] = (originalValue) => {
34
+ if (!mapping.converters) {
35
+ return originalValue;
36
+ }
26
37
  return mapping.converters?.reduce((value, converterName) => {
27
38
  let shouldProcessAsArray = false;
28
39
  if ((converterName.includes("[Arr]") ||
@@ -65,10 +76,11 @@ export class ImportDataActions {
65
76
  /**
66
77
  * Validates a single data item based on defined validation rules.
67
78
  * @param item The data item to validate.
79
+ * @param attributeMap The attribute mappings for the data item.
68
80
  * @param context The context for resolving templated parameters in validation rules.
69
81
  * @returns A promise that resolves to true if the item is valid, false otherwise.
70
82
  */
71
- async validateItem(item, attributeMap, context) {
83
+ validateItem(item, attributeMap, context) {
72
84
  for (const mapping of attributeMap) {
73
85
  const { validationActions } = mapping;
74
86
  if (!validationActions ||
@@ -111,7 +123,7 @@ export class ImportDataActions {
111
123
  const { action, params } = actionDef;
112
124
  console.log(`Executing post-import action '${action}' for attribute '${mapping.targetKey}' with params ${params.join(", ")}...`);
113
125
  try {
114
- await this.executeAction(action, params, context, item);
126
+ await tryAwaitWithRetry(async () => await this.executeAction(action, params, context, item));
115
127
  }
116
128
  catch (error) {
117
129
  logger.error(`Failed to execute post-import action '${action}' for attribute '${mapping.targetKey}':`, error);
@@ -1,5 +1,6 @@
1
1
  import { indexSchema } from "appwrite-utils";
2
- import { Databases, Query } from "node-appwrite";
2
+ import { Databases, IndexType, Query } from "node-appwrite";
3
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
3
4
  // import {}
4
5
  export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
5
6
  const existingIndex = await db.listIndexes(dbId, collectionId, [
@@ -13,6 +14,6 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
13
14
  };
14
15
  export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes) => {
15
16
  for (const index of indexes) {
16
- await createOrUpdateIndex(dbId, db, collectionId, index);
17
+ await tryAwaitWithRetry(async () => await createOrUpdateIndex(dbId, db, collectionId, index));
17
18
  }
18
19
  };
@@ -1,15 +1,27 @@
1
1
  import winston from "winston";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ // Ensure the logs directory exists
5
+ const logDir = path.join(process.cwd(), "zlogs");
6
+ if (!fs.existsSync(logDir)) {
7
+ fs.mkdirSync(logDir);
8
+ }
2
9
  export const logger = winston.createLogger({
3
- level: "info",
10
+ level: "debug",
4
11
  format: winston.format.json({ space: 2 }),
5
12
  defaultMeta: { service: "appwrite-utils-cli" },
6
13
  transports: [
7
- //
8
- // - Write all logs with importance level of `error` or less to `error.log`
9
- // - Write all logs with importance level of `info` or less to `combined.log`
10
- //
11
- new winston.transports.File({ filename: "error.log", level: "error" }),
12
- new winston.transports.File({ filename: "warn.log", level: "warn" }),
13
- new winston.transports.File({ filename: "combined.log" }),
14
+ new winston.transports.File({
15
+ filename: path.join(logDir, "error.log"),
16
+ level: "error",
17
+ }),
18
+ new winston.transports.File({
19
+ filename: path.join(logDir, "warn.log"),
20
+ level: "warn",
21
+ }),
22
+ new winston.transports.File({
23
+ filename: path.join(logDir, "info.log"),
24
+ level: "info",
25
+ }),
14
26
  ],
15
27
  });
@@ -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;
@@ -107,8 +111,8 @@ export declare const ContextObject: z.ZodObject<{
107
111
  action: string;
108
112
  }[] | undefined;
109
113
  }[];
110
- finalItem?: any;
111
114
  context?: any;
115
+ finalItem?: any;
112
116
  }, {
113
117
  collectionId: string;
114
118
  dbId: 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;
@@ -130,18 +135,18 @@ export declare const ContextObject: z.ZodObject<{
130
135
  action: string;
131
136
  }[] | undefined;
132
137
  }[];
133
- finalItem?: any;
134
138
  context?: any;
139
+ finalItem?: any;
135
140
  }>;
136
141
  export type ContextObject = z.infer<typeof ContextObject>;
137
142
  export declare const createOrFindAfterImportOperation: (database: Databases, collectionId: string, context: ContextObject) => Promise<void>;
138
143
  export declare const addBatch: (database: Databases, data: string) => Promise<string>;
139
144
  export declare const getAfterImportOperations: (database: Databases, collectionId: string) => Promise<{
140
- error: string;
141
145
  $id: string;
142
146
  $createdAt: string;
143
147
  $updatedAt: string;
144
148
  status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
149
+ error: string;
145
150
  collectionId: string;
146
151
  operationType: string;
147
152
  progress: number;
@@ -150,11 +155,11 @@ export declare const getAfterImportOperations: (database: Databases, collectionI
150
155
  batches?: string[] | undefined;
151
156
  }[]>;
152
157
  export declare const findOrCreateOperation: (database: Databases, collectionId: string, operationType: string, additionalQueries?: string[]) => Promise<{
153
- error: string;
154
158
  $id: string;
155
159
  $createdAt: string;
156
160
  $updatedAt: string;
157
161
  status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
162
+ error: string;
158
163
  collectionId: string;
159
164
  operationType: string;
160
165
  progress: number;
@@ -3,6 +3,7 @@ import { BatchSchema, OperationSchema } from "./backup.js";
3
3
  import { AttributeMappingsSchema } from "appwrite-utils";
4
4
  import { z } from "zod";
5
5
  import { logger } from "./logging.js";
6
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
7
  /**
7
8
  * Object that contains the context for an action that needs to be executed after import
8
9
  * Used in the afterImportActionsDefinitions
@@ -66,18 +67,18 @@ export const getAfterImportOperations = async (database, collectionId) => {
66
67
  return allOps;
67
68
  };
68
69
  export const findOrCreateOperation = async (database, collectionId, operationType, additionalQueries) => {
69
- const operations = await database.listDocuments("migrations", "currentOperations", [
70
+ const operations = await tryAwaitWithRetry(async () => await database.listDocuments("migrations", "currentOperations", [
70
71
  Query.equal("collectionId", collectionId),
71
72
  Query.equal("operationType", operationType),
72
73
  Query.equal("status", "pending"),
73
74
  ...(additionalQueries || []),
74
- ]);
75
+ ]));
75
76
  if (operations.documents.length > 0) {
76
77
  return OperationSchema.parse(operations.documents[0]); // Assuming the first document is the operation we want
77
78
  }
78
79
  else {
79
80
  // Create a new operation document
80
- const op = await database.createDocument("migrations", "currentOperations", ID.unique(), {
81
+ const op = await tryAwaitWithRetry(async () => await database.createDocument("migrations", "currentOperations", ID.unique(), {
81
82
  operationType,
82
83
  collectionId,
83
84
  status: "pending",
@@ -85,12 +86,12 @@ export const findOrCreateOperation = async (database, collectionId, operationTyp
85
86
  progress: 0,
86
87
  total: 0,
87
88
  error: "",
88
- });
89
+ }));
89
90
  return OperationSchema.parse(op);
90
91
  }
91
92
  };
92
93
  export const updateOperation = async (database, operationId, updateFields) => {
93
- await database.updateDocument("migrations", "currentOperations", operationId, updateFields);
94
+ await tryAwaitWithRetry(async () => await database.updateDocument("migrations", "currentOperations", operationId, updateFields));
94
95
  };
95
96
  // Actual max 1073741824
96
97
  export const maxDataLength = 1073741820;
@@ -1,4 +1,4 @@
1
1
  import { type AppwriteConfig } from "appwrite-utils";
2
2
  import { z } from "zod";
3
3
  export declare const generateOpenApi: (config: AppwriteConfig) => Promise<void>;
4
- export declare function transformTypeToOpenApi<T extends z.ZodTypeAny>(schema: T): z.infer<T>;
4
+ export declare function transformTypeToOpenApi<T extends z.ZodTypeAny>(schema: T, description?: string | Record<string, any> | null | undefined): T;