diff --git a/nebula-logger-plugins/Log-Retention-Rules/README.md b/nebula-logger-plugins/Log-Retention-Rules/README.md deleted file mode 100644 index 72e6f78a3..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# 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) diff --git a/nebula-logger-plugins/Log-Retention-Rules/content/btn-install-unlocked-package-plugin.png b/nebula-logger-plugins/Log-Retention-Rules/content/btn-install-unlocked-package-plugin.png deleted file mode 100644 index 9c3bbf6a8..000000000 Binary files a/nebula-logger-plugins/Log-Retention-Rules/content/btn-install-unlocked-package-plugin.png and /dev/null differ diff --git a/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rule-with-conditions.png b/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rule-with-conditions.png deleted file mode 100644 index 112a4e100..000000000 Binary files a/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rule-with-conditions.png and /dev/null differ diff --git a/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rules-list-view.png b/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rules-list-view.png deleted file mode 100644 index 78835b50c..000000000 Binary files a/nebula-logger-plugins/Log-Retention-Rules/content/example-log-retention-rules-list-view.png and /dev/null differ diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls b/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls deleted file mode 100644 index 43f9bc79b..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls +++ /dev/null @@ -1,523 +0,0 @@ -//-----------------------------------------------------------------------------------------------------------// -// 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.DebugsShouldUseLoggingLevel, PMD.ExcessiveParameterList') -public without sharing class LogRetentionRulesPlugin extends LoggerSObjectHandlerPlugin { - private static final Map RULE_NAME_TO_RULE = new Map(); - private static final Map> RULE_NAME_TO_CONDITIONS = new Map>(); - - public LogRetentionRulesPlugin() { - this.loadConfiguredFilters(); - } - - public override void execute( - TriggerOperation triggerOperationType, - List triggerNew, - Map triggerNewMap, - List triggerOld, - Map triggerOldMap - ) { - switch on triggerOperationType { - when AFTER_INSERT { - List logEntries = requeryLogEntries((List) triggerNew); - this.setLogRetentionDate(logEntries); - } - } - } - - private List requeryLogEntries(List logEntries) { - // Requery the LogEntry__c records so the parent Log__c fields can be included/used in rules - List logEntryFieldNames = new List(Schema.LogEntry__c.SObjectType.getDescribe().fields.getMap().keySet()); - - List logFieldNames = new List(Schema.Log__c.SObjectType.getDescribe().fields.getMap().keySet()); - logFieldNames.addAll(new List{ 'Owner.Name', 'Owner.Type' }); - for (String logFieldName : logFieldNames) { - logEntryFieldNames.add('Log__r.' + logFieldName); - } - - List textReplacements = new List{ String.join(logEntryFieldNames, ',') }; - String logEntryQuery = String.format('SELECT {0} FROM LogEntry__c WHERE Id IN :logEntries', textReplacements); - return (List) Database.query(logEntryQuery); - } - - private void setLogRetentionDate(List logEntries) { - System.debug('starting setLogRetentionDate==' + logEntries); - - Map logIdToLog = new Map(); - List filterResults = this.runFilters(logEntries); - System.debug('filterResults==' + filterResults); - for (FilterResult filterResult : filterResults) { - System.debug('filterResult==' + filterResult); - System.debug('filter matches for ' + filterResult.rule.DeveloperName + '==' + filterResult.matchesFilter); - if (filterResult.matchesFilter == true) { - Id logId = (Id) filterResult.record.get(Schema.LogEntry__c.Log__c); - Log__c log = logIdToLog.get(logId); - if (log == null) { - log = new Log__c(Id = logId); - } - - Integer numberOfDaysToRetainLogs = Integer.valueOf(filterResult.rule.NumberOfDaysToRetainLogs__c); - log.LogRetentionDate__c = numberOfDaysToRetainLogs == null ? null : System.today().addDays(numberOfDaysToRetainLogs); - - logIdToLog.put(log.Id, log); - } - } - System.debug('logIdToLog==' + logIdToLog.values()); - update logIdToLog.values(); - } - - // Private methods - private void loadConfiguredFilters() { - Map queriedRulesByDeveloperName = new Map(); - Map> queriedConditionsByRuleDeveloperName = new Map>(); - for (LogRetentionRule__mdt rule : [ - SELECT - DeveloperName, - ConditionLogicType__c, - CustomConditionLogic__c, - NumberOfDaysToRetainLogs__c, - (SELECT FieldPath__c, Operator__c, ValueType__c, Value__c FROM LogRetentionRuleConditions__r ORDER BY SortOrder__c NULLS LAST, DeveloperName) - FROM LogRetentionRule__mdt - WHERE IsEnabled__c = TRUE - ORDER BY ExecutionOrder__c NULLS LAST, DeveloperName - ]) { - queriedRulesByDeveloperName.put(rule.DeveloperName, rule); - queriedConditionsByRuleDeveloperName.put(rule.DeveloperName, rule.LogRetentionRuleConditions__r); - - if (Test.isRunningTest() == true) { - queriedRulesByDeveloperName.clear(); - queriedConditionsByRuleDeveloperName.clear(); - } - RULE_NAME_TO_RULE.putAll(queriedRulesByDeveloperName); - RULE_NAME_TO_CONDITIONS.putAll(queriedConditionsByRuleDeveloperName); - } - } - - private List runFilters(List records) { - System.debug('runFilters for records: ' + records); - System.debug('runFilters for RULE_NAME_TO_RULE: ' + RULE_NAME_TO_RULE); - List results = new List(); - for (SObject record : records) { - for (String filterDeveloperName : RULE_NAME_TO_RULE.keySet()) { - System.debug('processing filter: ' + filterDeveloperName); - LogRetentionRule__mdt filter = RULE_NAME_TO_RULE.get(filterDeveloperName); - List filerConditions = RULE_NAME_TO_CONDITIONS.get(filter.DeveloperName); - FilterResult filterResult = new FilterResult(record, filter, filerConditions); - - results.add(filterResult); - } - } - return results; - } - - @TestVisible - private static void setMockRetentionRule(LogRetentionRule__mdt rule) { - RULE_NAME_TO_RULE.put(rule.DeveloperName, rule); - } - - @TestVisible - private static void setMockRetentionRuleConditions(LogRetentionRule__mdt rule, List conditions) { - RULE_NAME_TO_CONDITIONS.put(rule.DeveloperName, conditions); - } - - @SuppressWarnings('PMD.ApexDoc, PMD.CyclomaticComplexity, PMD.FieldDeclarationsShouldBeAtStart') - @TestVisible - private class FilterResult { - public SObject record { get; private set; } - public List conditions { get; private set; } - public String conditionsLogic { get; private set; } - public String conditionsLogicType { get; private set; } - public LogRetentionRule__mdt rule { get; private set; } - public Boolean matchesFilter { get; private set; } - - private List filerConditions; - - public FilterResult(SObject record, LogRetentionRule__mdt rule, List filerConditions) { - this.record = record; - this.rule = rule; - this.filerConditions = filerConditions; - - this.conditions = new List(); - this.conditionsLogic = this.getFilterConditionsLogic(); - this.conditionsLogicType = rule.ConditionLogicType__c; - - this.process(); - } - - private void process() { - List booleanValues = new List(); - for (LogRetentionRuleCondition__mdt filerCondition : this.filerConditions) { - FilterConditionResult filerConditionResult = new FilterConditionResult(this.record, filerCondition); - this.conditions.add(filerConditionResult.getCondition()); - booleanValues.add(String.valueOf(filerConditionResult.matchesFilter)); - } - - String parsedConditionsLogic = String.format(this.getFilterConditionsLogic(), booleanValues); - - this.matchesFilter = new BooleanExpression().evaluate(parsedConditionsLogic); - } - - private String getFilterConditionsLogic() { - String conditionsLogic = String.isBlank(this.rule.CustomConditionLogic__c) ? '' : this.rule.CustomConditionLogic__c; - - if (this.rule.ConditionLogicType__c != 'Custom') { - List standardLogicPieces = new List(); - for (Integer i = 0; i < this.filerConditions.size(); i++) { - standardLogicPieces.add(String.valueOf(i + 1)); - } - conditionsLogic = '(' + String.join(standardLogicPieces, ' ' + this.rule.ConditionLogicType__c + ' ') + ')'; - } - - List parsedCharacters = new List(); - Boolean hasFoundNumbers = false; - String foundNumberString = ''; - - for (String character : conditionsLogic.split('')) { - if (!character.isNumeric() && !hasFoundNumbers) { - parsedCharacters.add(character); - } else if (!character.isNumeric() && hasFoundNumbers) { - hasFoundNumbers = false; - Integer foundNumber = Integer.valueOf(foundNumberString) - 1; - - parsedCharacters.add('{' + foundNumber + '}'); - foundNumberString = ''; - parsedCharacters.add(character); - } else if (character.isNumeric()) { - hasFoundNumbers = true; - foundNumberString += character; - } else if (hasFoundNumbers && !character.isNumeric() && !String.isBlank(foundNumberString)) { - Integer foundNumber = Integer.valueOf(foundNumberString) - 1; - - parsedCharacters.add('{' + foundNumber + '}'); - foundNumberString = ''; - } else { - parsedCharacters.add(character); - } - } - return String.join(parsedCharacters, '').toUpperCase(); - } - } - - @SuppressWarnings('PMD.ApexDoc') - @TestVisible - private class FilterConditionResult { - private Boolean matchesFilter; - private SObject record; - private LogRetentionRuleCondition__mdt filerCondition; - private Schema.SObjectType sobjectType; - - private FilterConditionResult(SObject record, LogRetentionRuleCondition__mdt filerCondition) { - this.sobjectType = record.getSObjectType(); - this.record = record; - this.filerCondition = filerCondition; - - this.matchesFilter = this.matchesFilter(); - } - - public String getCondition() { - return this.filerCondition.FieldPath__c + - ' ' + - this.filerCondition.Operator__c + - ' ' + - this.getComparisonValue() + - ' (' + - this.filerCondition.ValueType__c + - ')'; - } - - public Boolean matchesFilter() { - if (this.filerCondition.ValueType__c == 'RegEx') { - return this.matchesRegEx(); - } - - Schema.SObjectField field = new FieldPath(this.sobjectType, this.filerCondition.FieldPath__c).getField(); - - Object recordFieldValue = this.getFieldValue(); - Object comparisonValue = this.getComparisonValue(); - - switch on field.getDescribe().getSoapType() { - when DOUBLE, INTEGER { - return this.compareDecimal((Decimal) recordFieldValue, this.getAsDecimal(comparisonValue)); - } - when DATETIME { - return this.compareDatetime((Datetime) recordFieldValue, this.getAsDatetime(comparisonValue)); - } - when STRING, ID { - return this.compareString((String) recordFieldValue, String.valueOf(comparisonValue)); - } - when else { - throw new IllegalArgumentException('Could not process field path: ' + this.filerCondition.FieldPath__c); - } - } - } - - private Boolean matchesRegEx() { - Pattern pattern = Pattern.compile(this.filerCondition.Value__c); - return pattern.matcher(String.valueOf(this.getFieldValue())).matches(); - } - - private Object getFieldValue() { - return new FieldPath(this.sobjectType, this.filerCondition.FieldPath__c).getValue(this.record); - } - - private Object getComparisonValue() { - switch on this.filerCondition.ValueType__c { - when 'Field' { - return new FieldPath(this.sobjectType, this.filerCondition.Value__c).getValue(this.record); - } - when 'RegEx' { - return this.filerCondition.Value__c; - } - when 'Value' { - return this.filerCondition.Value__c; - } - when else { - throw new IllegalArgumentException('Unknown Value Type, cannot parse comparison value'); - } - } - } - - // Helper methods for dealing with converting field values & strings - // (stored in CMDT) to the appropriate data type - private Datetime getAsDatetime(Object datetimeValue) { - if (datetimeValue == null) { - return null; - } else if (datetimeValue instanceof Datetime) { - return (Datetime) datetimeValue; - } else { - String datetimeString = (String) datetimeValue; - return (Datetime) JSON.deserialize(datetimeString, Datetime.class); - } - } - - private Decimal getAsDecimal(Object decimalValue) { - if (decimalValue == null) { - return null; - } else if (decimalValue instanceof Decimal) { - return (Decimal) decimalValue; - } else { - String decimalString = (String) decimalValue; - return (Decimal) JSON.deserialize(decimalString, Decimal.class); - } - } - - // In Apex, you can't use comparison operators on instances of Object, so several private methods are used for each data type - // Example of what you can't do in Apex: - // Object today = System.today(); - // Object yesterday = System.today().addDays(-1); - // System.assert(today > yesterday); // This line cannot execute since it's comparing Object - private Boolean compareDatetime(Datetime recordFieldValue, Datetime comparisonValue) { - switch on this.filerCondition.Operator__c { - when 'EQUAL_TO' { - return recordFieldValue == comparisonValue; - } - when 'NOT_EQUAL_TO' { - return recordFieldValue != comparisonValue; - } - when 'LESS_THAN' { - return recordFieldValue < comparisonValue; - } - when 'LESS_THAN_OR_EQUAL_TO' { - return recordFieldValue <= comparisonValue; - } - when 'GREATER_THAN' { - return recordFieldValue > comparisonValue; - } - when 'GREATER_THAN_OR_EQUAL_TO' { - return recordFieldValue >= comparisonValue; - } - when else { - throw new IllegalArgumentException('Unsupported operator for Datetime: ' + this.filerCondition.Operator__c); - } - } - } - - private Boolean compareDecimal(Decimal recordFieldValue, Decimal comparisonValue) { - switch on this.filerCondition.Operator__c { - when 'EQUAL_TO' { - return recordFieldValue == comparisonValue; - } - when 'NOT_EQUAL_TO' { - return recordFieldValue != comparisonValue; - } - when 'LESS_THAN' { - return recordFieldValue < comparisonValue; - } - when 'LESS_THAN_OR_EQUAL_TO' { - return recordFieldValue <= comparisonValue; - } - when 'GREATER_THAN' { - return recordFieldValue > comparisonValue; - } - when 'GREATER_THAN_OR_EQUAL_TO' { - return recordFieldValue >= comparisonValue; - } - when else { - throw new IllegalArgumentException('Unsupported operator for Decimal: ' + this.filerCondition.Operator__c); - } - } - } - - private Boolean compareString(String recordFieldValue, String comparisonValue) { - switch on this.filerCondition.Operator__c { - when 'EQUAL_TO' { - return recordFieldValue == comparisonValue; - } - when 'NOT_EQUAL_TO' { - return recordFieldValue != comparisonValue; - } - when 'STARTS_WITH' { - return recordFieldValue.startsWith(comparisonValue); - } - when 'CONTAINS' { - return recordFieldValue.contains(comparisonValue); - } - when 'ENDS_WITH' { - return recordFieldValue.endsWith(comparisonValue); - } - when else { - throw new IllegalArgumentException('Unsupported operator for String: ' + this.filerCondition.Operator__c); - } - } - } - } - - // Credit goes to this StackExchange post for the original BooleanExpression class - - // below is a modified version of the class - // https://salesforce.stackexchange.com/questions/113300/boolean-evaluation-in-apex/113308 - @SuppressWarnings('PMD.ApexDoc') - private class BooleanExpression { - public Boolean evaluate(String x) { - x = simplify(x); - - if (isSimpleExpression(x)) { - return Boolean.valueOf(x); - } - - if (x.contains('&&')) { - return andJoin(x.split('&&', 2)[0], x.split('&&', 2)[1]); - } - - if (x.contains('||')) { - String p1 = x.split('\\|\\|', 2)[0]; - String p2 = x.split('\\|\\|', 2)[1]; - - return orJoin(p1, p2); - } - - if (x.startsWith('!')) { - return !evaluate(x.substring(1)); - } - - return Boolean.valueOf(x); - } - - private Boolean orJoin(String x, String y) { - return evaluate(x) || evaluate(y); - } - - private Boolean andJoin(String x, String y) { - return evaluate(x) && evaluate(y); - } - - private Boolean isSimpleExpression(String x) { - return x == 'true' || x == 'false'; - } - - private String simplify(String x) { - x = x.trim(); - x = x.replace('AND', '&&'); - x = x.replace('OR', '||'); - while (x.contains('(') == true) { - String sub = x.substringAfterLast('(').substringBefore(')'); - x = x.replace('(' + sub + ')', String.valueOf(evaluate(sub))); - } - return x; - } - } - - @SuppressWarnings('PMD.ApexDoc') - private class FieldPath { - private List fieldChain; - private Schema.DescribeFieldResult fieldDescribe; - private String fieldPath; - private Schema.SObjectType sobjectType; - - public FieldPath(Schema.SObjectType sobjectType, String fieldPath) { - this.fieldChain = this.getFieldChain(sobjectType, fieldPath); - this.fieldPath = fieldPath; - - this.fieldDescribe = this.getLastFieldDescribe(); - } - - public Schema.SObjectField getField() { - return this.fieldChain[this.fieldChain.size() - 1]; - } - - public Object getValue(SObject record) { - Schema.SObjectType parentSObjectType = this.sobjectType; - SObject parentRecord = record; - - for (Schema.SObjectField field : this.fieldChain) { - Schema.DescribeFieldResult fieldDescribe = field.getDescribe(); - // TODO delete? String relationshipName = fieldDescribe.getRelationshipName(); - - if (fieldDescribe.getSoapType() != Schema.SoapType.Id) { - return parentRecord.get(fieldDescribe.getName()); - } else { - parentSObjectType = fieldDescribe.getReferenceTo().get(0); - - SObject newParentRecord = parentRecord.getSObject(field); - if (newParentRecord == null) { - return null; - } else { - parentRecord = newParentRecord; - } - } - } - - return null; - } - - private List getFieldChain(Schema.SObjectType sobjectType, String fieldPath) { - Schema.SObjectType currentSObjectType = sobjectType; - - List fields = new List(); - List fieldPathPieces = fieldPath.split('\\.'); - Integer lastFieldIndex = fieldPathPieces.size() <= 1 ? 0 : fieldPathPieces.size() - 1; - - for (Integer i = 0; i < fieldPathPieces.size(); i++) { - String fieldPathPiece = fieldPathPieces[i]; - - String fieldApiName; - if (i == lastFieldIndex) { - fieldApiName = fieldPathPiece; - } else if (fieldPathPiece.endsWith('__r')) { - fieldApiName = fieldPathPiece.replace('__r', '__c'); - } else { - fieldApiName = fieldPathPiece + 'Id'; - } - - Schema.SObjectField field = currentSObjectType.getDescribe().fields.getMap().get(fieldApiName); - - // TODO add support for polymorphic fields - if (i < lastFieldIndex) { - currentSObjectType = field.getDescribe().getReferenceTo().get(0); - } - - fields.add(field); - } - - return fields; - } - - private Schema.DescribeFieldResult getLastFieldDescribe() { - Integer lastFieldIndex = this.fieldChain.size() - 1; - return this.fieldChain[lastFieldIndex].getDescribe(); - } - } -} diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls-meta.xml deleted file mode 100644 index f8e5ab635..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin.cls-meta.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 52.0 - Active - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls b/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls deleted file mode 100644 index 4bd6325c8..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls +++ /dev/null @@ -1,160 +0,0 @@ -//-----------------------------------------------------------------------------------------------------------// -// 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 conditions = new List{ - 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 conditions = new List{ - 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 conditions = new List{ - // createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName1), - // createMockRuleCondition('Name', 'EQUAL_TO', 'Value', accountName2) - // }; - - // RecordValidator validator = new RecordValidator(account).setRule(rule, conditions); - // List 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 conditions = new List{ - // 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 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 - ); - } -} diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls-meta.xml deleted file mode 100644 index f8e5ab635..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/classes/LogRetentionRulesPlugin_Tests.cls-meta.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 52.0 - Active - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/customMetadata/LoggerPlugin.LogRetentionRules.md-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/customMetadata/LoggerPlugin.LogRetentionRules.md-meta.xml deleted file mode 100644 index 057ffb232..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/customMetadata/LoggerPlugin.LogRetentionRules.md-meta.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - false - - Description__c - 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. - - - PluginApiName__c - LogRetentionRulesPlugin - - - PluginType__c - Apex - - - SObjectType__c - LogEntry__c - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRuleCondition__mdt-Log Retention Rule Condition Layout.layout-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRuleCondition__mdt-Log Retention Rule Condition Layout.layout-meta.xml deleted file mode 100644 index e36a1256d..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRuleCondition__mdt-Log Retention Rule Condition Layout.layout-meta.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - false - true - true - - - - Required - MasterLabel - - - Required - DeveloperName - - - - - Required - LogRetentionRule__c - - - Edit - SortOrder__c - - - - - - true - true - true - - - - Required - FieldPath__c - - - Required - Operator__c - - - - - Required - ValueType__c - - - Edit - Value__c - - - - - - false - true - true - - - - Edit - IsProtected - - - Readonly - CreatedById - - - - - Required - NamespacePrefix - - - Readonly - LastModifiedById - - - - - - true - true - false - - - - - - - false - false - false - false - false - - 00h1F000005cUjP - 4 - 0 - Default - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRule__mdt-Log Retention Rule Layout.layout-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRule__mdt-Log Retention Rule Layout.layout-meta.xml deleted file mode 100644 index 958d04dad..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/layouts/LogRetentionRule__mdt-Log Retention Rule Layout.layout-meta.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - false - true - true - - - - Required - MasterLabel - - - Required - DeveloperName - - - - - Edit - IsEnabled__c - - - Edit - ExecutionOrder__c - - - - - - true - true - true - - - - Edit - NumberOfDaysToRetainLogs__c - - - - - Required - ConditionLogicType__c - - - Edit - CustomConditionLogic__c - - - - - - false - true - true - - - - Edit - IsProtected - - - Readonly - CreatedById - - - - - Edit - NamespacePrefix - - - Readonly - LastModifiedById - - - - - - true - true - false - - - - - - - - NumberOfDaysToRetainLogs__c - - - MasterLabel - DeveloperName - SortOrder__c - FieldPath__c - Operator__c - ValueType__c - Value__c - LogRetentionRuleCondition__mdt.LogRetentionRule__c - SortOrder__c - Asc - - false - false - false - false - false - - 00h17000007xeHS - 4 - 0 - Default - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/LogRetentionRuleCondition__mdt.object-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/LogRetentionRuleCondition__mdt.object-meta.xml deleted file mode 100644 index 24cdcfb5d..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/LogRetentionRuleCondition__mdt.object-meta.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - 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. - - Log Retention Rule Conditions - Public - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/FieldPath__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/FieldPath__c.field-meta.xml deleted file mode 100644 index 4724e4a9e..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/FieldPath__c.field-meta.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - FieldPath__c - false - SubscriberControlled - The API name of the LogEntryEvent__e field. Note: parent field, such as CreatedBy.Name, are not supported. - - 255 - true - Text - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/LogRetentionRule__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/LogRetentionRule__c.field-meta.xml deleted file mode 100644 index d89974c73..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/LogRetentionRule__c.field-meta.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - LogRetentionRule__c - false - SubscriberControlled - - LogRetentionRule__mdt - Log Retention Rule Conditions - LogRetentionRuleConditions - true - MetadataRelationship - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Operator__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Operator__c.field-meta.xml deleted file mode 100644 index b62486bba..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Operator__c.field-meta.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - Operator__c - false - SubscriberControlled - - true - Picklist - - true - - false - - EQUAL_TO - false - - - - NOT_EQUAL_TO - false - - - - LESS_THAN - false - - - - GREATER_THAN - false - - - - LESS_THAN_OR_EQUAL_TO - false - - - - GREATER_THAN_OR_EQUAL_TO - false - - - - STARTS_WITH - false - - - - CONTAINS - false - - - - ENDS_WITH - false - - - - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/SortOrder__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/SortOrder__c.field-meta.xml deleted file mode 100644 index 78d7b35b1..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/SortOrder__c.field-meta.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - SortOrder__c - false - SubscriberControlled - - 3 - false - 0 - Number - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/ValueType__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/ValueType__c.field-meta.xml deleted file mode 100644 index 54ea1d6d1..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/ValueType__c.field-meta.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - ValueType__c - false - SubscriberControlled - - true - Picklist - - true - - false - - Value - false - - - - Field - false - - - - RegEx - false - - - - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Value__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Value__c.field-meta.xml deleted file mode 100644 index 0e1738715..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/fields/Value__c.field-meta.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - Value__c - false - SubscriberControlled - - 255 - false - Text - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/listViews/All.listView-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/listViews/All.listView-meta.xml deleted file mode 100644 index 0b411c06f..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRuleCondition__mdt/listViews/All.listView-meta.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - All - MasterLabel - DeveloperName - LogRetentionRule__c - SortOrder__c - FieldPath__c - Operator__c - ValueType__c - Value__c - Everything - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/LogRetentionRule__mdt.object-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/LogRetentionRule__mdt.object-meta.xml deleted file mode 100644 index 95a15408d..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/LogRetentionRule__mdt.object-meta.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - 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. - - Log Retention Rules - Public - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ConditionLogicType__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ConditionLogicType__c.field-meta.xml deleted file mode 100644 index 57f1f320f..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ConditionLogicType__c.field-meta.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - ConditionLogicType__c - false - SubscriberControlled - - true - Picklist - - true - - false - - AND - true - - - - OR - false - - - - Custom - false - - - - - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/CustomConditionLogic__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/CustomConditionLogic__c.field-meta.xml deleted file mode 100644 index cb30f8610..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/CustomConditionLogic__c.field-meta.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - CustomConditionLogic__c - false - SubscriberControlled - - 255 - false - Text - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ExecutionOrder__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ExecutionOrder__c.field-meta.xml deleted file mode 100644 index d37bda01f..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/ExecutionOrder__c.field-meta.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - ExecutionOrder__c - false - SubscriberControlled - The specific order to execute the retention rules in the org. Rules are executed in order by sorting by execution order, then rule name (ORDER BY ExecutionOrder__c NULLS LAST, DeveloperName). In the event that multiple rules apply to a record, the first matching rule is used to set the log retention date. - - 3 - false - 0 - Number - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/IsEnabled__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/IsEnabled__c.field-meta.xml deleted file mode 100644 index 7c3dca299..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/IsEnabled__c.field-meta.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - IsEnabled__c - true - false - SubscriberControlled - - Checkbox - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/NumberOfDaysToRetainLogs__c.field-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/NumberOfDaysToRetainLogs__c.field-meta.xml deleted file mode 100644 index 613a84788..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/fields/NumberOfDaysToRetainLogs__c.field-meta.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - NumberOfDaysToRetainLogs__c - false - SubscriberControlled - This value is used to set the field Log__c.LogRetentionDate__c, which is then used by LogBatchPurger to delete old logs. To keep logs indefinitely, set this field to blank (null). - - 18 - false - 0 - Number - false - diff --git a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/listViews/All.listView-meta.xml b/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/listViews/All.listView-meta.xml deleted file mode 100644 index 9c1c81673..000000000 --- a/nebula-logger-plugins/Log-Retention-Rules/plugin/objects/LogRetentionRule__mdt/listViews/All.listView-meta.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - All - MasterLabel - DeveloperName - IsEnabled__c - ExecutionOrder__c - NumberOfDaysToRetainLogs__c - Everything - - diff --git a/nebula-logger/main/logger-engine/classes/Logger.cls b/nebula-logger/main/logger-engine/classes/Logger.cls index c5dff04ae..a917f80dc 100644 --- a/nebula-logger/main/logger-engine/classes/Logger.cls +++ b/nebula-logger/main/logger-engine/classes/Logger.cls @@ -2458,11 +2458,7 @@ global with sharing class Logger { // LogScenarioRule__mdt scenarioRule = LogScenarioRule__mdt.getInstance(scenario); LogScenarioRule__mdt matchingScenarioRule; - for (LogScenarioRule__mdt scenarioRule : [ - SELECT UserLoggingLevel__c - FROM LogScenarioRule__mdt - WHERE IsEnabled__c = TRUE AND Scenario__c = :scenario - ]) { + for (LogScenarioRule__mdt scenarioRule : [SELECT UserLoggingLevel__c FROM LogScenarioRule__mdt WHERE IsEnabled__c = TRUE AND Scenario__c = :scenario]) { matchingScenarioRule = scenarioRule; } diff --git a/sfdx-project.json b/sfdx-project.json index b656cfa27..6e26743dc 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -13,19 +13,6 @@ "versionDescription": "Added new CMDT LogScenarioRule__mdt to configure scenario-specific logging levels and retention dates", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases" }, - { - "package": "Nebula Logger Plugin - Log Retention Rules", - "path": "nebula-logger-plugins/Log-Retention-Rules/plugin", - "dependencies": [ - { - "package": "Nebula Logger - Unlocked Package@4.6.12-0-logger-scenario-rules" - } - ], - "versionName": "Beta Release", - "versionNumber": "0.9.0.0", - "versionDescription": "Initial beta version of new plugin", - "default": false - }, { "package": "Nebula Logger Plugin - Logger Admin Dashboard", "path": "nebula-logger-plugins/Logger-Admin-Dashboard/plugin",