sfdx-hardis 6.5.5 → 6.6.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.
@@ -5538,15 +5538,12 @@
5538
5538
  "import:data:org:hardis"
5539
5539
  ]
5540
5540
  },
5541
- "hardis:org:diagnose:audittrail": {
5541
+ "hardis:org:files:export": {
5542
5542
  "aliases": [],
5543
5543
  "args": {},
5544
- "description": "Export Audit trail into a CSV file with selected criteria, and highlight suspect actions\n\nAlso detects updates of Custom Settings values (disable by defining `SKIP_AUDIT_TRAIL_CUSTOM_SETTINGS=true`)\n\nRegular setup actions performed in major orgs are filtered.\n\n- \"\"\n - createScratchOrg\n - changedsenderemail\n - deleteScratchOrg\n - loginasgrantedtopartnerbt\n- Certificate and Key Management\n - insertCertificate\n- Custom App Licenses\n - addeduserpackagelicense\n - granteduserpackagelicense\n - revokeduserpackagelicense\n- Customer Portal\n - createdcustomersuccessuser\n - CSPUserDisabled\n- Currency\n - updateddatedexchrate\n- Data Management\n - queueMembership\n- Email Administration\n - dkimRotationPreparationSuccessful\n - dkimRotationSuccessful\n- External Objects\n - xdsEncryptedFieldChange\n- Groups\n - groupMembership\n- Holidays\n - holiday_insert\n- Inbox mobile and legacy desktop apps\n - enableSIQUserNonEAC\n - siqUserAcceptedTOS\n- Manage Users\n - activateduser\n - createduser\n - changedcommunitynickname\n - changedemail\n - changedfederationid\n - changedpassword\n - changedinteractionuseroffon\n - changedinteractionuseronoff\n - changedmarketinguseroffon\n - changedmarketinguseronoff\n - changedofflineuseroffon\n - changedprofileforuserstdtostd\n - changedprofileforuser\n - changedprofileforusercusttostd\n - changedprofileforuserstdtocust\n - changedroleforusertonone\n - changedroleforuser\n - changedroleforuserfromnone\n - changedUserAdminVerifiedStatusVerified\n - changedUserEmailVerifiedStatusUnverified\n - changedUserEmailVerifiedStatusVerified\n - changedknowledgeuseroffon\n - changedsfcontentuseroffon\n - changedsupportuseroffon\n - changedusername\n - changedUserPhoneNumber\n - changedUserPhoneVerifiedStatusUnverified\n - changedUserPhoneVerifiedStatusVerified\n - deactivateduser\n - deleteAuthenticatorPairing\n - deleteTwoFactorInfo2\n - deleteTwoFactorTempCode\n - frozeuser\n - insertAuthenticatorPairing\n - insertTwoFactorInfo2\n - insertTwoFactorTempCode\n - lightningloginenroll\n - PermSetAssign\n - PermSetGroupAssign\n - PermSetGroupUnassign\n - PermSetLicenseAssign\n - PermSetUnassign\n - PermSetLicenseUnassign\n - registeredUserPhoneNumber\n - resetpassword\n - suNetworkAdminLogin\n - suNetworkAdminLogout\n - suOrgAdminLogin\n - suOrgAdminLogout\n - unfrozeuser\n - useremailchangesent\n- Mobile Administration\n - assigneduserstomobileconfig\n- Reporting Snapshots\n - createdReportJob\n - deletedReportJob\n- Sandboxes\n - DeleteSandbox\n\nBy default, deployment user defined in .sfdx-hardis.yml targetUsername property will be excluded.\n\nYou can define additional users to exclude in .sfdx-hardis.yml **monitoringExcludeUsernames** property.\n\nYou can also add more sections / actions considered as not suspect using property **monitoringAllowedSectionsActions**\n\nExample:\n\n```yaml\nmonitoringExcludeUsernames:\n - deploymentuser@cloudity.com\n - marketingcloud@cloudity.com\n - integration-user@cloudity.com\n\nmonitoringAllowedSectionsActions:\n \"Some section\": [] // Will ignore all actions from such section\n \"Some other section\": [\"actionType1\",\"actionType2\",\"actionType3\"] // Will ignore only those 3 actions from section \"Some other section\". Other actions in the same section will be considered as suspect.\n```\n\n## Excel output example\n\n![](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-monitoring-audittrail-excel.jpg)\n\n## Local output example\n\n![](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-monitoring-audittrail-local.jpg)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-suspect-audit-trail/) and can output Grafana, Slack and MsTeams Notifications.\n",
5544
+ "description": "\n## Command Behavior\n\n**Exports file attachments (ContentVersion, Attachment) from a Salesforce org based on a predefined configuration.**\n\nThis command enables the mass download of files associated with Salesforce records, providing a robust solution for backing up files, migrating them to other systems, or integrating them with external document management solutions.\n\nKey functionalities:\n\n- **Configuration-Driven Export:** Relies on an `export.json` file within a designated file export project to define the export criteria, including the SOQL query for parent records, file types to export, output naming conventions, and file size filtering.\n- **File Size Filtering:** Supports minimum file size filtering via the `fileSizeMin` configuration parameter (in KB). Files smaller than the specified size will be skipped during export.\n- **File Validation:** After downloading each file, validates the integrity by:\n - **Checksum Validation:** For ContentVersion files, compares MD5 checksum with Salesforce's stored checksum\n - **Size Validation:** For both ContentVersion and Attachment files, verifies actual file size matches expected size\n - **Status Tracking:** Files are categorized with specific statuses: `success` (valid files), `failed` (download errors), `skipped` (filtered files), `invalid` (downloaded but failed validation)\n - All validation results are logged in the CSV export log for audit purposes\n- **Resume/Restart Capability:** \n - **Resume Mode:** When `--resume` flag is used (default in CI environments), checks existing downloaded files for validity. Valid files are skipped, invalid files are re-downloaded.\n - **Restart Mode:** When resume is disabled, clears the output folder and starts a fresh export.\n - **Interactive Mode:** When existing files are found and `--resume` is not explicitly specified (non-CI environments), prompts the user to choose between resume or restart.\n- **Interactive Project Selection:** If the file export project path is not provided via the `--path` flag, it interactively prompts the user to select one.\n- **Configurable Export Options:** Allows overriding default export settings such as `chunksize` (number of records processed in a batch), `polltimeout` (timeout for Bulk API calls), and `startchunknumber` (to resume a failed export).\n- **Support for ContentVersion and Attachment:** Handles both modern Salesforce Files (ContentVersion) and older Attachments.\n\nSee this article for a practical example:\n\n[![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **FilesExporter Class:** The core logic is encapsulated within the `FilesExporter` class, which orchestrates the entire export process.\n- **SOQL Queries (Bulk API):** It uses Salesforce Bulk API queries to efficiently retrieve large volumes of parent record IDs and file metadata, including checksums and file sizes.\n- **File Download:** Downloads the actual file content from Salesforce.\n- **File Validation:** After each successful download, validates file integrity by comparing checksums (ContentVersion) and file sizes (both ContentVersion and Attachment) against Salesforce metadata.\n- **Resume Logic:** In resume mode, checks for existing files before downloading, validates their integrity, and only re-downloads invalid or missing files. This enables efficient recovery from interrupted exports.\n- **File System Operations:** Writes the downloaded files to the local file system, organizing them into folders based on the configured naming conventions.\n- **Configuration Loading:** Reads the `export.json` file to get the export configuration. It also allows for interactive overriding of these settings.\n- **Interactive Prompts:** Uses `selectFilesWorkspace` to allow the user to choose a file export project, `promptFilesExportConfiguration` for customizing export options, and prompts for resume/restart choice when existing files are found.\n- **Error Handling:** Includes mechanisms to handle potential errors during the export process, such as network issues, API limits, and file validation failures. Each file is assigned a specific status (`success`, `failed`, `skipped`, `invalid`) for comprehensive tracking and troubleshooting.\n</details>\n",
5545
5545
  "examples": [
5546
- "$ sf hardis:org:diagnose:audittrail",
5547
- "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com",
5548
- "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com",
5549
- "$ sf hardis:org:diagnose:audittrail --lastndays 5"
5546
+ "$ sf hardis:org:files:export"
5550
5547
  ],
5551
5548
  "flags": {
5552
5549
  "json": {
@@ -5564,30 +5561,48 @@
5564
5561
  "multiple": false,
5565
5562
  "type": "option"
5566
5563
  },
5567
- "excludeusers": {
5568
- "char": "e",
5569
- "description": "Comma-separated list of usernames to exclude",
5570
- "name": "excludeusers",
5564
+ "path": {
5565
+ "char": "p",
5566
+ "description": "Path to the file export project",
5567
+ "name": "path",
5571
5568
  "hasDynamicHelp": false,
5572
5569
  "multiple": false,
5573
5570
  "type": "option"
5574
5571
  },
5575
- "lastndays": {
5572
+ "chunksize": {
5573
+ "char": "c",
5574
+ "description": "Number of records to add in a chunk before it is processed",
5575
+ "name": "chunksize",
5576
+ "default": 1000,
5577
+ "hasDynamicHelp": false,
5578
+ "multiple": false,
5579
+ "type": "option"
5580
+ },
5581
+ "polltimeout": {
5576
5582
  "char": "t",
5577
- "description": "Number of days to extract from today (included)",
5578
- "name": "lastndays",
5583
+ "description": "Timeout in MS for Bulk API calls",
5584
+ "name": "polltimeout",
5585
+ "default": 300000,
5579
5586
  "hasDynamicHelp": false,
5580
5587
  "multiple": false,
5581
5588
  "type": "option"
5582
5589
  },
5583
- "outputfile": {
5584
- "char": "f",
5585
- "description": "Force the path and name of output report file. Must end with .csv",
5586
- "name": "outputfile",
5590
+ "startchunknumber": {
5591
+ "char": "s",
5592
+ "description": "Chunk number to start from",
5593
+ "name": "startchunknumber",
5594
+ "default": 0,
5587
5595
  "hasDynamicHelp": false,
5588
5596
  "multiple": false,
5589
5597
  "type": "option"
5590
5598
  },
5599
+ "resume": {
5600
+ "char": "r",
5601
+ "description": "Resume previous export by checking existing files (default in CI)",
5602
+ "name": "resume",
5603
+ "allowNo": false,
5604
+ "type": "boolean"
5605
+ },
5591
5606
  "debug": {
5592
5607
  "char": "d",
5593
5608
  "description": "Activate debug mode (more logs)",
@@ -5626,13 +5641,13 @@
5626
5641
  },
5627
5642
  "hasDynamicHelp": true,
5628
5643
  "hiddenAliases": [],
5629
- "id": "hardis:org:diagnose:audittrail",
5644
+ "id": "hardis:org:files:export",
5630
5645
  "pluginAlias": "sfdx-hardis",
5631
5646
  "pluginName": "sfdx-hardis",
5632
5647
  "pluginType": "core",
5633
5648
  "strict": true,
5634
5649
  "enableJsonFlag": true,
5635
- "title": "Diagnose content of Setup Audit Trail",
5650
+ "title": "Export files",
5636
5651
  "requiresProject": false,
5637
5652
  "isESM": true,
5638
5653
  "relativePath": [
@@ -5640,43 +5655,43 @@
5640
5655
  "commands",
5641
5656
  "hardis",
5642
5657
  "org",
5643
- "diagnose",
5644
- "audittrail.js"
5658
+ "files",
5659
+ "export.js"
5645
5660
  ],
5646
5661
  "aliasPermutations": [],
5647
5662
  "permutations": [
5648
- "hardis:org:diagnose:audittrail",
5649
- "org:hardis:diagnose:audittrail",
5650
- "org:diagnose:hardis:audittrail",
5651
- "org:diagnose:audittrail:hardis",
5652
- "hardis:diagnose:org:audittrail",
5653
- "diagnose:hardis:org:audittrail",
5654
- "diagnose:org:hardis:audittrail",
5655
- "diagnose:org:audittrail:hardis",
5656
- "hardis:diagnose:audittrail:org",
5657
- "diagnose:hardis:audittrail:org",
5658
- "diagnose:audittrail:hardis:org",
5659
- "diagnose:audittrail:org:hardis",
5660
- "hardis:org:audittrail:diagnose",
5661
- "org:hardis:audittrail:diagnose",
5662
- "org:audittrail:hardis:diagnose",
5663
- "org:audittrail:diagnose:hardis",
5664
- "hardis:audittrail:org:diagnose",
5665
- "audittrail:hardis:org:diagnose",
5666
- "audittrail:org:hardis:diagnose",
5667
- "audittrail:org:diagnose:hardis",
5668
- "hardis:audittrail:diagnose:org",
5669
- "audittrail:hardis:diagnose:org",
5670
- "audittrail:diagnose:hardis:org",
5671
- "audittrail:diagnose:org:hardis"
5663
+ "hardis:org:files:export",
5664
+ "org:hardis:files:export",
5665
+ "org:files:hardis:export",
5666
+ "org:files:export:hardis",
5667
+ "hardis:files:org:export",
5668
+ "files:hardis:org:export",
5669
+ "files:org:hardis:export",
5670
+ "files:org:export:hardis",
5671
+ "hardis:files:export:org",
5672
+ "files:hardis:export:org",
5673
+ "files:export:hardis:org",
5674
+ "files:export:org:hardis",
5675
+ "hardis:org:export:files",
5676
+ "org:hardis:export:files",
5677
+ "org:export:hardis:files",
5678
+ "org:export:files:hardis",
5679
+ "hardis:export:org:files",
5680
+ "export:hardis:org:files",
5681
+ "export:org:hardis:files",
5682
+ "export:org:files:hardis",
5683
+ "hardis:export:files:org",
5684
+ "export:hardis:files:org",
5685
+ "export:files:hardis:org",
5686
+ "export:files:org:hardis"
5672
5687
  ]
5673
5688
  },
5674
- "hardis:org:diagnose:instanceupgrade": {
5689
+ "hardis:org:files:import": {
5675
5690
  "aliases": [],
5676
5691
  "args": {},
5677
- "description": "\n## Command Behavior\n\n**Retrieves and displays the scheduled upgrade date for a Salesforce org's instance.**\n\nThis command provides crucial information about when your Salesforce instance will be upgraded to the next major release (Spring, Summer, or Winter). This is vital for release planning, testing, and ensuring compatibility with upcoming Salesforce features.\n\nKey functionalities:\n\n- **Instance Identification:** Determines the Salesforce instance name of your target org.\n- **Upgrade Date Retrieval:** Fetches the planned start time of the next major core service upgrade for that instance from the Salesforce Status API.\n- **Days Until Upgrade:** Calculates and displays the number of days remaining until the next major upgrade.\n- **Severity-Based Logging:** Adjusts the log severity (info, warning) based on the proximity of the upgrade date, providing a visual cue for urgency.\n- **Notifications:** Sends notifications to configured channels (e.g., Slack, MS Teams, Grafana) with the upgrade information, making it suitable for automated monitoring.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Query:** It first queries the `Organization` object in Salesforce to get the `InstanceName` of the target org.\n- **Salesforce Status API Integration:** It makes an HTTP GET request to the Salesforce Status API (`https://api.status.salesforce.com/v1/instances/{instanceName}/status`) to retrieve detailed information about the instance, including scheduled maintenances.\n- **Data Parsing:** It parses the JSON response from the Status API to extract the relevant major release upgrade information.\n- **Date Calculation:** Uses the `moment` library to calculate the difference in days between the current date and the planned upgrade date.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including the instance name, upgrade date, and days remaining, along with relevant metrics for monitoring dashboards.\n- **User Feedback:** Provides clear messages to the user about the upgrade status and proximity.\n</details>\n",
5692
+ "description": "\nThis command facilitates the mass upload of files into Salesforce, allowing you to populate records with associated documents, images, or other file types. It's a crucial tool for data migration, content seeding, or synchronizing external file repositories with Salesforce.\n\nKey functionalities:\n\n- **Configuration-Driven Import:** Relies on an `export.json` file within a designated file export project (created using `sf hardis:org:configure:files`) to determine which files to import and how they should be associated with Salesforce records.\n- **Interactive Project Selection:** If the file import project path is not provided via the `--path` flag, it interactively prompts the user to select one.\n- **Overwrite Option:** The `--overwrite` flag allows you to replace existing files in Salesforce with local versions that have the same name. Be aware that this option doubles the number of API calls used.\n- **Support for ContentVersion and Attachment:** Handles both modern Salesforce Files (ContentVersion) and older Attachments.\n\nSee this article for how to export files, which is often a prerequisite for importing:\n\n[![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **FilesImporter Class:** The core logic is encapsulated within the `FilesImporter` class, which orchestrates the entire import process.\n- **File System Scan:** Scans the local file system within the configured project directory to identify files for import.\n- **Salesforce API Interaction:** Uses Salesforce APIs (e.g., ContentVersion, Attachment) to upload files and associate them with records.\n- **Configuration Loading:** Reads the `export.json` file to get the import configuration, including SOQL queries to identify parent records for file association.\n- **Interactive Prompts:** Uses `selectFilesWorkspace` to allow the user to choose a file import project and `prompts` for confirming the overwrite behavior.\n- **Error Handling:** Includes mechanisms to handle potential errors during the import process, such as API limits or file upload failures.\n</details>\n",
5678
5693
  "examples": [
5679
- "$ sf hardis:org:diagnose:instanceupgrade"
5694
+ "$ sf hardis:org:files:import"
5680
5695
  ],
5681
5696
  "flags": {
5682
5697
  "json": {
@@ -5694,6 +5709,21 @@
5694
5709
  "multiple": false,
5695
5710
  "type": "option"
5696
5711
  },
5712
+ "path": {
5713
+ "char": "p",
5714
+ "description": "Path to the file export project",
5715
+ "name": "path",
5716
+ "hasDynamicHelp": false,
5717
+ "multiple": false,
5718
+ "type": "option"
5719
+ },
5720
+ "overwrite": {
5721
+ "char": "f",
5722
+ "description": "Override existing files (doubles the number of API calls)",
5723
+ "name": "overwrite",
5724
+ "allowNo": false,
5725
+ "type": "boolean"
5726
+ },
5697
5727
  "debug": {
5698
5728
  "char": "d",
5699
5729
  "description": "Activate debug mode (more logs)",
@@ -5732,13 +5762,13 @@
5732
5762
  },
5733
5763
  "hasDynamicHelp": true,
5734
5764
  "hiddenAliases": [],
5735
- "id": "hardis:org:diagnose:instanceupgrade",
5765
+ "id": "hardis:org:files:import",
5736
5766
  "pluginAlias": "sfdx-hardis",
5737
5767
  "pluginName": "sfdx-hardis",
5738
5768
  "pluginType": "core",
5739
5769
  "strict": true,
5740
5770
  "enableJsonFlag": true,
5741
- "title": "Get Instance Upgrade date",
5771
+ "title": "Import files",
5742
5772
  "requiresProject": false,
5743
5773
  "isESM": true,
5744
5774
  "relativePath": [
@@ -5746,46 +5776,46 @@
5746
5776
  "commands",
5747
5777
  "hardis",
5748
5778
  "org",
5749
- "diagnose",
5750
- "instanceupgrade.js"
5779
+ "files",
5780
+ "import.js"
5751
5781
  ],
5752
5782
  "aliasPermutations": [],
5753
5783
  "permutations": [
5754
- "hardis:org:diagnose:instanceupgrade",
5755
- "org:hardis:diagnose:instanceupgrade",
5756
- "org:diagnose:hardis:instanceupgrade",
5757
- "org:diagnose:instanceupgrade:hardis",
5758
- "hardis:diagnose:org:instanceupgrade",
5759
- "diagnose:hardis:org:instanceupgrade",
5760
- "diagnose:org:hardis:instanceupgrade",
5761
- "diagnose:org:instanceupgrade:hardis",
5762
- "hardis:diagnose:instanceupgrade:org",
5763
- "diagnose:hardis:instanceupgrade:org",
5764
- "diagnose:instanceupgrade:hardis:org",
5765
- "diagnose:instanceupgrade:org:hardis",
5766
- "hardis:org:instanceupgrade:diagnose",
5767
- "org:hardis:instanceupgrade:diagnose",
5768
- "org:instanceupgrade:hardis:diagnose",
5769
- "org:instanceupgrade:diagnose:hardis",
5770
- "hardis:instanceupgrade:org:diagnose",
5771
- "instanceupgrade:hardis:org:diagnose",
5772
- "instanceupgrade:org:hardis:diagnose",
5773
- "instanceupgrade:org:diagnose:hardis",
5774
- "hardis:instanceupgrade:diagnose:org",
5775
- "instanceupgrade:hardis:diagnose:org",
5776
- "instanceupgrade:diagnose:hardis:org",
5777
- "instanceupgrade:diagnose:org:hardis"
5784
+ "hardis:org:files:import",
5785
+ "org:hardis:files:import",
5786
+ "org:files:hardis:import",
5787
+ "org:files:import:hardis",
5788
+ "hardis:files:org:import",
5789
+ "files:hardis:org:import",
5790
+ "files:org:hardis:import",
5791
+ "files:org:import:hardis",
5792
+ "hardis:files:import:org",
5793
+ "files:hardis:import:org",
5794
+ "files:import:hardis:org",
5795
+ "files:import:org:hardis",
5796
+ "hardis:org:import:files",
5797
+ "org:hardis:import:files",
5798
+ "org:import:hardis:files",
5799
+ "org:import:files:hardis",
5800
+ "hardis:import:org:files",
5801
+ "import:hardis:org:files",
5802
+ "import:org:hardis:files",
5803
+ "import:org:files:hardis",
5804
+ "hardis:import:files:org",
5805
+ "import:hardis:files:org",
5806
+ "import:files:hardis:org",
5807
+ "import:files:org:hardis"
5778
5808
  ]
5779
5809
  },
5780
- "hardis:org:diagnose:legacyapi": {
5810
+ "hardis:org:diagnose:audittrail": {
5781
5811
  "aliases": [],
5782
5812
  "args": {},
5783
- "description": "Checks if an org uses retired or someday retired API version\n\n\nSee article below\n\n[![Handle Salesforce API versions Deprecation like a pro](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deprecated-api.jpg)](https://nicolas.vuillamy.fr/handle-salesforce-api-versions-deprecation-like-a-pro-335065f52238)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-deprecated-api-calls/) and can output Grafana, Slack and MsTeams Notifications.\n",
5813
+ "description": "Export Audit trail into a CSV file with selected criteria, and highlight suspect actions\n\nAlso detects updates of Custom Settings values (disable by defining `SKIP_AUDIT_TRAIL_CUSTOM_SETTINGS=true`)\n\nRegular setup actions performed in major orgs are filtered.\n\n- \"\"\n - createScratchOrg\n - changedsenderemail\n - deleteScratchOrg\n - loginasgrantedtopartnerbt\n- Certificate and Key Management\n - insertCertificate\n- Custom App Licenses\n - addeduserpackagelicense\n - granteduserpackagelicense\n - revokeduserpackagelicense\n- Customer Portal\n - createdcustomersuccessuser\n - CSPUserDisabled\n- Currency\n - updateddatedexchrate\n- Data Management\n - queueMembership\n- Email Administration\n - dkimRotationPreparationSuccessful\n - dkimRotationSuccessful\n- External Objects\n - xdsEncryptedFieldChange\n- Groups\n - groupMembership\n- Holidays\n - holiday_insert\n- Inbox mobile and legacy desktop apps\n - enableSIQUserNonEAC\n - siqUserAcceptedTOS\n- Manage Users\n - activateduser\n - createduser\n - changedcommunitynickname\n - changedemail\n - changedfederationid\n - changedpassword\n - changedinteractionuseroffon\n - changedinteractionuseronoff\n - changedmarketinguseroffon\n - changedmarketinguseronoff\n - changedofflineuseroffon\n - changedprofileforuserstdtostd\n - changedprofileforuser\n - changedprofileforusercusttostd\n - changedprofileforuserstdtocust\n - changedroleforusertonone\n - changedroleforuser\n - changedroleforuserfromnone\n - changedUserAdminVerifiedStatusVerified\n - changedUserEmailVerifiedStatusUnverified\n - changedUserEmailVerifiedStatusVerified\n - changedknowledgeuseroffon\n - changedsfcontentuseroffon\n - changedsupportuseroffon\n - changedusername\n - changedUserPhoneNumber\n - changedUserPhoneVerifiedStatusUnverified\n - changedUserPhoneVerifiedStatusVerified\n - deactivateduser\n - deleteAuthenticatorPairing\n - deleteTwoFactorInfo2\n - deleteTwoFactorTempCode\n - frozeuser\n - insertAuthenticatorPairing\n - insertTwoFactorInfo2\n - insertTwoFactorTempCode\n - lightningloginenroll\n - PermSetAssign\n - PermSetGroupAssign\n - PermSetGroupUnassign\n - PermSetLicenseAssign\n - PermSetUnassign\n - PermSetLicenseUnassign\n - registeredUserPhoneNumber\n - resetpassword\n - suNetworkAdminLogin\n - suNetworkAdminLogout\n - suOrgAdminLogin\n - suOrgAdminLogout\n - unfrozeuser\n - useremailchangesent\n- Mobile Administration\n - assigneduserstomobileconfig\n- Reporting Snapshots\n - createdReportJob\n - deletedReportJob\n- Sandboxes\n - DeleteSandbox\n\nBy default, deployment user defined in .sfdx-hardis.yml targetUsername property will be excluded.\n\nYou can define additional users to exclude in .sfdx-hardis.yml **monitoringExcludeUsernames** property.\n\nYou can also add more sections / actions considered as not suspect using property **monitoringAllowedSectionsActions**\n\nExample:\n\n```yaml\nmonitoringExcludeUsernames:\n - deploymentuser@cloudity.com\n - marketingcloud@cloudity.com\n - integration-user@cloudity.com\n\nmonitoringAllowedSectionsActions:\n \"Some section\": [] // Will ignore all actions from such section\n \"Some other section\": [\"actionType1\",\"actionType2\",\"actionType3\"] // Will ignore only those 3 actions from section \"Some other section\". Other actions in the same section will be considered as suspect.\n```\n\n## Excel output example\n\n![](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-monitoring-audittrail-excel.jpg)\n\n## Local output example\n\n![](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/screenshot-monitoring-audittrail-local.jpg)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-suspect-audit-trail/) and can output Grafana, Slack and MsTeams Notifications.\n",
5784
5814
  "examples": [
5785
- "$ sf hardis:org:diagnose:legacyapi",
5786
- "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com",
5787
- "$ sf hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv'",
5788
- "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv"
5815
+ "$ sf hardis:org:diagnose:audittrail",
5816
+ "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com",
5817
+ "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com",
5818
+ "$ sf hardis:org:diagnose:audittrail --lastndays 5"
5789
5819
  ],
5790
5820
  "flags": {
5791
5821
  "json": {
@@ -5803,20 +5833,18 @@
5803
5833
  "multiple": false,
5804
5834
  "type": "option"
5805
5835
  },
5806
- "eventtype": {
5836
+ "excludeusers": {
5807
5837
  "char": "e",
5808
- "description": "Type of EventLogFile event to analyze",
5809
- "name": "eventtype",
5810
- "default": "ApiTotalUsage",
5838
+ "description": "Comma-separated list of usernames to exclude",
5839
+ "name": "excludeusers",
5811
5840
  "hasDynamicHelp": false,
5812
5841
  "multiple": false,
5813
5842
  "type": "option"
5814
5843
  },
5815
- "limit": {
5816
- "char": "l",
5817
- "description": "Number of latest EventLogFile events to analyze",
5818
- "name": "limit",
5819
- "default": 999,
5844
+ "lastndays": {
5845
+ "char": "t",
5846
+ "description": "Number of days to extract from today (included)",
5847
+ "name": "lastndays",
5820
5848
  "hasDynamicHelp": false,
5821
5849
  "multiple": false,
5822
5850
  "type": "option"
@@ -5867,13 +5895,13 @@
5867
5895
  },
5868
5896
  "hasDynamicHelp": true,
5869
5897
  "hiddenAliases": [],
5870
- "id": "hardis:org:diagnose:legacyapi",
5898
+ "id": "hardis:org:diagnose:audittrail",
5871
5899
  "pluginAlias": "sfdx-hardis",
5872
5900
  "pluginName": "sfdx-hardis",
5873
5901
  "pluginType": "core",
5874
5902
  "strict": true,
5875
5903
  "enableJsonFlag": true,
5876
- "title": "Check for legacy API use",
5904
+ "title": "Diagnose content of Setup Audit Trail",
5877
5905
  "requiresProject": false,
5878
5906
  "isESM": true,
5879
5907
  "relativePath": [
@@ -5882,50 +5910,50 @@
5882
5910
  "hardis",
5883
5911
  "org",
5884
5912
  "diagnose",
5885
- "legacyapi.js"
5913
+ "audittrail.js"
5886
5914
  ],
5887
5915
  "aliasPermutations": [],
5888
5916
  "permutations": [
5889
- "hardis:org:diagnose:legacyapi",
5890
- "org:hardis:diagnose:legacyapi",
5891
- "org:diagnose:hardis:legacyapi",
5892
- "org:diagnose:legacyapi:hardis",
5893
- "hardis:diagnose:org:legacyapi",
5894
- "diagnose:hardis:org:legacyapi",
5895
- "diagnose:org:hardis:legacyapi",
5896
- "diagnose:org:legacyapi:hardis",
5897
- "hardis:diagnose:legacyapi:org",
5898
- "diagnose:hardis:legacyapi:org",
5899
- "diagnose:legacyapi:hardis:org",
5900
- "diagnose:legacyapi:org:hardis",
5901
- "hardis:org:legacyapi:diagnose",
5902
- "org:hardis:legacyapi:diagnose",
5903
- "org:legacyapi:hardis:diagnose",
5904
- "org:legacyapi:diagnose:hardis",
5905
- "hardis:legacyapi:org:diagnose",
5906
- "legacyapi:hardis:org:diagnose",
5907
- "legacyapi:org:hardis:diagnose",
5908
- "legacyapi:org:diagnose:hardis",
5909
- "hardis:legacyapi:diagnose:org",
5910
- "legacyapi:hardis:diagnose:org",
5911
- "legacyapi:diagnose:hardis:org",
5912
- "legacyapi:diagnose:org:hardis"
5913
- ]
5914
- },
5915
- "hardis:org:diagnose:licenses": {
5916
- "aliases": [],
5917
- "args": {},
5918
- "description": "\n**Lists and analyzes User Licenses and Permission Set Licenses subscribed and used in a Salesforce org.**\n\nThis command provides a comprehensive overview of your Salesforce license consumption. It's particularly useful for:\n\n- **License Management:** Understanding which licenses are active, how many are available, and how many are being used.\n- **Cost Optimization:** Identifying unused or underutilized licenses that could be reallocated or decommissioned.\n- **Compliance:** Ensuring that your organization is compliant with Salesforce licensing agreements.\n- **Monitoring:** Tracking license usage trends over time.\n\nKey functionalities:\n\n- **User License Details:** Retrieves information about standard and custom User Licenses, including `MasterLabel`, `Name`, `TotalLicenses`, and `UsedLicenses`.\n- **Permission Set License Details:** Retrieves information about Permission Set Licenses, including `MasterLabel`, `PermissionSetLicenseKey`, `TotalLicenses`, and `UsedLicenses`.\n- **Used Licenses Filter:** The `--usedonly` flag allows you to filter the report to show only licenses that have at least one `UsedLicenses` count greater than zero.\n- **CSV Report Generation:** Generates a CSV file containing all the retrieved license information, suitable for detailed analysis.\n- **Notifications:** Sends notifications to configured channels (e.g., Grafana, Slack, MS Teams) with a summary of license usage, including lists of active and used licenses.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Queries:** It executes SOQL queries against the `UserLicense` and `PermissionSetLicense` objects in Salesforce to retrieve license data.\n- **Data Transformation:** It processes the query results, reformatting the data to be more readable and consistent for reporting purposes (e.g., removing `Id` and `attributes`, renaming `PermissionSetLicenseKey` to `Name`).\n- **Data Aggregation:** It aggregates license information, creating a `licensesByKey` object for quick lookups and a `usedLicenses` array for a concise list of actively used licenses.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of license data.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Feedback:** Provides clear messages to the user about the license extraction process and the used licenses.\n</details>\n",
5919
- "examples": [
5920
- "$ sf hardis:org:diagnose:licenses"
5921
- ],
5922
- "flags": {
5923
- "json": {
5924
- "description": "Format output as json.",
5925
- "helpGroup": "GLOBAL",
5926
- "name": "json",
5927
- "allowNo": false,
5928
- "type": "boolean"
5917
+ "hardis:org:diagnose:audittrail",
5918
+ "org:hardis:diagnose:audittrail",
5919
+ "org:diagnose:hardis:audittrail",
5920
+ "org:diagnose:audittrail:hardis",
5921
+ "hardis:diagnose:org:audittrail",
5922
+ "diagnose:hardis:org:audittrail",
5923
+ "diagnose:org:hardis:audittrail",
5924
+ "diagnose:org:audittrail:hardis",
5925
+ "hardis:diagnose:audittrail:org",
5926
+ "diagnose:hardis:audittrail:org",
5927
+ "diagnose:audittrail:hardis:org",
5928
+ "diagnose:audittrail:org:hardis",
5929
+ "hardis:org:audittrail:diagnose",
5930
+ "org:hardis:audittrail:diagnose",
5931
+ "org:audittrail:hardis:diagnose",
5932
+ "org:audittrail:diagnose:hardis",
5933
+ "hardis:audittrail:org:diagnose",
5934
+ "audittrail:hardis:org:diagnose",
5935
+ "audittrail:org:hardis:diagnose",
5936
+ "audittrail:org:diagnose:hardis",
5937
+ "hardis:audittrail:diagnose:org",
5938
+ "audittrail:hardis:diagnose:org",
5939
+ "audittrail:diagnose:hardis:org",
5940
+ "audittrail:diagnose:org:hardis"
5941
+ ]
5942
+ },
5943
+ "hardis:org:diagnose:instanceupgrade": {
5944
+ "aliases": [],
5945
+ "args": {},
5946
+ "description": "\n## Command Behavior\n\n**Retrieves and displays the scheduled upgrade date for a Salesforce org's instance.**\n\nThis command provides crucial information about when your Salesforce instance will be upgraded to the next major release (Spring, Summer, or Winter). This is vital for release planning, testing, and ensuring compatibility with upcoming Salesforce features.\n\nKey functionalities:\n\n- **Instance Identification:** Determines the Salesforce instance name of your target org.\n- **Upgrade Date Retrieval:** Fetches the planned start time of the next major core service upgrade for that instance from the Salesforce Status API.\n- **Days Until Upgrade:** Calculates and displays the number of days remaining until the next major upgrade.\n- **Severity-Based Logging:** Adjusts the log severity (info, warning) based on the proximity of the upgrade date, providing a visual cue for urgency.\n- **Notifications:** Sends notifications to configured channels (e.g., Slack, MS Teams, Grafana) with the upgrade information, making it suitable for automated monitoring.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Query:** It first queries the `Organization` object in Salesforce to get the `InstanceName` of the target org.\n- **Salesforce Status API Integration:** It makes an HTTP GET request to the Salesforce Status API (`https://api.status.salesforce.com/v1/instances/{instanceName}/status`) to retrieve detailed information about the instance, including scheduled maintenances.\n- **Data Parsing:** It parses the JSON response from the Status API to extract the relevant major release upgrade information.\n- **Date Calculation:** Uses the `moment` library to calculate the difference in days between the current date and the planned upgrade date.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including the instance name, upgrade date, and days remaining, along with relevant metrics for monitoring dashboards.\n- **User Feedback:** Provides clear messages to the user about the upgrade status and proximity.\n</details>\n",
5947
+ "examples": [
5948
+ "$ sf hardis:org:diagnose:instanceupgrade"
5949
+ ],
5950
+ "flags": {
5951
+ "json": {
5952
+ "description": "Format output as json.",
5953
+ "helpGroup": "GLOBAL",
5954
+ "name": "json",
5955
+ "allowNo": false,
5956
+ "type": "boolean"
5929
5957
  },
5930
5958
  "flags-dir": {
5931
5959
  "helpGroup": "GLOBAL",
@@ -5935,21 +5963,6 @@
5935
5963
  "multiple": false,
5936
5964
  "type": "option"
5937
5965
  },
5938
- "outputfile": {
5939
- "char": "f",
5940
- "description": "Force the path and name of output report file. Must end with .csv",
5941
- "name": "outputfile",
5942
- "hasDynamicHelp": false,
5943
- "multiple": false,
5944
- "type": "option"
5945
- },
5946
- "usedonly": {
5947
- "char": "u",
5948
- "description": "Filter to have only used licenses",
5949
- "name": "usedonly",
5950
- "allowNo": false,
5951
- "type": "boolean"
5952
- },
5953
5966
  "debug": {
5954
5967
  "char": "d",
5955
5968
  "description": "Activate debug mode (more logs)",
@@ -5988,13 +6001,13 @@
5988
6001
  },
5989
6002
  "hasDynamicHelp": true,
5990
6003
  "hiddenAliases": [],
5991
- "id": "hardis:org:diagnose:licenses",
6004
+ "id": "hardis:org:diagnose:instanceupgrade",
5992
6005
  "pluginAlias": "sfdx-hardis",
5993
6006
  "pluginName": "sfdx-hardis",
5994
6007
  "pluginType": "core",
5995
6008
  "strict": true,
5996
6009
  "enableJsonFlag": true,
5997
- "title": "List licenses subscribed and used in a Salesforce org",
6010
+ "title": "Get Instance Upgrade date",
5998
6011
  "requiresProject": false,
5999
6012
  "isESM": true,
6000
6013
  "relativePath": [
@@ -6003,42 +6016,45 @@
6003
6016
  "hardis",
6004
6017
  "org",
6005
6018
  "diagnose",
6006
- "licenses.js"
6019
+ "instanceupgrade.js"
6007
6020
  ],
6008
6021
  "aliasPermutations": [],
6009
6022
  "permutations": [
6010
- "hardis:org:diagnose:licenses",
6011
- "org:hardis:diagnose:licenses",
6012
- "org:diagnose:hardis:licenses",
6013
- "org:diagnose:licenses:hardis",
6014
- "hardis:diagnose:org:licenses",
6015
- "diagnose:hardis:org:licenses",
6016
- "diagnose:org:hardis:licenses",
6017
- "diagnose:org:licenses:hardis",
6018
- "hardis:diagnose:licenses:org",
6019
- "diagnose:hardis:licenses:org",
6020
- "diagnose:licenses:hardis:org",
6021
- "diagnose:licenses:org:hardis",
6022
- "hardis:org:licenses:diagnose",
6023
- "org:hardis:licenses:diagnose",
6024
- "org:licenses:hardis:diagnose",
6025
- "org:licenses:diagnose:hardis",
6026
- "hardis:licenses:org:diagnose",
6027
- "licenses:hardis:org:diagnose",
6028
- "licenses:org:hardis:diagnose",
6029
- "licenses:org:diagnose:hardis",
6030
- "hardis:licenses:diagnose:org",
6031
- "licenses:hardis:diagnose:org",
6032
- "licenses:diagnose:hardis:org",
6033
- "licenses:diagnose:org:hardis"
6023
+ "hardis:org:diagnose:instanceupgrade",
6024
+ "org:hardis:diagnose:instanceupgrade",
6025
+ "org:diagnose:hardis:instanceupgrade",
6026
+ "org:diagnose:instanceupgrade:hardis",
6027
+ "hardis:diagnose:org:instanceupgrade",
6028
+ "diagnose:hardis:org:instanceupgrade",
6029
+ "diagnose:org:hardis:instanceupgrade",
6030
+ "diagnose:org:instanceupgrade:hardis",
6031
+ "hardis:diagnose:instanceupgrade:org",
6032
+ "diagnose:hardis:instanceupgrade:org",
6033
+ "diagnose:instanceupgrade:hardis:org",
6034
+ "diagnose:instanceupgrade:org:hardis",
6035
+ "hardis:org:instanceupgrade:diagnose",
6036
+ "org:hardis:instanceupgrade:diagnose",
6037
+ "org:instanceupgrade:hardis:diagnose",
6038
+ "org:instanceupgrade:diagnose:hardis",
6039
+ "hardis:instanceupgrade:org:diagnose",
6040
+ "instanceupgrade:hardis:org:diagnose",
6041
+ "instanceupgrade:org:hardis:diagnose",
6042
+ "instanceupgrade:org:diagnose:hardis",
6043
+ "hardis:instanceupgrade:diagnose:org",
6044
+ "instanceupgrade:hardis:diagnose:org",
6045
+ "instanceupgrade:diagnose:hardis:org",
6046
+ "instanceupgrade:diagnose:org:hardis"
6034
6047
  ]
6035
6048
  },
6036
- "hardis:org:diagnose:releaseupdates": {
6049
+ "hardis:org:diagnose:legacyapi": {
6037
6050
  "aliases": [],
6038
6051
  "args": {},
6039
- "description": "Export Release Updates into a CSV file with selected criteria, and highlight Release Updates that should be checked.\n\nBefore publishing **Breaking Changes** ❌, Salesforce announce them in the setup menu [**Release Updates**](https://help.salesforce.com/s/articleView?id=sf.release_updates.htm&type=5)\n\n⚠️ Some of them are very important, because if you don't make the related upgrades in time (ex: before Winter 25) , your production org can crash !\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-release-updates/) and can output Grafana, Slack and MsTeams Notifications.\n",
6052
+ "description": "Checks if an org uses retired or someday retired API version\n\n\nSee article below\n\n[![Handle Salesforce API versions Deprecation like a pro](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-deprecated-api.jpg)](https://nicolas.vuillamy.fr/handle-salesforce-api-versions-deprecation-like-a-pro-335065f52238)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-deprecated-api-calls/) and can output Grafana, Slack and MsTeams Notifications.\n",
6040
6053
  "examples": [
6041
- "$ sf hardis:org:diagnose:releaseupdates"
6054
+ "$ sf hardis:org:diagnose:legacyapi",
6055
+ "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com",
6056
+ "$ sf hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv'",
6057
+ "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv"
6042
6058
  ],
6043
6059
  "flags": {
6044
6060
  "json": {
@@ -6056,6 +6072,24 @@
6056
6072
  "multiple": false,
6057
6073
  "type": "option"
6058
6074
  },
6075
+ "eventtype": {
6076
+ "char": "e",
6077
+ "description": "Type of EventLogFile event to analyze",
6078
+ "name": "eventtype",
6079
+ "default": "ApiTotalUsage",
6080
+ "hasDynamicHelp": false,
6081
+ "multiple": false,
6082
+ "type": "option"
6083
+ },
6084
+ "limit": {
6085
+ "char": "l",
6086
+ "description": "Number of latest EventLogFile events to analyze",
6087
+ "name": "limit",
6088
+ "default": 999,
6089
+ "hasDynamicHelp": false,
6090
+ "multiple": false,
6091
+ "type": "option"
6092
+ },
6059
6093
  "outputfile": {
6060
6094
  "char": "f",
6061
6095
  "description": "Force the path and name of output report file. Must end with .csv",
@@ -6102,13 +6136,13 @@
6102
6136
  },
6103
6137
  "hasDynamicHelp": true,
6104
6138
  "hiddenAliases": [],
6105
- "id": "hardis:org:diagnose:releaseupdates",
6139
+ "id": "hardis:org:diagnose:legacyapi",
6106
6140
  "pluginAlias": "sfdx-hardis",
6107
6141
  "pluginName": "sfdx-hardis",
6108
6142
  "pluginType": "core",
6109
6143
  "strict": true,
6110
6144
  "enableJsonFlag": true,
6111
- "title": "Check Release Updates of an org",
6145
+ "title": "Check for legacy API use",
6112
6146
  "requiresProject": false,
6113
6147
  "isESM": true,
6114
6148
  "relativePath": [
@@ -6117,42 +6151,42 @@
6117
6151
  "hardis",
6118
6152
  "org",
6119
6153
  "diagnose",
6120
- "releaseupdates.js"
6154
+ "legacyapi.js"
6121
6155
  ],
6122
6156
  "aliasPermutations": [],
6123
6157
  "permutations": [
6124
- "hardis:org:diagnose:releaseupdates",
6125
- "org:hardis:diagnose:releaseupdates",
6126
- "org:diagnose:hardis:releaseupdates",
6127
- "org:diagnose:releaseupdates:hardis",
6128
- "hardis:diagnose:org:releaseupdates",
6129
- "diagnose:hardis:org:releaseupdates",
6130
- "diagnose:org:hardis:releaseupdates",
6131
- "diagnose:org:releaseupdates:hardis",
6132
- "hardis:diagnose:releaseupdates:org",
6133
- "diagnose:hardis:releaseupdates:org",
6134
- "diagnose:releaseupdates:hardis:org",
6135
- "diagnose:releaseupdates:org:hardis",
6136
- "hardis:org:releaseupdates:diagnose",
6137
- "org:hardis:releaseupdates:diagnose",
6138
- "org:releaseupdates:hardis:diagnose",
6139
- "org:releaseupdates:diagnose:hardis",
6140
- "hardis:releaseupdates:org:diagnose",
6141
- "releaseupdates:hardis:org:diagnose",
6142
- "releaseupdates:org:hardis:diagnose",
6143
- "releaseupdates:org:diagnose:hardis",
6144
- "hardis:releaseupdates:diagnose:org",
6145
- "releaseupdates:hardis:diagnose:org",
6146
- "releaseupdates:diagnose:hardis:org",
6147
- "releaseupdates:diagnose:org:hardis"
6158
+ "hardis:org:diagnose:legacyapi",
6159
+ "org:hardis:diagnose:legacyapi",
6160
+ "org:diagnose:hardis:legacyapi",
6161
+ "org:diagnose:legacyapi:hardis",
6162
+ "hardis:diagnose:org:legacyapi",
6163
+ "diagnose:hardis:org:legacyapi",
6164
+ "diagnose:org:hardis:legacyapi",
6165
+ "diagnose:org:legacyapi:hardis",
6166
+ "hardis:diagnose:legacyapi:org",
6167
+ "diagnose:hardis:legacyapi:org",
6168
+ "diagnose:legacyapi:hardis:org",
6169
+ "diagnose:legacyapi:org:hardis",
6170
+ "hardis:org:legacyapi:diagnose",
6171
+ "org:hardis:legacyapi:diagnose",
6172
+ "org:legacyapi:hardis:diagnose",
6173
+ "org:legacyapi:diagnose:hardis",
6174
+ "hardis:legacyapi:org:diagnose",
6175
+ "legacyapi:hardis:org:diagnose",
6176
+ "legacyapi:org:hardis:diagnose",
6177
+ "legacyapi:org:diagnose:hardis",
6178
+ "hardis:legacyapi:diagnose:org",
6179
+ "legacyapi:hardis:diagnose:org",
6180
+ "legacyapi:diagnose:hardis:org",
6181
+ "legacyapi:diagnose:org:hardis"
6148
6182
  ]
6149
6183
  },
6150
- "hardis:org:diagnose:unsecure-connected-apps": {
6184
+ "hardis:org:diagnose:licenses": {
6151
6185
  "aliases": [],
6152
6186
  "args": {},
6153
- "description": "\n## Command Behavior\n\n**Detects unsecured Connected Apps in a Salesforce org and generates detailed reports for security analysis.**\n\nThis command is a critical security diagnostic tool that helps administrators identify Connected Apps that may pose security risks due to improper configuration. It provides comprehensive analysis of OAuth tokens and Connected App security settings to ensure proper access control.\n\nKey functionalities:\n\n- **OAuth Token Analysis:** Queries all OAuth tokens in the org using SOQL to retrieve comprehensive token information including app names, users, authorization status, and usage statistics.\n- **Security Status Assessment:** Evaluates each Connected App's security configuration by checking the `IsUsingAdminAuthorization` flag to determine if admin pre-approval is required.\n- **Unsecured App Detection:** Identifies Connected Apps that allow users to authorize themselves without admin approval, which can pose security risks.\n- **Detailed Reporting:** Generates two comprehensive CSV reports:\n - **OAuth Tokens Report:** Lists all OAuth tokens with security status, user information, and usage data\n - **Connected Apps Summary:** Aggregates unsecured Connected Apps with counts of associated OAuth tokens\n- **Visual Indicators:** Uses status icons (❌ for unsecured, for secured) to provide immediate visual feedback on security status.\n- **Security Recommendations:** Provides actionable guidance on how to secure Connected Apps through proper configuration.\n- **Notifications:** Sends alerts to configured channels (Grafana, Slack, MS Teams) with security findings and attached reports.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-security/) and can output Grafana, Slack and MsTeams Notifications.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/jHv8yrSK8Dg\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query Execution:** Executes a comprehensive SOQL query on the `OauthToken` object, joining with `AppMenuItem` and `User` objects to gather complete security context.\n- **Security Analysis Logic:** Analyzes the `AppMenuItem.IsUsingAdminAuthorization` field to determine if a Connected App requires admin pre-approval for user authorization.\n- **Data Transformation:** Processes raw SOQL results to add security status indicators and reorganize data for optimal reporting and analysis.\n- **Aggregation Processing:** Groups OAuth tokens by Connected App name to provide summary statistics and identify the most problematic applications.\n- **Report Generation:** Uses `generateCsvFile` to create structured CSV reports with proper formatting and metadata for easy analysis and sharing.\n- **Notification Integration:** Integrates with the `NotifProvider` to send security alerts with detailed metrics, including the number of unsecured Connected Apps and associated OAuth tokens.\n- **File Management:** Generates multiple output formats (CSV, XLSX) and manages file paths using `generateReportPath` for consistent report organization.\n- **Connection Management:** Uses `setConnectionVariables` to ensure proper authentication context for notification providers that require org connection details.\n</details>\n",
6187
+ "description": "\n**Lists and analyzes User Licenses and Permission Set Licenses subscribed and used in a Salesforce org.**\n\nThis command provides a comprehensive overview of your Salesforce license consumption. It's particularly useful for:\n\n- **License Management:** Understanding which licenses are active, how many are available, and how many are being used.\n- **Cost Optimization:** Identifying unused or underutilized licenses that could be reallocated or decommissioned.\n- **Compliance:** Ensuring that your organization is compliant with Salesforce licensing agreements.\n- **Monitoring:** Tracking license usage trends over time.\n\nKey functionalities:\n\n- **User License Details:** Retrieves information about standard and custom User Licenses, including `MasterLabel`, `Name`, `TotalLicenses`, and `UsedLicenses`.\n- **Permission Set License Details:** Retrieves information about Permission Set Licenses, including `MasterLabel`, `PermissionSetLicenseKey`, `TotalLicenses`, and `UsedLicenses`.\n- **Used Licenses Filter:** The `--usedonly` flag allows you to filter the report to show only licenses that have at least one `UsedLicenses` count greater than zero.\n- **CSV Report Generation:** Generates a CSV file containing all the retrieved license information, suitable for detailed analysis.\n- **Notifications:** Sends notifications to configured channels (e.g., Grafana, Slack, MS Teams) with a summary of license usage, including lists of active and used licenses.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Queries:** It executes SOQL queries against the `UserLicense` and `PermissionSetLicense` objects in Salesforce to retrieve license data.\n- **Data Transformation:** It processes the query results, reformatting the data to be more readable and consistent for reporting purposes (e.g., removing `Id` and `attributes`, renaming `PermissionSetLicenseKey` to `Name`).\n- **Data Aggregation:** It aggregates license information, creating a `licensesByKey` object for quick lookups and a `usedLicenses` array for a concise list of actively used licenses.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of license data.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Feedback:** Provides clear messages to the user about the license extraction process and the used licenses.\n</details>\n",
6154
6188
  "examples": [
6155
- "$ sf hardis:org:diagnose:unsecure-connected-apps"
6189
+ "$ sf hardis:org:diagnose:licenses"
6156
6190
  ],
6157
6191
  "flags": {
6158
6192
  "json": {
@@ -6178,6 +6212,13 @@
6178
6212
  "multiple": false,
6179
6213
  "type": "option"
6180
6214
  },
6215
+ "usedonly": {
6216
+ "char": "u",
6217
+ "description": "Filter to have only used licenses",
6218
+ "name": "usedonly",
6219
+ "allowNo": false,
6220
+ "type": "boolean"
6221
+ },
6181
6222
  "debug": {
6182
6223
  "char": "d",
6183
6224
  "description": "Activate debug mode (more logs)",
@@ -6216,13 +6257,13 @@
6216
6257
  },
6217
6258
  "hasDynamicHelp": true,
6218
6259
  "hiddenAliases": [],
6219
- "id": "hardis:org:diagnose:unsecure-connected-apps",
6260
+ "id": "hardis:org:diagnose:licenses",
6220
6261
  "pluginAlias": "sfdx-hardis",
6221
6262
  "pluginName": "sfdx-hardis",
6222
6263
  "pluginType": "core",
6223
6264
  "strict": true,
6224
6265
  "enableJsonFlag": true,
6225
- "title": "Detect Unsecured Connected Apps",
6266
+ "title": "List licenses subscribed and used in a Salesforce org",
6226
6267
  "requiresProject": false,
6227
6268
  "isESM": true,
6228
6269
  "relativePath": [
@@ -6231,57 +6272,56 @@
6231
6272
  "hardis",
6232
6273
  "org",
6233
6274
  "diagnose",
6234
- "unsecure-connected-apps.js"
6275
+ "licenses.js"
6235
6276
  ],
6236
6277
  "aliasPermutations": [],
6237
6278
  "permutations": [
6238
- "hardis:org:diagnose:unsecure-connected-apps",
6239
- "org:hardis:diagnose:unsecure-connected-apps",
6240
- "org:diagnose:hardis:unsecure-connected-apps",
6241
- "org:diagnose:unsecure-connected-apps:hardis",
6242
- "hardis:diagnose:org:unsecure-connected-apps",
6243
- "diagnose:hardis:org:unsecure-connected-apps",
6244
- "diagnose:org:hardis:unsecure-connected-apps",
6245
- "diagnose:org:unsecure-connected-apps:hardis",
6246
- "hardis:diagnose:unsecure-connected-apps:org",
6247
- "diagnose:hardis:unsecure-connected-apps:org",
6248
- "diagnose:unsecure-connected-apps:hardis:org",
6249
- "diagnose:unsecure-connected-apps:org:hardis",
6250
- "hardis:org:unsecure-connected-apps:diagnose",
6251
- "org:hardis:unsecure-connected-apps:diagnose",
6252
- "org:unsecure-connected-apps:hardis:diagnose",
6253
- "org:unsecure-connected-apps:diagnose:hardis",
6254
- "hardis:unsecure-connected-apps:org:diagnose",
6255
- "unsecure-connected-apps:hardis:org:diagnose",
6256
- "unsecure-connected-apps:org:hardis:diagnose",
6257
- "unsecure-connected-apps:org:diagnose:hardis",
6258
- "hardis:unsecure-connected-apps:diagnose:org",
6259
- "unsecure-connected-apps:hardis:diagnose:org",
6260
- "unsecure-connected-apps:diagnose:hardis:org",
6261
- "unsecure-connected-apps:diagnose:org:hardis"
6262
- ]
6263
- },
6264
- "hardis:org:diagnose:unused-apex-classes": {
6265
- "aliases": [],
6266
- "args": {},
6267
- "description": "List all async Apex classes (Batch,Queueable,Schedulable) that has not been called for more than 365 days.\n \nThe result class list probably can be removed from the project, and that will improve your test classes performances :)\n\nThe number of unused day is overridable using --days option. \n\nThe command uses queries on AsyncApexJob and CronTrigger technical tables to build the result.\n\nApex Classes CreatedBy and CreatedOn fields are calculated from MIN(date from git, date from org)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-apex-classes/) and can output Grafana, Slack and MsTeams Notifications.\n\n![](https://sfdx-hardis.cloudity.com/assets/images/screenshot-monitoring-unused-apex-grafana.jpg)\n",
6268
- "examples": [
6269
- "$ sf hardis:org:diagnose:unused-apex-classes",
6270
- "$ sf hardis:org:diagnose:unused-apex-classes --days 700"
6271
- ],
6272
- "flags": {
6273
- "json": {
6274
- "description": "Format output as json.",
6275
- "helpGroup": "GLOBAL",
6276
- "name": "json",
6277
- "allowNo": false,
6278
- "type": "boolean"
6279
- },
6280
- "flags-dir": {
6281
- "helpGroup": "GLOBAL",
6282
- "name": "flags-dir",
6283
- "summary": "Import flag values from a directory.",
6284
- "hasDynamicHelp": false,
6279
+ "hardis:org:diagnose:licenses",
6280
+ "org:hardis:diagnose:licenses",
6281
+ "org:diagnose:hardis:licenses",
6282
+ "org:diagnose:licenses:hardis",
6283
+ "hardis:diagnose:org:licenses",
6284
+ "diagnose:hardis:org:licenses",
6285
+ "diagnose:org:hardis:licenses",
6286
+ "diagnose:org:licenses:hardis",
6287
+ "hardis:diagnose:licenses:org",
6288
+ "diagnose:hardis:licenses:org",
6289
+ "diagnose:licenses:hardis:org",
6290
+ "diagnose:licenses:org:hardis",
6291
+ "hardis:org:licenses:diagnose",
6292
+ "org:hardis:licenses:diagnose",
6293
+ "org:licenses:hardis:diagnose",
6294
+ "org:licenses:diagnose:hardis",
6295
+ "hardis:licenses:org:diagnose",
6296
+ "licenses:hardis:org:diagnose",
6297
+ "licenses:org:hardis:diagnose",
6298
+ "licenses:org:diagnose:hardis",
6299
+ "hardis:licenses:diagnose:org",
6300
+ "licenses:hardis:diagnose:org",
6301
+ "licenses:diagnose:hardis:org",
6302
+ "licenses:diagnose:org:hardis"
6303
+ ]
6304
+ },
6305
+ "hardis:org:diagnose:releaseupdates": {
6306
+ "aliases": [],
6307
+ "args": {},
6308
+ "description": "Export Release Updates into a CSV file with selected criteria, and highlight Release Updates that should be checked.\n\nBefore publishing **Breaking Changes** ❌, Salesforce announce them in the setup menu [**Release Updates**](https://help.salesforce.com/s/articleView?id=sf.release_updates.htm&type=5)\n\n⚠️ Some of them are very important, because if you don't make the related upgrades in time (ex: before Winter 25) , your production org can crash !\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-release-updates/) and can output Grafana, Slack and MsTeams Notifications.\n",
6309
+ "examples": [
6310
+ "$ sf hardis:org:diagnose:releaseupdates"
6311
+ ],
6312
+ "flags": {
6313
+ "json": {
6314
+ "description": "Format output as json.",
6315
+ "helpGroup": "GLOBAL",
6316
+ "name": "json",
6317
+ "allowNo": false,
6318
+ "type": "boolean"
6319
+ },
6320
+ "flags-dir": {
6321
+ "helpGroup": "GLOBAL",
6322
+ "name": "flags-dir",
6323
+ "summary": "Import flag values from a directory.",
6324
+ "hasDynamicHelp": false,
6285
6325
  "multiple": false,
6286
6326
  "type": "option"
6287
6327
  },
@@ -6293,14 +6333,6 @@
6293
6333
  "multiple": false,
6294
6334
  "type": "option"
6295
6335
  },
6296
- "days": {
6297
- "char": "t",
6298
- "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
6299
- "name": "days",
6300
- "hasDynamicHelp": false,
6301
- "multiple": false,
6302
- "type": "option"
6303
- },
6304
6336
  "debug": {
6305
6337
  "char": "d",
6306
6338
  "description": "Activate debug mode (more logs)",
@@ -6339,13 +6371,13 @@
6339
6371
  },
6340
6372
  "hasDynamicHelp": true,
6341
6373
  "hiddenAliases": [],
6342
- "id": "hardis:org:diagnose:unused-apex-classes",
6374
+ "id": "hardis:org:diagnose:releaseupdates",
6343
6375
  "pluginAlias": "sfdx-hardis",
6344
6376
  "pluginName": "sfdx-hardis",
6345
6377
  "pluginType": "core",
6346
6378
  "strict": true,
6347
6379
  "enableJsonFlag": true,
6348
- "title": "Detect unused Apex classes in an org",
6380
+ "title": "Check Release Updates of an org",
6349
6381
  "requiresProject": false,
6350
6382
  "isESM": true,
6351
6383
  "relativePath": [
@@ -6354,42 +6386,42 @@
6354
6386
  "hardis",
6355
6387
  "org",
6356
6388
  "diagnose",
6357
- "unused-apex-classes.js"
6389
+ "releaseupdates.js"
6358
6390
  ],
6359
6391
  "aliasPermutations": [],
6360
6392
  "permutations": [
6361
- "hardis:org:diagnose:unused-apex-classes",
6362
- "org:hardis:diagnose:unused-apex-classes",
6363
- "org:diagnose:hardis:unused-apex-classes",
6364
- "org:diagnose:unused-apex-classes:hardis",
6365
- "hardis:diagnose:org:unused-apex-classes",
6366
- "diagnose:hardis:org:unused-apex-classes",
6367
- "diagnose:org:hardis:unused-apex-classes",
6368
- "diagnose:org:unused-apex-classes:hardis",
6369
- "hardis:diagnose:unused-apex-classes:org",
6370
- "diagnose:hardis:unused-apex-classes:org",
6371
- "diagnose:unused-apex-classes:hardis:org",
6372
- "diagnose:unused-apex-classes:org:hardis",
6373
- "hardis:org:unused-apex-classes:diagnose",
6374
- "org:hardis:unused-apex-classes:diagnose",
6375
- "org:unused-apex-classes:hardis:diagnose",
6376
- "org:unused-apex-classes:diagnose:hardis",
6377
- "hardis:unused-apex-classes:org:diagnose",
6378
- "unused-apex-classes:hardis:org:diagnose",
6379
- "unused-apex-classes:org:hardis:diagnose",
6380
- "unused-apex-classes:org:diagnose:hardis",
6381
- "hardis:unused-apex-classes:diagnose:org",
6382
- "unused-apex-classes:hardis:diagnose:org",
6383
- "unused-apex-classes:diagnose:hardis:org",
6384
- "unused-apex-classes:diagnose:org:hardis"
6393
+ "hardis:org:diagnose:releaseupdates",
6394
+ "org:hardis:diagnose:releaseupdates",
6395
+ "org:diagnose:hardis:releaseupdates",
6396
+ "org:diagnose:releaseupdates:hardis",
6397
+ "hardis:diagnose:org:releaseupdates",
6398
+ "diagnose:hardis:org:releaseupdates",
6399
+ "diagnose:org:hardis:releaseupdates",
6400
+ "diagnose:org:releaseupdates:hardis",
6401
+ "hardis:diagnose:releaseupdates:org",
6402
+ "diagnose:hardis:releaseupdates:org",
6403
+ "diagnose:releaseupdates:hardis:org",
6404
+ "diagnose:releaseupdates:org:hardis",
6405
+ "hardis:org:releaseupdates:diagnose",
6406
+ "org:hardis:releaseupdates:diagnose",
6407
+ "org:releaseupdates:hardis:diagnose",
6408
+ "org:releaseupdates:diagnose:hardis",
6409
+ "hardis:releaseupdates:org:diagnose",
6410
+ "releaseupdates:hardis:org:diagnose",
6411
+ "releaseupdates:org:hardis:diagnose",
6412
+ "releaseupdates:org:diagnose:hardis",
6413
+ "hardis:releaseupdates:diagnose:org",
6414
+ "releaseupdates:hardis:diagnose:org",
6415
+ "releaseupdates:diagnose:hardis:org",
6416
+ "releaseupdates:diagnose:org:hardis"
6385
6417
  ]
6386
6418
  },
6387
- "hardis:org:diagnose:unused-connected-apps": {
6419
+ "hardis:org:diagnose:unsecure-connected-apps": {
6388
6420
  "aliases": [],
6389
6421
  "args": {},
6390
- "description": "\n## Command Behavior\n\n**Identifies and reports on potentially unused Connected Apps in a Salesforce org, suggesting candidates for deletion or deactivation.**\n\nThis command helps improve org security and reduce technical debt by pinpointing Connected Apps that are no longer actively used. Connected Apps can pose security risks if left unmonitored, and cleaning them up contributes to a healthier Salesforce environment.\n\nKey functionalities:\n\n- **Connected App Data Collection:** Gathers information about all Connected Apps in the org, including creation and last modified dates, and associated users.\n- **Usage Analysis:** Analyzes `LoginHistory` and `OAuthToken` records to determine the last usage date of each Connected App.\n- **Inactivity Detection:** Flags Connected Apps as potentially unused if they have no recent login history or OAuth token usage.\n- **Accessibility Check:** Examines Connected App metadata to identify if they are accessible (e.g., if they require admin approval and have no profiles or permission sets assigned).\n- **Ignored Apps:** Automatically ignores a predefined list of common Salesforce Connected Apps (e.g., `Salesforce CLI`, `Salesforce Mobile Dashboards`). You can extend this list by defining the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable.\n- **CSV Report Generation:** Generates a CSV file containing details of all analyzed Connected Apps, including their usage status, last usage date, and reasons for being flagged as potentially unused.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of potentially unused Connected Apps.\n\n**Default Ignored Connected Apps:**\n\n- Ant Migration Tool\n- Chatter Desktop\n- Chatter Mobile for BlackBerry\n- Force.com IDE\n- OIQ_Integration\n- Salesforce CLI\n- Salesforce Files\n- Salesforce Mobile Dashboards\n- Salesforce Touch\n- Salesforce for Outlook\n- SalesforceA\n- SalesforceA for Android\n- SalesforceA for iOS\n- SalesforceDX Namespace Registry\n- SalesforceIQ\n\nYou can add more ignored apps by defining a comma-separated list of names in the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable.\n\n_Example: \nALLOWED_INACTIVE_CONNECTED_APPS=My App 1,My App 2, My App 3_\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-connected-apps/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Queries:** It performs SOQL queries against `ConnectedApplication`, `LoginHistory`, and `OAuthToken` objects to gather comprehensive data about Connected Apps and their usage.\n- **Temporary SFDX Project:** It creates a temporary SFDX project to retrieve Connected App metadata, allowing for local parsing and analysis of their XML files.\n- **Metadata Parsing:** It parses the `connectedApp-meta.xml` files to check for `isAdminApproved` and the presence of `profileName` or `permissionsetName` to determine accessibility.\n- **Data Correlation:** It correlates data from various Salesforce objects to build a complete picture of each Connected App's usage and status.\n- **Date Calculation:** Uses `moment` to calculate the time since the last OAuth token usage.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of unused Connected Apps.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **File System Operations:** Uses `fs-extra` for creating and removing temporary directories and files.\n- **Environment Variable Reading:** Reads the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable to customize the list of ignored Connected Apps.\n</details>\n",
6422
+ "description": "\n## Command Behavior\n\n**Detects unsecured Connected Apps in a Salesforce org and generates detailed reports for security analysis.**\n\nThis command is a critical security diagnostic tool that helps administrators identify Connected Apps that may pose security risks due to improper configuration. It provides comprehensive analysis of OAuth tokens and Connected App security settings to ensure proper access control.\n\nKey functionalities:\n\n- **OAuth Token Analysis:** Queries all OAuth tokens in the org using SOQL to retrieve comprehensive token information including app names, users, authorization status, and usage statistics.\n- **Security Status Assessment:** Evaluates each Connected App's security configuration by checking the `IsUsingAdminAuthorization` flag to determine if admin pre-approval is required.\n- **Unsecured App Detection:** Identifies Connected Apps that allow users to authorize themselves without admin approval, which can pose security risks.\n- **Detailed Reporting:** Generates two comprehensive CSV reports:\n - **OAuth Tokens Report:** Lists all OAuth tokens with security status, user information, and usage data\n - **Connected Apps Summary:** Aggregates unsecured Connected Apps with counts of associated OAuth tokens\n- **Visual Indicators:** Uses status icons (❌ for unsecured, for secured) to provide immediate visual feedback on security status.\n- **Security Recommendations:** Provides actionable guidance on how to secure Connected Apps through proper configuration.\n- **Notifications:** Sends alerts to configured channels (Grafana, Slack, MS Teams) with security findings and attached reports.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-security/) and can output Grafana, Slack and MsTeams Notifications.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/jHv8yrSK8Dg\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query Execution:** Executes a comprehensive SOQL query on the `OauthToken` object, joining with `AppMenuItem` and `User` objects to gather complete security context.\n- **Security Analysis Logic:** Analyzes the `AppMenuItem.IsUsingAdminAuthorization` field to determine if a Connected App requires admin pre-approval for user authorization.\n- **Data Transformation:** Processes raw SOQL results to add security status indicators and reorganize data for optimal reporting and analysis.\n- **Aggregation Processing:** Groups OAuth tokens by Connected App name to provide summary statistics and identify the most problematic applications.\n- **Report Generation:** Uses `generateCsvFile` to create structured CSV reports with proper formatting and metadata for easy analysis and sharing.\n- **Notification Integration:** Integrates with the `NotifProvider` to send security alerts with detailed metrics, including the number of unsecured Connected Apps and associated OAuth tokens.\n- **File Management:** Generates multiple output formats (CSV, XLSX) and manages file paths using `generateReportPath` for consistent report organization.\n- **Connection Management:** Uses `setConnectionVariables` to ensure proper authentication context for notification providers that require org connection details.\n</details>\n",
6391
6423
  "examples": [
6392
- "$ sf hardis:org:diagnose:unused-connected-apps"
6424
+ "$ sf hardis:org:diagnose:unsecure-connected-apps"
6393
6425
  ],
6394
6426
  "flags": {
6395
6427
  "json": {
@@ -6453,30 +6485,13 @@
6453
6485
  },
6454
6486
  "hasDynamicHelp": true,
6455
6487
  "hiddenAliases": [],
6456
- "id": "hardis:org:diagnose:unused-connected-apps",
6488
+ "id": "hardis:org:diagnose:unsecure-connected-apps",
6457
6489
  "pluginAlias": "sfdx-hardis",
6458
6490
  "pluginName": "sfdx-hardis",
6459
6491
  "pluginType": "core",
6460
6492
  "strict": true,
6461
6493
  "enableJsonFlag": true,
6462
- "title": "Unused Connected Apps in an org",
6463
- "allowedInactiveConnectedApps": [
6464
- "Ant Migration Tool",
6465
- "Chatter Desktop",
6466
- "Chatter Mobile for BlackBerry",
6467
- "Force.com IDE",
6468
- "OIQ_Integration",
6469
- "Salesforce CLI",
6470
- "Salesforce Files",
6471
- "Salesforce Mobile Dashboards",
6472
- "Salesforce Touch",
6473
- "Salesforce for Outlook",
6474
- "SalesforceA",
6475
- "SalesforceA for Android",
6476
- "SalesforceA for iOS",
6477
- "SalesforceDX Namespace Registry",
6478
- "SalesforceIQ"
6479
- ],
6494
+ "title": "Detect Unsecured Connected Apps",
6480
6495
  "requiresProject": false,
6481
6496
  "isESM": true,
6482
6497
  "relativePath": [
@@ -6485,43 +6500,43 @@
6485
6500
  "hardis",
6486
6501
  "org",
6487
6502
  "diagnose",
6488
- "unused-connected-apps.js"
6503
+ "unsecure-connected-apps.js"
6489
6504
  ],
6490
6505
  "aliasPermutations": [],
6491
6506
  "permutations": [
6492
- "hardis:org:diagnose:unused-connected-apps",
6493
- "org:hardis:diagnose:unused-connected-apps",
6494
- "org:diagnose:hardis:unused-connected-apps",
6495
- "org:diagnose:unused-connected-apps:hardis",
6496
- "hardis:diagnose:org:unused-connected-apps",
6497
- "diagnose:hardis:org:unused-connected-apps",
6498
- "diagnose:org:hardis:unused-connected-apps",
6499
- "diagnose:org:unused-connected-apps:hardis",
6500
- "hardis:diagnose:unused-connected-apps:org",
6501
- "diagnose:hardis:unused-connected-apps:org",
6502
- "diagnose:unused-connected-apps:hardis:org",
6503
- "diagnose:unused-connected-apps:org:hardis",
6504
- "hardis:org:unused-connected-apps:diagnose",
6505
- "org:hardis:unused-connected-apps:diagnose",
6506
- "org:unused-connected-apps:hardis:diagnose",
6507
- "org:unused-connected-apps:diagnose:hardis",
6508
- "hardis:unused-connected-apps:org:diagnose",
6509
- "unused-connected-apps:hardis:org:diagnose",
6510
- "unused-connected-apps:org:hardis:diagnose",
6511
- "unused-connected-apps:org:diagnose:hardis",
6512
- "hardis:unused-connected-apps:diagnose:org",
6513
- "unused-connected-apps:hardis:diagnose:org",
6514
- "unused-connected-apps:diagnose:hardis:org",
6515
- "unused-connected-apps:diagnose:org:hardis"
6507
+ "hardis:org:diagnose:unsecure-connected-apps",
6508
+ "org:hardis:diagnose:unsecure-connected-apps",
6509
+ "org:diagnose:hardis:unsecure-connected-apps",
6510
+ "org:diagnose:unsecure-connected-apps:hardis",
6511
+ "hardis:diagnose:org:unsecure-connected-apps",
6512
+ "diagnose:hardis:org:unsecure-connected-apps",
6513
+ "diagnose:org:hardis:unsecure-connected-apps",
6514
+ "diagnose:org:unsecure-connected-apps:hardis",
6515
+ "hardis:diagnose:unsecure-connected-apps:org",
6516
+ "diagnose:hardis:unsecure-connected-apps:org",
6517
+ "diagnose:unsecure-connected-apps:hardis:org",
6518
+ "diagnose:unsecure-connected-apps:org:hardis",
6519
+ "hardis:org:unsecure-connected-apps:diagnose",
6520
+ "org:hardis:unsecure-connected-apps:diagnose",
6521
+ "org:unsecure-connected-apps:hardis:diagnose",
6522
+ "org:unsecure-connected-apps:diagnose:hardis",
6523
+ "hardis:unsecure-connected-apps:org:diagnose",
6524
+ "unsecure-connected-apps:hardis:org:diagnose",
6525
+ "unsecure-connected-apps:org:hardis:diagnose",
6526
+ "unsecure-connected-apps:org:diagnose:hardis",
6527
+ "hardis:unsecure-connected-apps:diagnose:org",
6528
+ "unsecure-connected-apps:hardis:diagnose:org",
6529
+ "unsecure-connected-apps:diagnose:hardis:org",
6530
+ "unsecure-connected-apps:diagnose:org:hardis"
6516
6531
  ]
6517
6532
  },
6518
- "hardis:org:diagnose:unusedlicenses": {
6533
+ "hardis:org:diagnose:unused-apex-classes": {
6519
6534
  "aliases": [],
6520
6535
  "args": {},
6521
- "description": "\n## Command Behavior\n\n**Detects and suggests the deletion of unused Permission Set License Assignments in a Salesforce org.**\n\nWhen a Permission Set (PS) linked to a Permission Set License (PSL) is assigned to a user, a Permission Set License Assignment (PSLA) is automatically created. However, when that PS is unassigned from the user, the PSLA is *not* automatically deleted. This can lead to organizations being charged for unused PSLAs, representing a hidden cost and technical debt.\n\nThis command identifies such useless PSLAs and provides options to delete them, helping to optimize license usage and reduce unnecessary expenses.\n\nKey functionalities:\n\n- **PSLA Detection:** Queries the Salesforce org to find all active PSLAs.\n- **Usage Verification:** Correlates PSLAs with actual Permission Set Assignments and Permission Set Group Assignments to determine if the underlying Permission Sets are still assigned to the user.\n- **Special Case Handling:** Accounts for specific scenarios where profiles might implicitly assign PSLAs (e.g., `Salesforce API Only` profile assigning `SalesforceAPIIntegrationPsl`) and allows for always excluding certain PSLAs from the unused check.\n- **Reporting:** Generates a CSV report of all identified unused PSLAs, including the user and the associated Permission Set License.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of unused PSLAs.\n- **Interactive Deletion:** In non-CI environments, it offers an interactive prompt to bulk delete the identified unused PSLAs.\n\nMany thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-licenses/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves extensive querying of Salesforce objects and data correlation:\n\n- **SOQL Queries (Bulk API):** It uses `bulkQuery` and `bulkQueryChunksIn` to efficiently retrieve large volumes of data from `PermissionSetLicenseAssign`, `PermissionSetLicense`, `PermissionSet`, `PermissionSetGroupComponent`, and `PermissionSetAssignment` objects.\n- **Data Correlation:** It meticulously correlates data across these objects to determine if a `PermissionSetLicenseAssign` record has a corresponding active assignment to a Permission Set or Permission Set Group for the same user.\n- **Filtering Logic:** It applies complex filtering logic to exclude PSLAs that are genuinely in use or are part of predefined exceptions (e.g., `alwaysExcludeForActiveUsersPermissionSetLicenses`).\n- **Bulk Deletion:** If the user opts to delete unused PSLAs, it uses `bulkUpdate` with the `delete` operation to efficiently remove multiple records.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of unused PSLAs.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Interaction:** Uses `prompts` for interactive confirmation before performing deletion operations.\n</details>\n",
6536
+ "description": "List all async Apex classes (Batch,Queueable,Schedulable) that has not been called for more than 365 days.\n \nThe result class list probably can be removed from the project, and that will improve your test classes performances :)\n\nThe number of unused day is overridable using --days option. \n\nThe command uses queries on AsyncApexJob and CronTrigger technical tables to build the result.\n\nApex Classes CreatedBy and CreatedOn fields are calculated from MIN(date from git, date from org)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-apex-classes/) and can output Grafana, Slack and MsTeams Notifications.\n\n![](https://sfdx-hardis.cloudity.com/assets/images/screenshot-monitoring-unused-apex-grafana.jpg)\n",
6522
6537
  "examples": [
6523
- "$ sf hardis:org:diagnose:unusedlicenses",
6524
- "$ sf hardis:org:diagnose:unusedlicenses --fix"
6538
+ "$ sf hardis:org:diagnose:unused-apex-classes",
6539
+ "$ sf hardis:org:diagnose:unused-apex-classes --days 700"
6525
6540
  ],
6526
6541
  "flags": {
6527
6542
  "json": {
@@ -6547,6 +6562,14 @@
6547
6562
  "multiple": false,
6548
6563
  "type": "option"
6549
6564
  },
6565
+ "days": {
6566
+ "char": "t",
6567
+ "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
6568
+ "name": "days",
6569
+ "hasDynamicHelp": false,
6570
+ "multiple": false,
6571
+ "type": "option"
6572
+ },
6550
6573
  "debug": {
6551
6574
  "char": "d",
6552
6575
  "description": "Activate debug mode (more logs)",
@@ -6585,32 +6608,14 @@
6585
6608
  },
6586
6609
  "hasDynamicHelp": true,
6587
6610
  "hiddenAliases": [],
6588
- "id": "hardis:org:diagnose:unusedlicenses",
6611
+ "id": "hardis:org:diagnose:unused-apex-classes",
6589
6612
  "pluginAlias": "sfdx-hardis",
6590
6613
  "pluginName": "sfdx-hardis",
6591
6614
  "pluginType": "core",
6592
6615
  "strict": true,
6593
6616
  "enableJsonFlag": true,
6594
- "title": "Detect unused Permission Set Licenses (beta)",
6617
+ "title": "Detect unused Apex classes in an org",
6595
6618
  "requiresProject": false,
6596
- "additionalPermissionSetsToAlwaysGet": [
6597
- "Sales_User"
6598
- ],
6599
- "permSetsPermSetLicenses": [
6600
- {
6601
- "permSet": "Sales_User",
6602
- "permSetLicense": "SalesUserPsl"
6603
- }
6604
- ],
6605
- "profilesPermissionSetLicenses": [
6606
- {
6607
- "profile": "Salesforce API Only",
6608
- "permSetLicense": "SalesforceAPIIntegrationPsl"
6609
- }
6610
- ],
6611
- "alwaysExcludeForActiveUsersPermissionSetLicenses": [
6612
- "IdentityConnect"
6613
- ],
6614
6619
  "isESM": true,
6615
6620
  "relativePath": [
6616
6621
  "lib",
@@ -6618,46 +6623,42 @@
6618
6623
  "hardis",
6619
6624
  "org",
6620
6625
  "diagnose",
6621
- "unusedlicenses.js"
6626
+ "unused-apex-classes.js"
6622
6627
  ],
6623
6628
  "aliasPermutations": [],
6624
6629
  "permutations": [
6625
- "hardis:org:diagnose:unusedlicenses",
6626
- "org:hardis:diagnose:unusedlicenses",
6627
- "org:diagnose:hardis:unusedlicenses",
6628
- "org:diagnose:unusedlicenses:hardis",
6629
- "hardis:diagnose:org:unusedlicenses",
6630
- "diagnose:hardis:org:unusedlicenses",
6631
- "diagnose:org:hardis:unusedlicenses",
6632
- "diagnose:org:unusedlicenses:hardis",
6633
- "hardis:diagnose:unusedlicenses:org",
6634
- "diagnose:hardis:unusedlicenses:org",
6635
- "diagnose:unusedlicenses:hardis:org",
6636
- "diagnose:unusedlicenses:org:hardis",
6637
- "hardis:org:unusedlicenses:diagnose",
6638
- "org:hardis:unusedlicenses:diagnose",
6639
- "org:unusedlicenses:hardis:diagnose",
6640
- "org:unusedlicenses:diagnose:hardis",
6641
- "hardis:unusedlicenses:org:diagnose",
6642
- "unusedlicenses:hardis:org:diagnose",
6643
- "unusedlicenses:org:hardis:diagnose",
6644
- "unusedlicenses:org:diagnose:hardis",
6645
- "hardis:unusedlicenses:diagnose:org",
6646
- "unusedlicenses:hardis:diagnose:org",
6647
- "unusedlicenses:diagnose:hardis:org",
6648
- "unusedlicenses:diagnose:org:hardis"
6649
- ]
6650
- },
6651
- "hardis:org:diagnose:unusedusers": {
6652
- "aliases": [],
6653
- "args": {},
6654
- "description": "\n## Command Behavior\n\n**Detects and reports on inactive or unused Salesforce user accounts, helping to optimize license usage and enhance security.**\n\nEfficient user management is vital in Salesforce to ensure resources are optimized and costs are controlled. However, inactive or unused user accounts can often go unnoticed, leading to wasted licenses and potential security risks. This tool addresses this challenge by enabling administrators to identify users who haven't logged in within a specified period.\n\nBy analyzing user login activity and last login timestamps, this feature highlights inactive user accounts, allowing administrators to take appropriate action. Whether it's deactivating dormant accounts, freeing up licenses, or ensuring compliance with security policies, this functionality empowers administrators to maintain a lean and secure Salesforce environment.\n\nKey functionalities:\n\n- **Inactivity Detection:** Identifies users who have not logged in for a specified number of days (`--days` flag, default 180 days in CI, 365 days otherwise).\n- **License Type Filtering:** Allows filtering users by license type using `--licensetypes` (e.g., `all-crm`, `all-paying`) or specific license identifiers using `--licenseidentifiers`.\n - `all-crm`: Includes `SFDC`, `AUL`, `AUL1`, `AULL_IGHT` licenses.\n - `all-paying`: Includes `SFDC`, `AUL`, `AUL1`, `AULL_IGHT`, `PID_Customer_Community`, `PID_Customer_Community_Login`, `PID_Partner_Community`, `PID_Partner_Community_Login` licenses.\n - Note: You can see the full list of available license identifiers in [Salesforce Documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/sfdx_cli_reference/sforce_api_objects_userlicense.htm).\n- **Active User Retrieval:** The `--returnactiveusers` flag inverts the command, allowing you to retrieve active users who *have* logged in during the specified period.\n- **CSV Report Generation:** Generates a CSV file containing details of all identified users (inactive or active), including their last login date, profile, and license information.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of inactive or active users.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-users/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query (Bulk API):** It uses `bulkQuery` to efficiently retrieve user records from the Salesforce `User` object. The SOQL query dynamically constructs its WHERE clause based on the `--days`, `--licensetypes`, `--licenseidentifiers`, and `--returnactiveusers` flags.\n- **Interactive Prompts:** Uses `prompts` to interactively ask the user for the number of inactive days and license types if not provided via flags.\n- **License Mapping:** Internally maps common license type aliases (e.g., `all-crm`) to their corresponding Salesforce `LicenseDefinitionKey` values.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of users.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Feedback:** Provides a summary of the findings in the console, indicating the number of inactive or active users found.\n</details>",
6655
- "examples": [
6656
- "$ sf hardis:org:diagnose:unusedusers",
6657
- "$ sf hardis:org:diagnose:unusedusers --days 365",
6658
- "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm",
6659
- "$ sf hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1",
6660
- "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers"
6630
+ "hardis:org:diagnose:unused-apex-classes",
6631
+ "org:hardis:diagnose:unused-apex-classes",
6632
+ "org:diagnose:hardis:unused-apex-classes",
6633
+ "org:diagnose:unused-apex-classes:hardis",
6634
+ "hardis:diagnose:org:unused-apex-classes",
6635
+ "diagnose:hardis:org:unused-apex-classes",
6636
+ "diagnose:org:hardis:unused-apex-classes",
6637
+ "diagnose:org:unused-apex-classes:hardis",
6638
+ "hardis:diagnose:unused-apex-classes:org",
6639
+ "diagnose:hardis:unused-apex-classes:org",
6640
+ "diagnose:unused-apex-classes:hardis:org",
6641
+ "diagnose:unused-apex-classes:org:hardis",
6642
+ "hardis:org:unused-apex-classes:diagnose",
6643
+ "org:hardis:unused-apex-classes:diagnose",
6644
+ "org:unused-apex-classes:hardis:diagnose",
6645
+ "org:unused-apex-classes:diagnose:hardis",
6646
+ "hardis:unused-apex-classes:org:diagnose",
6647
+ "unused-apex-classes:hardis:org:diagnose",
6648
+ "unused-apex-classes:org:hardis:diagnose",
6649
+ "unused-apex-classes:org:diagnose:hardis",
6650
+ "hardis:unused-apex-classes:diagnose:org",
6651
+ "unused-apex-classes:hardis:diagnose:org",
6652
+ "unused-apex-classes:diagnose:hardis:org",
6653
+ "unused-apex-classes:diagnose:org:hardis"
6654
+ ]
6655
+ },
6656
+ "hardis:org:diagnose:unused-connected-apps": {
6657
+ "aliases": [],
6658
+ "args": {},
6659
+ "description": "\n## Command Behavior\n\n**Identifies and reports on potentially unused Connected Apps in a Salesforce org, suggesting candidates for deletion or deactivation.**\n\nThis command helps improve org security and reduce technical debt by pinpointing Connected Apps that are no longer actively used. Connected Apps can pose security risks if left unmonitored, and cleaning them up contributes to a healthier Salesforce environment.\n\nKey functionalities:\n\n- **Connected App Data Collection:** Gathers information about all Connected Apps in the org, including creation and last modified dates, and associated users.\n- **Usage Analysis:** Analyzes `LoginHistory` and `OAuthToken` records to determine the last usage date of each Connected App.\n- **Inactivity Detection:** Flags Connected Apps as potentially unused if they have no recent login history or OAuth token usage.\n- **Accessibility Check:** Examines Connected App metadata to identify if they are accessible (e.g., if they require admin approval and have no profiles or permission sets assigned).\n- **Ignored Apps:** Automatically ignores a predefined list of common Salesforce Connected Apps (e.g., `Salesforce CLI`, `Salesforce Mobile Dashboards`). You can extend this list by defining the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable.\n- **CSV Report Generation:** Generates a CSV file containing details of all analyzed Connected Apps, including their usage status, last usage date, and reasons for being flagged as potentially unused.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of potentially unused Connected Apps.\n\n**Default Ignored Connected Apps:**\n\n- Ant Migration Tool\n- Chatter Desktop\n- Chatter Mobile for BlackBerry\n- Force.com IDE\n- OIQ_Integration\n- Salesforce CLI\n- Salesforce Files\n- Salesforce Mobile Dashboards\n- Salesforce Touch\n- Salesforce for Outlook\n- SalesforceA\n- SalesforceA for Android\n- SalesforceA for iOS\n- SalesforceDX Namespace Registry\n- SalesforceIQ\n\nYou can add more ignored apps by defining a comma-separated list of names in the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable.\n\n_Example: \nALLOWED_INACTIVE_CONNECTED_APPS=My App 1,My App 2, My App 3_\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-connected-apps/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce SOQL Queries:** It performs SOQL queries against `ConnectedApplication`, `LoginHistory`, and `OAuthToken` objects to gather comprehensive data about Connected Apps and their usage.\n- **Temporary SFDX Project:** It creates a temporary SFDX project to retrieve Connected App metadata, allowing for local parsing and analysis of their XML files.\n- **Metadata Parsing:** It parses the `connectedApp-meta.xml` files to check for `isAdminApproved` and the presence of `profileName` or `permissionsetName` to determine accessibility.\n- **Data Correlation:** It correlates data from various Salesforce objects to build a complete picture of each Connected App's usage and status.\n- **Date Calculation:** Uses `moment` to calculate the time since the last OAuth token usage.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of unused Connected Apps.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **File System Operations:** Uses `fs-extra` for creating and removing temporary directories and files.\n- **Environment Variable Reading:** Reads the `ALLOWED_INACTIVE_CONNECTED_APPS` environment variable to customize the list of ignored Connected Apps.\n</details>\n",
6660
+ "examples": [
6661
+ "$ sf hardis:org:diagnose:unused-connected-apps"
6661
6662
  ],
6662
6663
  "flags": {
6663
6664
  "json": {
@@ -6683,41 +6684,6 @@
6683
6684
  "multiple": false,
6684
6685
  "type": "option"
6685
6686
  },
6686
- "days": {
6687
- "char": "t",
6688
- "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
6689
- "name": "days",
6690
- "hasDynamicHelp": false,
6691
- "multiple": false,
6692
- "type": "option"
6693
- },
6694
- "licensetypes": {
6695
- "char": "l",
6696
- "description": "Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm",
6697
- "name": "licensetypes",
6698
- "hasDynamicHelp": false,
6699
- "multiple": false,
6700
- "options": [
6701
- "all",
6702
- "all-crm",
6703
- "all-paying"
6704
- ],
6705
- "type": "option"
6706
- },
6707
- "licenseidentifiers": {
6708
- "char": "i",
6709
- "description": "Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_userlicense.htm",
6710
- "name": "licenseidentifiers",
6711
- "hasDynamicHelp": false,
6712
- "multiple": false,
6713
- "type": "option"
6714
- },
6715
- "returnactiveusers": {
6716
- "description": "Inverts the command by returning the active users",
6717
- "name": "returnactiveusers",
6718
- "allowNo": false,
6719
- "type": "boolean"
6720
- },
6721
6687
  "debug": {
6722
6688
  "char": "d",
6723
6689
  "description": "Activate debug mode (more logs)",
@@ -6756,13 +6722,30 @@
6756
6722
  },
6757
6723
  "hasDynamicHelp": true,
6758
6724
  "hiddenAliases": [],
6759
- "id": "hardis:org:diagnose:unusedusers",
6725
+ "id": "hardis:org:diagnose:unused-connected-apps",
6760
6726
  "pluginAlias": "sfdx-hardis",
6761
6727
  "pluginName": "sfdx-hardis",
6762
6728
  "pluginType": "core",
6763
6729
  "strict": true,
6764
6730
  "enableJsonFlag": true,
6765
- "title": "Detect unused Users in Salesforce",
6731
+ "title": "Unused Connected Apps in an org",
6732
+ "allowedInactiveConnectedApps": [
6733
+ "Ant Migration Tool",
6734
+ "Chatter Desktop",
6735
+ "Chatter Mobile for BlackBerry",
6736
+ "Force.com IDE",
6737
+ "OIQ_Integration",
6738
+ "Salesforce CLI",
6739
+ "Salesforce Files",
6740
+ "Salesforce Mobile Dashboards",
6741
+ "Salesforce Touch",
6742
+ "Salesforce for Outlook",
6743
+ "SalesforceA",
6744
+ "SalesforceA for Android",
6745
+ "SalesforceA for iOS",
6746
+ "SalesforceDX Namespace Registry",
6747
+ "SalesforceIQ"
6748
+ ],
6766
6749
  "requiresProject": false,
6767
6750
  "isESM": true,
6768
6751
  "relativePath": [
@@ -6771,42 +6754,43 @@
6771
6754
  "hardis",
6772
6755
  "org",
6773
6756
  "diagnose",
6774
- "unusedusers.js"
6757
+ "unused-connected-apps.js"
6775
6758
  ],
6776
6759
  "aliasPermutations": [],
6777
6760
  "permutations": [
6778
- "hardis:org:diagnose:unusedusers",
6779
- "org:hardis:diagnose:unusedusers",
6780
- "org:diagnose:hardis:unusedusers",
6781
- "org:diagnose:unusedusers:hardis",
6782
- "hardis:diagnose:org:unusedusers",
6783
- "diagnose:hardis:org:unusedusers",
6784
- "diagnose:org:hardis:unusedusers",
6785
- "diagnose:org:unusedusers:hardis",
6786
- "hardis:diagnose:unusedusers:org",
6787
- "diagnose:hardis:unusedusers:org",
6788
- "diagnose:unusedusers:hardis:org",
6789
- "diagnose:unusedusers:org:hardis",
6790
- "hardis:org:unusedusers:diagnose",
6791
- "org:hardis:unusedusers:diagnose",
6792
- "org:unusedusers:hardis:diagnose",
6793
- "org:unusedusers:diagnose:hardis",
6794
- "hardis:unusedusers:org:diagnose",
6795
- "unusedusers:hardis:org:diagnose",
6796
- "unusedusers:org:hardis:diagnose",
6797
- "unusedusers:org:diagnose:hardis",
6798
- "hardis:unusedusers:diagnose:org",
6799
- "unusedusers:hardis:diagnose:org",
6800
- "unusedusers:diagnose:hardis:org",
6801
- "unusedusers:diagnose:org:hardis"
6761
+ "hardis:org:diagnose:unused-connected-apps",
6762
+ "org:hardis:diagnose:unused-connected-apps",
6763
+ "org:diagnose:hardis:unused-connected-apps",
6764
+ "org:diagnose:unused-connected-apps:hardis",
6765
+ "hardis:diagnose:org:unused-connected-apps",
6766
+ "diagnose:hardis:org:unused-connected-apps",
6767
+ "diagnose:org:hardis:unused-connected-apps",
6768
+ "diagnose:org:unused-connected-apps:hardis",
6769
+ "hardis:diagnose:unused-connected-apps:org",
6770
+ "diagnose:hardis:unused-connected-apps:org",
6771
+ "diagnose:unused-connected-apps:hardis:org",
6772
+ "diagnose:unused-connected-apps:org:hardis",
6773
+ "hardis:org:unused-connected-apps:diagnose",
6774
+ "org:hardis:unused-connected-apps:diagnose",
6775
+ "org:unused-connected-apps:hardis:diagnose",
6776
+ "org:unused-connected-apps:diagnose:hardis",
6777
+ "hardis:unused-connected-apps:org:diagnose",
6778
+ "unused-connected-apps:hardis:org:diagnose",
6779
+ "unused-connected-apps:org:hardis:diagnose",
6780
+ "unused-connected-apps:org:diagnose:hardis",
6781
+ "hardis:unused-connected-apps:diagnose:org",
6782
+ "unused-connected-apps:hardis:diagnose:org",
6783
+ "unused-connected-apps:diagnose:hardis:org",
6784
+ "unused-connected-apps:diagnose:org:hardis"
6802
6785
  ]
6803
6786
  },
6804
- "hardis:org:files:export": {
6787
+ "hardis:org:diagnose:unusedlicenses": {
6805
6788
  "aliases": [],
6806
6789
  "args": {},
6807
- "description": "\n## Command Behavior\n\n**Exports file attachments (ContentVersion, Attachment) from a Salesforce org based on a predefined configuration.**\n\nThis command enables the mass download of files associated with Salesforce records, providing a robust solution for backing up files, migrating them to other systems, or integrating them with external document management solutions.\n\nKey functionalities:\n\n- **Configuration-Driven Export:** Relies on an `export.json` file within a designated file export project to define the export criteria, including the SOQL query for parent records, file types to export, output naming conventions, and file size filtering.\n- **File Size Filtering:** Supports minimum file size filtering via the `fileSizeMin` configuration parameter (in KB). Files smaller than the specified size will be skipped during export.\n- **File Validation:** After downloading each file, validates the integrity by:\n - **Checksum Validation:** For ContentVersion files, compares MD5 checksum with Salesforce's stored checksum\n - **Size Validation:** For both ContentVersion and Attachment files, verifies actual file size matches expected size\n - **Status Tracking:** Files are categorized with specific statuses: `success` (valid files), `failed` (download errors), `skipped` (filtered files), `invalid` (downloaded but failed validation)\n - All validation results are logged in the CSV export log for audit purposes\n- **Resume/Restart Capability:** \n - **Resume Mode:** When `--resume` flag is used (default in CI environments), checks existing downloaded files for validity. Valid files are skipped, invalid files are re-downloaded.\n - **Restart Mode:** When resume is disabled, clears the output folder and starts a fresh export.\n - **Interactive Mode:** When existing files are found and `--resume` is not explicitly specified (non-CI environments), prompts the user to choose between resume or restart.\n- **Interactive Project Selection:** If the file export project path is not provided via the `--path` flag, it interactively prompts the user to select one.\n- **Configurable Export Options:** Allows overriding default export settings such as `chunksize` (number of records processed in a batch), `polltimeout` (timeout for Bulk API calls), and `startchunknumber` (to resume a failed export).\n- **Support for ContentVersion and Attachment:** Handles both modern Salesforce Files (ContentVersion) and older Attachments.\n\nSee this article for a practical example:\n\n[![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **FilesExporter Class:** The core logic is encapsulated within the `FilesExporter` class, which orchestrates the entire export process.\n- **SOQL Queries (Bulk API):** It uses Salesforce Bulk API queries to efficiently retrieve large volumes of parent record IDs and file metadata, including checksums and file sizes.\n- **File Download:** Downloads the actual file content from Salesforce.\n- **File Validation:** After each successful download, validates file integrity by comparing checksums (ContentVersion) and file sizes (both ContentVersion and Attachment) against Salesforce metadata.\n- **Resume Logic:** In resume mode, checks for existing files before downloading, validates their integrity, and only re-downloads invalid or missing files. This enables efficient recovery from interrupted exports.\n- **File System Operations:** Writes the downloaded files to the local file system, organizing them into folders based on the configured naming conventions.\n- **Configuration Loading:** Reads the `export.json` file to get the export configuration. It also allows for interactive overriding of these settings.\n- **Interactive Prompts:** Uses `selectFilesWorkspace` to allow the user to choose a file export project, `promptFilesExportConfiguration` for customizing export options, and prompts for resume/restart choice when existing files are found.\n- **Error Handling:** Includes mechanisms to handle potential errors during the export process, such as network issues, API limits, and file validation failures. Each file is assigned a specific status (`success`, `failed`, `skipped`, `invalid`) for comprehensive tracking and troubleshooting.\n</details>\n",
6790
+ "description": "\n## Command Behavior\n\n**Detects and suggests the deletion of unused Permission Set License Assignments in a Salesforce org.**\n\nWhen a Permission Set (PS) linked to a Permission Set License (PSL) is assigned to a user, a Permission Set License Assignment (PSLA) is automatically created. However, when that PS is unassigned from the user, the PSLA is *not* automatically deleted. This can lead to organizations being charged for unused PSLAs, representing a hidden cost and technical debt.\n\nThis command identifies such useless PSLAs and provides options to delete them, helping to optimize license usage and reduce unnecessary expenses.\n\nKey functionalities:\n\n- **PSLA Detection:** Queries the Salesforce org to find all active PSLAs.\n- **Usage Verification:** Correlates PSLAs with actual Permission Set Assignments and Permission Set Group Assignments to determine if the underlying Permission Sets are still assigned to the user.\n- **Special Case Handling:** Accounts for specific scenarios where profiles might implicitly assign PSLAs (e.g., `Salesforce API Only` profile assigning `SalesforceAPIIntegrationPsl`) and allows for always excluding certain PSLAs from the unused check.\n- **Reporting:** Generates a CSV report of all identified unused PSLAs, including the user and the associated Permission Set License.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of unused PSLAs.\n- **Interactive Deletion:** In non-CI environments, it offers an interactive prompt to bulk delete the identified unused PSLAs.\n\nMany thanks to [Vincent Finet](https://www.linkedin.com/in/vincentfinet/) for the inspiration during his great speaker session at [French Touch Dreamin '23](https://frenchtouchdreamin.com/), and his kind agreement for reusing such inspiration in this command :)\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-unused-licenses/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves extensive querying of Salesforce objects and data correlation:\n\n- **SOQL Queries (Bulk API):** It uses `bulkQuery` and `bulkQueryChunksIn` to efficiently retrieve large volumes of data from `PermissionSetLicenseAssign`, `PermissionSetLicense`, `PermissionSet`, `PermissionSetGroupComponent`, and `PermissionSetAssignment` objects.\n- **Data Correlation:** It meticulously correlates data across these objects to determine if a `PermissionSetLicenseAssign` record has a corresponding active assignment to a Permission Set or Permission Set Group for the same user.\n- **Filtering Logic:** It applies complex filtering logic to exclude PSLAs that are genuinely in use or are part of predefined exceptions (e.g., `alwaysExcludeForActiveUsersPermissionSetLicenses`).\n- **Bulk Deletion:** If the user opts to delete unused PSLAs, it uses `bulkUpdate` with the `delete` operation to efficiently remove multiple records.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of unused PSLAs.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Interaction:** Uses `prompts` for interactive confirmation before performing deletion operations.\n</details>\n",
6808
6791
  "examples": [
6809
- "$ sf hardis:org:files:export"
6792
+ "$ sf hardis:org:diagnose:unusedlicenses",
6793
+ "$ sf hardis:org:diagnose:unusedlicenses --fix"
6810
6794
  ],
6811
6795
  "flags": {
6812
6796
  "json": {
@@ -6824,48 +6808,14 @@
6824
6808
  "multiple": false,
6825
6809
  "type": "option"
6826
6810
  },
6827
- "path": {
6828
- "char": "p",
6829
- "description": "Path to the file export project",
6830
- "name": "path",
6831
- "hasDynamicHelp": false,
6832
- "multiple": false,
6833
- "type": "option"
6834
- },
6835
- "chunksize": {
6836
- "char": "c",
6837
- "description": "Number of records to add in a chunk before it is processed",
6838
- "name": "chunksize",
6839
- "default": 1000,
6840
- "hasDynamicHelp": false,
6841
- "multiple": false,
6842
- "type": "option"
6843
- },
6844
- "polltimeout": {
6845
- "char": "t",
6846
- "description": "Timeout in MS for Bulk API calls",
6847
- "name": "polltimeout",
6848
- "default": 300000,
6849
- "hasDynamicHelp": false,
6850
- "multiple": false,
6851
- "type": "option"
6852
- },
6853
- "startchunknumber": {
6854
- "char": "s",
6855
- "description": "Chunk number to start from",
6856
- "name": "startchunknumber",
6857
- "default": 0,
6811
+ "outputfile": {
6812
+ "char": "f",
6813
+ "description": "Force the path and name of output report file. Must end with .csv",
6814
+ "name": "outputfile",
6858
6815
  "hasDynamicHelp": false,
6859
6816
  "multiple": false,
6860
6817
  "type": "option"
6861
6818
  },
6862
- "resume": {
6863
- "char": "r",
6864
- "description": "Resume previous export by checking existing files (default in CI)",
6865
- "name": "resume",
6866
- "allowNo": false,
6867
- "type": "boolean"
6868
- },
6869
6819
  "debug": {
6870
6820
  "char": "d",
6871
6821
  "description": "Activate debug mode (more logs)",
@@ -6904,63 +6854,85 @@
6904
6854
  },
6905
6855
  "hasDynamicHelp": true,
6906
6856
  "hiddenAliases": [],
6907
- "id": "hardis:org:files:export",
6857
+ "id": "hardis:org:diagnose:unusedlicenses",
6908
6858
  "pluginAlias": "sfdx-hardis",
6909
6859
  "pluginName": "sfdx-hardis",
6910
6860
  "pluginType": "core",
6911
6861
  "strict": true,
6912
6862
  "enableJsonFlag": true,
6913
- "title": "Export files",
6863
+ "title": "Detect unused Permission Set Licenses (beta)",
6914
6864
  "requiresProject": false,
6865
+ "additionalPermissionSetsToAlwaysGet": [
6866
+ "Sales_User"
6867
+ ],
6868
+ "permSetsPermSetLicenses": [
6869
+ {
6870
+ "permSet": "Sales_User",
6871
+ "permSetLicense": "SalesUserPsl"
6872
+ }
6873
+ ],
6874
+ "profilesPermissionSetLicenses": [
6875
+ {
6876
+ "profile": "Salesforce API Only",
6877
+ "permSetLicense": "SalesforceAPIIntegrationPsl"
6878
+ }
6879
+ ],
6880
+ "alwaysExcludeForActiveUsersPermissionSetLicenses": [
6881
+ "IdentityConnect"
6882
+ ],
6915
6883
  "isESM": true,
6916
6884
  "relativePath": [
6917
6885
  "lib",
6918
6886
  "commands",
6919
6887
  "hardis",
6920
6888
  "org",
6921
- "files",
6922
- "export.js"
6889
+ "diagnose",
6890
+ "unusedlicenses.js"
6923
6891
  ],
6924
6892
  "aliasPermutations": [],
6925
6893
  "permutations": [
6926
- "hardis:org:files:export",
6927
- "org:hardis:files:export",
6928
- "org:files:hardis:export",
6929
- "org:files:export:hardis",
6930
- "hardis:files:org:export",
6931
- "files:hardis:org:export",
6932
- "files:org:hardis:export",
6933
- "files:org:export:hardis",
6934
- "hardis:files:export:org",
6935
- "files:hardis:export:org",
6936
- "files:export:hardis:org",
6937
- "files:export:org:hardis",
6938
- "hardis:org:export:files",
6939
- "org:hardis:export:files",
6940
- "org:export:hardis:files",
6941
- "org:export:files:hardis",
6942
- "hardis:export:org:files",
6943
- "export:hardis:org:files",
6944
- "export:org:hardis:files",
6945
- "export:org:files:hardis",
6946
- "hardis:export:files:org",
6947
- "export:hardis:files:org",
6948
- "export:files:hardis:org",
6949
- "export:files:org:hardis"
6950
- ]
6951
- },
6952
- "hardis:org:files:import": {
6953
- "aliases": [],
6954
- "args": {},
6955
- "description": "\nThis command facilitates the mass upload of files into Salesforce, allowing you to populate records with associated documents, images, or other file types. It's a crucial tool for data migration, content seeding, or synchronizing external file repositories with Salesforce.\n\nKey functionalities:\n\n- **Configuration-Driven Import:** Relies on an `export.json` file within a designated file export project (created using `sf hardis:org:configure:files`) to determine which files to import and how they should be associated with Salesforce records.\n- **Interactive Project Selection:** If the file import project path is not provided via the `--path` flag, it interactively prompts the user to select one.\n- **Overwrite Option:** The `--overwrite` flag allows you to replace existing files in Salesforce with local versions that have the same name. Be aware that this option doubles the number of API calls used.\n- **Support for ContentVersion and Attachment:** Handles both modern Salesforce Files (ContentVersion) and older Attachments.\n\nSee this article for how to export files, which is often a prerequisite for importing:\n\n[![How to mass download notes and attachments files from a Salesforce org](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-mass-download.jpg)](https://nicolas.vuillamy.fr/how-to-mass-download-notes-and-attachments-files-from-a-salesforce-org-83a028824afd)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **FilesImporter Class:** The core logic is encapsulated within the `FilesImporter` class, which orchestrates the entire import process.\n- **File System Scan:** Scans the local file system within the configured project directory to identify files for import.\n- **Salesforce API Interaction:** Uses Salesforce APIs (e.g., ContentVersion, Attachment) to upload files and associate them with records.\n- **Configuration Loading:** Reads the `export.json` file to get the import configuration, including SOQL queries to identify parent records for file association.\n- **Interactive Prompts:** Uses `selectFilesWorkspace` to allow the user to choose a file import project and `prompts` for confirming the overwrite behavior.\n- **Error Handling:** Includes mechanisms to handle potential errors during the import process, such as API limits or file upload failures.\n</details>\n",
6956
- "examples": [
6957
- "$ sf hardis:org:files:import"
6958
- ],
6959
- "flags": {
6960
- "json": {
6961
- "description": "Format output as json.",
6962
- "helpGroup": "GLOBAL",
6963
- "name": "json",
6894
+ "hardis:org:diagnose:unusedlicenses",
6895
+ "org:hardis:diagnose:unusedlicenses",
6896
+ "org:diagnose:hardis:unusedlicenses",
6897
+ "org:diagnose:unusedlicenses:hardis",
6898
+ "hardis:diagnose:org:unusedlicenses",
6899
+ "diagnose:hardis:org:unusedlicenses",
6900
+ "diagnose:org:hardis:unusedlicenses",
6901
+ "diagnose:org:unusedlicenses:hardis",
6902
+ "hardis:diagnose:unusedlicenses:org",
6903
+ "diagnose:hardis:unusedlicenses:org",
6904
+ "diagnose:unusedlicenses:hardis:org",
6905
+ "diagnose:unusedlicenses:org:hardis",
6906
+ "hardis:org:unusedlicenses:diagnose",
6907
+ "org:hardis:unusedlicenses:diagnose",
6908
+ "org:unusedlicenses:hardis:diagnose",
6909
+ "org:unusedlicenses:diagnose:hardis",
6910
+ "hardis:unusedlicenses:org:diagnose",
6911
+ "unusedlicenses:hardis:org:diagnose",
6912
+ "unusedlicenses:org:hardis:diagnose",
6913
+ "unusedlicenses:org:diagnose:hardis",
6914
+ "hardis:unusedlicenses:diagnose:org",
6915
+ "unusedlicenses:hardis:diagnose:org",
6916
+ "unusedlicenses:diagnose:hardis:org",
6917
+ "unusedlicenses:diagnose:org:hardis"
6918
+ ]
6919
+ },
6920
+ "hardis:org:diagnose:unusedusers": {
6921
+ "aliases": [],
6922
+ "args": {},
6923
+ "description": "\n## Command Behavior\n\n**Detects and reports on inactive or unused Salesforce user accounts, helping to optimize license usage and enhance security.**\n\nEfficient user management is vital in Salesforce to ensure resources are optimized and costs are controlled. However, inactive or unused user accounts can often go unnoticed, leading to wasted licenses and potential security risks. This tool addresses this challenge by enabling administrators to identify users who haven't logged in within a specified period.\n\nBy analyzing user login activity and last login timestamps, this feature highlights inactive user accounts, allowing administrators to take appropriate action. Whether it's deactivating dormant accounts, freeing up licenses, or ensuring compliance with security policies, this functionality empowers administrators to maintain a lean and secure Salesforce environment.\n\nKey functionalities:\n\n- **Inactivity Detection:** Identifies users who have not logged in for a specified number of days (`--days` flag, default 180 days in CI, 365 days otherwise).\n- **License Type Filtering:** Allows filtering users by license type using `--licensetypes` (e.g., `all-crm`, `all-paying`) or specific license identifiers using `--licenseidentifiers`.\n - `all-crm`: Includes `SFDC`, `AUL`, `AUL1`, `AULL_IGHT` licenses.\n - `all-paying`: Includes `SFDC`, `AUL`, `AUL1`, `AULL_IGHT`, `PID_Customer_Community`, `PID_Customer_Community_Login`, `PID_Partner_Community`, `PID_Partner_Community_Login` licenses.\n - Note: You can see the full list of available license identifiers in [Salesforce Documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/sfdx_cli_reference/sforce_api_objects_userlicense.htm).\n- **Active User Retrieval:** The `--returnactiveusers` flag inverts the command, allowing you to retrieve active users who *have* logged in during the specified period.\n- **CSV Report Generation:** Generates a CSV file containing details of all identified users (inactive or active), including their last login date, profile, and license information.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of inactive or active users.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-inactive-users/) and can output Grafana, Slack and MsTeams Notifications.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query (Bulk API):** It uses `bulkQuery` to efficiently retrieve user records from the Salesforce `User` object. The SOQL query dynamically constructs its WHERE clause based on the `--days`, `--licensetypes`, `--licenseidentifiers`, and `--returnactiveusers` flags.\n- **Interactive Prompts:** Uses `prompts` to interactively ask the user for the number of inactive days and license types if not provided via flags.\n- **License Mapping:** Internally maps common license type aliases (e.g., `all-crm`) to their corresponding Salesforce `LicenseDefinitionKey` values.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of users.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and metrics for monitoring dashboards.\n- **User Feedback:** Provides a summary of the findings in the console, indicating the number of inactive or active users found.\n</details>",
6924
+ "examples": [
6925
+ "$ sf hardis:org:diagnose:unusedusers",
6926
+ "$ sf hardis:org:diagnose:unusedusers --days 365",
6927
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm",
6928
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1",
6929
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers"
6930
+ ],
6931
+ "flags": {
6932
+ "json": {
6933
+ "description": "Format output as json.",
6934
+ "helpGroup": "GLOBAL",
6935
+ "name": "json",
6964
6936
  "allowNo": false,
6965
6937
  "type": "boolean"
6966
6938
  },
@@ -6972,18 +6944,46 @@
6972
6944
  "multiple": false,
6973
6945
  "type": "option"
6974
6946
  },
6975
- "path": {
6976
- "char": "p",
6977
- "description": "Path to the file export project",
6978
- "name": "path",
6947
+ "outputfile": {
6948
+ "char": "f",
6949
+ "description": "Force the path and name of output report file. Must end with .csv",
6950
+ "name": "outputfile",
6979
6951
  "hasDynamicHelp": false,
6980
6952
  "multiple": false,
6981
6953
  "type": "option"
6982
6954
  },
6983
- "overwrite": {
6984
- "char": "f",
6985
- "description": "Override existing files (doubles the number of API calls)",
6986
- "name": "overwrite",
6955
+ "days": {
6956
+ "char": "t",
6957
+ "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
6958
+ "name": "days",
6959
+ "hasDynamicHelp": false,
6960
+ "multiple": false,
6961
+ "type": "option"
6962
+ },
6963
+ "licensetypes": {
6964
+ "char": "l",
6965
+ "description": "Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm",
6966
+ "name": "licensetypes",
6967
+ "hasDynamicHelp": false,
6968
+ "multiple": false,
6969
+ "options": [
6970
+ "all",
6971
+ "all-crm",
6972
+ "all-paying"
6973
+ ],
6974
+ "type": "option"
6975
+ },
6976
+ "licenseidentifiers": {
6977
+ "char": "i",
6978
+ "description": "Comma-separated list of license identifiers, in case licensetypes is not used.. Identifiers available at https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_userlicense.htm",
6979
+ "name": "licenseidentifiers",
6980
+ "hasDynamicHelp": false,
6981
+ "multiple": false,
6982
+ "type": "option"
6983
+ },
6984
+ "returnactiveusers": {
6985
+ "description": "Inverts the command by returning the active users",
6986
+ "name": "returnactiveusers",
6987
6987
  "allowNo": false,
6988
6988
  "type": "boolean"
6989
6989
  },
@@ -7025,13 +7025,13 @@
7025
7025
  },
7026
7026
  "hasDynamicHelp": true,
7027
7027
  "hiddenAliases": [],
7028
- "id": "hardis:org:files:import",
7028
+ "id": "hardis:org:diagnose:unusedusers",
7029
7029
  "pluginAlias": "sfdx-hardis",
7030
7030
  "pluginName": "sfdx-hardis",
7031
7031
  "pluginType": "core",
7032
7032
  "strict": true,
7033
7033
  "enableJsonFlag": true,
7034
- "title": "Import files",
7034
+ "title": "Detect unused Users in Salesforce",
7035
7035
  "requiresProject": false,
7036
7036
  "isESM": true,
7037
7037
  "relativePath": [
@@ -7039,35 +7039,35 @@
7039
7039
  "commands",
7040
7040
  "hardis",
7041
7041
  "org",
7042
- "files",
7043
- "import.js"
7042
+ "diagnose",
7043
+ "unusedusers.js"
7044
7044
  ],
7045
7045
  "aliasPermutations": [],
7046
7046
  "permutations": [
7047
- "hardis:org:files:import",
7048
- "org:hardis:files:import",
7049
- "org:files:hardis:import",
7050
- "org:files:import:hardis",
7051
- "hardis:files:org:import",
7052
- "files:hardis:org:import",
7053
- "files:org:hardis:import",
7054
- "files:org:import:hardis",
7055
- "hardis:files:import:org",
7056
- "files:hardis:import:org",
7057
- "files:import:hardis:org",
7058
- "files:import:org:hardis",
7059
- "hardis:org:import:files",
7060
- "org:hardis:import:files",
7061
- "org:import:hardis:files",
7062
- "org:import:files:hardis",
7063
- "hardis:import:org:files",
7064
- "import:hardis:org:files",
7065
- "import:org:hardis:files",
7066
- "import:org:files:hardis",
7067
- "hardis:import:files:org",
7068
- "import:hardis:files:org",
7069
- "import:files:hardis:org",
7070
- "import:files:org:hardis"
7047
+ "hardis:org:diagnose:unusedusers",
7048
+ "org:hardis:diagnose:unusedusers",
7049
+ "org:diagnose:hardis:unusedusers",
7050
+ "org:diagnose:unusedusers:hardis",
7051
+ "hardis:diagnose:org:unusedusers",
7052
+ "diagnose:hardis:org:unusedusers",
7053
+ "diagnose:org:hardis:unusedusers",
7054
+ "diagnose:org:unusedusers:hardis",
7055
+ "hardis:diagnose:unusedusers:org",
7056
+ "diagnose:hardis:unusedusers:org",
7057
+ "diagnose:unusedusers:hardis:org",
7058
+ "diagnose:unusedusers:org:hardis",
7059
+ "hardis:org:unusedusers:diagnose",
7060
+ "org:hardis:unusedusers:diagnose",
7061
+ "org:unusedusers:hardis:diagnose",
7062
+ "org:unusedusers:diagnose:hardis",
7063
+ "hardis:unusedusers:org:diagnose",
7064
+ "unusedusers:hardis:org:diagnose",
7065
+ "unusedusers:org:hardis:diagnose",
7066
+ "unusedusers:org:diagnose:hardis",
7067
+ "hardis:unusedusers:diagnose:org",
7068
+ "unusedusers:hardis:diagnose:org",
7069
+ "unusedusers:diagnose:hardis:org",
7070
+ "unusedusers:diagnose:org:hardis"
7071
7071
  ]
7072
7072
  },
7073
7073
  "hardis:org:fix:listviewmine": {
@@ -7788,16 +7788,13 @@
7788
7788
  "limits:monitor:org:hardis"
7789
7789
  ]
7790
7790
  },
7791
- "hardis:org:refresh:after-refresh": {
7791
+ "hardis:org:purge:apexlog": {
7792
7792
  "aliases": [],
7793
7793
  "args": {},
7794
- "description": "\n## Command Behavior\n\n**Restores all previously backed-up Connected Apps (including Consumer Secrets), certificates, custom settings, records and other metadata to a Salesforce org after a sandbox refresh.**\n\nThis command is the second step in the sandbox refresh process. It scans the backup folder created before the refresh, allows interactive or flag-driven selection of items to restore, and automates cleanup and redeployment to the refreshed org while preserving credentials and configuration.\n\nKey functionalities:\n\n- **Choose a backup to restore:** Lets you pick the saved sandbox project that contains the artifacts to restore.\n- **Select which items to restore:** Finds Connected App XMLs, certificates, custom settings and other artifacts and lets you pick what to restore (or restore all).\n- **Safety checks and validation:** Confirms files exist and prompts before making changes to the target org.\n- **Prepare org for restore:** Optionally cleans up existing Connected Apps so saved apps can be re-deployed without conflict.\n- **Redeploy saved artifacts:** Restores Connected Apps (with saved secrets), certificates, SAML SSO configs, custom settings and other metadata.\n- **Handle SAML configs:** Cleans and updates SAML XML files and helps you choose certificates to wire into restored configs.\n- **Restore records:** Optionally runs data import from selected SFDMU workspaces to restore record data.\n- **Reporting & persistence:** Sends restore reports and can update project config to record what was restored.\n\nThis command is part of [sfdx-hardis Sandbox Refresh](https://sfdx-hardis.cloudity.com/salesforce-sandbox-refresh/) and is intended to be run after a sandbox refresh to re-apply saved metadata, credentials and data.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\n- **Backup Folder Handling:** Reads the immediate subfolders of `scripts/sandbox-refresh/` and validates the chosen project contains the expected `manifest/` and `force-app` layout.\n- **Metadata & Deployment APIs:** Uses `sf project deploy start --manifest` for package-based deploys, `sf project deploy start --metadata-dir` for MDAPI artifacts (certificates), and utility functions for Connected App deployment that preserve consumer secrets.\n- **SAML Handling:** Queries active certificates via tooling API, updates SAML XML files, and deploys using `sf project deploy start -m SamlSsoConfig`.\n- **Records Handling:** Uses interactive selection of SFDMU workspaces and runs data import utilities to restore records.\n- **Error Handling & Summary:** Aggregates results, logs success/warnings/errors, and returns a structured result indicating which items were restored and any failures.\n\n</details>\n",
7794
+ "description": "\n**Purges Apex debug logs from a Salesforce org.**\n\nThis command provides a quick and efficient way to clear out accumulated Apex debug logs from your Salesforce environment. This is particularly useful for:\n\n- **Storage Management:** Freeing up valuable data storage space in your Salesforce org.\n- **Performance Optimization:** Reducing the overhead associated with large volumes of debug logs.\n- **Troubleshooting:** Ensuring that new debug logs are generated cleanly without interference from old, irrelevant logs.\n\nKey functionalities:\n\n- **Log Identification:** Queries the `ApexLog` object to identify all existing debug logs.\n- **Confirmation Prompt:** Before deletion, it prompts for user confirmation, displaying the number of Apex logs that will be deleted.\n- **Bulk Deletion:** Uses the Salesforce Bulk API to efficiently delete a large number of Apex logs.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query:** It executes a SOQL query (`SELECT Id FROM ApexLog LIMIT 50000`) to retrieve the IDs of Apex logs to be deleted. The limit is set to 50,000 to handle large volumes of logs.\n- **CSV Export:** The retrieved log IDs are temporarily exported to a CSV file (`ApexLogsToDelete_*.csv`) in the `./tmp` directory.\n- **User Confirmation:** It uses the `prompts` library to ask for user confirmation before proceeding with the deletion, displaying the count of logs to be purged.\n- **Bulk API Deletion:** It then uses the Salesforce CLI's `sf data delete bulk` command, pointing to the generated CSV file, to perform the mass deletion of Apex logs.\n- **File System Operations:** It uses `fs-extra` to create the temporary directory and manage the CSV file.\n- **Error Handling:** Includes error handling for the query and deletion operations.\n</details>\n",
7795
7795
  "examples": [
7796
- "$ sf hardis:org:refresh:after-refresh",
7797
- "$ sf hardis:org:refresh:after-refresh --name \"MyConnectedApp\" // Process specific app, no selection prompt",
7798
- "$ sf hardis:org:refresh:after-refresh --name \"App1,App2,App3\" // Process multiple apps, no selection prompt",
7799
- "$ sf hardis:org:refresh:after-refresh --all // Process all apps, no selection prompt",
7800
- "$ sf hardis:org:refresh:after-refresh --target-org myDevOrg"
7796
+ "$ sf hardis:org:purge:apexlog",
7797
+ "$ sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com"
7801
7798
  ],
7802
7799
  "flags": {
7803
7800
  "json": {
@@ -7815,37 +7812,23 @@
7815
7812
  "multiple": false,
7816
7813
  "type": "option"
7817
7814
  },
7818
- "target-org": {
7819
- "char": "o",
7820
- "name": "target-org",
7821
- "noCacheDefault": true,
7822
- "required": true,
7823
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
7824
- "hasDynamicHelp": true,
7825
- "multiple": false,
7826
- "type": "option"
7827
- },
7828
- "name": {
7829
- "char": "n",
7830
- "description": "Connected App name(s) to process (bypasses selection prompt). For multiple apps, separate with commas (e.g., \"App1,App2\")",
7831
- "name": "name",
7832
- "summary": "Filter according to Name criteria",
7833
- "hasDynamicHelp": false,
7834
- "multiple": false,
7835
- "type": "option"
7815
+ "prompt": {
7816
+ "char": "z",
7817
+ "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7818
+ "name": "prompt",
7819
+ "allowNo": true,
7820
+ "type": "boolean"
7836
7821
  },
7837
- "all": {
7838
- "char": "a",
7839
- "description": "If set, all Connected Apps from the local repository will be processed. Takes precedence over --name if both are specified.",
7840
- "name": "all",
7841
- "summary": "Process all Connected Apps without selection prompt",
7822
+ "debug": {
7823
+ "char": "d",
7824
+ "description": "Activate debug mode (more logs)",
7825
+ "name": "debug",
7842
7826
  "allowNo": false,
7843
7827
  "type": "boolean"
7844
7828
  },
7845
7829
  "websocket": {
7846
7830
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
7847
7831
  "name": "websocket",
7848
- "summary": "Websocket host:port for VsCode SFDX Hardis UI integration",
7849
7832
  "hasDynamicHelp": false,
7850
7833
  "multiple": false,
7851
7834
  "type": "option"
@@ -7853,75 +7836,87 @@
7853
7836
  "skipauth": {
7854
7837
  "description": "Skip authentication check when a default username is required",
7855
7838
  "name": "skipauth",
7856
- "summary": "Skip authentication check when a default username is required",
7857
7839
  "allowNo": false,
7858
7840
  "type": "boolean"
7841
+ },
7842
+ "target-org": {
7843
+ "aliases": [
7844
+ "targetusername",
7845
+ "u"
7846
+ ],
7847
+ "char": "o",
7848
+ "deprecateAliases": true,
7849
+ "name": "target-org",
7850
+ "noCacheDefault": true,
7851
+ "required": true,
7852
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
7853
+ "hasDynamicHelp": true,
7854
+ "multiple": false,
7855
+ "type": "option"
7859
7856
  }
7860
7857
  },
7861
7858
  "hasDynamicHelp": true,
7862
7859
  "hiddenAliases": [],
7863
- "id": "hardis:org:refresh:after-refresh",
7860
+ "id": "hardis:org:purge:apexlog",
7864
7861
  "pluginAlias": "sfdx-hardis",
7865
7862
  "pluginName": "sfdx-hardis",
7866
7863
  "pluginType": "core",
7867
7864
  "strict": true,
7868
7865
  "enableJsonFlag": true,
7869
- "title": "Restore Connected Apps after org refresh",
7870
- "requiresProject": true,
7866
+ "title": "Purge Apex Logs",
7867
+ "requiresProject": false,
7871
7868
  "isESM": true,
7872
7869
  "relativePath": [
7873
7870
  "lib",
7874
7871
  "commands",
7875
7872
  "hardis",
7876
7873
  "org",
7877
- "refresh",
7878
- "after-refresh.js"
7874
+ "purge",
7875
+ "apexlog.js"
7879
7876
  ],
7880
7877
  "aliasPermutations": [],
7881
7878
  "permutations": [
7882
- "hardis:org:refresh:after-refresh",
7883
- "org:hardis:refresh:after-refresh",
7884
- "org:refresh:hardis:after-refresh",
7885
- "org:refresh:after-refresh:hardis",
7886
- "hardis:refresh:org:after-refresh",
7887
- "refresh:hardis:org:after-refresh",
7888
- "refresh:org:hardis:after-refresh",
7889
- "refresh:org:after-refresh:hardis",
7890
- "hardis:refresh:after-refresh:org",
7891
- "refresh:hardis:after-refresh:org",
7892
- "refresh:after-refresh:hardis:org",
7893
- "refresh:after-refresh:org:hardis",
7894
- "hardis:org:after-refresh:refresh",
7895
- "org:hardis:after-refresh:refresh",
7896
- "org:after-refresh:hardis:refresh",
7897
- "org:after-refresh:refresh:hardis",
7898
- "hardis:after-refresh:org:refresh",
7899
- "after-refresh:hardis:org:refresh",
7900
- "after-refresh:org:hardis:refresh",
7901
- "after-refresh:org:refresh:hardis",
7902
- "hardis:after-refresh:refresh:org",
7903
- "after-refresh:hardis:refresh:org",
7904
- "after-refresh:refresh:hardis:org",
7905
- "after-refresh:refresh:org:hardis"
7906
- ]
7907
- },
7908
- "hardis:org:refresh:before-refresh": {
7909
- "aliases": [],
7910
- "args": {},
7911
- "description": "\n## Command Behavior\n\n**Backs up all Connected Apps (including Consumer Secrets), certificates, custom settings, records and other metadata from a Salesforce org before a sandbox refresh, enabling full restoration after the refresh.**\n\nThis command prepares a complete backup prior to a sandbox refresh. It creates a dedicated project under `scripts/sandbox-refresh/<sandbox-folder>`, retrieves metadata and data, attempts to capture Connected App consumer secrets, and can optionally delete the apps so they can be reuploaded after the refresh.\n\nKey functionalities:\n\n- **Create a save project:** Generates a dedicated project folder to store all artifacts for the sandbox backup.\n- **Find and select Connected Apps:** Lists Connected Apps in the org and lets you pick specific apps, use a name filter, or process all apps.\n- **Save metadata for restore:** Builds a manifest and retrieves the metadata types you choose so they can be restored after the refresh.\n- **Capture Consumer Secrets:** Attempts to capture Connected App consumer secrets automatically (opens a browser session when possible) and falls back to a short manual prompt when needed.\n- **Collect certificates:** Saves certificate files and their definitions so they can be redeployed later.\n- **Export custom settings & records:** Lets you pick custom settings to export as JSON and optionally export records using configured data workspaces.\n- **Persist choices & report:** Stores your backup choices in project config and sends report files for traceability.\n- **Optional cleanup:** Can delete backed-up Connected Apps from the org so they can be re-uploaded cleanly after the refresh.\n- **Interactive safety checks:** Prompts you to confirm package contents and other potentially destructive actions; sensible defaults are chosen where appropriate.\n\nThis command is part of [sfdx-hardis Sandbox Refresh](https://sfdx-hardis.cloudity.com/salesforce-sandbox-refresh/) and is intended to be run before a sandbox refresh so that all credentials, certificates, metadata and data can be restored afterwards.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\n- **Salesforce CLI Integration:** Uses `sf org list metadata`, `sf project retrieve start`, `sf project generate`, `sf project deploy start`, and `sf data tree export`/`import` where applicable.\n- **Metadata Handling:** Writes and reads package XML files under the generated project (`manifest/`), copies MDAPI certificate artifacts into `force-app/main/default/certs`, and produces `package-metadata-to-restore.xml` for post-refresh deployment.\n- **Consumer Secret Handling:** Uses `puppeteer-core` with an executable path from `getChromeExecutablePath()` (env var `PUPPETEER_EXECUTABLE_PATH` may be required). Falls back to manual prompt when browser automation cannot be used.\n- **Data & Records:** Exports custom settings to JSON and supports exporting records through SFDMU workspaces chosen interactively.\n- **Config & Reporting:** Updates project/user config under `config/.sfdx-hardis.yml#refreshSandboxConfig` and reports artifacts to the WebSocket client.\n- **Error Handling:** Provides clear error messages and a summary response object indicating success/failure and which secrets were captured.\n\n</details>\n",
7912
- "examples": [
7913
- "$ sf hardis:org:refresh:before-refresh",
7914
- "$ sf hardis:org:refresh:before-refresh --name \"MyConnectedApp\"",
7915
- "$ sf hardis:org:refresh:before-refresh --name \"App1,App2,App3\"",
7916
- "$ sf hardis:org:refresh:before-refresh --all",
7917
- "$ sf hardis:org:refresh:before-refresh --delete"
7918
- ],
7919
- "flags": {
7920
- "json": {
7921
- "description": "Format output as json.",
7922
- "helpGroup": "GLOBAL",
7923
- "name": "json",
7924
- "allowNo": false,
7879
+ "hardis:org:purge:apexlog",
7880
+ "org:hardis:purge:apexlog",
7881
+ "org:purge:hardis:apexlog",
7882
+ "org:purge:apexlog:hardis",
7883
+ "hardis:purge:org:apexlog",
7884
+ "purge:hardis:org:apexlog",
7885
+ "purge:org:hardis:apexlog",
7886
+ "purge:org:apexlog:hardis",
7887
+ "hardis:purge:apexlog:org",
7888
+ "purge:hardis:apexlog:org",
7889
+ "purge:apexlog:hardis:org",
7890
+ "purge:apexlog:org:hardis",
7891
+ "hardis:org:apexlog:purge",
7892
+ "org:hardis:apexlog:purge",
7893
+ "org:apexlog:hardis:purge",
7894
+ "org:apexlog:purge:hardis",
7895
+ "hardis:apexlog:org:purge",
7896
+ "apexlog:hardis:org:purge",
7897
+ "apexlog:org:hardis:purge",
7898
+ "apexlog:org:purge:hardis",
7899
+ "hardis:apexlog:purge:org",
7900
+ "apexlog:hardis:purge:org",
7901
+ "apexlog:purge:hardis:org",
7902
+ "apexlog:purge:org:hardis"
7903
+ ]
7904
+ },
7905
+ "hardis:org:purge:flow": {
7906
+ "aliases": [],
7907
+ "args": {},
7908
+ "description": "\n**Purges old or unwanted Flow versions from a Salesforce org, with an option to delete related Flow Interviews.**\n\nThis command helps maintain a clean and performant Salesforce org by removing obsolete Flow versions. Over time, multiple versions of Flows can accumulate, consuming storage and potentially impacting performance. This tool provides a controlled way to clean up these versions.\n\nKey functionalities:\n\n- **Targeted Flow Selection:** Allows you to filter Flow versions to delete by name (`--name`) and status (`--status`, e.g., `Obsolete`, `Draft`, `Inactive`).\n- **Flow Interview Deletion:** If a Flow version cannot be deleted due to active Flow Interviews, the `--delete-flow-interviews` flag (or interactive prompt) allows you to delete these interviews first, then retry the Flow version deletion.\n- **Confirmation Prompt:** In interactive mode, it prompts for confirmation before proceeding with the deletion of Flow versions and Flow Interviews.\n- **Partial Success Handling:** The `--allowpurgefailure` flag (default `true`) allows the command to continue even if some deletions fail, reporting the errors.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Queries (Tooling API):** It queries the `Flow` object (using the Tooling API) to list Flow versions based on the provided filters (name, status, manageable state).\n- **Bulk Deletion (Tooling API):** It uses `bulkDeleteTooling` to perform mass deletions of Flow versions. If deletion fails due to active interviews, it extracts the interview IDs.\n- **Flow Interview Management:** If `delete-flow-interviews` is enabled, it queries `FlowInterview` objects, performs bulk deletion of the identified interviews using `bulkDelete`, and then retries the Flow version deletion.\n- **Interactive Prompts:** Uses the `prompts` library to interact with the user for selecting Flows, statuses, and confirming deletion actions.\n- **Error Reporting:** Logs detailed error messages for failed deletions, including the specific reasons.\n- **Command-Line Execution:** Uses `execSfdxJson` to execute Salesforce CLI commands for querying Flow data.\n</details>\n",
7909
+ "examples": [
7910
+ "$ sf hardis:org:purge:flow",
7911
+ "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --no-prompt --delete-flow-interviews",
7912
+ "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status \"Obsolete,Draft,InvalidDraft\" --name TestFlow"
7913
+ ],
7914
+ "flags": {
7915
+ "json": {
7916
+ "description": "Format output as json.",
7917
+ "helpGroup": "GLOBAL",
7918
+ "name": "json",
7919
+ "allowNo": false,
7925
7920
  "type": "boolean"
7926
7921
  },
7927
7922
  "flags-dir": {
@@ -7932,38 +7927,56 @@
7932
7927
  "multiple": false,
7933
7928
  "type": "option"
7934
7929
  },
7935
- "target-org": {
7936
- "char": "o",
7937
- "name": "target-org",
7938
- "noCacheDefault": true,
7939
- "required": true,
7940
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
7941
- "hasDynamicHelp": true,
7930
+ "prompt": {
7931
+ "char": "z",
7932
+ "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7933
+ "name": "prompt",
7934
+ "allowNo": true,
7935
+ "type": "boolean"
7936
+ },
7937
+ "name": {
7938
+ "char": "n",
7939
+ "description": "Filter according to Name criteria",
7940
+ "name": "name",
7941
+ "hasDynamicHelp": false,
7942
7942
  "multiple": false,
7943
7943
  "type": "option"
7944
7944
  },
7945
- "delete": {
7946
- "char": "d",
7947
- "description": "By default, Connected Apps are not deleted from the org after saving. Set this flag to force their deletion so they will be able to be reuploaded again after refreshing the org.",
7948
- "name": "delete",
7949
- "summary": "Delete Connected Apps from org after saving",
7945
+ "status": {
7946
+ "char": "s",
7947
+ "description": "Filter according to Status criteria",
7948
+ "name": "status",
7949
+ "hasDynamicHelp": false,
7950
+ "multiple": false,
7951
+ "type": "option"
7952
+ },
7953
+ "delete-flow-interviews": {
7954
+ "char": "w",
7955
+ "description": "If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions",
7956
+ "name": "delete-flow-interviews",
7950
7957
  "allowNo": false,
7951
7958
  "type": "boolean"
7952
7959
  },
7953
- "name": {
7954
- "char": "n",
7955
- "description": "Connected App name(s) to process. For multiple apps, separate with commas (e.g., \"App1,App2\")",
7956
- "name": "name",
7957
- "summary": "Filter according to Name criteria",
7960
+ "allowpurgefailure": {
7961
+ "char": "f",
7962
+ "description": "Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable",
7963
+ "name": "allowpurgefailure",
7964
+ "allowNo": true,
7965
+ "type": "boolean"
7966
+ },
7967
+ "instanceurl": {
7968
+ "char": "r",
7969
+ "description": "URL of org instance",
7970
+ "name": "instanceurl",
7971
+ "default": "https://login.salesforce.com",
7958
7972
  "hasDynamicHelp": false,
7959
7973
  "multiple": false,
7960
7974
  "type": "option"
7961
7975
  },
7962
- "all": {
7963
- "char": "a",
7964
- "description": "If set, all Connected Apps from the org will be processed. Takes precedence over --name if both are specified.",
7965
- "name": "all",
7966
- "summary": "Process all Connected Apps without selection prompt",
7976
+ "debug": {
7977
+ "char": "d",
7978
+ "description": "Activate debug mode (more logs)",
7979
+ "name": "debug",
7967
7980
  "allowNo": false,
7968
7981
  "type": "boolean"
7969
7982
  },
@@ -7979,61 +7992,80 @@
7979
7992
  "name": "skipauth",
7980
7993
  "allowNo": false,
7981
7994
  "type": "boolean"
7995
+ },
7996
+ "target-org": {
7997
+ "aliases": [
7998
+ "targetusername",
7999
+ "u"
8000
+ ],
8001
+ "char": "o",
8002
+ "deprecateAliases": true,
8003
+ "name": "target-org",
8004
+ "noCacheDefault": true,
8005
+ "required": true,
8006
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
8007
+ "hasDynamicHelp": true,
8008
+ "multiple": false,
8009
+ "type": "option"
7982
8010
  }
7983
8011
  },
7984
8012
  "hasDynamicHelp": true,
7985
8013
  "hiddenAliases": [],
7986
- "id": "hardis:org:refresh:before-refresh",
8014
+ "id": "hardis:org:purge:flow",
7987
8015
  "pluginAlias": "sfdx-hardis",
7988
8016
  "pluginName": "sfdx-hardis",
7989
8017
  "pluginType": "core",
7990
8018
  "strict": true,
7991
8019
  "enableJsonFlag": true,
7992
- "requiresProject": true,
8020
+ "title": "Purge Flow versions",
8021
+ "requiresProject": false,
7993
8022
  "isESM": true,
7994
8023
  "relativePath": [
7995
8024
  "lib",
7996
8025
  "commands",
7997
8026
  "hardis",
7998
8027
  "org",
7999
- "refresh",
8000
- "before-refresh.js"
8028
+ "purge",
8029
+ "flow.js"
8001
8030
  ],
8002
8031
  "aliasPermutations": [],
8003
8032
  "permutations": [
8004
- "hardis:org:refresh:before-refresh",
8005
- "org:hardis:refresh:before-refresh",
8006
- "org:refresh:hardis:before-refresh",
8007
- "org:refresh:before-refresh:hardis",
8008
- "hardis:refresh:org:before-refresh",
8009
- "refresh:hardis:org:before-refresh",
8010
- "refresh:org:hardis:before-refresh",
8011
- "refresh:org:before-refresh:hardis",
8012
- "hardis:refresh:before-refresh:org",
8013
- "refresh:hardis:before-refresh:org",
8014
- "refresh:before-refresh:hardis:org",
8015
- "refresh:before-refresh:org:hardis",
8016
- "hardis:org:before-refresh:refresh",
8017
- "org:hardis:before-refresh:refresh",
8018
- "org:before-refresh:hardis:refresh",
8019
- "org:before-refresh:refresh:hardis",
8020
- "hardis:before-refresh:org:refresh",
8021
- "before-refresh:hardis:org:refresh",
8022
- "before-refresh:org:hardis:refresh",
8023
- "before-refresh:org:refresh:hardis",
8024
- "hardis:before-refresh:refresh:org",
8025
- "before-refresh:hardis:refresh:org",
8026
- "before-refresh:refresh:hardis:org",
8027
- "before-refresh:refresh:org:hardis"
8033
+ "hardis:org:purge:flow",
8034
+ "org:hardis:purge:flow",
8035
+ "org:purge:hardis:flow",
8036
+ "org:purge:flow:hardis",
8037
+ "hardis:purge:org:flow",
8038
+ "purge:hardis:org:flow",
8039
+ "purge:org:hardis:flow",
8040
+ "purge:org:flow:hardis",
8041
+ "hardis:purge:flow:org",
8042
+ "purge:hardis:flow:org",
8043
+ "purge:flow:hardis:org",
8044
+ "purge:flow:org:hardis",
8045
+ "hardis:org:flow:purge",
8046
+ "org:hardis:flow:purge",
8047
+ "org:flow:hardis:purge",
8048
+ "org:flow:purge:hardis",
8049
+ "hardis:flow:org:purge",
8050
+ "flow:hardis:org:purge",
8051
+ "flow:org:hardis:purge",
8052
+ "flow:org:purge:hardis",
8053
+ "hardis:flow:purge:org",
8054
+ "flow:hardis:purge:org",
8055
+ "flow:purge:hardis:org",
8056
+ "flow:purge:org:hardis"
8028
8057
  ]
8029
8058
  },
8030
- "hardis:org:retrieve:packageconfig": {
8059
+ "hardis:org:refresh:after-refresh": {
8031
8060
  "aliases": [],
8032
8061
  "args": {},
8033
- "description": "\n**Retrieves the installed package configuration from a Salesforce org and optionally updates the local project configuration.**\n\nThis command is useful for maintaining an accurate record of installed packages within your Salesforce project, which is crucial for managing dependencies and ensuring consistent deployments across environments.\n\nKey functionalities:\n\n- **Package Listing:** Connects to a specified Salesforce org (or prompts for one if not provided) and retrieves a list of all installed packages.\n- **Configuration Update:** Offers the option to update your local project's configuration with the retrieved list of installed packages. This can be beneficial for automating package installations during environment setup or CI/CD processes.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Org Connection:** It establishes a connection to the target Salesforce org using the provided or prompted username.\n- **Metadata Retrieval:** It utilizes `MetadataUtils.listInstalledPackages` to query the Salesforce org and obtain details about the installed packages.\n- **Interactive Prompt:** It uses the `prompts` library to ask the user whether they want to update their local project configuration with the retrieved package list.\n- **Configuration Management:** If the user confirms, it calls `managePackageConfig` to update the project's configuration file (likely `.sfdx-hardis.yml`) with the new package information.\n- **User Feedback:** Provides clear messages to the user about the success of the package retrieval and configuration update.\n</details>\n",
8062
+ "description": "\n## Command Behavior\n\n**Restores all previously backed-up Connected Apps (including Consumer Secrets), certificates, custom settings, records and other metadata to a Salesforce org after a sandbox refresh.**\n\nThis command is the second step in the sandbox refresh process. It scans the backup folder created before the refresh, allows interactive or flag-driven selection of items to restore, and automates cleanup and redeployment to the refreshed org while preserving credentials and configuration.\n\nKey functionalities:\n\n- **Choose a backup to restore:** Lets you pick the saved sandbox project that contains the artifacts to restore.\n- **Select which items to restore:** Finds Connected App XMLs, certificates, custom settings and other artifacts and lets you pick what to restore (or restore all).\n- **Safety checks and validation:** Confirms files exist and prompts before making changes to the target org.\n- **Prepare org for restore:** Optionally cleans up existing Connected Apps so saved apps can be re-deployed without conflict.\n- **Redeploy saved artifacts:** Restores Connected Apps (with saved secrets), certificates, SAML SSO configs, custom settings and other metadata.\n- **Handle SAML configs:** Cleans and updates SAML XML files and helps you choose certificates to wire into restored configs.\n- **Restore records:** Optionally runs data import from selected SFDMU workspaces to restore record data.\n- **Reporting & persistence:** Sends restore reports and can update project config to record what was restored.\n\nThis command is part of [sfdx-hardis Sandbox Refresh](https://sfdx-hardis.cloudity.com/salesforce-sandbox-refresh/) and is intended to be run after a sandbox refresh to re-apply saved metadata, credentials and data.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\n- **Backup Folder Handling:** Reads the immediate subfolders of `scripts/sandbox-refresh/` and validates the chosen project contains the expected `manifest/` and `force-app` layout.\n- **Metadata & Deployment APIs:** Uses `sf project deploy start --manifest` for package-based deploys, `sf project deploy start --metadata-dir` for MDAPI artifacts (certificates), and utility functions for Connected App deployment that preserve consumer secrets.\n- **SAML Handling:** Queries active certificates via tooling API, updates SAML XML files, and deploys using `sf project deploy start -m SamlSsoConfig`.\n- **Records Handling:** Uses interactive selection of SFDMU workspaces and runs data import utilities to restore records.\n- **Error Handling & Summary:** Aggregates results, logs success/warnings/errors, and returns a structured result indicating which items were restored and any failures.\n\n</details>\n",
8034
8063
  "examples": [
8035
- "$ sf hardis:org:retrieve:packageconfig",
8036
- "sf hardis:org:retrieve:packageconfig -u myOrg"
8064
+ "$ sf hardis:org:refresh:after-refresh",
8065
+ "$ sf hardis:org:refresh:after-refresh --name \"MyConnectedApp\" // Process specific app, no selection prompt",
8066
+ "$ sf hardis:org:refresh:after-refresh --name \"App1,App2,App3\" // Process multiple apps, no selection prompt",
8067
+ "$ sf hardis:org:refresh:after-refresh --all // Process all apps, no selection prompt",
8068
+ "$ sf hardis:org:refresh:after-refresh --target-org myDevOrg"
8037
8069
  ],
8038
8070
  "flags": {
8039
8071
  "json": {
@@ -8051,16 +8083,37 @@
8051
8083
  "multiple": false,
8052
8084
  "type": "option"
8053
8085
  },
8054
- "debug": {
8055
- "char": "d",
8056
- "description": "Activate debug mode (more logs)",
8057
- "name": "debug",
8086
+ "target-org": {
8087
+ "char": "o",
8088
+ "name": "target-org",
8089
+ "noCacheDefault": true,
8090
+ "required": true,
8091
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
8092
+ "hasDynamicHelp": true,
8093
+ "multiple": false,
8094
+ "type": "option"
8095
+ },
8096
+ "name": {
8097
+ "char": "n",
8098
+ "description": "Connected App name(s) to process (bypasses selection prompt). For multiple apps, separate with commas (e.g., \"App1,App2\")",
8099
+ "name": "name",
8100
+ "summary": "Filter according to Name criteria",
8101
+ "hasDynamicHelp": false,
8102
+ "multiple": false,
8103
+ "type": "option"
8104
+ },
8105
+ "all": {
8106
+ "char": "a",
8107
+ "description": "If set, all Connected Apps from the local repository will be processed. Takes precedence over --name if both are specified.",
8108
+ "name": "all",
8109
+ "summary": "Process all Connected Apps without selection prompt",
8058
8110
  "allowNo": false,
8059
8111
  "type": "boolean"
8060
8112
  },
8061
8113
  "websocket": {
8062
8114
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
8063
8115
  "name": "websocket",
8116
+ "summary": "Websocket host:port for VsCode SFDX Hardis UI integration",
8064
8117
  "hasDynamicHelp": false,
8065
8118
  "multiple": false,
8066
8119
  "type": "option"
@@ -8068,79 +8121,68 @@
8068
8121
  "skipauth": {
8069
8122
  "description": "Skip authentication check when a default username is required",
8070
8123
  "name": "skipauth",
8124
+ "summary": "Skip authentication check when a default username is required",
8071
8125
  "allowNo": false,
8072
8126
  "type": "boolean"
8073
- },
8074
- "target-org": {
8075
- "aliases": [
8076
- "targetusername",
8077
- "u"
8078
- ],
8079
- "char": "o",
8080
- "deprecateAliases": true,
8081
- "name": "target-org",
8082
- "noCacheDefault": true,
8083
- "required": true,
8084
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
8085
- "hasDynamicHelp": true,
8086
- "multiple": false,
8087
- "type": "option"
8088
8127
  }
8089
8128
  },
8090
8129
  "hasDynamicHelp": true,
8091
8130
  "hiddenAliases": [],
8092
- "id": "hardis:org:retrieve:packageconfig",
8131
+ "id": "hardis:org:refresh:after-refresh",
8093
8132
  "pluginAlias": "sfdx-hardis",
8094
8133
  "pluginName": "sfdx-hardis",
8095
8134
  "pluginType": "core",
8096
8135
  "strict": true,
8097
8136
  "enableJsonFlag": true,
8098
- "title": "Retrieve package configuration from an org",
8099
- "requiresProject": false,
8137
+ "title": "Restore Connected Apps after org refresh",
8138
+ "requiresProject": true,
8100
8139
  "isESM": true,
8101
8140
  "relativePath": [
8102
8141
  "lib",
8103
8142
  "commands",
8104
8143
  "hardis",
8105
8144
  "org",
8106
- "retrieve",
8107
- "packageconfig.js"
8145
+ "refresh",
8146
+ "after-refresh.js"
8108
8147
  ],
8109
8148
  "aliasPermutations": [],
8110
8149
  "permutations": [
8111
- "hardis:org:retrieve:packageconfig",
8112
- "org:hardis:retrieve:packageconfig",
8113
- "org:retrieve:hardis:packageconfig",
8114
- "org:retrieve:packageconfig:hardis",
8115
- "hardis:retrieve:org:packageconfig",
8116
- "retrieve:hardis:org:packageconfig",
8117
- "retrieve:org:hardis:packageconfig",
8118
- "retrieve:org:packageconfig:hardis",
8119
- "hardis:retrieve:packageconfig:org",
8120
- "retrieve:hardis:packageconfig:org",
8121
- "retrieve:packageconfig:hardis:org",
8122
- "retrieve:packageconfig:org:hardis",
8123
- "hardis:org:packageconfig:retrieve",
8124
- "org:hardis:packageconfig:retrieve",
8125
- "org:packageconfig:hardis:retrieve",
8126
- "org:packageconfig:retrieve:hardis",
8127
- "hardis:packageconfig:org:retrieve",
8128
- "packageconfig:hardis:org:retrieve",
8129
- "packageconfig:org:hardis:retrieve",
8130
- "packageconfig:org:retrieve:hardis",
8131
- "hardis:packageconfig:retrieve:org",
8132
- "packageconfig:hardis:retrieve:org",
8133
- "packageconfig:retrieve:hardis:org",
8134
- "packageconfig:retrieve:org:hardis"
8150
+ "hardis:org:refresh:after-refresh",
8151
+ "org:hardis:refresh:after-refresh",
8152
+ "org:refresh:hardis:after-refresh",
8153
+ "org:refresh:after-refresh:hardis",
8154
+ "hardis:refresh:org:after-refresh",
8155
+ "refresh:hardis:org:after-refresh",
8156
+ "refresh:org:hardis:after-refresh",
8157
+ "refresh:org:after-refresh:hardis",
8158
+ "hardis:refresh:after-refresh:org",
8159
+ "refresh:hardis:after-refresh:org",
8160
+ "refresh:after-refresh:hardis:org",
8161
+ "refresh:after-refresh:org:hardis",
8162
+ "hardis:org:after-refresh:refresh",
8163
+ "org:hardis:after-refresh:refresh",
8164
+ "org:after-refresh:hardis:refresh",
8165
+ "org:after-refresh:refresh:hardis",
8166
+ "hardis:after-refresh:org:refresh",
8167
+ "after-refresh:hardis:org:refresh",
8168
+ "after-refresh:org:hardis:refresh",
8169
+ "after-refresh:org:refresh:hardis",
8170
+ "hardis:after-refresh:refresh:org",
8171
+ "after-refresh:hardis:refresh:org",
8172
+ "after-refresh:refresh:hardis:org",
8173
+ "after-refresh:refresh:org:hardis"
8135
8174
  ]
8136
8175
  },
8137
- "hardis:org:purge:apexlog": {
8176
+ "hardis:org:refresh:before-refresh": {
8138
8177
  "aliases": [],
8139
8178
  "args": {},
8140
- "description": "\n**Purges Apex debug logs from a Salesforce org.**\n\nThis command provides a quick and efficient way to clear out accumulated Apex debug logs from your Salesforce environment. This is particularly useful for:\n\n- **Storage Management:** Freeing up valuable data storage space in your Salesforce org.\n- **Performance Optimization:** Reducing the overhead associated with large volumes of debug logs.\n- **Troubleshooting:** Ensuring that new debug logs are generated cleanly without interference from old, irrelevant logs.\n\nKey functionalities:\n\n- **Log Identification:** Queries the `ApexLog` object to identify all existing debug logs.\n- **Confirmation Prompt:** Before deletion, it prompts for user confirmation, displaying the number of Apex logs that will be deleted.\n- **Bulk Deletion:** Uses the Salesforce Bulk API to efficiently delete a large number of Apex logs.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Query:** It executes a SOQL query (`SELECT Id FROM ApexLog LIMIT 50000`) to retrieve the IDs of Apex logs to be deleted. The limit is set to 50,000 to handle large volumes of logs.\n- **CSV Export:** The retrieved log IDs are temporarily exported to a CSV file (`ApexLogsToDelete_*.csv`) in the `./tmp` directory.\n- **User Confirmation:** It uses the `prompts` library to ask for user confirmation before proceeding with the deletion, displaying the count of logs to be purged.\n- **Bulk API Deletion:** It then uses the Salesforce CLI's `sf data delete bulk` command, pointing to the generated CSV file, to perform the mass deletion of Apex logs.\n- **File System Operations:** It uses `fs-extra` to create the temporary directory and manage the CSV file.\n- **Error Handling:** Includes error handling for the query and deletion operations.\n</details>\n",
8179
+ "description": "\n## Command Behavior\n\n**Backs up all Connected Apps (including Consumer Secrets), certificates, custom settings, records and other metadata from a Salesforce org before a sandbox refresh, enabling full restoration after the refresh.**\n\nThis command prepares a complete backup prior to a sandbox refresh. It creates a dedicated project under `scripts/sandbox-refresh/<sandbox-folder>`, retrieves metadata and data, attempts to capture Connected App consumer secrets, and can optionally delete the apps so they can be reuploaded after the refresh.\n\nKey functionalities:\n\n- **Create a save project:** Generates a dedicated project folder to store all artifacts for the sandbox backup.\n- **Find and select Connected Apps:** Lists Connected Apps in the org and lets you pick specific apps, use a name filter, or process all apps.\n- **Save metadata for restore:** Builds a manifest and retrieves the metadata types you choose so they can be restored after the refresh.\n- **Capture Consumer Secrets:** Attempts to capture Connected App consumer secrets automatically (opens a browser session when possible) and falls back to a short manual prompt when needed.\n- **Collect certificates:** Saves certificate files and their definitions so they can be redeployed later.\n- **Export custom settings & records:** Lets you pick custom settings to export as JSON and optionally export records using configured data workspaces.\n- **Persist choices & report:** Stores your backup choices in project config and sends report files for traceability.\n- **Optional cleanup:** Can delete backed-up Connected Apps from the org so they can be re-uploaded cleanly after the refresh.\n- **Interactive safety checks:** Prompts you to confirm package contents and other potentially destructive actions; sensible defaults are chosen where appropriate.\n\nThis command is part of [sfdx-hardis Sandbox Refresh](https://sfdx-hardis.cloudity.com/salesforce-sandbox-refresh/) and is intended to be run before a sandbox refresh so that all credentials, certificates, metadata and data can be restored afterwards.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\n- **Salesforce CLI Integration:** Uses `sf org list metadata`, `sf project retrieve start`, `sf project generate`, `sf project deploy start`, and `sf data tree export`/`import` where applicable.\n- **Metadata Handling:** Writes and reads package XML files under the generated project (`manifest/`), copies MDAPI certificate artifacts into `force-app/main/default/certs`, and produces `package-metadata-to-restore.xml` for post-refresh deployment.\n- **Consumer Secret Handling:** Uses `puppeteer-core` with an executable path from `getChromeExecutablePath()` (env var `PUPPETEER_EXECUTABLE_PATH` may be required). Falls back to manual prompt when browser automation cannot be used.\n- **Data & Records:** Exports custom settings to JSON and supports exporting records through SFDMU workspaces chosen interactively.\n- **Config & Reporting:** Updates project/user config under `config/.sfdx-hardis.yml#refreshSandboxConfig` and reports artifacts to the WebSocket client.\n- **Error Handling:** Provides clear error messages and a summary response object indicating success/failure and which secrets were captured.\n\n</details>\n",
8141
8180
  "examples": [
8142
- "$ sf hardis:org:purge:apexlog",
8143
- "$ sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com"
8181
+ "$ sf hardis:org:refresh:before-refresh",
8182
+ "$ sf hardis:org:refresh:before-refresh --name \"MyConnectedApp\"",
8183
+ "$ sf hardis:org:refresh:before-refresh --name \"App1,App2,App3\"",
8184
+ "$ sf hardis:org:refresh:before-refresh --all",
8185
+ "$ sf hardis:org:refresh:before-refresh --delete"
8144
8186
  ],
8145
8187
  "flags": {
8146
8188
  "json": {
@@ -8158,17 +8200,38 @@
8158
8200
  "multiple": false,
8159
8201
  "type": "option"
8160
8202
  },
8161
- "prompt": {
8162
- "char": "z",
8163
- "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
8164
- "name": "prompt",
8165
- "allowNo": true,
8166
- "type": "boolean"
8203
+ "target-org": {
8204
+ "char": "o",
8205
+ "name": "target-org",
8206
+ "noCacheDefault": true,
8207
+ "required": true,
8208
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
8209
+ "hasDynamicHelp": true,
8210
+ "multiple": false,
8211
+ "type": "option"
8167
8212
  },
8168
- "debug": {
8213
+ "delete": {
8169
8214
  "char": "d",
8170
- "description": "Activate debug mode (more logs)",
8171
- "name": "debug",
8215
+ "description": "By default, Connected Apps are not deleted from the org after saving. Set this flag to force their deletion so they will be able to be reuploaded again after refreshing the org.",
8216
+ "name": "delete",
8217
+ "summary": "Delete Connected Apps from org after saving",
8218
+ "allowNo": false,
8219
+ "type": "boolean"
8220
+ },
8221
+ "name": {
8222
+ "char": "n",
8223
+ "description": "Connected App name(s) to process. For multiple apps, separate with commas (e.g., \"App1,App2\")",
8224
+ "name": "name",
8225
+ "summary": "Filter according to Name criteria",
8226
+ "hasDynamicHelp": false,
8227
+ "multiple": false,
8228
+ "type": "option"
8229
+ },
8230
+ "all": {
8231
+ "char": "a",
8232
+ "description": "If set, all Connected Apps from the org will be processed. Takes precedence over --name if both are specified.",
8233
+ "name": "all",
8234
+ "summary": "Process all Connected Apps without selection prompt",
8172
8235
  "allowNo": false,
8173
8236
  "type": "boolean"
8174
8237
  },
@@ -8184,78 +8247,61 @@
8184
8247
  "name": "skipauth",
8185
8248
  "allowNo": false,
8186
8249
  "type": "boolean"
8187
- },
8188
- "target-org": {
8189
- "aliases": [
8190
- "targetusername",
8191
- "u"
8192
- ],
8193
- "char": "o",
8194
- "deprecateAliases": true,
8195
- "name": "target-org",
8196
- "noCacheDefault": true,
8197
- "required": true,
8198
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
8199
- "hasDynamicHelp": true,
8200
- "multiple": false,
8201
- "type": "option"
8202
8250
  }
8203
8251
  },
8204
8252
  "hasDynamicHelp": true,
8205
8253
  "hiddenAliases": [],
8206
- "id": "hardis:org:purge:apexlog",
8254
+ "id": "hardis:org:refresh:before-refresh",
8207
8255
  "pluginAlias": "sfdx-hardis",
8208
8256
  "pluginName": "sfdx-hardis",
8209
8257
  "pluginType": "core",
8210
8258
  "strict": true,
8211
8259
  "enableJsonFlag": true,
8212
- "title": "Purge Apex Logs",
8213
- "requiresProject": false,
8260
+ "requiresProject": true,
8214
8261
  "isESM": true,
8215
8262
  "relativePath": [
8216
8263
  "lib",
8217
8264
  "commands",
8218
8265
  "hardis",
8219
8266
  "org",
8220
- "purge",
8221
- "apexlog.js"
8267
+ "refresh",
8268
+ "before-refresh.js"
8222
8269
  ],
8223
8270
  "aliasPermutations": [],
8224
8271
  "permutations": [
8225
- "hardis:org:purge:apexlog",
8226
- "org:hardis:purge:apexlog",
8227
- "org:purge:hardis:apexlog",
8228
- "org:purge:apexlog:hardis",
8229
- "hardis:purge:org:apexlog",
8230
- "purge:hardis:org:apexlog",
8231
- "purge:org:hardis:apexlog",
8232
- "purge:org:apexlog:hardis",
8233
- "hardis:purge:apexlog:org",
8234
- "purge:hardis:apexlog:org",
8235
- "purge:apexlog:hardis:org",
8236
- "purge:apexlog:org:hardis",
8237
- "hardis:org:apexlog:purge",
8238
- "org:hardis:apexlog:purge",
8239
- "org:apexlog:hardis:purge",
8240
- "org:apexlog:purge:hardis",
8241
- "hardis:apexlog:org:purge",
8242
- "apexlog:hardis:org:purge",
8243
- "apexlog:org:hardis:purge",
8244
- "apexlog:org:purge:hardis",
8245
- "hardis:apexlog:purge:org",
8246
- "apexlog:hardis:purge:org",
8247
- "apexlog:purge:hardis:org",
8248
- "apexlog:purge:org:hardis"
8272
+ "hardis:org:refresh:before-refresh",
8273
+ "org:hardis:refresh:before-refresh",
8274
+ "org:refresh:hardis:before-refresh",
8275
+ "org:refresh:before-refresh:hardis",
8276
+ "hardis:refresh:org:before-refresh",
8277
+ "refresh:hardis:org:before-refresh",
8278
+ "refresh:org:hardis:before-refresh",
8279
+ "refresh:org:before-refresh:hardis",
8280
+ "hardis:refresh:before-refresh:org",
8281
+ "refresh:hardis:before-refresh:org",
8282
+ "refresh:before-refresh:hardis:org",
8283
+ "refresh:before-refresh:org:hardis",
8284
+ "hardis:org:before-refresh:refresh",
8285
+ "org:hardis:before-refresh:refresh",
8286
+ "org:before-refresh:hardis:refresh",
8287
+ "org:before-refresh:refresh:hardis",
8288
+ "hardis:before-refresh:org:refresh",
8289
+ "before-refresh:hardis:org:refresh",
8290
+ "before-refresh:org:hardis:refresh",
8291
+ "before-refresh:org:refresh:hardis",
8292
+ "hardis:before-refresh:refresh:org",
8293
+ "before-refresh:hardis:refresh:org",
8294
+ "before-refresh:refresh:hardis:org",
8295
+ "before-refresh:refresh:org:hardis"
8249
8296
  ]
8250
8297
  },
8251
- "hardis:org:purge:flow": {
8298
+ "hardis:org:retrieve:packageconfig": {
8252
8299
  "aliases": [],
8253
8300
  "args": {},
8254
- "description": "\n**Purges old or unwanted Flow versions from a Salesforce org, with an option to delete related Flow Interviews.**\n\nThis command helps maintain a clean and performant Salesforce org by removing obsolete Flow versions. Over time, multiple versions of Flows can accumulate, consuming storage and potentially impacting performance. This tool provides a controlled way to clean up these versions.\n\nKey functionalities:\n\n- **Targeted Flow Selection:** Allows you to filter Flow versions to delete by name (`--name`) and status (`--status`, e.g., `Obsolete`, `Draft`, `Inactive`).\n- **Flow Interview Deletion:** If a Flow version cannot be deleted due to active Flow Interviews, the `--delete-flow-interviews` flag (or interactive prompt) allows you to delete these interviews first, then retry the Flow version deletion.\n- **Confirmation Prompt:** In interactive mode, it prompts for confirmation before proceeding with the deletion of Flow versions and Flow Interviews.\n- **Partial Success Handling:** The `--allowpurgefailure` flag (default `true`) allows the command to continue even if some deletions fail, reporting the errors.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Queries (Tooling API):** It queries the `Flow` object (using the Tooling API) to list Flow versions based on the provided filters (name, status, manageable state).\n- **Bulk Deletion (Tooling API):** It uses `bulkDeleteTooling` to perform mass deletions of Flow versions. If deletion fails due to active interviews, it extracts the interview IDs.\n- **Flow Interview Management:** If `delete-flow-interviews` is enabled, it queries `FlowInterview` objects, performs bulk deletion of the identified interviews using `bulkDelete`, and then retries the Flow version deletion.\n- **Interactive Prompts:** Uses the `prompts` library to interact with the user for selecting Flows, statuses, and confirming deletion actions.\n- **Error Reporting:** Logs detailed error messages for failed deletions, including the specific reasons.\n- **Command-Line Execution:** Uses `execSfdxJson` to execute Salesforce CLI commands for querying Flow data.\n</details>\n",
8301
+ "description": "\n**Retrieves the installed package configuration from a Salesforce org and optionally updates the local project configuration.**\n\nThis command is useful for maintaining an accurate record of installed packages within your Salesforce project, which is crucial for managing dependencies and ensuring consistent deployments across environments.\n\nKey functionalities:\n\n- **Package Listing:** Connects to a specified Salesforce org (or prompts for one if not provided) and retrieves a list of all installed packages.\n- **Configuration Update:** Offers the option to update your local project's configuration with the retrieved list of installed packages. This can be beneficial for automating package installations during environment setup or CI/CD processes.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Org Connection:** It establishes a connection to the target Salesforce org using the provided or prompted username.\n- **Metadata Retrieval:** It utilizes `MetadataUtils.listInstalledPackages` to query the Salesforce org and obtain details about the installed packages.\n- **Interactive Prompt:** It uses the `prompts` library to ask the user whether they want to update their local project configuration with the retrieved package list.\n- **Configuration Management:** If the user confirms, it calls `managePackageConfig` to update the project's configuration file (likely `.sfdx-hardis.yml`) with the new package information.\n- **User Feedback:** Provides clear messages to the user about the success of the package retrieval and configuration update.\n</details>\n",
8255
8302
  "examples": [
8256
- "$ sf hardis:org:purge:flow",
8257
- "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --no-prompt --delete-flow-interviews",
8258
- "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status \"Obsolete,Draft,InvalidDraft\" --name TestFlow"
8303
+ "$ sf hardis:org:retrieve:packageconfig",
8304
+ "sf hardis:org:retrieve:packageconfig -u myOrg"
8259
8305
  ],
8260
8306
  "flags": {
8261
8307
  "json": {
@@ -8273,52 +8319,6 @@
8273
8319
  "multiple": false,
8274
8320
  "type": "option"
8275
8321
  },
8276
- "prompt": {
8277
- "char": "z",
8278
- "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
8279
- "name": "prompt",
8280
- "allowNo": true,
8281
- "type": "boolean"
8282
- },
8283
- "name": {
8284
- "char": "n",
8285
- "description": "Filter according to Name criteria",
8286
- "name": "name",
8287
- "hasDynamicHelp": false,
8288
- "multiple": false,
8289
- "type": "option"
8290
- },
8291
- "status": {
8292
- "char": "s",
8293
- "description": "Filter according to Status criteria",
8294
- "name": "status",
8295
- "hasDynamicHelp": false,
8296
- "multiple": false,
8297
- "type": "option"
8298
- },
8299
- "delete-flow-interviews": {
8300
- "char": "w",
8301
- "description": "If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions",
8302
- "name": "delete-flow-interviews",
8303
- "allowNo": false,
8304
- "type": "boolean"
8305
- },
8306
- "allowpurgefailure": {
8307
- "char": "f",
8308
- "description": "Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable",
8309
- "name": "allowpurgefailure",
8310
- "allowNo": true,
8311
- "type": "boolean"
8312
- },
8313
- "instanceurl": {
8314
- "char": "r",
8315
- "description": "URL of org instance",
8316
- "name": "instanceurl",
8317
- "default": "https://login.salesforce.com",
8318
- "hasDynamicHelp": false,
8319
- "multiple": false,
8320
- "type": "option"
8321
- },
8322
8322
  "debug": {
8323
8323
  "char": "d",
8324
8324
  "description": "Activate debug mode (more logs)",
@@ -8357,13 +8357,13 @@
8357
8357
  },
8358
8358
  "hasDynamicHelp": true,
8359
8359
  "hiddenAliases": [],
8360
- "id": "hardis:org:purge:flow",
8360
+ "id": "hardis:org:retrieve:packageconfig",
8361
8361
  "pluginAlias": "sfdx-hardis",
8362
8362
  "pluginName": "sfdx-hardis",
8363
8363
  "pluginType": "core",
8364
8364
  "strict": true,
8365
8365
  "enableJsonFlag": true,
8366
- "title": "Purge Flow versions",
8366
+ "title": "Retrieve package configuration from an org",
8367
8367
  "requiresProject": false,
8368
8368
  "isESM": true,
8369
8369
  "relativePath": [
@@ -8371,35 +8371,35 @@
8371
8371
  "commands",
8372
8372
  "hardis",
8373
8373
  "org",
8374
- "purge",
8375
- "flow.js"
8374
+ "retrieve",
8375
+ "packageconfig.js"
8376
8376
  ],
8377
8377
  "aliasPermutations": [],
8378
8378
  "permutations": [
8379
- "hardis:org:purge:flow",
8380
- "org:hardis:purge:flow",
8381
- "org:purge:hardis:flow",
8382
- "org:purge:flow:hardis",
8383
- "hardis:purge:org:flow",
8384
- "purge:hardis:org:flow",
8385
- "purge:org:hardis:flow",
8386
- "purge:org:flow:hardis",
8387
- "hardis:purge:flow:org",
8388
- "purge:hardis:flow:org",
8389
- "purge:flow:hardis:org",
8390
- "purge:flow:org:hardis",
8391
- "hardis:org:flow:purge",
8392
- "org:hardis:flow:purge",
8393
- "org:flow:hardis:purge",
8394
- "org:flow:purge:hardis",
8395
- "hardis:flow:org:purge",
8396
- "flow:hardis:org:purge",
8397
- "flow:org:hardis:purge",
8398
- "flow:org:purge:hardis",
8399
- "hardis:flow:purge:org",
8400
- "flow:hardis:purge:org",
8401
- "flow:purge:hardis:org",
8402
- "flow:purge:org:hardis"
8379
+ "hardis:org:retrieve:packageconfig",
8380
+ "org:hardis:retrieve:packageconfig",
8381
+ "org:retrieve:hardis:packageconfig",
8382
+ "org:retrieve:packageconfig:hardis",
8383
+ "hardis:retrieve:org:packageconfig",
8384
+ "retrieve:hardis:org:packageconfig",
8385
+ "retrieve:org:hardis:packageconfig",
8386
+ "retrieve:org:packageconfig:hardis",
8387
+ "hardis:retrieve:packageconfig:org",
8388
+ "retrieve:hardis:packageconfig:org",
8389
+ "retrieve:packageconfig:hardis:org",
8390
+ "retrieve:packageconfig:org:hardis",
8391
+ "hardis:org:packageconfig:retrieve",
8392
+ "org:hardis:packageconfig:retrieve",
8393
+ "org:packageconfig:hardis:retrieve",
8394
+ "org:packageconfig:retrieve:hardis",
8395
+ "hardis:packageconfig:org:retrieve",
8396
+ "packageconfig:hardis:org:retrieve",
8397
+ "packageconfig:org:hardis:retrieve",
8398
+ "packageconfig:org:retrieve:hardis",
8399
+ "hardis:packageconfig:retrieve:org",
8400
+ "packageconfig:hardis:retrieve:org",
8401
+ "packageconfig:retrieve:hardis:org",
8402
+ "packageconfig:retrieve:org:hardis"
8403
8403
  ]
8404
8404
  },
8405
8405
  "hardis:org:test:apex": {
@@ -11279,8 +11279,254 @@
11279
11279
  "args": {},
11280
11280
  "description": "Remove XML elements using Glob patterns and XPath expressions\n \nThis can be very useful to avoid to always remove manually the same elements in the same XML file.\n\n- **globpattern** can be any glob pattern allowing to identify the XML files to update, for example `/**/*.flexipage-meta.xml`\n\n- **xpath** can be any xpath following the format `//ns:PARENT-TAG-NAME//ns:TAG-NAME[contains(text(),'TAG-VALUE')]`. If an element is found, the whole **PARENT-TAG-NAME** (with its subtree) will be removed.\n\n![How to build cleaning XPath](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/doc-clean-xml.jpg)\n\nNote: If globpattern and xpath are not sent, elements defined in property **cleanXmlPatterns** in **.sfdx-hardis.yml** config file will be used\n \n ",
11281
11281
  "examples": [
11282
- "$ sf hardis:project:clean:xml",
11283
- "$ sf hardis:project:clean:xml --globpattern \"/**/*.flexipage-meta.xml\" --xpath \"//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]\""
11282
+ "$ sf hardis:project:clean:xml",
11283
+ "$ sf hardis:project:clean:xml --globpattern \"/**/*.flexipage-meta.xml\" --xpath \"//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]\""
11284
+ ],
11285
+ "flags": {
11286
+ "json": {
11287
+ "description": "Format output as json.",
11288
+ "helpGroup": "GLOBAL",
11289
+ "name": "json",
11290
+ "allowNo": false,
11291
+ "type": "boolean"
11292
+ },
11293
+ "flags-dir": {
11294
+ "helpGroup": "GLOBAL",
11295
+ "name": "flags-dir",
11296
+ "summary": "Import flag values from a directory.",
11297
+ "hasDynamicHelp": false,
11298
+ "multiple": false,
11299
+ "type": "option"
11300
+ },
11301
+ "folder": {
11302
+ "char": "f",
11303
+ "description": "Root folder",
11304
+ "name": "folder",
11305
+ "default": "force-app",
11306
+ "hasDynamicHelp": false,
11307
+ "multiple": false,
11308
+ "type": "option"
11309
+ },
11310
+ "globpattern": {
11311
+ "char": "p",
11312
+ "dependsOn": [
11313
+ "xpath"
11314
+ ],
11315
+ "description": "Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml",
11316
+ "name": "globpattern",
11317
+ "hasDynamicHelp": false,
11318
+ "multiple": false,
11319
+ "type": "option"
11320
+ },
11321
+ "xpath": {
11322
+ "char": "x",
11323
+ "dependsOn": [
11324
+ "globpattern"
11325
+ ],
11326
+ "description": "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]",
11327
+ "name": "xpath",
11328
+ "hasDynamicHelp": false,
11329
+ "multiple": false,
11330
+ "type": "option"
11331
+ },
11332
+ "namespace": {
11333
+ "char": "n",
11334
+ "description": "XML Namespace to use",
11335
+ "name": "namespace",
11336
+ "default": "http://soap.sforce.com/2006/04/metadata",
11337
+ "hasDynamicHelp": false,
11338
+ "multiple": false,
11339
+ "type": "option"
11340
+ },
11341
+ "debug": {
11342
+ "char": "d",
11343
+ "description": "Activate debug mode (more logs)",
11344
+ "name": "debug",
11345
+ "allowNo": false,
11346
+ "type": "boolean"
11347
+ },
11348
+ "websocket": {
11349
+ "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
11350
+ "name": "websocket",
11351
+ "hasDynamicHelp": false,
11352
+ "multiple": false,
11353
+ "type": "option"
11354
+ },
11355
+ "skipauth": {
11356
+ "description": "Skip authentication check when a default username is required",
11357
+ "name": "skipauth",
11358
+ "allowNo": false,
11359
+ "type": "boolean"
11360
+ }
11361
+ },
11362
+ "hasDynamicHelp": false,
11363
+ "hiddenAliases": [],
11364
+ "id": "hardis:project:clean:xml",
11365
+ "pluginAlias": "sfdx-hardis",
11366
+ "pluginName": "sfdx-hardis",
11367
+ "pluginType": "core",
11368
+ "strict": true,
11369
+ "enableJsonFlag": true,
11370
+ "title": "Clean retrieved empty items in dx sources",
11371
+ "requiresProject": true,
11372
+ "isESM": true,
11373
+ "relativePath": [
11374
+ "lib",
11375
+ "commands",
11376
+ "hardis",
11377
+ "project",
11378
+ "clean",
11379
+ "xml.js"
11380
+ ],
11381
+ "aliasPermutations": [],
11382
+ "permutations": [
11383
+ "hardis:project:clean:xml",
11384
+ "project:hardis:clean:xml",
11385
+ "project:clean:hardis:xml",
11386
+ "project:clean:xml:hardis",
11387
+ "hardis:clean:project:xml",
11388
+ "clean:hardis:project:xml",
11389
+ "clean:project:hardis:xml",
11390
+ "clean:project:xml:hardis",
11391
+ "hardis:clean:xml:project",
11392
+ "clean:hardis:xml:project",
11393
+ "clean:xml:hardis:project",
11394
+ "clean:xml:project:hardis",
11395
+ "hardis:project:xml:clean",
11396
+ "project:hardis:xml:clean",
11397
+ "project:xml:hardis:clean",
11398
+ "project:xml:clean:hardis",
11399
+ "hardis:xml:project:clean",
11400
+ "xml:hardis:project:clean",
11401
+ "xml:project:hardis:clean",
11402
+ "xml:project:clean:hardis",
11403
+ "hardis:xml:clean:project",
11404
+ "xml:hardis:clean:project",
11405
+ "xml:clean:hardis:project",
11406
+ "xml:clean:project:hardis"
11407
+ ]
11408
+ },
11409
+ "hardis:project:fix:profiletabs": {
11410
+ "aliases": [],
11411
+ "args": {},
11412
+ "description": "\n## Command Behavior\n\n**Interactively updates tab visibility settings in Salesforce profiles, addressing a common issue where tab visibilities are not correctly retrieved by `sf project retrieve start`.**\n\nThis command provides a user-friendly interface to manage tab settings within your profile XML files, ensuring that your local project accurately reflects the intended tab configurations in your Salesforce org.\n\nKey functionalities:\n\n- **Interactive Tab Selection:** Displays a multi-select menu of all available tabs in your org, allowing you to choose which tabs to update.\n- **Visibility Control:** Lets you set the visibility for the selected tabs to either `DefaultOn` (Visible) or `Hidden`.\n- **Profile Selection:** Presents a multi-select menu of all .profile-meta.xml files in your project, allowing you to apply the tab visibility changes to specific profiles.\n- **XML Updates:** Modifies the <tabVisibilities> section of the selected profile XML files to reflect the chosen tab settings. If a tab visibility setting already exists for a selected tab, it will be updated; otherwise, a new one will be added.\n- **Sorted Output:** The <tabVisibilities> in the updated profile XML files are sorted alphabetically for consistency and readability.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Queries (Tooling API):** It queries the `TabDefinition` object using `soqlQueryTooling` to retrieve a list of all available tabs in the target org.\n- **File Discovery:** Uses `glob` to find all .profile-meta.xml files within the specified project path.\n- **Interactive Prompts:** Leverages the `prompts` library to create interactive menus for selecting tabs, visibility settings, and profiles.\n- **XML Parsing and Manipulation:** Uses `parseXmlFile` to read the content of profile XML files and `writeXmlFile` to write the modified content back. It manipulates the `tabVisibilities` array within the parsed XML to add or update tab settings.\n- **Array Sorting:** Employs the `sort-array` library to sort the `tabVisibilities` alphabetically by tab name.\n- **Logging:** Provides feedback to the user about which profiles have been updated and a summary of the changes.\n</details>\n",
11413
+ "examples": [
11414
+ "$ sf hardis:project:fix:profiletabs"
11415
+ ],
11416
+ "flags": {
11417
+ "json": {
11418
+ "description": "Format output as json.",
11419
+ "helpGroup": "GLOBAL",
11420
+ "name": "json",
11421
+ "allowNo": false,
11422
+ "type": "boolean"
11423
+ },
11424
+ "flags-dir": {
11425
+ "helpGroup": "GLOBAL",
11426
+ "name": "flags-dir",
11427
+ "summary": "Import flag values from a directory.",
11428
+ "hasDynamicHelp": false,
11429
+ "multiple": false,
11430
+ "type": "option"
11431
+ },
11432
+ "path": {
11433
+ "char": "p",
11434
+ "description": "Root folder",
11435
+ "name": "path",
11436
+ "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
11437
+ "hasDynamicHelp": false,
11438
+ "multiple": false,
11439
+ "type": "option"
11440
+ },
11441
+ "debug": {
11442
+ "char": "d",
11443
+ "description": "Activate debug mode (more logs)",
11444
+ "name": "debug",
11445
+ "allowNo": false,
11446
+ "type": "boolean"
11447
+ },
11448
+ "websocket": {
11449
+ "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
11450
+ "name": "websocket",
11451
+ "hasDynamicHelp": false,
11452
+ "multiple": false,
11453
+ "type": "option"
11454
+ },
11455
+ "skipauth": {
11456
+ "description": "Skip authentication check when a default username is required",
11457
+ "name": "skipauth",
11458
+ "allowNo": false,
11459
+ "type": "boolean"
11460
+ },
11461
+ "target-org": {
11462
+ "aliases": [
11463
+ "targetusername",
11464
+ "u"
11465
+ ],
11466
+ "char": "o",
11467
+ "deprecateAliases": true,
11468
+ "name": "target-org",
11469
+ "noCacheDefault": true,
11470
+ "required": true,
11471
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
11472
+ "hasDynamicHelp": true,
11473
+ "multiple": false,
11474
+ "type": "option"
11475
+ }
11476
+ },
11477
+ "hasDynamicHelp": true,
11478
+ "hiddenAliases": [],
11479
+ "id": "hardis:project:fix:profiletabs",
11480
+ "pluginAlias": "sfdx-hardis",
11481
+ "pluginName": "sfdx-hardis",
11482
+ "pluginType": "core",
11483
+ "strict": true,
11484
+ "enableJsonFlag": true,
11485
+ "title": "Fix profiles to add tabs that are not retrieved by SF CLI",
11486
+ "requiresProject": true,
11487
+ "isESM": true,
11488
+ "relativePath": [
11489
+ "lib",
11490
+ "commands",
11491
+ "hardis",
11492
+ "project",
11493
+ "fix",
11494
+ "profiletabs.js"
11495
+ ],
11496
+ "aliasPermutations": [],
11497
+ "permutations": [
11498
+ "hardis:project:fix:profiletabs",
11499
+ "project:hardis:fix:profiletabs",
11500
+ "project:fix:hardis:profiletabs",
11501
+ "project:fix:profiletabs:hardis",
11502
+ "hardis:fix:project:profiletabs",
11503
+ "fix:hardis:project:profiletabs",
11504
+ "fix:project:hardis:profiletabs",
11505
+ "fix:project:profiletabs:hardis",
11506
+ "hardis:fix:profiletabs:project",
11507
+ "fix:hardis:profiletabs:project",
11508
+ "fix:profiletabs:hardis:project",
11509
+ "fix:profiletabs:project:hardis",
11510
+ "hardis:project:profiletabs:fix",
11511
+ "project:hardis:profiletabs:fix",
11512
+ "project:profiletabs:hardis:fix",
11513
+ "project:profiletabs:fix:hardis",
11514
+ "hardis:profiletabs:project:fix",
11515
+ "profiletabs:hardis:project:fix",
11516
+ "profiletabs:project:hardis:fix",
11517
+ "profiletabs:project:fix:hardis",
11518
+ "hardis:profiletabs:fix:project",
11519
+ "profiletabs:hardis:fix:project",
11520
+ "profiletabs:fix:hardis:project",
11521
+ "profiletabs:fix:project:hardis"
11522
+ ]
11523
+ },
11524
+ "hardis:project:fix:v53flexipages": {
11525
+ "aliases": [],
11526
+ "args": {},
11527
+ "description": "\n## Command Behavior\n\n**Fixes Salesforce FlexiPages for compatibility with API Version 53.0 (Winter '22 release) by adding missing identifiers to component instances.**\n\nSalesforce introduced a change in API Version 53.0 that requires `identifier` tags within `componentInstance` and `fieldInstance` elements in FlexiPage metadata. If these identifiers are missing, deployments to orgs with API version 53.0 or higher will fail. This command automates the process of adding these missing identifiers, ensuring your FlexiPages remain deployable.\n\nKey functionalities:\n\n- **Targeted FlexiPage Processing:** Scans all .flexipage-meta.xml files within the specified root folder (defaults to current working directory).\n- **Identifier Injection:** Inserts a unique `identifier` tag (e.g., `SFDX_HARDIS_REPLACEMENT_ID`) into `componentInstance` and `fieldInstance` elements that lack one.\n\n**Important Note:** After running this command, ensure you update your `apiVersion` to `53.0` (or higher) in your `package.xml` and `sfdx-project.json` files.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **File Discovery:** Uses `glob` to find all .flexipage-meta.xml files.\n- **Content Reading:** Reads the XML content of each FlexiPage file.\n- **Regular Expression Replacement:** Employs a set of regular expressions to identify specific XML patterns (componentName.../componentName.../componentInstance, componentName.../componentName.../visibilityRule, fieldItem.../fieldItem.../fieldInstance) that are missing the `identifier` tag.\n- **Dynamic ID Generation:** For each match, it generates a unique identifier (e.g., `sfdxHardisIdX`) and injects it into the XML structure.\n- **File Writing:** If changes are made, the modified XML content is written back to the FlexiPage file using `fs.writeFile`.\n- **Logging:** Provides messages about which FlexiPages are being processed and a summary of the total number of identifiers added.\n</details>\n",
11528
+ "examples": [
11529
+ "$ sf hardis:project:fix:v53flexipages"
11284
11530
  ],
11285
11531
  "flags": {
11286
11532
  "json": {
@@ -11298,42 +11544,11 @@
11298
11544
  "multiple": false,
11299
11545
  "type": "option"
11300
11546
  },
11301
- "folder": {
11302
- "char": "f",
11303
- "description": "Root folder",
11304
- "name": "folder",
11305
- "default": "force-app",
11306
- "hasDynamicHelp": false,
11307
- "multiple": false,
11308
- "type": "option"
11309
- },
11310
- "globpattern": {
11547
+ "path": {
11311
11548
  "char": "p",
11312
- "dependsOn": [
11313
- "xpath"
11314
- ],
11315
- "description": "Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml",
11316
- "name": "globpattern",
11317
- "hasDynamicHelp": false,
11318
- "multiple": false,
11319
- "type": "option"
11320
- },
11321
- "xpath": {
11322
- "char": "x",
11323
- "dependsOn": [
11324
- "globpattern"
11325
- ],
11326
- "description": "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]",
11327
- "name": "xpath",
11328
- "hasDynamicHelp": false,
11329
- "multiple": false,
11330
- "type": "option"
11331
- },
11332
- "namespace": {
11333
- "char": "n",
11334
- "description": "XML Namespace to use",
11335
- "name": "namespace",
11336
- "default": "http://soap.sforce.com/2006/04/metadata",
11549
+ "description": "Root folder",
11550
+ "name": "path",
11551
+ "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
11337
11552
  "hasDynamicHelp": false,
11338
11553
  "multiple": false,
11339
11554
  "type": "option"
@@ -11361,13 +11576,13 @@
11361
11576
  },
11362
11577
  "hasDynamicHelp": false,
11363
11578
  "hiddenAliases": [],
11364
- "id": "hardis:project:clean:xml",
11579
+ "id": "hardis:project:fix:v53flexipages",
11365
11580
  "pluginAlias": "sfdx-hardis",
11366
11581
  "pluginName": "sfdx-hardis",
11367
11582
  "pluginType": "core",
11368
11583
  "strict": true,
11369
11584
  "enableJsonFlag": true,
11370
- "title": "Clean retrieved empty items in dx sources",
11585
+ "title": "Fix flexipages for v53",
11371
11586
  "requiresProject": true,
11372
11587
  "isESM": true,
11373
11588
  "relativePath": [
@@ -11375,35 +11590,35 @@
11375
11590
  "commands",
11376
11591
  "hardis",
11377
11592
  "project",
11378
- "clean",
11379
- "xml.js"
11593
+ "fix",
11594
+ "v53flexipages.js"
11380
11595
  ],
11381
11596
  "aliasPermutations": [],
11382
11597
  "permutations": [
11383
- "hardis:project:clean:xml",
11384
- "project:hardis:clean:xml",
11385
- "project:clean:hardis:xml",
11386
- "project:clean:xml:hardis",
11387
- "hardis:clean:project:xml",
11388
- "clean:hardis:project:xml",
11389
- "clean:project:hardis:xml",
11390
- "clean:project:xml:hardis",
11391
- "hardis:clean:xml:project",
11392
- "clean:hardis:xml:project",
11393
- "clean:xml:hardis:project",
11394
- "clean:xml:project:hardis",
11395
- "hardis:project:xml:clean",
11396
- "project:hardis:xml:clean",
11397
- "project:xml:hardis:clean",
11398
- "project:xml:clean:hardis",
11399
- "hardis:xml:project:clean",
11400
- "xml:hardis:project:clean",
11401
- "xml:project:hardis:clean",
11402
- "xml:project:clean:hardis",
11403
- "hardis:xml:clean:project",
11404
- "xml:hardis:clean:project",
11405
- "xml:clean:hardis:project",
11406
- "xml:clean:project:hardis"
11598
+ "hardis:project:fix:v53flexipages",
11599
+ "project:hardis:fix:v53flexipages",
11600
+ "project:fix:hardis:v53flexipages",
11601
+ "project:fix:v53flexipages:hardis",
11602
+ "hardis:fix:project:v53flexipages",
11603
+ "fix:hardis:project:v53flexipages",
11604
+ "fix:project:hardis:v53flexipages",
11605
+ "fix:project:v53flexipages:hardis",
11606
+ "hardis:fix:v53flexipages:project",
11607
+ "fix:hardis:v53flexipages:project",
11608
+ "fix:v53flexipages:hardis:project",
11609
+ "fix:v53flexipages:project:hardis",
11610
+ "hardis:project:v53flexipages:fix",
11611
+ "project:hardis:v53flexipages:fix",
11612
+ "project:v53flexipages:hardis:fix",
11613
+ "project:v53flexipages:fix:hardis",
11614
+ "hardis:v53flexipages:project:fix",
11615
+ "v53flexipages:hardis:project:fix",
11616
+ "v53flexipages:project:hardis:fix",
11617
+ "v53flexipages:project:fix:hardis",
11618
+ "hardis:v53flexipages:fix:project",
11619
+ "v53flexipages:hardis:fix:project",
11620
+ "v53flexipages:fix:hardis:project",
11621
+ "v53flexipages:fix:project:hardis"
11407
11622
  ]
11408
11623
  },
11409
11624
  "hardis:project:deploy:notify": {
@@ -12537,171 +12752,58 @@
12537
12752
  "strict": true,
12538
12753
  "enableJsonFlag": true,
12539
12754
  "requiresProject": true,
12540
- "isESM": true,
12541
- "relativePath": [
12542
- "lib",
12543
- "commands",
12544
- "hardis",
12545
- "project",
12546
- "deploy",
12547
- "validate.js"
12548
- ],
12549
- "aliasPermutations": [
12550
- "hardis:deploy:validate",
12551
- "deploy:hardis:validate",
12552
- "deploy:validate:hardis",
12553
- "hardis:validate:deploy",
12554
- "validate:hardis:deploy",
12555
- "validate:deploy:hardis"
12556
- ],
12557
- "permutations": [
12558
- "hardis:project:deploy:validate",
12559
- "project:hardis:deploy:validate",
12560
- "project:deploy:hardis:validate",
12561
- "project:deploy:validate:hardis",
12562
- "hardis:deploy:project:validate",
12563
- "deploy:hardis:project:validate",
12564
- "deploy:project:hardis:validate",
12565
- "deploy:project:validate:hardis",
12566
- "hardis:deploy:validate:project",
12567
- "deploy:hardis:validate:project",
12568
- "deploy:validate:hardis:project",
12569
- "deploy:validate:project:hardis",
12570
- "hardis:project:validate:deploy",
12571
- "project:hardis:validate:deploy",
12572
- "project:validate:hardis:deploy",
12573
- "project:validate:deploy:hardis",
12574
- "hardis:validate:project:deploy",
12575
- "validate:hardis:project:deploy",
12576
- "validate:project:hardis:deploy",
12577
- "validate:project:deploy:hardis",
12578
- "hardis:validate:deploy:project",
12579
- "validate:hardis:deploy:project",
12580
- "validate:deploy:hardis:project",
12581
- "validate:deploy:project:hardis"
12582
- ]
12583
- },
12584
- "hardis:project:fix:profiletabs": {
12585
- "aliases": [],
12586
- "args": {},
12587
- "description": "\n## Command Behavior\n\n**Interactively updates tab visibility settings in Salesforce profiles, addressing a common issue where tab visibilities are not correctly retrieved by `sf project retrieve start`.**\n\nThis command provides a user-friendly interface to manage tab settings within your profile XML files, ensuring that your local project accurately reflects the intended tab configurations in your Salesforce org.\n\nKey functionalities:\n\n- **Interactive Tab Selection:** Displays a multi-select menu of all available tabs in your org, allowing you to choose which tabs to update.\n- **Visibility Control:** Lets you set the visibility for the selected tabs to either `DefaultOn` (Visible) or `Hidden`.\n- **Profile Selection:** Presents a multi-select menu of all .profile-meta.xml files in your project, allowing you to apply the tab visibility changes to specific profiles.\n- **XML Updates:** Modifies the <tabVisibilities> section of the selected profile XML files to reflect the chosen tab settings. If a tab visibility setting already exists for a selected tab, it will be updated; otherwise, a new one will be added.\n- **Sorted Output:** The <tabVisibilities> in the updated profile XML files are sorted alphabetically for consistency and readability.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **SOQL Queries (Tooling API):** It queries the `TabDefinition` object using `soqlQueryTooling` to retrieve a list of all available tabs in the target org.\n- **File Discovery:** Uses `glob` to find all .profile-meta.xml files within the specified project path.\n- **Interactive Prompts:** Leverages the `prompts` library to create interactive menus for selecting tabs, visibility settings, and profiles.\n- **XML Parsing and Manipulation:** Uses `parseXmlFile` to read the content of profile XML files and `writeXmlFile` to write the modified content back. It manipulates the `tabVisibilities` array within the parsed XML to add or update tab settings.\n- **Array Sorting:** Employs the `sort-array` library to sort the `tabVisibilities` alphabetically by tab name.\n- **Logging:** Provides feedback to the user about which profiles have been updated and a summary of the changes.\n</details>\n",
12588
- "examples": [
12589
- "$ sf hardis:project:fix:profiletabs"
12590
- ],
12591
- "flags": {
12592
- "json": {
12593
- "description": "Format output as json.",
12594
- "helpGroup": "GLOBAL",
12595
- "name": "json",
12596
- "allowNo": false,
12597
- "type": "boolean"
12598
- },
12599
- "flags-dir": {
12600
- "helpGroup": "GLOBAL",
12601
- "name": "flags-dir",
12602
- "summary": "Import flag values from a directory.",
12603
- "hasDynamicHelp": false,
12604
- "multiple": false,
12605
- "type": "option"
12606
- },
12607
- "path": {
12608
- "char": "p",
12609
- "description": "Root folder",
12610
- "name": "path",
12611
- "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
12612
- "hasDynamicHelp": false,
12613
- "multiple": false,
12614
- "type": "option"
12615
- },
12616
- "debug": {
12617
- "char": "d",
12618
- "description": "Activate debug mode (more logs)",
12619
- "name": "debug",
12620
- "allowNo": false,
12621
- "type": "boolean"
12622
- },
12623
- "websocket": {
12624
- "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
12625
- "name": "websocket",
12626
- "hasDynamicHelp": false,
12627
- "multiple": false,
12628
- "type": "option"
12629
- },
12630
- "skipauth": {
12631
- "description": "Skip authentication check when a default username is required",
12632
- "name": "skipauth",
12633
- "allowNo": false,
12634
- "type": "boolean"
12635
- },
12636
- "target-org": {
12637
- "aliases": [
12638
- "targetusername",
12639
- "u"
12640
- ],
12641
- "char": "o",
12642
- "deprecateAliases": true,
12643
- "name": "target-org",
12644
- "noCacheDefault": true,
12645
- "required": true,
12646
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
12647
- "hasDynamicHelp": true,
12648
- "multiple": false,
12649
- "type": "option"
12650
- }
12651
- },
12652
- "hasDynamicHelp": true,
12653
- "hiddenAliases": [],
12654
- "id": "hardis:project:fix:profiletabs",
12655
- "pluginAlias": "sfdx-hardis",
12656
- "pluginName": "sfdx-hardis",
12657
- "pluginType": "core",
12658
- "strict": true,
12659
- "enableJsonFlag": true,
12660
- "title": "Fix profiles to add tabs that are not retrieved by SF CLI",
12661
- "requiresProject": true,
12662
- "isESM": true,
12663
- "relativePath": [
12664
- "lib",
12665
- "commands",
12666
- "hardis",
12667
- "project",
12668
- "fix",
12669
- "profiletabs.js"
12670
- ],
12671
- "aliasPermutations": [],
12672
- "permutations": [
12673
- "hardis:project:fix:profiletabs",
12674
- "project:hardis:fix:profiletabs",
12675
- "project:fix:hardis:profiletabs",
12676
- "project:fix:profiletabs:hardis",
12677
- "hardis:fix:project:profiletabs",
12678
- "fix:hardis:project:profiletabs",
12679
- "fix:project:hardis:profiletabs",
12680
- "fix:project:profiletabs:hardis",
12681
- "hardis:fix:profiletabs:project",
12682
- "fix:hardis:profiletabs:project",
12683
- "fix:profiletabs:hardis:project",
12684
- "fix:profiletabs:project:hardis",
12685
- "hardis:project:profiletabs:fix",
12686
- "project:hardis:profiletabs:fix",
12687
- "project:profiletabs:hardis:fix",
12688
- "project:profiletabs:fix:hardis",
12689
- "hardis:profiletabs:project:fix",
12690
- "profiletabs:hardis:project:fix",
12691
- "profiletabs:project:hardis:fix",
12692
- "profiletabs:project:fix:hardis",
12693
- "hardis:profiletabs:fix:project",
12694
- "profiletabs:hardis:fix:project",
12695
- "profiletabs:fix:hardis:project",
12696
- "profiletabs:fix:project:hardis"
12755
+ "isESM": true,
12756
+ "relativePath": [
12757
+ "lib",
12758
+ "commands",
12759
+ "hardis",
12760
+ "project",
12761
+ "deploy",
12762
+ "validate.js"
12763
+ ],
12764
+ "aliasPermutations": [
12765
+ "hardis:deploy:validate",
12766
+ "deploy:hardis:validate",
12767
+ "deploy:validate:hardis",
12768
+ "hardis:validate:deploy",
12769
+ "validate:hardis:deploy",
12770
+ "validate:deploy:hardis"
12771
+ ],
12772
+ "permutations": [
12773
+ "hardis:project:deploy:validate",
12774
+ "project:hardis:deploy:validate",
12775
+ "project:deploy:hardis:validate",
12776
+ "project:deploy:validate:hardis",
12777
+ "hardis:deploy:project:validate",
12778
+ "deploy:hardis:project:validate",
12779
+ "deploy:project:hardis:validate",
12780
+ "deploy:project:validate:hardis",
12781
+ "hardis:deploy:validate:project",
12782
+ "deploy:hardis:validate:project",
12783
+ "deploy:validate:hardis:project",
12784
+ "deploy:validate:project:hardis",
12785
+ "hardis:project:validate:deploy",
12786
+ "project:hardis:validate:deploy",
12787
+ "project:validate:hardis:deploy",
12788
+ "project:validate:deploy:hardis",
12789
+ "hardis:validate:project:deploy",
12790
+ "validate:hardis:project:deploy",
12791
+ "validate:project:hardis:deploy",
12792
+ "validate:project:deploy:hardis",
12793
+ "hardis:validate:deploy:project",
12794
+ "validate:hardis:deploy:project",
12795
+ "validate:deploy:hardis:project",
12796
+ "validate:deploy:project:hardis"
12697
12797
  ]
12698
12798
  },
12699
- "hardis:project:fix:v53flexipages": {
12799
+ "hardis:project:metadata:findduplicates": {
12700
12800
  "aliases": [],
12701
12801
  "args": {},
12702
- "description": "\n## Command Behavior\n\n**Fixes Salesforce FlexiPages for compatibility with API Version 53.0 (Winter '22 release) by adding missing identifiers to component instances.**\n\nSalesforce introduced a change in API Version 53.0 that requires `identifier` tags within `componentInstance` and `fieldInstance` elements in FlexiPage metadata. If these identifiers are missing, deployments to orgs with API version 53.0 or higher will fail. This command automates the process of adding these missing identifiers, ensuring your FlexiPages remain deployable.\n\nKey functionalities:\n\n- **Targeted FlexiPage Processing:** Scans all .flexipage-meta.xml files within the specified root folder (defaults to current working directory).\n- **Identifier Injection:** Inserts a unique `identifier` tag (e.g., `SFDX_HARDIS_REPLACEMENT_ID`) into `componentInstance` and `fieldInstance` elements that lack one.\n\n**Important Note:** After running this command, ensure you update your `apiVersion` to `53.0` (or higher) in your `package.xml` and `sfdx-project.json` files.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **File Discovery:** Uses `glob` to find all .flexipage-meta.xml files.\n- **Content Reading:** Reads the XML content of each FlexiPage file.\n- **Regular Expression Replacement:** Employs a set of regular expressions to identify specific XML patterns (componentName.../componentName.../componentInstance, componentName.../componentName.../visibilityRule, fieldItem.../fieldItem.../fieldInstance) that are missing the `identifier` tag.\n- **Dynamic ID Generation:** For each match, it generates a unique identifier (e.g., `sfdxHardisIdX`) and injects it into the XML structure.\n- **File Writing:** If changes are made, the modified XML content is written back to the FlexiPage file using `fs.writeFile`.\n- **Logging:** Provides messages about which FlexiPages are being processed and a summary of the total number of identifiers added.\n</details>\n",
12802
+ "description": "find duplicate values in XML file(s).\n Find duplicate values in XML file(s). Keys to be checked can be configured in `config/sfdx-hardis.yml` using property metadataDuplicateFindKeys.\n\nDefault config :\nmetadataDuplicateFindKeys :\n[object Object]\n",
12703
12803
  "examples": [
12704
- "$ sf hardis:project:fix:v53flexipages"
12804
+ "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Layout xmlns=\"http://soap.sforce.com/2006/04/metadata\">\n <layoutSections>\n ...\n <layoutColumns>\n <layoutItems>\n <behavior>Required</behavior>\n <field>Name</field>\n </layoutItems>\n <layoutItems>\n <behavior>Required</behavior>\n <field>Name</field>\n </layoutItems>\n </layoutColumns>\n </layoutSections>\n</Layout>\n",
12805
+ "\n$ sf hardis:project:metadata:findduplicates --file layout.layout-meta.xml\n[sfdx-hardis] Duplicate values in layout.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : Name\n",
12806
+ "\n$ sf hardis:project.metadata:findduplicates -f \"force-app/main/default/**/*.xml\"\n[sfdx-hardis] hardis:project:metadata:findduplicates execution time 0:00:00.397\n[sfdx-hardis] Duplicate values in layout1.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : CreatedById\n\n[sfdx-hardis] Duplicate values in layout2.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : LastModifiedById, Name\n"
12705
12807
  ],
12706
12808
  "flags": {
12707
12809
  "json": {
@@ -12719,22 +12821,14 @@
12719
12821
  "multiple": false,
12720
12822
  "type": "option"
12721
12823
  },
12722
- "path": {
12723
- "char": "p",
12724
- "description": "Root folder",
12725
- "name": "path",
12726
- "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
12824
+ "files": {
12825
+ "char": "f",
12826
+ "description": "XML metadata files path",
12827
+ "name": "files",
12727
12828
  "hasDynamicHelp": false,
12728
- "multiple": false,
12829
+ "multiple": true,
12729
12830
  "type": "option"
12730
12831
  },
12731
- "debug": {
12732
- "char": "d",
12733
- "description": "Activate debug mode (more logs)",
12734
- "name": "debug",
12735
- "allowNo": false,
12736
- "type": "boolean"
12737
- },
12738
12832
  "websocket": {
12739
12833
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
12740
12834
  "name": "websocket",
@@ -12751,13 +12845,32 @@
12751
12845
  },
12752
12846
  "hasDynamicHelp": false,
12753
12847
  "hiddenAliases": [],
12754
- "id": "hardis:project:fix:v53flexipages",
12848
+ "id": "hardis:project:metadata:findduplicates",
12755
12849
  "pluginAlias": "sfdx-hardis",
12756
12850
  "pluginName": "sfdx-hardis",
12757
12851
  "pluginType": "core",
12758
12852
  "strict": true,
12759
12853
  "enableJsonFlag": true,
12760
- "title": "Fix flexipages for v53",
12854
+ "metadataDuplicateFindKeys": {
12855
+ "layout": [
12856
+ "Layout.layoutSections.layoutColumns.layoutItems.field",
12857
+ "Layout.quickActionListItems.quickActionName"
12858
+ ],
12859
+ "profile": [
12860
+ "Profile.fieldPermissions.field",
12861
+ "Profile.objectPermissions.object",
12862
+ "Profile.classAccesses.apexClass"
12863
+ ],
12864
+ "labels": [
12865
+ "CustomLabels.labels.fullName"
12866
+ ],
12867
+ "permissionset": [
12868
+ "PermissionSet.fieldPermissions.field",
12869
+ "PermissionSet.objectPermissions.object",
12870
+ "PermissionSet.classAccesses.apexClass"
12871
+ ]
12872
+ },
12873
+ "title": "XML duplicate values finder",
12761
12874
  "requiresProject": true,
12762
12875
  "isESM": true,
12763
12876
  "relativePath": [
@@ -12765,35 +12878,35 @@
12765
12878
  "commands",
12766
12879
  "hardis",
12767
12880
  "project",
12768
- "fix",
12769
- "v53flexipages.js"
12881
+ "metadata",
12882
+ "findduplicates.js"
12770
12883
  ],
12771
12884
  "aliasPermutations": [],
12772
12885
  "permutations": [
12773
- "hardis:project:fix:v53flexipages",
12774
- "project:hardis:fix:v53flexipages",
12775
- "project:fix:hardis:v53flexipages",
12776
- "project:fix:v53flexipages:hardis",
12777
- "hardis:fix:project:v53flexipages",
12778
- "fix:hardis:project:v53flexipages",
12779
- "fix:project:hardis:v53flexipages",
12780
- "fix:project:v53flexipages:hardis",
12781
- "hardis:fix:v53flexipages:project",
12782
- "fix:hardis:v53flexipages:project",
12783
- "fix:v53flexipages:hardis:project",
12784
- "fix:v53flexipages:project:hardis",
12785
- "hardis:project:v53flexipages:fix",
12786
- "project:hardis:v53flexipages:fix",
12787
- "project:v53flexipages:hardis:fix",
12788
- "project:v53flexipages:fix:hardis",
12789
- "hardis:v53flexipages:project:fix",
12790
- "v53flexipages:hardis:project:fix",
12791
- "v53flexipages:project:hardis:fix",
12792
- "v53flexipages:project:fix:hardis",
12793
- "hardis:v53flexipages:fix:project",
12794
- "v53flexipages:hardis:fix:project",
12795
- "v53flexipages:fix:hardis:project",
12796
- "v53flexipages:fix:project:hardis"
12886
+ "hardis:project:metadata:findduplicates",
12887
+ "project:hardis:metadata:findduplicates",
12888
+ "project:metadata:hardis:findduplicates",
12889
+ "project:metadata:findduplicates:hardis",
12890
+ "hardis:metadata:project:findduplicates",
12891
+ "metadata:hardis:project:findduplicates",
12892
+ "metadata:project:hardis:findduplicates",
12893
+ "metadata:project:findduplicates:hardis",
12894
+ "hardis:metadata:findduplicates:project",
12895
+ "metadata:hardis:findduplicates:project",
12896
+ "metadata:findduplicates:hardis:project",
12897
+ "metadata:findduplicates:project:hardis",
12898
+ "hardis:project:findduplicates:metadata",
12899
+ "project:hardis:findduplicates:metadata",
12900
+ "project:findduplicates:hardis:metadata",
12901
+ "project:findduplicates:metadata:hardis",
12902
+ "hardis:findduplicates:project:metadata",
12903
+ "findduplicates:hardis:project:metadata",
12904
+ "findduplicates:project:hardis:metadata",
12905
+ "findduplicates:project:metadata:hardis",
12906
+ "hardis:findduplicates:metadata:project",
12907
+ "findduplicates:hardis:metadata:project",
12908
+ "findduplicates:metadata:hardis:project",
12909
+ "findduplicates:metadata:project:hardis"
12797
12910
  ]
12798
12911
  },
12799
12912
  "hardis:project:generate:bypass": {
@@ -13191,119 +13304,6 @@
13191
13304
  "gitdelta:generate:project:hardis"
13192
13305
  ]
13193
13306
  },
13194
- "hardis:project:metadata:findduplicates": {
13195
- "aliases": [],
13196
- "args": {},
13197
- "description": "find duplicate values in XML file(s).\n Find duplicate values in XML file(s). Keys to be checked can be configured in `config/sfdx-hardis.yml` using property metadataDuplicateFindKeys.\n\nDefault config :\nmetadataDuplicateFindKeys :\n[object Object]\n",
13198
- "examples": [
13199
- "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Layout xmlns=\"http://soap.sforce.com/2006/04/metadata\">\n <layoutSections>\n ...\n <layoutColumns>\n <layoutItems>\n <behavior>Required</behavior>\n <field>Name</field>\n </layoutItems>\n <layoutItems>\n <behavior>Required</behavior>\n <field>Name</field>\n </layoutItems>\n </layoutColumns>\n </layoutSections>\n</Layout>\n",
13200
- "\n$ sf hardis:project:metadata:findduplicates --file layout.layout-meta.xml\n[sfdx-hardis] Duplicate values in layout.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : Name\n",
13201
- "\n$ sf hardis:project.metadata:findduplicates -f \"force-app/main/default/**/*.xml\"\n[sfdx-hardis] hardis:project:metadata:findduplicates execution time 0:00:00.397\n[sfdx-hardis] Duplicate values in layout1.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : CreatedById\n\n[sfdx-hardis] Duplicate values in layout2.layout-meta.xml\n - Key : Layout.layoutSections.layoutColumns.layoutItems.field\n - Values : LastModifiedById, Name\n"
13202
- ],
13203
- "flags": {
13204
- "json": {
13205
- "description": "Format output as json.",
13206
- "helpGroup": "GLOBAL",
13207
- "name": "json",
13208
- "allowNo": false,
13209
- "type": "boolean"
13210
- },
13211
- "flags-dir": {
13212
- "helpGroup": "GLOBAL",
13213
- "name": "flags-dir",
13214
- "summary": "Import flag values from a directory.",
13215
- "hasDynamicHelp": false,
13216
- "multiple": false,
13217
- "type": "option"
13218
- },
13219
- "files": {
13220
- "char": "f",
13221
- "description": "XML metadata files path",
13222
- "name": "files",
13223
- "hasDynamicHelp": false,
13224
- "multiple": true,
13225
- "type": "option"
13226
- },
13227
- "websocket": {
13228
- "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
13229
- "name": "websocket",
13230
- "hasDynamicHelp": false,
13231
- "multiple": false,
13232
- "type": "option"
13233
- },
13234
- "skipauth": {
13235
- "description": "Skip authentication check when a default username is required",
13236
- "name": "skipauth",
13237
- "allowNo": false,
13238
- "type": "boolean"
13239
- }
13240
- },
13241
- "hasDynamicHelp": false,
13242
- "hiddenAliases": [],
13243
- "id": "hardis:project:metadata:findduplicates",
13244
- "pluginAlias": "sfdx-hardis",
13245
- "pluginName": "sfdx-hardis",
13246
- "pluginType": "core",
13247
- "strict": true,
13248
- "enableJsonFlag": true,
13249
- "metadataDuplicateFindKeys": {
13250
- "layout": [
13251
- "Layout.layoutSections.layoutColumns.layoutItems.field",
13252
- "Layout.quickActionListItems.quickActionName"
13253
- ],
13254
- "profile": [
13255
- "Profile.fieldPermissions.field",
13256
- "Profile.objectPermissions.object",
13257
- "Profile.classAccesses.apexClass"
13258
- ],
13259
- "labels": [
13260
- "CustomLabels.labels.fullName"
13261
- ],
13262
- "permissionset": [
13263
- "PermissionSet.fieldPermissions.field",
13264
- "PermissionSet.objectPermissions.object",
13265
- "PermissionSet.classAccesses.apexClass"
13266
- ]
13267
- },
13268
- "title": "XML duplicate values finder",
13269
- "requiresProject": true,
13270
- "isESM": true,
13271
- "relativePath": [
13272
- "lib",
13273
- "commands",
13274
- "hardis",
13275
- "project",
13276
- "metadata",
13277
- "findduplicates.js"
13278
- ],
13279
- "aliasPermutations": [],
13280
- "permutations": [
13281
- "hardis:project:metadata:findduplicates",
13282
- "project:hardis:metadata:findduplicates",
13283
- "project:metadata:hardis:findduplicates",
13284
- "project:metadata:findduplicates:hardis",
13285
- "hardis:metadata:project:findduplicates",
13286
- "metadata:hardis:project:findduplicates",
13287
- "metadata:project:hardis:findduplicates",
13288
- "metadata:project:findduplicates:hardis",
13289
- "hardis:metadata:findduplicates:project",
13290
- "metadata:hardis:findduplicates:project",
13291
- "metadata:findduplicates:hardis:project",
13292
- "metadata:findduplicates:project:hardis",
13293
- "hardis:project:findduplicates:metadata",
13294
- "project:hardis:findduplicates:metadata",
13295
- "project:findduplicates:hardis:metadata",
13296
- "project:findduplicates:metadata:hardis",
13297
- "hardis:findduplicates:project:metadata",
13298
- "findduplicates:hardis:project:metadata",
13299
- "findduplicates:project:hardis:metadata",
13300
- "findduplicates:project:metadata:hardis",
13301
- "hardis:findduplicates:metadata:project",
13302
- "findduplicates:hardis:metadata:project",
13303
- "findduplicates:metadata:hardis:project",
13304
- "findduplicates:metadata:project:hardis"
13305
- ]
13306
- },
13307
13307
  "hardis:scratch:pool:create": {
13308
13308
  "aliases": [],
13309
13309
  "args": {},
@@ -15278,5 +15278,5 @@
15278
15278
  ]
15279
15279
  }
15280
15280
  },
15281
- "version": "6.5.5"
15281
+ "version": "6.6.0"
15282
15282
  }