@njdamstra/appwrite-utils-cli 1.11.6 → 1.11.8
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.
|
@@ -250,6 +250,31 @@ export async function executeMigrationPlan(adapter, options) {
|
|
|
250
250
|
skipped += entries.length;
|
|
251
251
|
continue;
|
|
252
252
|
}
|
|
253
|
+
// Temporarily set ALL required attributes in this collection to optional.
|
|
254
|
+
// Appwrite rejects partial updateRow calls if a required attribute is missing
|
|
255
|
+
// from the payload — even if the field exists in the document. This affects
|
|
256
|
+
// every data-copy step during migration.
|
|
257
|
+
const schemaRes = await tryAwaitWithRetry(() => adapter.getTable({ databaseId: first.databaseId, tableId: first.collectionId }));
|
|
258
|
+
const allAttrs = schemaRes?.data?.attributes || schemaRes?.data?.columns || [];
|
|
259
|
+
const originallyRequired = allAttrs
|
|
260
|
+
.filter((a) => a.required === true && a.status === "available")
|
|
261
|
+
.map((a) => a.key);
|
|
262
|
+
if (originallyRequired.length > 0) {
|
|
263
|
+
MessageFormatter.info(` Temporarily setting ${originallyRequired.length} required attribute(s) to optional...`, { prefix: "Execute" });
|
|
264
|
+
for (const key of originallyRequired) {
|
|
265
|
+
try {
|
|
266
|
+
await tryAwaitWithRetry(() => adapter.updateAttribute({
|
|
267
|
+
databaseId: first.databaseId,
|
|
268
|
+
tableId: first.collectionId,
|
|
269
|
+
key,
|
|
270
|
+
required: false,
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Non-fatal — attribute might not support updating required
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
253
278
|
// Migrate each attribute in this collection
|
|
254
279
|
for (const entry of entries) {
|
|
255
280
|
const cpEntry = getOrCreateCheckpointEntry(checkpoint, entry);
|
|
@@ -275,6 +300,25 @@ export async function executeMigrationPlan(adapter, options) {
|
|
|
275
300
|
MessageFormatter.error(` ${entry.attributeKey}: FAILED — ${cpEntry.error}`, undefined, { prefix: "Execute" });
|
|
276
301
|
}
|
|
277
302
|
}
|
|
303
|
+
// Restore required flags for ALL originally-required attributes.
|
|
304
|
+
// This covers both migrated attributes (recreated as optional) and
|
|
305
|
+
// non-migrated attributes (temporarily set to optional above).
|
|
306
|
+
if (originallyRequired.length > 0) {
|
|
307
|
+
MessageFormatter.info(` Restoring ${originallyRequired.length} required attribute(s)...`, { prefix: "Execute" });
|
|
308
|
+
for (const key of originallyRequired) {
|
|
309
|
+
try {
|
|
310
|
+
await tryAwaitWithRetry(() => adapter.updateAttribute({
|
|
311
|
+
databaseId: first.databaseId,
|
|
312
|
+
tableId: first.collectionId,
|
|
313
|
+
key,
|
|
314
|
+
required: true,
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
MessageFormatter.info(` Warning: could not restore required flag for ${key}`, { prefix: "Execute" });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
278
322
|
// After collection completes, offer to update local YAML
|
|
279
323
|
const successInGroup = entries.filter((e) => {
|
|
280
324
|
const cp = findCheckpointEntry(checkpoint, e);
|
|
@@ -426,21 +470,8 @@ async function migrateOneAttribute(adapter, entry, cpEntry, checkpoint, checkpoi
|
|
|
426
470
|
advance("backup_deleted");
|
|
427
471
|
}
|
|
428
472
|
// Step 9: Mark completed
|
|
429
|
-
//
|
|
430
|
-
|
|
431
|
-
try {
|
|
432
|
-
await tryAwaitWithRetry(() => adapter.updateAttribute({
|
|
433
|
-
databaseId,
|
|
434
|
-
tableId: collectionId,
|
|
435
|
-
key: attributeKey,
|
|
436
|
-
required: true,
|
|
437
|
-
}));
|
|
438
|
-
}
|
|
439
|
-
catch {
|
|
440
|
-
// Non-fatal — attribute is migrated, just not set back to required
|
|
441
|
-
MessageFormatter.info(` Warning: could not set ${attributeKey} back to required`, { prefix: "Migrate" });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
473
|
+
// NOTE: required flag is restored AFTER all attributes in the collection
|
|
474
|
+
// are migrated, to avoid partial-update validation errors on other attributes.
|
|
444
475
|
advance("completed");
|
|
445
476
|
}
|
|
446
477
|
// ────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@njdamstra/appwrite-utils-cli",
|
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
|
4
|
-
"version": "1.11.
|
|
4
|
+
"version": "1.11.8",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -338,6 +338,40 @@ export async function executeMigrationPlan(
|
|
|
338
338
|
continue;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
// Temporarily set ALL required attributes in this collection to optional.
|
|
342
|
+
// Appwrite rejects partial updateRow calls if a required attribute is missing
|
|
343
|
+
// from the payload — even if the field exists in the document. This affects
|
|
344
|
+
// every data-copy step during migration.
|
|
345
|
+
const schemaRes = await tryAwaitWithRetry(() =>
|
|
346
|
+
adapter.getTable({ databaseId: first.databaseId, tableId: first.collectionId })
|
|
347
|
+
);
|
|
348
|
+
const allAttrs: any[] =
|
|
349
|
+
schemaRes?.data?.attributes || schemaRes?.data?.columns || [];
|
|
350
|
+
const originallyRequired = allAttrs
|
|
351
|
+
.filter((a: any) => a.required === true && a.status === "available")
|
|
352
|
+
.map((a: any) => a.key as string);
|
|
353
|
+
|
|
354
|
+
if (originallyRequired.length > 0) {
|
|
355
|
+
MessageFormatter.info(
|
|
356
|
+
` Temporarily setting ${originallyRequired.length} required attribute(s) to optional...`,
|
|
357
|
+
{ prefix: "Execute" }
|
|
358
|
+
);
|
|
359
|
+
for (const key of originallyRequired) {
|
|
360
|
+
try {
|
|
361
|
+
await tryAwaitWithRetry(() =>
|
|
362
|
+
adapter.updateAttribute({
|
|
363
|
+
databaseId: first.databaseId,
|
|
364
|
+
tableId: first.collectionId,
|
|
365
|
+
key,
|
|
366
|
+
required: false,
|
|
367
|
+
} as any)
|
|
368
|
+
);
|
|
369
|
+
} catch {
|
|
370
|
+
// Non-fatal — attribute might not support updating required
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
341
375
|
// Migrate each attribute in this collection
|
|
342
376
|
for (const entry of entries) {
|
|
343
377
|
const cpEntry = getOrCreateCheckpointEntry(checkpoint, entry);
|
|
@@ -382,6 +416,33 @@ export async function executeMigrationPlan(
|
|
|
382
416
|
}
|
|
383
417
|
}
|
|
384
418
|
|
|
419
|
+
// Restore required flags for ALL originally-required attributes.
|
|
420
|
+
// This covers both migrated attributes (recreated as optional) and
|
|
421
|
+
// non-migrated attributes (temporarily set to optional above).
|
|
422
|
+
if (originallyRequired.length > 0) {
|
|
423
|
+
MessageFormatter.info(
|
|
424
|
+
` Restoring ${originallyRequired.length} required attribute(s)...`,
|
|
425
|
+
{ prefix: "Execute" }
|
|
426
|
+
);
|
|
427
|
+
for (const key of originallyRequired) {
|
|
428
|
+
try {
|
|
429
|
+
await tryAwaitWithRetry(() =>
|
|
430
|
+
adapter.updateAttribute({
|
|
431
|
+
databaseId: first.databaseId,
|
|
432
|
+
tableId: first.collectionId,
|
|
433
|
+
key,
|
|
434
|
+
required: true,
|
|
435
|
+
} as any)
|
|
436
|
+
);
|
|
437
|
+
} catch {
|
|
438
|
+
MessageFormatter.info(
|
|
439
|
+
` Warning: could not restore required flag for ${key}`,
|
|
440
|
+
{ prefix: "Execute" }
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
385
446
|
// After collection completes, offer to update local YAML
|
|
386
447
|
const successInGroup = entries.filter((e) => {
|
|
387
448
|
const cp = findCheckpointEntry(checkpoint, e);
|
|
@@ -630,25 +691,8 @@ async function migrateOneAttribute(
|
|
|
630
691
|
}
|
|
631
692
|
|
|
632
693
|
// Step 9: Mark completed
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
try {
|
|
636
|
-
await tryAwaitWithRetry(() =>
|
|
637
|
-
adapter.updateAttribute({
|
|
638
|
-
databaseId,
|
|
639
|
-
tableId: collectionId,
|
|
640
|
-
key: attributeKey,
|
|
641
|
-
required: true,
|
|
642
|
-
} as any)
|
|
643
|
-
);
|
|
644
|
-
} catch {
|
|
645
|
-
// Non-fatal — attribute is migrated, just not set back to required
|
|
646
|
-
MessageFormatter.info(
|
|
647
|
-
` Warning: could not set ${attributeKey} back to required`,
|
|
648
|
-
{ prefix: "Migrate" }
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
694
|
+
// NOTE: required flag is restored AFTER all attributes in the collection
|
|
695
|
+
// are migrated, to avoid partial-update validation errors on other attributes.
|
|
652
696
|
advance("completed");
|
|
653
697
|
}
|
|
654
698
|
|