sfdx-hardis 6.4.2 → 6.4.3

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/defaults/templates/files/LargeFilesOnly.json +11 -0
  3. package/lib/commands/hardis/git/pull-requests/extract.js +1 -1
  4. package/lib/commands/hardis/git/pull-requests/extract.js.map +1 -1
  5. package/lib/commands/hardis/lint/access.js +1 -1
  6. package/lib/commands/hardis/lint/access.js.map +1 -1
  7. package/lib/commands/hardis/lint/metadatastatus.js +1 -1
  8. package/lib/commands/hardis/lint/metadatastatus.js.map +1 -1
  9. package/lib/commands/hardis/lint/missingattributes.js +1 -1
  10. package/lib/commands/hardis/lint/missingattributes.js.map +1 -1
  11. package/lib/commands/hardis/lint/unusedmetadatas.js +1 -1
  12. package/lib/commands/hardis/lint/unusedmetadatas.js.map +1 -1
  13. package/lib/commands/hardis/misc/servicenow-report.js +1 -1
  14. package/lib/commands/hardis/misc/servicenow-report.js.map +1 -1
  15. package/lib/commands/hardis/org/configure/files.js +2 -0
  16. package/lib/commands/hardis/org/configure/files.js.map +1 -1
  17. package/lib/commands/hardis/org/diagnose/audittrail.js +1 -1
  18. package/lib/commands/hardis/org/diagnose/audittrail.js.map +1 -1
  19. package/lib/commands/hardis/org/diagnose/legacyapi.js +2 -2
  20. package/lib/commands/hardis/org/diagnose/legacyapi.js.map +1 -1
  21. package/lib/commands/hardis/org/diagnose/licenses.js +1 -1
  22. package/lib/commands/hardis/org/diagnose/licenses.js.map +1 -1
  23. package/lib/commands/hardis/org/diagnose/releaseupdates.js +1 -1
  24. package/lib/commands/hardis/org/diagnose/releaseupdates.js.map +1 -1
  25. package/lib/commands/hardis/org/diagnose/unused-apex-classes.js +1 -1
  26. package/lib/commands/hardis/org/diagnose/unused-apex-classes.js.map +1 -1
  27. package/lib/commands/hardis/org/diagnose/unused-connected-apps.js +1 -1
  28. package/lib/commands/hardis/org/diagnose/unused-connected-apps.js.map +1 -1
  29. package/lib/commands/hardis/org/files/export.js +2 -1
  30. package/lib/commands/hardis/org/files/export.js.map +1 -1
  31. package/lib/commands/hardis/org/monitor/backup.js +1 -1
  32. package/lib/commands/hardis/org/monitor/backup.js.map +1 -1
  33. package/lib/commands/hardis/org/monitor/limits.js +1 -1
  34. package/lib/commands/hardis/org/monitor/limits.js.map +1 -1
  35. package/lib/commands/hardis/org/multi-org-query.js +1 -1
  36. package/lib/commands/hardis/org/multi-org-query.js.map +1 -1
  37. package/lib/commands/hardis/project/audit/callincallout.js +4 -1
  38. package/lib/commands/hardis/project/audit/callincallout.js.map +1 -1
  39. package/lib/commands/hardis/project/audit/remotesites.js +4 -1
  40. package/lib/commands/hardis/project/audit/remotesites.js.map +1 -1
  41. package/lib/common/utils/filesUtils.d.ts +4 -1
  42. package/lib/common/utils/filesUtils.js +105 -51
  43. package/lib/common/utils/filesUtils.js.map +1 -1
  44. package/oclif.manifest.json +1393 -1393
  45. package/package.json +1 -1
@@ -4948,7 +4948,7 @@
4948
4948
  "hardis:org:configure:files": {
4949
4949
  "aliases": [],
4950
4950
  "args": {},
4951
- "description": "\n## Command Behavior\n\n**Configures a project for exporting file attachments from a Salesforce org.**\n\nThis command streamlines the setup of configurations for mass downloading files (such as Notes, Attachments, or Salesforce Files) associated with Salesforce records. It's particularly useful for data backups, migrations, or integrating Salesforce files with external systems.\n\nKey functionalities:\n\n- **Template-Based Configuration:** Allows you to choose from predefined templates for common file export scenarios or start with a blank configuration. Templates can pre-populate the export settings.\n- **Interactive Setup:** Guides you through defining the export project folder name and other export parameters.\n- **`export.json` Generation:** Creates an `export.json` file within the designated project folder. This file contains the configuration for the file export operation, including:\n - **SOQL Query:** A SOQL query to select the parent records from which files will be exported.\n - **File Types:** Specifies which types of files (e.g., `ContentVersion`, `Attachment`) to include.\n - **Output Folder/File Naming:** Defines how the exported files and their containing folders will be named based on record fields.\n - **Overwrite Options:** Controls whether existing files or parent records should be overwritten during the export.\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- **Template Selection:** It uses `selectTemplate` to present predefined file export templates or a blank option to the user.\n- **Interactive Prompts:** The `promptFilesExportConfiguration` utility is used to gather detailed export settings from the user, such as the SOQL query, file types, and naming conventions.\n- **File System Operations:** Employs `fs-extra` to create the project directory (`files/your-project-name/`) and write the `export.json` configuration file.\n- **PascalCase Conversion:** Uses `pascalcase` to format the files export path consistently.\n- **JSON Serialization:** Serializes the collected export configuration into a JSON string and writes it to `export.json`.\n- **WebSocket Communication:** Uses `WebSocketClient.requestOpenFile` to open the generated `export.json` file in VS Code, facilitating immediate configuration.\n</details>\n",
4951
+ "description": "\n## Command Behavior\n\n**Configures a project for exporting file attachments from a Salesforce org.**\n\nThis command streamlines the setup of configurations for mass downloading files (such as Notes, Attachments, or Salesforce Files) associated with Salesforce records. It's particularly useful for data backups, migrations, or integrating Salesforce files with external systems.\n\nKey functionalities:\n\n- **Template-Based Configuration:** Allows you to choose from predefined templates for common file export scenarios or start with a blank configuration. Templates can pre-populate the export settings.\n- **Interactive Setup:** Guides you through defining the export project folder name and other export parameters.\n- **`export.json` Generation:** Creates an `export.json` file within the designated project folder. This file contains the configuration for the file export operation, including:\n - **SOQL Query:** A SOQL query to select the parent records from which files will be exported.\n - **File Types:** Specifies which types of files (e.g., `ContentVersion`, `Attachment`) to include.\n - **File Size Filtering:** Minimum file size in KB to filter files during export (files smaller than this will be skipped).\n - **Output Folder/File Naming:** Defines how the exported files and their containing folders will be named based on record fields.\n - **Overwrite Options:** Controls whether existing files or parent records should be overwritten during the export.\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- **Template Selection:** It uses `selectTemplate` to present predefined file export templates or a blank option to the user.\n- **Interactive Prompts:** The `promptFilesExportConfiguration` utility is used to gather detailed export settings from the user, such as the SOQL query, file types, and naming conventions.\n- **File System Operations:** Employs `fs-extra` to create the project directory (`files/your-project-name/`) and write the `export.json` configuration file.\n- **PascalCase Conversion:** Uses `pascalcase` to format the files export path consistently.\n- **JSON Serialization:** Serializes the collected export configuration into a JSON string and writes it to `export.json`.\n- **WebSocket Communication:** Uses `WebSocketClient.requestOpenFile` to open the generated `export.json` file in VS Code, facilitating immediate configuration.\n</details>\n",
4952
4952
  "examples": [
4953
4953
  "$ sf hardis:org:configure:files"
4954
4954
  ],
@@ -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- **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.\n- **File Download:** Downloads the actual file content from Salesforce.\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 and `promptFilesExportConfiguration` for customizing export options.\n- **Error Handling:** Includes mechanisms to handle potential errors during the export process, such as network issues or API limits.\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,26 +5561,37 @@
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"
@@ -5626,13 +5634,13 @@
5626
5634
  },
5627
5635
  "hasDynamicHelp": true,
5628
5636
  "hiddenAliases": [],
5629
- "id": "hardis:org:diagnose:audittrail",
5637
+ "id": "hardis:org:files:export",
5630
5638
  "pluginAlias": "sfdx-hardis",
5631
5639
  "pluginName": "sfdx-hardis",
5632
5640
  "pluginType": "core",
5633
5641
  "strict": true,
5634
5642
  "enableJsonFlag": true,
5635
- "title": "Diagnose content of Setup Audit Trail",
5643
+ "title": "Export files",
5636
5644
  "requiresProject": false,
5637
5645
  "isESM": true,
5638
5646
  "relativePath": [
@@ -5640,43 +5648,43 @@
5640
5648
  "commands",
5641
5649
  "hardis",
5642
5650
  "org",
5643
- "diagnose",
5644
- "audittrail.js"
5651
+ "files",
5652
+ "export.js"
5645
5653
  ],
5646
5654
  "aliasPermutations": [],
5647
5655
  "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"
5656
+ "hardis:org:files:export",
5657
+ "org:hardis:files:export",
5658
+ "org:files:hardis:export",
5659
+ "org:files:export:hardis",
5660
+ "hardis:files:org:export",
5661
+ "files:hardis:org:export",
5662
+ "files:org:hardis:export",
5663
+ "files:org:export:hardis",
5664
+ "hardis:files:export:org",
5665
+ "files:hardis:export:org",
5666
+ "files:export:hardis:org",
5667
+ "files:export:org:hardis",
5668
+ "hardis:org:export:files",
5669
+ "org:hardis:export:files",
5670
+ "org:export:hardis:files",
5671
+ "org:export:files:hardis",
5672
+ "hardis:export:org:files",
5673
+ "export:hardis:org:files",
5674
+ "export:org:hardis:files",
5675
+ "export:org:files:hardis",
5676
+ "hardis:export:files:org",
5677
+ "export:hardis:files:org",
5678
+ "export:files:hardis:org",
5679
+ "export:files:org:hardis"
5672
5680
  ]
5673
5681
  },
5674
- "hardis:org:diagnose:instanceupgrade": {
5682
+ "hardis:org:files:import": {
5675
5683
  "aliases": [],
5676
5684
  "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",
5685
+ "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
5686
  "examples": [
5679
- "$ sf hardis:org:diagnose:instanceupgrade"
5687
+ "$ sf hardis:org:files:import"
5680
5688
  ],
5681
5689
  "flags": {
5682
5690
  "json": {
@@ -5694,6 +5702,21 @@
5694
5702
  "multiple": false,
5695
5703
  "type": "option"
5696
5704
  },
5705
+ "path": {
5706
+ "char": "p",
5707
+ "description": "Path to the file export project",
5708
+ "name": "path",
5709
+ "hasDynamicHelp": false,
5710
+ "multiple": false,
5711
+ "type": "option"
5712
+ },
5713
+ "overwrite": {
5714
+ "char": "f",
5715
+ "description": "Override existing files (doubles the number of API calls)",
5716
+ "name": "overwrite",
5717
+ "allowNo": false,
5718
+ "type": "boolean"
5719
+ },
5697
5720
  "debug": {
5698
5721
  "char": "d",
5699
5722
  "description": "Activate debug mode (more logs)",
@@ -5732,13 +5755,13 @@
5732
5755
  },
5733
5756
  "hasDynamicHelp": true,
5734
5757
  "hiddenAliases": [],
5735
- "id": "hardis:org:diagnose:instanceupgrade",
5758
+ "id": "hardis:org:files:import",
5736
5759
  "pluginAlias": "sfdx-hardis",
5737
5760
  "pluginName": "sfdx-hardis",
5738
5761
  "pluginType": "core",
5739
5762
  "strict": true,
5740
5763
  "enableJsonFlag": true,
5741
- "title": "Get Instance Upgrade date",
5764
+ "title": "Import files",
5742
5765
  "requiresProject": false,
5743
5766
  "isESM": true,
5744
5767
  "relativePath": [
@@ -5746,46 +5769,44 @@
5746
5769
  "commands",
5747
5770
  "hardis",
5748
5771
  "org",
5749
- "diagnose",
5750
- "instanceupgrade.js"
5772
+ "files",
5773
+ "import.js"
5751
5774
  ],
5752
5775
  "aliasPermutations": [],
5753
5776
  "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"
5777
+ "hardis:org:files:import",
5778
+ "org:hardis:files:import",
5779
+ "org:files:hardis:import",
5780
+ "org:files:import:hardis",
5781
+ "hardis:files:org:import",
5782
+ "files:hardis:org:import",
5783
+ "files:org:hardis:import",
5784
+ "files:org:import:hardis",
5785
+ "hardis:files:import:org",
5786
+ "files:hardis:import:org",
5787
+ "files:import:hardis:org",
5788
+ "files:import:org:hardis",
5789
+ "hardis:org:import:files",
5790
+ "org:hardis:import:files",
5791
+ "org:import:hardis:files",
5792
+ "org:import:files:hardis",
5793
+ "hardis:import:org:files",
5794
+ "import:hardis:org:files",
5795
+ "import:org:hardis:files",
5796
+ "import:org:files:hardis",
5797
+ "hardis:import:files:org",
5798
+ "import:hardis:files:org",
5799
+ "import:files:hardis:org",
5800
+ "import:files:org:hardis"
5778
5801
  ]
5779
5802
  },
5780
- "hardis:org:diagnose:legacyapi": {
5803
+ "hardis:org:fix:listviewmine": {
5781
5804
  "aliases": [],
5782
5805
  "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",
5806
+ "description": "Fix listviews whose scope Mine has been replaced by Everything\n\n[![Invalid scope:Mine, not allowed ? Deploy your ListViews anyway !](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-invalid-scope-mine.jpg)](https://nicolas.vuillamy.fr/invalid-scope-mine-not-allowed-deploy-your-listviews-anyway-443aceca8ac7)\n\nList of ListViews can be:\n\n- read from .sfdx-hardis.yml file in property **listViewsToSetToMine**\n- sent in argument listviews\n\nNote: property **listViewsToSetToMine** can be auto-generated by command hardis:work:save if .sfdx-hardis.yml contains the following configuration\n\n```yaml\nautoCleanTypes:\n - listViewsMine\n```\n\n- Example of sfdx-hardis.yml property `listViewsToSetToMine`:\n\n```yaml\nlistViewsToSetToMine:\n - \"force-app/main/default/objects/Operation__c/listViews/MyCurrentOperations.listView-meta.xml\"\n - \"force-app/main/default/objects/Operation__c/listViews/MyFinalizedOperations.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/Default_Opportunity_Pipeline.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/MyCurrentSubscriptions.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/MySubscriptions.listView-meta.xml\"\n - \"force-app/main/default/objects/Account/listViews/MyActivePartners.listView-meta.xml\"\n```\n\n- If manually written, this could also be:\n\n```yaml\nlistViewsToSetToMine:\n - \"Operation__c:MyCurrentOperations\"\n - \"Operation__c:MyFinalizedOperations\"\n - \"Opportunity:Default_Opportunity_Pipeline\"\n - \"Opportunity:MyCurrentSubscriptions\"\n - \"Opportunity:MySubscriptions\"\n - \"Account:MyActivePartners\"\n```\n\nTroubleshooting: if you need to run this command from an alpine-linux based docker image, use this workaround in your dockerfile:\n\n```dockerfile\n# Do not use puppeteer embedded chromium\nRUN apk add --update --no-cache chromium\nENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=\"true\"\nENV CHROMIUM_PATH=\"/usr/bin/chromium-browser\"\nENV PUPPETEER_EXECUTABLE_PATH=\"$\\{CHROMIUM_PATH}\" // remove \\ before {\n```\n",
5784
5807
  "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"
5808
+ "$ sf hardis:org:fix:listviewmine",
5809
+ "$ sf hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners"
5789
5810
  ],
5790
5811
  "flags": {
5791
5812
  "json": {
@@ -5803,28 +5824,10 @@
5803
5824
  "multiple": false,
5804
5825
  "type": "option"
5805
5826
  },
5806
- "eventtype": {
5807
- "char": "e",
5808
- "description": "Type of EventLogFile event to analyze",
5809
- "name": "eventtype",
5810
- "default": "ApiTotalUsage",
5811
- "hasDynamicHelp": false,
5812
- "multiple": false,
5813
- "type": "option"
5814
- },
5815
- "limit": {
5827
+ "listviews": {
5816
5828
  "char": "l",
5817
- "description": "Number of latest EventLogFile events to analyze",
5818
- "name": "limit",
5819
- "default": 999,
5820
- "hasDynamicHelp": false,
5821
- "multiple": false,
5822
- "type": "option"
5823
- },
5824
- "outputfile": {
5825
- "char": "f",
5826
- "description": "Force the path and name of output report file. Must end with .csv",
5827
- "name": "outputfile",
5829
+ "description": "Comma-separated list of listviews following format Object:ListViewName\nExample: Contact:MyContacts,Contact:MyActiveContacts,Opportunity:MYClosedOpportunities",
5830
+ "name": "listviews",
5828
5831
  "hasDynamicHelp": false,
5829
5832
  "multiple": false,
5830
5833
  "type": "option"
@@ -5867,63 +5870,65 @@
5867
5870
  },
5868
5871
  "hasDynamicHelp": true,
5869
5872
  "hiddenAliases": [],
5870
- "id": "hardis:org:diagnose:legacyapi",
5873
+ "id": "hardis:org:fix:listviewmine",
5871
5874
  "pluginAlias": "sfdx-hardis",
5872
5875
  "pluginName": "sfdx-hardis",
5873
5876
  "pluginType": "core",
5874
5877
  "strict": true,
5875
5878
  "enableJsonFlag": true,
5876
- "title": "Check for legacy API use",
5877
- "requiresProject": false,
5879
+ "title": "Fix listviews with ",
5880
+ "requiresProject": true,
5878
5881
  "isESM": true,
5879
5882
  "relativePath": [
5880
5883
  "lib",
5881
5884
  "commands",
5882
5885
  "hardis",
5883
5886
  "org",
5884
- "diagnose",
5885
- "legacyapi.js"
5887
+ "fix",
5888
+ "listviewmine.js"
5886
5889
  ],
5887
5890
  "aliasPermutations": [],
5888
5891
  "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",
5892
+ "hardis:org:fix:listviewmine",
5893
+ "org:hardis:fix:listviewmine",
5894
+ "org:fix:hardis:listviewmine",
5895
+ "org:fix:listviewmine:hardis",
5896
+ "hardis:fix:org:listviewmine",
5897
+ "fix:hardis:org:listviewmine",
5898
+ "fix:org:hardis:listviewmine",
5899
+ "fix:org:listviewmine:hardis",
5900
+ "hardis:fix:listviewmine:org",
5901
+ "fix:hardis:listviewmine:org",
5902
+ "fix:listviewmine:hardis:org",
5903
+ "fix:listviewmine:org:hardis",
5904
+ "hardis:org:listviewmine:fix",
5905
+ "org:hardis:listviewmine:fix",
5906
+ "org:listviewmine:hardis:fix",
5907
+ "org:listviewmine:fix:hardis",
5908
+ "hardis:listviewmine:org:fix",
5909
+ "listviewmine:hardis:org:fix",
5910
+ "listviewmine:org:hardis:fix",
5911
+ "listviewmine:org:fix:hardis",
5912
+ "hardis:listviewmine:fix:org",
5913
+ "listviewmine:hardis:fix:org",
5914
+ "listviewmine:fix:hardis:org",
5915
+ "listviewmine:fix:org:hardis"
5916
+ ]
5917
+ },
5918
+ "hardis:org:generate:packagexmlfull": {
5919
+ "aliases": [],
5920
+ "args": {},
5921
+ "description": "\n## Command Behavior\n\n**Generates a comprehensive `package.xml` file for a Salesforce org, including all metadata components, even managed ones.**\n\nThis command is essential for various Salesforce development and administration tasks, especially when you need a complete snapshot of an org's metadata. It goes beyond typical source tracking by including managed package components, which is crucial for understanding the full metadata footprint of an org.\n\nKey functionalities:\n\n- **Full Org Metadata Retrieval:** Connects to a specified Salesforce org (or prompts for one if not provided) and retrieves a complete list of all metadata types and their members.\n- **Managed Package Inclusion:** Unlike standard source retrieval, this command explicitly includes metadata from managed packages, providing a truly comprehensive `package.xml`.\n- **Customizable Output:** Allows you to specify the output file path for the generated `package.xml`.\n- **Interactive Org Selection:** If no target org is specified, it interactively prompts the user to choose an org. (or use --no-prompt to skip this step)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce Metadata API Interaction:** It leverages the Salesforce Metadata API to list all available metadata types and then retrieve all components for each type.\n- **`buildOrgManifest` Utility:** The core logic for querying the org's metadata and constructing the `package.xml` is encapsulated within the `buildOrgManifest` utility function.\n- **XML Generation:** It dynamically builds the XML structure of the `package.xml` file, including the `types` and `members` elements for all retrieved metadata.\n- **File System Operations:** It writes the generated `package.xml` file to the specified output path.\n- **Interactive Prompts:** Uses `promptOrgUsernameDefault` to guide the user in selecting the target Salesforce org.\n</details>\n",
5922
+ "examples": [
5923
+ "$ sf hardis:org:generate:packagexmlfull",
5924
+ "$ sf hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml",
5925
+ "$ sf hardis:org:generate:packagexmlfull --target-org nico@example.com"
5926
+ ],
5927
+ "flags": {
5928
+ "json": {
5929
+ "description": "Format output as json.",
5930
+ "helpGroup": "GLOBAL",
5931
+ "name": "json",
5927
5932
  "allowNo": false,
5928
5933
  "type": "boolean"
5929
5934
  },
@@ -5936,20 +5941,12 @@
5936
5941
  "type": "option"
5937
5942
  },
5938
5943
  "outputfile": {
5939
- "char": "f",
5940
- "description": "Force the path and name of output report file. Must end with .csv",
5944
+ "description": "Output package.xml file",
5941
5945
  "name": "outputfile",
5942
5946
  "hasDynamicHelp": false,
5943
5947
  "multiple": false,
5944
5948
  "type": "option"
5945
5949
  },
5946
- "usedonly": {
5947
- "char": "u",
5948
- "description": "Filter to have only used licenses",
5949
- "name": "usedonly",
5950
- "allowNo": false,
5951
- "type": "boolean"
5952
- },
5953
5950
  "debug": {
5954
5951
  "char": "d",
5955
5952
  "description": "Activate debug mode (more logs)",
@@ -5957,6 +5954,13 @@
5957
5954
  "allowNo": false,
5958
5955
  "type": "boolean"
5959
5956
  },
5957
+ "no-prompt": {
5958
+ "char": "n",
5959
+ "description": "Do not prompt for org username, use the default one",
5960
+ "name": "no-prompt",
5961
+ "allowNo": false,
5962
+ "type": "boolean"
5963
+ },
5960
5964
  "websocket": {
5961
5965
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
5962
5966
  "name": "websocket",
@@ -5988,13 +5992,13 @@
5988
5992
  },
5989
5993
  "hasDynamicHelp": true,
5990
5994
  "hiddenAliases": [],
5991
- "id": "hardis:org:diagnose:licenses",
5995
+ "id": "hardis:org:generate:packagexmlfull",
5992
5996
  "pluginAlias": "sfdx-hardis",
5993
5997
  "pluginName": "sfdx-hardis",
5994
5998
  "pluginType": "core",
5995
5999
  "strict": true,
5996
6000
  "enableJsonFlag": true,
5997
- "title": "List licenses subscribed and used in a Salesforce org",
6001
+ "title": "Generate Full Org package.xml",
5998
6002
  "requiresProject": false,
5999
6003
  "isESM": true,
6000
6004
  "relativePath": [
@@ -6002,43 +6006,46 @@
6002
6006
  "commands",
6003
6007
  "hardis",
6004
6008
  "org",
6005
- "diagnose",
6006
- "licenses.js"
6009
+ "generate",
6010
+ "packagexmlfull.js"
6007
6011
  ],
6008
6012
  "aliasPermutations": [],
6009
6013
  "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"
6014
+ "hardis:org:generate:packagexmlfull",
6015
+ "org:hardis:generate:packagexmlfull",
6016
+ "org:generate:hardis:packagexmlfull",
6017
+ "org:generate:packagexmlfull:hardis",
6018
+ "hardis:generate:org:packagexmlfull",
6019
+ "generate:hardis:org:packagexmlfull",
6020
+ "generate:org:hardis:packagexmlfull",
6021
+ "generate:org:packagexmlfull:hardis",
6022
+ "hardis:generate:packagexmlfull:org",
6023
+ "generate:hardis:packagexmlfull:org",
6024
+ "generate:packagexmlfull:hardis:org",
6025
+ "generate:packagexmlfull:org:hardis",
6026
+ "hardis:org:packagexmlfull:generate",
6027
+ "org:hardis:packagexmlfull:generate",
6028
+ "org:packagexmlfull:hardis:generate",
6029
+ "org:packagexmlfull:generate:hardis",
6030
+ "hardis:packagexmlfull:org:generate",
6031
+ "packagexmlfull:hardis:org:generate",
6032
+ "packagexmlfull:org:hardis:generate",
6033
+ "packagexmlfull:org:generate:hardis",
6034
+ "hardis:packagexmlfull:generate:org",
6035
+ "packagexmlfull:hardis:generate:org",
6036
+ "packagexmlfull:generate:hardis:org",
6037
+ "packagexmlfull:generate:org:hardis"
6034
6038
  ]
6035
6039
  },
6036
- "hardis:org:diagnose:releaseupdates": {
6040
+ "hardis:org:diagnose:audittrail": {
6037
6041
  "aliases": [],
6038
6042
  "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",
6043
+ "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",
6040
6044
  "examples": [
6041
- "$ sf hardis:org:diagnose:releaseupdates"
6045
+ "$ sf hardis:org:diagnose:audittrail",
6046
+ "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com",
6047
+ "$ sf hardis:org:diagnose:audittrail --excludeusers baptiste@titi.com,bertrand@titi.com",
6048
+ "$ sf hardis:org:diagnose:audittrail --lastndays 5"
6042
6049
  ],
6043
6050
  "flags": {
6044
6051
  "json": {
@@ -6056,6 +6063,22 @@
6056
6063
  "multiple": false,
6057
6064
  "type": "option"
6058
6065
  },
6066
+ "excludeusers": {
6067
+ "char": "e",
6068
+ "description": "Comma-separated list of usernames to exclude",
6069
+ "name": "excludeusers",
6070
+ "hasDynamicHelp": false,
6071
+ "multiple": false,
6072
+ "type": "option"
6073
+ },
6074
+ "lastndays": {
6075
+ "char": "t",
6076
+ "description": "Number of days to extract from today (included)",
6077
+ "name": "lastndays",
6078
+ "hasDynamicHelp": false,
6079
+ "multiple": false,
6080
+ "type": "option"
6081
+ },
6059
6082
  "outputfile": {
6060
6083
  "char": "f",
6061
6084
  "description": "Force the path and name of output report file. Must end with .csv",
@@ -6102,13 +6125,13 @@
6102
6125
  },
6103
6126
  "hasDynamicHelp": true,
6104
6127
  "hiddenAliases": [],
6105
- "id": "hardis:org:diagnose:releaseupdates",
6128
+ "id": "hardis:org:diagnose:audittrail",
6106
6129
  "pluginAlias": "sfdx-hardis",
6107
6130
  "pluginName": "sfdx-hardis",
6108
6131
  "pluginType": "core",
6109
6132
  "strict": true,
6110
6133
  "enableJsonFlag": true,
6111
- "title": "Check Release Updates of an org",
6134
+ "title": "Diagnose content of Setup Audit Trail",
6112
6135
  "requiresProject": false,
6113
6136
  "isESM": true,
6114
6137
  "relativePath": [
@@ -6117,42 +6140,42 @@
6117
6140
  "hardis",
6118
6141
  "org",
6119
6142
  "diagnose",
6120
- "releaseupdates.js"
6143
+ "audittrail.js"
6121
6144
  ],
6122
6145
  "aliasPermutations": [],
6123
6146
  "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"
6147
+ "hardis:org:diagnose:audittrail",
6148
+ "org:hardis:diagnose:audittrail",
6149
+ "org:diagnose:hardis:audittrail",
6150
+ "org:diagnose:audittrail:hardis",
6151
+ "hardis:diagnose:org:audittrail",
6152
+ "diagnose:hardis:org:audittrail",
6153
+ "diagnose:org:hardis:audittrail",
6154
+ "diagnose:org:audittrail:hardis",
6155
+ "hardis:diagnose:audittrail:org",
6156
+ "diagnose:hardis:audittrail:org",
6157
+ "diagnose:audittrail:hardis:org",
6158
+ "diagnose:audittrail:org:hardis",
6159
+ "hardis:org:audittrail:diagnose",
6160
+ "org:hardis:audittrail:diagnose",
6161
+ "org:audittrail:hardis:diagnose",
6162
+ "org:audittrail:diagnose:hardis",
6163
+ "hardis:audittrail:org:diagnose",
6164
+ "audittrail:hardis:org:diagnose",
6165
+ "audittrail:org:hardis:diagnose",
6166
+ "audittrail:org:diagnose:hardis",
6167
+ "hardis:audittrail:diagnose:org",
6168
+ "audittrail:hardis:diagnose:org",
6169
+ "audittrail:diagnose:hardis:org",
6170
+ "audittrail:diagnose:org:hardis"
6148
6171
  ]
6149
6172
  },
6150
- "hardis:org:diagnose:unsecure-connected-apps": {
6173
+ "hardis:org:diagnose:instanceupgrade": {
6151
6174
  "aliases": [],
6152
6175
  "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",
6176
+ "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",
6154
6177
  "examples": [
6155
- "$ sf hardis:org:diagnose:unsecure-connected-apps"
6178
+ "$ sf hardis:org:diagnose:instanceupgrade"
6156
6179
  ],
6157
6180
  "flags": {
6158
6181
  "json": {
@@ -6170,14 +6193,6 @@
6170
6193
  "multiple": false,
6171
6194
  "type": "option"
6172
6195
  },
6173
- "outputfile": {
6174
- "char": "f",
6175
- "description": "Force the path and name of output report file. Must end with .csv",
6176
- "name": "outputfile",
6177
- "hasDynamicHelp": false,
6178
- "multiple": false,
6179
- "type": "option"
6180
- },
6181
6196
  "debug": {
6182
6197
  "char": "d",
6183
6198
  "description": "Activate debug mode (more logs)",
@@ -6216,13 +6231,13 @@
6216
6231
  },
6217
6232
  "hasDynamicHelp": true,
6218
6233
  "hiddenAliases": [],
6219
- "id": "hardis:org:diagnose:unsecure-connected-apps",
6234
+ "id": "hardis:org:diagnose:instanceupgrade",
6220
6235
  "pluginAlias": "sfdx-hardis",
6221
6236
  "pluginName": "sfdx-hardis",
6222
6237
  "pluginType": "core",
6223
6238
  "strict": true,
6224
6239
  "enableJsonFlag": true,
6225
- "title": "Detect Unsecured Connected Apps",
6240
+ "title": "Get Instance Upgrade date",
6226
6241
  "requiresProject": false,
6227
6242
  "isESM": true,
6228
6243
  "relativePath": [
@@ -6231,72 +6246,84 @@
6231
6246
  "hardis",
6232
6247
  "org",
6233
6248
  "diagnose",
6234
- "unsecure-connected-apps.js"
6249
+ "instanceupgrade.js"
6235
6250
  ],
6236
6251
  "aliasPermutations": [],
6237
6252
  "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",
6253
+ "hardis:org:diagnose:instanceupgrade",
6254
+ "org:hardis:diagnose:instanceupgrade",
6255
+ "org:diagnose:hardis:instanceupgrade",
6256
+ "org:diagnose:instanceupgrade:hardis",
6257
+ "hardis:diagnose:org:instanceupgrade",
6258
+ "diagnose:hardis:org:instanceupgrade",
6259
+ "diagnose:org:hardis:instanceupgrade",
6260
+ "diagnose:org:instanceupgrade:hardis",
6261
+ "hardis:diagnose:instanceupgrade:org",
6262
+ "diagnose:hardis:instanceupgrade:org",
6263
+ "diagnose:instanceupgrade:hardis:org",
6264
+ "diagnose:instanceupgrade:org:hardis",
6265
+ "hardis:org:instanceupgrade:diagnose",
6266
+ "org:hardis:instanceupgrade:diagnose",
6267
+ "org:instanceupgrade:hardis:diagnose",
6268
+ "org:instanceupgrade:diagnose:hardis",
6269
+ "hardis:instanceupgrade:org:diagnose",
6270
+ "instanceupgrade:hardis:org:diagnose",
6271
+ "instanceupgrade:org:hardis:diagnose",
6272
+ "instanceupgrade:org:diagnose:hardis",
6273
+ "hardis:instanceupgrade:diagnose:org",
6274
+ "instanceupgrade:hardis:diagnose:org",
6275
+ "instanceupgrade:diagnose:hardis:org",
6276
+ "instanceupgrade:diagnose:org:hardis"
6277
+ ]
6278
+ },
6279
+ "hardis:org:diagnose:legacyapi": {
6280
+ "aliases": [],
6281
+ "args": {},
6282
+ "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",
6283
+ "examples": [
6284
+ "$ sf hardis:org:diagnose:legacyapi",
6285
+ "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com",
6286
+ "$ sf hardis:org:diagnose:legacyapi --outputfile 'c:/path/to/folder/legacyapi.csv'",
6287
+ "$ sf hardis:org:diagnose:legacyapi -u hardis@myclient.com --outputfile ./tmp/legacyapi.csv"
6288
+ ],
6289
+ "flags": {
6290
+ "json": {
6291
+ "description": "Format output as json.",
6292
+ "helpGroup": "GLOBAL",
6293
+ "name": "json",
6294
+ "allowNo": false,
6295
+ "type": "boolean"
6296
+ },
6297
+ "flags-dir": {
6298
+ "helpGroup": "GLOBAL",
6282
6299
  "name": "flags-dir",
6283
6300
  "summary": "Import flag values from a directory.",
6284
6301
  "hasDynamicHelp": false,
6285
6302
  "multiple": false,
6286
6303
  "type": "option"
6287
6304
  },
6288
- "outputfile": {
6289
- "char": "f",
6290
- "description": "Force the path and name of output report file. Must end with .csv",
6291
- "name": "outputfile",
6305
+ "eventtype": {
6306
+ "char": "e",
6307
+ "description": "Type of EventLogFile event to analyze",
6308
+ "name": "eventtype",
6309
+ "default": "ApiTotalUsage",
6292
6310
  "hasDynamicHelp": false,
6293
6311
  "multiple": false,
6294
6312
  "type": "option"
6295
6313
  },
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",
6314
+ "limit": {
6315
+ "char": "l",
6316
+ "description": "Number of latest EventLogFile events to analyze",
6317
+ "name": "limit",
6318
+ "default": 999,
6319
+ "hasDynamicHelp": false,
6320
+ "multiple": false,
6321
+ "type": "option"
6322
+ },
6323
+ "outputfile": {
6324
+ "char": "f",
6325
+ "description": "Force the path and name of output report file. Must end with .csv",
6326
+ "name": "outputfile",
6300
6327
  "hasDynamicHelp": false,
6301
6328
  "multiple": false,
6302
6329
  "type": "option"
@@ -6339,13 +6366,13 @@
6339
6366
  },
6340
6367
  "hasDynamicHelp": true,
6341
6368
  "hiddenAliases": [],
6342
- "id": "hardis:org:diagnose:unused-apex-classes",
6369
+ "id": "hardis:org:diagnose:legacyapi",
6343
6370
  "pluginAlias": "sfdx-hardis",
6344
6371
  "pluginName": "sfdx-hardis",
6345
6372
  "pluginType": "core",
6346
6373
  "strict": true,
6347
6374
  "enableJsonFlag": true,
6348
- "title": "Detect unused Apex classes in an org",
6375
+ "title": "Check for legacy API use",
6349
6376
  "requiresProject": false,
6350
6377
  "isESM": true,
6351
6378
  "relativePath": [
@@ -6354,42 +6381,42 @@
6354
6381
  "hardis",
6355
6382
  "org",
6356
6383
  "diagnose",
6357
- "unused-apex-classes.js"
6384
+ "legacyapi.js"
6358
6385
  ],
6359
6386
  "aliasPermutations": [],
6360
6387
  "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"
6388
+ "hardis:org:diagnose:legacyapi",
6389
+ "org:hardis:diagnose:legacyapi",
6390
+ "org:diagnose:hardis:legacyapi",
6391
+ "org:diagnose:legacyapi:hardis",
6392
+ "hardis:diagnose:org:legacyapi",
6393
+ "diagnose:hardis:org:legacyapi",
6394
+ "diagnose:org:hardis:legacyapi",
6395
+ "diagnose:org:legacyapi:hardis",
6396
+ "hardis:diagnose:legacyapi:org",
6397
+ "diagnose:hardis:legacyapi:org",
6398
+ "diagnose:legacyapi:hardis:org",
6399
+ "diagnose:legacyapi:org:hardis",
6400
+ "hardis:org:legacyapi:diagnose",
6401
+ "org:hardis:legacyapi:diagnose",
6402
+ "org:legacyapi:hardis:diagnose",
6403
+ "org:legacyapi:diagnose:hardis",
6404
+ "hardis:legacyapi:org:diagnose",
6405
+ "legacyapi:hardis:org:diagnose",
6406
+ "legacyapi:org:hardis:diagnose",
6407
+ "legacyapi:org:diagnose:hardis",
6408
+ "hardis:legacyapi:diagnose:org",
6409
+ "legacyapi:hardis:diagnose:org",
6410
+ "legacyapi:diagnose:hardis:org",
6411
+ "legacyapi:diagnose:org:hardis"
6385
6412
  ]
6386
6413
  },
6387
- "hardis:org:diagnose:unused-connected-apps": {
6414
+ "hardis:org:diagnose:licenses": {
6388
6415
  "aliases": [],
6389
6416
  "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",
6417
+ "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",
6391
6418
  "examples": [
6392
- "$ sf hardis:org:diagnose:unused-connected-apps"
6419
+ "$ sf hardis:org:diagnose:licenses"
6393
6420
  ],
6394
6421
  "flags": {
6395
6422
  "json": {
@@ -6415,6 +6442,13 @@
6415
6442
  "multiple": false,
6416
6443
  "type": "option"
6417
6444
  },
6445
+ "usedonly": {
6446
+ "char": "u",
6447
+ "description": "Filter to have only used licenses",
6448
+ "name": "usedonly",
6449
+ "allowNo": false,
6450
+ "type": "boolean"
6451
+ },
6418
6452
  "debug": {
6419
6453
  "char": "d",
6420
6454
  "description": "Activate debug mode (more logs)",
@@ -6453,30 +6487,13 @@
6453
6487
  },
6454
6488
  "hasDynamicHelp": true,
6455
6489
  "hiddenAliases": [],
6456
- "id": "hardis:org:diagnose:unused-connected-apps",
6490
+ "id": "hardis:org:diagnose:licenses",
6457
6491
  "pluginAlias": "sfdx-hardis",
6458
6492
  "pluginName": "sfdx-hardis",
6459
6493
  "pluginType": "core",
6460
6494
  "strict": true,
6461
6495
  "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
- ],
6496
+ "title": "List licenses subscribed and used in a Salesforce org",
6480
6497
  "requiresProject": false,
6481
6498
  "isESM": true,
6482
6499
  "relativePath": [
@@ -6485,43 +6502,42 @@
6485
6502
  "hardis",
6486
6503
  "org",
6487
6504
  "diagnose",
6488
- "unused-connected-apps.js"
6505
+ "licenses.js"
6489
6506
  ],
6490
6507
  "aliasPermutations": [],
6491
6508
  "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"
6509
+ "hardis:org:diagnose:licenses",
6510
+ "org:hardis:diagnose:licenses",
6511
+ "org:diagnose:hardis:licenses",
6512
+ "org:diagnose:licenses:hardis",
6513
+ "hardis:diagnose:org:licenses",
6514
+ "diagnose:hardis:org:licenses",
6515
+ "diagnose:org:hardis:licenses",
6516
+ "diagnose:org:licenses:hardis",
6517
+ "hardis:diagnose:licenses:org",
6518
+ "diagnose:hardis:licenses:org",
6519
+ "diagnose:licenses:hardis:org",
6520
+ "diagnose:licenses:org:hardis",
6521
+ "hardis:org:licenses:diagnose",
6522
+ "org:hardis:licenses:diagnose",
6523
+ "org:licenses:hardis:diagnose",
6524
+ "org:licenses:diagnose:hardis",
6525
+ "hardis:licenses:org:diagnose",
6526
+ "licenses:hardis:org:diagnose",
6527
+ "licenses:org:hardis:diagnose",
6528
+ "licenses:org:diagnose:hardis",
6529
+ "hardis:licenses:diagnose:org",
6530
+ "licenses:hardis:diagnose:org",
6531
+ "licenses:diagnose:hardis:org",
6532
+ "licenses:diagnose:org:hardis"
6516
6533
  ]
6517
6534
  },
6518
- "hardis:org:diagnose:unusedlicenses": {
6535
+ "hardis:org:diagnose:releaseupdates": {
6519
6536
  "aliases": [],
6520
6537
  "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",
6538
+ "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",
6522
6539
  "examples": [
6523
- "$ sf hardis:org:diagnose:unusedlicenses",
6524
- "$ sf hardis:org:diagnose:unusedlicenses --fix"
6540
+ "$ sf hardis:org:diagnose:releaseupdates"
6525
6541
  ],
6526
6542
  "flags": {
6527
6543
  "json": {
@@ -6585,32 +6601,14 @@
6585
6601
  },
6586
6602
  "hasDynamicHelp": true,
6587
6603
  "hiddenAliases": [],
6588
- "id": "hardis:org:diagnose:unusedlicenses",
6604
+ "id": "hardis:org:diagnose:releaseupdates",
6589
6605
  "pluginAlias": "sfdx-hardis",
6590
6606
  "pluginName": "sfdx-hardis",
6591
6607
  "pluginType": "core",
6592
6608
  "strict": true,
6593
6609
  "enableJsonFlag": true,
6594
- "title": "Detect unused Permission Set Licenses (beta)",
6610
+ "title": "Check Release Updates of an org",
6595
6611
  "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
6612
  "isESM": true,
6615
6613
  "relativePath": [
6616
6614
  "lib",
@@ -6618,46 +6616,42 @@
6618
6616
  "hardis",
6619
6617
  "org",
6620
6618
  "diagnose",
6621
- "unusedlicenses.js"
6619
+ "releaseupdates.js"
6622
6620
  ],
6623
6621
  "aliasPermutations": [],
6624
6622
  "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"
6623
+ "hardis:org:diagnose:releaseupdates",
6624
+ "org:hardis:diagnose:releaseupdates",
6625
+ "org:diagnose:hardis:releaseupdates",
6626
+ "org:diagnose:releaseupdates:hardis",
6627
+ "hardis:diagnose:org:releaseupdates",
6628
+ "diagnose:hardis:org:releaseupdates",
6629
+ "diagnose:org:hardis:releaseupdates",
6630
+ "diagnose:org:releaseupdates:hardis",
6631
+ "hardis:diagnose:releaseupdates:org",
6632
+ "diagnose:hardis:releaseupdates:org",
6633
+ "diagnose:releaseupdates:hardis:org",
6634
+ "diagnose:releaseupdates:org:hardis",
6635
+ "hardis:org:releaseupdates:diagnose",
6636
+ "org:hardis:releaseupdates:diagnose",
6637
+ "org:releaseupdates:hardis:diagnose",
6638
+ "org:releaseupdates:diagnose:hardis",
6639
+ "hardis:releaseupdates:org:diagnose",
6640
+ "releaseupdates:hardis:org:diagnose",
6641
+ "releaseupdates:org:hardis:diagnose",
6642
+ "releaseupdates:org:diagnose:hardis",
6643
+ "hardis:releaseupdates:diagnose:org",
6644
+ "releaseupdates:hardis:diagnose:org",
6645
+ "releaseupdates:diagnose:hardis:org",
6646
+ "releaseupdates:diagnose:org:hardis"
6649
6647
  ]
6650
6648
  },
6651
- "hardis:org:diagnose:unusedusers": {
6649
+ "hardis:org:diagnose:unsecure-connected-apps": {
6652
6650
  "aliases": [],
6653
6651
  "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>",
6652
+ "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",
6655
6653
  "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"
6654
+ "$ sf hardis:org:diagnose:unsecure-connected-apps"
6661
6655
  ],
6662
6656
  "flags": {
6663
6657
  "json": {
@@ -6683,41 +6677,6 @@
6683
6677
  "multiple": false,
6684
6678
  "type": "option"
6685
6679
  },
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
6680
  "debug": {
6722
6681
  "char": "d",
6723
6682
  "description": "Activate debug mode (more logs)",
@@ -6756,13 +6715,13 @@
6756
6715
  },
6757
6716
  "hasDynamicHelp": true,
6758
6717
  "hiddenAliases": [],
6759
- "id": "hardis:org:diagnose:unusedusers",
6718
+ "id": "hardis:org:diagnose:unsecure-connected-apps",
6760
6719
  "pluginAlias": "sfdx-hardis",
6761
6720
  "pluginName": "sfdx-hardis",
6762
6721
  "pluginType": "core",
6763
6722
  "strict": true,
6764
6723
  "enableJsonFlag": true,
6765
- "title": "Detect unused Users in Salesforce",
6724
+ "title": "Detect Unsecured Connected Apps",
6766
6725
  "requiresProject": false,
6767
6726
  "isESM": true,
6768
6727
  "relativePath": [
@@ -6771,42 +6730,43 @@
6771
6730
  "hardis",
6772
6731
  "org",
6773
6732
  "diagnose",
6774
- "unusedusers.js"
6733
+ "unsecure-connected-apps.js"
6775
6734
  ],
6776
6735
  "aliasPermutations": [],
6777
6736
  "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"
6737
+ "hardis:org:diagnose:unsecure-connected-apps",
6738
+ "org:hardis:diagnose:unsecure-connected-apps",
6739
+ "org:diagnose:hardis:unsecure-connected-apps",
6740
+ "org:diagnose:unsecure-connected-apps:hardis",
6741
+ "hardis:diagnose:org:unsecure-connected-apps",
6742
+ "diagnose:hardis:org:unsecure-connected-apps",
6743
+ "diagnose:org:hardis:unsecure-connected-apps",
6744
+ "diagnose:org:unsecure-connected-apps:hardis",
6745
+ "hardis:diagnose:unsecure-connected-apps:org",
6746
+ "diagnose:hardis:unsecure-connected-apps:org",
6747
+ "diagnose:unsecure-connected-apps:hardis:org",
6748
+ "diagnose:unsecure-connected-apps:org:hardis",
6749
+ "hardis:org:unsecure-connected-apps:diagnose",
6750
+ "org:hardis:unsecure-connected-apps:diagnose",
6751
+ "org:unsecure-connected-apps:hardis:diagnose",
6752
+ "org:unsecure-connected-apps:diagnose:hardis",
6753
+ "hardis:unsecure-connected-apps:org:diagnose",
6754
+ "unsecure-connected-apps:hardis:org:diagnose",
6755
+ "unsecure-connected-apps:org:hardis:diagnose",
6756
+ "unsecure-connected-apps:org:diagnose:hardis",
6757
+ "hardis:unsecure-connected-apps:diagnose:org",
6758
+ "unsecure-connected-apps:hardis:diagnose:org",
6759
+ "unsecure-connected-apps:diagnose:hardis:org",
6760
+ "unsecure-connected-apps:diagnose:org:hardis"
6802
6761
  ]
6803
6762
  },
6804
- "hardis:org:files:export": {
6763
+ "hardis:org:diagnose:unused-apex-classes": {
6805
6764
  "aliases": [],
6806
6765
  "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, and output naming conventions.\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.\n- **File Download:** Downloads the actual file content from Salesforce.\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 and `promptFilesExportConfiguration` for customizing export options.\n- **Error Handling:** Includes mechanisms to handle potential errors during the export process, such as network issues or API limits.\n</details>\n",
6766
+ "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",
6808
6767
  "examples": [
6809
- "$ sf hardis:org:files:export"
6768
+ "$ sf hardis:org:diagnose:unused-apex-classes",
6769
+ "$ sf hardis:org:diagnose:unused-apex-classes --days 700"
6810
6770
  ],
6811
6771
  "flags": {
6812
6772
  "json": {
@@ -6824,37 +6784,18 @@
6824
6784
  "multiple": false,
6825
6785
  "type": "option"
6826
6786
  },
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,
6787
+ "outputfile": {
6788
+ "char": "f",
6789
+ "description": "Force the path and name of output report file. Must end with .csv",
6790
+ "name": "outputfile",
6840
6791
  "hasDynamicHelp": false,
6841
6792
  "multiple": false,
6842
6793
  "type": "option"
6843
6794
  },
6844
- "polltimeout": {
6795
+ "days": {
6845
6796
  "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,
6797
+ "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
6798
+ "name": "days",
6858
6799
  "hasDynamicHelp": false,
6859
6800
  "multiple": false,
6860
6801
  "type": "option"
@@ -6897,13 +6838,13 @@
6897
6838
  },
6898
6839
  "hasDynamicHelp": true,
6899
6840
  "hiddenAliases": [],
6900
- "id": "hardis:org:files:export",
6841
+ "id": "hardis:org:diagnose:unused-apex-classes",
6901
6842
  "pluginAlias": "sfdx-hardis",
6902
6843
  "pluginName": "sfdx-hardis",
6903
6844
  "pluginType": "core",
6904
6845
  "strict": true,
6905
6846
  "enableJsonFlag": true,
6906
- "title": "Export files",
6847
+ "title": "Detect unused Apex classes in an org",
6907
6848
  "requiresProject": false,
6908
6849
  "isESM": true,
6909
6850
  "relativePath": [
@@ -6911,43 +6852,43 @@
6911
6852
  "commands",
6912
6853
  "hardis",
6913
6854
  "org",
6914
- "files",
6915
- "export.js"
6855
+ "diagnose",
6856
+ "unused-apex-classes.js"
6916
6857
  ],
6917
6858
  "aliasPermutations": [],
6918
6859
  "permutations": [
6919
- "hardis:org:files:export",
6920
- "org:hardis:files:export",
6921
- "org:files:hardis:export",
6922
- "org:files:export:hardis",
6923
- "hardis:files:org:export",
6924
- "files:hardis:org:export",
6925
- "files:org:hardis:export",
6926
- "files:org:export:hardis",
6927
- "hardis:files:export:org",
6928
- "files:hardis:export:org",
6929
- "files:export:hardis:org",
6930
- "files:export:org:hardis",
6931
- "hardis:org:export:files",
6932
- "org:hardis:export:files",
6933
- "org:export:hardis:files",
6934
- "org:export:files:hardis",
6935
- "hardis:export:org:files",
6936
- "export:hardis:org:files",
6937
- "export:org:hardis:files",
6938
- "export:org:files:hardis",
6939
- "hardis:export:files:org",
6940
- "export:hardis:files:org",
6941
- "export:files:hardis:org",
6942
- "export:files:org:hardis"
6860
+ "hardis:org:diagnose:unused-apex-classes",
6861
+ "org:hardis:diagnose:unused-apex-classes",
6862
+ "org:diagnose:hardis:unused-apex-classes",
6863
+ "org:diagnose:unused-apex-classes:hardis",
6864
+ "hardis:diagnose:org:unused-apex-classes",
6865
+ "diagnose:hardis:org:unused-apex-classes",
6866
+ "diagnose:org:hardis:unused-apex-classes",
6867
+ "diagnose:org:unused-apex-classes:hardis",
6868
+ "hardis:diagnose:unused-apex-classes:org",
6869
+ "diagnose:hardis:unused-apex-classes:org",
6870
+ "diagnose:unused-apex-classes:hardis:org",
6871
+ "diagnose:unused-apex-classes:org:hardis",
6872
+ "hardis:org:unused-apex-classes:diagnose",
6873
+ "org:hardis:unused-apex-classes:diagnose",
6874
+ "org:unused-apex-classes:hardis:diagnose",
6875
+ "org:unused-apex-classes:diagnose:hardis",
6876
+ "hardis:unused-apex-classes:org:diagnose",
6877
+ "unused-apex-classes:hardis:org:diagnose",
6878
+ "unused-apex-classes:org:hardis:diagnose",
6879
+ "unused-apex-classes:org:diagnose:hardis",
6880
+ "hardis:unused-apex-classes:diagnose:org",
6881
+ "unused-apex-classes:hardis:diagnose:org",
6882
+ "unused-apex-classes:diagnose:hardis:org",
6883
+ "unused-apex-classes:diagnose:org:hardis"
6943
6884
  ]
6944
6885
  },
6945
- "hardis:org:files:import": {
6886
+ "hardis:org:diagnose:unused-connected-apps": {
6946
6887
  "aliases": [],
6947
6888
  "args": {},
6948
- "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",
6889
+ "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",
6949
6890
  "examples": [
6950
- "$ sf hardis:org:files:import"
6891
+ "$ sf hardis:org:diagnose:unused-connected-apps"
6951
6892
  ],
6952
6893
  "flags": {
6953
6894
  "json": {
@@ -6965,21 +6906,14 @@
6965
6906
  "multiple": false,
6966
6907
  "type": "option"
6967
6908
  },
6968
- "path": {
6969
- "char": "p",
6970
- "description": "Path to the file export project",
6971
- "name": "path",
6909
+ "outputfile": {
6910
+ "char": "f",
6911
+ "description": "Force the path and name of output report file. Must end with .csv",
6912
+ "name": "outputfile",
6972
6913
  "hasDynamicHelp": false,
6973
6914
  "multiple": false,
6974
6915
  "type": "option"
6975
6916
  },
6976
- "overwrite": {
6977
- "char": "f",
6978
- "description": "Override existing files (doubles the number of API calls)",
6979
- "name": "overwrite",
6980
- "allowNo": false,
6981
- "type": "boolean"
6982
- },
6983
6917
  "debug": {
6984
6918
  "char": "d",
6985
6919
  "description": "Activate debug mode (more logs)",
@@ -7018,58 +6952,75 @@
7018
6952
  },
7019
6953
  "hasDynamicHelp": true,
7020
6954
  "hiddenAliases": [],
7021
- "id": "hardis:org:files:import",
6955
+ "id": "hardis:org:diagnose:unused-connected-apps",
7022
6956
  "pluginAlias": "sfdx-hardis",
7023
6957
  "pluginName": "sfdx-hardis",
7024
6958
  "pluginType": "core",
7025
6959
  "strict": true,
7026
6960
  "enableJsonFlag": true,
7027
- "title": "Import files",
7028
- "requiresProject": false,
7029
- "isESM": true,
7030
- "relativePath": [
7031
- "lib",
7032
- "commands",
6961
+ "title": "Unused Connected Apps in an org",
6962
+ "allowedInactiveConnectedApps": [
6963
+ "Ant Migration Tool",
6964
+ "Chatter Desktop",
6965
+ "Chatter Mobile for BlackBerry",
6966
+ "Force.com IDE",
6967
+ "OIQ_Integration",
6968
+ "Salesforce CLI",
6969
+ "Salesforce Files",
6970
+ "Salesforce Mobile Dashboards",
6971
+ "Salesforce Touch",
6972
+ "Salesforce for Outlook",
6973
+ "SalesforceA",
6974
+ "SalesforceA for Android",
6975
+ "SalesforceA for iOS",
6976
+ "SalesforceDX Namespace Registry",
6977
+ "SalesforceIQ"
6978
+ ],
6979
+ "requiresProject": false,
6980
+ "isESM": true,
6981
+ "relativePath": [
6982
+ "lib",
6983
+ "commands",
7033
6984
  "hardis",
7034
6985
  "org",
7035
- "files",
7036
- "import.js"
6986
+ "diagnose",
6987
+ "unused-connected-apps.js"
7037
6988
  ],
7038
6989
  "aliasPermutations": [],
7039
6990
  "permutations": [
7040
- "hardis:org:files:import",
7041
- "org:hardis:files:import",
7042
- "org:files:hardis:import",
7043
- "org:files:import:hardis",
7044
- "hardis:files:org:import",
7045
- "files:hardis:org:import",
7046
- "files:org:hardis:import",
7047
- "files:org:import:hardis",
7048
- "hardis:files:import:org",
7049
- "files:hardis:import:org",
7050
- "files:import:hardis:org",
7051
- "files:import:org:hardis",
7052
- "hardis:org:import:files",
7053
- "org:hardis:import:files",
7054
- "org:import:hardis:files",
7055
- "org:import:files:hardis",
7056
- "hardis:import:org:files",
7057
- "import:hardis:org:files",
7058
- "import:org:hardis:files",
7059
- "import:org:files:hardis",
7060
- "hardis:import:files:org",
7061
- "import:hardis:files:org",
7062
- "import:files:hardis:org",
7063
- "import:files:org:hardis"
6991
+ "hardis:org:diagnose:unused-connected-apps",
6992
+ "org:hardis:diagnose:unused-connected-apps",
6993
+ "org:diagnose:hardis:unused-connected-apps",
6994
+ "org:diagnose:unused-connected-apps:hardis",
6995
+ "hardis:diagnose:org:unused-connected-apps",
6996
+ "diagnose:hardis:org:unused-connected-apps",
6997
+ "diagnose:org:hardis:unused-connected-apps",
6998
+ "diagnose:org:unused-connected-apps:hardis",
6999
+ "hardis:diagnose:unused-connected-apps:org",
7000
+ "diagnose:hardis:unused-connected-apps:org",
7001
+ "diagnose:unused-connected-apps:hardis:org",
7002
+ "diagnose:unused-connected-apps:org:hardis",
7003
+ "hardis:org:unused-connected-apps:diagnose",
7004
+ "org:hardis:unused-connected-apps:diagnose",
7005
+ "org:unused-connected-apps:hardis:diagnose",
7006
+ "org:unused-connected-apps:diagnose:hardis",
7007
+ "hardis:unused-connected-apps:org:diagnose",
7008
+ "unused-connected-apps:hardis:org:diagnose",
7009
+ "unused-connected-apps:org:hardis:diagnose",
7010
+ "unused-connected-apps:org:diagnose:hardis",
7011
+ "hardis:unused-connected-apps:diagnose:org",
7012
+ "unused-connected-apps:hardis:diagnose:org",
7013
+ "unused-connected-apps:diagnose:hardis:org",
7014
+ "unused-connected-apps:diagnose:org:hardis"
7064
7015
  ]
7065
7016
  },
7066
- "hardis:org:fix:listviewmine": {
7017
+ "hardis:org:diagnose:unusedlicenses": {
7067
7018
  "aliases": [],
7068
7019
  "args": {},
7069
- "description": "Fix listviews whose scope Mine has been replaced by Everything\n\n[![Invalid scope:Mine, not allowed ? Deploy your ListViews anyway !](https://github.com/hardisgroupcom/sfdx-hardis/raw/main/docs/assets/images/article-invalid-scope-mine.jpg)](https://nicolas.vuillamy.fr/invalid-scope-mine-not-allowed-deploy-your-listviews-anyway-443aceca8ac7)\n\nList of ListViews can be:\n\n- read from .sfdx-hardis.yml file in property **listViewsToSetToMine**\n- sent in argument listviews\n\nNote: property **listViewsToSetToMine** can be auto-generated by command hardis:work:save if .sfdx-hardis.yml contains the following configuration\n\n```yaml\nautoCleanTypes:\n - listViewsMine\n```\n\n- Example of sfdx-hardis.yml property `listViewsToSetToMine`:\n\n```yaml\nlistViewsToSetToMine:\n - \"force-app/main/default/objects/Operation__c/listViews/MyCurrentOperations.listView-meta.xml\"\n - \"force-app/main/default/objects/Operation__c/listViews/MyFinalizedOperations.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/Default_Opportunity_Pipeline.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/MyCurrentSubscriptions.listView-meta.xml\"\n - \"force-app/main/default/objects/Opportunity/listViews/MySubscriptions.listView-meta.xml\"\n - \"force-app/main/default/objects/Account/listViews/MyActivePartners.listView-meta.xml\"\n```\n\n- If manually written, this could also be:\n\n```yaml\nlistViewsToSetToMine:\n - \"Operation__c:MyCurrentOperations\"\n - \"Operation__c:MyFinalizedOperations\"\n - \"Opportunity:Default_Opportunity_Pipeline\"\n - \"Opportunity:MyCurrentSubscriptions\"\n - \"Opportunity:MySubscriptions\"\n - \"Account:MyActivePartners\"\n```\n\nTroubleshooting: if you need to run this command from an alpine-linux based docker image, use this workaround in your dockerfile:\n\n```dockerfile\n# Do not use puppeteer embedded chromium\nRUN apk add --update --no-cache chromium\nENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=\"true\"\nENV CHROMIUM_PATH=\"/usr/bin/chromium-browser\"\nENV PUPPETEER_EXECUTABLE_PATH=\"$\\{CHROMIUM_PATH}\" // remove \\ before {\n```\n",
7020
+ "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",
7070
7021
  "examples": [
7071
- "$ sf hardis:org:fix:listviewmine",
7072
- "$ sf hardis:org:fix:listviewmine --listviews Opportunity:MySubscriptions,Account:MyActivePartners"
7022
+ "$ sf hardis:org:diagnose:unusedlicenses",
7023
+ "$ sf hardis:org:diagnose:unusedlicenses --fix"
7073
7024
  ],
7074
7025
  "flags": {
7075
7026
  "json": {
@@ -7087,10 +7038,10 @@
7087
7038
  "multiple": false,
7088
7039
  "type": "option"
7089
7040
  },
7090
- "listviews": {
7091
- "char": "l",
7092
- "description": "Comma-separated list of listviews following format Object:ListViewName\nExample: Contact:MyContacts,Contact:MyActiveContacts,Opportunity:MYClosedOpportunities",
7093
- "name": "listviews",
7041
+ "outputfile": {
7042
+ "char": "f",
7043
+ "description": "Force the path and name of output report file. Must end with .csv",
7044
+ "name": "outputfile",
7094
7045
  "hasDynamicHelp": false,
7095
7046
  "multiple": false,
7096
7047
  "type": "option"
@@ -7133,59 +7084,79 @@
7133
7084
  },
7134
7085
  "hasDynamicHelp": true,
7135
7086
  "hiddenAliases": [],
7136
- "id": "hardis:org:fix:listviewmine",
7087
+ "id": "hardis:org:diagnose:unusedlicenses",
7137
7088
  "pluginAlias": "sfdx-hardis",
7138
7089
  "pluginName": "sfdx-hardis",
7139
7090
  "pluginType": "core",
7140
7091
  "strict": true,
7141
7092
  "enableJsonFlag": true,
7142
- "title": "Fix listviews with ",
7143
- "requiresProject": true,
7093
+ "title": "Detect unused Permission Set Licenses (beta)",
7094
+ "requiresProject": false,
7095
+ "additionalPermissionSetsToAlwaysGet": [
7096
+ "Sales_User"
7097
+ ],
7098
+ "permSetsPermSetLicenses": [
7099
+ {
7100
+ "permSet": "Sales_User",
7101
+ "permSetLicense": "SalesUserPsl"
7102
+ }
7103
+ ],
7104
+ "profilesPermissionSetLicenses": [
7105
+ {
7106
+ "profile": "Salesforce API Only",
7107
+ "permSetLicense": "SalesforceAPIIntegrationPsl"
7108
+ }
7109
+ ],
7110
+ "alwaysExcludeForActiveUsersPermissionSetLicenses": [
7111
+ "IdentityConnect"
7112
+ ],
7144
7113
  "isESM": true,
7145
7114
  "relativePath": [
7146
7115
  "lib",
7147
7116
  "commands",
7148
7117
  "hardis",
7149
7118
  "org",
7150
- "fix",
7151
- "listviewmine.js"
7119
+ "diagnose",
7120
+ "unusedlicenses.js"
7152
7121
  ],
7153
7122
  "aliasPermutations": [],
7154
7123
  "permutations": [
7155
- "hardis:org:fix:listviewmine",
7156
- "org:hardis:fix:listviewmine",
7157
- "org:fix:hardis:listviewmine",
7158
- "org:fix:listviewmine:hardis",
7159
- "hardis:fix:org:listviewmine",
7160
- "fix:hardis:org:listviewmine",
7161
- "fix:org:hardis:listviewmine",
7162
- "fix:org:listviewmine:hardis",
7163
- "hardis:fix:listviewmine:org",
7164
- "fix:hardis:listviewmine:org",
7165
- "fix:listviewmine:hardis:org",
7166
- "fix:listviewmine:org:hardis",
7167
- "hardis:org:listviewmine:fix",
7168
- "org:hardis:listviewmine:fix",
7169
- "org:listviewmine:hardis:fix",
7170
- "org:listviewmine:fix:hardis",
7171
- "hardis:listviewmine:org:fix",
7172
- "listviewmine:hardis:org:fix",
7173
- "listviewmine:org:hardis:fix",
7174
- "listviewmine:org:fix:hardis",
7175
- "hardis:listviewmine:fix:org",
7176
- "listviewmine:hardis:fix:org",
7177
- "listviewmine:fix:hardis:org",
7178
- "listviewmine:fix:org:hardis"
7124
+ "hardis:org:diagnose:unusedlicenses",
7125
+ "org:hardis:diagnose:unusedlicenses",
7126
+ "org:diagnose:hardis:unusedlicenses",
7127
+ "org:diagnose:unusedlicenses:hardis",
7128
+ "hardis:diagnose:org:unusedlicenses",
7129
+ "diagnose:hardis:org:unusedlicenses",
7130
+ "diagnose:org:hardis:unusedlicenses",
7131
+ "diagnose:org:unusedlicenses:hardis",
7132
+ "hardis:diagnose:unusedlicenses:org",
7133
+ "diagnose:hardis:unusedlicenses:org",
7134
+ "diagnose:unusedlicenses:hardis:org",
7135
+ "diagnose:unusedlicenses:org:hardis",
7136
+ "hardis:org:unusedlicenses:diagnose",
7137
+ "org:hardis:unusedlicenses:diagnose",
7138
+ "org:unusedlicenses:hardis:diagnose",
7139
+ "org:unusedlicenses:diagnose:hardis",
7140
+ "hardis:unusedlicenses:org:diagnose",
7141
+ "unusedlicenses:hardis:org:diagnose",
7142
+ "unusedlicenses:org:hardis:diagnose",
7143
+ "unusedlicenses:org:diagnose:hardis",
7144
+ "hardis:unusedlicenses:diagnose:org",
7145
+ "unusedlicenses:hardis:diagnose:org",
7146
+ "unusedlicenses:diagnose:hardis:org",
7147
+ "unusedlicenses:diagnose:org:hardis"
7179
7148
  ]
7180
7149
  },
7181
- "hardis:org:generate:packagexmlfull": {
7150
+ "hardis:org:diagnose:unusedusers": {
7182
7151
  "aliases": [],
7183
7152
  "args": {},
7184
- "description": "\n## Command Behavior\n\n**Generates a comprehensive `package.xml` file for a Salesforce org, including all metadata components, even managed ones.**\n\nThis command is essential for various Salesforce development and administration tasks, especially when you need a complete snapshot of an org's metadata. It goes beyond typical source tracking by including managed package components, which is crucial for understanding the full metadata footprint of an org.\n\nKey functionalities:\n\n- **Full Org Metadata Retrieval:** Connects to a specified Salesforce org (or prompts for one if not provided) and retrieves a complete list of all metadata types and their members.\n- **Managed Package Inclusion:** Unlike standard source retrieval, this command explicitly includes metadata from managed packages, providing a truly comprehensive `package.xml`.\n- **Customizable Output:** Allows you to specify the output file path for the generated `package.xml`.\n- **Interactive Org Selection:** If no target org is specified, it interactively prompts the user to choose an org. (or use --no-prompt to skip this step)\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\nThe command's technical implementation involves:\n\n- **Salesforce Metadata API Interaction:** It leverages the Salesforce Metadata API to list all available metadata types and then retrieve all components for each type.\n- **`buildOrgManifest` Utility:** The core logic for querying the org's metadata and constructing the `package.xml` is encapsulated within the `buildOrgManifest` utility function.\n- **XML Generation:** It dynamically builds the XML structure of the `package.xml` file, including the `types` and `members` elements for all retrieved metadata.\n- **File System Operations:** It writes the generated `package.xml` file to the specified output path.\n- **Interactive Prompts:** Uses `promptOrgUsernameDefault` to guide the user in selecting the target Salesforce org.\n</details>\n",
7153
+ "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>",
7185
7154
  "examples": [
7186
- "$ sf hardis:org:generate:packagexmlfull",
7187
- "$ sf hardis:org:generate:packagexmlfull --outputfile /tmp/packagexmlfull.xml",
7188
- "$ sf hardis:org:generate:packagexmlfull --target-org nico@example.com"
7155
+ "$ sf hardis:org:diagnose:unusedusers",
7156
+ "$ sf hardis:org:diagnose:unusedusers --days 365",
7157
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm",
7158
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licenseidentifiers SFDC,AUL,AUL1",
7159
+ "$ sf hardis:org:diagnose:unusedusers --days 60 --licensetypes all-crm --returnactiveusers"
7189
7160
  ],
7190
7161
  "flags": {
7191
7162
  "json": {
@@ -7204,12 +7175,48 @@
7204
7175
  "type": "option"
7205
7176
  },
7206
7177
  "outputfile": {
7207
- "description": "Output package.xml file",
7178
+ "char": "f",
7179
+ "description": "Force the path and name of output report file. Must end with .csv",
7208
7180
  "name": "outputfile",
7209
7181
  "hasDynamicHelp": false,
7210
7182
  "multiple": false,
7211
7183
  "type": "option"
7212
7184
  },
7185
+ "days": {
7186
+ "char": "t",
7187
+ "description": "Extracts the users that have been inactive for the amount of days specified. In CI, default is 180 days",
7188
+ "name": "days",
7189
+ "hasDynamicHelp": false,
7190
+ "multiple": false,
7191
+ "type": "option"
7192
+ },
7193
+ "licensetypes": {
7194
+ "char": "l",
7195
+ "description": "Type of licenses to check. If set, do not use licenseidentifiers option. In CI, default is all-crm",
7196
+ "name": "licensetypes",
7197
+ "hasDynamicHelp": false,
7198
+ "multiple": false,
7199
+ "options": [
7200
+ "all",
7201
+ "all-crm",
7202
+ "all-paying"
7203
+ ],
7204
+ "type": "option"
7205
+ },
7206
+ "licenseidentifiers": {
7207
+ "char": "i",
7208
+ "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",
7209
+ "name": "licenseidentifiers",
7210
+ "hasDynamicHelp": false,
7211
+ "multiple": false,
7212
+ "type": "option"
7213
+ },
7214
+ "returnactiveusers": {
7215
+ "description": "Inverts the command by returning the active users",
7216
+ "name": "returnactiveusers",
7217
+ "allowNo": false,
7218
+ "type": "boolean"
7219
+ },
7213
7220
  "debug": {
7214
7221
  "char": "d",
7215
7222
  "description": "Activate debug mode (more logs)",
@@ -7217,13 +7224,6 @@
7217
7224
  "allowNo": false,
7218
7225
  "type": "boolean"
7219
7226
  },
7220
- "no-prompt": {
7221
- "char": "n",
7222
- "description": "Do not prompt for org username, use the default one",
7223
- "name": "no-prompt",
7224
- "allowNo": false,
7225
- "type": "boolean"
7226
- },
7227
7227
  "websocket": {
7228
7228
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
7229
7229
  "name": "websocket",
@@ -7255,13 +7255,13 @@
7255
7255
  },
7256
7256
  "hasDynamicHelp": true,
7257
7257
  "hiddenAliases": [],
7258
- "id": "hardis:org:generate:packagexmlfull",
7258
+ "id": "hardis:org:diagnose:unusedusers",
7259
7259
  "pluginAlias": "sfdx-hardis",
7260
7260
  "pluginName": "sfdx-hardis",
7261
7261
  "pluginType": "core",
7262
7262
  "strict": true,
7263
7263
  "enableJsonFlag": true,
7264
- "title": "Generate Full Org package.xml",
7264
+ "title": "Detect unused Users in Salesforce",
7265
7265
  "requiresProject": false,
7266
7266
  "isESM": true,
7267
7267
  "relativePath": [
@@ -7269,44 +7269,43 @@
7269
7269
  "commands",
7270
7270
  "hardis",
7271
7271
  "org",
7272
- "generate",
7273
- "packagexmlfull.js"
7272
+ "diagnose",
7273
+ "unusedusers.js"
7274
7274
  ],
7275
7275
  "aliasPermutations": [],
7276
7276
  "permutations": [
7277
- "hardis:org:generate:packagexmlfull",
7278
- "org:hardis:generate:packagexmlfull",
7279
- "org:generate:hardis:packagexmlfull",
7280
- "org:generate:packagexmlfull:hardis",
7281
- "hardis:generate:org:packagexmlfull",
7282
- "generate:hardis:org:packagexmlfull",
7283
- "generate:org:hardis:packagexmlfull",
7284
- "generate:org:packagexmlfull:hardis",
7285
- "hardis:generate:packagexmlfull:org",
7286
- "generate:hardis:packagexmlfull:org",
7287
- "generate:packagexmlfull:hardis:org",
7288
- "generate:packagexmlfull:org:hardis",
7289
- "hardis:org:packagexmlfull:generate",
7290
- "org:hardis:packagexmlfull:generate",
7291
- "org:packagexmlfull:hardis:generate",
7292
- "org:packagexmlfull:generate:hardis",
7293
- "hardis:packagexmlfull:org:generate",
7294
- "packagexmlfull:hardis:org:generate",
7295
- "packagexmlfull:org:hardis:generate",
7296
- "packagexmlfull:org:generate:hardis",
7297
- "hardis:packagexmlfull:generate:org",
7298
- "packagexmlfull:hardis:generate:org",
7299
- "packagexmlfull:generate:hardis:org",
7300
- "packagexmlfull:generate:org:hardis"
7277
+ "hardis:org:diagnose:unusedusers",
7278
+ "org:hardis:diagnose:unusedusers",
7279
+ "org:diagnose:hardis:unusedusers",
7280
+ "org:diagnose:unusedusers:hardis",
7281
+ "hardis:diagnose:org:unusedusers",
7282
+ "diagnose:hardis:org:unusedusers",
7283
+ "diagnose:org:hardis:unusedusers",
7284
+ "diagnose:org:unusedusers:hardis",
7285
+ "hardis:diagnose:unusedusers:org",
7286
+ "diagnose:hardis:unusedusers:org",
7287
+ "diagnose:unusedusers:hardis:org",
7288
+ "diagnose:unusedusers:org:hardis",
7289
+ "hardis:org:unusedusers:diagnose",
7290
+ "org:hardis:unusedusers:diagnose",
7291
+ "org:unusedusers:hardis:diagnose",
7292
+ "org:unusedusers:diagnose:hardis",
7293
+ "hardis:unusedusers:org:diagnose",
7294
+ "unusedusers:hardis:org:diagnose",
7295
+ "unusedusers:org:hardis:diagnose",
7296
+ "unusedusers:org:diagnose:hardis",
7297
+ "hardis:unusedusers:diagnose:org",
7298
+ "unusedusers:hardis:diagnose:org",
7299
+ "unusedusers:diagnose:hardis:org",
7300
+ "unusedusers:diagnose:org:hardis"
7301
7301
  ]
7302
7302
  },
7303
- "hardis:org:purge:apexlog": {
7303
+ "hardis:org:monitor:all": {
7304
7304
  "aliases": [],
7305
7305
  "args": {},
7306
- "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",
7306
+ "description": "Monitor org, generate reports and sends notifications\n\nYou can disable some commands defining either a **monitoringDisable** property in `.sfdx-hardis.yml`, or a comma separated list in env variable **MONITORING_DISABLE**\n\nExample in .sfdx-hardis.yml:\n \n```yaml\nmonitoringDisable:\n - METADATA_STATUS\n - MISSING_ATTRIBUTES\n - UNUSED_METADATAS\n```\n \nExample in env var:\n\n```sh\nMONITORING_DISABLE=METADATA_STATUS,MISSING_ATTRIBUTES,UNUSED_METADATAS\n```\n\nA [default list of monitoring commands](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/#monitoring-commands) is used, if you want to override it you can define property **monitoringCommands** in your .sfdx-hardis.yml file\n\nExample:\n\n```yaml\nmonitoringCommands:\n - title: My Custom command\n command: sf my:custom:command\n - title: My Custom command 2\n command: sf my:other:custom:command\n```\n\nYou can force the daily run of all commands by defining env var `MONITORING_IGNORE_FREQUENCY=true`\n\nThe default list of commands is the following:\n\n| Key | Description | Command | Frequency |\n| :---: | :---- | :---- | :-----: |\n| [AUDIT_TRAIL](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail) | Detect suspect setup actions in major org | [sf hardis:org:diagnose:audittrail](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail) | daily |\n| [LEGACY_API](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi) | Detect calls to deprecated API versions | [sf hardis:org:diagnose:legacyapi](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi) | daily |\n| [ORG_LIMITS](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits) | Detect if org limits are close to be reached | [sf hardis:org:monitor:limits](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits) | daily |\n| [UNSECURED_CONNECTED_APPS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unsecure-connected-apps) | Detect unsecured Connected Apps in an org | [sf hardis:org:diagnose:unsecure-connected-apps](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unsecure-connected-apps) | daily |\n| [LICENSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/licenses) | Extract licenses information | [sf hardis:org:diagnose:licenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/licenses) | weekly |\n| [LINT_ACCESS](https://sfdx-hardis.cloudity.com/hardis/lint/access) | Detect custom elements with no access rights defined in permission sets | [sf hardis:lint:access](https://sfdx-hardis.cloudity.com/hardis/lint/access) | weekly |\n| [UNUSED_LICENSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses) | Detect permission set licenses that are assigned to users that do not need them | [sf hardis:org:diagnose:unusedlicenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses) | weekly |\n| [UNUSED_USERS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | Detect active users without recent logins | [sf hardis:org:diagnose:unusedusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | weekly |\n| [ACTIVE_USERS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | Detect active users with recent logins | [sf hardis:org:diagnose:unusedusers --returnactiveusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | weekly |\n| [ORG_INFO](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/instanceupgrade) | Get org info + SF instance info + next major upgrade date | [sf hardis:org:diagnose:instanceupgrade](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/instanceupgrade) | weekly |\n| [RELEASE_UPDATES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/releaseupdates) | Gather warnings about incoming and overdue Release Updates | [sf hardis:org:diagnose:releaseupdates](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/releaseupdates) | weekly |\n| [UNUSED_METADATAS](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas) | Detect custom labels and custom permissions that are not in use | [sf hardis:lint:unusedmetadatas](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas) | weekly |\n| [UNUSED_APEX_CLASSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-apex-classes) | Detect unused Apex classes in an org | [sf hardis:org:diagnose:unused-apex-classes](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-apex-classes) | weekly |\n| [CONNECTED_APPS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-connected-apps) | Detect unused Connected Apps in an org | [sf hardis:org:diagnose:unused-connected-apps](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-connected-apps) | weekly |\n| [METADATA_STATUS](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus) | Detect inactive metadata | [sf hardis:lint:metadatastatus](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus) | weekly |\n| [MISSING_ATTRIBUTES](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes) | Detect missing description on custom field | [sf hardis:lint:missingattributes](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes) | weekly |\n\n",
7307
7307
  "examples": [
7308
- "$ sf hardis:org:purge:apexlog",
7309
- "$ sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com"
7308
+ "$ sf hardis:org:monitor:all"
7310
7309
  ],
7311
7310
  "flags": {
7312
7311
  "json": {
@@ -7324,13 +7323,6 @@
7324
7323
  "multiple": false,
7325
7324
  "type": "option"
7326
7325
  },
7327
- "prompt": {
7328
- "char": "z",
7329
- "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7330
- "name": "prompt",
7331
- "allowNo": true,
7332
- "type": "boolean"
7333
- },
7334
7326
  "debug": {
7335
7327
  "char": "d",
7336
7328
  "description": "Activate debug mode (more logs)",
@@ -7369,59 +7361,159 @@
7369
7361
  },
7370
7362
  "hasDynamicHelp": true,
7371
7363
  "hiddenAliases": [],
7372
- "id": "hardis:org:purge:apexlog",
7364
+ "id": "hardis:org:monitor:all",
7373
7365
  "pluginAlias": "sfdx-hardis",
7374
7366
  "pluginName": "sfdx-hardis",
7375
7367
  "pluginType": "core",
7376
7368
  "strict": true,
7377
7369
  "enableJsonFlag": true,
7378
- "title": "Purge Apex Logs",
7379
- "requiresProject": false,
7370
+ "title": "Monitor org",
7371
+ "monitoringCommandsDefault": [
7372
+ {
7373
+ "key": "AUDIT_TRAIL",
7374
+ "title": "Detect suspect setup actions in major org",
7375
+ "command": "sf hardis:org:diagnose:audittrail",
7376
+ "frequency": "daily"
7377
+ },
7378
+ {
7379
+ "key": "LEGACY_API",
7380
+ "title": "Detect calls to deprecated API versions",
7381
+ "command": "sf hardis:org:diagnose:legacyapi",
7382
+ "frequency": "daily"
7383
+ },
7384
+ {
7385
+ "key": "ORG_LIMITS",
7386
+ "title": "Detect if org limits are close to be reached",
7387
+ "command": "sf hardis:org:monitor:limits",
7388
+ "frequency": "daily"
7389
+ },
7390
+ {
7391
+ "key": "UNSECURED_CONNECTED_APPS",
7392
+ "title": "Detect unsecured Connected Apps in an org",
7393
+ "command": "sf hardis:org:diagnose:unsecure-connected-apps",
7394
+ "frequency": "daily"
7395
+ },
7396
+ {
7397
+ "key": "LICENSES",
7398
+ "title": "Extract licenses information",
7399
+ "command": "sf hardis:org:diagnose:licenses",
7400
+ "frequency": "weekly"
7401
+ },
7402
+ {
7403
+ "key": "LINT_ACCESS",
7404
+ "title": "Detect custom elements with no access rights defined in permission sets",
7405
+ "command": "sf hardis:lint:access",
7406
+ "frequency": "weekly"
7407
+ },
7408
+ {
7409
+ "key": "UNUSED_LICENSES",
7410
+ "title": "Detect permission set licenses that are assigned to users that do not need them",
7411
+ "command": "sf hardis:org:diagnose:unusedlicenses",
7412
+ "frequency": "weekly"
7413
+ },
7414
+ {
7415
+ "key": "UNUSED_USERS",
7416
+ "title": "Detect active users without recent logins",
7417
+ "command": "sf hardis:org:diagnose:unusedusers",
7418
+ "frequency": "weekly"
7419
+ },
7420
+ {
7421
+ "key": "ACTIVE_USERS",
7422
+ "title": "Detect active users with recent logins",
7423
+ "command": "sf hardis:org:diagnose:unusedusers --returnactiveusers",
7424
+ "frequency": "weekly"
7425
+ },
7426
+ {
7427
+ "key": "ORG_INFO",
7428
+ "title": "Get org info + SF instance info + next major upgrade date",
7429
+ "command": "sf hardis:org:diagnose:instanceupgrade",
7430
+ "frequency": "weekly"
7431
+ },
7432
+ {
7433
+ "key": "RELEASE_UPDATES",
7434
+ "title": "Gather warnings about incoming and overdue Release Updates",
7435
+ "command": "sf hardis:org:diagnose:releaseupdates",
7436
+ "frequency": "weekly"
7437
+ },
7438
+ {
7439
+ "key": "UNUSED_METADATAS",
7440
+ "title": "Detect custom labels and custom permissions that are not in use",
7441
+ "command": "sf hardis:lint:unusedmetadatas",
7442
+ "frequency": "weekly"
7443
+ },
7444
+ {
7445
+ "key": "UNUSED_APEX_CLASSES",
7446
+ "title": "Detect unused Apex classes in an org",
7447
+ "command": "sf hardis:org:diagnose:unused-apex-classes",
7448
+ "frequency": "weekly"
7449
+ },
7450
+ {
7451
+ "key": "CONNECTED_APPS",
7452
+ "title": "Detect unused Connected Apps in an org",
7453
+ "command": "sf hardis:org:diagnose:unused-connected-apps",
7454
+ "frequency": "weekly"
7455
+ },
7456
+ {
7457
+ "key": "METADATA_STATUS",
7458
+ "title": "Detect inactive metadata",
7459
+ "command": "sf hardis:lint:metadatastatus",
7460
+ "frequency": "weekly"
7461
+ },
7462
+ {
7463
+ "key": "MISSING_ATTRIBUTES",
7464
+ "title": "Detect missing description on custom field",
7465
+ "command": "sf hardis:lint:missingattributes",
7466
+ "frequency": "weekly"
7467
+ }
7468
+ ],
7469
+ "requiresProject": true,
7470
+ "triggerNotification": true,
7380
7471
  "isESM": true,
7381
7472
  "relativePath": [
7382
7473
  "lib",
7383
7474
  "commands",
7384
7475
  "hardis",
7385
7476
  "org",
7386
- "purge",
7387
- "apexlog.js"
7477
+ "monitor",
7478
+ "all.js"
7388
7479
  ],
7389
7480
  "aliasPermutations": [],
7390
7481
  "permutations": [
7391
- "hardis:org:purge:apexlog",
7392
- "org:hardis:purge:apexlog",
7393
- "org:purge:hardis:apexlog",
7394
- "org:purge:apexlog:hardis",
7395
- "hardis:purge:org:apexlog",
7396
- "purge:hardis:org:apexlog",
7397
- "purge:org:hardis:apexlog",
7398
- "purge:org:apexlog:hardis",
7399
- "hardis:purge:apexlog:org",
7400
- "purge:hardis:apexlog:org",
7401
- "purge:apexlog:hardis:org",
7402
- "purge:apexlog:org:hardis",
7403
- "hardis:org:apexlog:purge",
7404
- "org:hardis:apexlog:purge",
7405
- "org:apexlog:hardis:purge",
7406
- "org:apexlog:purge:hardis",
7407
- "hardis:apexlog:org:purge",
7408
- "apexlog:hardis:org:purge",
7409
- "apexlog:org:hardis:purge",
7410
- "apexlog:org:purge:hardis",
7411
- "hardis:apexlog:purge:org",
7412
- "apexlog:hardis:purge:org",
7413
- "apexlog:purge:hardis:org",
7414
- "apexlog:purge:org:hardis"
7482
+ "hardis:org:monitor:all",
7483
+ "org:hardis:monitor:all",
7484
+ "org:monitor:hardis:all",
7485
+ "org:monitor:all:hardis",
7486
+ "hardis:monitor:org:all",
7487
+ "monitor:hardis:org:all",
7488
+ "monitor:org:hardis:all",
7489
+ "monitor:org:all:hardis",
7490
+ "hardis:monitor:all:org",
7491
+ "monitor:hardis:all:org",
7492
+ "monitor:all:hardis:org",
7493
+ "monitor:all:org:hardis",
7494
+ "hardis:org:all:monitor",
7495
+ "org:hardis:all:monitor",
7496
+ "org:all:hardis:monitor",
7497
+ "org:all:monitor:hardis",
7498
+ "hardis:all:org:monitor",
7499
+ "all:hardis:org:monitor",
7500
+ "all:org:hardis:monitor",
7501
+ "all:org:monitor:hardis",
7502
+ "hardis:all:monitor:org",
7503
+ "all:hardis:monitor:org",
7504
+ "all:monitor:hardis:org",
7505
+ "all:monitor:org:hardis"
7415
7506
  ]
7416
7507
  },
7417
- "hardis:org:purge:flow": {
7508
+ "hardis:org:monitor:backup": {
7418
7509
  "aliases": [],
7419
7510
  "args": {},
7420
- "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",
7511
+ "description": "Retrieve sfdx sources in the context of a monitoring backup\n\nThe command exists in 2 modes: filtered(default & recommended) and full.\n\n## Filtered mode (default, better performances)\n\nAutomatically skips metadatas from installed packages with namespace. \n\nYou can remove more metadata types from backup, especially in case you have too many metadatas and that provokes a crash, using:\n\n- Manual update of `manifest/package-skip-items.xml` config file (then commit & push in the same branch)\n\n - Works with full wildcard (`<members>*</members>`) , named metadata (`<members>Account.Name</members>`) or partial wildcards names (`<members>pi__*</members>` , `<members>*__dlm</members>` , or `<members>prefix*suffix</members>`)\n\n- Environment variable MONITORING_BACKUP_SKIP_METADATA_TYPES (example: `MONITORING_BACKUP_SKIP_METADATA_TYPES=CustomLabel,StaticResource,Translation`): that will be applied to all monitoring branches.\n\n## Full mode\n\nActivate it with **--full** parameter, or variable MONITORING_BACKUP_MODE_FULL=true\n\nIgnores filters (namespaces items & manifest/package-skip-items.xml) to retrieve ALL metadatas, including those you might not care about (reports, translations...)\n\nAs we can retrieve only 10000 files by call, the list of all metadatas will be chunked to make multiple calls (and take more time than filtered mode)\n\n- if you use `--full-apply-filters` , manifest/package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES filters will be applied anyway\n- if you use `--exclude-namespaces` , namespaced items will be ignored\n\n_With those both options, it's like if you are not using --full, but with chunked metadata download_\n\n## In CI/CD\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-metadata-backup/) and can output Grafana, Slack and MsTeams Notifications.\n\n## Troubleshooting\n\nIf you have unknown errors (it happens !), you can investigate using the full command with smaller chunks.\n\nExample: `sf hardis:org:monitor:backup --full --exclude-namespaces --full-apply-filters --max-by-chunk 500`\n\nIt will allow you the identify the responsible metadata and ignore it using package-skip-items.xml or MONITORING_BACKUP_SKIP_METADATA_TYPES env variable.\n\n## Documentation\n\n[Doc generation (including visual flows)](https://sfdx-hardis.cloudity.com/hardis/doc/project2markdown/) is triggered at the end of the command.\n\nIf you want to also upload HTML Documentation on your Salesforce Org as static resource, use variable **SFDX_HARDIS_DOC_DEPLOY_TO_ORG=\"true\"**\n\nIf you want to also upload HTML Documentation on Cloudflare, use variable **SFDX_HARDIS_DOC_DEPLOY_TO_CLOUDFLARE=\"true\"**\n\n- If you want to generate the documentation in multiple languages, define variable SFDX_DOC_LANGUAGES (ex: SFDX_DOC_LANGUAGES=en,fr,de)\n- You can define one Cloudflare site by language, for example with the following variables:\n - CLOUDFLARE_PROJECT_NAME_EN=cloudity-demo-english\n - CLOUDFLARE_PROJECT_NAME_FR=cloudity-demo-french\n - CLOUDFLARE_PROJECT_NAME_DE=cloudity-demo-german\n\nIf Flow history doc always display a single state, you probably need to update your workflow configuration:\n\n- on Gitlab: Env variable [`GIT_FETCH_EXTRA_FLAGS: --depth 10000`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/.gitlab-ci.yml#L11)\n- on GitHub: [`fetch-depth: 0`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/.github/workflows/org-monitoring.yml#L58)\n- on Azure: [`fetchDepth: \"0\"`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/azure-pipelines.yml#L39)\n- on Bitbucket: [`step: clone: depth: full`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/bitbucket-pipelines.yml#L18)\n",
7421
7512
  "examples": [
7422
- "$ sf hardis:org:purge:flow",
7423
- "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --no-prompt --delete-flow-interviews",
7424
- "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status \"Obsolete,Draft,InvalidDraft\" --name TestFlow"
7513
+ "$ sf hardis:org:monitor:backup",
7514
+ "$ sf hardis:org:monitor:backup --full",
7515
+ "$ sf hardis:org:monitor:backup --full --exclude-namespaces",
7516
+ "$ sf hardis:org:monitor:backup --full --exclude-namespaces --full-apply-filters"
7425
7517
  ],
7426
7518
  "flags": {
7427
7519
  "json": {
@@ -7439,48 +7531,53 @@
7439
7531
  "multiple": false,
7440
7532
  "type": "option"
7441
7533
  },
7442
- "prompt": {
7443
- "char": "z",
7444
- "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7445
- "name": "prompt",
7446
- "allowNo": true,
7534
+ "full": {
7535
+ "description": "Dot not take in account filtering using package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES. Efficient but much much slower !",
7536
+ "name": "full",
7537
+ "allowNo": false,
7447
7538
  "type": "boolean"
7448
7539
  },
7449
- "name": {
7450
- "char": "n",
7451
- "description": "Filter according to Name criteria",
7452
- "name": "name",
7540
+ "max-by-chunk": {
7541
+ "char": "m",
7542
+ "description": "If mode --full is activated, maximum number of metadatas in a package.xml chunk",
7543
+ "name": "max-by-chunk",
7544
+ "default": 3000,
7453
7545
  "hasDynamicHelp": false,
7454
7546
  "multiple": false,
7455
7547
  "type": "option"
7456
7548
  },
7457
- "status": {
7458
- "char": "s",
7459
- "description": "Filter according to Status criteria",
7460
- "name": "status",
7549
+ "exclude-namespaces": {
7550
+ "char": "e",
7551
+ "description": "If mode --full is activated, exclude namespaced metadatas",
7552
+ "name": "exclude-namespaces",
7553
+ "allowNo": false,
7554
+ "type": "boolean"
7555
+ },
7556
+ "full-apply-filters": {
7557
+ "char": "z",
7558
+ "description": "If mode --full is activated, apply filters of manifest/package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES anyway",
7559
+ "name": "full-apply-filters",
7560
+ "allowNo": false,
7561
+ "type": "boolean"
7562
+ },
7563
+ "start-chunk": {
7564
+ "description": "Use this parameter to troubleshoot a specific chunk. It will be used as the first chunk to retrieve",
7565
+ "name": "start-chunk",
7566
+ "default": 1,
7461
7567
  "hasDynamicHelp": false,
7462
7568
  "multiple": false,
7463
7569
  "type": "option"
7464
7570
  },
7465
- "delete-flow-interviews": {
7466
- "char": "w",
7467
- "description": "If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions",
7468
- "name": "delete-flow-interviews",
7571
+ "skip-doc": {
7572
+ "description": "Skip the generation of project documentation at the end of the command",
7573
+ "name": "skip-doc",
7469
7574
  "allowNo": false,
7470
7575
  "type": "boolean"
7471
7576
  },
7472
- "allowpurgefailure": {
7577
+ "outputfile": {
7473
7578
  "char": "f",
7474
- "description": "Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable",
7475
- "name": "allowpurgefailure",
7476
- "allowNo": true,
7477
- "type": "boolean"
7478
- },
7479
- "instanceurl": {
7480
- "char": "r",
7481
- "description": "URL of org instance",
7482
- "name": "instanceurl",
7483
- "default": "https://login.salesforce.com",
7579
+ "description": "Force the path and name of output report file. Must end with .csv",
7580
+ "name": "outputfile",
7484
7581
  "hasDynamicHelp": false,
7485
7582
  "multiple": false,
7486
7583
  "type": "option"
@@ -7523,57 +7620,58 @@
7523
7620
  },
7524
7621
  "hasDynamicHelp": true,
7525
7622
  "hiddenAliases": [],
7526
- "id": "hardis:org:purge:flow",
7623
+ "id": "hardis:org:monitor:backup",
7527
7624
  "pluginAlias": "sfdx-hardis",
7528
7625
  "pluginName": "sfdx-hardis",
7529
7626
  "pluginType": "core",
7530
7627
  "strict": true,
7531
7628
  "enableJsonFlag": true,
7532
- "title": "Purge Flow versions",
7533
- "requiresProject": false,
7629
+ "title": "Backup DX sources",
7630
+ "requiresProject": true,
7631
+ "triggerNotification": true,
7534
7632
  "isESM": true,
7535
7633
  "relativePath": [
7536
7634
  "lib",
7537
7635
  "commands",
7538
7636
  "hardis",
7539
7637
  "org",
7540
- "purge",
7541
- "flow.js"
7638
+ "monitor",
7639
+ "backup.js"
7542
7640
  ],
7543
7641
  "aliasPermutations": [],
7544
7642
  "permutations": [
7545
- "hardis:org:purge:flow",
7546
- "org:hardis:purge:flow",
7547
- "org:purge:hardis:flow",
7548
- "org:purge:flow:hardis",
7549
- "hardis:purge:org:flow",
7550
- "purge:hardis:org:flow",
7551
- "purge:org:hardis:flow",
7552
- "purge:org:flow:hardis",
7553
- "hardis:purge:flow:org",
7554
- "purge:hardis:flow:org",
7555
- "purge:flow:hardis:org",
7556
- "purge:flow:org:hardis",
7557
- "hardis:org:flow:purge",
7558
- "org:hardis:flow:purge",
7559
- "org:flow:hardis:purge",
7560
- "org:flow:purge:hardis",
7561
- "hardis:flow:org:purge",
7562
- "flow:hardis:org:purge",
7563
- "flow:org:hardis:purge",
7564
- "flow:org:purge:hardis",
7565
- "hardis:flow:purge:org",
7566
- "flow:hardis:purge:org",
7567
- "flow:purge:hardis:org",
7568
- "flow:purge:org:hardis"
7643
+ "hardis:org:monitor:backup",
7644
+ "org:hardis:monitor:backup",
7645
+ "org:monitor:hardis:backup",
7646
+ "org:monitor:backup:hardis",
7647
+ "hardis:monitor:org:backup",
7648
+ "monitor:hardis:org:backup",
7649
+ "monitor:org:hardis:backup",
7650
+ "monitor:org:backup:hardis",
7651
+ "hardis:monitor:backup:org",
7652
+ "monitor:hardis:backup:org",
7653
+ "monitor:backup:hardis:org",
7654
+ "monitor:backup:org:hardis",
7655
+ "hardis:org:backup:monitor",
7656
+ "org:hardis:backup:monitor",
7657
+ "org:backup:hardis:monitor",
7658
+ "org:backup:monitor:hardis",
7659
+ "hardis:backup:org:monitor",
7660
+ "backup:hardis:org:monitor",
7661
+ "backup:org:hardis:monitor",
7662
+ "backup:org:monitor:hardis",
7663
+ "hardis:backup:monitor:org",
7664
+ "backup:hardis:monitor:org",
7665
+ "backup:monitor:hardis:org",
7666
+ "backup:monitor:org:hardis"
7569
7667
  ]
7570
7668
  },
7571
- "hardis:org:monitor:all": {
7669
+ "hardis:org:monitor:limits": {
7572
7670
  "aliases": [],
7573
7671
  "args": {},
7574
- "description": "Monitor org, generate reports and sends notifications\n\nYou can disable some commands defining either a **monitoringDisable** property in `.sfdx-hardis.yml`, or a comma separated list in env variable **MONITORING_DISABLE**\n\nExample in .sfdx-hardis.yml:\n \n```yaml\nmonitoringDisable:\n - METADATA_STATUS\n - MISSING_ATTRIBUTES\n - UNUSED_METADATAS\n```\n \nExample in env var:\n\n```sh\nMONITORING_DISABLE=METADATA_STATUS,MISSING_ATTRIBUTES,UNUSED_METADATAS\n```\n\nA [default list of monitoring commands](https://sfdx-hardis.cloudity.com/salesforce-monitoring-home/#monitoring-commands) is used, if you want to override it you can define property **monitoringCommands** in your .sfdx-hardis.yml file\n\nExample:\n\n```yaml\nmonitoringCommands:\n - title: My Custom command\n command: sf my:custom:command\n - title: My Custom command 2\n command: sf my:other:custom:command\n```\n\nYou can force the daily run of all commands by defining env var `MONITORING_IGNORE_FREQUENCY=true`\n\nThe default list of commands is the following:\n\n| Key | Description | Command | Frequency |\n| :---: | :---- | :---- | :-----: |\n| [AUDIT_TRAIL](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail) | Detect suspect setup actions in major org | [sf hardis:org:diagnose:audittrail](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/audittrail) | daily |\n| [LEGACY_API](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi) | Detect calls to deprecated API versions | [sf hardis:org:diagnose:legacyapi](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/legacyapi) | daily |\n| [ORG_LIMITS](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits) | Detect if org limits are close to be reached | [sf hardis:org:monitor:limits](https://sfdx-hardis.cloudity.com/hardis/org/monitor/limits) | daily |\n| [UNSECURED_CONNECTED_APPS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unsecure-connected-apps) | Detect unsecured Connected Apps in an org | [sf hardis:org:diagnose:unsecure-connected-apps](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unsecure-connected-apps) | daily |\n| [LICENSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/licenses) | Extract licenses information | [sf hardis:org:diagnose:licenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/licenses) | weekly |\n| [LINT_ACCESS](https://sfdx-hardis.cloudity.com/hardis/lint/access) | Detect custom elements with no access rights defined in permission sets | [sf hardis:lint:access](https://sfdx-hardis.cloudity.com/hardis/lint/access) | weekly |\n| [UNUSED_LICENSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses) | Detect permission set licenses that are assigned to users that do not need them | [sf hardis:org:diagnose:unusedlicenses](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedlicenses) | weekly |\n| [UNUSED_USERS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | Detect active users without recent logins | [sf hardis:org:diagnose:unusedusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | weekly |\n| [ACTIVE_USERS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | Detect active users with recent logins | [sf hardis:org:diagnose:unusedusers --returnactiveusers](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unusedusers) | weekly |\n| [ORG_INFO](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/instanceupgrade) | Get org info + SF instance info + next major upgrade date | [sf hardis:org:diagnose:instanceupgrade](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/instanceupgrade) | weekly |\n| [RELEASE_UPDATES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/releaseupdates) | Gather warnings about incoming and overdue Release Updates | [sf hardis:org:diagnose:releaseupdates](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/releaseupdates) | weekly |\n| [UNUSED_METADATAS](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas) | Detect custom labels and custom permissions that are not in use | [sf hardis:lint:unusedmetadatas](https://sfdx-hardis.cloudity.com/hardis/lint/unusedmetadatas) | weekly |\n| [UNUSED_APEX_CLASSES](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-apex-classes) | Detect unused Apex classes in an org | [sf hardis:org:diagnose:unused-apex-classes](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-apex-classes) | weekly |\n| [CONNECTED_APPS](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-connected-apps) | Detect unused Connected Apps in an org | [sf hardis:org:diagnose:unused-connected-apps](https://sfdx-hardis.cloudity.com/hardis/org/diagnose/unused-connected-apps) | weekly |\n| [METADATA_STATUS](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus) | Detect inactive metadata | [sf hardis:lint:metadatastatus](https://sfdx-hardis.cloudity.com/hardis/lint/metadatastatus) | weekly |\n| [MISSING_ATTRIBUTES](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes) | Detect missing description on custom field | [sf hardis:lint:missingattributes](https://sfdx-hardis.cloudity.com/hardis/lint/missingattributes) | weekly |\n\n",
7672
+ "description": "\n## Command Behavior\n\n**Checks the current usage of various Salesforce org limits and sends notifications if thresholds are exceeded.**\n\nThis command is a critical component of proactive Salesforce org management, helping administrators and developers monitor resource consumption and prevent hitting critical limits that could impact performance or functionality. It provides early warnings when limits are approaching their capacity.\n\nKey functionalities:\n\n- **Limit Retrieval:** Fetches a comprehensive list of all Salesforce org limits using the Salesforce CLI.\n- **Usage Calculation:** Calculates the percentage of each limit that is currently being used.\n- **Threshold-Based Alerting:** Assigns a severity (success, warning, or error) to each limit based on configurable thresholds:\n - **Warning:** If usage exceeds 50% (configurable via `LIMIT_THRESHOLD_WARNING` environment variable).\n - **Error:** If usage exceeds 75% (configurable via `LIMIT_THRESHOLD_ERROR` environment variable).\n- **CSV Report Generation:** Generates a CSV file containing all org limits, their current usage, maximum allowed, and calculated percentage used, along with the assigned severity.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of limits that have exceeded the warning or error thresholds.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-limits/) 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 CLI Integration:** It executes the `sf org limits list` command to retrieve the current org limits. It parses the JSON output of this command.\n- **Data Processing:** It iterates through the retrieved limits, calculates the `used` and `percentUsed` values, and assigns a `severity` (success, warning, error) based on the configured thresholds.\n- **Environment Variable Configuration:** Reads `LIMIT_THRESHOLD_WARNING` and `LIMIT_THRESHOLD_ERROR` environment variables to set the warning and error thresholds for limit usage.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of org limits.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and detailed metrics for each limit, which can be consumed by monitoring dashboards like Grafana.\n- **Exit Code Management:** Sets the process exit code to 1 if any limit is in an 'error' state, indicating a critical issue.\n</details>\n",
7575
7673
  "examples": [
7576
- "$ sf hardis:org:monitor:all"
7674
+ "$ sf hardis:org:monitor:limits"
7577
7675
  ],
7578
7676
  "flags": {
7579
7677
  "json": {
@@ -7591,6 +7689,14 @@
7591
7689
  "multiple": false,
7592
7690
  "type": "option"
7593
7691
  },
7692
+ "outputfile": {
7693
+ "char": "f",
7694
+ "description": "Force the path and name of output report file. Must end with .csv",
7695
+ "name": "outputfile",
7696
+ "hasDynamicHelp": false,
7697
+ "multiple": false,
7698
+ "type": "option"
7699
+ },
7594
7700
  "debug": {
7595
7701
  "char": "d",
7596
7702
  "description": "Activate debug mode (more logs)",
@@ -7629,159 +7735,59 @@
7629
7735
  },
7630
7736
  "hasDynamicHelp": true,
7631
7737
  "hiddenAliases": [],
7632
- "id": "hardis:org:monitor:all",
7738
+ "id": "hardis:org:monitor:limits",
7633
7739
  "pluginAlias": "sfdx-hardis",
7634
7740
  "pluginName": "sfdx-hardis",
7635
7741
  "pluginType": "core",
7636
7742
  "strict": true,
7637
7743
  "enableJsonFlag": true,
7638
- "title": "Monitor org",
7639
- "monitoringCommandsDefault": [
7640
- {
7641
- "key": "AUDIT_TRAIL",
7642
- "title": "Detect suspect setup actions in major org",
7643
- "command": "sf hardis:org:diagnose:audittrail",
7644
- "frequency": "daily"
7645
- },
7646
- {
7647
- "key": "LEGACY_API",
7648
- "title": "Detect calls to deprecated API versions",
7649
- "command": "sf hardis:org:diagnose:legacyapi",
7650
- "frequency": "daily"
7651
- },
7652
- {
7653
- "key": "ORG_LIMITS",
7654
- "title": "Detect if org limits are close to be reached",
7655
- "command": "sf hardis:org:monitor:limits",
7656
- "frequency": "daily"
7657
- },
7658
- {
7659
- "key": "UNSECURED_CONNECTED_APPS",
7660
- "title": "Detect unsecured Connected Apps in an org",
7661
- "command": "sf hardis:org:diagnose:unsecure-connected-apps",
7662
- "frequency": "daily"
7663
- },
7664
- {
7665
- "key": "LICENSES",
7666
- "title": "Extract licenses information",
7667
- "command": "sf hardis:org:diagnose:licenses",
7668
- "frequency": "weekly"
7669
- },
7670
- {
7671
- "key": "LINT_ACCESS",
7672
- "title": "Detect custom elements with no access rights defined in permission sets",
7673
- "command": "sf hardis:lint:access",
7674
- "frequency": "weekly"
7675
- },
7676
- {
7677
- "key": "UNUSED_LICENSES",
7678
- "title": "Detect permission set licenses that are assigned to users that do not need them",
7679
- "command": "sf hardis:org:diagnose:unusedlicenses",
7680
- "frequency": "weekly"
7681
- },
7682
- {
7683
- "key": "UNUSED_USERS",
7684
- "title": "Detect active users without recent logins",
7685
- "command": "sf hardis:org:diagnose:unusedusers",
7686
- "frequency": "weekly"
7687
- },
7688
- {
7689
- "key": "ACTIVE_USERS",
7690
- "title": "Detect active users with recent logins",
7691
- "command": "sf hardis:org:diagnose:unusedusers --returnactiveusers",
7692
- "frequency": "weekly"
7693
- },
7694
- {
7695
- "key": "ORG_INFO",
7696
- "title": "Get org info + SF instance info + next major upgrade date",
7697
- "command": "sf hardis:org:diagnose:instanceupgrade",
7698
- "frequency": "weekly"
7699
- },
7700
- {
7701
- "key": "RELEASE_UPDATES",
7702
- "title": "Gather warnings about incoming and overdue Release Updates",
7703
- "command": "sf hardis:org:diagnose:releaseupdates",
7704
- "frequency": "weekly"
7705
- },
7706
- {
7707
- "key": "UNUSED_METADATAS",
7708
- "title": "Detect custom labels and custom permissions that are not in use",
7709
- "command": "sf hardis:lint:unusedmetadatas",
7710
- "frequency": "weekly"
7711
- },
7712
- {
7713
- "key": "UNUSED_APEX_CLASSES",
7714
- "title": "Detect unused Apex classes in an org",
7715
- "command": "sf hardis:org:diagnose:unused-apex-classes",
7716
- "frequency": "weekly"
7717
- },
7718
- {
7719
- "key": "CONNECTED_APPS",
7720
- "title": "Detect unused Connected Apps in an org",
7721
- "command": "sf hardis:org:diagnose:unused-connected-apps",
7722
- "frequency": "weekly"
7723
- },
7724
- {
7725
- "key": "METADATA_STATUS",
7726
- "title": "Detect inactive metadata",
7727
- "command": "sf hardis:lint:metadatastatus",
7728
- "frequency": "weekly"
7729
- },
7730
- {
7731
- "key": "MISSING_ATTRIBUTES",
7732
- "title": "Detect missing description on custom field",
7733
- "command": "sf hardis:lint:missingattributes",
7734
- "frequency": "weekly"
7735
- }
7736
- ],
7744
+ "title": "Check org limits",
7737
7745
  "requiresProject": true,
7738
7746
  "triggerNotification": true,
7739
7747
  "isESM": true,
7740
- "relativePath": [
7741
- "lib",
7742
- "commands",
7743
- "hardis",
7744
- "org",
7745
- "monitor",
7746
- "all.js"
7747
- ],
7748
- "aliasPermutations": [],
7749
- "permutations": [
7750
- "hardis:org:monitor:all",
7751
- "org:hardis:monitor:all",
7752
- "org:monitor:hardis:all",
7753
- "org:monitor:all:hardis",
7754
- "hardis:monitor:org:all",
7755
- "monitor:hardis:org:all",
7756
- "monitor:org:hardis:all",
7757
- "monitor:org:all:hardis",
7758
- "hardis:monitor:all:org",
7759
- "monitor:hardis:all:org",
7760
- "monitor:all:hardis:org",
7761
- "monitor:all:org:hardis",
7762
- "hardis:org:all:monitor",
7763
- "org:hardis:all:monitor",
7764
- "org:all:hardis:monitor",
7765
- "org:all:monitor:hardis",
7766
- "hardis:all:org:monitor",
7767
- "all:hardis:org:monitor",
7768
- "all:org:hardis:monitor",
7769
- "all:org:monitor:hardis",
7770
- "hardis:all:monitor:org",
7771
- "all:hardis:monitor:org",
7772
- "all:monitor:hardis:org",
7773
- "all:monitor:org:hardis"
7748
+ "relativePath": [
7749
+ "lib",
7750
+ "commands",
7751
+ "hardis",
7752
+ "org",
7753
+ "monitor",
7754
+ "limits.js"
7755
+ ],
7756
+ "aliasPermutations": [],
7757
+ "permutations": [
7758
+ "hardis:org:monitor:limits",
7759
+ "org:hardis:monitor:limits",
7760
+ "org:monitor:hardis:limits",
7761
+ "org:monitor:limits:hardis",
7762
+ "hardis:monitor:org:limits",
7763
+ "monitor:hardis:org:limits",
7764
+ "monitor:org:hardis:limits",
7765
+ "monitor:org:limits:hardis",
7766
+ "hardis:monitor:limits:org",
7767
+ "monitor:hardis:limits:org",
7768
+ "monitor:limits:hardis:org",
7769
+ "monitor:limits:org:hardis",
7770
+ "hardis:org:limits:monitor",
7771
+ "org:hardis:limits:monitor",
7772
+ "org:limits:hardis:monitor",
7773
+ "org:limits:monitor:hardis",
7774
+ "hardis:limits:org:monitor",
7775
+ "limits:hardis:org:monitor",
7776
+ "limits:org:hardis:monitor",
7777
+ "limits:org:monitor:hardis",
7778
+ "hardis:limits:monitor:org",
7779
+ "limits:hardis:monitor:org",
7780
+ "limits:monitor:hardis:org",
7781
+ "limits:monitor:org:hardis"
7774
7782
  ]
7775
7783
  },
7776
- "hardis:org:monitor:backup": {
7784
+ "hardis:org:purge:apexlog": {
7777
7785
  "aliases": [],
7778
7786
  "args": {},
7779
- "description": "Retrieve sfdx sources in the context of a monitoring backup\n\nThe command exists in 2 modes: filtered(default & recommended) and full.\n\n## Filtered mode (default, better performances)\n\nAutomatically skips metadatas from installed packages with namespace. \n\nYou can remove more metadata types from backup, especially in case you have too many metadatas and that provokes a crash, using:\n\n- Manual update of `manifest/package-skip-items.xml` config file (then commit & push in the same branch)\n\n - Works with full wildcard (`<members>*</members>`) , named metadata (`<members>Account.Name</members>`) or partial wildcards names (`<members>pi__*</members>` , `<members>*__dlm</members>` , or `<members>prefix*suffix</members>`)\n\n- Environment variable MONITORING_BACKUP_SKIP_METADATA_TYPES (example: `MONITORING_BACKUP_SKIP_METADATA_TYPES=CustomLabel,StaticResource,Translation`): that will be applied to all monitoring branches.\n\n## Full mode\n\nActivate it with **--full** parameter, or variable MONITORING_BACKUP_MODE_FULL=true\n\nIgnores filters (namespaces items & manifest/package-skip-items.xml) to retrieve ALL metadatas, including those you might not care about (reports, translations...)\n\nAs we can retrieve only 10000 files by call, the list of all metadatas will be chunked to make multiple calls (and take more time than filtered mode)\n\n- if you use `--full-apply-filters` , manifest/package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES filters will be applied anyway\n- if you use `--exclude-namespaces` , namespaced items will be ignored\n\n_With those both options, it's like if you are not using --full, but with chunked metadata download_\n\n## In CI/CD\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-metadata-backup/) and can output Grafana, Slack and MsTeams Notifications.\n\n## Troubleshooting\n\nIf you have unknown errors (it happens !), you can investigate using the full command with smaller chunks.\n\nExample: `sf hardis:org:monitor:backup --full --exclude-namespaces --full-apply-filters --max-by-chunk 500`\n\nIt will allow you the identify the responsible metadata and ignore it using package-skip-items.xml or MONITORING_BACKUP_SKIP_METADATA_TYPES env variable.\n\n## Documentation\n\n[Doc generation (including visual flows)](https://sfdx-hardis.cloudity.com/hardis/doc/project2markdown/) is triggered at the end of the command.\n\nIf you want to also upload HTML Documentation on your Salesforce Org as static resource, use variable **SFDX_HARDIS_DOC_DEPLOY_TO_ORG=\"true\"**\n\nIf you want to also upload HTML Documentation on Cloudflare, use variable **SFDX_HARDIS_DOC_DEPLOY_TO_CLOUDFLARE=\"true\"**\n\n- If you want to generate the documentation in multiple languages, define variable SFDX_DOC_LANGUAGES (ex: SFDX_DOC_LANGUAGES=en,fr,de)\n- You can define one Cloudflare site by language, for example with the following variables:\n - CLOUDFLARE_PROJECT_NAME_EN=cloudity-demo-english\n - CLOUDFLARE_PROJECT_NAME_FR=cloudity-demo-french\n - CLOUDFLARE_PROJECT_NAME_DE=cloudity-demo-german\n\nIf Flow history doc always display a single state, you probably need to update your workflow configuration:\n\n- on Gitlab: Env variable [`GIT_FETCH_EXTRA_FLAGS: --depth 10000`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/.gitlab-ci.yml#L11)\n- on GitHub: [`fetch-depth: 0`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/.github/workflows/org-monitoring.yml#L58)\n- on Azure: [`fetchDepth: \"0\"`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/azure-pipelines.yml#L39)\n- on Bitbucket: [`step: clone: depth: full`](https://github.com/hardisgroupcom/sfdx-hardis/blob/main/defaults/monitoring/bitbucket-pipelines.yml#L18)\n",
7787
+ "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",
7780
7788
  "examples": [
7781
- "$ sf hardis:org:monitor:backup",
7782
- "$ sf hardis:org:monitor:backup --full",
7783
- "$ sf hardis:org:monitor:backup --full --exclude-namespaces",
7784
- "$ sf hardis:org:monitor:backup --full --exclude-namespaces --full-apply-filters"
7789
+ "$ sf hardis:org:purge:apexlog",
7790
+ "$ sf hardis:org:purge:apexlog --target-org nicolas.vuillamy@gmail.com"
7785
7791
  ],
7786
7792
  "flags": {
7787
7793
  "json": {
@@ -7799,57 +7805,13 @@
7799
7805
  "multiple": false,
7800
7806
  "type": "option"
7801
7807
  },
7802
- "full": {
7803
- "description": "Dot not take in account filtering using package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES. Efficient but much much slower !",
7804
- "name": "full",
7805
- "allowNo": false,
7806
- "type": "boolean"
7807
- },
7808
- "max-by-chunk": {
7809
- "char": "m",
7810
- "description": "If mode --full is activated, maximum number of metadatas in a package.xml chunk",
7811
- "name": "max-by-chunk",
7812
- "default": 3000,
7813
- "hasDynamicHelp": false,
7814
- "multiple": false,
7815
- "type": "option"
7816
- },
7817
- "exclude-namespaces": {
7818
- "char": "e",
7819
- "description": "If mode --full is activated, exclude namespaced metadatas",
7820
- "name": "exclude-namespaces",
7821
- "allowNo": false,
7822
- "type": "boolean"
7823
- },
7824
- "full-apply-filters": {
7808
+ "prompt": {
7825
7809
  "char": "z",
7826
- "description": "If mode --full is activated, apply filters of manifest/package-skip-items.xml and MONITORING_BACKUP_SKIP_METADATA_TYPES anyway",
7827
- "name": "full-apply-filters",
7828
- "allowNo": false,
7829
- "type": "boolean"
7830
- },
7831
- "start-chunk": {
7832
- "description": "Use this parameter to troubleshoot a specific chunk. It will be used as the first chunk to retrieve",
7833
- "name": "start-chunk",
7834
- "default": 1,
7835
- "hasDynamicHelp": false,
7836
- "multiple": false,
7837
- "type": "option"
7838
- },
7839
- "skip-doc": {
7840
- "description": "Skip the generation of project documentation at the end of the command",
7841
- "name": "skip-doc",
7842
- "allowNo": false,
7810
+ "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7811
+ "name": "prompt",
7812
+ "allowNo": true,
7843
7813
  "type": "boolean"
7844
7814
  },
7845
- "outputfile": {
7846
- "char": "f",
7847
- "description": "Force the path and name of output report file. Must end with .csv",
7848
- "name": "outputfile",
7849
- "hasDynamicHelp": false,
7850
- "multiple": false,
7851
- "type": "option"
7852
- },
7853
7815
  "debug": {
7854
7816
  "char": "d",
7855
7817
  "description": "Activate debug mode (more logs)",
@@ -7888,58 +7850,59 @@
7888
7850
  },
7889
7851
  "hasDynamicHelp": true,
7890
7852
  "hiddenAliases": [],
7891
- "id": "hardis:org:monitor:backup",
7853
+ "id": "hardis:org:purge:apexlog",
7892
7854
  "pluginAlias": "sfdx-hardis",
7893
7855
  "pluginName": "sfdx-hardis",
7894
7856
  "pluginType": "core",
7895
7857
  "strict": true,
7896
7858
  "enableJsonFlag": true,
7897
- "title": "Backup DX sources",
7898
- "requiresProject": true,
7899
- "triggerNotification": true,
7859
+ "title": "Purge Apex Logs",
7860
+ "requiresProject": false,
7900
7861
  "isESM": true,
7901
7862
  "relativePath": [
7902
7863
  "lib",
7903
7864
  "commands",
7904
7865
  "hardis",
7905
7866
  "org",
7906
- "monitor",
7907
- "backup.js"
7867
+ "purge",
7868
+ "apexlog.js"
7908
7869
  ],
7909
7870
  "aliasPermutations": [],
7910
7871
  "permutations": [
7911
- "hardis:org:monitor:backup",
7912
- "org:hardis:monitor:backup",
7913
- "org:monitor:hardis:backup",
7914
- "org:monitor:backup:hardis",
7915
- "hardis:monitor:org:backup",
7916
- "monitor:hardis:org:backup",
7917
- "monitor:org:hardis:backup",
7918
- "monitor:org:backup:hardis",
7919
- "hardis:monitor:backup:org",
7920
- "monitor:hardis:backup:org",
7921
- "monitor:backup:hardis:org",
7922
- "monitor:backup:org:hardis",
7923
- "hardis:org:backup:monitor",
7924
- "org:hardis:backup:monitor",
7925
- "org:backup:hardis:monitor",
7926
- "org:backup:monitor:hardis",
7927
- "hardis:backup:org:monitor",
7928
- "backup:hardis:org:monitor",
7929
- "backup:org:hardis:monitor",
7930
- "backup:org:monitor:hardis",
7931
- "hardis:backup:monitor:org",
7932
- "backup:hardis:monitor:org",
7933
- "backup:monitor:hardis:org",
7934
- "backup:monitor:org:hardis"
7872
+ "hardis:org:purge:apexlog",
7873
+ "org:hardis:purge:apexlog",
7874
+ "org:purge:hardis:apexlog",
7875
+ "org:purge:apexlog:hardis",
7876
+ "hardis:purge:org:apexlog",
7877
+ "purge:hardis:org:apexlog",
7878
+ "purge:org:hardis:apexlog",
7879
+ "purge:org:apexlog:hardis",
7880
+ "hardis:purge:apexlog:org",
7881
+ "purge:hardis:apexlog:org",
7882
+ "purge:apexlog:hardis:org",
7883
+ "purge:apexlog:org:hardis",
7884
+ "hardis:org:apexlog:purge",
7885
+ "org:hardis:apexlog:purge",
7886
+ "org:apexlog:hardis:purge",
7887
+ "org:apexlog:purge:hardis",
7888
+ "hardis:apexlog:org:purge",
7889
+ "apexlog:hardis:org:purge",
7890
+ "apexlog:org:hardis:purge",
7891
+ "apexlog:org:purge:hardis",
7892
+ "hardis:apexlog:purge:org",
7893
+ "apexlog:hardis:purge:org",
7894
+ "apexlog:purge:hardis:org",
7895
+ "apexlog:purge:org:hardis"
7935
7896
  ]
7936
7897
  },
7937
- "hardis:org:monitor:limits": {
7898
+ "hardis:org:purge:flow": {
7938
7899
  "aliases": [],
7939
7900
  "args": {},
7940
- "description": "\n## Command Behavior\n\n**Checks the current usage of various Salesforce org limits and sends notifications if thresholds are exceeded.**\n\nThis command is a critical component of proactive Salesforce org management, helping administrators and developers monitor resource consumption and prevent hitting critical limits that could impact performance or functionality. It provides early warnings when limits are approaching their capacity.\n\nKey functionalities:\n\n- **Limit Retrieval:** Fetches a comprehensive list of all Salesforce org limits using the Salesforce CLI.\n- **Usage Calculation:** Calculates the percentage of each limit that is currently being used.\n- **Threshold-Based Alerting:** Assigns a severity (success, warning, or error) to each limit based on configurable thresholds:\n - **Warning:** If usage exceeds 50% (configurable via `LIMIT_THRESHOLD_WARNING` environment variable).\n - **Error:** If usage exceeds 75% (configurable via `LIMIT_THRESHOLD_ERROR` environment variable).\n- **CSV Report Generation:** Generates a CSV file containing all org limits, their current usage, maximum allowed, and calculated percentage used, along with the assigned severity.\n- **Notifications:** Sends notifications to configured channels (Grafana, Slack, MS Teams) with a summary of limits that have exceeded the warning or error thresholds.\n\nThis command is part of [sfdx-hardis Monitoring](https://sfdx-hardis.cloudity.com/salesforce-monitoring-org-limits/) 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 CLI Integration:** It executes the `sf org limits list` command to retrieve the current org limits. It parses the JSON output of this command.\n- **Data Processing:** It iterates through the retrieved limits, calculates the `used` and `percentUsed` values, and assigns a `severity` (success, warning, error) based on the configured thresholds.\n- **Environment Variable Configuration:** Reads `LIMIT_THRESHOLD_WARNING` and `LIMIT_THRESHOLD_ERROR` environment variables to set the warning and error thresholds for limit usage.\n- **Report Generation:** It uses `generateCsvFile` to create the CSV report of org limits.\n- **Notification Integration:** It integrates with the `NotifProvider` to send notifications, including attachments of the generated CSV report and detailed metrics for each limit, which can be consumed by monitoring dashboards like Grafana.\n- **Exit Code Management:** Sets the process exit code to 1 if any limit is in an 'error' state, indicating a critical issue.\n</details>\n",
7901
+ "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",
7941
7902
  "examples": [
7942
- "$ sf hardis:org:monitor:limits"
7903
+ "$ sf hardis:org:purge:flow",
7904
+ "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --no-prompt --delete-flow-interviews",
7905
+ "$ sf hardis:org:purge:flow --target-org nicolas.vuillamy@gmail.com --status \"Obsolete,Draft,InvalidDraft\" --name TestFlow"
7943
7906
  ],
7944
7907
  "flags": {
7945
7908
  "json": {
@@ -7957,10 +7920,48 @@
7957
7920
  "multiple": false,
7958
7921
  "type": "option"
7959
7922
  },
7960
- "outputfile": {
7923
+ "prompt": {
7924
+ "char": "z",
7925
+ "description": "Prompt for confirmation (true by default, use --no-prompt to skip)",
7926
+ "name": "prompt",
7927
+ "allowNo": true,
7928
+ "type": "boolean"
7929
+ },
7930
+ "name": {
7931
+ "char": "n",
7932
+ "description": "Filter according to Name criteria",
7933
+ "name": "name",
7934
+ "hasDynamicHelp": false,
7935
+ "multiple": false,
7936
+ "type": "option"
7937
+ },
7938
+ "status": {
7939
+ "char": "s",
7940
+ "description": "Filter according to Status criteria",
7941
+ "name": "status",
7942
+ "hasDynamicHelp": false,
7943
+ "multiple": false,
7944
+ "type": "option"
7945
+ },
7946
+ "delete-flow-interviews": {
7947
+ "char": "w",
7948
+ "description": "If the presence of Flow interviews prevent to delete flows versions, delete them before retrying to delete flow versions",
7949
+ "name": "delete-flow-interviews",
7950
+ "allowNo": false,
7951
+ "type": "boolean"
7952
+ },
7953
+ "allowpurgefailure": {
7961
7954
  "char": "f",
7962
- "description": "Force the path and name of output report file. Must end with .csv",
7963
- "name": "outputfile",
7955
+ "description": "Allows purges to fail without exiting with 1. Use --no-allowpurgefailure to disable",
7956
+ "name": "allowpurgefailure",
7957
+ "allowNo": true,
7958
+ "type": "boolean"
7959
+ },
7960
+ "instanceurl": {
7961
+ "char": "r",
7962
+ "description": "URL of org instance",
7963
+ "name": "instanceurl",
7964
+ "default": "https://login.salesforce.com",
7964
7965
  "hasDynamicHelp": false,
7965
7966
  "multiple": false,
7966
7967
  "type": "option"
@@ -8003,50 +8004,49 @@
8003
8004
  },
8004
8005
  "hasDynamicHelp": true,
8005
8006
  "hiddenAliases": [],
8006
- "id": "hardis:org:monitor:limits",
8007
+ "id": "hardis:org:purge:flow",
8007
8008
  "pluginAlias": "sfdx-hardis",
8008
8009
  "pluginName": "sfdx-hardis",
8009
8010
  "pluginType": "core",
8010
8011
  "strict": true,
8011
8012
  "enableJsonFlag": true,
8012
- "title": "Check org limits",
8013
- "requiresProject": true,
8014
- "triggerNotification": true,
8013
+ "title": "Purge Flow versions",
8014
+ "requiresProject": false,
8015
8015
  "isESM": true,
8016
8016
  "relativePath": [
8017
8017
  "lib",
8018
8018
  "commands",
8019
8019
  "hardis",
8020
8020
  "org",
8021
- "monitor",
8022
- "limits.js"
8021
+ "purge",
8022
+ "flow.js"
8023
8023
  ],
8024
8024
  "aliasPermutations": [],
8025
8025
  "permutations": [
8026
- "hardis:org:monitor:limits",
8027
- "org:hardis:monitor:limits",
8028
- "org:monitor:hardis:limits",
8029
- "org:monitor:limits:hardis",
8030
- "hardis:monitor:org:limits",
8031
- "monitor:hardis:org:limits",
8032
- "monitor:org:hardis:limits",
8033
- "monitor:org:limits:hardis",
8034
- "hardis:monitor:limits:org",
8035
- "monitor:hardis:limits:org",
8036
- "monitor:limits:hardis:org",
8037
- "monitor:limits:org:hardis",
8038
- "hardis:org:limits:monitor",
8039
- "org:hardis:limits:monitor",
8040
- "org:limits:hardis:monitor",
8041
- "org:limits:monitor:hardis",
8042
- "hardis:limits:org:monitor",
8043
- "limits:hardis:org:monitor",
8044
- "limits:org:hardis:monitor",
8045
- "limits:org:monitor:hardis",
8046
- "hardis:limits:monitor:org",
8047
- "limits:hardis:monitor:org",
8048
- "limits:monitor:hardis:org",
8049
- "limits:monitor:org:hardis"
8026
+ "hardis:org:purge:flow",
8027
+ "org:hardis:purge:flow",
8028
+ "org:purge:hardis:flow",
8029
+ "org:purge:flow:hardis",
8030
+ "hardis:purge:org:flow",
8031
+ "purge:hardis:org:flow",
8032
+ "purge:org:hardis:flow",
8033
+ "purge:org:flow:hardis",
8034
+ "hardis:purge:flow:org",
8035
+ "purge:hardis:flow:org",
8036
+ "purge:flow:hardis:org",
8037
+ "purge:flow:org:hardis",
8038
+ "hardis:org:flow:purge",
8039
+ "org:hardis:flow:purge",
8040
+ "org:flow:hardis:purge",
8041
+ "org:flow:purge:hardis",
8042
+ "hardis:flow:org:purge",
8043
+ "flow:hardis:org:purge",
8044
+ "flow:org:hardis:purge",
8045
+ "flow:org:purge:hardis",
8046
+ "hardis:flow:purge:org",
8047
+ "flow:hardis:purge:org",
8048
+ "flow:purge:hardis:org",
8049
+ "flow:purge:org:hardis"
8050
8050
  ]
8051
8051
  },
8052
8052
  "hardis:org:refresh:after-refresh": {
@@ -11272,8 +11272,254 @@
11272
11272
  "args": {},
11273
11273
  "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 ",
11274
11274
  "examples": [
11275
- "$ sf hardis:project:clean:xml",
11276
- "$ sf hardis:project:clean:xml --globpattern \"/**/*.flexipage-meta.xml\" --xpath \"//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]\""
11275
+ "$ sf hardis:project:clean:xml",
11276
+ "$ sf hardis:project:clean:xml --globpattern \"/**/*.flexipage-meta.xml\" --xpath \"//ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]\""
11277
+ ],
11278
+ "flags": {
11279
+ "json": {
11280
+ "description": "Format output as json.",
11281
+ "helpGroup": "GLOBAL",
11282
+ "name": "json",
11283
+ "allowNo": false,
11284
+ "type": "boolean"
11285
+ },
11286
+ "flags-dir": {
11287
+ "helpGroup": "GLOBAL",
11288
+ "name": "flags-dir",
11289
+ "summary": "Import flag values from a directory.",
11290
+ "hasDynamicHelp": false,
11291
+ "multiple": false,
11292
+ "type": "option"
11293
+ },
11294
+ "folder": {
11295
+ "char": "f",
11296
+ "description": "Root folder",
11297
+ "name": "folder",
11298
+ "default": "force-app",
11299
+ "hasDynamicHelp": false,
11300
+ "multiple": false,
11301
+ "type": "option"
11302
+ },
11303
+ "globpattern": {
11304
+ "char": "p",
11305
+ "dependsOn": [
11306
+ "xpath"
11307
+ ],
11308
+ "description": "Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml",
11309
+ "name": "globpattern",
11310
+ "hasDynamicHelp": false,
11311
+ "multiple": false,
11312
+ "type": "option"
11313
+ },
11314
+ "xpath": {
11315
+ "char": "x",
11316
+ "dependsOn": [
11317
+ "globpattern"
11318
+ ],
11319
+ "description": "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]",
11320
+ "name": "xpath",
11321
+ "hasDynamicHelp": false,
11322
+ "multiple": false,
11323
+ "type": "option"
11324
+ },
11325
+ "namespace": {
11326
+ "char": "n",
11327
+ "description": "XML Namespace to use",
11328
+ "name": "namespace",
11329
+ "default": "http://soap.sforce.com/2006/04/metadata",
11330
+ "hasDynamicHelp": false,
11331
+ "multiple": false,
11332
+ "type": "option"
11333
+ },
11334
+ "debug": {
11335
+ "char": "d",
11336
+ "description": "Activate debug mode (more logs)",
11337
+ "name": "debug",
11338
+ "allowNo": false,
11339
+ "type": "boolean"
11340
+ },
11341
+ "websocket": {
11342
+ "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
11343
+ "name": "websocket",
11344
+ "hasDynamicHelp": false,
11345
+ "multiple": false,
11346
+ "type": "option"
11347
+ },
11348
+ "skipauth": {
11349
+ "description": "Skip authentication check when a default username is required",
11350
+ "name": "skipauth",
11351
+ "allowNo": false,
11352
+ "type": "boolean"
11353
+ }
11354
+ },
11355
+ "hasDynamicHelp": false,
11356
+ "hiddenAliases": [],
11357
+ "id": "hardis:project:clean:xml",
11358
+ "pluginAlias": "sfdx-hardis",
11359
+ "pluginName": "sfdx-hardis",
11360
+ "pluginType": "core",
11361
+ "strict": true,
11362
+ "enableJsonFlag": true,
11363
+ "title": "Clean retrieved empty items in dx sources",
11364
+ "requiresProject": true,
11365
+ "isESM": true,
11366
+ "relativePath": [
11367
+ "lib",
11368
+ "commands",
11369
+ "hardis",
11370
+ "project",
11371
+ "clean",
11372
+ "xml.js"
11373
+ ],
11374
+ "aliasPermutations": [],
11375
+ "permutations": [
11376
+ "hardis:project:clean:xml",
11377
+ "project:hardis:clean:xml",
11378
+ "project:clean:hardis:xml",
11379
+ "project:clean:xml:hardis",
11380
+ "hardis:clean:project:xml",
11381
+ "clean:hardis:project:xml",
11382
+ "clean:project:hardis:xml",
11383
+ "clean:project:xml:hardis",
11384
+ "hardis:clean:xml:project",
11385
+ "clean:hardis:xml:project",
11386
+ "clean:xml:hardis:project",
11387
+ "clean:xml:project:hardis",
11388
+ "hardis:project:xml:clean",
11389
+ "project:hardis:xml:clean",
11390
+ "project:xml:hardis:clean",
11391
+ "project:xml:clean:hardis",
11392
+ "hardis:xml:project:clean",
11393
+ "xml:hardis:project:clean",
11394
+ "xml:project:hardis:clean",
11395
+ "xml:project:clean:hardis",
11396
+ "hardis:xml:clean:project",
11397
+ "xml:hardis:clean:project",
11398
+ "xml:clean:hardis:project",
11399
+ "xml:clean:project:hardis"
11400
+ ]
11401
+ },
11402
+ "hardis:project:fix:profiletabs": {
11403
+ "aliases": [],
11404
+ "args": {},
11405
+ "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",
11406
+ "examples": [
11407
+ "$ sf hardis:project:fix:profiletabs"
11408
+ ],
11409
+ "flags": {
11410
+ "json": {
11411
+ "description": "Format output as json.",
11412
+ "helpGroup": "GLOBAL",
11413
+ "name": "json",
11414
+ "allowNo": false,
11415
+ "type": "boolean"
11416
+ },
11417
+ "flags-dir": {
11418
+ "helpGroup": "GLOBAL",
11419
+ "name": "flags-dir",
11420
+ "summary": "Import flag values from a directory.",
11421
+ "hasDynamicHelp": false,
11422
+ "multiple": false,
11423
+ "type": "option"
11424
+ },
11425
+ "path": {
11426
+ "char": "p",
11427
+ "description": "Root folder",
11428
+ "name": "path",
11429
+ "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
11430
+ "hasDynamicHelp": false,
11431
+ "multiple": false,
11432
+ "type": "option"
11433
+ },
11434
+ "debug": {
11435
+ "char": "d",
11436
+ "description": "Activate debug mode (more logs)",
11437
+ "name": "debug",
11438
+ "allowNo": false,
11439
+ "type": "boolean"
11440
+ },
11441
+ "websocket": {
11442
+ "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
11443
+ "name": "websocket",
11444
+ "hasDynamicHelp": false,
11445
+ "multiple": false,
11446
+ "type": "option"
11447
+ },
11448
+ "skipauth": {
11449
+ "description": "Skip authentication check when a default username is required",
11450
+ "name": "skipauth",
11451
+ "allowNo": false,
11452
+ "type": "boolean"
11453
+ },
11454
+ "target-org": {
11455
+ "aliases": [
11456
+ "targetusername",
11457
+ "u"
11458
+ ],
11459
+ "char": "o",
11460
+ "deprecateAliases": true,
11461
+ "name": "target-org",
11462
+ "noCacheDefault": true,
11463
+ "required": true,
11464
+ "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
11465
+ "hasDynamicHelp": true,
11466
+ "multiple": false,
11467
+ "type": "option"
11468
+ }
11469
+ },
11470
+ "hasDynamicHelp": true,
11471
+ "hiddenAliases": [],
11472
+ "id": "hardis:project:fix:profiletabs",
11473
+ "pluginAlias": "sfdx-hardis",
11474
+ "pluginName": "sfdx-hardis",
11475
+ "pluginType": "core",
11476
+ "strict": true,
11477
+ "enableJsonFlag": true,
11478
+ "title": "Fix profiles to add tabs that are not retrieved by SF CLI",
11479
+ "requiresProject": true,
11480
+ "isESM": true,
11481
+ "relativePath": [
11482
+ "lib",
11483
+ "commands",
11484
+ "hardis",
11485
+ "project",
11486
+ "fix",
11487
+ "profiletabs.js"
11488
+ ],
11489
+ "aliasPermutations": [],
11490
+ "permutations": [
11491
+ "hardis:project:fix:profiletabs",
11492
+ "project:hardis:fix:profiletabs",
11493
+ "project:fix:hardis:profiletabs",
11494
+ "project:fix:profiletabs:hardis",
11495
+ "hardis:fix:project:profiletabs",
11496
+ "fix:hardis:project:profiletabs",
11497
+ "fix:project:hardis:profiletabs",
11498
+ "fix:project:profiletabs:hardis",
11499
+ "hardis:fix:profiletabs:project",
11500
+ "fix:hardis:profiletabs:project",
11501
+ "fix:profiletabs:hardis:project",
11502
+ "fix:profiletabs:project:hardis",
11503
+ "hardis:project:profiletabs:fix",
11504
+ "project:hardis:profiletabs:fix",
11505
+ "project:profiletabs:hardis:fix",
11506
+ "project:profiletabs:fix:hardis",
11507
+ "hardis:profiletabs:project:fix",
11508
+ "profiletabs:hardis:project:fix",
11509
+ "profiletabs:project:hardis:fix",
11510
+ "profiletabs:project:fix:hardis",
11511
+ "hardis:profiletabs:fix:project",
11512
+ "profiletabs:hardis:fix:project",
11513
+ "profiletabs:fix:hardis:project",
11514
+ "profiletabs:fix:project:hardis"
11515
+ ]
11516
+ },
11517
+ "hardis:project:fix:v53flexipages": {
11518
+ "aliases": [],
11519
+ "args": {},
11520
+ "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",
11521
+ "examples": [
11522
+ "$ sf hardis:project:fix:v53flexipages"
11277
11523
  ],
11278
11524
  "flags": {
11279
11525
  "json": {
@@ -11291,42 +11537,11 @@
11291
11537
  "multiple": false,
11292
11538
  "type": "option"
11293
11539
  },
11294
- "folder": {
11295
- "char": "f",
11296
- "description": "Root folder",
11297
- "name": "folder",
11298
- "default": "force-app",
11299
- "hasDynamicHelp": false,
11300
- "multiple": false,
11301
- "type": "option"
11302
- },
11303
- "globpattern": {
11540
+ "path": {
11304
11541
  "char": "p",
11305
- "dependsOn": [
11306
- "xpath"
11307
- ],
11308
- "description": "Glob pattern to find files to clean. Ex: /**/*.flexipage-meta.xml",
11309
- "name": "globpattern",
11310
- "hasDynamicHelp": false,
11311
- "multiple": false,
11312
- "type": "option"
11313
- },
11314
- "xpath": {
11315
- "char": "x",
11316
- "dependsOn": [
11317
- "globpattern"
11318
- ],
11319
- "description": "XPath to use to detect the elements to remove. Ex: //ns:flexiPageRegions//ns:name[contains(text(),'dashboardName')]",
11320
- "name": "xpath",
11321
- "hasDynamicHelp": false,
11322
- "multiple": false,
11323
- "type": "option"
11324
- },
11325
- "namespace": {
11326
- "char": "n",
11327
- "description": "XML Namespace to use",
11328
- "name": "namespace",
11329
- "default": "http://soap.sforce.com/2006/04/metadata",
11542
+ "description": "Root folder",
11543
+ "name": "path",
11544
+ "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
11330
11545
  "hasDynamicHelp": false,
11331
11546
  "multiple": false,
11332
11547
  "type": "option"
@@ -11354,13 +11569,13 @@
11354
11569
  },
11355
11570
  "hasDynamicHelp": false,
11356
11571
  "hiddenAliases": [],
11357
- "id": "hardis:project:clean:xml",
11572
+ "id": "hardis:project:fix:v53flexipages",
11358
11573
  "pluginAlias": "sfdx-hardis",
11359
11574
  "pluginName": "sfdx-hardis",
11360
11575
  "pluginType": "core",
11361
11576
  "strict": true,
11362
11577
  "enableJsonFlag": true,
11363
- "title": "Clean retrieved empty items in dx sources",
11578
+ "title": "Fix flexipages for v53",
11364
11579
  "requiresProject": true,
11365
11580
  "isESM": true,
11366
11581
  "relativePath": [
@@ -11368,35 +11583,35 @@
11368
11583
  "commands",
11369
11584
  "hardis",
11370
11585
  "project",
11371
- "clean",
11372
- "xml.js"
11586
+ "fix",
11587
+ "v53flexipages.js"
11373
11588
  ],
11374
11589
  "aliasPermutations": [],
11375
11590
  "permutations": [
11376
- "hardis:project:clean:xml",
11377
- "project:hardis:clean:xml",
11378
- "project:clean:hardis:xml",
11379
- "project:clean:xml:hardis",
11380
- "hardis:clean:project:xml",
11381
- "clean:hardis:project:xml",
11382
- "clean:project:hardis:xml",
11383
- "clean:project:xml:hardis",
11384
- "hardis:clean:xml:project",
11385
- "clean:hardis:xml:project",
11386
- "clean:xml:hardis:project",
11387
- "clean:xml:project:hardis",
11388
- "hardis:project:xml:clean",
11389
- "project:hardis:xml:clean",
11390
- "project:xml:hardis:clean",
11391
- "project:xml:clean:hardis",
11392
- "hardis:xml:project:clean",
11393
- "xml:hardis:project:clean",
11394
- "xml:project:hardis:clean",
11395
- "xml:project:clean:hardis",
11396
- "hardis:xml:clean:project",
11397
- "xml:hardis:clean:project",
11398
- "xml:clean:hardis:project",
11399
- "xml:clean:project:hardis"
11591
+ "hardis:project:fix:v53flexipages",
11592
+ "project:hardis:fix:v53flexipages",
11593
+ "project:fix:hardis:v53flexipages",
11594
+ "project:fix:v53flexipages:hardis",
11595
+ "hardis:fix:project:v53flexipages",
11596
+ "fix:hardis:project:v53flexipages",
11597
+ "fix:project:hardis:v53flexipages",
11598
+ "fix:project:v53flexipages:hardis",
11599
+ "hardis:fix:v53flexipages:project",
11600
+ "fix:hardis:v53flexipages:project",
11601
+ "fix:v53flexipages:hardis:project",
11602
+ "fix:v53flexipages:project:hardis",
11603
+ "hardis:project:v53flexipages:fix",
11604
+ "project:hardis:v53flexipages:fix",
11605
+ "project:v53flexipages:hardis:fix",
11606
+ "project:v53flexipages:fix:hardis",
11607
+ "hardis:v53flexipages:project:fix",
11608
+ "v53flexipages:hardis:project:fix",
11609
+ "v53flexipages:project:hardis:fix",
11610
+ "v53flexipages:project:fix:hardis",
11611
+ "hardis:v53flexipages:fix:project",
11612
+ "v53flexipages:hardis:fix:project",
11613
+ "v53flexipages:fix:hardis:project",
11614
+ "v53flexipages:fix:project:hardis"
11400
11615
  ]
11401
11616
  },
11402
11617
  "hardis:project:deploy:notify": {
@@ -12530,171 +12745,58 @@
12530
12745
  "strict": true,
12531
12746
  "enableJsonFlag": true,
12532
12747
  "requiresProject": true,
12533
- "isESM": true,
12534
- "relativePath": [
12535
- "lib",
12536
- "commands",
12537
- "hardis",
12538
- "project",
12539
- "deploy",
12540
- "validate.js"
12541
- ],
12542
- "aliasPermutations": [
12543
- "hardis:deploy:validate",
12544
- "deploy:hardis:validate",
12545
- "deploy:validate:hardis",
12546
- "hardis:validate:deploy",
12547
- "validate:hardis:deploy",
12548
- "validate:deploy:hardis"
12549
- ],
12550
- "permutations": [
12551
- "hardis:project:deploy:validate",
12552
- "project:hardis:deploy:validate",
12553
- "project:deploy:hardis:validate",
12554
- "project:deploy:validate:hardis",
12555
- "hardis:deploy:project:validate",
12556
- "deploy:hardis:project:validate",
12557
- "deploy:project:hardis:validate",
12558
- "deploy:project:validate:hardis",
12559
- "hardis:deploy:validate:project",
12560
- "deploy:hardis:validate:project",
12561
- "deploy:validate:hardis:project",
12562
- "deploy:validate:project:hardis",
12563
- "hardis:project:validate:deploy",
12564
- "project:hardis:validate:deploy",
12565
- "project:validate:hardis:deploy",
12566
- "project:validate:deploy:hardis",
12567
- "hardis:validate:project:deploy",
12568
- "validate:hardis:project:deploy",
12569
- "validate:project:hardis:deploy",
12570
- "validate:project:deploy:hardis",
12571
- "hardis:validate:deploy:project",
12572
- "validate:hardis:deploy:project",
12573
- "validate:deploy:hardis:project",
12574
- "validate:deploy:project:hardis"
12575
- ]
12576
- },
12577
- "hardis:project:fix:profiletabs": {
12578
- "aliases": [],
12579
- "args": {},
12580
- "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",
12581
- "examples": [
12582
- "$ sf hardis:project:fix:profiletabs"
12583
- ],
12584
- "flags": {
12585
- "json": {
12586
- "description": "Format output as json.",
12587
- "helpGroup": "GLOBAL",
12588
- "name": "json",
12589
- "allowNo": false,
12590
- "type": "boolean"
12591
- },
12592
- "flags-dir": {
12593
- "helpGroup": "GLOBAL",
12594
- "name": "flags-dir",
12595
- "summary": "Import flag values from a directory.",
12596
- "hasDynamicHelp": false,
12597
- "multiple": false,
12598
- "type": "option"
12599
- },
12600
- "path": {
12601
- "char": "p",
12602
- "description": "Root folder",
12603
- "name": "path",
12604
- "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
12605
- "hasDynamicHelp": false,
12606
- "multiple": false,
12607
- "type": "option"
12608
- },
12609
- "debug": {
12610
- "char": "d",
12611
- "description": "Activate debug mode (more logs)",
12612
- "name": "debug",
12613
- "allowNo": false,
12614
- "type": "boolean"
12615
- },
12616
- "websocket": {
12617
- "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
12618
- "name": "websocket",
12619
- "hasDynamicHelp": false,
12620
- "multiple": false,
12621
- "type": "option"
12622
- },
12623
- "skipauth": {
12624
- "description": "Skip authentication check when a default username is required",
12625
- "name": "skipauth",
12626
- "allowNo": false,
12627
- "type": "boolean"
12628
- },
12629
- "target-org": {
12630
- "aliases": [
12631
- "targetusername",
12632
- "u"
12633
- ],
12634
- "char": "o",
12635
- "deprecateAliases": true,
12636
- "name": "target-org",
12637
- "noCacheDefault": true,
12638
- "required": true,
12639
- "summary": "Username or alias of the target org. Not required if the `target-org` configuration variable is already set.",
12640
- "hasDynamicHelp": true,
12641
- "multiple": false,
12642
- "type": "option"
12643
- }
12644
- },
12645
- "hasDynamicHelp": true,
12646
- "hiddenAliases": [],
12647
- "id": "hardis:project:fix:profiletabs",
12648
- "pluginAlias": "sfdx-hardis",
12649
- "pluginName": "sfdx-hardis",
12650
- "pluginType": "core",
12651
- "strict": true,
12652
- "enableJsonFlag": true,
12653
- "title": "Fix profiles to add tabs that are not retrieved by SF CLI",
12654
- "requiresProject": true,
12655
- "isESM": true,
12656
- "relativePath": [
12657
- "lib",
12658
- "commands",
12659
- "hardis",
12660
- "project",
12661
- "fix",
12662
- "profiletabs.js"
12663
- ],
12664
- "aliasPermutations": [],
12665
- "permutations": [
12666
- "hardis:project:fix:profiletabs",
12667
- "project:hardis:fix:profiletabs",
12668
- "project:fix:hardis:profiletabs",
12669
- "project:fix:profiletabs:hardis",
12670
- "hardis:fix:project:profiletabs",
12671
- "fix:hardis:project:profiletabs",
12672
- "fix:project:hardis:profiletabs",
12673
- "fix:project:profiletabs:hardis",
12674
- "hardis:fix:profiletabs:project",
12675
- "fix:hardis:profiletabs:project",
12676
- "fix:profiletabs:hardis:project",
12677
- "fix:profiletabs:project:hardis",
12678
- "hardis:project:profiletabs:fix",
12679
- "project:hardis:profiletabs:fix",
12680
- "project:profiletabs:hardis:fix",
12681
- "project:profiletabs:fix:hardis",
12682
- "hardis:profiletabs:project:fix",
12683
- "profiletabs:hardis:project:fix",
12684
- "profiletabs:project:hardis:fix",
12685
- "profiletabs:project:fix:hardis",
12686
- "hardis:profiletabs:fix:project",
12687
- "profiletabs:hardis:fix:project",
12688
- "profiletabs:fix:hardis:project",
12689
- "profiletabs:fix:project:hardis"
12748
+ "isESM": true,
12749
+ "relativePath": [
12750
+ "lib",
12751
+ "commands",
12752
+ "hardis",
12753
+ "project",
12754
+ "deploy",
12755
+ "validate.js"
12756
+ ],
12757
+ "aliasPermutations": [
12758
+ "hardis:deploy:validate",
12759
+ "deploy:hardis:validate",
12760
+ "deploy:validate:hardis",
12761
+ "hardis:validate:deploy",
12762
+ "validate:hardis:deploy",
12763
+ "validate:deploy:hardis"
12764
+ ],
12765
+ "permutations": [
12766
+ "hardis:project:deploy:validate",
12767
+ "project:hardis:deploy:validate",
12768
+ "project:deploy:hardis:validate",
12769
+ "project:deploy:validate:hardis",
12770
+ "hardis:deploy:project:validate",
12771
+ "deploy:hardis:project:validate",
12772
+ "deploy:project:hardis:validate",
12773
+ "deploy:project:validate:hardis",
12774
+ "hardis:deploy:validate:project",
12775
+ "deploy:hardis:validate:project",
12776
+ "deploy:validate:hardis:project",
12777
+ "deploy:validate:project:hardis",
12778
+ "hardis:project:validate:deploy",
12779
+ "project:hardis:validate:deploy",
12780
+ "project:validate:hardis:deploy",
12781
+ "project:validate:deploy:hardis",
12782
+ "hardis:validate:project:deploy",
12783
+ "validate:hardis:project:deploy",
12784
+ "validate:project:hardis:deploy",
12785
+ "validate:project:deploy:hardis",
12786
+ "hardis:validate:deploy:project",
12787
+ "validate:hardis:deploy:project",
12788
+ "validate:deploy:hardis:project",
12789
+ "validate:deploy:project:hardis"
12690
12790
  ]
12691
12791
  },
12692
- "hardis:project:fix:v53flexipages": {
12792
+ "hardis:project:metadata:findduplicates": {
12693
12793
  "aliases": [],
12694
12794
  "args": {},
12695
- "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",
12795
+ "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",
12696
12796
  "examples": [
12697
- "$ sf hardis:project:fix:v53flexipages"
12797
+ "\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",
12798
+ "\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",
12799
+ "\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"
12698
12800
  ],
12699
12801
  "flags": {
12700
12802
  "json": {
@@ -12712,22 +12814,14 @@
12712
12814
  "multiple": false,
12713
12815
  "type": "option"
12714
12816
  },
12715
- "path": {
12716
- "char": "p",
12717
- "description": "Root folder",
12718
- "name": "path",
12719
- "default": "/home/runner/work/sfdx-hardis/sfdx-hardis",
12817
+ "files": {
12818
+ "char": "f",
12819
+ "description": "XML metadata files path",
12820
+ "name": "files",
12720
12821
  "hasDynamicHelp": false,
12721
- "multiple": false,
12822
+ "multiple": true,
12722
12823
  "type": "option"
12723
12824
  },
12724
- "debug": {
12725
- "char": "d",
12726
- "description": "Activate debug mode (more logs)",
12727
- "name": "debug",
12728
- "allowNo": false,
12729
- "type": "boolean"
12730
- },
12731
12825
  "websocket": {
12732
12826
  "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
12733
12827
  "name": "websocket",
@@ -12744,13 +12838,32 @@
12744
12838
  },
12745
12839
  "hasDynamicHelp": false,
12746
12840
  "hiddenAliases": [],
12747
- "id": "hardis:project:fix:v53flexipages",
12841
+ "id": "hardis:project:metadata:findduplicates",
12748
12842
  "pluginAlias": "sfdx-hardis",
12749
12843
  "pluginName": "sfdx-hardis",
12750
12844
  "pluginType": "core",
12751
12845
  "strict": true,
12752
12846
  "enableJsonFlag": true,
12753
- "title": "Fix flexipages for v53",
12847
+ "metadataDuplicateFindKeys": {
12848
+ "layout": [
12849
+ "Layout.layoutSections.layoutColumns.layoutItems.field",
12850
+ "Layout.quickActionListItems.quickActionName"
12851
+ ],
12852
+ "profile": [
12853
+ "Profile.fieldPermissions.field",
12854
+ "Profile.objectPermissions.object",
12855
+ "Profile.classAccesses.apexClass"
12856
+ ],
12857
+ "labels": [
12858
+ "CustomLabels.labels.fullName"
12859
+ ],
12860
+ "permissionset": [
12861
+ "PermissionSet.fieldPermissions.field",
12862
+ "PermissionSet.objectPermissions.object",
12863
+ "PermissionSet.classAccesses.apexClass"
12864
+ ]
12865
+ },
12866
+ "title": "XML duplicate values finder",
12754
12867
  "requiresProject": true,
12755
12868
  "isESM": true,
12756
12869
  "relativePath": [
@@ -12758,35 +12871,35 @@
12758
12871
  "commands",
12759
12872
  "hardis",
12760
12873
  "project",
12761
- "fix",
12762
- "v53flexipages.js"
12874
+ "metadata",
12875
+ "findduplicates.js"
12763
12876
  ],
12764
12877
  "aliasPermutations": [],
12765
12878
  "permutations": [
12766
- "hardis:project:fix:v53flexipages",
12767
- "project:hardis:fix:v53flexipages",
12768
- "project:fix:hardis:v53flexipages",
12769
- "project:fix:v53flexipages:hardis",
12770
- "hardis:fix:project:v53flexipages",
12771
- "fix:hardis:project:v53flexipages",
12772
- "fix:project:hardis:v53flexipages",
12773
- "fix:project:v53flexipages:hardis",
12774
- "hardis:fix:v53flexipages:project",
12775
- "fix:hardis:v53flexipages:project",
12776
- "fix:v53flexipages:hardis:project",
12777
- "fix:v53flexipages:project:hardis",
12778
- "hardis:project:v53flexipages:fix",
12779
- "project:hardis:v53flexipages:fix",
12780
- "project:v53flexipages:hardis:fix",
12781
- "project:v53flexipages:fix:hardis",
12782
- "hardis:v53flexipages:project:fix",
12783
- "v53flexipages:hardis:project:fix",
12784
- "v53flexipages:project:hardis:fix",
12785
- "v53flexipages:project:fix:hardis",
12786
- "hardis:v53flexipages:fix:project",
12787
- "v53flexipages:hardis:fix:project",
12788
- "v53flexipages:fix:hardis:project",
12789
- "v53flexipages:fix:project:hardis"
12879
+ "hardis:project:metadata:findduplicates",
12880
+ "project:hardis:metadata:findduplicates",
12881
+ "project:metadata:hardis:findduplicates",
12882
+ "project:metadata:findduplicates:hardis",
12883
+ "hardis:metadata:project:findduplicates",
12884
+ "metadata:hardis:project:findduplicates",
12885
+ "metadata:project:hardis:findduplicates",
12886
+ "metadata:project:findduplicates:hardis",
12887
+ "hardis:metadata:findduplicates:project",
12888
+ "metadata:hardis:findduplicates:project",
12889
+ "metadata:findduplicates:hardis:project",
12890
+ "metadata:findduplicates:project:hardis",
12891
+ "hardis:project:findduplicates:metadata",
12892
+ "project:hardis:findduplicates:metadata",
12893
+ "project:findduplicates:hardis:metadata",
12894
+ "project:findduplicates:metadata:hardis",
12895
+ "hardis:findduplicates:project:metadata",
12896
+ "findduplicates:hardis:project:metadata",
12897
+ "findduplicates:project:hardis:metadata",
12898
+ "findduplicates:project:metadata:hardis",
12899
+ "hardis:findduplicates:metadata:project",
12900
+ "findduplicates:hardis:metadata:project",
12901
+ "findduplicates:metadata:hardis:project",
12902
+ "findduplicates:metadata:project:hardis"
12790
12903
  ]
12791
12904
  },
12792
12905
  "hardis:project:generate:bypass": {
@@ -13184,119 +13297,6 @@
13184
13297
  "gitdelta:generate:project:hardis"
13185
13298
  ]
13186
13299
  },
13187
- "hardis:project:metadata:findduplicates": {
13188
- "aliases": [],
13189
- "args": {},
13190
- "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",
13191
- "examples": [
13192
- "\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",
13193
- "\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",
13194
- "\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"
13195
- ],
13196
- "flags": {
13197
- "json": {
13198
- "description": "Format output as json.",
13199
- "helpGroup": "GLOBAL",
13200
- "name": "json",
13201
- "allowNo": false,
13202
- "type": "boolean"
13203
- },
13204
- "flags-dir": {
13205
- "helpGroup": "GLOBAL",
13206
- "name": "flags-dir",
13207
- "summary": "Import flag values from a directory.",
13208
- "hasDynamicHelp": false,
13209
- "multiple": false,
13210
- "type": "option"
13211
- },
13212
- "files": {
13213
- "char": "f",
13214
- "description": "XML metadata files path",
13215
- "name": "files",
13216
- "hasDynamicHelp": false,
13217
- "multiple": true,
13218
- "type": "option"
13219
- },
13220
- "websocket": {
13221
- "description": "Websocket host:port for VsCode SFDX Hardis UI integration",
13222
- "name": "websocket",
13223
- "hasDynamicHelp": false,
13224
- "multiple": false,
13225
- "type": "option"
13226
- },
13227
- "skipauth": {
13228
- "description": "Skip authentication check when a default username is required",
13229
- "name": "skipauth",
13230
- "allowNo": false,
13231
- "type": "boolean"
13232
- }
13233
- },
13234
- "hasDynamicHelp": false,
13235
- "hiddenAliases": [],
13236
- "id": "hardis:project:metadata:findduplicates",
13237
- "pluginAlias": "sfdx-hardis",
13238
- "pluginName": "sfdx-hardis",
13239
- "pluginType": "core",
13240
- "strict": true,
13241
- "enableJsonFlag": true,
13242
- "metadataDuplicateFindKeys": {
13243
- "layout": [
13244
- "Layout.layoutSections.layoutColumns.layoutItems.field",
13245
- "Layout.quickActionListItems.quickActionName"
13246
- ],
13247
- "profile": [
13248
- "Profile.fieldPermissions.field",
13249
- "Profile.objectPermissions.object",
13250
- "Profile.classAccesses.apexClass"
13251
- ],
13252
- "labels": [
13253
- "CustomLabels.labels.fullName"
13254
- ],
13255
- "permissionset": [
13256
- "PermissionSet.fieldPermissions.field",
13257
- "PermissionSet.objectPermissions.object",
13258
- "PermissionSet.classAccesses.apexClass"
13259
- ]
13260
- },
13261
- "title": "XML duplicate values finder",
13262
- "requiresProject": true,
13263
- "isESM": true,
13264
- "relativePath": [
13265
- "lib",
13266
- "commands",
13267
- "hardis",
13268
- "project",
13269
- "metadata",
13270
- "findduplicates.js"
13271
- ],
13272
- "aliasPermutations": [],
13273
- "permutations": [
13274
- "hardis:project:metadata:findduplicates",
13275
- "project:hardis:metadata:findduplicates",
13276
- "project:metadata:hardis:findduplicates",
13277
- "project:metadata:findduplicates:hardis",
13278
- "hardis:metadata:project:findduplicates",
13279
- "metadata:hardis:project:findduplicates",
13280
- "metadata:project:hardis:findduplicates",
13281
- "metadata:project:findduplicates:hardis",
13282
- "hardis:metadata:findduplicates:project",
13283
- "metadata:hardis:findduplicates:project",
13284
- "metadata:findduplicates:hardis:project",
13285
- "metadata:findduplicates:project:hardis",
13286
- "hardis:project:findduplicates:metadata",
13287
- "project:hardis:findduplicates:metadata",
13288
- "project:findduplicates:hardis:metadata",
13289
- "project:findduplicates:metadata:hardis",
13290
- "hardis:findduplicates:project:metadata",
13291
- "findduplicates:hardis:project:metadata",
13292
- "findduplicates:project:hardis:metadata",
13293
- "findduplicates:project:metadata:hardis",
13294
- "hardis:findduplicates:metadata:project",
13295
- "findduplicates:hardis:metadata:project",
13296
- "findduplicates:metadata:hardis:project",
13297
- "findduplicates:metadata:project:hardis"
13298
- ]
13299
- },
13300
13300
  "hardis:scratch:pool:create": {
13301
13301
  "aliases": [],
13302
13302
  "args": {},
@@ -15271,5 +15271,5 @@
15271
15271
  ]
15272
15272
  }
15273
15273
  },
15274
- "version": "6.4.2"
15274
+ "version": "6.4.3"
15275
15275
  }