appwrite-utils-cli 1.0.9 → 1.1.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/README.md +48 -0
- package/dist/collections/attributes.d.ts +8 -0
- package/dist/collections/attributes.js +195 -0
- package/dist/collections/indexes.d.ts +8 -0
- package/dist/collections/indexes.js +150 -0
- package/dist/collections/methods.js +105 -53
- package/dist/interactiveCLI.js +134 -42
- package/dist/migrations/transfer.js +29 -40
- package/package.json +1 -1
- package/src/collections/attributes.ts +339 -0
- package/src/collections/indexes.ts +264 -0
- package/src/collections/methods.ts +175 -87
- package/src/interactiveCLI.ts +137 -42
- package/src/migrations/transfer.ts +48 -99
@@ -1,6 +1,270 @@
|
|
1
1
|
import { indexSchema, type Index } from "appwrite-utils";
|
2
2
|
import { Databases, IndexType, Query, type Models } from "node-appwrite";
|
3
3
|
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
4
|
+
import chalk from "chalk";
|
5
|
+
|
6
|
+
// Interface for index with status
|
7
|
+
interface IndexWithStatus {
|
8
|
+
key: string;
|
9
|
+
type: string;
|
10
|
+
status: 'available' | 'processing' | 'deleting' | 'stuck' | 'failed';
|
11
|
+
error: string;
|
12
|
+
attributes: string[];
|
13
|
+
orders?: string[];
|
14
|
+
$createdAt: string;
|
15
|
+
$updatedAt: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Wait for index to become available, with retry logic for stuck indexes and exponential backoff
|
20
|
+
*/
|
21
|
+
const waitForIndexAvailable = async (
|
22
|
+
db: Databases,
|
23
|
+
dbId: string,
|
24
|
+
collectionId: string,
|
25
|
+
indexKey: string,
|
26
|
+
maxWaitTime: number = 60000, // 1 minute
|
27
|
+
retryCount: number = 0,
|
28
|
+
maxRetries: number = 5
|
29
|
+
): Promise<boolean> => {
|
30
|
+
const startTime = Date.now();
|
31
|
+
let checkInterval = 2000; // Start with 2 seconds
|
32
|
+
|
33
|
+
// Calculate exponential backoff: 2s, 4s, 8s, 16s, 30s (capped at 30s)
|
34
|
+
if (retryCount > 0) {
|
35
|
+
const exponentialDelay = Math.min(2000 * Math.pow(2, retryCount), 30000);
|
36
|
+
console.log(chalk.blue(`Waiting for index '${indexKey}' to become available (retry ${retryCount}, backoff: ${exponentialDelay}ms)...`));
|
37
|
+
await delay(exponentialDelay);
|
38
|
+
} else {
|
39
|
+
console.log(chalk.blue(`Waiting for index '${indexKey}' to become available...`));
|
40
|
+
}
|
41
|
+
|
42
|
+
while (Date.now() - startTime < maxWaitTime) {
|
43
|
+
try {
|
44
|
+
const indexList = await db.listIndexes(dbId, collectionId);
|
45
|
+
const index = indexList.indexes.find(
|
46
|
+
(idx: any) => idx.key === indexKey
|
47
|
+
) as IndexWithStatus | undefined;
|
48
|
+
|
49
|
+
if (!index) {
|
50
|
+
console.log(chalk.red(`Index '${indexKey}' not found`));
|
51
|
+
return false;
|
52
|
+
}
|
53
|
+
|
54
|
+
console.log(chalk.gray(`Index '${indexKey}' status: ${index.status}`));
|
55
|
+
|
56
|
+
switch (index.status) {
|
57
|
+
case 'available':
|
58
|
+
console.log(chalk.green(`✅ Index '${indexKey}' is now available`));
|
59
|
+
return true;
|
60
|
+
|
61
|
+
case 'failed':
|
62
|
+
console.log(chalk.red(`❌ Index '${indexKey}' failed: ${index.error}`));
|
63
|
+
return false;
|
64
|
+
|
65
|
+
case 'stuck':
|
66
|
+
console.log(chalk.yellow(`⚠️ Index '${indexKey}' is stuck, will retry...`));
|
67
|
+
return false;
|
68
|
+
|
69
|
+
case 'processing':
|
70
|
+
// Continue waiting
|
71
|
+
break;
|
72
|
+
|
73
|
+
case 'deleting':
|
74
|
+
console.log(chalk.yellow(`Index '${indexKey}' is being deleted`));
|
75
|
+
break;
|
76
|
+
|
77
|
+
default:
|
78
|
+
console.log(chalk.yellow(`Unknown status '${index.status}' for index '${indexKey}'`));
|
79
|
+
break;
|
80
|
+
}
|
81
|
+
|
82
|
+
await delay(checkInterval);
|
83
|
+
} catch (error) {
|
84
|
+
console.log(chalk.red(`Error checking index status: ${error}`));
|
85
|
+
return false;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
// Timeout reached
|
90
|
+
console.log(chalk.yellow(`⏰ Timeout waiting for index '${indexKey}' (${maxWaitTime}ms)`));
|
91
|
+
|
92
|
+
// If we have retries left and this isn't the last retry, try recreating
|
93
|
+
if (retryCount < maxRetries) {
|
94
|
+
console.log(chalk.yellow(`🔄 Retrying index creation (attempt ${retryCount + 1}/${maxRetries})`));
|
95
|
+
return false; // Signal that we need to retry
|
96
|
+
}
|
97
|
+
|
98
|
+
return false;
|
99
|
+
};
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Delete collection and recreate for index retry (reused from attributes.ts)
|
103
|
+
*/
|
104
|
+
const deleteAndRecreateCollectionForIndex = async (
|
105
|
+
db: Databases,
|
106
|
+
dbId: string,
|
107
|
+
collection: Models.Collection,
|
108
|
+
retryCount: number
|
109
|
+
): Promise<Models.Collection | null> => {
|
110
|
+
try {
|
111
|
+
console.log(chalk.yellow(`🗑️ Deleting collection '${collection.name}' for index retry ${retryCount}`));
|
112
|
+
|
113
|
+
// Delete the collection
|
114
|
+
await db.deleteCollection(dbId, collection.$id);
|
115
|
+
console.log(chalk.yellow(`Deleted collection '${collection.name}'`));
|
116
|
+
|
117
|
+
// Wait a bit before recreating
|
118
|
+
await delay(2000);
|
119
|
+
|
120
|
+
// Recreate the collection
|
121
|
+
console.log(chalk.blue(`🔄 Recreating collection '${collection.name}'`));
|
122
|
+
const newCollection = await db.createCollection(
|
123
|
+
dbId,
|
124
|
+
collection.$id,
|
125
|
+
collection.name,
|
126
|
+
collection.$permissions,
|
127
|
+
collection.documentSecurity,
|
128
|
+
collection.enabled
|
129
|
+
);
|
130
|
+
|
131
|
+
console.log(chalk.green(`✅ Recreated collection '${collection.name}'`));
|
132
|
+
return newCollection;
|
133
|
+
|
134
|
+
} catch (error) {
|
135
|
+
console.log(chalk.red(`Failed to delete/recreate collection '${collection.name}': ${error}`));
|
136
|
+
return null;
|
137
|
+
}
|
138
|
+
};
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Enhanced index creation with proper status monitoring and retry logic
|
142
|
+
*/
|
143
|
+
export const createOrUpdateIndexWithStatusCheck = async (
|
144
|
+
dbId: string,
|
145
|
+
db: Databases,
|
146
|
+
collectionId: string,
|
147
|
+
collection: Models.Collection,
|
148
|
+
index: Index,
|
149
|
+
retryCount: number = 0,
|
150
|
+
maxRetries: number = 5
|
151
|
+
): Promise<boolean> => {
|
152
|
+
console.log(chalk.blue(`Creating/updating index '${index.key}' (attempt ${retryCount + 1}/${maxRetries + 1})`));
|
153
|
+
|
154
|
+
try {
|
155
|
+
// First, try to create/update the index using existing logic
|
156
|
+
await createOrUpdateIndex(dbId, db, collectionId, index);
|
157
|
+
|
158
|
+
// Now wait for the index to become available
|
159
|
+
const success = await waitForIndexAvailable(
|
160
|
+
db,
|
161
|
+
dbId,
|
162
|
+
collectionId,
|
163
|
+
index.key,
|
164
|
+
60000, // 1 minute timeout
|
165
|
+
retryCount,
|
166
|
+
maxRetries
|
167
|
+
);
|
168
|
+
|
169
|
+
if (success) {
|
170
|
+
return true;
|
171
|
+
}
|
172
|
+
|
173
|
+
// If not successful and we have retries left, delete collection and try again
|
174
|
+
if (retryCount < maxRetries) {
|
175
|
+
console.log(chalk.yellow(`Index '${index.key}' failed/stuck, retrying...`));
|
176
|
+
|
177
|
+
// Get fresh collection data
|
178
|
+
const freshCollection = await db.getCollection(dbId, collectionId);
|
179
|
+
|
180
|
+
// Delete and recreate collection
|
181
|
+
const newCollection = await deleteAndRecreateCollectionForIndex(db, dbId, freshCollection, retryCount + 1);
|
182
|
+
|
183
|
+
if (newCollection) {
|
184
|
+
// Retry with the new collection
|
185
|
+
return await createOrUpdateIndexWithStatusCheck(
|
186
|
+
dbId,
|
187
|
+
db,
|
188
|
+
newCollection.$id,
|
189
|
+
newCollection,
|
190
|
+
index,
|
191
|
+
retryCount + 1,
|
192
|
+
maxRetries
|
193
|
+
);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
console.log(chalk.red(`❌ Failed to create index '${index.key}' after ${maxRetries + 1} attempts`));
|
198
|
+
return false;
|
199
|
+
|
200
|
+
} catch (error) {
|
201
|
+
console.log(chalk.red(`Error creating index '${index.key}': ${error}`));
|
202
|
+
|
203
|
+
if (retryCount < maxRetries) {
|
204
|
+
console.log(chalk.yellow(`Retrying index '${index.key}' due to error...`));
|
205
|
+
|
206
|
+
// Wait a bit before retry
|
207
|
+
await delay(2000);
|
208
|
+
|
209
|
+
return await createOrUpdateIndexWithStatusCheck(
|
210
|
+
dbId,
|
211
|
+
db,
|
212
|
+
collectionId,
|
213
|
+
collection,
|
214
|
+
index,
|
215
|
+
retryCount + 1,
|
216
|
+
maxRetries
|
217
|
+
);
|
218
|
+
}
|
219
|
+
|
220
|
+
return false;
|
221
|
+
}
|
222
|
+
};
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Enhanced index creation with status monitoring for all indexes
|
226
|
+
*/
|
227
|
+
export const createOrUpdateIndexesWithStatusCheck = async (
|
228
|
+
dbId: string,
|
229
|
+
db: Databases,
|
230
|
+
collectionId: string,
|
231
|
+
collection: Models.Collection,
|
232
|
+
indexes: Index[]
|
233
|
+
): Promise<boolean> => {
|
234
|
+
console.log(chalk.blue(`Creating/updating ${indexes.length} indexes with status monitoring...`));
|
235
|
+
|
236
|
+
const failedIndexes: string[] = [];
|
237
|
+
|
238
|
+
for (const index of indexes) {
|
239
|
+
console.log(chalk.blue(`\n--- Processing index: ${index.key} ---`));
|
240
|
+
|
241
|
+
const success = await createOrUpdateIndexWithStatusCheck(
|
242
|
+
dbId,
|
243
|
+
db,
|
244
|
+
collectionId,
|
245
|
+
collection,
|
246
|
+
index
|
247
|
+
);
|
248
|
+
|
249
|
+
if (success) {
|
250
|
+
console.log(chalk.green(`✅ Successfully created index: ${index.key}`));
|
251
|
+
|
252
|
+
// Add delay between successful indexes
|
253
|
+
await delay(1000);
|
254
|
+
} else {
|
255
|
+
console.log(chalk.red(`❌ Failed to create index: ${index.key}`));
|
256
|
+
failedIndexes.push(index.key);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
if (failedIndexes.length > 0) {
|
261
|
+
console.log(chalk.red(`\n❌ Failed to create ${failedIndexes.length} indexes: ${failedIndexes.join(', ')}`));
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
|
265
|
+
console.log(chalk.green(`\n✅ Successfully created all ${indexes.length} indexes`));
|
266
|
+
return true;
|
267
|
+
};
|
4
268
|
|
5
269
|
export const createOrUpdateIndex = async (
|
6
270
|
dbId: string,
|
@@ -23,6 +23,7 @@ import {
|
|
23
23
|
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
24
24
|
import { MessageFormatter } from "../shared/messageFormatter.js";
|
25
25
|
import { ProgressManager } from "../shared/progressManager.js";
|
26
|
+
import chalk from "chalk";
|
26
27
|
|
27
28
|
export const documentExists = async (
|
28
29
|
db: Databases,
|
@@ -615,6 +616,123 @@ export const transferDocumentsBetweenDbsLocalToLocal = async (
|
|
615
616
|
);
|
616
617
|
};
|
617
618
|
|
619
|
+
/**
|
620
|
+
* Enhanced document transfer with fault tolerance and exponential backoff
|
621
|
+
*/
|
622
|
+
const transferDocumentWithRetry = async (
|
623
|
+
db: Databases,
|
624
|
+
dbId: string,
|
625
|
+
collectionId: string,
|
626
|
+
documentId: string,
|
627
|
+
documentData: any,
|
628
|
+
permissions: string[],
|
629
|
+
maxRetries: number = 3,
|
630
|
+
retryCount: number = 0
|
631
|
+
): Promise<boolean> => {
|
632
|
+
try {
|
633
|
+
await db.createDocument(
|
634
|
+
dbId,
|
635
|
+
collectionId,
|
636
|
+
documentId,
|
637
|
+
documentData,
|
638
|
+
permissions
|
639
|
+
);
|
640
|
+
return true;
|
641
|
+
} catch (error: any) {
|
642
|
+
// Check if document already exists
|
643
|
+
if (error.code === 409 || error.message?.includes('already exists')) {
|
644
|
+
console.log(chalk.yellow(`Document ${documentId} already exists, skipping...`));
|
645
|
+
return true;
|
646
|
+
}
|
647
|
+
|
648
|
+
if (retryCount < maxRetries) {
|
649
|
+
// Calculate exponential backoff: 1s, 2s, 4s
|
650
|
+
const exponentialDelay = Math.min(1000 * Math.pow(2, retryCount), 8000);
|
651
|
+
console.log(chalk.yellow(`Retrying document ${documentId} (attempt ${retryCount + 1}/${maxRetries}, backoff: ${exponentialDelay}ms)`));
|
652
|
+
|
653
|
+
await delay(exponentialDelay);
|
654
|
+
|
655
|
+
return await transferDocumentWithRetry(
|
656
|
+
db,
|
657
|
+
dbId,
|
658
|
+
collectionId,
|
659
|
+
documentId,
|
660
|
+
documentData,
|
661
|
+
permissions,
|
662
|
+
maxRetries,
|
663
|
+
retryCount + 1
|
664
|
+
);
|
665
|
+
}
|
666
|
+
|
667
|
+
console.log(chalk.red(`Failed to transfer document ${documentId} after ${maxRetries} retries: ${error.message}`));
|
668
|
+
return false;
|
669
|
+
}
|
670
|
+
};
|
671
|
+
|
672
|
+
/**
|
673
|
+
* Enhanced batch document transfer with fault tolerance
|
674
|
+
*/
|
675
|
+
const transferDocumentBatchWithRetry = async (
|
676
|
+
db: Databases,
|
677
|
+
dbId: string,
|
678
|
+
collectionId: string,
|
679
|
+
documents: any[],
|
680
|
+
batchSize: number = 10
|
681
|
+
): Promise<{ successful: number; failed: number }> => {
|
682
|
+
let successful = 0;
|
683
|
+
let failed = 0;
|
684
|
+
|
685
|
+
// Process documents in smaller batches to avoid overwhelming the server
|
686
|
+
const documentBatches = chunk(documents, batchSize);
|
687
|
+
|
688
|
+
for (const batch of documentBatches) {
|
689
|
+
console.log(chalk.blue(`Processing batch of ${batch.length} documents...`));
|
690
|
+
|
691
|
+
const batchPromises = batch.map(async (doc) => {
|
692
|
+
const toCreateObject: Partial<typeof doc> = { ...doc };
|
693
|
+
delete toCreateObject.$databaseId;
|
694
|
+
delete toCreateObject.$collectionId;
|
695
|
+
delete toCreateObject.$createdAt;
|
696
|
+
delete toCreateObject.$updatedAt;
|
697
|
+
delete toCreateObject.$id;
|
698
|
+
delete toCreateObject.$permissions;
|
699
|
+
|
700
|
+
const result = await transferDocumentWithRetry(
|
701
|
+
db,
|
702
|
+
dbId,
|
703
|
+
collectionId,
|
704
|
+
doc.$id,
|
705
|
+
toCreateObject,
|
706
|
+
doc.$permissions || []
|
707
|
+
);
|
708
|
+
|
709
|
+
return { docId: doc.$id, success: result };
|
710
|
+
});
|
711
|
+
|
712
|
+
const results = await Promise.allSettled(batchPromises);
|
713
|
+
|
714
|
+
results.forEach((result, index) => {
|
715
|
+
if (result.status === 'fulfilled') {
|
716
|
+
if (result.value.success) {
|
717
|
+
successful++;
|
718
|
+
} else {
|
719
|
+
failed++;
|
720
|
+
}
|
721
|
+
} else {
|
722
|
+
console.log(chalk.red(`Batch promise rejected for document ${batch[index].$id}: ${result.reason}`));
|
723
|
+
failed++;
|
724
|
+
}
|
725
|
+
});
|
726
|
+
|
727
|
+
// Add delay between batches to avoid rate limiting
|
728
|
+
if (documentBatches.indexOf(batch) < documentBatches.length - 1) {
|
729
|
+
await delay(500);
|
730
|
+
}
|
731
|
+
}
|
732
|
+
|
733
|
+
return { successful, failed };
|
734
|
+
};
|
735
|
+
|
618
736
|
export const transferDocumentsBetweenDbsLocalToRemote = async (
|
619
737
|
localDb: Databases,
|
620
738
|
endpoint: string,
|
@@ -625,100 +743,70 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (
|
|
625
743
|
fromCollId: string,
|
626
744
|
toCollId: string
|
627
745
|
) => {
|
746
|
+
console.log(chalk.blue(`Starting enhanced document transfer from ${fromCollId} to ${toCollId}...`));
|
747
|
+
|
628
748
|
const client = new Client()
|
629
749
|
.setEndpoint(endpoint)
|
630
750
|
.setProject(projectId)
|
631
751
|
.setKey(apiKey);
|
632
|
-
|
752
|
+
|
633
753
|
const remoteDb = new Databases(client);
|
634
|
-
let
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
754
|
+
let totalDocumentsProcessed = 0;
|
755
|
+
let totalSuccessful = 0;
|
756
|
+
let totalFailed = 0;
|
757
|
+
|
758
|
+
// Fetch documents in batches
|
759
|
+
let hasMoreDocuments = true;
|
760
|
+
let lastDocumentId: string | undefined;
|
761
|
+
|
762
|
+
while (hasMoreDocuments) {
|
763
|
+
const queries = [Query.limit(50)];
|
764
|
+
if (lastDocumentId) {
|
765
|
+
queries.push(Query.cursorAfter(lastDocumentId));
|
766
|
+
}
|
767
|
+
|
768
|
+
const fromCollDocs = await tryAwaitWithRetry(async () =>
|
769
|
+
localDb.listDocuments(fromDbId, fromCollId, queries)
|
770
|
+
);
|
771
|
+
|
772
|
+
if (fromCollDocs.documents.length === 0) {
|
773
|
+
hasMoreDocuments = false;
|
774
|
+
break;
|
775
|
+
}
|
776
|
+
|
777
|
+
console.log(chalk.blue(`Processing ${fromCollDocs.documents.length} documents...`));
|
778
|
+
|
779
|
+
const { successful, failed } = await transferDocumentBatchWithRetry(
|
780
|
+
remoteDb,
|
781
|
+
toDbId,
|
782
|
+
toCollId,
|
783
|
+
fromCollDocs.documents
|
784
|
+
);
|
785
|
+
|
786
|
+
totalDocumentsProcessed += fromCollDocs.documents.length;
|
787
|
+
totalSuccessful += successful;
|
788
|
+
totalFailed += failed;
|
789
|
+
|
790
|
+
// Check if we have more documents to process
|
791
|
+
if (fromCollDocs.documents.length < 50) {
|
792
|
+
hasMoreDocuments = false;
|
793
|
+
} else {
|
794
|
+
lastDocumentId = fromCollDocs.documents[fromCollDocs.documents.length - 1].$id;
|
795
|
+
}
|
796
|
+
|
797
|
+
console.log(chalk.gray(`Batch complete: ${successful} successful, ${failed} failed`));
|
798
|
+
}
|
799
|
+
|
800
|
+
if (totalDocumentsProcessed === 0) {
|
639
801
|
MessageFormatter.info(`No documents found in collection ${fromCollId}`, { prefix: "Transfer" });
|
640
802
|
return;
|
641
|
-
}
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
delete toCreateObject.$collectionId;
|
648
|
-
delete toCreateObject.$createdAt;
|
649
|
-
delete toCreateObject.$updatedAt;
|
650
|
-
delete toCreateObject.$id;
|
651
|
-
delete toCreateObject.$permissions;
|
652
|
-
return tryAwaitWithRetry(async () =>
|
653
|
-
remoteDb.createDocument(
|
654
|
-
toDbId,
|
655
|
-
toCollId,
|
656
|
-
doc.$id,
|
657
|
-
toCreateObject,
|
658
|
-
doc.$permissions
|
659
|
-
)
|
660
|
-
);
|
661
|
-
});
|
662
|
-
await Promise.all(batchedPromises);
|
663
|
-
totalDocumentsTransferred += fromCollDocs.documents.length;
|
803
|
+
}
|
804
|
+
|
805
|
+
const message = `Total documents processed: ${totalDocumentsProcessed}, successful: ${totalSuccessful}, failed: ${totalFailed}`;
|
806
|
+
|
807
|
+
if (totalFailed > 0) {
|
808
|
+
MessageFormatter.warning(message, { prefix: "Transfer" });
|
664
809
|
} else {
|
665
|
-
|
666
|
-
const toCreateObject: Partial<typeof doc> = {
|
667
|
-
...doc,
|
668
|
-
};
|
669
|
-
delete toCreateObject.$databaseId;
|
670
|
-
delete toCreateObject.$collectionId;
|
671
|
-
delete toCreateObject.$createdAt;
|
672
|
-
delete toCreateObject.$updatedAt;
|
673
|
-
delete toCreateObject.$id;
|
674
|
-
delete toCreateObject.$permissions;
|
675
|
-
return tryAwaitWithRetry(async () =>
|
676
|
-
remoteDb.createDocument(
|
677
|
-
toDbId,
|
678
|
-
toCollId,
|
679
|
-
doc.$id,
|
680
|
-
toCreateObject,
|
681
|
-
doc.$permissions
|
682
|
-
)
|
683
|
-
);
|
684
|
-
});
|
685
|
-
await Promise.all(batchedPromises);
|
686
|
-
totalDocumentsTransferred += fromCollDocs.documents.length;
|
687
|
-
while (fromCollDocs.documents.length === 50) {
|
688
|
-
fromCollDocs = await tryAwaitWithRetry(async () =>
|
689
|
-
localDb.listDocuments(fromDbId, fromCollId, [
|
690
|
-
Query.limit(50),
|
691
|
-
Query.cursorAfter(
|
692
|
-
fromCollDocs.documents[fromCollDocs.documents.length - 1].$id
|
693
|
-
),
|
694
|
-
])
|
695
|
-
);
|
696
|
-
const batchedPromises = fromCollDocs.documents.map((doc) => {
|
697
|
-
const toCreateObject: Partial<typeof doc> = {
|
698
|
-
...doc,
|
699
|
-
};
|
700
|
-
delete toCreateObject.$databaseId;
|
701
|
-
delete toCreateObject.$collectionId;
|
702
|
-
delete toCreateObject.$createdAt;
|
703
|
-
delete toCreateObject.$updatedAt;
|
704
|
-
delete toCreateObject.$id;
|
705
|
-
delete toCreateObject.$permissions;
|
706
|
-
return tryAwaitWithRetry(async () =>
|
707
|
-
remoteDb.createDocument(
|
708
|
-
toDbId,
|
709
|
-
toCollId,
|
710
|
-
doc.$id,
|
711
|
-
toCreateObject,
|
712
|
-
doc.$permissions
|
713
|
-
)
|
714
|
-
);
|
715
|
-
});
|
716
|
-
await Promise.all(batchedPromises);
|
717
|
-
totalDocumentsTransferred += fromCollDocs.documents.length;
|
718
|
-
}
|
810
|
+
MessageFormatter.success(message, { prefix: "Transfer" });
|
719
811
|
}
|
720
|
-
MessageFormatter.success(
|
721
|
-
`Total documents transferred from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}: ${totalDocumentsTransferred}`,
|
722
|
-
{ prefix: "Transfer" }
|
723
|
-
);
|
724
812
|
};
|