appwrite-utils-cli 0.0.286 → 0.9.2

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 +162 -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 +224 -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 +289 -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,9 +1,10 @@
1
- import { Databases, ID, Permission, Query } from "node-appwrite";
1
+ import { Client, Databases, ID, Permission, Query, } from "node-appwrite";
2
2
  import { nameToIdMapping, processQueue } from "./queue.js";
3
3
  import { createUpdateCollectionAttributes } from "./attributes.js";
4
4
  import { createOrUpdateIndexes } from "./indexes.js";
5
5
  import _ from "lodash";
6
6
  import { SchemaGenerator } from "./schemaStrings.js";
7
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
7
8
  export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
8
9
  // Had to do this because kept running into issues with type checking arrays so, sorry 40ms
9
10
  const collection = await db.getCollection(dbId, targetCollectionId);
@@ -48,9 +49,7 @@ export const documentExists = async (db, dbId, targetCollectionId, toCreateObjec
48
49
  export const checkForCollection = async (db, dbId, collection) => {
49
50
  try {
50
51
  console.log(`Checking for collection with name: ${collection.name}`);
51
- const response = await db.listCollections(dbId, [
52
- Query.equal("name", collection.name),
53
- ]);
52
+ const response = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [Query.equal("name", collection.name)]));
54
53
  if (response.collections.length > 0) {
55
54
  console.log(`Collection found: ${response.collections[0].$id}`);
56
55
  return { ...collection, ...response.collections[0] };
@@ -70,13 +69,11 @@ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) =>
70
69
  if (nameToIdMapping.has(collectionName)) {
71
70
  const collectionId = nameToIdMapping.get(collectionName);
72
71
  console.log(`\tCollection found in cache: ${collectionId}`);
73
- return await db.getCollection(dbId, collectionId);
72
+ return await tryAwaitWithRetry(async () => await db.getCollection(dbId, collectionId));
74
73
  }
75
74
  else {
76
75
  console.log(`\tFetching collection by name: ${collectionName}`);
77
- const collectionsPulled = await db.listCollections(dbId, [
78
- Query.equal("name", collectionName),
79
- ]);
76
+ const collectionsPulled = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [Query.equal("name", collectionName)]));
80
77
  if (collectionsPulled.total > 0) {
81
78
  const collection = collectionsPulled.collections[0];
82
79
  console.log(`\tCollection found: ${collection.$id}`);
@@ -91,7 +88,7 @@ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) =>
91
88
  };
92
89
  export const wipeDatabase = async (database, databaseId) => {
93
90
  console.log(`Wiping database: ${databaseId}`);
94
- const { collections: existingCollections } = await database.listCollections(databaseId);
91
+ const existingCollections = await fetchAllCollections(databaseId, database);
95
92
  let collectionsDeleted = [];
96
93
  for (const { $id: collectionId, name: name } of existingCollections) {
97
94
  console.log(`Deleting collection: ${collectionId}`);
@@ -99,7 +96,7 @@ export const wipeDatabase = async (database, databaseId) => {
99
96
  collectionId: collectionId,
100
97
  collectionName: name,
101
98
  });
102
- await database.deleteCollection(databaseId, collectionId);
99
+ tryAwaitWithRetry(async () => await database.deleteCollection(databaseId, collectionId)); // Try to delete the collection and ignore errors if it doesn't exist or if it's already being deleted
103
100
  }
104
101
  return collectionsDeleted;
105
102
  };
@@ -141,21 +138,21 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
141
138
  }
142
139
  }
143
140
  // Check if the collection already exists by name
144
- let collectionsFound = await database.listCollections(databaseId, [
141
+ let collectionsFound = await tryAwaitWithRetry(async () => await database.listCollections(databaseId, [
145
142
  Query.equal("name", collection.name),
146
- ]);
143
+ ]));
147
144
  let collectionToUse = collectionsFound.total > 0 ? collectionsFound.collections[0] : null;
148
145
  // Determine the correct ID for the collection
149
146
  let collectionId;
150
147
  if (!collectionToUse) {
151
148
  console.log(`Creating collection: ${collection.name}`);
152
- const foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") ===
149
+ let foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") ===
153
150
  collection.name.toLowerCase().trim().replace(" ", ""));
154
- if (foundColl && !usedIds.has(foundColl.collectionId)) {
155
- collectionId = foundColl.collectionId; // Use ID from deleted collection if not already used
151
+ if (collection.$id) {
152
+ collectionId = collection.$id; // Always use the provided $id if present
156
153
  }
157
- else if (collection.$id && !usedIds.has(collection.$id)) {
158
- collectionId = collection.$id; // Use the provided $id if not already used
154
+ else if (foundColl && !usedIds.has(foundColl.collectionId)) {
155
+ collectionId = foundColl.collectionId; // Use ID from deleted collection if not already used
159
156
  }
160
157
  else {
161
158
  collectionId = ID.unique(); // Generate a new unique ID
@@ -163,7 +160,7 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
163
160
  usedIds.add(collectionId); // Mark this ID as used
164
161
  // Create the collection with the determined ID
165
162
  try {
166
- collectionToUse = await database.createCollection(databaseId, collectionId, collection.name, permissions, collection.documentSecurity, collection.enabled);
163
+ collectionToUse = await tryAwaitWithRetry(async () => await database.createCollection(databaseId, collectionId, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
167
164
  collection.$id = collectionToUse.$id;
168
165
  nameToIdMapping.set(collection.name, collectionToUse.$id);
169
166
  }
@@ -173,7 +170,8 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
173
170
  }
174
171
  }
175
172
  else {
176
- console.log(`Collection ${collection.name} exists, using it`);
173
+ console.log(`Collection ${collection.name} exists, updating it`);
174
+ await tryAwaitWithRetry(async () => await database.updateCollection(databaseId, collectionToUse.$id, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
177
175
  }
178
176
  // Update attributes and indexes for the collection
179
177
  console.log("Creating Attributes");
@@ -205,7 +203,7 @@ export const fetchAllCollections = async (dbId, database) => {
205
203
  if (lastCollectionId) {
206
204
  queries.push(Query.cursorAfter(lastCollectionId));
207
205
  }
208
- const response = await database.listCollections(dbId, queries);
206
+ const response = await tryAwaitWithRetry(async () => await database.listCollections(dbId, queries));
209
207
  collections = collections.concat(response.collections);
210
208
  moreCollections = response.collections.length === 500;
211
209
  if (moreCollections) {
@@ -216,3 +214,134 @@ export const fetchAllCollections = async (dbId, database) => {
216
214
  console.log(`Fetched a total of ${collections.length} collections.`);
217
215
  return collections;
218
216
  };
217
+ /**
218
+ * Transfers all documents from one collection to another in a different database
219
+ * within the same Appwrite Project
220
+ */
221
+ export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDbId, fromCollId, toCollId) => {
222
+ let fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
223
+ let totalDocumentsTransferred = 0;
224
+ if (fromCollDocs.documents.length === 0) {
225
+ console.log(`No documents found in collection ${fromCollId}`);
226
+ return;
227
+ }
228
+ else if (fromCollDocs.documents.length < 50) {
229
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
230
+ const toCreateObject = {
231
+ ...doc,
232
+ };
233
+ delete toCreateObject.$databaseId;
234
+ delete toCreateObject.$collectionId;
235
+ delete toCreateObject.$createdAt;
236
+ delete toCreateObject.$updatedAt;
237
+ delete toCreateObject.$id;
238
+ delete toCreateObject.$permissions;
239
+ return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
240
+ });
241
+ await Promise.all(batchedPromises);
242
+ totalDocumentsTransferred += fromCollDocs.documents.length;
243
+ }
244
+ else {
245
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
246
+ const toCreateObject = {
247
+ ...doc,
248
+ };
249
+ delete toCreateObject.$databaseId;
250
+ delete toCreateObject.$collectionId;
251
+ delete toCreateObject.$createdAt;
252
+ delete toCreateObject.$updatedAt;
253
+ delete toCreateObject.$id;
254
+ delete toCreateObject.$permissions;
255
+ return tryAwaitWithRetry(async () => db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
256
+ });
257
+ await Promise.all(batchedPromises);
258
+ totalDocumentsTransferred += fromCollDocs.documents.length;
259
+ while (fromCollDocs.documents.length === 50) {
260
+ fromCollDocs = await tryAwaitWithRetry(async () => await db.listDocuments(fromDbId, fromCollId, [
261
+ Query.limit(50),
262
+ Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
263
+ ]));
264
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
265
+ const toCreateObject = {
266
+ ...doc,
267
+ };
268
+ delete toCreateObject.$databaseId;
269
+ delete toCreateObject.$collectionId;
270
+ delete toCreateObject.$createdAt;
271
+ delete toCreateObject.$updatedAt;
272
+ delete toCreateObject.$id;
273
+ delete toCreateObject.$permissions;
274
+ return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
275
+ });
276
+ await Promise.all(batchedPromises);
277
+ totalDocumentsTransferred += fromCollDocs.documents.length;
278
+ }
279
+ }
280
+ console.log(`Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`);
281
+ };
282
+ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint, projectId, apiKey, fromDbId, toDbId, fromCollId, toCollId) => {
283
+ const client = new Client()
284
+ .setEndpoint(endpoint)
285
+ .setProject(projectId)
286
+ .setKey(apiKey);
287
+ let totalDocumentsTransferred = 0;
288
+ const remoteDb = new Databases(client);
289
+ let fromCollDocs = await tryAwaitWithRetry(async () => localDb.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
290
+ if (fromCollDocs.documents.length === 0) {
291
+ console.log(`No documents found in collection ${fromCollId}`);
292
+ return;
293
+ }
294
+ else if (fromCollDocs.documents.length < 50) {
295
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
296
+ const toCreateObject = {
297
+ ...doc,
298
+ };
299
+ delete toCreateObject.$databaseId;
300
+ delete toCreateObject.$collectionId;
301
+ delete toCreateObject.$createdAt;
302
+ delete toCreateObject.$updatedAt;
303
+ delete toCreateObject.$id;
304
+ delete toCreateObject.$permissions;
305
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
306
+ });
307
+ await Promise.all(batchedPromises);
308
+ totalDocumentsTransferred += fromCollDocs.documents.length;
309
+ }
310
+ else {
311
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
312
+ const toCreateObject = {
313
+ ...doc,
314
+ };
315
+ delete toCreateObject.$databaseId;
316
+ delete toCreateObject.$collectionId;
317
+ delete toCreateObject.$createdAt;
318
+ delete toCreateObject.$updatedAt;
319
+ delete toCreateObject.$id;
320
+ delete toCreateObject.$permissions;
321
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
322
+ });
323
+ await Promise.all(batchedPromises);
324
+ totalDocumentsTransferred += fromCollDocs.documents.length;
325
+ while (fromCollDocs.documents.length === 50) {
326
+ fromCollDocs = await tryAwaitWithRetry(async () => localDb.listDocuments(fromDbId, fromCollId, [
327
+ Query.limit(50),
328
+ Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
329
+ ]));
330
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
331
+ const toCreateObject = {
332
+ ...doc,
333
+ };
334
+ delete toCreateObject.$databaseId;
335
+ delete toCreateObject.$collectionId;
336
+ delete toCreateObject.$createdAt;
337
+ delete toCreateObject.$updatedAt;
338
+ delete toCreateObject.$id;
339
+ delete toCreateObject.$permissions;
340
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
341
+ });
342
+ await Promise.all(batchedPromises);
343
+ totalDocumentsTransferred += fromCollDocs.documents.length;
344
+ }
345
+ }
346
+ console.log(`Total documents transferred from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}: ${totalDocumentsTransferred}`);
347
+ };
@@ -1,3 +1,4 @@
1
+ import { type AttributeMappings } from "appwrite-utils";
1
2
  /**
2
3
  * Deeply converts all properties of an object (or array) to strings.
3
4
  * @param data The input data to convert.
@@ -28,24 +29,7 @@ export declare const convertObjectBySchema: (obj: Record<string, any>, schema: R
28
29
  * @param attributeMappings The attributeMappings defining how keys in the object should be converted.
29
30
  * @returns The converted object with keys renamed according to attributeMappings.
30
31
  */
31
- export declare const convertObjectByAttributeMappings: (obj: Record<string, any>, attributeMappings: {
32
- targetKey: string;
33
- oldKey?: string | undefined;
34
- oldKeys?: string[] | undefined;
35
- fileData?: {
36
- path: string;
37
- name: string;
38
- } | undefined;
39
- converters?: string[] | undefined;
40
- validationActions?: {
41
- params: string[];
42
- action: string;
43
- }[] | undefined;
44
- postImportActions?: {
45
- params: (string | Record<string, any>)[];
46
- action: string;
47
- }[] | undefined;
48
- }[]) => Record<string, any>;
32
+ export declare const convertObjectByAttributeMappings: (obj: Record<string, any>, attributeMappings: AttributeMappings) => Record<string, any>;
49
33
  /**
50
34
  * Ensures data conversion without mutating the original input.
51
35
  * @param data The data to convert.
@@ -89,12 +89,20 @@ export const convertObjectByAttributeMappings = (obj, attributeMappings) => {
89
89
  return current;
90
90
  };
91
91
  for (const mapping of attributeMappings) {
92
- if (Array.isArray(mapping.oldKeys)) {
92
+ if (mapping.valueToSet !== undefined) {
93
+ result[mapping.targetKey] = mapping.valueToSet;
94
+ }
95
+ else if (Array.isArray(mapping.oldKeys)) {
93
96
  // Collect and flatten values from multiple oldKeys
94
97
  const values = mapping.oldKeys
95
98
  .map((oldKey) => resolveValue(obj, oldKey))
96
99
  .flat(Infinity);
97
- result[mapping.targetKey] = values.filter((value) => value !== undefined);
100
+ if (values.length > 0) {
101
+ result[mapping.targetKey] = values.filter((value) => value !== undefined);
102
+ }
103
+ else {
104
+ result[mapping.targetKey] = null;
105
+ }
98
106
  }
99
107
  else if (mapping.oldKey) {
100
108
  // Resolve single oldKey
@@ -104,6 +112,9 @@ export const convertObjectByAttributeMappings = (obj, attributeMappings) => {
104
112
  ? value.flat(Infinity)
105
113
  : value;
106
114
  }
115
+ else {
116
+ result[mapping.targetKey] = value ? value : null;
117
+ }
107
118
  }
108
119
  }
109
120
  return result;