-
-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: added new plugin 'Log Retention Rules' to close #226
This plugin complements the log retention functionality in LoggerSettings__c & the LoggerScenarioRule__mdt objects The plugin's code is based on my other repo, ApexValidationRules - I've repurposed the core code to be used for configuring log retention rules & conditions
- Loading branch information
Showing
30 changed files
with
1,281 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Log Retention Rules plugin for Nebula Logger | ||
|
||
> :information_source: Requires Nebula Logger v4.6.12 or newer | ||
Adds the ability to create & deploy advanced, configurable rules for setting the retention date of `Log__c` records, using custom metadata types `LogRetentionRule__mdt` and `LogRetentionRuleCondition__mdt`. | ||
|
||
[![Install Unlocked Package Plugin](./content/btn-install-unlocked-package-plugin.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=zzzzzzzz) | ||
[![Install Unlocked Package Plugin](./content/btn-install-unlocked-package-plugin-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=zzzzzzzz) | ||
|
||
--- | ||
|
||
## What's Included | ||
|
||
This plugin includes some add-on metadata for Logger to support the Slack integration | ||
|
||
1. Plugin configuration details stored in Logger's CMDT objects `LoggerPlugin__mdt.LogRetentionRules` | ||
2. New custom metadata types | ||
- `LogRetentionRule__mdt` - Used to configure rules that set the value of `Log__c`.`LogRetentionDate__c`. Each rules consists of 1 or more conditions, stored in `LogRetentionRuleCondition__mdt`. | ||
- `LogRetentionRuleCondition__mdt` - Used to configure field-level conditions for retention rules - each condition checks a `LogEntry__c` or `Log__c` field for a specific value, regular expression (regex), or field comparisons. | ||
3. Apex class `LogRetentionRulesPlugin` (and corresponding tests in `LogRetentionRulesPlugin_Tests`). The plugin class reads the configured rules & conditions and automatically sets the field `Log__c.LogRetentionDate__c` when new `Log__c` records are inserted. | ||
|
||
--- | ||
|
||
## Installation Steps | ||
|
||
After installing the unlocked package Nebula Logger v4.6.12 (or newer), you can then install the Log Retention Rules plugin unlocked package in a sandbox. | ||
|
||
This plugin is currently in beta and cannot be installed in production - however, if you want to use it in your production org, you can deploy the metadata to your org using your preferred deployment tool (changes sets, sfdx, the metadata API, etc.). | ||
|
||
### Quick Start: Adding Log Retention Rules and Conditions | ||
|
||
After installing the plugin, you can add rules using the custom metadata types `LogRetentionRule__mdt` and `LogRetentionRuleCondition__mdt`. Multiple rules can be configured in your orgs, as shown below: | ||
|
||
![Log Retention Rules plugin: Example Rule](./content/example-log-retention-rules-list-view.png) | ||
|
||
In this example rule, for every inserted `LogEntry__c` record, 2 conditions are checked: | ||
|
||
1. Does the entry's parent `Log__c` record have 1 or more error? (based on the roll-up summary field `Log__c.TotalERRORLogEntries__c`) | ||
2. Does the entry's parent `Log__c` record have the scenario 'feature B'? (based on the text field `Log__c.Scenario__c`) | ||
|
||
If any `LogEntry__c` record is inserted that meets these 2 conditions, then the parent `Log__c` record will have the field `LogRetentionDate__c` set to `System.today() + 30`. | ||
|
||
![Log Retention Rules plugin: Example Rule](./content/example-log-retention-rule-with-conditions.png) |
Binary file added
BIN
+28.7 KB
...ger-plugins/Log-Retention-Rules/content/btn-install-unlocked-package-plugin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+182 KB
...gins/Log-Retention-Rules/content/example-log-retention-rule-with-conditions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+158 KB
...r-plugins/Log-Retention-Rules/content/example-log-retention-rules-list-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
523 changes: 523 additions & 0 deletions
523
nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls
Large diffs are not rendered by default.
Oops, something went wrong.
5 changes: 5 additions & 0 deletions
5
...la-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<apiVersion>52.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
160 changes: 160 additions & 0 deletions
160
nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
//-----------------------------------------------------------------------------------------------------------// | ||
// This file is part of the Nebula Logger project, released under the MIT License. // | ||
// The core functionality of this plugin's code originated in https://github.com/jongpie/ApexValidationRules // | ||
// See LICENSE file or go to https://github.com/jongpie/NebulaLogger for full license details. // | ||
//-----------------------------------------------------------------------------------------------------------// | ||
|
||
// TODO revise suppressed PMD rules/clean up code | ||
@SuppressWarnings( | ||
'PMD.ApexDoc, PMD.AvoidDebugStatements, PMD.ApexAssertionsShouldIncludeMessage, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList, PMD.MethodNamingConventions' | ||
) | ||
@IsTest | ||
private class LogRetentionRulesPlugin_Tests { | ||
@IsTest | ||
static void it_should_set_retention_date_for_rule_with_one_condition() { | ||
enablePlugin(); | ||
Date originalLogRetentionDate = System.today().addDays(Integer.valueOf(Logger.getUserSettings().DefaultNumberOfDaysToRetainLogs__c)); | ||
Integer numberOfDaysToRetainLogs = 90; | ||
Date expectedLogRetentionDate = System.today().addDays(numberOfDaysToRetainLogs); | ||
String scenario = 'Some scenario'; | ||
LogRetentionRule__mdt rule = createMockRule('rule_with_multiple_AND_conditions', numberOfDaysToRetainLogs); | ||
rule.NumberOfDaysToRetainLogs__c = numberOfDaysToRetainLogs; | ||
LogRetentionRulesPlugin.setMockRetentionRule(rule); | ||
List<LogRetentionRuleCondition__mdt> conditions = new List<LogRetentionRuleCondition__mdt>{ | ||
createMockRuleCondition('Log__r.Scenario__c', 'EQUAL_TO', 'Value', scenario) | ||
}; | ||
LogRetentionRulesPlugin.setMockRetentionRuleConditions(rule, conditions); | ||
|
||
Log__c log = new Log__c(Scenario__c = scenario, TransactionId__c = '1234'); | ||
insert log; | ||
log = [SELECT Id, LogRetentionDate__c FROM Log__c WHERE Id = :log.Id]; | ||
System.assertEquals(originalLogRetentionDate, log.LogRetentionDate__c); | ||
|
||
LogEntry__c logEntry = new LogEntry__c(Log__c = log.Id, LoggingLevel__c = LoggingLevel.ERROR.name(), TransactionEntryNumber__c = 1); | ||
insert logEntry; | ||
log = [SELECT Id, LogRetentionDate__c FROM Log__c WHERE Id = :log.Id]; | ||
System.assertEquals(expectedLogRetentionDate, log.LogRetentionDate__c); | ||
} | ||
|
||
@IsTest | ||
static void it_should_set_retention_date_for_rule_with_multiple_and_conditions() { | ||
enablePlugin(); | ||
Date originalLogRetentionDate = System.today().addDays(Integer.valueOf(Logger.getUserSettings().DefaultNumberOfDaysToRetainLogs__c)); | ||
Integer numberOfDaysToRetainLogs = 90; | ||
Date expectedLogRetentionDate = System.today().addDays(numberOfDaysToRetainLogs); | ||
String scenario = 'Some scenario'; | ||
Integer numberOfERRORLogEntries = 1; | ||
LogRetentionRule__mdt rule = createMockRule('rule_with_multiple_AND_conditions', numberOfDaysToRetainLogs); | ||
LogRetentionRulesPlugin.setMockRetentionRule(rule); | ||
List<LogRetentionRuleCondition__mdt> conditions = new List<LogRetentionRuleCondition__mdt>{ | ||
createMockRuleCondition('Log__r.Scenario__c', 'EQUAL_TO', 'Value', scenario), | ||
createMockRuleCondition('Log__r.TotalERRORLogEntries__c', 'GREATER_THAN_OR_EQUAL_TO', 'Value', numberOfERRORLogEntries) | ||
}; | ||
LogRetentionRulesPlugin.setMockRetentionRuleConditions(rule, conditions); | ||
|
||
Log__c log = new Log__c(Scenario__c = scenario, TransactionId__c = '1234'); | ||
insert log; | ||
log = [SELECT Id, LogRetentionDate__c FROM Log__c WHERE Id = :log.Id]; | ||
System.assertEquals(originalLogRetentionDate, log.LogRetentionDate__c); | ||
|
||
LogEntry__c logEntry = new LogEntry__c(Log__c = log.Id, LoggingLevel__c = LoggingLevel.ERROR.name(), TransactionEntryNumber__c = 1); | ||
insert logEntry; | ||
log = [SELECT Id, LogRetentionDate__c, Scenario__c, TotalERRORLogEntries__c FROM Log__c WHERE Id = :log.Id]; | ||
System.assertEquals(expectedLogRetentionDate, log.LogRetentionDate__c, log); | ||
} | ||
|
||
// @IsTest | ||
// static void validateForRuleWithOrConditions() { | ||
// String accountName1 = 'Some account'; | ||
// String accountName2 = 'another account'; | ||
// Account account = new Account(Name = 'Test account'); | ||
// account.Name = accountName1; | ||
|
||
// LogRetentionRule__mdt rule = createMockRule(); | ||
// rule.ConditionLogicType__c = 'OR'; | ||
// List<LogRetentionRuleCondition__mdt> conditions = new List<LogRetentionRuleCondition__mdt>{ | ||
// createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName1), | ||
// createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName2) | ||
// }; | ||
|
||
// RecordValidator validator = new RecordValidator(account).setRule(rule, conditions); | ||
// List<RecordValidator.ValidationRuleResult> results = validator.validate(false); | ||
// System.assertEquals(1, results.size(), 'Expected 1 validation rule result'); | ||
|
||
// RecordValidator.ValidationRuleResult result = results.get(0); | ||
// System.assertEquals(true, result.hasError, result); | ||
// System.assertEquals(rule.ErrorMessage__c, result.errorMessage, result); | ||
|
||
// try { | ||
// validator.validate(); | ||
// System.assert(false, 'Exception expected on line above'); | ||
// } catch (RecordValidator.RecordValidatorException ex) { | ||
// System.assert(ex.getMessage().contains(rule.ErrorMessage__c), ex); | ||
// } | ||
// } | ||
|
||
// @IsTest | ||
// static void validateForRuleWithCustomConditions() { | ||
// String accountName1 = 'Some account'; | ||
// String accountName2 = 'another account'; | ||
// Integer accountAnnualRevenue = 123000; | ||
// Account account = new Account(Name = 'Test account'); | ||
// account.Name = accountName1; | ||
// account.AnnualRevenue = accountAnnualRevenue; | ||
|
||
// LogRetentionRule__mdt rule = createMockRule(); | ||
// rule.ConditionLogicType__c = 'Custom'; | ||
// rule.CustomConditionLogic__c = '((1 OR 2) AND 3)'; | ||
// List<LogRetentionRuleCondition__mdt> conditions = new List<LogRetentionRuleCondition__mdt>{ | ||
// createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName1), | ||
// createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName2), | ||
// createMockRuleCondition('AnnualRevenue', 'GREATER_THAN_OR_EQUAL_TO', 'Value', accountAnnualRevenue) | ||
// }; | ||
|
||
// RecordValidator validator = new RecordValidator(account).setRule(rule, conditions); | ||
// List<RecordValidator.ValidationRuleResult> results = validator.validate(false); | ||
// System.assertEquals(1, results.size(), 'Expected 1 validation rule result'); | ||
|
||
// RecordValidator.ValidationRuleResult result = results.get(0); | ||
// System.assertEquals(true, result.hasError, result); | ||
// System.assertEquals(rule.ErrorMessage__c, result.errorMessage, result); | ||
|
||
// try { | ||
// validator.validate(); | ||
// System.assert(false, 'Exception expected on line above'); | ||
// } catch (RecordValidator.RecordValidatorException ex) { | ||
// System.assert(ex.getMessage().contains(rule.ErrorMessage__c), ex); | ||
// } | ||
// } | ||
|
||
static void enablePlugin() { | ||
// Set the plugin's parameters | ||
LoggerPlugin__mdt slackPluginConfig = new LoggerPlugin__mdt( | ||
IsEnabled__c = true, | ||
PluginApiName__c = LogRetentionRulesPlugin.class.getName(), | ||
PluginType__c = 'Apex' | ||
); | ||
LoggerSObjectHandler.setMockPlugin(Schema.LogEntry__c.SObjectType, slackPluginConfig); | ||
} | ||
|
||
static LogRetentionRule__mdt createMockRule(String developerName, Integer numberOfDaysToRetainLogs) { | ||
return new LogRetentionRule__mdt( | ||
ConditionLogicType__c = 'AND', | ||
CustomConditionLogic__c = null, | ||
DeveloperName = developerName, | ||
IsEnabled__c = true, | ||
NumberOfDaysToRetainLogs__c = numberOfDaysToRetainLogs | ||
); | ||
} | ||
|
||
static LogRetentionRuleCondition__mdt createMockRuleCondition(String fieldPath, String operator, String valueType, Object value) { | ||
String valueString = value instanceof String ? (String) value : JSON.serialize(value); | ||
return new LogRetentionRuleCondition__mdt( | ||
FieldPath__c = fieldPath, | ||
Operator__c = operator, | ||
SortOrder__c = null, | ||
Value__c = valueString, | ||
ValueType__c = valueType | ||
); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
...ger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<apiVersion>52.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
27 changes: 27 additions & 0 deletions
27
...gins/Log-Retention-Rules/plugin/customMetadata/LoggerPlugin.LogRetentionRules.md-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<CustomMetadata | ||
xmlns="http://soap.sforce.com/2006/04/metadata" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" | ||
> | ||
<label>Log Retention Rules</label> | ||
<protected>false</protected> | ||
<values> | ||
<field>Description__c</field> | ||
<value | ||
xsi:type="xsd:string" | ||
>Adds the ability to create & deploy advanced, configurable rules for setting the retention date of Log__c records, using custom metadata types LogRetentionRule__mdt and LogRetentionRuleCondition__mdt.</value> | ||
</values> | ||
<values> | ||
<field>PluginApiName__c</field> | ||
<value xsi:type="xsd:string">LogRetentionRulesPlugin</value> | ||
</values> | ||
<values> | ||
<field>PluginType__c</field> | ||
<value xsi:type="xsd:string">Apex</value> | ||
</values> | ||
<values> | ||
<field>SObjectType__c</field> | ||
<value xsi:type="xsd:string">LogEntry__c</value> | ||
</values> | ||
</CustomMetadata> |
105 changes: 105 additions & 0 deletions
105
...ayouts/LogRetentionRuleCondition__mdt-Log Retention Rule Condition Layout.layout-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<Layout xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<layoutSections> | ||
<customLabel>false</customLabel> | ||
<detailHeading>true</detailHeading> | ||
<editHeading>true</editHeading> | ||
<label>Information</label> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>MasterLabel</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>DeveloperName</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>LogRetentionRule__c</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Edit</behavior> | ||
<field>SortOrder__c</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<style>TwoColumnsTopToBottom</style> | ||
</layoutSections> | ||
<layoutSections> | ||
<customLabel>true</customLabel> | ||
<detailHeading>true</detailHeading> | ||
<editHeading>true</editHeading> | ||
<label>Condition Details</label> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>FieldPath__c</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>Operator__c</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>ValueType__c</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Edit</behavior> | ||
<field>Value__c</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<style>TwoColumnsTopToBottom</style> | ||
</layoutSections> | ||
<layoutSections> | ||
<customLabel>false</customLabel> | ||
<detailHeading>true</detailHeading> | ||
<editHeading>true</editHeading> | ||
<label>System Information</label> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Edit</behavior> | ||
<field>IsProtected</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Readonly</behavior> | ||
<field>CreatedById</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<layoutColumns> | ||
<layoutItems> | ||
<behavior>Required</behavior> | ||
<field>NamespacePrefix</field> | ||
</layoutItems> | ||
<layoutItems> | ||
<behavior>Readonly</behavior> | ||
<field>LastModifiedById</field> | ||
</layoutItems> | ||
</layoutColumns> | ||
<style>TwoColumnsTopToBottom</style> | ||
</layoutSections> | ||
<layoutSections> | ||
<customLabel>true</customLabel> | ||
<detailHeading>true</detailHeading> | ||
<editHeading>false</editHeading> | ||
<label>Custom Links</label> | ||
<layoutColumns /> | ||
<layoutColumns /> | ||
<layoutColumns /> | ||
<style>CustomLinks</style> | ||
</layoutSections> | ||
<showEmailCheckbox>false</showEmailCheckbox> | ||
<showHighlightsPanel>false</showHighlightsPanel> | ||
<showInteractionLogPanel>false</showInteractionLogPanel> | ||
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox> | ||
<showSubmitAndAttachButton>false</showSubmitAndAttachButton> | ||
<summaryLayout> | ||
<masterLabel>00h1F000005cUjP</masterLabel> | ||
<sizeX>4</sizeX> | ||
<sizeY>0</sizeY> | ||
<summaryLayoutStyle>Default</summaryLayoutStyle> | ||
</summaryLayout> | ||
</Layout> |
Oops, something went wrong.