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.
- package/dist/adapters/AdapterFactory.d.ts +87 -0
- package/dist/adapters/AdapterFactory.js +217 -0
- package/dist/adapters/DatabaseAdapter.d.ts +217 -0
- package/dist/adapters/DatabaseAdapter.js +50 -0
- package/dist/adapters/LegacyAdapter.d.ts +49 -0
- package/dist/adapters/LegacyAdapter.js +382 -0
- package/dist/adapters/TablesDBAdapter.d.ts +55 -0
- package/dist/adapters/TablesDBAdapter.js +302 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/collections/attributes.js +41 -22
- package/dist/collections/methods.d.ts +4 -3
- package/dist/collections/methods.js +34 -14
- package/dist/config/yamlConfig.d.ts +40 -437
- package/dist/config/yamlConfig.js +8 -2
- package/dist/databases/setup.js +2 -2
- package/dist/main.js +0 -0
- package/dist/migrations/appwriteToX.d.ts +26 -37
- package/dist/migrations/comprehensiveTransfer.js +4 -4
- package/dist/migrations/dataLoader.d.ts +124 -1484
- package/dist/migrations/dataLoader.js +2 -1
- package/dist/migrations/relationships.d.ts +2 -3
- package/dist/migrations/relationships.js +1 -1
- package/dist/migrations/services/UserMappingService.js +1 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +24 -279
- package/dist/migrations/yaml/YamlImportConfigLoader.js +7 -2
- package/dist/schemas/authUser.d.ts +7 -47
- package/dist/schemas/authUser.js +1 -1
- package/dist/shared/jsonSchemaGenerator.d.ts +0 -2
- package/dist/shared/jsonSchemaGenerator.js +4 -17
- package/dist/shared/migrationHelpers.d.ts +17 -119
- package/dist/shared/operationQueue.js +16 -7
- package/dist/shared/schemaGenerator.js +2 -17
- package/dist/storage/schemas.d.ts +149 -296
- package/dist/users/methods.d.ts +2 -2
- package/dist/utils/configMigration.js +0 -1
- package/dist/utils/getClientFromConfig.d.ts +26 -0
- package/dist/utils/getClientFromConfig.js +37 -0
- package/dist/utils/loadConfigs.js +0 -2
- package/dist/utils/schemaStrings.js +2 -17
- package/dist/utils/setupFiles.js +2 -0
- package/dist/utils/versionDetection.d.ts +56 -0
- package/dist/utils/versionDetection.js +217 -0
- package/dist/utils/yamlConverter.d.ts +0 -1
- package/dist/utils/yamlConverter.js +0 -2
- package/dist/utilsController.js +2 -0
- package/package.json +3 -2
- package/src/adapters/AdapterFactory.ts +296 -0
- package/src/adapters/DatabaseAdapter.ts +290 -0
- package/src/adapters/LegacyAdapter.ts +667 -0
- package/src/adapters/TablesDBAdapter.ts +429 -0
- package/src/adapters/index.ts +37 -0
- package/src/collections/attributes.ts +347 -153
- package/src/collections/methods.ts +43 -28
- package/src/config/yamlConfig.ts +8 -2
- package/src/databases/setup.ts +2 -2
- package/src/migrations/afterImportActions.ts +2 -2
- package/src/migrations/comprehensiveTransfer.ts +4 -0
- package/src/migrations/dataLoader.ts +2 -1
- package/src/migrations/relationships.ts +1 -1
- package/src/migrations/services/UserMappingService.ts +1 -1
- package/src/migrations/yaml/YamlImportConfigLoader.ts +7 -2
- package/src/schemas/authUser.ts +1 -1
- package/src/shared/jsonSchemaGenerator.ts +4 -19
- package/src/shared/operationQueue.ts +20 -13
- package/src/shared/schemaGenerator.ts +2 -16
- package/src/types/node-appwrite-tablesdb.d.ts +44 -0
- package/src/users/methods.ts +2 -2
- package/src/utils/configMigration.ts +0 -1
- package/src/utils/getClientFromConfig.ts +56 -0
- package/src/utils/loadConfigs.ts +0 -2
- package/src/utils/schemaStrings.ts +2 -16
- package/src/utils/setupFiles.ts +2 -0
- package/src/utils/versionDetection.ts +265 -0
- package/src/utils/yamlConverter.ts +0 -2
- package/src/utilsController.ts +2 -0
- package/dist/functions/openapi.d.ts +0 -4
- package/dist/functions/openapi.js +0 -60
- package/dist/migrations/attributes.d.ts +0 -4
- package/dist/migrations/attributes.js +0 -301
- package/dist/migrations/backup.d.ts +0 -687
- package/dist/migrations/backup.js +0 -175
- package/dist/migrations/collections.d.ts +0 -22
- package/dist/migrations/collections.js +0 -347
- package/dist/migrations/converters.d.ts +0 -46
- package/dist/migrations/converters.js +0 -139
- package/dist/migrations/databases.d.ts +0 -2
- package/dist/migrations/databases.js +0 -28
- package/dist/migrations/dbHelpers.d.ts +0 -5
- package/dist/migrations/dbHelpers.js +0 -57
- package/dist/migrations/helper.d.ts +0 -3
- package/dist/migrations/helper.js +0 -21
- package/dist/migrations/indexes.d.ts +0 -4
- package/dist/migrations/indexes.js +0 -19
- package/dist/migrations/logging.d.ts +0 -10
- package/dist/migrations/logging.js +0 -46
- package/dist/migrations/migrationHelper.d.ts +0 -173
- package/dist/migrations/migrationHelper.js +0 -130
- package/dist/migrations/openapi.d.ts +0 -4
- package/dist/migrations/openapi.js +0 -60
- package/dist/migrations/queue.d.ts +0 -13
- package/dist/migrations/queue.js +0 -79
- package/dist/migrations/schemaStrings.d.ts +0 -14
- package/dist/migrations/schemaStrings.js +0 -478
- package/dist/migrations/setupDatabase.d.ts +0 -6
- package/dist/migrations/setupDatabase.js +0 -115
- package/dist/migrations/storage.d.ts +0 -10
- package/dist/migrations/storage.js +0 -340
- package/dist/migrations/users.d.ts +0 -16
- package/dist/migrations/users.js +0 -276
- package/dist/migrations/validationRules.d.ts +0 -43
- package/dist/migrations/validationRules.js +0 -42
- package/dist/shared/attributeManager.d.ts +0 -17
- package/dist/shared/attributeManager.js +0 -272
- package/src/functions/openapi.ts +0 -83
- package/src/shared/attributeManager.ts +0 -428
@@ -12,7 +12,7 @@ import chalk from "chalk";
|
|
12
12
|
interface AttributeWithStatus {
|
13
13
|
key: string;
|
14
14
|
type: string;
|
15
|
-
status:
|
15
|
+
status: "available" | "processing" | "deleting" | "stuck" | "failed";
|
16
16
|
error: string;
|
17
17
|
required: boolean;
|
18
18
|
array?: boolean;
|
@@ -35,72 +35,108 @@ const waitForAttributeAvailable = async (
|
|
35
35
|
): Promise<boolean> => {
|
36
36
|
const startTime = Date.now();
|
37
37
|
let checkInterval = 2000; // Start with 2 seconds
|
38
|
-
|
38
|
+
|
39
39
|
// Calculate exponential backoff: 2s, 4s, 8s, 16s, 30s (capped at 30s)
|
40
40
|
if (retryCount > 0) {
|
41
41
|
const exponentialDelay = Math.min(2000 * Math.pow(2, retryCount), 30000);
|
42
|
-
console.log(
|
42
|
+
console.log(
|
43
|
+
chalk.blue(
|
44
|
+
`Waiting for attribute '${attributeKey}' to become available (retry ${retryCount}, backoff: ${exponentialDelay}ms)...`
|
45
|
+
)
|
46
|
+
);
|
43
47
|
await delay(exponentialDelay);
|
44
48
|
} else {
|
45
|
-
console.log(
|
49
|
+
console.log(
|
50
|
+
chalk.blue(
|
51
|
+
`Waiting for attribute '${attributeKey}' to become available...`
|
52
|
+
)
|
53
|
+
);
|
46
54
|
}
|
47
|
-
|
55
|
+
|
48
56
|
while (Date.now() - startTime < maxWaitTime) {
|
49
57
|
try {
|
50
58
|
const collection = await db.getCollection(dbId, collectionId);
|
51
59
|
const attribute = (collection.attributes as any[]).find(
|
52
60
|
(attr: AttributeWithStatus) => attr.key === attributeKey
|
53
61
|
) as AttributeWithStatus | undefined;
|
54
|
-
|
62
|
+
|
55
63
|
if (!attribute) {
|
56
64
|
console.log(chalk.red(`Attribute '${attributeKey}' not found`));
|
57
65
|
return false;
|
58
66
|
}
|
59
|
-
|
60
|
-
console.log(
|
61
|
-
|
67
|
+
|
68
|
+
console.log(
|
69
|
+
chalk.gray(`Attribute '${attributeKey}' status: ${attribute.status}`)
|
70
|
+
);
|
71
|
+
|
62
72
|
switch (attribute.status) {
|
63
|
-
case
|
64
|
-
console.log(
|
73
|
+
case "available":
|
74
|
+
console.log(
|
75
|
+
chalk.green(`✅ Attribute '${attributeKey}' is now available`)
|
76
|
+
);
|
65
77
|
return true;
|
66
|
-
|
67
|
-
case
|
68
|
-
console.log(
|
78
|
+
|
79
|
+
case "failed":
|
80
|
+
console.log(
|
81
|
+
chalk.red(
|
82
|
+
`❌ Attribute '${attributeKey}' failed: ${attribute.error}`
|
83
|
+
)
|
84
|
+
);
|
69
85
|
return false;
|
70
|
-
|
71
|
-
case
|
72
|
-
console.log(
|
86
|
+
|
87
|
+
case "stuck":
|
88
|
+
console.log(
|
89
|
+
chalk.yellow(
|
90
|
+
`⚠️ Attribute '${attributeKey}' is stuck, will retry...`
|
91
|
+
)
|
92
|
+
);
|
73
93
|
return false;
|
74
|
-
|
75
|
-
case
|
94
|
+
|
95
|
+
case "processing":
|
76
96
|
// Continue waiting
|
77
97
|
break;
|
78
|
-
|
79
|
-
case
|
80
|
-
console.log(
|
98
|
+
|
99
|
+
case "deleting":
|
100
|
+
console.log(
|
101
|
+
chalk.yellow(`Attribute '${attributeKey}' is being deleted`)
|
102
|
+
);
|
81
103
|
break;
|
82
|
-
|
104
|
+
|
83
105
|
default:
|
84
|
-
console.log(
|
106
|
+
console.log(
|
107
|
+
chalk.yellow(
|
108
|
+
`Unknown status '${attribute.status}' for attribute '${attributeKey}'`
|
109
|
+
)
|
110
|
+
);
|
85
111
|
break;
|
86
112
|
}
|
87
|
-
|
113
|
+
|
88
114
|
await delay(checkInterval);
|
89
115
|
} catch (error) {
|
90
116
|
console.log(chalk.red(`Error checking attribute status: ${error}`));
|
91
117
|
return false;
|
92
118
|
}
|
93
119
|
}
|
94
|
-
|
120
|
+
|
95
121
|
// Timeout reached
|
96
|
-
console.log(
|
97
|
-
|
122
|
+
console.log(
|
123
|
+
chalk.yellow(
|
124
|
+
`⏰ Timeout waiting for attribute '${attributeKey}' (${maxWaitTime}ms)`
|
125
|
+
)
|
126
|
+
);
|
127
|
+
|
98
128
|
// If we have retries left and this isn't the last retry, try recreating
|
99
129
|
if (retryCount < maxRetries) {
|
100
|
-
console.log(
|
130
|
+
console.log(
|
131
|
+
chalk.yellow(
|
132
|
+
`🔄 Retrying attribute creation (attempt ${
|
133
|
+
retryCount + 1
|
134
|
+
}/${maxRetries})`
|
135
|
+
)
|
136
|
+
);
|
101
137
|
return false; // Signal that we need to retry
|
102
138
|
}
|
103
|
-
|
139
|
+
|
104
140
|
return false;
|
105
141
|
};
|
106
142
|
|
@@ -114,17 +150,27 @@ const waitForAllAttributesAvailable = async (
|
|
114
150
|
attributeKeys: string[],
|
115
151
|
maxWaitTime: number = 60000
|
116
152
|
): Promise<string[]> => {
|
117
|
-
console.log(
|
118
|
-
|
153
|
+
console.log(
|
154
|
+
chalk.blue(
|
155
|
+
`Waiting for ${attributeKeys.length} attributes to become available...`
|
156
|
+
)
|
157
|
+
);
|
158
|
+
|
119
159
|
const failedAttributes: string[] = [];
|
120
|
-
|
160
|
+
|
121
161
|
for (const attributeKey of attributeKeys) {
|
122
|
-
const success = await waitForAttributeAvailable(
|
162
|
+
const success = await waitForAttributeAvailable(
|
163
|
+
db,
|
164
|
+
dbId,
|
165
|
+
collectionId,
|
166
|
+
attributeKey,
|
167
|
+
maxWaitTime
|
168
|
+
);
|
123
169
|
if (!success) {
|
124
170
|
failedAttributes.push(attributeKey);
|
125
171
|
}
|
126
172
|
}
|
127
|
-
|
173
|
+
|
128
174
|
return failedAttributes;
|
129
175
|
};
|
130
176
|
|
@@ -138,15 +184,19 @@ const deleteAndRecreateCollection = async (
|
|
138
184
|
retryCount: number
|
139
185
|
): Promise<Models.Collection | null> => {
|
140
186
|
try {
|
141
|
-
console.log(
|
142
|
-
|
187
|
+
console.log(
|
188
|
+
chalk.yellow(
|
189
|
+
`🗑️ Deleting collection '${collection.name}' for retry ${retryCount}`
|
190
|
+
)
|
191
|
+
);
|
192
|
+
|
143
193
|
// Delete the collection
|
144
194
|
await db.deleteCollection(dbId, collection.$id);
|
145
195
|
console.log(chalk.yellow(`Deleted collection '${collection.name}'`));
|
146
|
-
|
196
|
+
|
147
197
|
// Wait a bit before recreating
|
148
198
|
await delay(2000);
|
149
|
-
|
199
|
+
|
150
200
|
// Recreate the collection
|
151
201
|
console.log(chalk.blue(`🔄 Recreating collection '${collection.name}'`));
|
152
202
|
const newCollection = await db.createCollection(
|
@@ -157,12 +207,15 @@ const deleteAndRecreateCollection = async (
|
|
157
207
|
collection.documentSecurity,
|
158
208
|
collection.enabled
|
159
209
|
);
|
160
|
-
|
210
|
+
|
161
211
|
console.log(chalk.green(`✅ Recreated collection '${collection.name}'`));
|
162
212
|
return newCollection;
|
163
|
-
|
164
213
|
} catch (error) {
|
165
|
-
console.log(
|
214
|
+
console.log(
|
215
|
+
chalk.red(
|
216
|
+
`Failed to delete/recreate collection '${collection.name}': ${error}`
|
217
|
+
)
|
218
|
+
);
|
166
219
|
return null;
|
167
220
|
}
|
168
221
|
};
|
@@ -242,111 +295,147 @@ export const createOrUpdateAttributeWithStatusCheck = async (
|
|
242
295
|
retryCount: number = 0,
|
243
296
|
maxRetries: number = 5
|
244
297
|
): Promise<boolean> => {
|
245
|
-
console.log(
|
246
|
-
|
298
|
+
console.log(
|
299
|
+
chalk.blue(
|
300
|
+
`Creating/updating attribute '${attribute.key}' (attempt ${
|
301
|
+
retryCount + 1
|
302
|
+
}/${maxRetries + 1})`
|
303
|
+
)
|
304
|
+
);
|
305
|
+
|
247
306
|
try {
|
248
307
|
// First, try to create/update the attribute using existing logic
|
249
308
|
await createOrUpdateAttribute(db, dbId, collection, attribute);
|
250
|
-
|
309
|
+
|
251
310
|
// Now wait for the attribute to become available
|
252
311
|
const success = await waitForAttributeAvailable(
|
253
|
-
db,
|
254
|
-
dbId,
|
255
|
-
collection.$id,
|
256
|
-
attribute.key,
|
312
|
+
db,
|
313
|
+
dbId,
|
314
|
+
collection.$id,
|
315
|
+
attribute.key,
|
257
316
|
60000, // 1 minute timeout
|
258
|
-
retryCount,
|
317
|
+
retryCount,
|
259
318
|
maxRetries
|
260
319
|
);
|
261
|
-
|
320
|
+
|
262
321
|
if (success) {
|
263
322
|
return true;
|
264
323
|
}
|
265
|
-
|
324
|
+
|
266
325
|
// If not successful and we have retries left, delete specific attribute and try again
|
267
326
|
if (retryCount < maxRetries) {
|
268
|
-
console.log(
|
269
|
-
|
327
|
+
console.log(
|
328
|
+
chalk.yellow(
|
329
|
+
`Attribute '${attribute.key}' failed/stuck, deleting and retrying...`
|
330
|
+
)
|
331
|
+
);
|
332
|
+
|
270
333
|
// Try to delete the specific stuck attribute instead of the entire collection
|
271
334
|
try {
|
272
335
|
await db.deleteAttribute(dbId, collection.$id, attribute.key);
|
273
|
-
console.log(
|
274
|
-
|
336
|
+
console.log(
|
337
|
+
chalk.yellow(
|
338
|
+
`Deleted stuck attribute '${attribute.key}', will retry creation`
|
339
|
+
)
|
340
|
+
);
|
341
|
+
|
275
342
|
// Wait a bit before retry
|
276
343
|
await delay(3000);
|
277
|
-
|
344
|
+
|
278
345
|
// Get fresh collection data
|
279
346
|
const freshCollection = await db.getCollection(dbId, collection.$id);
|
280
|
-
|
347
|
+
|
281
348
|
// Retry with the same collection (attribute should be gone now)
|
282
349
|
return await createOrUpdateAttributeWithStatusCheck(
|
283
|
-
db,
|
284
|
-
dbId,
|
285
|
-
freshCollection,
|
286
|
-
attribute,
|
287
|
-
retryCount + 1,
|
350
|
+
db,
|
351
|
+
dbId,
|
352
|
+
freshCollection,
|
353
|
+
attribute,
|
354
|
+
retryCount + 1,
|
288
355
|
maxRetries
|
289
356
|
);
|
290
357
|
} catch (deleteError) {
|
291
|
-
console.log(
|
292
|
-
|
358
|
+
console.log(
|
359
|
+
chalk.red(
|
360
|
+
`Failed to delete stuck attribute '${attribute.key}': ${deleteError}`
|
361
|
+
)
|
362
|
+
);
|
363
|
+
|
293
364
|
// If attribute deletion fails, only then try collection recreation as last resort
|
294
365
|
if (retryCount >= maxRetries - 1) {
|
295
|
-
console.log(
|
296
|
-
|
366
|
+
console.log(
|
367
|
+
chalk.yellow(
|
368
|
+
`Last resort: Recreating collection for attribute '${attribute.key}'`
|
369
|
+
)
|
370
|
+
);
|
371
|
+
|
297
372
|
// Get fresh collection data
|
298
373
|
const freshCollection = await db.getCollection(dbId, collection.$id);
|
299
|
-
|
374
|
+
|
300
375
|
// Delete and recreate collection
|
301
|
-
const newCollection = await deleteAndRecreateCollection(
|
302
|
-
|
376
|
+
const newCollection = await deleteAndRecreateCollection(
|
377
|
+
db,
|
378
|
+
dbId,
|
379
|
+
freshCollection,
|
380
|
+
retryCount + 1
|
381
|
+
);
|
382
|
+
|
303
383
|
if (newCollection) {
|
304
384
|
// Retry with the new collection
|
305
385
|
return await createOrUpdateAttributeWithStatusCheck(
|
306
|
-
db,
|
307
|
-
dbId,
|
308
|
-
newCollection,
|
309
|
-
attribute,
|
310
|
-
retryCount + 1,
|
386
|
+
db,
|
387
|
+
dbId,
|
388
|
+
newCollection,
|
389
|
+
attribute,
|
390
|
+
retryCount + 1,
|
311
391
|
maxRetries
|
312
392
|
);
|
313
393
|
}
|
314
394
|
} else {
|
315
395
|
// Continue to next retry without collection recreation
|
316
396
|
return await createOrUpdateAttributeWithStatusCheck(
|
317
|
-
db,
|
318
|
-
dbId,
|
319
|
-
collection,
|
320
|
-
attribute,
|
321
|
-
retryCount + 1,
|
397
|
+
db,
|
398
|
+
dbId,
|
399
|
+
collection,
|
400
|
+
attribute,
|
401
|
+
retryCount + 1,
|
322
402
|
maxRetries
|
323
403
|
);
|
324
404
|
}
|
325
405
|
}
|
326
406
|
}
|
327
|
-
|
328
|
-
console.log(
|
407
|
+
|
408
|
+
console.log(
|
409
|
+
chalk.red(
|
410
|
+
`❌ Failed to create attribute '${attribute.key}' after ${
|
411
|
+
maxRetries + 1
|
412
|
+
} attempts`
|
413
|
+
)
|
414
|
+
);
|
329
415
|
return false;
|
330
|
-
|
331
416
|
} catch (error) {
|
332
|
-
console.log(
|
333
|
-
|
417
|
+
console.log(
|
418
|
+
chalk.red(`Error creating attribute '${attribute.key}': ${error}`)
|
419
|
+
);
|
420
|
+
|
334
421
|
if (retryCount < maxRetries) {
|
335
|
-
console.log(
|
336
|
-
|
422
|
+
console.log(
|
423
|
+
chalk.yellow(`Retrying attribute '${attribute.key}' due to error...`)
|
424
|
+
);
|
425
|
+
|
337
426
|
// Wait a bit before retry
|
338
427
|
await delay(2000);
|
339
|
-
|
428
|
+
|
340
429
|
return await createOrUpdateAttributeWithStatusCheck(
|
341
|
-
db,
|
342
|
-
dbId,
|
343
|
-
collection,
|
344
|
-
attribute,
|
345
|
-
retryCount + 1,
|
430
|
+
db,
|
431
|
+
dbId,
|
432
|
+
collection,
|
433
|
+
attribute,
|
434
|
+
retryCount + 1,
|
346
435
|
maxRetries
|
347
436
|
);
|
348
437
|
}
|
349
|
-
|
438
|
+
|
350
439
|
return false;
|
351
440
|
}
|
352
441
|
};
|
@@ -408,7 +497,10 @@ export const createOrUpdateAttribute = async (
|
|
408
497
|
// Relationship attribute logic with adjustments
|
409
498
|
let collectionFoundViaRelatedCollection: Models.Collection | undefined;
|
410
499
|
let relatedCollectionId: string | undefined;
|
411
|
-
if (
|
500
|
+
if (
|
501
|
+
finalAttribute.type === "relationship" &&
|
502
|
+
finalAttribute.relatedCollection
|
503
|
+
) {
|
412
504
|
if (nameToIdMapping.has(finalAttribute.relatedCollection)) {
|
413
505
|
relatedCollectionId = nameToIdMapping.get(
|
414
506
|
finalAttribute.relatedCollection
|
@@ -437,9 +529,13 @@ export const createOrUpdateAttribute = async (
|
|
437
529
|
);
|
438
530
|
}
|
439
531
|
}
|
440
|
-
//
|
441
|
-
if (
|
442
|
-
|
532
|
+
// ONLY queue relationship attributes that have actual unresolved dependencies
|
533
|
+
if (!(relatedCollectionId && collectionFoundViaRelatedCollection)) {
|
534
|
+
console.log(
|
535
|
+
chalk.yellow(
|
536
|
+
`⏳ Queueing relationship attribute '${finalAttribute.key}' - related collection '${finalAttribute.relatedCollection}' not found yet`
|
537
|
+
)
|
538
|
+
);
|
443
539
|
enqueueOperation({
|
444
540
|
type: "attribute",
|
445
541
|
collectionId: collection.$id,
|
@@ -480,7 +576,8 @@ export const createOrUpdateAttribute = async (
|
|
480
576
|
finalAttribute.required || false,
|
481
577
|
finalAttribute.xdefault !== undefined && !finalAttribute.required
|
482
578
|
? finalAttribute.xdefault
|
483
|
-
: null
|
579
|
+
: null,
|
580
|
+
finalAttribute.size
|
484
581
|
)
|
485
582
|
);
|
486
583
|
}
|
@@ -491,14 +588,27 @@ export const createOrUpdateAttribute = async (
|
|
491
588
|
finalAttribute.min &&
|
492
589
|
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
493
590
|
) {
|
494
|
-
|
591
|
+
finalAttribute.min = undefined;
|
495
592
|
}
|
496
593
|
if (
|
497
594
|
finalAttribute.max &&
|
498
595
|
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
499
596
|
) {
|
500
|
-
|
597
|
+
finalAttribute.max = undefined;
|
501
598
|
}
|
599
|
+
const minValue =
|
600
|
+
finalAttribute.min !== undefined && finalAttribute.min !== null
|
601
|
+
? parseInt(finalAttribute.min)
|
602
|
+
: -9007199254740991;
|
603
|
+
const maxValue =
|
604
|
+
finalAttribute.max !== undefined && finalAttribute.max !== null
|
605
|
+
? parseInt(finalAttribute.max)
|
606
|
+
: 9007199254740991;
|
607
|
+
console.log(
|
608
|
+
`DEBUG: Creating integer attribute '${
|
609
|
+
finalAttribute.key
|
610
|
+
}' with min=${minValue}, max=${maxValue}, minType=${typeof minValue}, maxType=${typeof maxValue}`
|
611
|
+
);
|
502
612
|
await tryAwaitWithRetry(
|
503
613
|
async () =>
|
504
614
|
await db.createIntegerAttribute(
|
@@ -506,8 +616,8 @@ export const createOrUpdateAttribute = async (
|
|
506
616
|
collection.$id,
|
507
617
|
finalAttribute.key,
|
508
618
|
finalAttribute.required || false,
|
509
|
-
|
510
|
-
|
619
|
+
minValue,
|
620
|
+
maxValue,
|
511
621
|
finalAttribute.xdefault !== undefined && !finalAttribute.required
|
512
622
|
? finalAttribute.xdefault
|
513
623
|
: null,
|
@@ -519,14 +629,27 @@ export const createOrUpdateAttribute = async (
|
|
519
629
|
finalAttribute.min &&
|
520
630
|
BigInt(finalAttribute.min) === BigInt(-9223372036854776000)
|
521
631
|
) {
|
522
|
-
|
632
|
+
finalAttribute.min = undefined;
|
523
633
|
}
|
524
634
|
if (
|
525
635
|
finalAttribute.max &&
|
526
636
|
BigInt(finalAttribute.max) === BigInt(9223372036854776000)
|
527
637
|
) {
|
528
|
-
|
638
|
+
finalAttribute.max = undefined;
|
529
639
|
}
|
640
|
+
const minValue =
|
641
|
+
finalAttribute.min !== undefined && finalAttribute.min !== null
|
642
|
+
? parseInt(finalAttribute.min)
|
643
|
+
: 9007199254740991;
|
644
|
+
const maxValue =
|
645
|
+
finalAttribute.max !== undefined && finalAttribute.max !== null
|
646
|
+
? parseInt(finalAttribute.max)
|
647
|
+
: 9007199254740991;
|
648
|
+
console.log(
|
649
|
+
`DEBUG: Updating integer attribute '${
|
650
|
+
finalAttribute.key
|
651
|
+
}' with min=${minValue}, max=${maxValue}, minType=${typeof minValue}, maxType=${typeof maxValue}`
|
652
|
+
);
|
530
653
|
await tryAwaitWithRetry(
|
531
654
|
async () =>
|
532
655
|
await db.updateIntegerAttribute(
|
@@ -534,11 +657,11 @@ export const createOrUpdateAttribute = async (
|
|
534
657
|
collection.$id,
|
535
658
|
finalAttribute.key,
|
536
659
|
finalAttribute.required || false,
|
537
|
-
finalAttribute.min !== undefined ? finalAttribute.min : -2147483647,
|
538
|
-
finalAttribute.max !== undefined ? finalAttribute.max : 2147483647,
|
539
660
|
finalAttribute.xdefault !== undefined && !finalAttribute.required
|
540
661
|
? finalAttribute.xdefault
|
541
|
-
: null
|
662
|
+
: null,
|
663
|
+
minValue,
|
664
|
+
maxValue
|
542
665
|
)
|
543
666
|
);
|
544
667
|
}
|
@@ -850,115 +973,186 @@ export const createUpdateCollectionAttributesWithStatusCheck = async (
|
|
850
973
|
}
|
851
974
|
|
852
975
|
// First, get fresh collection data and determine which attributes actually need processing
|
853
|
-
console.log(
|
854
|
-
|
976
|
+
console.log(
|
977
|
+
chalk.blue(
|
978
|
+
`Analyzing ${attributes.length} attributes to determine which need processing...`
|
979
|
+
)
|
980
|
+
);
|
981
|
+
|
855
982
|
let currentCollection = collection;
|
856
983
|
try {
|
857
984
|
currentCollection = await db.getCollection(dbId, collection.$id);
|
858
985
|
} catch (error) {
|
859
|
-
console.log(
|
986
|
+
console.log(
|
987
|
+
chalk.yellow(`Warning: Could not refresh collection data: ${error}`)
|
988
|
+
);
|
860
989
|
}
|
861
|
-
|
990
|
+
|
862
991
|
const existingAttributesMap = new Map<string, Attribute>();
|
863
992
|
try {
|
864
|
-
|
865
|
-
|
866
|
-
|
993
|
+
const parsedAttributes = currentCollection.attributes.map((attr) =>
|
994
|
+
// @ts-expect-error
|
995
|
+
parseAttribute(attr)
|
996
|
+
);
|
997
|
+
parsedAttributes.forEach((attr) =>
|
998
|
+
existingAttributesMap.set(attr.key, attr)
|
999
|
+
);
|
867
1000
|
} catch (error) {
|
868
|
-
console.log(
|
1001
|
+
console.log(
|
1002
|
+
chalk.yellow(`Warning: Could not parse existing attributes: ${error}`)
|
1003
|
+
);
|
869
1004
|
}
|
870
|
-
|
1005
|
+
|
871
1006
|
// Filter to only attributes that need processing (new or changed)
|
872
|
-
const attributesToProcess = attributes.filter(attribute => {
|
1007
|
+
const attributesToProcess = attributes.filter((attribute) => {
|
873
1008
|
const existing = existingAttributesMap.get(attribute.key);
|
874
1009
|
if (!existing) {
|
875
1010
|
console.log(chalk.blue(`➕ New attribute: ${attribute.key}`));
|
876
1011
|
return true;
|
877
1012
|
}
|
878
|
-
|
1013
|
+
|
879
1014
|
const needsUpdate = !attributesSame(existing, attribute);
|
880
1015
|
if (needsUpdate) {
|
881
1016
|
console.log(chalk.blue(`🔄 Changed attribute: ${attribute.key}`));
|
882
1017
|
} else {
|
883
|
-
console.log(
|
1018
|
+
console.log(
|
1019
|
+
chalk.gray(`✅ Unchanged attribute: ${attribute.key} (skipping)`)
|
1020
|
+
);
|
884
1021
|
}
|
885
1022
|
return needsUpdate;
|
886
1023
|
});
|
887
|
-
|
1024
|
+
|
888
1025
|
if (attributesToProcess.length === 0) {
|
889
|
-
console.log(
|
1026
|
+
console.log(
|
1027
|
+
chalk.green(
|
1028
|
+
`✅ All ${attributes.length} attributes are already up to date for collection: ${collection.name}`
|
1029
|
+
)
|
1030
|
+
);
|
890
1031
|
return true;
|
891
1032
|
}
|
892
|
-
|
893
|
-
console.log(
|
894
|
-
|
1033
|
+
|
1034
|
+
console.log(
|
1035
|
+
chalk.blue(
|
1036
|
+
`Creating ${attributesToProcess.length} attributes sequentially with status monitoring...`
|
1037
|
+
)
|
1038
|
+
);
|
1039
|
+
|
895
1040
|
let remainingAttributes = [...attributesToProcess];
|
896
1041
|
let overallRetryCount = 0;
|
897
1042
|
const maxOverallRetries = 3;
|
898
|
-
|
899
|
-
while (
|
1043
|
+
|
1044
|
+
while (
|
1045
|
+
remainingAttributes.length > 0 &&
|
1046
|
+
overallRetryCount < maxOverallRetries
|
1047
|
+
) {
|
900
1048
|
const attributesToProcessThisRound = [...remainingAttributes];
|
901
1049
|
remainingAttributes = []; // Reset for next iteration
|
902
|
-
|
903
|
-
console.log(
|
904
|
-
|
1050
|
+
|
1051
|
+
console.log(
|
1052
|
+
chalk.blue(
|
1053
|
+
`\n=== Attempt ${
|
1054
|
+
overallRetryCount + 1
|
1055
|
+
}/${maxOverallRetries} - Processing ${
|
1056
|
+
attributesToProcessThisRound.length
|
1057
|
+
} attributes ===`
|
1058
|
+
)
|
1059
|
+
);
|
1060
|
+
|
905
1061
|
for (const attribute of attributesToProcessThisRound) {
|
906
|
-
console.log(
|
907
|
-
|
1062
|
+
console.log(
|
1063
|
+
chalk.blue(`\n--- Processing attribute: ${attribute.key} ---`)
|
1064
|
+
);
|
1065
|
+
|
908
1066
|
const success = await createOrUpdateAttributeWithStatusCheck(
|
909
|
-
db,
|
910
|
-
dbId,
|
911
|
-
currentCollection,
|
1067
|
+
db,
|
1068
|
+
dbId,
|
1069
|
+
currentCollection,
|
912
1070
|
attribute
|
913
1071
|
);
|
914
|
-
|
1072
|
+
|
915
1073
|
if (success) {
|
916
|
-
console.log(
|
917
|
-
|
1074
|
+
console.log(
|
1075
|
+
chalk.green(`✅ Successfully created attribute: ${attribute.key}`)
|
1076
|
+
);
|
1077
|
+
|
918
1078
|
// Get updated collection data for next iteration
|
919
1079
|
try {
|
920
1080
|
currentCollection = await db.getCollection(dbId, collection.$id);
|
921
1081
|
} catch (error) {
|
922
|
-
console.log(
|
1082
|
+
console.log(
|
1083
|
+
chalk.yellow(`Warning: Could not refresh collection data: ${error}`)
|
1084
|
+
);
|
923
1085
|
}
|
924
|
-
|
1086
|
+
|
925
1087
|
// Add delay between successful attributes
|
926
1088
|
await delay(1000);
|
927
1089
|
} else {
|
928
|
-
console.log(
|
1090
|
+
console.log(
|
1091
|
+
chalk.red(
|
1092
|
+
`❌ Failed to create attribute: ${attribute.key}, will retry in next round`
|
1093
|
+
)
|
1094
|
+
);
|
929
1095
|
remainingAttributes.push(attribute); // Add back to retry list
|
930
1096
|
}
|
931
1097
|
}
|
932
|
-
|
1098
|
+
|
933
1099
|
if (remainingAttributes.length === 0) {
|
934
|
-
console.log(
|
1100
|
+
console.log(
|
1101
|
+
chalk.green(
|
1102
|
+
`\n✅ Successfully created all ${attributesToProcess.length} attributes for collection: ${collection.name}`
|
1103
|
+
)
|
1104
|
+
);
|
935
1105
|
return true;
|
936
1106
|
}
|
937
|
-
|
1107
|
+
|
938
1108
|
overallRetryCount++;
|
939
|
-
|
1109
|
+
|
940
1110
|
if (overallRetryCount < maxOverallRetries) {
|
941
|
-
console.log(
|
1111
|
+
console.log(
|
1112
|
+
chalk.yellow(
|
1113
|
+
`\n⏳ Waiting 5 seconds before retrying ${attributesToProcess.length} failed attributes...`
|
1114
|
+
)
|
1115
|
+
);
|
942
1116
|
await delay(5000);
|
943
|
-
|
1117
|
+
|
944
1118
|
// Refresh collection data before retry
|
945
1119
|
try {
|
946
1120
|
currentCollection = await db.getCollection(dbId, collection.$id);
|
947
1121
|
console.log(chalk.blue(`Refreshed collection data for retry`));
|
948
1122
|
} catch (error) {
|
949
|
-
console.log(
|
1123
|
+
console.log(
|
1124
|
+
chalk.yellow(
|
1125
|
+
`Warning: Could not refresh collection data for retry: ${error}`
|
1126
|
+
)
|
1127
|
+
);
|
950
1128
|
}
|
951
1129
|
}
|
952
1130
|
}
|
953
|
-
|
1131
|
+
|
954
1132
|
// If we get here, some attributes still failed after all retries
|
955
1133
|
if (attributesToProcess.length > 0) {
|
956
|
-
console.log(
|
957
|
-
|
1134
|
+
console.log(
|
1135
|
+
chalk.red(
|
1136
|
+
`\n❌ Failed to create ${
|
1137
|
+
attributesToProcess.length
|
1138
|
+
} attributes after ${maxOverallRetries} attempts: ${attributesToProcess
|
1139
|
+
.map((a) => a.key)
|
1140
|
+
.join(", ")}`
|
1141
|
+
)
|
1142
|
+
);
|
1143
|
+
console.log(
|
1144
|
+
chalk.red(
|
1145
|
+
`This may indicate a fundamental issue with the attribute definitions or Appwrite instance`
|
1146
|
+
)
|
1147
|
+
);
|
958
1148
|
return false;
|
959
1149
|
}
|
960
|
-
|
961
|
-
console.log(
|
1150
|
+
|
1151
|
+
console.log(
|
1152
|
+
chalk.green(
|
1153
|
+
`\n✅ Successfully created all ${attributes.length} attributes for collection: ${collection.name}`
|
1154
|
+
)
|
1155
|
+
);
|
962
1156
|
return true;
|
963
1157
|
};
|
964
1158
|
|