appwrite-utils-cli 0.9.5 → 0.9.52
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 +2 -0
- package/dist/collections/attributes.js +4 -4
- package/dist/collections/indexes.js +3 -1
- package/dist/collections/methods.js +13 -7
- package/dist/interactiveCLI.js +28 -14
- package/dist/migrations/transfer.js +86 -52
- package/dist/utils/helperFunctions.d.ts +1 -0
- package/dist/utils/helperFunctions.js +1 -0
- package/package.json +53 -53
- package/src/collections/attributes.ts +5 -4
- package/src/collections/indexes.ts +3 -1
- package/src/collections/methods.ts +17 -7
- package/src/interactiveCLI.ts +41 -15
- package/src/migrations/transfer.ts +160 -95
- package/src/utils/helperFunctions.ts +3 -0
- package/zlogs/announcements.json +397 -0
- package/zlogs/announcementscomments.json +36 -0
- package/zlogs/articles.json +138 -0
- package/zlogs/articlescomments.json +4 -0
- package/zlogs/businesscategories.json +7097 -0
- package/zlogs/contacts.json +517063 -0
- package/zlogs/contactscouncils.json +61905 -0
- package/zlogs/contactssociallinks.json +13776 -0
- package/zlogs/councils.json +5076 -0
- package/zlogs/documents.json +917 -0
- package/zlogs/emails.json +4 -0
- package/zlogs/events.json +132625 -0
- package/zlogs/knowledgebase.json +333 -0
- package/zlogs/knowledgebasecomments.json +4 -0
- package/zlogs/linkcategories.json +180 -0
- package/zlogs/links.json +4364 -0
- package/zlogs/memberrequests.json +83 -0
- package/zlogs/memberrequestscomments.json +65 -0
- package/zlogs/mergedUserMap.json +56 -0
- package/zlogs/oldIdToNewIdPerCollectionMap.json +27663 -0
- package/zlogs/regions.json +145 -0
- package/zlogs/testimonials.json +335 -0
- package/zlogs/users.json +25516 -0
package/README.md
CHANGED
@@ -124,6 +124,8 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
124
124
|
|
125
125
|
## Changelog
|
126
126
|
|
127
|
+
- 0.9.52: Add delay after creating indexes, attributes, and others to prevent `fetch failed` errors during large-scale collection creation
|
128
|
+
- 0.9.51: Fix transfer databases, remove "ensure duplicates" check
|
127
129
|
- 0.9.5: Fixed not checking for storage bucket for each database (checking the creation status) when importing data
|
128
130
|
- 0.9.4: Fixed migrations database ensuring it has the required collections
|
129
131
|
- 0.9.3: Fixed deployment error && fix lack of existing `appwriteConfig.ts` file from causing error (you want to be able to setup yeah? haha)
|
@@ -2,7 +2,7 @@ import { Query } from "node-appwrite";
|
|
2
2
|
import { attributeSchema, parseAttribute, } from "appwrite-utils";
|
3
3
|
import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
|
4
4
|
import _ from "lodash";
|
5
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
5
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
6
6
|
const attributesSame = (databaseAttribute, configAttribute) => {
|
7
7
|
return (databaseAttribute.key == configAttribute.key &&
|
8
8
|
databaseAttribute.type == configAttribute.type &&
|
@@ -207,18 +207,18 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
|
|
207
207
|
};
|
208
208
|
export const createUpdateCollectionAttributes = async (db, dbId, collection, attributes) => {
|
209
209
|
console.log(`Creating/Updating attributes for collection: ${collection.name}`);
|
210
|
-
const batchSize = 3;
|
210
|
+
const batchSize = 3;
|
211
211
|
for (let i = 0; i < attributes.length; i += batchSize) {
|
212
|
-
// Slice the attributes array to get a batch of at most batchSize elements
|
213
212
|
const batch = attributes.slice(i, i + batchSize);
|
214
213
|
const attributePromises = batch.map((attribute) => createOrUpdateAttribute(db, dbId, collection, attribute));
|
215
|
-
// Await the completion of all promises in the current batch
|
216
214
|
const results = await Promise.allSettled(attributePromises);
|
217
215
|
results.forEach((result) => {
|
218
216
|
if (result.status === "rejected") {
|
219
217
|
console.error("An attribute promise was rejected:", result.reason);
|
220
218
|
}
|
221
219
|
});
|
220
|
+
// Add delay after each batch
|
221
|
+
await delay(2000);
|
222
222
|
}
|
223
223
|
console.log(`Finished creating/updating attributes for collection: ${collection.name}`);
|
224
224
|
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { indexSchema } from "appwrite-utils";
|
2
2
|
import { Databases, IndexType, Query } from "node-appwrite";
|
3
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
3
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
4
4
|
export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
|
5
5
|
const existingIndex = await db.listIndexes(dbId, collectionId, [
|
6
6
|
Query.equal("key", index.key),
|
@@ -23,5 +23,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
|
|
23
23
|
export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes) => {
|
24
24
|
for (const index of indexes) {
|
25
25
|
await tryAwaitWithRetry(async () => await createOrUpdateIndex(dbId, db, collectionId, index));
|
26
|
+
// Add delay after each index creation/update
|
27
|
+
await delay(1000);
|
26
28
|
}
|
27
29
|
};
|
@@ -4,7 +4,7 @@ import { createUpdateCollectionAttributes } from "./attributes.js";
|
|
4
4
|
import { createOrUpdateIndexes } from "./indexes.js";
|
5
5
|
import _ from "lodash";
|
6
6
|
import { SchemaGenerator } from "../utils/schemaStrings.js";
|
7
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
7
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
8
8
|
export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
|
9
9
|
// Had to do this because kept running into issues with type checking arrays so, sorry 40ms
|
10
10
|
const collection = await db.getCollection(dbId, targetCollectionId);
|
@@ -109,7 +109,7 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
|
|
109
109
|
if (!configCollections) {
|
110
110
|
return;
|
111
111
|
}
|
112
|
-
const usedIds = new Set();
|
112
|
+
const usedIds = new Set();
|
113
113
|
for (const { attributes, indexes, ...collection } of configCollections) {
|
114
114
|
// Prepare permissions for the collection
|
115
115
|
const permissions = [];
|
@@ -149,15 +149,15 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
|
|
149
149
|
let foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") ===
|
150
150
|
collection.name.toLowerCase().trim().replace(" ", ""));
|
151
151
|
if (collection.$id) {
|
152
|
-
collectionId = collection.$id;
|
152
|
+
collectionId = collection.$id;
|
153
153
|
}
|
154
154
|
else if (foundColl && !usedIds.has(foundColl.collectionId)) {
|
155
|
-
collectionId = foundColl.collectionId;
|
155
|
+
collectionId = foundColl.collectionId;
|
156
156
|
}
|
157
157
|
else {
|
158
|
-
collectionId = ID.unique();
|
158
|
+
collectionId = ID.unique();
|
159
159
|
}
|
160
|
-
usedIds.add(collectionId);
|
160
|
+
usedIds.add(collectionId);
|
161
161
|
// Create the collection with the determined ID
|
162
162
|
try {
|
163
163
|
collectionToUse = await tryAwaitWithRetry(async () => await database.createCollection(databaseId, collectionId, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
|
@@ -166,18 +166,24 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
|
|
166
166
|
}
|
167
167
|
catch (error) {
|
168
168
|
console.error(`Failed to create collection ${collection.name} with ID ${collectionId}: ${error}`);
|
169
|
-
continue;
|
169
|
+
continue;
|
170
170
|
}
|
171
171
|
}
|
172
172
|
else {
|
173
173
|
console.log(`Collection ${collection.name} exists, updating it`);
|
174
174
|
await tryAwaitWithRetry(async () => await database.updateCollection(databaseId, collectionToUse.$id, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
|
175
175
|
}
|
176
|
+
// Add delay after creating/updating collection
|
177
|
+
await delay(1000);
|
176
178
|
// Update attributes and indexes for the collection
|
177
179
|
console.log("Creating Attributes");
|
178
180
|
await createUpdateCollectionAttributes(database, databaseId, collectionToUse, attributes);
|
181
|
+
// Add delay after creating attributes
|
182
|
+
await delay(1000);
|
179
183
|
console.log("Creating Indexes");
|
180
184
|
await createOrUpdateIndexes(databaseId, database, collectionToUse.$id, indexes ?? []);
|
185
|
+
// Add delay after creating indexes
|
186
|
+
await delay(1000);
|
181
187
|
}
|
182
188
|
// Process any remaining tasks in the queue
|
183
189
|
await processQueue(database, databaseId);
|
package/dist/interactiveCLI.js
CHANGED
@@ -416,14 +416,14 @@ export class InteractiveCLI {
|
|
416
416
|
default: false,
|
417
417
|
},
|
418
418
|
]);
|
419
|
-
const { checkDuplicates } = await inquirer.prompt([
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
]);
|
419
|
+
// const { checkDuplicates } = await inquirer.prompt([
|
420
|
+
// {
|
421
|
+
// type: "confirm",
|
422
|
+
// name: "checkDuplicates",
|
423
|
+
// message: "Do you want to check for duplicates during import?",
|
424
|
+
// default: true,
|
425
|
+
// },
|
426
|
+
// ]);
|
427
427
|
console.log("Importing data...");
|
428
428
|
await this.controller.importData({
|
429
429
|
databases: selectedDatabases,
|
@@ -431,7 +431,8 @@ export class InteractiveCLI {
|
|
431
431
|
? selectedCollections.map((c) => c.$id)
|
432
432
|
: undefined,
|
433
433
|
shouldWriteFile,
|
434
|
-
checkDuplicates,
|
434
|
+
checkDuplicates: false,
|
435
|
+
// checkDuplicates,
|
435
436
|
});
|
436
437
|
}
|
437
438
|
async transferData() {
|
@@ -476,10 +477,21 @@ export class InteractiveCLI {
|
|
476
477
|
}
|
477
478
|
else {
|
478
479
|
targetClient = sourceClient;
|
479
|
-
|
480
|
+
const allDatabases = await fetchAllDatabases(sourceClient);
|
481
|
+
sourceDatabases = targetDatabases = allDatabases;
|
482
|
+
}
|
483
|
+
const fromDbs = await this.selectDatabases(sourceDatabases, "Select the source database:", false);
|
484
|
+
console.log(fromDbs);
|
485
|
+
const fromDb = fromDbs;
|
486
|
+
if (!fromDb) {
|
487
|
+
throw new Error("No source database selected");
|
488
|
+
}
|
489
|
+
const availableDbs = targetDatabases.filter((db) => db.$id !== fromDb.$id);
|
490
|
+
const targetDbs = await this.selectDatabases(availableDbs, "Select the target database:", false);
|
491
|
+
const targetDb = targetDbs;
|
492
|
+
if (!targetDb) {
|
493
|
+
throw new Error("No target database selected");
|
480
494
|
}
|
481
|
-
const [fromDb] = await this.selectDatabases(sourceDatabases, "Select the source database:", false);
|
482
|
-
const [targetDb] = await this.selectDatabases(targetDatabases.filter((db) => db.$id !== fromDb.$id), "Select the target database:", false);
|
483
495
|
const selectedCollections = await this.selectCollections(fromDb, sourceClient, "Select collections to transfer):");
|
484
496
|
const { transferStorage } = await inquirer.prompt([
|
485
497
|
{
|
@@ -499,8 +511,10 @@ export class InteractiveCLI {
|
|
499
511
|
const targetBuckets = isRemote
|
500
512
|
? await listBuckets(targetStorage)
|
501
513
|
: sourceBuckets;
|
502
|
-
|
503
|
-
|
514
|
+
const sourceBucketPicked = await this.selectBuckets(sourceBuckets.buckets, "Select the source bucket:", false);
|
515
|
+
const targetBucketPicked = await this.selectBuckets(targetBuckets.buckets, "Select the target bucket:", false);
|
516
|
+
sourceBucket = sourceBucketPicked;
|
517
|
+
targetBucket = targetBucketPicked;
|
504
518
|
}
|
505
519
|
let transferOptions = {
|
506
520
|
fromDb,
|
@@ -100,14 +100,26 @@ export const transferStorageLocalToRemote = async (localStorage, endpoint, proje
|
|
100
100
|
* within the same Appwrite Project
|
101
101
|
*/
|
102
102
|
export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDbId, fromCollId, toCollId) => {
|
103
|
-
let fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
|
104
103
|
let totalDocumentsTransferred = 0;
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
104
|
+
let lastDocumentId;
|
105
|
+
let hasMoreDocuments = true;
|
106
|
+
while (hasMoreDocuments) {
|
107
|
+
const queryParams = [Query.limit(50)];
|
108
|
+
if (lastDocumentId) {
|
109
|
+
queryParams.push(Query.cursorAfter(lastDocumentId));
|
110
|
+
}
|
111
|
+
const fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, queryParams));
|
112
|
+
if (fromCollDocs.documents.length === 0) {
|
113
|
+
if (totalDocumentsTransferred === 0) {
|
114
|
+
console.log(`No documents found in collection ${fromCollId}`);
|
115
|
+
}
|
116
|
+
break;
|
117
|
+
}
|
118
|
+
const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await db.listDocuments(toDbId, toCollId, [
|
119
|
+
Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
|
120
|
+
]));
|
121
|
+
const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
|
122
|
+
const batchedPromises = docsToCreate.map((doc) => {
|
111
123
|
const toCreateObject = {
|
112
124
|
...doc,
|
113
125
|
};
|
@@ -120,42 +132,13 @@ export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDb
|
|
120
132
|
return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
|
121
133
|
});
|
122
134
|
await Promise.all(batchedPromises);
|
123
|
-
totalDocumentsTransferred +=
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
delete toCreateObject.$databaseId;
|
131
|
-
delete toCreateObject.$collectionId;
|
132
|
-
delete toCreateObject.$createdAt;
|
133
|
-
delete toCreateObject.$updatedAt;
|
134
|
-
delete toCreateObject.$id;
|
135
|
-
delete toCreateObject.$permissions;
|
136
|
-
return tryAwaitWithRetry(async () => db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
|
137
|
-
});
|
138
|
-
await Promise.all(batchedPromises);
|
139
|
-
totalDocumentsTransferred += fromCollDocs.documents.length;
|
140
|
-
while (fromCollDocs.documents.length === 50) {
|
141
|
-
fromCollDocs = await tryAwaitWithRetry(async () => await db.listDocuments(fromDbId, fromCollId, [
|
142
|
-
Query.limit(50),
|
143
|
-
Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
|
144
|
-
]));
|
145
|
-
const batchedPromises = fromCollDocs.documents.map((doc) => {
|
146
|
-
const toCreateObject = {
|
147
|
-
...doc,
|
148
|
-
};
|
149
|
-
delete toCreateObject.$databaseId;
|
150
|
-
delete toCreateObject.$collectionId;
|
151
|
-
delete toCreateObject.$createdAt;
|
152
|
-
delete toCreateObject.$updatedAt;
|
153
|
-
delete toCreateObject.$id;
|
154
|
-
delete toCreateObject.$permissions;
|
155
|
-
return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
|
156
|
-
});
|
157
|
-
await Promise.all(batchedPromises);
|
158
|
-
totalDocumentsTransferred += fromCollDocs.documents.length;
|
135
|
+
totalDocumentsTransferred += docsToCreate.length;
|
136
|
+
if (fromCollDocs.documents.length < 50) {
|
137
|
+
hasMoreDocuments = false;
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
lastDocumentId =
|
141
|
+
fromCollDocs.documents[fromCollDocs.documents.length - 1].$id;
|
159
142
|
}
|
160
143
|
}
|
161
144
|
console.log(`Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`);
|
@@ -173,7 +156,11 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint
|
|
173
156
|
return;
|
174
157
|
}
|
175
158
|
else if (fromCollDocs.documents.length < 50) {
|
176
|
-
const
|
159
|
+
const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await remoteDb.listDocuments(toDbId, toCollId, [
|
160
|
+
Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
|
161
|
+
]));
|
162
|
+
const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
|
163
|
+
const batchedPromises = docsToCreate.map((doc) => {
|
177
164
|
const toCreateObject = {
|
178
165
|
...doc,
|
179
166
|
};
|
@@ -189,7 +176,11 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint
|
|
189
176
|
totalDocumentsTransferred += fromCollDocs.documents.length;
|
190
177
|
}
|
191
178
|
else {
|
192
|
-
const
|
179
|
+
const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await remoteDb.listDocuments(toDbId, toCollId, [
|
180
|
+
Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
|
181
|
+
]));
|
182
|
+
const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
|
183
|
+
const batchedPromises = docsToCreate.map((doc) => {
|
193
184
|
const toCreateObject = {
|
194
185
|
...doc,
|
195
186
|
};
|
@@ -305,9 +296,7 @@ export const transferDatabaseLocalToRemote = async (localDb, endpoint, projectId
|
|
305
296
|
let lastCollectionId;
|
306
297
|
let fromCollections = await tryAwaitWithRetry(async () => await localDb.listCollections(fromDbId, [Query.limit(50)]));
|
307
298
|
const allFromCollections = fromCollections.collections;
|
308
|
-
if (fromCollections.collections.length
|
309
|
-
}
|
310
|
-
else {
|
299
|
+
if (fromCollections.collections.length >= 50) {
|
311
300
|
lastCollectionId =
|
312
301
|
fromCollections.collections[fromCollections.collections.length - 1].$id;
|
313
302
|
while (lastCollectionId) {
|
@@ -324,13 +313,58 @@ export const transferDatabaseLocalToRemote = async (localDb, endpoint, projectId
|
|
324
313
|
}
|
325
314
|
}
|
326
315
|
for (const collection of allFromCollections) {
|
327
|
-
|
328
|
-
|
316
|
+
let toCollection;
|
317
|
+
const toCollectionExists = await tryAwaitWithRetry(async () => await remoteDb.listCollections(toDbId, [
|
318
|
+
Query.equal("$id", collection.$id),
|
319
|
+
]));
|
320
|
+
if (toCollectionExists.collections.length > 0) {
|
321
|
+
console.log(`Collection ${collection.name} already exists. Updating...`);
|
322
|
+
toCollection = toCollectionExists.collections[0];
|
323
|
+
// Update collection if needed
|
324
|
+
if (toCollection.name !== collection.name ||
|
325
|
+
toCollection.$permissions !== collection.$permissions ||
|
326
|
+
toCollection.documentSecurity !== collection.documentSecurity ||
|
327
|
+
toCollection.enabled !== collection.enabled) {
|
328
|
+
toCollection = await tryAwaitWithRetry(async () => await remoteDb.updateCollection(toDbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled));
|
329
|
+
console.log(`Collection ${toCollection.name} updated`);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
else {
|
333
|
+
toCollection = await tryAwaitWithRetry(async () => await remoteDb.createCollection(toDbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled));
|
334
|
+
console.log(`Collection ${toCollection.name} created`);
|
335
|
+
}
|
336
|
+
// Check and update attributes
|
337
|
+
const existingAttributes = await tryAwaitWithRetry(async () => await remoteDb.listAttributes(toDbId, toCollection.$id));
|
329
338
|
for (const attribute of collection.attributes) {
|
330
|
-
|
339
|
+
const parsedAttribute = parseAttribute(attribute);
|
340
|
+
const existingAttribute = existingAttributes.attributes.find(
|
341
|
+
// @ts-expect-error
|
342
|
+
(attr) => attr.key === parsedAttribute.key);
|
343
|
+
if (!existingAttribute) {
|
344
|
+
await tryAwaitWithRetry(async () => await createOrUpdateAttribute(remoteDb, toDbId, toCollection, parsedAttribute));
|
345
|
+
console.log(`Attribute ${parsedAttribute.key} created`);
|
346
|
+
}
|
347
|
+
else {
|
348
|
+
// Check if attribute needs updating
|
349
|
+
// Note: Appwrite doesn't allow updating most attribute properties
|
350
|
+
// You might need to delete and recreate the attribute if significant changes are needed
|
351
|
+
console.log(`Attribute ${parsedAttribute.key} already exists`);
|
352
|
+
}
|
331
353
|
}
|
354
|
+
// Check and update indexes
|
355
|
+
const existingIndexes = await tryAwaitWithRetry(async () => await remoteDb.listIndexes(toDbId, toCollection.$id));
|
332
356
|
for (const index of collection.indexes) {
|
333
|
-
|
357
|
+
const existingIndex = existingIndexes.indexes.find((idx) => idx.key === index.key);
|
358
|
+
if (!existingIndex) {
|
359
|
+
await tryAwaitWithRetry(async () => await remoteDb.createIndex(toDbId, toCollection.$id, index.key, index.type, index.attributes, index.orders));
|
360
|
+
console.log(`Index ${index.key} created`);
|
361
|
+
}
|
362
|
+
else {
|
363
|
+
// Check if index needs updating
|
364
|
+
// Note: Appwrite doesn't allow updating indexes
|
365
|
+
// You might need to delete and recreate the index if changes are needed
|
366
|
+
console.log(`Index ${index.key} already exists`);
|
367
|
+
}
|
334
368
|
}
|
335
369
|
await transferDocumentsBetweenDbsLocalToRemote(localDb, endpoint, projectId, apiKey, fromDbId, toDbId, collection.$id, toCollection.$id);
|
336
370
|
}
|
@@ -45,3 +45,4 @@ export declare let numTimesFailedTotal: number;
|
|
45
45
|
*/
|
46
46
|
export declare const tryAwaitWithRetry: <T>(createFunction: () => Promise<T>, attemptNum?: number, throwError?: boolean) => Promise<T>;
|
47
47
|
export declare const getAppwriteClient: (endpoint: string, projectId: string, apiKey: string) => Client;
|
48
|
+
export declare const delay: (ms: number) => Promise<unknown>;
|
package/package.json
CHANGED
@@ -1,53 +1,53 @@
|
|
1
|
-
{
|
2
|
-
"name": "appwrite-utils-cli",
|
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": "0.9.
|
5
|
-
"main": "src/main.ts",
|
6
|
-
"type": "module",
|
7
|
-
"repository": {
|
8
|
-
"type": "git",
|
9
|
-
"url": "https://github.com/zachhandley/AppwriteUtils"
|
10
|
-
},
|
11
|
-
"author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
|
12
|
-
"keywords": [
|
13
|
-
"appwrite",
|
14
|
-
"cli",
|
15
|
-
"utils",
|
16
|
-
"migrations",
|
17
|
-
"data",
|
18
|
-
"database",
|
19
|
-
"import",
|
20
|
-
"migration",
|
21
|
-
"utility"
|
22
|
-
],
|
23
|
-
"bin": {
|
24
|
-
"appwrite-migrate": "./dist/main.js"
|
25
|
-
},
|
26
|
-
"scripts": {
|
27
|
-
"build": "bun run tsc",
|
28
|
-
"start": "tsx --no-cache src/main.ts",
|
29
|
-
"deploy": "bun run build && npm publish --access public",
|
30
|
-
"postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
|
31
|
-
},
|
32
|
-
"dependencies": {
|
33
|
-
"@types/inquirer": "^9.0.7",
|
34
|
-
"appwrite-utils": "^0.3.8",
|
35
|
-
"commander": "^12.1.0",
|
36
|
-
"inquirer": "^9.3.6",
|
37
|
-
"js-yaml": "^4.1.0",
|
38
|
-
"lodash": "^4.17.21",
|
39
|
-
"luxon": "^3.5.0",
|
40
|
-
"nanostores": "^0.10.3",
|
41
|
-
"node-appwrite": "^13.0.0",
|
42
|
-
"tsx": "^4.17.0",
|
43
|
-
"ulidx": "^2.4.0",
|
44
|
-
"winston": "^3.14.
|
45
|
-
"zod": "^3.23.8"
|
46
|
-
},
|
47
|
-
"devDependencies": {
|
48
|
-
"@types/js-yaml": "^4.0.9",
|
49
|
-
"@types/lodash": "^4.17.7",
|
50
|
-
"@types/luxon": "^3.4.2",
|
51
|
-
"typescript": "^5.5.4"
|
52
|
-
}
|
53
|
-
}
|
1
|
+
{
|
2
|
+
"name": "appwrite-utils-cli",
|
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": "0.9.52",
|
5
|
+
"main": "src/main.ts",
|
6
|
+
"type": "module",
|
7
|
+
"repository": {
|
8
|
+
"type": "git",
|
9
|
+
"url": "https://github.com/zachhandley/AppwriteUtils"
|
10
|
+
},
|
11
|
+
"author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
|
12
|
+
"keywords": [
|
13
|
+
"appwrite",
|
14
|
+
"cli",
|
15
|
+
"utils",
|
16
|
+
"migrations",
|
17
|
+
"data",
|
18
|
+
"database",
|
19
|
+
"import",
|
20
|
+
"migration",
|
21
|
+
"utility"
|
22
|
+
],
|
23
|
+
"bin": {
|
24
|
+
"appwrite-migrate": "./dist/main.js"
|
25
|
+
},
|
26
|
+
"scripts": {
|
27
|
+
"build": "bun run tsc",
|
28
|
+
"start": "tsx --no-cache src/main.ts",
|
29
|
+
"deploy": "bun run build && npm publish --access public",
|
30
|
+
"postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
|
31
|
+
},
|
32
|
+
"dependencies": {
|
33
|
+
"@types/inquirer": "^9.0.7",
|
34
|
+
"appwrite-utils": "^0.3.8",
|
35
|
+
"commander": "^12.1.0",
|
36
|
+
"inquirer": "^9.3.6",
|
37
|
+
"js-yaml": "^4.1.0",
|
38
|
+
"lodash": "^4.17.21",
|
39
|
+
"luxon": "^3.5.0",
|
40
|
+
"nanostores": "^0.10.3",
|
41
|
+
"node-appwrite": "^13.0.0",
|
42
|
+
"tsx": "^4.17.0",
|
43
|
+
"ulidx": "^2.4.0",
|
44
|
+
"winston": "^3.14.2",
|
45
|
+
"zod": "^3.23.8"
|
46
|
+
},
|
47
|
+
"devDependencies": {
|
48
|
+
"@types/js-yaml": "^4.0.9",
|
49
|
+
"@types/lodash": "^4.17.7",
|
50
|
+
"@types/luxon": "^3.4.2",
|
51
|
+
"typescript": "^5.5.4"
|
52
|
+
}
|
53
|
+
}
|
@@ -6,7 +6,7 @@ import {
|
|
6
6
|
} from "appwrite-utils";
|
7
7
|
import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
|
8
8
|
import _ from "lodash";
|
9
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
9
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
10
10
|
|
11
11
|
const attributesSame = (
|
12
12
|
databaseAttribute: Attribute,
|
@@ -461,21 +461,22 @@ export const createUpdateCollectionAttributes = async (
|
|
461
461
|
`Creating/Updating attributes for collection: ${collection.name}`
|
462
462
|
);
|
463
463
|
|
464
|
-
const batchSize = 3;
|
464
|
+
const batchSize = 3;
|
465
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
466
|
const batch = attributes.slice(i, i + batchSize);
|
468
467
|
const attributePromises = batch.map((attribute) =>
|
469
468
|
createOrUpdateAttribute(db, dbId, collection, attribute)
|
470
469
|
);
|
471
470
|
|
472
|
-
// Await the completion of all promises in the current batch
|
473
471
|
const results = await Promise.allSettled(attributePromises);
|
474
472
|
results.forEach((result) => {
|
475
473
|
if (result.status === "rejected") {
|
476
474
|
console.error("An attribute promise was rejected:", result.reason);
|
477
475
|
}
|
478
476
|
});
|
477
|
+
|
478
|
+
// Add delay after each batch
|
479
|
+
await delay(2000);
|
479
480
|
}
|
480
481
|
console.log(
|
481
482
|
`Finished creating/updating attributes for collection: ${collection.name}`
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { indexSchema, type Index } from "appwrite-utils";
|
2
2
|
import { Databases, IndexType, Query, type Models } from "node-appwrite";
|
3
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
3
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
4
4
|
|
5
5
|
export const createOrUpdateIndex = async (
|
6
6
|
dbId: string,
|
@@ -49,5 +49,7 @@ export const createOrUpdateIndexes = async (
|
|
49
49
|
await tryAwaitWithRetry(
|
50
50
|
async () => await createOrUpdateIndex(dbId, db, collectionId, index)
|
51
51
|
);
|
52
|
+
// Add delay after each index creation/update
|
53
|
+
await delay(1000);
|
52
54
|
}
|
53
55
|
};
|
@@ -12,7 +12,7 @@ import { createUpdateCollectionAttributes } from "./attributes.js";
|
|
12
12
|
import { createOrUpdateIndexes } from "./indexes.js";
|
13
13
|
import _ from "lodash";
|
14
14
|
import { SchemaGenerator } from "../utils/schemaStrings.js";
|
15
|
-
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
15
|
+
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
16
16
|
|
17
17
|
export const documentExists = async (
|
18
18
|
db: Databases,
|
@@ -163,7 +163,7 @@ export const createOrUpdateCollections = async (
|
|
163
163
|
if (!configCollections) {
|
164
164
|
return;
|
165
165
|
}
|
166
|
-
const usedIds = new Set();
|
166
|
+
const usedIds = new Set();
|
167
167
|
|
168
168
|
for (const { attributes, indexes, ...collection } of configCollections) {
|
169
169
|
// Prepare permissions for the collection
|
@@ -215,14 +215,14 @@ export const createOrUpdateCollections = async (
|
|
215
215
|
);
|
216
216
|
|
217
217
|
if (collection.$id) {
|
218
|
-
collectionId = collection.$id;
|
218
|
+
collectionId = collection.$id;
|
219
219
|
} else if (foundColl && !usedIds.has(foundColl.collectionId)) {
|
220
|
-
collectionId = foundColl.collectionId;
|
220
|
+
collectionId = foundColl.collectionId;
|
221
221
|
} else {
|
222
|
-
collectionId = ID.unique();
|
222
|
+
collectionId = ID.unique();
|
223
223
|
}
|
224
224
|
|
225
|
-
usedIds.add(collectionId);
|
225
|
+
usedIds.add(collectionId);
|
226
226
|
|
227
227
|
// Create the collection with the determined ID
|
228
228
|
try {
|
@@ -243,7 +243,7 @@ export const createOrUpdateCollections = async (
|
|
243
243
|
console.error(
|
244
244
|
`Failed to create collection ${collection.name} with ID ${collectionId}: ${error}`
|
245
245
|
);
|
246
|
-
continue;
|
246
|
+
continue;
|
247
247
|
}
|
248
248
|
} else {
|
249
249
|
console.log(`Collection ${collection.name} exists, updating it`);
|
@@ -260,6 +260,9 @@ export const createOrUpdateCollections = async (
|
|
260
260
|
);
|
261
261
|
}
|
262
262
|
|
263
|
+
// Add delay after creating/updating collection
|
264
|
+
await delay(1000);
|
265
|
+
|
263
266
|
// Update attributes and indexes for the collection
|
264
267
|
console.log("Creating Attributes");
|
265
268
|
await createUpdateCollectionAttributes(
|
@@ -268,6 +271,10 @@ export const createOrUpdateCollections = async (
|
|
268
271
|
collectionToUse!,
|
269
272
|
attributes
|
270
273
|
);
|
274
|
+
|
275
|
+
// Add delay after creating attributes
|
276
|
+
await delay(1000);
|
277
|
+
|
271
278
|
console.log("Creating Indexes");
|
272
279
|
await createOrUpdateIndexes(
|
273
280
|
databaseId,
|
@@ -275,6 +282,9 @@ export const createOrUpdateCollections = async (
|
|
275
282
|
collectionToUse!.$id,
|
276
283
|
indexes ?? []
|
277
284
|
);
|
285
|
+
|
286
|
+
// Add delay after creating indexes
|
287
|
+
await delay(1000);
|
278
288
|
}
|
279
289
|
// Process any remaining tasks in the queue
|
280
290
|
await processQueue(database, databaseId);
|