appwrite-utils-cli 0.0.286 → 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
@@ -0,0 +1,483 @@
1
+ import { Query, type Databases, type Models } from "node-appwrite";
2
+ import {
3
+ attributeSchema,
4
+ parseAttribute,
5
+ type Attribute,
6
+ } from "appwrite-utils";
7
+ import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
8
+ import _ from "lodash";
9
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
10
+
11
+ const attributesSame = (
12
+ databaseAttribute: Attribute,
13
+ configAttribute: Attribute
14
+ ): boolean => {
15
+ return (
16
+ databaseAttribute.key == configAttribute.key &&
17
+ databaseAttribute.type == configAttribute.type &&
18
+ databaseAttribute.array == configAttribute.array
19
+ );
20
+ };
21
+
22
+ export const createOrUpdateAttribute = async (
23
+ db: Databases,
24
+ dbId: string,
25
+ collection: Models.Collection,
26
+ attribute: Attribute
27
+ ): Promise<void> => {
28
+ let action = "create";
29
+ let foundAttribute: Attribute | undefined;
30
+ const updateEnabled = false;
31
+ let finalAttribute: any = attribute;
32
+ try {
33
+ const collectionAttr = collection.attributes.find(
34
+ // @ts-expect-error
35
+ (attr) => attr.key === attribute.key
36
+ ) as unknown as any;
37
+ foundAttribute = parseAttribute(collectionAttr);
38
+ } catch (error) {
39
+ foundAttribute = undefined;
40
+ }
41
+
42
+ if (
43
+ foundAttribute &&
44
+ attributesSame(foundAttribute, attribute) &&
45
+ updateEnabled
46
+ ) {
47
+ // Check if mutable properties have changed and set action to "update" if necessary
48
+ const requiredChanged =
49
+ "required" in foundAttribute && "required" in attribute
50
+ ? foundAttribute.required !== attribute.required
51
+ : false;
52
+
53
+ // const xdefaultChanged =
54
+ // "xdefault" in foundAttribute && "xdefault" in attribute
55
+ // ? foundAttribute.xdefault !== attribute.xdefault
56
+ // : false;
57
+
58
+ const onDeleteChanged =
59
+ foundAttribute.type === "relationship" &&
60
+ attribute.type === "relationship" &&
61
+ "onDelete" in foundAttribute &&
62
+ "onDelete" in attribute
63
+ ? foundAttribute.onDelete !== attribute.onDelete
64
+ : false;
65
+
66
+ if (requiredChanged || onDeleteChanged) {
67
+ console.log(
68
+ `Required changed: ${requiredChanged}\nOnDelete changed: ${onDeleteChanged}`
69
+ );
70
+ console.log(
71
+ `Found attribute: ${JSON.stringify(foundAttribute, null, 2)}`
72
+ );
73
+ console.log(`Attribute: ${JSON.stringify(attribute, null, 2)}`);
74
+ finalAttribute = {
75
+ ...attribute,
76
+ ...foundAttribute,
77
+ };
78
+ action = "update";
79
+ } else {
80
+ // If no properties that can be updated have changed, return early
81
+ return;
82
+ }
83
+ } else if (
84
+ foundAttribute &&
85
+ !attributesSame(foundAttribute, attribute) &&
86
+ updateEnabled
87
+ ) {
88
+ console.log(
89
+ `Deleting attribute with same key ${
90
+ attribute.key
91
+ } -- but different values -- ${JSON.stringify(
92
+ attribute,
93
+ null,
94
+ 2
95
+ )} -- ${JSON.stringify(foundAttribute, null, 2)}`
96
+ );
97
+ await db.deleteAttribute(dbId, collection.$id, attribute.key);
98
+ // After deletion, you might want to create the attribute anew
99
+ finalAttribute = attribute;
100
+ action = "create";
101
+ } else if (!updateEnabled && foundAttribute) {
102
+ return;
103
+ }
104
+
105
+ // Relationship attribute logic with adjustments
106
+ let collectionFoundViaRelatedCollection: Models.Collection | undefined;
107
+ let relatedCollectionId: string | undefined;
108
+ if (finalAttribute.type === "relationship") {
109
+ if (nameToIdMapping.has(finalAttribute.relatedCollection)) {
110
+ relatedCollectionId = nameToIdMapping.get(
111
+ finalAttribute.relatedCollection
112
+ );
113
+ try {
114
+ collectionFoundViaRelatedCollection = await db.getCollection(
115
+ dbId,
116
+ relatedCollectionId!
117
+ );
118
+ } catch (e) {
119
+ console.log(
120
+ `Collection not found: ${finalAttribute.relatedCollection} when nameToIdMapping was set`
121
+ );
122
+ collectionFoundViaRelatedCollection = undefined;
123
+ }
124
+ } else {
125
+ const collectionsPulled = await db.listCollections(dbId, [
126
+ Query.equal("name", finalAttribute.relatedCollection),
127
+ ]);
128
+ if (collectionsPulled.total > 0) {
129
+ collectionFoundViaRelatedCollection = collectionsPulled.collections[0];
130
+ relatedCollectionId = collectionFoundViaRelatedCollection.$id;
131
+ nameToIdMapping.set(
132
+ finalAttribute.relatedCollection,
133
+ relatedCollectionId
134
+ );
135
+ }
136
+ }
137
+ if (!(relatedCollectionId && collectionFoundViaRelatedCollection)) {
138
+ console.log(`Enqueueing operation for attribute: ${finalAttribute.key}`);
139
+ enqueueOperation({
140
+ type: "attribute",
141
+ collectionId: collection.$id,
142
+ collection: collection,
143
+ attribute,
144
+ dependencies: [finalAttribute.relatedCollection],
145
+ });
146
+ return;
147
+ }
148
+ }
149
+ finalAttribute = attributeSchema.parse(finalAttribute);
150
+ switch (finalAttribute.type) {
151
+ case "string":
152
+ if (action === "create") {
153
+ await tryAwaitWithRetry(
154
+ async () =>
155
+ await db.createStringAttribute(
156
+ dbId,
157
+ collection.$id,
158
+ finalAttribute.key,
159
+ finalAttribute.size,
160
+ finalAttribute.required || false,
161
+ (finalAttribute.xdefault as string) || undefined,
162
+ finalAttribute.array || false,
163
+ finalAttribute.encrypted
164
+ )
165
+ );
166
+ } else {
167
+ await tryAwaitWithRetry(
168
+ async () =>
169
+ await db.updateStringAttribute(
170
+ dbId,
171
+ collection.$id,
172
+ finalAttribute.key,
173
+ finalAttribute.required || false,
174
+ (finalAttribute.xdefault as string) || undefined
175
+ )
176
+ );
177
+ }
178
+ break;
179
+ case "integer":
180
+ if (action === "create") {
181
+ if (
182
+ finalAttribute.min &&
183
+ BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
184
+ ) {
185
+ delete finalAttribute.min;
186
+ }
187
+ if (
188
+ finalAttribute.max &&
189
+ BigInt(finalAttribute.max) === BigInt(9223372036854776000)
190
+ ) {
191
+ delete finalAttribute.max;
192
+ }
193
+ await tryAwaitWithRetry(
194
+ async () =>
195
+ await db.createIntegerAttribute(
196
+ dbId,
197
+ collection.$id,
198
+ finalAttribute.key,
199
+ finalAttribute.required || false,
200
+ finalAttribute.min,
201
+ finalAttribute.max,
202
+ finalAttribute.xdefault || undefined,
203
+ finalAttribute.array
204
+ )
205
+ );
206
+ } else {
207
+ if (
208
+ finalAttribute.min &&
209
+ BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
210
+ ) {
211
+ delete finalAttribute.min;
212
+ }
213
+ if (
214
+ finalAttribute.max &&
215
+ BigInt(finalAttribute.max) === BigInt(9223372036854776000)
216
+ ) {
217
+ delete finalAttribute.max;
218
+ }
219
+ await tryAwaitWithRetry(
220
+ async () =>
221
+ await db.updateIntegerAttribute(
222
+ dbId,
223
+ collection.$id,
224
+ finalAttribute.key,
225
+ finalAttribute.required || false,
226
+ finalAttribute.min || 0,
227
+ finalAttribute.max || 2147483647,
228
+ finalAttribute.xdefault || undefined
229
+ )
230
+ );
231
+ }
232
+ break;
233
+ case "float":
234
+ if (action === "create") {
235
+ await tryAwaitWithRetry(
236
+ async () =>
237
+ await db.createFloatAttribute(
238
+ dbId,
239
+ collection.$id,
240
+ finalAttribute.key,
241
+ finalAttribute.required || false,
242
+ finalAttribute.min,
243
+ finalAttribute.max,
244
+ finalAttribute.xdefault || undefined,
245
+ finalAttribute.array
246
+ )
247
+ );
248
+ } else {
249
+ await tryAwaitWithRetry(
250
+ async () =>
251
+ await db.updateFloatAttribute(
252
+ dbId,
253
+ collection.$id,
254
+ finalAttribute.key,
255
+ finalAttribute.required || false,
256
+ finalAttribute.min || 0,
257
+ finalAttribute.max || 2147483647,
258
+ finalAttribute.xdefault || undefined
259
+ )
260
+ );
261
+ }
262
+ break;
263
+ case "boolean":
264
+ if (action === "create") {
265
+ await tryAwaitWithRetry(
266
+ async () =>
267
+ await db.createBooleanAttribute(
268
+ dbId,
269
+ collection.$id,
270
+ finalAttribute.key,
271
+ finalAttribute.required || false,
272
+ finalAttribute.xdefault || undefined,
273
+ finalAttribute.array
274
+ )
275
+ );
276
+ } else {
277
+ await tryAwaitWithRetry(
278
+ async () =>
279
+ await db.updateBooleanAttribute(
280
+ dbId,
281
+ collection.$id,
282
+ finalAttribute.key,
283
+ finalAttribute.required || false,
284
+ finalAttribute.xdefault || null
285
+ )
286
+ );
287
+ }
288
+ break;
289
+ case "datetime":
290
+ if (action === "create") {
291
+ await tryAwaitWithRetry(
292
+ async () =>
293
+ await db.createDatetimeAttribute(
294
+ dbId,
295
+ collection.$id,
296
+ finalAttribute.key,
297
+ finalAttribute.required || false,
298
+ finalAttribute.xdefault || undefined,
299
+ finalAttribute.array
300
+ )
301
+ );
302
+ } else {
303
+ await tryAwaitWithRetry(
304
+ async () =>
305
+ await db.updateDatetimeAttribute(
306
+ dbId,
307
+ collection.$id,
308
+ finalAttribute.key,
309
+ finalAttribute.required || false,
310
+ finalAttribute.xdefault || undefined
311
+ )
312
+ );
313
+ }
314
+ break;
315
+ case "email":
316
+ if (action === "create") {
317
+ await tryAwaitWithRetry(
318
+ async () =>
319
+ await db.createEmailAttribute(
320
+ dbId,
321
+ collection.$id,
322
+ finalAttribute.key,
323
+ finalAttribute.required || false,
324
+ finalAttribute.xdefault || undefined,
325
+ finalAttribute.array
326
+ )
327
+ );
328
+ } else {
329
+ await tryAwaitWithRetry(
330
+ async () =>
331
+ await db.updateEmailAttribute(
332
+ dbId,
333
+ collection.$id,
334
+ finalAttribute.key,
335
+ finalAttribute.required || false,
336
+ finalAttribute.xdefault || undefined
337
+ )
338
+ );
339
+ }
340
+ break;
341
+ case "ip":
342
+ if (action === "create") {
343
+ await tryAwaitWithRetry(
344
+ async () =>
345
+ await db.createIpAttribute(
346
+ dbId,
347
+ collection.$id,
348
+ finalAttribute.key,
349
+ finalAttribute.required || false,
350
+ finalAttribute.xdefault || undefined,
351
+ finalAttribute.array
352
+ )
353
+ );
354
+ } else {
355
+ await tryAwaitWithRetry(
356
+ async () =>
357
+ await db.updateIpAttribute(
358
+ dbId,
359
+ collection.$id,
360
+ finalAttribute.key,
361
+ finalAttribute.required || false,
362
+ finalAttribute.xdefault || undefined
363
+ )
364
+ );
365
+ }
366
+ break;
367
+ case "url":
368
+ if (action === "create") {
369
+ await tryAwaitWithRetry(
370
+ async () =>
371
+ await db.createUrlAttribute(
372
+ dbId,
373
+ collection.$id,
374
+ finalAttribute.key,
375
+ finalAttribute.required || false,
376
+ finalAttribute.xdefault || undefined,
377
+ finalAttribute.array
378
+ )
379
+ );
380
+ } else {
381
+ await tryAwaitWithRetry(
382
+ async () =>
383
+ await db.updateUrlAttribute(
384
+ dbId,
385
+ collection.$id,
386
+ finalAttribute.key,
387
+ finalAttribute.required || false,
388
+ finalAttribute.xdefault || undefined
389
+ )
390
+ );
391
+ }
392
+ break;
393
+ case "enum":
394
+ if (action === "create") {
395
+ await tryAwaitWithRetry(
396
+ async () =>
397
+ await db.createEnumAttribute(
398
+ dbId,
399
+ collection.$id,
400
+ finalAttribute.key,
401
+ finalAttribute.elements,
402
+ finalAttribute.required || false,
403
+ finalAttribute.xdefault || undefined,
404
+ finalAttribute.array
405
+ )
406
+ );
407
+ } else {
408
+ await tryAwaitWithRetry(
409
+ async () =>
410
+ await db.updateEnumAttribute(
411
+ dbId,
412
+ collection.$id,
413
+ finalAttribute.key,
414
+ finalAttribute.elements,
415
+ finalAttribute.required || false,
416
+ finalAttribute.xdefault || undefined
417
+ )
418
+ );
419
+ }
420
+ break;
421
+ case "relationship":
422
+ if (action === "create") {
423
+ await tryAwaitWithRetry(
424
+ async () =>
425
+ await db.createRelationshipAttribute(
426
+ dbId,
427
+ collection.$id,
428
+ relatedCollectionId!,
429
+ finalAttribute.relationType,
430
+ finalAttribute.twoWay,
431
+ finalAttribute.key,
432
+ finalAttribute.twoWayKey,
433
+ finalAttribute.onDelete
434
+ )
435
+ );
436
+ } else {
437
+ await tryAwaitWithRetry(
438
+ async () =>
439
+ await db.updateRelationshipAttribute(
440
+ dbId,
441
+ collection.$id,
442
+ finalAttribute.key,
443
+ finalAttribute.onDelete
444
+ )
445
+ );
446
+ }
447
+ break;
448
+ default:
449
+ console.error("Invalid attribute type");
450
+ break;
451
+ }
452
+ };
453
+
454
+ export const createUpdateCollectionAttributes = async (
455
+ db: Databases,
456
+ dbId: string,
457
+ collection: Models.Collection,
458
+ attributes: Attribute[]
459
+ ): Promise<void> => {
460
+ console.log(
461
+ `Creating/Updating attributes for collection: ${collection.name}`
462
+ );
463
+
464
+ const batchSize = 3; // Size of each batch
465
+ for (let i = 0; i < attributes.length; i += batchSize) {
466
+ // Slice the attributes array to get a batch of at most batchSize elements
467
+ const batch = attributes.slice(i, i + batchSize);
468
+ const attributePromises = batch.map((attribute) =>
469
+ createOrUpdateAttribute(db, dbId, collection, attribute)
470
+ );
471
+
472
+ // Await the completion of all promises in the current batch
473
+ const results = await Promise.allSettled(attributePromises);
474
+ results.forEach((result) => {
475
+ if (result.status === "rejected") {
476
+ console.error("An attribute promise was rejected:", result.reason);
477
+ }
478
+ });
479
+ }
480
+ console.log(
481
+ `Finished creating/updating attributes for collection: ${collection.name}`
482
+ );
483
+ };
@@ -0,0 +1,53 @@
1
+ import { indexSchema, type Index } from "appwrite-utils";
2
+ import { Databases, IndexType, Query, type Models } from "node-appwrite";
3
+ import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
4
+
5
+ export const createOrUpdateIndex = async (
6
+ dbId: string,
7
+ db: Databases,
8
+ collectionId: string,
9
+ index: Index
10
+ ) => {
11
+ const existingIndex = await db.listIndexes(dbId, collectionId, [
12
+ Query.equal("key", index.key),
13
+ ]);
14
+ let createIndex = false;
15
+ let newIndex: Models.Index | null = null;
16
+ if (
17
+ existingIndex.total > 0 &&
18
+ existingIndex.indexes.some(
19
+ (index) =>
20
+ (index.key === index.key &&
21
+ index.type === index.type &&
22
+ index.attributes === index.attributes) ||
23
+ JSON.stringify(index) === JSON.stringify(index)
24
+ )
25
+ ) {
26
+ await db.deleteIndex(dbId, collectionId, existingIndex.indexes[0].key);
27
+ createIndex = true;
28
+ }
29
+ if (createIndex) {
30
+ newIndex = await db.createIndex(
31
+ dbId,
32
+ collectionId,
33
+ index.key,
34
+ index.type as IndexType,
35
+ index.attributes,
36
+ index.orders
37
+ );
38
+ }
39
+ return newIndex;
40
+ };
41
+
42
+ export const createOrUpdateIndexes = async (
43
+ dbId: string,
44
+ db: Databases,
45
+ collectionId: string,
46
+ indexes: Index[]
47
+ ) => {
48
+ for (const index of indexes) {
49
+ await tryAwaitWithRetry(
50
+ async () => await createOrUpdateIndex(dbId, db, collectionId, index)
51
+ );
52
+ }
53
+ };