appwrite-utils-cli 0.0.67 → 0.0.68

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
@@ -132,6 +132,7 @@ This setup ensures that developers have robust tools at their fingertips to mana
132
132
 
133
133
  ### Changelog
134
134
 
135
+ - 0.0.68: Fixed the occasional case where, when mapping ID's from old data to new, there would be an array of ID's to match against. `idMappings` now supports arrays.
135
136
  - 0.0.67: Fixed `updates` in `importDef`'s update mappings overwriting postImportActions from the original
136
137
  - 0.0.57: Fixed `dataLoader`'s `idMapping`'s giving me issues
137
138
  - 0.0.55: Added `documentExists` check to batch creation functionality to try to prevent duplicates
@@ -666,7 +666,16 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
666
666
  converters?: string[] | undefined;
667
667
  validationActions?: {
668
668
  params: string[];
669
- action: string;
669
+ action: string; /**
670
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
671
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
672
+ * and update the field with the file's ID if necessary.
673
+ *
674
+ * @param attributeMappings - The attribute mappings from the import definition.
675
+ * @param context - The context object containing information about the database, collection, and document.
676
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
677
+ * @returns The attribute mappings updated with any necessary post-import actions.
678
+ */
670
679
  }[] | undefined;
671
680
  postImportActions?: {
672
681
  params: (string | Record<string, any>)[];
@@ -1286,7 +1295,16 @@ export declare const CollectionImportDataSchema: z.ZodObject<{
1286
1295
  converters?: string[] | undefined;
1287
1296
  validationActions?: {
1288
1297
  params: string[];
1289
- action: string;
1298
+ action: string; /**
1299
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
1300
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
1301
+ * and update the field with the file's ID if necessary.
1302
+ *
1303
+ * @param attributeMappings - The attribute mappings from the import definition.
1304
+ * @param context - The context object containing information about the database, collection, and document.
1305
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
1306
+ * @returns The attribute mappings updated with any necessary post-import actions.
1307
+ */
1290
1308
  }[] | undefined;
1291
1309
  postImportActions?: {
1292
1310
  params: (string | Record<string, any>)[];
@@ -1690,7 +1708,16 @@ export declare class DataLoader {
1690
1708
  converters?: string[] | undefined;
1691
1709
  validationActions?: {
1692
1710
  params: string[];
1693
- action: string;
1711
+ action: string; /**
1712
+ * Generates attribute mappings with post-import actions based on the provided attribute mappings.
1713
+ * This method checks each mapping for a fileData attribute and adds a post-import action to create a file
1714
+ * and update the field with the file's ID if necessary.
1715
+ *
1716
+ * @param attributeMappings - The attribute mappings from the import definition.
1717
+ * @param context - The context object containing information about the database, collection, and document.
1718
+ * @param item - The item being imported, used for resolving template paths in fileData mappings.
1719
+ * @returns The attribute mappings updated with any necessary post-import actions.
1720
+ */
1694
1721
  }[] | undefined;
1695
1722
  postImportActions?: {
1696
1723
  params: (string | Record<string, any>)[];
@@ -441,90 +441,101 @@ export class DataLoader {
441
441
  const targetCollectionKey = this.getCollectionKey(idMapping.targetCollection);
442
442
  const fieldToSetKey = idMapping.fieldToSet || idMapping.sourceField;
443
443
  const targetFieldKey = idMapping.targetFieldToMatch || idMapping.targetField;
444
- const valueToMatch = this.getValueFromData(collectionData.data[i].finalData, collectionData.data[i].context, idMapping.sourceField);
444
+ const sourceValue = this.getValueFromData(collectionData.data[i].finalData, collectionData.data[i].context, idMapping.sourceField);
445
445
  // Skip if value to match is missing or empty
446
- if (!valueToMatch ||
447
- _.isEmpty(valueToMatch) ||
448
- valueToMatch === null)
446
+ if (!sourceValue ||
447
+ _.isEmpty(sourceValue) ||
448
+ sourceValue === null)
449
449
  continue;
450
450
  const isFieldToSetArray = collectionConfig.attributes.find((attribute) => attribute.key === fieldToSetKey)?.array;
451
451
  const targetCollectionData = this.importMap.get(targetCollectionKey);
452
452
  if (!targetCollectionData || !targetCollectionData.data)
453
453
  continue;
454
- // Find matching data in the target collection
455
- const foundData = targetCollectionData.data.filter(({ context, finalData }) => {
456
- const targetValue = this.getValueFromData(finalData, context, targetFieldKey);
457
- const isMatch = `${targetValue}` === `${valueToMatch}`;
458
- // Ensure the targetValue is defined and not null
459
- return (isMatch &&
460
- targetValue !== undefined &&
461
- targetValue !== null);
462
- });
454
+ // Handle cases where sourceValue is an array
455
+ const sourceValues = Array.isArray(sourceValue)
456
+ ? sourceValue
457
+ : [sourceValue];
458
+ const newData = [];
459
+ for (const valueToMatch of sourceValues) {
460
+ // Find matching data in the target collection
461
+ const foundData = targetCollectionData.data.filter(({ context, finalData }) => {
462
+ const targetValue = this.getValueFromData(finalData, context, targetFieldKey);
463
+ const isMatch = `${targetValue}` === `${valueToMatch}`;
464
+ // Ensure the targetValue is defined and not null
465
+ return (isMatch &&
466
+ targetValue !== undefined &&
467
+ targetValue !== null);
468
+ });
469
+ if (foundData.length) {
470
+ newData.push(...foundData.map((data) => {
471
+ return this.getValueFromData(data.finalData, data.context, idMapping.targetField);
472
+ }));
473
+ }
474
+ else {
475
+ logger.info(`No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(idMapping, null, 2)}`);
476
+ }
477
+ }
463
478
  const getCurrentDataFiltered = (currentData) => {
464
479
  if (Array.isArray(currentData.finalData[fieldToSetKey])) {
465
- return currentData.finalData[fieldToSetKey].filter((data) => `${data}` !== `${valueToMatch}`);
480
+ return currentData.finalData[fieldToSetKey].filter((data) => !sourceValues.includes(`${data}`));
466
481
  }
467
482
  return currentData.finalData[fieldToSetKey];
468
483
  };
469
484
  // Get the current data to be updated
470
485
  const currentDataFiltered = getCurrentDataFiltered(collectionData.data[i]);
471
- // Log and skip if no matching data found
472
- if (!foundData.length) {
473
- // console.log(
474
- // `No data found for collection ${collectionConfig.name}: - Target collection: ${targetCollectionKey} - Value to match: ${valueToMatch} - Field to set: ${fieldToSetKey} - Target field to match: ${targetFieldKey} - Target field value: ${idMapping.targetField}`
475
- // );
476
- logger.info(`No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(idMapping, null, 2)}`);
477
- continue;
478
- }
479
- needsUpdate = true;
480
- // Extract the new data to set
481
- const newData = foundData.map((data) => {
482
- const valueFound = this.getValueFromData(data.finalData, data.context, idMapping.targetField);
483
- return valueFound;
484
- });
485
- // Handle cases where current data is an array
486
- if (isFieldToSetArray) {
487
- if (!currentDataFiltered) {
488
- // Set new data if current data is undefined
489
- collectionData.data[i].finalData[fieldToSetKey] =
490
- Array.isArray(newData) ? newData : [newData];
486
+ if (newData.length) {
487
+ needsUpdate = true;
488
+ // Handle cases where current data is an array
489
+ if (isFieldToSetArray) {
490
+ if (!currentDataFiltered) {
491
+ // Set new data if current data is undefined
492
+ collectionData.data[i].finalData[fieldToSetKey] =
493
+ Array.isArray(newData) ? newData : [newData];
494
+ }
495
+ else {
496
+ if (Array.isArray(currentDataFiltered)) {
497
+ // Convert current data to array and merge if new data is non-empty array
498
+ collectionData.data[i].finalData[fieldToSetKey] = [
499
+ ...new Set([...currentDataFiltered, ...newData].filter((value) => value !== null &&
500
+ value !== undefined &&
501
+ value !== "")),
502
+ ];
503
+ }
504
+ else {
505
+ // Merge arrays if new data is non-empty array and filter for uniqueness
506
+ collectionData.data[i].finalData[fieldToSetKey] = [
507
+ ...new Set([
508
+ ...(Array.isArray(currentDataFiltered)
509
+ ? currentDataFiltered
510
+ : [currentDataFiltered]),
511
+ ...newData,
512
+ ].filter((value) => value !== null &&
513
+ value !== undefined &&
514
+ value !== "")),
515
+ ];
516
+ }
517
+ }
491
518
  }
492
519
  else {
493
- if (Array.isArray(currentDataFiltered)) {
494
- // Convert current data to array and merge if new data is non-empty array
495
- collectionData.data[i].finalData[fieldToSetKey] = [
496
- ...new Set([...currentDataFiltered, ...newData].filter((value) => `${value}` !== `${valueToMatch}` && value)),
497
- ];
520
+ if (!currentDataFiltered) {
521
+ // Set new data if current data is undefined
522
+ collectionData.data[i].finalData[fieldToSetKey] =
523
+ Array.isArray(newData) ? newData[0] : newData;
498
524
  }
499
- else {
500
- // Merge arrays if new data is non-empty array and filter for uniqueness
525
+ else if (Array.isArray(newData) && newData.length > 0) {
526
+ // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
527
+ // and take the first value, because it's an array and the attribute is not an array
501
528
  collectionData.data[i].finalData[fieldToSetKey] = [
502
- ...new Set([
503
- ...(Array.isArray(currentDataFiltered)
504
- ? currentDataFiltered
505
- : [currentDataFiltered]),
506
- ...newData,
507
- ].filter((value) => `${value}` !== `${valueToMatch}` && value)),
508
- ];
529
+ ...new Set([currentDataFiltered, ...newData].filter((value) => value !== null &&
530
+ value !== undefined &&
531
+ value !== "")),
532
+ ].slice(0, 1)[0];
533
+ }
534
+ else if (!Array.isArray(newData) &&
535
+ newData !== undefined) {
536
+ // Simply update the field if new data is not an array and defined
537
+ collectionData.data[i].finalData[fieldToSetKey] = newData;
509
538
  }
510
- }
511
- }
512
- else {
513
- if (!currentDataFiltered) {
514
- // Set new data if current data is undefined
515
- collectionData.data[i].finalData[fieldToSetKey] =
516
- Array.isArray(newData) ? newData[0] : newData;
517
- }
518
- else if (Array.isArray(newData) && newData.length > 0) {
519
- // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
520
- // and take the first value, because it's an array and the attribute is not an array
521
- collectionData.data[i].finalData[fieldToSetKey] = [
522
- ...new Set([currentDataFiltered, ...newData].filter((value) => `${value}` !== `${valueToMatch}` && value)),
523
- ].slice(0, 1)[0];
524
- }
525
- else if (!Array.isArray(newData) && newData !== undefined) {
526
- // Simply update the field if new data is not an array and defined
527
- collectionData.data[i].finalData[fieldToSetKey] = newData;
528
539
  }
529
540
  }
530
541
  }
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.67",
4
+ "version": "0.0.68",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@types/inquirer": "^9.0.7",
36
- "appwrite-utils": "^0.3.3",
36
+ "appwrite-utils": "^0.3.5",
37
37
  "commander": "^12.0.0",
38
38
  "inquirer": "^9.2.20",
39
39
  "js-yaml": "^4.1.0",
@@ -542,7 +542,7 @@ export class DataLoader {
542
542
  idMapping.fieldToSet || idMapping.sourceField;
543
543
  const targetFieldKey =
544
544
  idMapping.targetFieldToMatch || idMapping.targetField;
545
- const valueToMatch = this.getValueFromData(
545
+ const sourceValue = this.getValueFromData(
546
546
  collectionData.data[i].finalData,
547
547
  collectionData.data[i].context,
548
548
  idMapping.sourceField
@@ -550,9 +550,9 @@ export class DataLoader {
550
550
 
551
551
  // Skip if value to match is missing or empty
552
552
  if (
553
- !valueToMatch ||
554
- _.isEmpty(valueToMatch) ||
555
- valueToMatch === null
553
+ !sourceValue ||
554
+ _.isEmpty(sourceValue) ||
555
+ sourceValue === null
556
556
  )
557
557
  continue;
558
558
 
@@ -565,28 +565,56 @@ export class DataLoader {
565
565
  if (!targetCollectionData || !targetCollectionData.data)
566
566
  continue;
567
567
 
568
- // Find matching data in the target collection
569
- const foundData = targetCollectionData.data.filter(
570
- ({ context, finalData }) => {
571
- const targetValue = this.getValueFromData(
572
- finalData,
573
- context,
574
- targetFieldKey
568
+ // Handle cases where sourceValue is an array
569
+ const sourceValues = Array.isArray(sourceValue)
570
+ ? sourceValue
571
+ : [sourceValue];
572
+ const newData = [];
573
+
574
+ for (const valueToMatch of sourceValues) {
575
+ // Find matching data in the target collection
576
+ const foundData = targetCollectionData.data.filter(
577
+ ({ context, finalData }) => {
578
+ const targetValue = this.getValueFromData(
579
+ finalData,
580
+ context,
581
+ targetFieldKey
582
+ );
583
+ const isMatch = `${targetValue}` === `${valueToMatch}`;
584
+ // Ensure the targetValue is defined and not null
585
+ return (
586
+ isMatch &&
587
+ targetValue !== undefined &&
588
+ targetValue !== null
589
+ );
590
+ }
591
+ );
592
+
593
+ if (foundData.length) {
594
+ newData.push(
595
+ ...foundData.map((data) => {
596
+ return this.getValueFromData(
597
+ data.finalData,
598
+ data.context,
599
+ idMapping.targetField
600
+ );
601
+ })
575
602
  );
576
- const isMatch = `${targetValue}` === `${valueToMatch}`;
577
- // Ensure the targetValue is defined and not null
578
- return (
579
- isMatch &&
580
- targetValue !== undefined &&
581
- targetValue !== null
603
+ } else {
604
+ logger.info(
605
+ `No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(
606
+ idMapping,
607
+ null,
608
+ 2
609
+ )}`
582
610
  );
583
611
  }
584
- );
612
+ }
585
613
 
586
614
  const getCurrentDataFiltered = (currentData: any) => {
587
615
  if (Array.isArray(currentData.finalData[fieldToSetKey])) {
588
616
  return currentData.finalData[fieldToSetKey].filter(
589
- (data: any) => `${data}` !== `${valueToMatch}`
617
+ (data: any) => !sourceValues.includes(`${data}`)
590
618
  );
591
619
  }
592
620
  return currentData.finalData[fieldToSetKey];
@@ -596,87 +624,74 @@ export class DataLoader {
596
624
  const currentDataFiltered = getCurrentDataFiltered(
597
625
  collectionData.data[i]
598
626
  );
599
- // Log and skip if no matching data found
600
- if (!foundData.length) {
601
- // console.log(
602
- // `No data found for collection ${collectionConfig.name}: - Target collection: ${targetCollectionKey} - Value to match: ${valueToMatch} - Field to set: ${fieldToSetKey} - Target field to match: ${targetFieldKey} - Target field value: ${idMapping.targetField}`
603
- // );
604
- logger.info(
605
- `No data found for collection: ${targetCollectionKey} with value: ${valueToMatch} for field: ${fieldToSetKey} -- idMapping: ${JSON.stringify(
606
- idMapping,
607
- null,
608
- 2
609
- )}`
610
- );
611
- continue;
612
- }
613
627
 
614
- needsUpdate = true;
628
+ if (newData.length) {
629
+ needsUpdate = true;
615
630
 
616
- // Extract the new data to set
617
- const newData = foundData.map((data) => {
618
- const valueFound = this.getValueFromData(
619
- data.finalData,
620
- data.context,
621
- idMapping.targetField
622
- );
623
- return valueFound;
624
- });
625
-
626
- // Handle cases where current data is an array
627
- if (isFieldToSetArray) {
628
- if (!currentDataFiltered) {
629
- // Set new data if current data is undefined
630
- collectionData.data[i].finalData[fieldToSetKey] =
631
- Array.isArray(newData) ? newData : [newData];
632
- } else {
633
- if (Array.isArray(currentDataFiltered)) {
634
- // Convert current data to array and merge if new data is non-empty array
635
- collectionData.data[i].finalData[fieldToSetKey] = [
636
- ...new Set(
637
- [...currentDataFiltered, ...newData].filter(
638
- (value: any) =>
639
- `${value}` !== `${valueToMatch}` && value
640
- )
641
- ),
642
- ];
631
+ // Handle cases where current data is an array
632
+ if (isFieldToSetArray) {
633
+ if (!currentDataFiltered) {
634
+ // Set new data if current data is undefined
635
+ collectionData.data[i].finalData[fieldToSetKey] =
636
+ Array.isArray(newData) ? newData : [newData];
643
637
  } else {
644
- // Merge arrays if new data is non-empty array and filter for uniqueness
638
+ if (Array.isArray(currentDataFiltered)) {
639
+ // Convert current data to array and merge if new data is non-empty array
640
+ collectionData.data[i].finalData[fieldToSetKey] = [
641
+ ...new Set(
642
+ [...currentDataFiltered, ...newData].filter(
643
+ (value: any) =>
644
+ value !== null &&
645
+ value !== undefined &&
646
+ value !== ""
647
+ )
648
+ ),
649
+ ];
650
+ } else {
651
+ // Merge arrays if new data is non-empty array and filter for uniqueness
652
+ collectionData.data[i].finalData[fieldToSetKey] = [
653
+ ...new Set(
654
+ [
655
+ ...(Array.isArray(currentDataFiltered)
656
+ ? currentDataFiltered
657
+ : [currentDataFiltered]),
658
+ ...newData,
659
+ ].filter(
660
+ (value: any) =>
661
+ value !== null &&
662
+ value !== undefined &&
663
+ value !== ""
664
+ )
665
+ ),
666
+ ];
667
+ }
668
+ }
669
+ } else {
670
+ if (!currentDataFiltered) {
671
+ // Set new data if current data is undefined
672
+ collectionData.data[i].finalData[fieldToSetKey] =
673
+ Array.isArray(newData) ? newData[0] : newData;
674
+ } else if (Array.isArray(newData) && newData.length > 0) {
675
+ // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
676
+ // and take the first value, because it's an array and the attribute is not an array
645
677
  collectionData.data[i].finalData[fieldToSetKey] = [
646
678
  ...new Set(
647
- [
648
- ...(Array.isArray(currentDataFiltered)
649
- ? currentDataFiltered
650
- : [currentDataFiltered]),
651
- ...newData,
652
- ].filter(
679
+ [currentDataFiltered, ...newData].filter(
653
680
  (value: any) =>
654
- `${value}` !== `${valueToMatch}` && value
681
+ value !== null &&
682
+ value !== undefined &&
683
+ value !== ""
655
684
  )
656
685
  ),
657
- ];
686
+ ].slice(0, 1)[0];
687
+ } else if (
688
+ !Array.isArray(newData) &&
689
+ newData !== undefined
690
+ ) {
691
+ // Simply update the field if new data is not an array and defined
692
+ collectionData.data[i].finalData[fieldToSetKey] = newData;
658
693
  }
659
694
  }
660
- } else {
661
- if (!currentDataFiltered) {
662
- // Set new data if current data is undefined
663
- collectionData.data[i].finalData[fieldToSetKey] =
664
- Array.isArray(newData) ? newData[0] : newData;
665
- } else if (Array.isArray(newData) && newData.length > 0) {
666
- // Convert current data to array and merge if new data is non-empty array, then filter for uniqueness
667
- // and take the first value, because it's an array and the attribute is not an array
668
- collectionData.data[i].finalData[fieldToSetKey] = [
669
- ...new Set(
670
- [currentDataFiltered, ...newData].filter(
671
- (value: any) =>
672
- `${value}` !== `${valueToMatch}` && value
673
- )
674
- ),
675
- ].slice(0, 1)[0];
676
- } else if (!Array.isArray(newData) && newData !== undefined) {
677
- // Simply update the field if new data is not an array and defined
678
- collectionData.data[i].finalData[fieldToSetKey] = newData;
679
- }
680
695
  }
681
696
  }
682
697
  }