appwrite-utils-cli 0.0.11 → 0.0.12

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.
@@ -119,8 +119,8 @@ export class ImportController {
119
119
  continue;
120
120
  console.log(`Processing update definitions for collection ID: ${collection.$id}`);
121
121
  await this.processBatch(db, collection, importDef, dataToImport);
122
+ await setAllPendingAfterImportActionsToReady(this.database, db.$id, collection.$id);
122
123
  }
123
- await setAllPendingAfterImportActionsToReady(this.database, db.$id, collection.$id);
124
124
  }
125
125
  async loadData(importDef) {
126
126
  const filePath = path.resolve(this.appwriteFolderPath, importDef.filePath);
@@ -151,7 +151,7 @@ export class ImportController {
151
151
  async processBatch(db, collection, importDef, dataToImport, updateDefs = [], isMembersCollection = false) {
152
152
  for (let i = 0; i < dataToImport.length; i += this.batchLimit) {
153
153
  const batch = dataToImport.slice(i, i + this.batchLimit);
154
- batch.map(async (item) => {
154
+ const results = await Promise.allSettled(batch.map(async (item) => {
155
155
  let context = this.createContext(db, collection, item);
156
156
  let finalItem = await this.transformData(item, importDef.attributeMappings);
157
157
  let createIdToUse = undefined;
@@ -191,29 +191,23 @@ export class ImportController {
191
191
  logger.error(`Skipping user & contact creation for ${item} due to lack of email...`);
192
192
  }
193
193
  context = { ...context, ...finalItem };
194
- if (!(await this.importDataActions.validateItem(finalItem, importDef.attributeMappings, context))) {
194
+ const validated = await this.importDataActions.validateItem(finalItem, importDef.attributeMappings, context);
195
+ if (!validated) {
195
196
  console.error("Validation failed for item:", finalItem);
197
+ logger.error("Validation failed for item:", finalItem);
196
198
  return;
197
199
  }
198
- let afterContext;
199
200
  if ((importDef.type === "create" || !importDef.type) &&
200
201
  !associatedDoc) {
201
202
  const createdContext = await this.handleCreate(context, finalItem, updateDefs, createIdToUse);
202
- if (createdContext) {
203
- afterContext = createdContext;
204
- }
203
+ context = { ...context, ...createdContext };
205
204
  logger.info(`Handled create for ${context.docId}}`);
206
205
  }
207
206
  else {
208
207
  const updatedContext = await this.handleUpdate(context, finalItem, importDef);
209
- if (updatedContext) {
210
- afterContext = updatedContext;
211
- }
208
+ context = { ...context, ...updatedContext };
212
209
  logger.info(`Handled update for ${context.docId}`);
213
210
  }
214
- if (afterContext) {
215
- context = { ...context, ...afterContext };
216
- }
217
211
  const afterImportActionContext = structuredClone(context);
218
212
  const attributeMappingsWithActions = this.getAttributeMappingsWithActions(importDef.attributeMappings, context, finalItem);
219
213
  if (attributeMappingsWithActions.some((m) => m.postImportActions)) {
@@ -232,13 +226,13 @@ export class ImportController {
232
226
  // attributeMappings: attributeMappingsWithActions,
233
227
  // });
234
228
  }
229
+ }));
230
+ results.forEach((result) => {
231
+ if (result.status === "rejected") {
232
+ console.error("A process batch promise was rejected:", result.reason);
233
+ logger.error("An error occurred during creation: ", result.reason);
234
+ }
235
235
  });
236
- // results.forEach((result) => {
237
- // if (result.status === "rejected") {
238
- // console.error("A process batch promise was rejected:", result.reason);
239
- // logger.error("An error occurred during creation: ", result.reason);
240
- // }
241
- // });
242
236
  }
243
237
  }
244
238
  async handleCreate(context, finalItem, updateDefs, id) {
@@ -364,10 +358,6 @@ export class ImportController {
364
358
  await this.importDataActions.executeAfterImportActions(finalItem, attributeMappings, context);
365
359
  // Mark batch as processed
366
360
  await this.database.deleteDocument("migrations", "batches", batch.$id);
367
- await updateOperation(this.database, operation.$id, {
368
- status: "completed",
369
- batches: [],
370
- });
371
361
  }
372
362
  catch (error) {
373
363
  logger.error(`Failed to execute batch ${batch.$id}:`, error);
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.0.11",
4
+ "version": "0.0.12",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -113,7 +113,6 @@ export class ImportController {
113
113
  async importCollections(db: ConfigDatabase) {
114
114
  const maxParallel = 3; // Maximum number of collections to process in parallel
115
115
  let activePromises: Promise<void>[] = []; // Array to keep track of active promises
116
-
117
116
  for (const collection of this.config.collections) {
118
117
  // Function that returns a promise for processing a single collection
119
118
  const processCollection = async (col: ConfigCollection) => {
@@ -200,13 +199,12 @@ export class ImportController {
200
199
  `Processing update definitions for collection ID: ${collection.$id}`
201
200
  );
202
201
  await this.processBatch(db, collection, importDef, dataToImport);
202
+ await setAllPendingAfterImportActionsToReady(
203
+ this.database,
204
+ db.$id,
205
+ collection.$id
206
+ );
203
207
  }
204
-
205
- await setAllPendingAfterImportActionsToReady(
206
- this.database,
207
- db.$id,
208
- collection.$id
209
- );
210
208
  }
211
209
 
212
210
  async loadData(importDef: ImportDef): Promise<any[]> {
@@ -258,145 +256,138 @@ export class ImportController {
258
256
  ) {
259
257
  for (let i = 0; i < dataToImport.length; i += this.batchLimit) {
260
258
  const batch = dataToImport.slice(i, i + this.batchLimit);
261
- batch.map(async (item: any) => {
262
- let context = this.createContext(db, collection, item);
263
- let finalItem = await this.transformData(
264
- item,
265
- importDef.attributeMappings
266
- );
267
- let createIdToUse: string | undefined = undefined;
268
- let associatedDoc: Models.Document | undefined;
269
- if (
270
- isMembersCollection &&
271
- (finalItem.hasOwnProperty("email") || item.hasOwnProperty("phone"))
272
- ) {
273
- console.log("Found members collection, creating user...");
274
- const usersController = new UsersController(
275
- this.config,
276
- this.database
277
- );
278
- const userToCreate = AuthUserCreateSchema.safeParse({
279
- ...finalItem,
280
- });
281
- if (!userToCreate.success) {
282
- console.error(userToCreate.error);
283
- logger.error(userToCreate.error);
284
- return;
285
- }
286
- const user = await usersController.createUserAndReturn(
287
- userToCreate.data
288
- );
289
- createIdToUse = user.$id;
290
- context.docId = createIdToUse;
291
- context = { ...context, ...user };
292
- console.log(
293
- "Created user, deleting keys in finalItem that exist in user..."
294
- );
295
- const associatedDocFound = await this.database.listDocuments(
296
- db.$id,
297
- context.collId,
298
- [Query.equal("$id", createIdToUse)]
259
+ const results = await Promise.allSettled(
260
+ batch.map(async (item: any) => {
261
+ let context = this.createContext(db, collection, item);
262
+ let finalItem = await this.transformData(
263
+ item,
264
+ importDef.attributeMappings
299
265
  );
300
- if (associatedDocFound.documents.length > 0) {
301
- associatedDoc = associatedDocFound.documents[0];
302
- }
303
- // Delete keys in finalItem that also exist in user
304
- let deletedKeys: string[] = [];
305
- Object.keys(finalItem).forEach((key) => {
306
- if (user.hasOwnProperty(key)) {
307
- delete finalItem[key];
308
- deletedKeys.push(key);
266
+ let createIdToUse: string | undefined = undefined;
267
+ let associatedDoc: Models.Document | undefined;
268
+ if (
269
+ isMembersCollection &&
270
+ (finalItem.hasOwnProperty("email") || item.hasOwnProperty("phone"))
271
+ ) {
272
+ console.log("Found members collection, creating user...");
273
+ const usersController = new UsersController(
274
+ this.config,
275
+ this.database
276
+ );
277
+ const userToCreate = AuthUserCreateSchema.safeParse({
278
+ ...finalItem,
279
+ });
280
+ if (!userToCreate.success) {
281
+ console.error(userToCreate.error);
282
+ logger.error(userToCreate.error);
283
+ return;
309
284
  }
310
- });
311
- console.log(
312
- `Set createIdToUse to ${createIdToUse}. Deleted keys: ${deletedKeys.join(
313
- ", "
314
- )}.`
315
- );
316
- } else if (isMembersCollection) {
317
- logger.error(
318
- `Skipping user & contact creation for ${item} due to lack of email...`
319
- );
320
- }
321
-
322
- context = { ...context, ...finalItem };
285
+ const user = await usersController.createUserAndReturn(
286
+ userToCreate.data
287
+ );
288
+ createIdToUse = user.$id;
289
+ context.docId = createIdToUse;
290
+ context = { ...context, ...user };
291
+ console.log(
292
+ "Created user, deleting keys in finalItem that exist in user..."
293
+ );
294
+ const associatedDocFound = await this.database.listDocuments(
295
+ db.$id,
296
+ context.collId,
297
+ [Query.equal("$id", createIdToUse)]
298
+ );
299
+ if (associatedDocFound.documents.length > 0) {
300
+ associatedDoc = associatedDocFound.documents[0];
301
+ }
302
+ // Delete keys in finalItem that also exist in user
303
+ let deletedKeys: string[] = [];
304
+ Object.keys(finalItem).forEach((key) => {
305
+ if (user.hasOwnProperty(key)) {
306
+ delete finalItem[key];
307
+ deletedKeys.push(key);
308
+ }
309
+ });
310
+ console.log(
311
+ `Set createIdToUse to ${createIdToUse}. Deleted keys: ${deletedKeys.join(
312
+ ", "
313
+ )}.`
314
+ );
315
+ } else if (isMembersCollection) {
316
+ logger.error(
317
+ `Skipping user & contact creation for ${item} due to lack of email...`
318
+ );
319
+ }
323
320
 
324
- if (
325
- !(await this.importDataActions.validateItem(
321
+ context = { ...context, ...finalItem };
322
+ const validated = await this.importDataActions.validateItem(
326
323
  finalItem,
327
324
  importDef.attributeMappings,
328
325
  context
329
- ))
330
- ) {
331
- console.error("Validation failed for item:", finalItem);
332
- return;
333
- }
334
-
335
- let afterContext;
336
- if (
337
- (importDef.type === "create" || !importDef.type) &&
338
- !associatedDoc
339
- ) {
340
- const createdContext = await this.handleCreate(
341
- context,
342
- finalItem,
343
- updateDefs,
344
- createIdToUse
345
326
  );
346
- if (createdContext) {
347
- afterContext = createdContext;
327
+ if (!validated) {
328
+ console.error("Validation failed for item:", finalItem);
329
+ logger.error("Validation failed for item:", finalItem);
330
+ return;
348
331
  }
349
- logger.info(`Handled create for ${context.docId}}`);
350
- } else {
351
- const updatedContext = await this.handleUpdate(
352
- context,
353
- finalItem,
354
- importDef
355
- );
356
- if (updatedContext) {
357
- afterContext = updatedContext;
332
+
333
+ if (
334
+ (importDef.type === "create" || !importDef.type) &&
335
+ !associatedDoc
336
+ ) {
337
+ const createdContext = await this.handleCreate(
338
+ context,
339
+ finalItem,
340
+ updateDefs,
341
+ createIdToUse
342
+ );
343
+ context = { ...context, ...createdContext };
344
+ logger.info(`Handled create for ${context.docId}}`);
345
+ } else {
346
+ const updatedContext = await this.handleUpdate(
347
+ context,
348
+ finalItem,
349
+ importDef
350
+ );
351
+ context = { ...context, ...updatedContext };
352
+ logger.info(`Handled update for ${context.docId}`);
358
353
  }
359
- logger.info(`Handled update for ${context.docId}`);
360
- }
361
- if (afterContext) {
362
- context = { ...context, ...afterContext };
363
- }
364
- const afterImportActionContext = structuredClone(context);
365
- const attributeMappingsWithActions =
366
- this.getAttributeMappingsWithActions(
367
- importDef.attributeMappings,
368
- context,
369
- finalItem
370
- );
371
- if (attributeMappingsWithActions.some((m) => m.postImportActions)) {
372
- logger.info(
373
- `Pushing to post-import actions queue for ${context.docId}`
374
- );
375
- const afterImportOperationContext = ContextObject.parse({
376
- dbId: db.$id,
377
- collectionId: collection.$id,
378
- finalItem: finalItem,
379
- attributeMappings: attributeMappingsWithActions,
380
- context: afterImportActionContext,
381
- });
382
- await createOrFindAfterImportOperation(
383
- this.database,
384
- context.collId,
385
- afterImportOperationContext
386
- );
387
- // this.postImportActionsQueue.push({
388
- // context: afterImportActionContext,
389
- // finalItem: finalItem,
390
- // attributeMappings: attributeMappingsWithActions,
391
- // });
354
+ const afterImportActionContext = structuredClone(context);
355
+ const attributeMappingsWithActions =
356
+ this.getAttributeMappingsWithActions(
357
+ importDef.attributeMappings,
358
+ context,
359
+ finalItem
360
+ );
361
+ if (attributeMappingsWithActions.some((m) => m.postImportActions)) {
362
+ logger.info(
363
+ `Pushing to post-import actions queue for ${context.docId}`
364
+ );
365
+ const afterImportOperationContext = ContextObject.parse({
366
+ dbId: db.$id,
367
+ collectionId: collection.$id,
368
+ finalItem: finalItem,
369
+ attributeMappings: attributeMappingsWithActions,
370
+ context: afterImportActionContext,
371
+ });
372
+ await createOrFindAfterImportOperation(
373
+ this.database,
374
+ context.collId,
375
+ afterImportOperationContext
376
+ );
377
+ // this.postImportActionsQueue.push({
378
+ // context: afterImportActionContext,
379
+ // finalItem: finalItem,
380
+ // attributeMappings: attributeMappingsWithActions,
381
+ // });
382
+ }
383
+ })
384
+ );
385
+ results.forEach((result) => {
386
+ if (result.status === "rejected") {
387
+ console.error("A process batch promise was rejected:", result.reason);
388
+ logger.error("An error occurred during creation: ", result.reason);
392
389
  }
393
390
  });
394
- // results.forEach((result) => {
395
- // if (result.status === "rejected") {
396
- // console.error("A process batch promise was rejected:", result.reason);
397
- // logger.error("An error occurred during creation: ", result.reason);
398
- // }
399
- // });
400
391
  }
401
392
  }
402
393
 
@@ -584,10 +575,6 @@ export class ImportController {
584
575
  "batches",
585
576
  batch.$id
586
577
  );
587
- await updateOperation(this.database, operation.$id, {
588
- status: "completed",
589
- batches: [],
590
- });
591
578
  } catch (error) {
592
579
  logger.error(`Failed to execute batch ${batch.$id}:`, error);
593
580
  }