appwrite-utils-cli 0.0.285 → 0.9.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 +122 -96
- package/dist/collections/attributes.d.ts +4 -0
- package/dist/collections/attributes.js +224 -0
- package/dist/collections/indexes.d.ts +4 -0
- package/dist/collections/indexes.js +27 -0
- package/dist/collections/methods.d.ts +16 -0
- package/dist/collections/methods.js +216 -0
- package/dist/databases/methods.d.ts +6 -0
- package/dist/databases/methods.js +33 -0
- package/dist/interactiveCLI.d.ts +19 -0
- package/dist/interactiveCLI.js +555 -0
- package/dist/main.js +227 -62
- package/dist/migrations/afterImportActions.js +37 -40
- package/dist/migrations/appwriteToX.d.ts +26 -25
- package/dist/migrations/appwriteToX.js +42 -6
- package/dist/migrations/attributes.js +21 -20
- package/dist/migrations/backup.d.ts +93 -87
- package/dist/migrations/collections.d.ts +6 -0
- package/dist/migrations/collections.js +149 -20
- package/dist/migrations/converters.d.ts +2 -18
- package/dist/migrations/converters.js +13 -2
- package/dist/migrations/dataLoader.d.ts +276 -161
- package/dist/migrations/dataLoader.js +535 -292
- package/dist/migrations/databases.js +8 -2
- package/dist/migrations/helper.d.ts +3 -0
- package/dist/migrations/helper.js +21 -0
- package/dist/migrations/importController.d.ts +5 -2
- package/dist/migrations/importController.js +125 -88
- package/dist/migrations/importDataActions.d.ts +9 -1
- package/dist/migrations/importDataActions.js +15 -3
- package/dist/migrations/indexes.js +3 -2
- package/dist/migrations/logging.js +20 -8
- package/dist/migrations/migrationHelper.d.ts +9 -4
- package/dist/migrations/migrationHelper.js +6 -5
- package/dist/migrations/openapi.d.ts +1 -1
- package/dist/migrations/openapi.js +33 -18
- package/dist/migrations/queue.js +3 -2
- package/dist/migrations/relationships.d.ts +2 -2
- package/dist/migrations/schemaStrings.js +53 -41
- package/dist/migrations/setupDatabase.d.ts +2 -4
- package/dist/migrations/setupDatabase.js +24 -105
- package/dist/migrations/storage.d.ts +3 -1
- package/dist/migrations/storage.js +110 -16
- package/dist/migrations/transfer.d.ts +30 -0
- package/dist/migrations/transfer.js +337 -0
- package/dist/migrations/users.d.ts +2 -1
- package/dist/migrations/users.js +78 -43
- package/dist/schemas/authUser.d.ts +2 -2
- package/dist/storage/methods.d.ts +15 -0
- package/dist/storage/methods.js +207 -0
- package/dist/storage/schemas.d.ts +687 -0
- package/dist/storage/schemas.js +175 -0
- package/dist/utils/getClientFromConfig.d.ts +4 -0
- package/dist/utils/getClientFromConfig.js +16 -0
- package/dist/utils/helperFunctions.d.ts +11 -1
- package/dist/utils/helperFunctions.js +38 -0
- package/dist/utils/retryFailedPromises.d.ts +2 -0
- package/dist/utils/retryFailedPromises.js +21 -0
- package/dist/utils/schemaStrings.d.ts +13 -0
- package/dist/utils/schemaStrings.js +403 -0
- package/dist/utils/setupFiles.js +110 -61
- package/dist/utilsController.d.ts +40 -22
- package/dist/utilsController.js +164 -84
- package/package.json +13 -15
- package/src/collections/attributes.ts +483 -0
- package/src/collections/indexes.ts +53 -0
- package/src/collections/methods.ts +331 -0
- package/src/databases/methods.ts +47 -0
- package/src/init.ts +64 -64
- package/src/interactiveCLI.ts +767 -0
- package/src/main.ts +292 -83
- package/src/migrations/afterImportActions.ts +553 -490
- package/src/migrations/appwriteToX.ts +237 -174
- package/src/migrations/attributes.ts +483 -422
- package/src/migrations/backup.ts +205 -205
- package/src/migrations/collections.ts +545 -300
- package/src/migrations/converters.ts +161 -150
- package/src/migrations/dataLoader.ts +1615 -1304
- package/src/migrations/databases.ts +44 -25
- package/src/migrations/dbHelpers.ts +92 -92
- package/src/migrations/helper.ts +40 -0
- package/src/migrations/importController.ts +448 -384
- package/src/migrations/importDataActions.ts +315 -307
- package/src/migrations/indexes.ts +40 -37
- package/src/migrations/logging.ts +29 -16
- package/src/migrations/migrationHelper.ts +207 -201
- package/src/migrations/openapi.ts +83 -70
- package/src/migrations/queue.ts +118 -119
- package/src/migrations/relationships.ts +324 -324
- package/src/migrations/schemaStrings.ts +472 -460
- package/src/migrations/setupDatabase.ts +118 -219
- package/src/migrations/storage.ts +538 -358
- package/src/migrations/transfer.ts +608 -0
- package/src/migrations/users.ts +362 -285
- package/src/migrations/validationRules.ts +63 -63
- package/src/schemas/authUser.ts +23 -23
- package/src/setup.ts +8 -8
- package/src/storage/methods.ts +371 -0
- package/src/storage/schemas.ts +205 -0
- package/src/types.ts +9 -9
- package/src/utils/getClientFromConfig.ts +17 -0
- package/src/utils/helperFunctions.ts +181 -127
- package/src/utils/index.ts +2 -2
- package/src/utils/loadConfigs.ts +59 -59
- package/src/utils/retryFailedPromises.ts +27 -0
- package/src/utils/schemaStrings.ts +473 -0
- package/src/utils/setupFiles.ts +228 -182
- package/src/utilsController.ts +325 -194
- package/tsconfig.json +37 -37
@@ -1,7 +1,13 @@
|
|
1
|
-
import { Databases, Query } from "node-appwrite";
|
1
|
+
import { Client, Databases, IndexType, Query, } from "node-appwrite";
|
2
|
+
import { getAppwriteClient, tryAwaitWithRetry, } from "../utils/helperFunctions.js";
|
3
|
+
import { transferDocumentsBetweenDbsLocalToLocal, transferDocumentsBetweenDbsLocalToRemote, } from "./collections.js";
|
4
|
+
import { createOrUpdateAttribute } from "./attributes.js";
|
5
|
+
import { parseAttribute } from "appwrite-utils";
|
2
6
|
export const fetchAllDatabases = async (database) => {
|
3
|
-
const databases = await database.list([Query.limit(25)]);
|
7
|
+
const databases = await tryAwaitWithRetry(async () => await database.list([Query.limit(25)]));
|
4
8
|
const allDatabases = databases.databases;
|
9
|
+
if (allDatabases.length === 0)
|
10
|
+
return [];
|
5
11
|
let lastDatabaseId = allDatabases[allDatabases.length - 1].$id;
|
6
12
|
if (databases.databases.length < 25) {
|
7
13
|
return allDatabases;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { tryAwaitWithRetry } from "appwrite-utils";
|
2
|
+
import { ulid } from "ulidx";
|
3
|
+
export const logOperation = async (db, dbId, operationDetails, operationId) => {
|
4
|
+
try {
|
5
|
+
let operation;
|
6
|
+
if (operationId) {
|
7
|
+
// Update existing operation log
|
8
|
+
operation = await tryAwaitWithRetry(async () => await db.updateDocument("migrations", "currentOperations", operationId, operationDetails));
|
9
|
+
}
|
10
|
+
else {
|
11
|
+
// Create new operation log
|
12
|
+
operation = await db.createDocument("migrations", "currentOperations", ulid(), operationDetails);
|
13
|
+
}
|
14
|
+
console.log(`Operation logged: ${operation.$id}`);
|
15
|
+
return operation;
|
16
|
+
}
|
17
|
+
catch (error) {
|
18
|
+
console.error(`Error logging operation: ${error}`);
|
19
|
+
throw error;
|
20
|
+
}
|
21
|
+
};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { type Databases, type Storage } from "node-appwrite";
|
1
|
+
import { type Databases, type Models, type Storage } from "node-appwrite";
|
2
2
|
import type { AppwriteConfig, ConfigDatabase } from "appwrite-utils";
|
3
3
|
import type { ImportDataActions } from "./importDataActions.js";
|
4
4
|
import type { SetupOptions } from "../utilsController.js";
|
@@ -12,9 +12,12 @@ export declare class ImportController {
|
|
12
12
|
private setupOptions;
|
13
13
|
private documentCache;
|
14
14
|
private batchLimit;
|
15
|
+
private hasImportedUsers;
|
15
16
|
private postImportActionsQueue;
|
16
|
-
|
17
|
+
private databasesToRun;
|
18
|
+
constructor(config: AppwriteConfig, database: Databases, storage: Storage, appwriteFolderPath: string, importDataActions: ImportDataActions, setupOptions: SetupOptions, databasesToRun?: Models.Database[]);
|
17
19
|
run(): Promise<void>;
|
20
|
+
updateOthersToFinalData(updatedDb: Models.Database, targetDb: Models.Database): Promise<void>;
|
18
21
|
importCollections(db: ConfigDatabase, dataLoader: DataLoader): Promise<void>;
|
19
22
|
executePostImportActions(dbId: string, dataLoader: DataLoader): Promise<void>;
|
20
23
|
}
|
@@ -1,12 +1,13 @@
|
|
1
|
-
import { ID, Query, } from "node-appwrite";
|
1
|
+
import { AppwriteException, ID, Query, } from "node-appwrite";
|
2
2
|
import _ from "lodash";
|
3
|
-
import { areCollectionNamesSame } from "../utils/index.js";
|
3
|
+
import { areCollectionNamesSame, tryAwaitWithRetry } from "../utils/index.js";
|
4
4
|
import { resolveAndUpdateRelationships } from "./relationships.js";
|
5
5
|
import { UsersController } from "./users.js";
|
6
6
|
import { logger } from "./logging.js";
|
7
7
|
import { updateOperation } from "./migrationHelper.js";
|
8
8
|
import { BatchSchema, OperationCreateSchema, OperationSchema, } from "./backup.js";
|
9
9
|
import { DataLoader } from "./dataLoader.js";
|
10
|
+
import { transferDatabaseLocalToLocal, transferStorageLocalToLocal, } from "./transfer.js";
|
10
11
|
export class ImportController {
|
11
12
|
config;
|
12
13
|
database;
|
@@ -16,8 +17,10 @@ export class ImportController {
|
|
16
17
|
setupOptions;
|
17
18
|
documentCache;
|
18
19
|
batchLimit = 25; // Define batch size limit
|
20
|
+
hasImportedUsers = false;
|
19
21
|
postImportActionsQueue = [];
|
20
|
-
|
22
|
+
databasesToRun;
|
23
|
+
constructor(config, database, storage, appwriteFolderPath, importDataActions, setupOptions, databasesToRun) {
|
21
24
|
this.config = config;
|
22
25
|
this.database = database;
|
23
26
|
this.storage = storage;
|
@@ -25,43 +28,54 @@ export class ImportController {
|
|
25
28
|
this.importDataActions = importDataActions;
|
26
29
|
this.setupOptions = setupOptions;
|
27
30
|
this.documentCache = new Map();
|
31
|
+
this.databasesToRun = databasesToRun || [];
|
28
32
|
}
|
29
33
|
async run() {
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
let databasesToProcess;
|
35
|
+
if (this.databasesToRun.length > 0) {
|
36
|
+
// Use the provided databases
|
37
|
+
databasesToProcess = this.databasesToRun;
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
// If no databases are specified, fetch all databases
|
41
|
+
const allDatabases = await this.database.list();
|
42
|
+
databasesToProcess = allDatabases.databases;
|
43
|
+
}
|
44
|
+
let dataLoader;
|
45
|
+
let databaseRan;
|
46
|
+
for (let db of databasesToProcess) {
|
47
|
+
if (db.name.toLowerCase().trim().replace(" ", "") === "migrations") {
|
41
48
|
continue;
|
42
49
|
}
|
43
|
-
if (!db.$id) {
|
44
|
-
const databases = await this.database.list([
|
45
|
-
Query.equal("name", db.name),
|
46
|
-
]);
|
47
|
-
if (databases.databases.length > 0) {
|
48
|
-
db.$id = databases.databases[0].$id;
|
49
|
-
}
|
50
|
-
}
|
51
50
|
console.log(`---------------------------------`);
|
52
51
|
console.log(`Starting import data for database: ${db.name}`);
|
53
52
|
console.log(`---------------------------------`);
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
if (!databaseRan) {
|
54
|
+
databaseRan = db;
|
55
|
+
dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config, this.setupOptions.shouldWriteFile);
|
56
|
+
await dataLoader.start(db.$id);
|
57
|
+
await this.importCollections(db, dataLoader);
|
58
|
+
await resolveAndUpdateRelationships(db.$id, this.database, this.config);
|
59
|
+
await this.executePostImportActions(db.$id, dataLoader);
|
60
|
+
}
|
61
|
+
else if (databaseRan.$id !== db.$id) {
|
62
|
+
await this.updateOthersToFinalData(databaseRan, db);
|
63
|
+
}
|
60
64
|
console.log(`---------------------------------`);
|
61
65
|
console.log(`Finished import data for database: ${db.name}`);
|
62
66
|
console.log(`---------------------------------`);
|
63
67
|
}
|
64
68
|
}
|
69
|
+
async updateOthersToFinalData(updatedDb, targetDb) {
|
70
|
+
await transferDatabaseLocalToLocal(this.database, updatedDb.$id, targetDb.$id);
|
71
|
+
// Find the corresponding database configs
|
72
|
+
const updatedDbConfig = this.config.databases.find((db) => db.$id === updatedDb.$id);
|
73
|
+
const targetDbConfig = this.config.databases.find((db) => db.$id === targetDb.$id);
|
74
|
+
// Transfer database-specific bucket if both databases have a bucket defined
|
75
|
+
if (updatedDbConfig?.bucket && targetDbConfig?.bucket) {
|
76
|
+
await transferStorageLocalToLocal(this.storage, updatedDbConfig.bucket.$id, targetDbConfig.bucket.$id);
|
77
|
+
}
|
78
|
+
}
|
65
79
|
async importCollections(db, dataLoader) {
|
66
80
|
if (!this.config.collections) {
|
67
81
|
return;
|
@@ -71,7 +85,7 @@ export class ImportController {
|
|
71
85
|
dataLoader.getCollectionKey(collection.name);
|
72
86
|
const importOperationId = dataLoader.collectionImportOperations.get(dataLoader.getCollectionKey(collection.name));
|
73
87
|
const createBatches = (finalData) => {
|
74
|
-
let maxBatchLength =
|
88
|
+
let maxBatchLength = 100;
|
75
89
|
const finalBatches = [];
|
76
90
|
for (let i = 0; i < finalData.length; i++) {
|
77
91
|
if (i % maxBatchLength === 0) {
|
@@ -81,49 +95,50 @@ export class ImportController {
|
|
81
95
|
}
|
82
96
|
return finalBatches;
|
83
97
|
};
|
84
|
-
if (isUsersCollection) {
|
98
|
+
if (isUsersCollection && !this.hasImportedUsers) {
|
85
99
|
const usersDataMap = dataLoader.importMap.get(dataLoader.getCollectionKey("users"));
|
86
100
|
const usersData = usersDataMap?.data;
|
87
101
|
const usersController = new UsersController(this.config, this.database);
|
88
102
|
if (usersData) {
|
89
|
-
console.log("Found users data");
|
90
|
-
const
|
91
|
-
|
92
|
-
|
93
|
-
const
|
94
|
-
|
95
|
-
|
96
|
-
.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
if (!(dataLoader.userExistsMap.get(userId) === true)) {
|
102
|
-
const userId = userBatch.finalData.userId ||
|
103
|
-
userBatch.context.userId ||
|
104
|
-
userBatch.context.docId;
|
105
|
-
if (!userBatch.finalData.userId) {
|
106
|
-
userBatch.finalData.userId = userId;
|
107
|
-
}
|
108
|
-
return usersController
|
109
|
-
.createUserAndReturn(userBatch.finalData)
|
110
|
-
.then(() => console.log("Created user"))
|
111
|
-
.catch((error) => {
|
112
|
-
logger.error("Error creating user:", error, "\nUser data is ", userBatch.finalData);
|
113
|
-
});
|
114
|
-
}
|
115
|
-
else {
|
116
|
-
console.log("Skipped existing user: ", userId);
|
117
|
-
return Promise.resolve();
|
118
|
-
}
|
119
|
-
}
|
103
|
+
console.log("Found users data", usersData.length);
|
104
|
+
const userDataBatches = createBatches(usersData);
|
105
|
+
for (const batch of userDataBatches) {
|
106
|
+
console.log("Importing users batch", batch.length);
|
107
|
+
const userBatchPromises = batch
|
108
|
+
.filter((item) => {
|
109
|
+
let itemId;
|
110
|
+
if (item.finalData.userId) {
|
111
|
+
itemId = item.finalData.userId;
|
112
|
+
}
|
113
|
+
else if (item.finalData.docId) {
|
114
|
+
itemId = item.finalData.docId;
|
120
115
|
}
|
116
|
+
if (!itemId) {
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
return (item &&
|
120
|
+
item.finalData &&
|
121
|
+
!dataLoader.userExistsMap.has(itemId));
|
121
122
|
})
|
122
|
-
.
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
.map((item) => {
|
124
|
+
dataLoader.userExistsMap.set(item.finalData.userId ||
|
125
|
+
item.finalData.docId ||
|
126
|
+
item.context.userId ||
|
127
|
+
item.context.docId, true);
|
128
|
+
return usersController.createUserAndReturn(item.finalData);
|
129
|
+
});
|
130
|
+
const promiseResults = await Promise.allSettled(userBatchPromises);
|
131
|
+
for (const item of batch) {
|
132
|
+
if (item && item.finalData) {
|
133
|
+
dataLoader.userExistsMap.set(item.finalData.userId ||
|
134
|
+
item.finalData.docId ||
|
135
|
+
item.context.userId ||
|
136
|
+
item.context.docId, true);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
console.log("Finished importing users batch");
|
126
140
|
}
|
141
|
+
this.hasImportedUsers = true;
|
127
142
|
console.log("Finished importing users");
|
128
143
|
}
|
129
144
|
}
|
@@ -136,6 +151,7 @@ export class ImportController {
|
|
136
151
|
status: "in_progress",
|
137
152
|
});
|
138
153
|
const collectionData = dataLoader.importMap.get(dataLoader.getCollectionKey(collection.name));
|
154
|
+
console.log(`Processing collection: ${collection.name}...`);
|
139
155
|
if (!collectionData) {
|
140
156
|
console.log("No collection data for ", collection.name);
|
141
157
|
continue;
|
@@ -145,34 +161,55 @@ export class ImportController {
|
|
145
161
|
for (let i = 0; i < dataSplit.length; i++) {
|
146
162
|
const batches = dataSplit[i];
|
147
163
|
console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
|
148
|
-
const
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
164
|
+
// const documentExistsPromises = batches.map(async (item) => {
|
165
|
+
// try {
|
166
|
+
// const id =
|
167
|
+
// item.finalData.docId ||
|
168
|
+
// item.finalData.userId ||
|
169
|
+
// item.context.docId ||
|
170
|
+
// item.context.userId;
|
171
|
+
// if (!item.finalData) {
|
172
|
+
// return Promise.resolve(null);
|
173
|
+
// }
|
174
|
+
// return tryAwaitWithRetry(
|
175
|
+
// async () =>
|
176
|
+
// await documentExists(
|
177
|
+
// this.database,
|
178
|
+
// db.$id,
|
179
|
+
// collection.$id,
|
180
|
+
// item.finalData
|
181
|
+
// )
|
182
|
+
// );
|
183
|
+
// } catch (error) {
|
184
|
+
// console.error(error);
|
185
|
+
// return Promise.resolve(null);
|
186
|
+
// }
|
187
|
+
// });
|
188
|
+
// const documentExistsResults = await Promise.all(documentExistsPromises);
|
189
|
+
const batchPromises = batches.map((item, index) => {
|
190
|
+
try {
|
191
|
+
const id = item.finalData.docId ||
|
192
|
+
item.finalData.userId ||
|
193
|
+
item.context.docId ||
|
194
|
+
item.context.userId;
|
195
|
+
if (item.finalData.hasOwnProperty("userId")) {
|
196
|
+
delete item.finalData.userId;
|
197
|
+
}
|
198
|
+
if (item.finalData.hasOwnProperty("docId")) {
|
199
|
+
delete item.finalData.docId;
|
200
|
+
}
|
201
|
+
if (!item.finalData) {
|
202
|
+
return Promise.resolve();
|
203
|
+
}
|
204
|
+
return tryAwaitWithRetry(async () => await this.database.createDocument(db.$id, collection.$id, id, item.finalData));
|
158
205
|
}
|
159
|
-
|
206
|
+
catch (error) {
|
207
|
+
console.error(error);
|
160
208
|
return Promise.resolve();
|
161
209
|
}
|
162
|
-
return this.database
|
163
|
-
.createDocument(db.$id, collection.$id, id, item.finalData)
|
164
|
-
.then(() => {
|
165
|
-
processedItems++;
|
166
|
-
console.log("Created item");
|
167
|
-
})
|
168
|
-
.catch((error) => {
|
169
|
-
console.error(`Error creating item in ${collection.name}:`, error, "\nItem data is ", item.finalData);
|
170
|
-
throw error;
|
171
|
-
// Optionally, log the failed item for retry or review
|
172
|
-
});
|
173
210
|
});
|
174
211
|
// Wait for all promises in the current batch to resolve
|
175
|
-
await Promise.
|
212
|
+
await Promise.all(batchPromises);
|
176
213
|
console.log(`Completed batch ${i + 1} of ${dataSplit.length}`);
|
177
214
|
await updateOperation(this.database, importOperation.$id, {
|
178
215
|
progress: processedItems,
|
@@ -11,16 +11,24 @@ export declare class ImportDataActions {
|
|
11
11
|
private validityRuleDefinitions;
|
12
12
|
private afterImportActionsDefinitions;
|
13
13
|
constructor(db: Databases, storage: Storage, config: AppwriteConfig, converterDefinitions: ConverterFunctions, validityRuleDefinitions: ValidationRules, afterImportActionsDefinitions: AfterImportActions);
|
14
|
+
/**
|
15
|
+
* Runs converter functions on the item based on the provided attribute mappings.
|
16
|
+
*
|
17
|
+
* @param item - The item to be transformed.
|
18
|
+
* @param attributeMappings - The mappings that define how each attribute should be transformed.
|
19
|
+
* @returns The transformed item.
|
20
|
+
*/
|
14
21
|
runConverterFunctions(item: any, attributeMappings: AttributeMappings): any;
|
15
22
|
/**
|
16
23
|
* Validates a single data item based on defined validation rules.
|
17
24
|
* @param item The data item to validate.
|
25
|
+
* @param attributeMap The attribute mappings for the data item.
|
18
26
|
* @param context The context for resolving templated parameters in validation rules.
|
19
27
|
* @returns A promise that resolves to true if the item is valid, false otherwise.
|
20
28
|
*/
|
21
29
|
validateItem(item: any, attributeMap: AttributeMappings, context: {
|
22
30
|
[key: string]: any;
|
23
|
-
}):
|
31
|
+
}): boolean;
|
24
32
|
executeAfterImportActions(item: any, attributeMap: AttributeMappings, context: {
|
25
33
|
[key: string]: any;
|
26
34
|
}): Promise<void>;
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import {
|
1
|
+
import {} from "node-appwrite";
|
2
2
|
import { validationRules, } from "appwrite-utils";
|
3
3
|
import { converterFunctions } from "appwrite-utils";
|
4
4
|
import { convertObjectBySchema } from "./converters.js";
|
5
5
|
import {} from "appwrite-utils";
|
6
6
|
import { afterImportActions } from "./afterImportActions.js";
|
7
7
|
import { logger } from "./logging.js";
|
8
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
8
9
|
export class ImportDataActions {
|
9
10
|
db;
|
10
11
|
storage;
|
@@ -20,9 +21,19 @@ export class ImportDataActions {
|
|
20
21
|
this.validityRuleDefinitions = validityRuleDefinitions;
|
21
22
|
this.afterImportActionsDefinitions = afterImportActionsDefinitions;
|
22
23
|
}
|
24
|
+
/**
|
25
|
+
* Runs converter functions on the item based on the provided attribute mappings.
|
26
|
+
*
|
27
|
+
* @param item - The item to be transformed.
|
28
|
+
* @param attributeMappings - The mappings that define how each attribute should be transformed.
|
29
|
+
* @returns The transformed item.
|
30
|
+
*/
|
23
31
|
runConverterFunctions(item, attributeMappings) {
|
24
32
|
const conversionSchema = attributeMappings.reduce((schema, mapping) => {
|
25
33
|
schema[mapping.targetKey] = (originalValue) => {
|
34
|
+
if (!mapping.converters) {
|
35
|
+
return originalValue;
|
36
|
+
}
|
26
37
|
return mapping.converters?.reduce((value, converterName) => {
|
27
38
|
let shouldProcessAsArray = false;
|
28
39
|
if ((converterName.includes("[Arr]") ||
|
@@ -65,10 +76,11 @@ export class ImportDataActions {
|
|
65
76
|
/**
|
66
77
|
* Validates a single data item based on defined validation rules.
|
67
78
|
* @param item The data item to validate.
|
79
|
+
* @param attributeMap The attribute mappings for the data item.
|
68
80
|
* @param context The context for resolving templated parameters in validation rules.
|
69
81
|
* @returns A promise that resolves to true if the item is valid, false otherwise.
|
70
82
|
*/
|
71
|
-
|
83
|
+
validateItem(item, attributeMap, context) {
|
72
84
|
for (const mapping of attributeMap) {
|
73
85
|
const { validationActions } = mapping;
|
74
86
|
if (!validationActions ||
|
@@ -111,7 +123,7 @@ export class ImportDataActions {
|
|
111
123
|
const { action, params } = actionDef;
|
112
124
|
console.log(`Executing post-import action '${action}' for attribute '${mapping.targetKey}' with params ${params.join(", ")}...`);
|
113
125
|
try {
|
114
|
-
await this.executeAction(action, params, context, item);
|
126
|
+
await tryAwaitWithRetry(async () => await this.executeAction(action, params, context, item));
|
115
127
|
}
|
116
128
|
catch (error) {
|
117
129
|
logger.error(`Failed to execute post-import action '${action}' for attribute '${mapping.targetKey}':`, error);
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { indexSchema } from "appwrite-utils";
|
2
|
-
import { Databases, Query } from "node-appwrite";
|
2
|
+
import { Databases, IndexType, Query } from "node-appwrite";
|
3
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
3
4
|
// import {}
|
4
5
|
export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
|
5
6
|
const existingIndex = await db.listIndexes(dbId, collectionId, [
|
@@ -13,6 +14,6 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
|
|
13
14
|
};
|
14
15
|
export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes) => {
|
15
16
|
for (const index of indexes) {
|
16
|
-
await createOrUpdateIndex(dbId, db, collectionId, index);
|
17
|
+
await tryAwaitWithRetry(async () => await createOrUpdateIndex(dbId, db, collectionId, index));
|
17
18
|
}
|
18
19
|
};
|
@@ -1,15 +1,27 @@
|
|
1
1
|
import winston from "winston";
|
2
|
+
import fs from "fs";
|
3
|
+
import path from "path";
|
4
|
+
// Ensure the logs directory exists
|
5
|
+
const logDir = path.join(process.cwd(), "zlogs");
|
6
|
+
if (!fs.existsSync(logDir)) {
|
7
|
+
fs.mkdirSync(logDir);
|
8
|
+
}
|
2
9
|
export const logger = winston.createLogger({
|
3
|
-
level: "
|
10
|
+
level: "debug",
|
4
11
|
format: winston.format.json({ space: 2 }),
|
5
12
|
defaultMeta: { service: "appwrite-utils-cli" },
|
6
13
|
transports: [
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
new winston.transports.File({
|
12
|
-
|
13
|
-
|
14
|
+
new winston.transports.File({
|
15
|
+
filename: path.join(logDir, "error.log"),
|
16
|
+
level: "error",
|
17
|
+
}),
|
18
|
+
new winston.transports.File({
|
19
|
+
filename: path.join(logDir, "warn.log"),
|
20
|
+
level: "warn",
|
21
|
+
}),
|
22
|
+
new winston.transports.File({
|
23
|
+
filename: path.join(logDir, "info.log"),
|
24
|
+
level: "info",
|
25
|
+
}),
|
14
26
|
],
|
15
27
|
});
|
@@ -19,6 +19,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
19
19
|
oldKey: z.ZodOptional<z.ZodString>;
|
20
20
|
oldKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
21
21
|
targetKey: z.ZodString;
|
22
|
+
valueToSet: z.ZodOptional<z.ZodAny>;
|
22
23
|
fileData: z.ZodOptional<z.ZodObject<{
|
23
24
|
name: z.ZodString;
|
24
25
|
path: z.ZodString;
|
@@ -54,6 +55,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
54
55
|
targetKey: string;
|
55
56
|
oldKey?: string | undefined;
|
56
57
|
oldKeys?: string[] | undefined;
|
58
|
+
valueToSet?: any;
|
57
59
|
fileData?: {
|
58
60
|
path: string;
|
59
61
|
name: string;
|
@@ -71,6 +73,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
71
73
|
targetKey: string;
|
72
74
|
oldKey?: string | undefined;
|
73
75
|
oldKeys?: string[] | undefined;
|
76
|
+
valueToSet?: any;
|
74
77
|
fileData?: {
|
75
78
|
path: string;
|
76
79
|
name: string;
|
@@ -93,6 +96,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
93
96
|
targetKey: string;
|
94
97
|
oldKey?: string | undefined;
|
95
98
|
oldKeys?: string[] | undefined;
|
99
|
+
valueToSet?: any;
|
96
100
|
fileData?: {
|
97
101
|
path: string;
|
98
102
|
name: string;
|
@@ -107,8 +111,8 @@ export declare const ContextObject: z.ZodObject<{
|
|
107
111
|
action: string;
|
108
112
|
}[] | undefined;
|
109
113
|
}[];
|
110
|
-
finalItem?: any;
|
111
114
|
context?: any;
|
115
|
+
finalItem?: any;
|
112
116
|
}, {
|
113
117
|
collectionId: string;
|
114
118
|
dbId: string;
|
@@ -116,6 +120,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
116
120
|
targetKey: string;
|
117
121
|
oldKey?: string | undefined;
|
118
122
|
oldKeys?: string[] | undefined;
|
123
|
+
valueToSet?: any;
|
119
124
|
fileData?: {
|
120
125
|
path: string;
|
121
126
|
name: string;
|
@@ -130,18 +135,18 @@ export declare const ContextObject: z.ZodObject<{
|
|
130
135
|
action: string;
|
131
136
|
}[] | undefined;
|
132
137
|
}[];
|
133
|
-
finalItem?: any;
|
134
138
|
context?: any;
|
139
|
+
finalItem?: any;
|
135
140
|
}>;
|
136
141
|
export type ContextObject = z.infer<typeof ContextObject>;
|
137
142
|
export declare const createOrFindAfterImportOperation: (database: Databases, collectionId: string, context: ContextObject) => Promise<void>;
|
138
143
|
export declare const addBatch: (database: Databases, data: string) => Promise<string>;
|
139
144
|
export declare const getAfterImportOperations: (database: Databases, collectionId: string) => Promise<{
|
140
|
-
error: string;
|
141
145
|
$id: string;
|
142
146
|
$createdAt: string;
|
143
147
|
$updatedAt: string;
|
144
148
|
status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
|
149
|
+
error: string;
|
145
150
|
collectionId: string;
|
146
151
|
operationType: string;
|
147
152
|
progress: number;
|
@@ -150,11 +155,11 @@ export declare const getAfterImportOperations: (database: Databases, collectionI
|
|
150
155
|
batches?: string[] | undefined;
|
151
156
|
}[]>;
|
152
157
|
export declare const findOrCreateOperation: (database: Databases, collectionId: string, operationType: string, additionalQueries?: string[]) => Promise<{
|
153
|
-
error: string;
|
154
158
|
$id: string;
|
155
159
|
$createdAt: string;
|
156
160
|
$updatedAt: string;
|
157
161
|
status: "error" | "pending" | "ready" | "in_progress" | "completed" | "cancelled";
|
162
|
+
error: string;
|
158
163
|
collectionId: string;
|
159
164
|
operationType: string;
|
160
165
|
progress: number;
|
@@ -3,6 +3,7 @@ import { BatchSchema, OperationSchema } from "./backup.js";
|
|
3
3
|
import { AttributeMappingsSchema } from "appwrite-utils";
|
4
4
|
import { z } from "zod";
|
5
5
|
import { logger } from "./logging.js";
|
6
|
+
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
6
7
|
/**
|
7
8
|
* Object that contains the context for an action that needs to be executed after import
|
8
9
|
* Used in the afterImportActionsDefinitions
|
@@ -66,18 +67,18 @@ export const getAfterImportOperations = async (database, collectionId) => {
|
|
66
67
|
return allOps;
|
67
68
|
};
|
68
69
|
export const findOrCreateOperation = async (database, collectionId, operationType, additionalQueries) => {
|
69
|
-
const operations = await database.listDocuments("migrations", "currentOperations", [
|
70
|
+
const operations = await tryAwaitWithRetry(async () => await database.listDocuments("migrations", "currentOperations", [
|
70
71
|
Query.equal("collectionId", collectionId),
|
71
72
|
Query.equal("operationType", operationType),
|
72
73
|
Query.equal("status", "pending"),
|
73
74
|
...(additionalQueries || []),
|
74
|
-
]);
|
75
|
+
]));
|
75
76
|
if (operations.documents.length > 0) {
|
76
77
|
return OperationSchema.parse(operations.documents[0]); // Assuming the first document is the operation we want
|
77
78
|
}
|
78
79
|
else {
|
79
80
|
// Create a new operation document
|
80
|
-
const op = await database.createDocument("migrations", "currentOperations", ID.unique(), {
|
81
|
+
const op = await tryAwaitWithRetry(async () => await database.createDocument("migrations", "currentOperations", ID.unique(), {
|
81
82
|
operationType,
|
82
83
|
collectionId,
|
83
84
|
status: "pending",
|
@@ -85,12 +86,12 @@ export const findOrCreateOperation = async (database, collectionId, operationTyp
|
|
85
86
|
progress: 0,
|
86
87
|
total: 0,
|
87
88
|
error: "",
|
88
|
-
});
|
89
|
+
}));
|
89
90
|
return OperationSchema.parse(op);
|
90
91
|
}
|
91
92
|
};
|
92
93
|
export const updateOperation = async (database, operationId, updateFields) => {
|
93
|
-
await database.updateDocument("migrations", "currentOperations", operationId, updateFields);
|
94
|
+
await tryAwaitWithRetry(async () => await database.updateDocument("migrations", "currentOperations", operationId, updateFields));
|
94
95
|
};
|
95
96
|
// Actual max 1073741824
|
96
97
|
export const maxDataLength = 1073741820;
|
@@ -1,4 +1,4 @@
|
|
1
1
|
import { type AppwriteConfig } from "appwrite-utils";
|
2
2
|
import { z } from "zod";
|
3
3
|
export declare const generateOpenApi: (config: AppwriteConfig) => Promise<void>;
|
4
|
-
export declare function transformTypeToOpenApi<T extends z.ZodTypeAny>(schema: T):
|
4
|
+
export declare function transformTypeToOpenApi<T extends z.ZodTypeAny>(schema: T, description?: string | Record<string, any> | null | undefined): T;
|