appwrite-utils-cli 0.9.981 → 0.9.983
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 +1 -0
- package/dist/migrations/dataLoader.d.ts +2 -42
- package/dist/migrations/dataLoader.js +21 -12
- package/dist/migrations/importController.js +8 -2
- package/dist/migrations/migrationHelper.d.ts +2 -2
- package/dist/migrations/users.d.ts +2 -2
- package/dist/migrations/users.js +13 -23
- package/dist/utils/helperFunctions.js +1 -1
- package/package.json +1 -1
- package/src/migrations/dataLoader.ts +30 -19
- package/src/migrations/importController.ts +7 -3
- package/src/migrations/users.ts +22 -32
- package/src/utils/helperFunctions.ts +1 -1
package/README.md
CHANGED
@@ -125,6 +125,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
125
125
|
|
126
126
|
## Changelog
|
127
127
|
|
128
|
+
- 0.9.983: Fixed `afterImportActions` not resolving
|
128
129
|
- 0.9.981: Try fixing `tryAwaitWithRetry` to catch `522` errors from Cloudflare, they were appearing for some users, also added a 1000ms delay to `tryAwaitWithRetry`
|
129
130
|
- 0.9.98: Fixing some import errors reported by users
|
130
131
|
- 0.9.95: Updated to include new version of `appwrite-utils`
|
@@ -708,27 +708,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
708
708
|
attributes?: ({
|
709
709
|
key: string;
|
710
710
|
type?: "string" | undefined;
|
711
|
-
format
|
712
|
-
/**
|
713
|
-
* Generates attribute mappings with post-import actions based on the provided attribute mappings.
|
714
|
-
* This method checks each mapping for a fileData attribute and adds a post-import action to create a file
|
715
|
-
* and update the field with the file's ID if necessary.
|
716
|
-
*
|
717
|
-
* @param attributeMappings - The attribute mappings from the import definition.
|
718
|
-
* @param context - The context object containing information about the database, collection, and document.
|
719
|
-
* @param item - The item being imported, used for resolving template paths in fileData mappings.
|
720
|
-
* @returns The attribute mappings updated with any necessary post-import actions.
|
721
|
-
*/
|
722
|
-
? /**
|
723
|
-
* Generates attribute mappings with post-import actions based on the provided attribute mappings.
|
724
|
-
* This method checks each mapping for a fileData attribute and adds a post-import action to create a file
|
725
|
-
* and update the field with the file's ID if necessary.
|
726
|
-
*
|
727
|
-
* @param attributeMappings - The attribute mappings from the import definition.
|
728
|
-
* @param context - The context object containing information about the database, collection, and document.
|
729
|
-
* @param item - The item being imported, used for resolving template paths in fileData mappings.
|
730
|
-
* @returns The attribute mappings updated with any necessary post-import actions.
|
731
|
-
*/: string | null | undefined;
|
711
|
+
format?: string | null | undefined;
|
732
712
|
description?: string | Record<string, string> | undefined;
|
733
713
|
required?: boolean | undefined;
|
734
714
|
array?: boolean | undefined;
|
@@ -1392,27 +1372,7 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
|
|
1392
1372
|
attributes?: ({
|
1393
1373
|
key: string;
|
1394
1374
|
type?: "string" | undefined;
|
1395
|
-
format
|
1396
|
-
/**
|
1397
|
-
* Generates attribute mappings with post-import actions based on the provided attribute mappings.
|
1398
|
-
* This method checks each mapping for a fileData attribute and adds a post-import action to create a file
|
1399
|
-
* and update the field with the file's ID if necessary.
|
1400
|
-
*
|
1401
|
-
* @param attributeMappings - The attribute mappings from the import definition.
|
1402
|
-
* @param context - The context object containing information about the database, collection, and document.
|
1403
|
-
* @param item - The item being imported, used for resolving template paths in fileData mappings.
|
1404
|
-
* @returns The attribute mappings updated with any necessary post-import actions.
|
1405
|
-
*/
|
1406
|
-
? /**
|
1407
|
-
* Generates attribute mappings with post-import actions based on the provided attribute mappings.
|
1408
|
-
* This method checks each mapping for a fileData attribute and adds a post-import action to create a file
|
1409
|
-
* and update the field with the file's ID if necessary.
|
1410
|
-
*
|
1411
|
-
* @param attributeMappings - The attribute mappings from the import definition.
|
1412
|
-
* @param context - The context object containing information about the database, collection, and document.
|
1413
|
-
* @param item - The item being imported, used for resolving template paths in fileData mappings.
|
1414
|
-
* @returns The attribute mappings updated with any necessary post-import actions.
|
1415
|
-
*/: string | null | undefined;
|
1375
|
+
format?: string | null | undefined;
|
1416
1376
|
description?: string | Record<string, string> | undefined;
|
1417
1377
|
required?: boolean | undefined;
|
1418
1378
|
array?: boolean | undefined;
|
@@ -1077,20 +1077,29 @@ export class DataLoader {
|
|
1077
1077
|
if (itemDataToUpdate) {
|
1078
1078
|
itemDataToUpdate.finalData = this.mergeObjects(itemDataToUpdate.finalData, transformedData);
|
1079
1079
|
itemDataToUpdate.context = context;
|
1080
|
-
//
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
...
|
1080
|
+
// Fix: Ensure we properly merge the attribute mappings and their actions
|
1081
|
+
const mergedAttributeMappings = newImportDef.attributeMappings.map((newMapping) => {
|
1082
|
+
const existingMapping = itemDataToUpdate.importDef?.attributeMappings.find((m) => m.targetKey === newMapping.targetKey);
|
1083
|
+
return {
|
1084
|
+
...newMapping,
|
1085
1085
|
postImportActions: [
|
1086
|
-
...(
|
1087
|
-
...(
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1086
|
+
...(existingMapping?.postImportActions || []),
|
1087
|
+
...(newMapping.postImportActions || [])
|
1088
|
+
]
|
1089
|
+
};
|
1090
|
+
});
|
1091
|
+
itemDataToUpdate.importDef = {
|
1092
|
+
...newImportDef,
|
1093
|
+
attributeMappings: mergedAttributeMappings
|
1091
1094
|
};
|
1092
|
-
|
1093
|
-
|
1095
|
+
// Debug logging
|
1096
|
+
if (mergedAttributeMappings.some(m => m.postImportActions?.length > 0)) {
|
1097
|
+
logger.info(`Post-import actions for ${collection.name}: ${JSON.stringify(mergedAttributeMappings
|
1098
|
+
.filter(m => m.postImportActions?.length > 0)
|
1099
|
+
.map(m => ({
|
1100
|
+
targetKey: m.targetKey,
|
1101
|
+
actions: m.postImportActions
|
1102
|
+
})), null, 2)}`);
|
1094
1103
|
}
|
1095
1104
|
}
|
1096
1105
|
else {
|
@@ -198,10 +198,13 @@ export class ImportController {
|
|
198
198
|
}
|
199
199
|
}
|
200
200
|
async executePostImportActions(dbId, dataLoader, specificCollections) {
|
201
|
-
|
201
|
+
console.log("Executing post-import actions...");
|
202
|
+
const collectionsToProcess = specificCollections && specificCollections.length > 0 ? specificCollections : (this.config.collections ? this.config.collections.map(c => c.name) : Array.from(dataLoader.importMap.keys()));
|
203
|
+
console.log("Collections to process:", collectionsToProcess);
|
202
204
|
// Iterate over each collection in the importMap
|
203
205
|
for (const [collectionKey, collectionData] of dataLoader.importMap.entries()) {
|
204
|
-
|
206
|
+
const allCollectionKeys = collectionsToProcess.map(c => dataLoader.getCollectionKey(c));
|
207
|
+
if (allCollectionKeys.includes(collectionKey)) {
|
205
208
|
console.log(`Processing post-import actions for collection: ${collectionKey}`);
|
206
209
|
// Iterate over each item in the collectionData.data
|
207
210
|
for (const item of collectionData.data) {
|
@@ -221,6 +224,9 @@ export class ImportController {
|
|
221
224
|
}
|
222
225
|
}
|
223
226
|
}
|
227
|
+
else {
|
228
|
+
console.log(`Skipping collection: ${collectionKey} because it's not valid for post-import actions`);
|
229
|
+
}
|
224
230
|
}
|
225
231
|
}
|
226
232
|
}
|
@@ -91,7 +91,6 @@ export declare const ContextObject: z.ZodObject<{
|
|
91
91
|
context: z.ZodAny;
|
92
92
|
}, "strip", z.ZodTypeAny, {
|
93
93
|
collectionId: string;
|
94
|
-
dbId: string;
|
95
94
|
attributeMappings: {
|
96
95
|
targetKey: string;
|
97
96
|
oldKey?: string | undefined;
|
@@ -111,11 +110,11 @@ export declare const ContextObject: z.ZodObject<{
|
|
111
110
|
action: string;
|
112
111
|
}[] | undefined;
|
113
112
|
}[];
|
113
|
+
dbId: string;
|
114
114
|
context?: any;
|
115
115
|
finalItem?: any;
|
116
116
|
}, {
|
117
117
|
collectionId: string;
|
118
|
-
dbId: string;
|
119
118
|
attributeMappings: {
|
120
119
|
targetKey: string;
|
121
120
|
oldKey?: string | undefined;
|
@@ -135,6 +134,7 @@ export declare const ContextObject: z.ZodObject<{
|
|
135
134
|
action: string;
|
136
135
|
}[] | undefined;
|
137
136
|
}[];
|
137
|
+
dbId: string;
|
138
138
|
context?: any;
|
139
139
|
finalItem?: any;
|
140
140
|
}>;
|
@@ -8,8 +8,8 @@ export declare class UsersController {
|
|
8
8
|
constructor(config: AppwriteConfig, db: Databases);
|
9
9
|
wipeUsers(): Promise<void>;
|
10
10
|
getAllUsers(): Promise<Models.User<Models.Preferences>[]>;
|
11
|
-
createUsersAndReturn(items: AuthUserCreate[]): Promise<Models.User<Models.Preferences>[]>;
|
12
|
-
createUserAndReturn(item: AuthUserCreate
|
11
|
+
createUsersAndReturn(items: AuthUserCreate[]): Promise<(Models.User<Models.Preferences> | undefined)[]>;
|
12
|
+
createUserAndReturn(item: AuthUserCreate): Promise<Models.User<Models.Preferences> | undefined>;
|
13
13
|
createAndCheckForUserAndReturn(item: AuthUserCreate): Promise<Models.User<Models.Preferences> | undefined>;
|
14
14
|
getUserIdByEmailOrPhone(email?: string, phone?: string): Promise<string | undefined>;
|
15
15
|
transferUsersBetweenDbsLocalToRemote: (endpoint: string, projectId: string, apiKey: string) => Promise<void>;
|
package/dist/migrations/users.js
CHANGED
@@ -78,36 +78,26 @@ export class UsersController {
|
|
78
78
|
const users = await Promise.all(items.map((item) => this.createUserAndReturn(item)));
|
79
79
|
return users;
|
80
80
|
}
|
81
|
-
async createUserAndReturn(item
|
81
|
+
async createUserAndReturn(item) {
|
82
82
|
try {
|
83
|
-
const user = await
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
83
|
+
const user = await tryAwaitWithRetry(async () => {
|
84
|
+
const createdUser = await this.users.create(item.userId || ID.unique(), item.email || undefined, item.phone && item.phone.length < 15 && item.phone.startsWith("+")
|
85
|
+
? item.phone
|
86
|
+
: undefined, `changeMe${item.email?.toLowerCase()}` || `changeMePlease`, item.name || undefined);
|
87
|
+
if (item.labels) {
|
88
|
+
await this.users.updateLabels(createdUser.$id, item.labels);
|
89
|
+
}
|
90
|
+
if (item.prefs) {
|
91
|
+
await this.users.updatePrefs(createdUser.$id, item.prefs);
|
92
|
+
}
|
93
|
+
return createdUser;
|
94
|
+
}); // Set throwError to true since we want to handle errors
|
92
95
|
return user;
|
93
96
|
}
|
94
97
|
catch (e) {
|
95
|
-
if (e instanceof AppwriteException) {
|
96
|
-
if (e.message.toLowerCase().includes("fetch failed") ||
|
97
|
-
e.message.toLowerCase().includes("server error")) {
|
98
|
-
const numberOfAttempts = numAttempts || 0;
|
99
|
-
if (numberOfAttempts > 5) {
|
100
|
-
throw e;
|
101
|
-
}
|
102
|
-
const user = await this.createUserAndReturn(item, numberOfAttempts + 1);
|
103
|
-
return user;
|
104
|
-
}
|
105
|
-
}
|
106
98
|
if (e instanceof Error) {
|
107
99
|
logger.error("FAILED CREATING USER: ", e.message, item);
|
108
100
|
}
|
109
|
-
console.log("FAILED CREATING USER: ", e, item);
|
110
|
-
throw e;
|
111
101
|
}
|
112
102
|
}
|
113
103
|
async createAndCheckForUserAndReturn(item) {
|
@@ -106,7 +106,7 @@ export const tryAwaitWithRetry = async (createFunction, attemptNum = 0, throwErr
|
|
106
106
|
if (attemptNum > 5) {
|
107
107
|
throw error;
|
108
108
|
}
|
109
|
-
await delay(
|
109
|
+
await delay(2500);
|
110
110
|
return tryAwaitWithRetry(createFunction, attemptNum + 1);
|
111
111
|
}
|
112
112
|
if (throwError) {
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "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": "0.9.
|
4
|
+
"version": "0.9.983",
|
5
5
|
"main": "src/main.ts",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
@@ -1480,26 +1480,37 @@ export class DataLoader {
|
|
1480
1480
|
transformedData
|
1481
1481
|
);
|
1482
1482
|
itemDataToUpdate.context = context;
|
1483
|
-
|
1483
|
+
|
1484
|
+
// Fix: Ensure we properly merge the attribute mappings and their actions
|
1485
|
+
const mergedAttributeMappings = newImportDef.attributeMappings.map((newMapping) => {
|
1486
|
+
const existingMapping = itemDataToUpdate.importDef?.attributeMappings.find(
|
1487
|
+
(m) => m.targetKey === newMapping.targetKey
|
1488
|
+
);
|
1489
|
+
|
1490
|
+
return {
|
1491
|
+
...newMapping,
|
1492
|
+
postImportActions: [
|
1493
|
+
...(existingMapping?.postImportActions || []),
|
1494
|
+
...(newMapping.postImportActions || [])
|
1495
|
+
]
|
1496
|
+
};
|
1497
|
+
});
|
1498
|
+
|
1484
1499
|
itemDataToUpdate.importDef = {
|
1485
|
-
...
|
1486
|
-
attributeMappings:
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
if (collection.name.toLowerCase() === "councils") {
|
1500
|
-
console.log(
|
1501
|
-
`Mappings in update councils: ${JSON.stringify(
|
1502
|
-
itemDataToUpdate.importDef.attributeMappings,
|
1500
|
+
...newImportDef,
|
1501
|
+
attributeMappings: mergedAttributeMappings
|
1502
|
+
};
|
1503
|
+
|
1504
|
+
// Debug logging
|
1505
|
+
if (mergedAttributeMappings.some(m => m.postImportActions?.length > 0)) {
|
1506
|
+
logger.info(
|
1507
|
+
`Post-import actions for ${collection.name}: ${JSON.stringify(
|
1508
|
+
mergedAttributeMappings
|
1509
|
+
.filter(m => m.postImportActions?.length > 0)
|
1510
|
+
.map(m => ({
|
1511
|
+
targetKey: m.targetKey,
|
1512
|
+
actions: m.postImportActions
|
1513
|
+
})),
|
1503
1514
|
null,
|
1504
1515
|
2
|
1505
1516
|
)}`
|
@@ -298,11 +298,13 @@ export class ImportController {
|
|
298
298
|
}
|
299
299
|
|
300
300
|
async executePostImportActions(dbId: string, dataLoader: DataLoader, specificCollections?: string[]) {
|
301
|
-
|
302
|
-
|
301
|
+
console.log("Executing post-import actions...");
|
302
|
+
const collectionsToProcess = specificCollections && specificCollections.length > 0 ? specificCollections : (this.config.collections ? this.config.collections.map(c => c.name) : Array.from(dataLoader.importMap.keys()));
|
303
|
+
console.log("Collections to process:", collectionsToProcess);
|
303
304
|
// Iterate over each collection in the importMap
|
304
305
|
for (const [collectionKey, collectionData] of dataLoader.importMap.entries()) {
|
305
|
-
|
306
|
+
const allCollectionKeys = collectionsToProcess.map(c => dataLoader.getCollectionKey(c));
|
307
|
+
if (allCollectionKeys.includes(collectionKey)) {
|
306
308
|
console.log(
|
307
309
|
`Processing post-import actions for collection: ${collectionKey}`
|
308
310
|
);
|
@@ -330,6 +332,8 @@ export class ImportController {
|
|
330
332
|
}
|
331
333
|
}
|
332
334
|
}
|
335
|
+
} else {
|
336
|
+
console.log(`Skipping collection: ${collectionKey} because it's not valid for post-import actions`);
|
333
337
|
}
|
334
338
|
}
|
335
339
|
}
|
package/src/migrations/users.ts
CHANGED
@@ -111,44 +111,34 @@ export class UsersController {
|
|
111
111
|
return users;
|
112
112
|
}
|
113
113
|
|
114
|
-
async createUserAndReturn(item: AuthUserCreate
|
114
|
+
async createUserAndReturn(item: AuthUserCreate) {
|
115
115
|
try {
|
116
|
-
const user = await
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
116
|
+
const user = await tryAwaitWithRetry(async () => {
|
117
|
+
const createdUser = await this.users.create(
|
118
|
+
item.userId || ID.unique(),
|
119
|
+
item.email || undefined,
|
120
|
+
item.phone && item.phone.length < 15 && item.phone.startsWith("+")
|
121
|
+
? item.phone
|
122
|
+
: undefined,
|
123
|
+
`changeMe${item.email?.toLowerCase()}` || `changeMePlease`,
|
124
|
+
item.name || undefined
|
125
|
+
);
|
126
|
+
|
127
|
+
if (item.labels) {
|
128
|
+
await this.users.updateLabels(createdUser.$id, item.labels);
|
129
|
+
}
|
130
|
+
if (item.prefs) {
|
131
|
+
await this.users.updatePrefs(createdUser.$id, item.prefs);
|
132
|
+
}
|
133
|
+
|
134
|
+
return createdUser;
|
135
|
+
}); // Set throwError to true since we want to handle errors
|
136
|
+
|
131
137
|
return user;
|
132
138
|
} catch (e) {
|
133
|
-
if (e instanceof AppwriteException) {
|
134
|
-
if (
|
135
|
-
e.message.toLowerCase().includes("fetch failed") ||
|
136
|
-
e.message.toLowerCase().includes("server error")
|
137
|
-
) {
|
138
|
-
const numberOfAttempts = numAttempts || 0;
|
139
|
-
if (numberOfAttempts > 5) {
|
140
|
-
throw e;
|
141
|
-
}
|
142
|
-
const user: Models.User<Models.Preferences> =
|
143
|
-
await this.createUserAndReturn(item, numberOfAttempts + 1);
|
144
|
-
return user;
|
145
|
-
}
|
146
|
-
}
|
147
139
|
if (e instanceof Error) {
|
148
140
|
logger.error("FAILED CREATING USER: ", e.message, item);
|
149
141
|
}
|
150
|
-
console.log("FAILED CREATING USER: ", e, item);
|
151
|
-
throw e;
|
152
142
|
}
|
153
143
|
}
|
154
144
|
|