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 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
- // Merge existing importDef with new importDef, focusing only on postImportActions
1081
- itemDataToUpdate.importDef = {
1082
- ...itemDataToUpdate.importDef,
1083
- attributeMappings: itemDataToUpdate.importDef?.attributeMappings.map((attrMapping, index) => ({
1084
- ...attrMapping,
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
- ...(attrMapping.postImportActions || []),
1087
- ...(newImportDef.attributeMappings[index]
1088
- ?.postImportActions || []),
1089
- ],
1090
- })) || [],
1086
+ ...(existingMapping?.postImportActions || []),
1087
+ ...(newMapping.postImportActions || [])
1088
+ ]
1089
+ };
1090
+ });
1091
+ itemDataToUpdate.importDef = {
1092
+ ...newImportDef,
1093
+ attributeMappings: mergedAttributeMappings
1091
1094
  };
1092
- if (collection.name.toLowerCase() === "councils") {
1093
- console.log(`Mappings in update councils: ${JSON.stringify(itemDataToUpdate.importDef.attributeMappings, null, 2)}`);
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
- const collectionsToProcess = specificCollections || Array.from(dataLoader.importMap.keys());
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
- if (collectionsToProcess.includes(collectionKey)) {
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, numAttempts?: number): Promise<Models.User<Models.Preferences>>;
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>;
@@ -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, numAttempts) {
81
+ async createUserAndReturn(item) {
82
82
  try {
83
- const user = await this.users.create(item.userId || ID.unique(), item.email || undefined, item.phone && item.phone.length < 15 && item.phone.startsWith("+")
84
- ? item.phone
85
- : undefined, `changeMe${item.email?.toLowerCase()}` || `changeMePlease`, item.name || undefined);
86
- if (item.labels) {
87
- await this.users.updateLabels(user.$id, item.labels);
88
- }
89
- if (item.prefs) {
90
- await this.users.updatePrefs(user.$id, item.prefs);
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(1000);
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.981",
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
- // Merge existing importDef with new importDef, focusing only on postImportActions
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
- ...itemDataToUpdate.importDef,
1486
- attributeMappings:
1487
- itemDataToUpdate.importDef?.attributeMappings.map(
1488
- (attrMapping, index) => ({
1489
- ...attrMapping,
1490
- postImportActions: [
1491
- ...(attrMapping.postImportActions || []),
1492
- ...(newImportDef.attributeMappings[index]
1493
- ?.postImportActions || []),
1494
- ],
1495
- })
1496
- ) || [],
1497
- } as ImportDef;
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
- const collectionsToProcess = specificCollections || Array.from(dataLoader.importMap.keys());
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
- if (collectionsToProcess.includes(collectionKey)) {
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
  }
@@ -111,44 +111,34 @@ export class UsersController {
111
111
  return users;
112
112
  }
113
113
 
114
- async createUserAndReturn(item: AuthUserCreate, numAttempts?: number) {
114
+ async createUserAndReturn(item: AuthUserCreate) {
115
115
  try {
116
- const user = await this.users.create(
117
- item.userId || ID.unique(),
118
- item.email || undefined,
119
- item.phone && item.phone.length < 15 && item.phone.startsWith("+")
120
- ? item.phone
121
- : undefined,
122
- `changeMe${item.email?.toLowerCase()}` || `changeMePlease`,
123
- item.name || undefined
124
- );
125
- if (item.labels) {
126
- await this.users.updateLabels(user.$id, item.labels);
127
- }
128
- if (item.prefs) {
129
- await this.users.updatePrefs(user.$id, item.prefs);
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
 
@@ -163,7 +163,7 @@ export const tryAwaitWithRetry = async <T>(
163
163
  if (attemptNum > 5) {
164
164
  throw error;
165
165
  }
166
- await delay(1000);
166
+ await delay(2500);
167
167
  return tryAwaitWithRetry(createFunction, attemptNum + 1);
168
168
  }
169
169
  if (throwError) {