appwrite-utils-cli 1.3.5 → 1.4.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 (116) hide show
  1. package/dist/adapters/AdapterFactory.d.ts +87 -0
  2. package/dist/adapters/AdapterFactory.js +217 -0
  3. package/dist/adapters/DatabaseAdapter.d.ts +217 -0
  4. package/dist/adapters/DatabaseAdapter.js +50 -0
  5. package/dist/adapters/LegacyAdapter.d.ts +49 -0
  6. package/dist/adapters/LegacyAdapter.js +382 -0
  7. package/dist/adapters/TablesDBAdapter.d.ts +55 -0
  8. package/dist/adapters/TablesDBAdapter.js +302 -0
  9. package/dist/adapters/index.d.ts +11 -0
  10. package/dist/adapters/index.js +12 -0
  11. package/dist/collections/attributes.js +41 -22
  12. package/dist/collections/methods.d.ts +4 -3
  13. package/dist/collections/methods.js +34 -14
  14. package/dist/config/yamlConfig.d.ts +40 -437
  15. package/dist/config/yamlConfig.js +8 -2
  16. package/dist/databases/setup.js +2 -2
  17. package/dist/main.js +0 -0
  18. package/dist/migrations/appwriteToX.d.ts +26 -37
  19. package/dist/migrations/comprehensiveTransfer.js +4 -4
  20. package/dist/migrations/dataLoader.d.ts +124 -1484
  21. package/dist/migrations/dataLoader.js +2 -1
  22. package/dist/migrations/relationships.d.ts +2 -3
  23. package/dist/migrations/relationships.js +1 -1
  24. package/dist/migrations/services/UserMappingService.js +1 -1
  25. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +24 -279
  26. package/dist/migrations/yaml/YamlImportConfigLoader.js +7 -2
  27. package/dist/schemas/authUser.d.ts +7 -47
  28. package/dist/schemas/authUser.js +1 -1
  29. package/dist/shared/jsonSchemaGenerator.d.ts +0 -2
  30. package/dist/shared/jsonSchemaGenerator.js +4 -17
  31. package/dist/shared/migrationHelpers.d.ts +17 -119
  32. package/dist/shared/operationQueue.js +16 -7
  33. package/dist/shared/schemaGenerator.js +2 -17
  34. package/dist/storage/schemas.d.ts +149 -296
  35. package/dist/users/methods.d.ts +2 -2
  36. package/dist/utils/configMigration.js +0 -1
  37. package/dist/utils/getClientFromConfig.d.ts +26 -0
  38. package/dist/utils/getClientFromConfig.js +37 -0
  39. package/dist/utils/loadConfigs.js +0 -2
  40. package/dist/utils/schemaStrings.js +2 -17
  41. package/dist/utils/setupFiles.js +2 -0
  42. package/dist/utils/versionDetection.d.ts +56 -0
  43. package/dist/utils/versionDetection.js +217 -0
  44. package/dist/utils/yamlConverter.d.ts +0 -1
  45. package/dist/utils/yamlConverter.js +0 -2
  46. package/dist/utilsController.js +2 -0
  47. package/package.json +3 -2
  48. package/src/adapters/AdapterFactory.ts +296 -0
  49. package/src/adapters/DatabaseAdapter.ts +290 -0
  50. package/src/adapters/LegacyAdapter.ts +667 -0
  51. package/src/adapters/TablesDBAdapter.ts +429 -0
  52. package/src/adapters/index.ts +37 -0
  53. package/src/collections/attributes.ts +347 -153
  54. package/src/collections/methods.ts +43 -28
  55. package/src/config/yamlConfig.ts +8 -2
  56. package/src/databases/setup.ts +2 -2
  57. package/src/migrations/afterImportActions.ts +2 -2
  58. package/src/migrations/comprehensiveTransfer.ts +4 -0
  59. package/src/migrations/dataLoader.ts +2 -1
  60. package/src/migrations/relationships.ts +1 -1
  61. package/src/migrations/services/UserMappingService.ts +1 -1
  62. package/src/migrations/yaml/YamlImportConfigLoader.ts +7 -2
  63. package/src/schemas/authUser.ts +1 -1
  64. package/src/shared/jsonSchemaGenerator.ts +4 -19
  65. package/src/shared/operationQueue.ts +20 -13
  66. package/src/shared/schemaGenerator.ts +2 -16
  67. package/src/types/node-appwrite-tablesdb.d.ts +44 -0
  68. package/src/users/methods.ts +2 -2
  69. package/src/utils/configMigration.ts +0 -1
  70. package/src/utils/getClientFromConfig.ts +56 -0
  71. package/src/utils/loadConfigs.ts +0 -2
  72. package/src/utils/schemaStrings.ts +2 -16
  73. package/src/utils/setupFiles.ts +2 -0
  74. package/src/utils/versionDetection.ts +265 -0
  75. package/src/utils/yamlConverter.ts +0 -2
  76. package/src/utilsController.ts +2 -0
  77. package/dist/functions/openapi.d.ts +0 -4
  78. package/dist/functions/openapi.js +0 -60
  79. package/dist/migrations/attributes.d.ts +0 -4
  80. package/dist/migrations/attributes.js +0 -301
  81. package/dist/migrations/backup.d.ts +0 -687
  82. package/dist/migrations/backup.js +0 -175
  83. package/dist/migrations/collections.d.ts +0 -22
  84. package/dist/migrations/collections.js +0 -347
  85. package/dist/migrations/converters.d.ts +0 -46
  86. package/dist/migrations/converters.js +0 -139
  87. package/dist/migrations/databases.d.ts +0 -2
  88. package/dist/migrations/databases.js +0 -28
  89. package/dist/migrations/dbHelpers.d.ts +0 -5
  90. package/dist/migrations/dbHelpers.js +0 -57
  91. package/dist/migrations/helper.d.ts +0 -3
  92. package/dist/migrations/helper.js +0 -21
  93. package/dist/migrations/indexes.d.ts +0 -4
  94. package/dist/migrations/indexes.js +0 -19
  95. package/dist/migrations/logging.d.ts +0 -10
  96. package/dist/migrations/logging.js +0 -46
  97. package/dist/migrations/migrationHelper.d.ts +0 -173
  98. package/dist/migrations/migrationHelper.js +0 -130
  99. package/dist/migrations/openapi.d.ts +0 -4
  100. package/dist/migrations/openapi.js +0 -60
  101. package/dist/migrations/queue.d.ts +0 -13
  102. package/dist/migrations/queue.js +0 -79
  103. package/dist/migrations/schemaStrings.d.ts +0 -14
  104. package/dist/migrations/schemaStrings.js +0 -478
  105. package/dist/migrations/setupDatabase.d.ts +0 -6
  106. package/dist/migrations/setupDatabase.js +0 -115
  107. package/dist/migrations/storage.d.ts +0 -10
  108. package/dist/migrations/storage.js +0 -340
  109. package/dist/migrations/users.d.ts +0 -16
  110. package/dist/migrations/users.js +0 -276
  111. package/dist/migrations/validationRules.d.ts +0 -43
  112. package/dist/migrations/validationRules.js +0 -42
  113. package/dist/shared/attributeManager.d.ts +0 -17
  114. package/dist/shared/attributeManager.js +0 -272
  115. package/src/functions/openapi.ts +0 -83
  116. package/src/shared/attributeManager.ts +0 -428
@@ -1,83 +0,0 @@
1
- import {
2
- OpenAPIRegistry,
3
- OpenApiGeneratorV3,
4
- OpenApiGeneratorV31,
5
- } from "@asteasolutions/zod-to-openapi";
6
- import {
7
- attributeSchema,
8
- CollectionSchema,
9
- type AppwriteConfig,
10
- type Attribute,
11
- type Collection,
12
- type CollectionCreate,
13
- } from "appwrite-utils";
14
- import { z } from "zod";
15
- import { writeFileSync } from "fs";
16
-
17
- const registry = new OpenAPIRegistry();
18
-
19
- export const generateOpenApi = async (config: AppwriteConfig) => {
20
- if (!config.collections) {
21
- return;
22
- }
23
- for (const collection of config.collections) {
24
- // Transform and register each attribute schema
25
- const attributeSchemas = collection.attributes.map((attribute) => {
26
- return transformTypeToOpenApi(attributeSchema, attribute.description);
27
- });
28
-
29
- // Create and register the collection schema with descriptions
30
- const updatedCollectionSchema = CollectionSchema.extend({
31
- // @ts-ignore
32
- attributes: attributeSchemas,
33
- }).openapi(collection.description ?? "No description");
34
-
35
- // Register the updated collection schema under the collection name
36
- registry.register(collection.name, updatedCollectionSchema);
37
- }
38
-
39
- // Convert the registry to OpenAPI JSON
40
- const generator = new OpenApiGeneratorV31(registry.definitions);
41
- const openApiSpec = generator.generateComponents();
42
-
43
- // Output the OpenAPI spec to a file
44
- writeFileSync(
45
- "./appwrite/openapi/openapi.json",
46
- JSON.stringify(openApiSpec, null, 2)
47
- );
48
- };
49
-
50
- export function transformTypeToOpenApi<T extends z.ZodTypeAny>(
51
- schema: T,
52
- description?: string | Record<string, any> | null | undefined
53
- ): T {
54
- // Check if description is an object (OpenAPI properties) or a string
55
- let updatedSchema: z.infer<T>;
56
- if (!description) {
57
- return schema;
58
- }
59
- if (typeof description === "string") {
60
- updatedSchema = schema.openapi(description);
61
- } else if (typeof description === "object") {
62
- updatedSchema = schema.openapi(description);
63
- } else {
64
- updatedSchema = schema;
65
- }
66
-
67
- // Check and transform attributes if they exist
68
- if ((schema as any)._def && (schema as any)._def.shape) {
69
- const shape = (schema as any)._def.shape();
70
- for (const key in shape) {
71
- const attributeDesc = shape[key].description;
72
- if (attributeDesc) {
73
- if (typeof attributeDesc === "string") {
74
- shape[key] = shape[key].openapi(attributeDesc);
75
- } else if (typeof attributeDesc === "object") {
76
- shape[key] = shape[key].openapi(attributeDesc);
77
- }
78
- }
79
- }
80
- }
81
-
82
- return updatedSchema;
83
- }
@@ -1,428 +0,0 @@
1
- import { type Databases, type Models } from "node-appwrite";
2
- import {
3
- parseAttribute,
4
- type Attribute,
5
- type CollectionCreate,
6
- } from "appwrite-utils";
7
- import { nameToIdMapping, type QueuedOperation, enqueueOperation } from "./operationQueue.js";
8
- import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
9
- import chalk from "chalk";
10
- import pLimit from "p-limit";
11
-
12
- // Concurrency limits for different operations
13
- const attributeLimit = pLimit(3); // Low limit for attribute operations
14
- const queryLimit = pLimit(25); // Higher limit for read operations
15
-
16
- export const attributesSame = (
17
- databaseAttribute: Attribute,
18
- configAttribute: Attribute
19
- ): boolean => {
20
- const attributesToCheck = [
21
- "key",
22
- "type",
23
- "array",
24
- "encrypted",
25
- "required",
26
- "size",
27
- "min",
28
- "max",
29
- "xdefault",
30
- "elements",
31
- "relationType",
32
- "twoWay",
33
- "twoWayKey",
34
- "onDelete",
35
- "relatedCollection",
36
- ];
37
-
38
- return attributesToCheck.every((attr) => {
39
- // Special handling for min/max values
40
- if (attr === "min" || attr === "max") {
41
- const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
42
- const configValue = configAttribute[attr as keyof typeof configAttribute];
43
-
44
- // Use type-specific default values when comparing
45
- if (databaseAttribute.type === "integer") {
46
- if (attr === "min") {
47
- return (dbValue ?? -2147483647) === (configValue ?? -2147483647);
48
- } else { // attr === "max"
49
- return (dbValue ?? 2147483647) === (configValue ?? 2147483647);
50
- }
51
- }
52
- if (databaseAttribute.type === "double" || databaseAttribute.type === "float") {
53
- if (attr === "min") {
54
- return (dbValue ?? -2147483647) === (configValue ?? -2147483647);
55
- } else { // attr === "max"
56
- return (dbValue ?? 2147483647) === (configValue ?? 2147483647);
57
- }
58
- }
59
- }
60
-
61
- // Check if both objects have the attribute
62
- const dbHasAttr = attr in databaseAttribute;
63
- const configHasAttr = attr in configAttribute;
64
-
65
- // If both have the attribute, compare values
66
- if (dbHasAttr && configHasAttr) {
67
- const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
68
- const configValue = configAttribute[attr as keyof typeof configAttribute];
69
-
70
- // Consider undefined and null as equivalent
71
- if (
72
- (dbValue === undefined || dbValue === null) &&
73
- (configValue === undefined || configValue === null)
74
- ) {
75
- return true;
76
- }
77
-
78
- return dbValue === configValue;
79
- }
80
-
81
- // If neither has the attribute, consider it the same
82
- if (!dbHasAttr && !configHasAttr) {
83
- return true;
84
- }
85
-
86
- // If one has the attribute and the other doesn't, check if it's undefined or null
87
- if (dbHasAttr && !configHasAttr) {
88
- const dbValue = databaseAttribute[attr as keyof typeof databaseAttribute];
89
- return dbValue === undefined || dbValue === null;
90
- }
91
-
92
- if (!dbHasAttr && configHasAttr) {
93
- const configValue = configAttribute[attr as keyof typeof configAttribute];
94
- return configValue === undefined || configValue === null;
95
- }
96
-
97
- // If we reach here, the attributes are different
98
- return false;
99
- });
100
- };
101
-
102
- export const createOrUpdateAttribute = async (
103
- db: Databases,
104
- dbId: string,
105
- collection: Models.Collection,
106
- attribute: Attribute,
107
- options: {
108
- updateEnabled?: boolean;
109
- useQueue?: boolean;
110
- verbose?: boolean;
111
- } = {}
112
- ): Promise<void> => {
113
- const { updateEnabled = true, useQueue = true, verbose = false } = options;
114
-
115
- let action = "create";
116
- let foundAttribute: Attribute | undefined;
117
- let finalAttribute: any = attribute;
118
-
119
- try {
120
- const collectionAttr = collection.attributes.find(
121
- (attr: any) => attr.key === attribute.key
122
- ) as unknown as any;
123
- foundAttribute = parseAttribute(collectionAttr);
124
-
125
- if (verbose) {
126
- console.log(`Found attribute: ${JSON.stringify(foundAttribute)}`);
127
- }
128
- } catch (error) {
129
- foundAttribute = undefined;
130
- }
131
-
132
- if (
133
- foundAttribute &&
134
- attributesSame(foundAttribute, attribute) &&
135
- updateEnabled
136
- ) {
137
- if (verbose) {
138
- console.log(chalk.green(`✓ Attribute ${attribute.key} is up to date`));
139
- }
140
- return;
141
- }
142
-
143
- if (foundAttribute) {
144
- action = "update";
145
- if (verbose) {
146
- console.log(chalk.yellow(`⚠ Updating attribute ${attribute.key}`));
147
- }
148
- } else {
149
- if (verbose) {
150
- console.log(chalk.blue(`+ Creating attribute ${attribute.key}`));
151
- }
152
- }
153
-
154
- // Handle relationship attributes with nameToIdMapping
155
- if (attribute.type === "relationship" && attribute.relatedCollection) {
156
- const relatedCollectionId = nameToIdMapping.get(attribute.relatedCollection);
157
- if (relatedCollectionId) {
158
- finalAttribute = {
159
- ...attribute,
160
- relatedCollection: relatedCollectionId,
161
- };
162
- }
163
- }
164
-
165
- // Handle BigInt values for integer, double and float types
166
- if (attribute.type === "integer" || attribute.type === "double" || attribute.type === "float") {
167
- if (typeof finalAttribute.min === "bigint") {
168
- finalAttribute.min = Number(finalAttribute.min);
169
- }
170
- if (typeof finalAttribute.max === "bigint") {
171
- finalAttribute.max = Number(finalAttribute.max);
172
- }
173
- }
174
-
175
- const queuedOperation: QueuedOperation = {
176
- type: "attribute",
177
- collectionId: collection.$id,
178
- attribute: finalAttribute,
179
- collection,
180
- };
181
-
182
- const executeOperation = async () => {
183
- await attributeLimit(async () => {
184
- if (action === "update" && foundAttribute) {
185
- // Delete existing attribute first
186
- await tryAwaitWithRetry(async () => {
187
- await db.deleteAttribute(dbId, collection.$id, attribute.key);
188
- });
189
- await delay(250);
190
- }
191
-
192
- // Create attribute based on type
193
- switch (finalAttribute.type) {
194
- case "string":
195
- await tryAwaitWithRetry(async () => {
196
- await db.createStringAttribute(
197
- dbId,
198
- collection.$id,
199
- finalAttribute.key,
200
- finalAttribute.size,
201
- finalAttribute.required,
202
- finalAttribute.xdefault,
203
- finalAttribute.array,
204
- finalAttribute.encrypted
205
- );
206
- });
207
- break;
208
- case "integer":
209
- await tryAwaitWithRetry(async () => {
210
- await db.createIntegerAttribute(
211
- dbId,
212
- collection.$id,
213
- finalAttribute.key,
214
- finalAttribute.required,
215
- finalAttribute.min,
216
- finalAttribute.max,
217
- finalAttribute.xdefault,
218
- finalAttribute.array
219
- );
220
- });
221
- break;
222
- case "double":
223
- case "float": // Backward compatibility
224
- await tryAwaitWithRetry(async () => {
225
- await db.createFloatAttribute(
226
- dbId,
227
- collection.$id,
228
- finalAttribute.key,
229
- finalAttribute.required,
230
- finalAttribute.min,
231
- finalAttribute.max,
232
- finalAttribute.xdefault,
233
- finalAttribute.array
234
- );
235
- });
236
- break;
237
- case "boolean":
238
- await tryAwaitWithRetry(async () => {
239
- await db.createBooleanAttribute(
240
- dbId,
241
- collection.$id,
242
- finalAttribute.key,
243
- finalAttribute.required,
244
- finalAttribute.xdefault,
245
- finalAttribute.array
246
- );
247
- });
248
- break;
249
- case "datetime":
250
- await tryAwaitWithRetry(async () => {
251
- await db.createDatetimeAttribute(
252
- dbId,
253
- collection.$id,
254
- finalAttribute.key,
255
- finalAttribute.required,
256
- finalAttribute.xdefault,
257
- finalAttribute.array
258
- );
259
- });
260
- break;
261
- case "email":
262
- await tryAwaitWithRetry(async () => {
263
- await db.createEmailAttribute(
264
- dbId,
265
- collection.$id,
266
- finalAttribute.key,
267
- finalAttribute.required,
268
- finalAttribute.xdefault,
269
- finalAttribute.array
270
- );
271
- });
272
- break;
273
- case "ip":
274
- await tryAwaitWithRetry(async () => {
275
- await db.createIpAttribute(
276
- dbId,
277
- collection.$id,
278
- finalAttribute.key,
279
- finalAttribute.required,
280
- finalAttribute.xdefault,
281
- finalAttribute.array
282
- );
283
- });
284
- break;
285
- case "url":
286
- await tryAwaitWithRetry(async () => {
287
- await db.createUrlAttribute(
288
- dbId,
289
- collection.$id,
290
- finalAttribute.key,
291
- finalAttribute.required,
292
- finalAttribute.xdefault,
293
- finalAttribute.array
294
- );
295
- });
296
- break;
297
- case "enum":
298
- await tryAwaitWithRetry(async () => {
299
- await db.createEnumAttribute(
300
- dbId,
301
- collection.$id,
302
- finalAttribute.key,
303
- finalAttribute.elements,
304
- finalAttribute.required,
305
- finalAttribute.xdefault,
306
- finalAttribute.array
307
- );
308
- });
309
- break;
310
- case "relationship":
311
- await tryAwaitWithRetry(async () => {
312
- await db.createRelationshipAttribute(
313
- dbId,
314
- collection.$id,
315
- finalAttribute.relatedCollection,
316
- finalAttribute.relationType,
317
- finalAttribute.twoWay,
318
- finalAttribute.key,
319
- finalAttribute.twoWayKey,
320
- finalAttribute.onDelete
321
- );
322
- });
323
- break;
324
- default:
325
- throw new Error(`Unknown attribute type: ${finalAttribute.type}`);
326
- }
327
- });
328
- };
329
-
330
- if (useQueue) {
331
- enqueueOperation(queuedOperation);
332
- } else {
333
- await executeOperation();
334
- }
335
- };
336
-
337
- export const createUpdateCollectionAttributes = async (
338
- db: Databases,
339
- dbId: string,
340
- collection: Models.Collection,
341
- collectionConfig: CollectionCreate,
342
- options: {
343
- updateEnabled?: boolean;
344
- useQueue?: boolean;
345
- verbose?: boolean;
346
- } = {}
347
- ): Promise<void> => {
348
- if (!collectionConfig.attributes) return;
349
-
350
- const { verbose = false } = options;
351
-
352
- if (verbose) {
353
- console.log(chalk.blue(`Processing ${collectionConfig.attributes.length} attributes for collection ${collection.name}`));
354
- }
355
-
356
- for (const attribute of collectionConfig.attributes) {
357
- try {
358
- await createOrUpdateAttribute(db, dbId, collection, attribute, options);
359
-
360
- if (verbose) {
361
- console.log(chalk.green(`✓ Processed attribute ${attribute.key}`));
362
- }
363
-
364
- // Add delay between attribute operations to prevent rate limiting
365
- await delay(250);
366
- } catch (error) {
367
- console.error(chalk.red(`❌ Failed to process attribute ${attribute.key}:`), error);
368
- throw error;
369
- }
370
- }
371
- };
372
-
373
- export const deleteObsoleteAttributes = async (
374
- db: Databases,
375
- dbId: string,
376
- collection: Models.Collection,
377
- collectionConfig: CollectionCreate,
378
- options: {
379
- useQueue?: boolean;
380
- verbose?: boolean;
381
- } = {}
382
- ): Promise<void> => {
383
- const { useQueue = true, verbose = false } = options;
384
-
385
- const configAttributes = collectionConfig.attributes || [];
386
- const configAttributeKeys = new Set(configAttributes.map(attr => attr.key));
387
-
388
- // Find attributes that exist in the database but not in the config
389
- const obsoleteAttributes = collection.attributes.filter(
390
- (attr: any) => !configAttributeKeys.has(attr.key)
391
- );
392
-
393
- if (obsoleteAttributes.length === 0) {
394
- return;
395
- }
396
-
397
- if (verbose) {
398
- console.log(chalk.yellow(`🗑️ Removing ${obsoleteAttributes.length} obsolete attributes from collection ${collection.name}`));
399
- }
400
-
401
- for (const attr of obsoleteAttributes) {
402
- const queuedOperation: QueuedOperation = {
403
- type: "attribute",
404
- collectionId: collection.$id,
405
- attribute: { key: (attr as any).key, type: "delete" } as unknown as Attribute,
406
- collection,
407
- };
408
-
409
- const executeOperation = async () => {
410
- await attributeLimit(() =>
411
- tryAwaitWithRetry(async () => {
412
- await db.deleteAttribute(dbId, collection.$id, (attr as any).key);
413
- })
414
- );
415
- };
416
-
417
- if (useQueue) {
418
- enqueueOperation(queuedOperation);
419
- } else {
420
- await executeOperation();
421
- await delay(250);
422
- }
423
-
424
- if (verbose) {
425
- console.log(chalk.gray(`🗑️ Deleted obsolete attribute ${(attr as any).key}`));
426
- }
427
- }
428
- };