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
@@ -0,0 +1,216 @@
1
+ import { Client, Databases, ID, Permission, Query, } from "node-appwrite";
2
+ import { nameToIdMapping, processQueue } from "../migrations/queue.js";
3
+ import { createUpdateCollectionAttributes } from "./attributes.js";
4
+ import { createOrUpdateIndexes } from "./indexes.js";
5
+ import _ from "lodash";
6
+ import { SchemaGenerator } from "../utils/schemaStrings.js";
7
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
8
+ export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
9
+ // Had to do this because kept running into issues with type checking arrays so, sorry 40ms
10
+ const collection = await db.getCollection(dbId, targetCollectionId);
11
+ const attributes = collection.attributes;
12
+ let arrayTypeAttributes = attributes
13
+ .filter((attribute) => attribute.array === true)
14
+ .map((attribute) => attribute.key);
15
+ // Function to check if a string is JSON
16
+ const isJsonString = (str) => {
17
+ try {
18
+ const json = JSON.parse(str);
19
+ return typeof json === "object" && json !== null; // Check if parsed JSON is an object or array
20
+ }
21
+ catch (e) {
22
+ return false;
23
+ }
24
+ };
25
+ // Validate and prepare query parameters
26
+ const validQueryParams = _.chain(toCreateObject)
27
+ .pickBy((value, key) => !arrayTypeAttributes.includes(key) &&
28
+ !key.startsWith("$") &&
29
+ !_.isNull(value) &&
30
+ !_.isUndefined(value) &&
31
+ !_.isEmpty(value) &&
32
+ !_.isObject(value) && // Keeps excluding objects
33
+ !_.isArray(value) && // Explicitly exclude arrays
34
+ !(_.isString(value) && isJsonString(value)) && // Exclude JSON strings
35
+ (_.isString(value) ? value.length < 4096 && value.length > 0 : true) // String length check
36
+ )
37
+ .mapValues((value, key) => _.isString(value) || _.isNumber(value) || _.isBoolean(value)
38
+ ? value
39
+ : null)
40
+ .omitBy(_.isNull) // Remove any null values that might have been added in mapValues
41
+ .toPairs()
42
+ .slice(0, 25) // Limit to 25 to adhere to query limit
43
+ .map(([key, value]) => Query.equal(key, value))
44
+ .value();
45
+ // Execute the query with the validated and prepared parameters
46
+ const result = await db.listDocuments(dbId, targetCollectionId, validQueryParams);
47
+ return result.documents[0] || null;
48
+ };
49
+ export const checkForCollection = async (db, dbId, collection) => {
50
+ try {
51
+ console.log(`Checking for collection with name: ${collection.name}`);
52
+ const response = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [Query.equal("name", collection.name)]));
53
+ if (response.collections.length > 0) {
54
+ console.log(`Collection found: ${response.collections[0].$id}`);
55
+ return { ...collection, ...response.collections[0] };
56
+ }
57
+ else {
58
+ console.log(`No collection found with name: ${collection.name}`);
59
+ return null;
60
+ }
61
+ }
62
+ catch (error) {
63
+ console.error(`Error checking for collection: ${error}`);
64
+ return null;
65
+ }
66
+ };
67
+ // Helper function to fetch and cache collection by name
68
+ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) => {
69
+ if (nameToIdMapping.has(collectionName)) {
70
+ const collectionId = nameToIdMapping.get(collectionName);
71
+ console.log(`\tCollection found in cache: ${collectionId}`);
72
+ return await tryAwaitWithRetry(async () => await db.getCollection(dbId, collectionId));
73
+ }
74
+ else {
75
+ console.log(`\tFetching collection by name: ${collectionName}`);
76
+ const collectionsPulled = await tryAwaitWithRetry(async () => await db.listCollections(dbId, [Query.equal("name", collectionName)]));
77
+ if (collectionsPulled.total > 0) {
78
+ const collection = collectionsPulled.collections[0];
79
+ console.log(`\tCollection found: ${collection.$id}`);
80
+ nameToIdMapping.set(collectionName, collection.$id);
81
+ return collection;
82
+ }
83
+ else {
84
+ console.log(`\tCollection not found by name: ${collectionName}`);
85
+ return undefined;
86
+ }
87
+ }
88
+ };
89
+ export const wipeDatabase = async (database, databaseId) => {
90
+ console.log(`Wiping database: ${databaseId}`);
91
+ const existingCollections = await fetchAllCollections(databaseId, database);
92
+ let collectionsDeleted = [];
93
+ for (const { $id: collectionId, name: name } of existingCollections) {
94
+ console.log(`Deleting collection: ${collectionId}`);
95
+ collectionsDeleted.push({
96
+ collectionId: collectionId,
97
+ collectionName: name,
98
+ });
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
100
+ }
101
+ return collectionsDeleted;
102
+ };
103
+ export const generateSchemas = async (config, appwriteFolderPath) => {
104
+ const schemaGenerator = new SchemaGenerator(config, appwriteFolderPath);
105
+ schemaGenerator.generateSchemas();
106
+ };
107
+ export const createOrUpdateCollections = async (database, databaseId, config, deletedCollections) => {
108
+ const configCollections = config.collections;
109
+ if (!configCollections) {
110
+ return;
111
+ }
112
+ const usedIds = new Set(); // To track IDs used in this operation
113
+ for (const { attributes, indexes, ...collection } of configCollections) {
114
+ // Prepare permissions for the collection
115
+ const permissions = [];
116
+ if (collection.$permissions && collection.$permissions.length > 0) {
117
+ for (const permission of collection.$permissions) {
118
+ switch (permission.permission) {
119
+ case "read":
120
+ permissions.push(Permission.read(permission.target));
121
+ break;
122
+ case "create":
123
+ permissions.push(Permission.create(permission.target));
124
+ break;
125
+ case "update":
126
+ permissions.push(Permission.update(permission.target));
127
+ break;
128
+ case "delete":
129
+ permissions.push(Permission.delete(permission.target));
130
+ break;
131
+ case "write":
132
+ permissions.push(Permission.write(permission.target));
133
+ break;
134
+ default:
135
+ console.log(`Unknown permission: ${permission.permission}`);
136
+ break;
137
+ }
138
+ }
139
+ }
140
+ // Check if the collection already exists by name
141
+ let collectionsFound = await tryAwaitWithRetry(async () => await database.listCollections(databaseId, [
142
+ Query.equal("name", collection.name),
143
+ ]));
144
+ let collectionToUse = collectionsFound.total > 0 ? collectionsFound.collections[0] : null;
145
+ // Determine the correct ID for the collection
146
+ let collectionId;
147
+ if (!collectionToUse) {
148
+ console.log(`Creating collection: ${collection.name}`);
149
+ let foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") ===
150
+ collection.name.toLowerCase().trim().replace(" ", ""));
151
+ if (collection.$id) {
152
+ collectionId = collection.$id; // Always use the provided $id if present
153
+ }
154
+ else if (foundColl && !usedIds.has(foundColl.collectionId)) {
155
+ collectionId = foundColl.collectionId; // Use ID from deleted collection if not already used
156
+ }
157
+ else {
158
+ collectionId = ID.unique(); // Generate a new unique ID
159
+ }
160
+ usedIds.add(collectionId); // Mark this ID as used
161
+ // Create the collection with the determined ID
162
+ try {
163
+ collectionToUse = await tryAwaitWithRetry(async () => await database.createCollection(databaseId, collectionId, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
164
+ collection.$id = collectionToUse.$id;
165
+ nameToIdMapping.set(collection.name, collectionToUse.$id);
166
+ }
167
+ catch (error) {
168
+ console.error(`Failed to create collection ${collection.name} with ID ${collectionId}: ${error}`);
169
+ continue; // Skip to the next collection on failure
170
+ }
171
+ }
172
+ else {
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));
175
+ }
176
+ // Update attributes and indexes for the collection
177
+ console.log("Creating Attributes");
178
+ await createUpdateCollectionAttributes(database, databaseId, collectionToUse, attributes);
179
+ console.log("Creating Indexes");
180
+ await createOrUpdateIndexes(databaseId, database, collectionToUse.$id, indexes ?? []);
181
+ }
182
+ // Process any remaining tasks in the queue
183
+ await processQueue(database, databaseId);
184
+ };
185
+ export const generateMockData = async (database, databaseId, configCollections) => {
186
+ for (const { collection, mockFunction } of configCollections) {
187
+ if (mockFunction) {
188
+ console.log(`Generating mock data for collection: ${collection.name}`);
189
+ const mockData = mockFunction();
190
+ for (const data of mockData) {
191
+ await database.createDocument(databaseId, collection.$id, ID.unique(), data);
192
+ }
193
+ }
194
+ }
195
+ };
196
+ export const fetchAllCollections = async (dbId, database) => {
197
+ console.log(`Fetching all collections for database ID: ${dbId}`);
198
+ let collections = [];
199
+ let moreCollections = true;
200
+ let lastCollectionId;
201
+ while (moreCollections) {
202
+ const queries = [Query.limit(500)];
203
+ if (lastCollectionId) {
204
+ queries.push(Query.cursorAfter(lastCollectionId));
205
+ }
206
+ const response = await tryAwaitWithRetry(async () => await database.listCollections(dbId, queries));
207
+ collections = collections.concat(response.collections);
208
+ moreCollections = response.collections.length === 500;
209
+ if (moreCollections) {
210
+ lastCollectionId =
211
+ response.collections[response.collections.length - 1].$id;
212
+ }
213
+ }
214
+ console.log(`Fetched a total of ${collections.length} collections.`);
215
+ return collections;
216
+ };
@@ -0,0 +1,6 @@
1
+ import { Databases, type Models } from "node-appwrite";
2
+ export declare const fetchAllDatabases: (database: Databases) => Promise<Models.Database[]>;
3
+ export declare const wipeDatabase: (database: Databases, databaseId: string) => Promise<{
4
+ collectionId: string;
5
+ collectionName: string;
6
+ }[]>;
@@ -0,0 +1,33 @@
1
+ import { Databases, Query } from "node-appwrite";
2
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
3
+ import { fetchAllCollections } from "../collections/methods.js";
4
+ export const fetchAllDatabases = async (database) => {
5
+ const databases = await tryAwaitWithRetry(async () => await database.list([Query.limit(25)]));
6
+ const allDatabases = databases.databases;
7
+ if (allDatabases.length === 0)
8
+ return [];
9
+ let lastDatabaseId = allDatabases[allDatabases.length - 1].$id;
10
+ while (databases.databases.length === 25) {
11
+ const moreDatabases = await database.list([
12
+ Query.limit(25),
13
+ Query.cursorAfter(lastDatabaseId),
14
+ ]);
15
+ allDatabases.push(...moreDatabases.databases);
16
+ if (moreDatabases.databases.length < 25)
17
+ break;
18
+ lastDatabaseId =
19
+ moreDatabases.databases[moreDatabases.databases.length - 1].$id;
20
+ }
21
+ return allDatabases;
22
+ };
23
+ export const wipeDatabase = async (database, databaseId) => {
24
+ console.log(`Wiping database: ${databaseId}`);
25
+ const existingCollections = await fetchAllCollections(databaseId, database);
26
+ let collectionsDeleted = [];
27
+ for (const { $id: collectionId, name } of existingCollections) {
28
+ console.log(`Deleting collection: ${collectionId}`);
29
+ collectionsDeleted.push({ collectionId, collectionName: name });
30
+ await tryAwaitWithRetry(async () => await database.deleteCollection(databaseId, collectionId));
31
+ }
32
+ return collectionsDeleted;
33
+ };
@@ -0,0 +1,19 @@
1
+ import { UtilsController } from "./utilsController.js";
2
+ export declare class InteractiveCLI {
3
+ private controller;
4
+ constructor(currentDir?: string, utilsController?: UtilsController);
5
+ run(): Promise<void>;
6
+ private selectDatabases;
7
+ private selectCollections;
8
+ private selectBuckets;
9
+ private createCollectionConfig;
10
+ private configureBuckets;
11
+ private createNewBucket;
12
+ private syncDb;
13
+ private synchronizeConfigurations;
14
+ private backupDatabase;
15
+ private wipeDatabase;
16
+ private generateSchemas;
17
+ private importData;
18
+ private transferData;
19
+ }