-
-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for plugins within LogBatchPurger + added archiving in …
…BigObject plugin (#288) * Replaced LoggerSObjectHandlerPlugin abstract class with new class LoggerPlugin that contains 2 interfaces + helper methods, switched to using multiple fields on LoggerPlugin__mdt to indicate Apex classes & Flows to run for a plugin, removed SObject-specific fields on LoggerPlugin__mdt, added new fields Log__c.LogPurgeAction__c and LoggerSettings__c.DefaultLogPurgeAction__c * Added tests in LoggerSettingsController_Tests & LogHandler_Tests for new custom setting LoggerSettings__c.DefaultLogPurgeAction__c * Updated LoggerSObjectHandler & plugin classes to use the NEW new plugin overhaul changes via LoggerPlugin class, removed old class LoggerSObjectHanderPlugin * Expanded the plugin framework to support some aspects of #128 by adding the ability to create plugins for LogBatchPurger, using the interface LoggerPlugin.Batchable - The BigObject plugin (and other plugins) can then leverage this to run additional logic before Log__c, LogEntry__c & LogEntryTag__c records are hard-deleted - For the BigObject plugin, it will be able to archive data into LogEntryArchive__b before LogBatchPurger deletes the data within the custom objects - Also finished some test improvements for triggerable plugins within LoggerSObjectHandler * Renamed BigObject plugin's CMDT file for save method to reflect the naming convention change ('CustomSaveMethodBigObject' instead of 'AdditionalSaveMethodsBigObject'), and renamed plugin CMDT file from 'BigObjectArchiving' to 'LogEntryArchiving', updated labels on some deprecated fields * Added 'deprecated' to the label of several deprecated fields on LoggerPlugin__mdt & removed the related handler methods * WIP Made progress on #128 - Split part of LogEntryArchiveBuilder into a new class, LogEntryArchivePlugin, and implemented interface LoggerPlugin.Batchable so that Log__c/LogEntry__c/LogEntryTag__c records can be archived via LogBatchPurger before they're deleted - The plugin class handles talking with Logger, and builder class handles converting LogEntryEvent__e or LogEntry__c records to LogEntryArchive__b records * Added tests for LogBatchPurger integration in LogEntryArchivePlugin_Tests * WIP Stubbed out a new LWC + controller class + custom tab for viewing LogEntryArchive__b as part of #117 * Standardized test-visible method naming conventions to start with `setMock` & updated approaches to use a Map instead of List for CMDT records * Finished some TODOs & standardized mocking approach for CMDT in LogHandler * Added Log__c list view to show logs that will be purged in the next 10 days, fixed LogEntryArchiveBuilder using the wrong value for LoggedById__c, updated index on LogEntryArchive__b again (still a WIP), retrieved & formatted metadata for LogEntryArchive__b, updated Admin.profile * Fixed some FLS issues * More improvements for CMDT records used in the classes LoggerParameter & LoggerPlugin - Added LoggerParameter.matchOnPrefix() to return CMDT records with a specified prefix in the DeveloperName field - Added inner Comparable class in LoggerPlugin to handle custom sorting, since there are some limitations with SOQL queries for CMDT - Classes like LogBatchPurger, LoggerSObjectHandler, and LoggerSettingsController no longer need to track their own mock CMDT records, the LoggerPlugin & LoggerParameter classes now fully handle mocks for their corresponding CMDT objects - LoggerParameter & LoggerPlugin now require mock CMDT records to have DeveloperName populated - they'll throw errors if the field is null - Also added System namespace to some calls for Test.isRunningTest() to handle crazy orgs that have a custom Test class deployed * Added the ability to disable all triggers during tests via new @testvisible method LoggerSObjectHandler.shouldExecute(Boolean) * Removed old integration test class + Flow for testing Flow plugins in LoggerSObjectHandler - I'll reimplement new tests when I also implement Flow plugins for LogBatchPurger * Moved LoggerTestUtils to the configuration folder, added public methods for mocking all CMDT records * Consolidated plugin-framework folders back into the configuration folders
- Loading branch information
Showing
227 changed files
with
4,196 additions
and
2,250 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
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
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
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
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
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
139 changes: 139 additions & 0 deletions
139
nebula-logger/core/main/configuration/classes/LoggerPlugin.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,139 @@ | ||
//------------------------------------------------------------------------------------------------// | ||
// This file is part of the Nebula Logger project, released under the MIT License. // | ||
// See LICENSE file or go to https://github.com/jongpie/NebulaLogger for full license details. // | ||
//------------------------------------------------------------------------------------------------// | ||
|
||
/** | ||
* @group Configuration | ||
* @description The core of the plugin framework, used to create custom Apex & Flow plugins for `LoggerSObjectHandler` and `LogBatchPurger` | ||
* based on configurations stored in the custom metadata type `LoggerPlugin__mdt` | ||
*/ | ||
public without sharing class LoggerPlugin { | ||
private static final Map<String, LoggerPlugin__mdt> DEVELOPER_NAME_TO_RECORD = loadRecords(); | ||
|
||
/** | ||
* @description Interface used to create plugins that can be used within Logger's batch job `LogBatchPurger` | ||
*/ | ||
@SuppressWarnings('PMD.ApexDoc') | ||
public interface Batchable { | ||
void start(LoggerPlugin__mdt configuration, LoggerBatchableContext input); | ||
void execute(LoggerPlugin__mdt configuration, LoggerBatchableContext input, List<SObject> scopeRecords); | ||
void finish(LoggerPlugin__mdt configuration, LoggerBatchableContext input); | ||
} | ||
|
||
/** | ||
* @description Interface used to create plugins that can be used within Logger's trigger handler framework `LoggerSObjectHandler` | ||
*/ | ||
@SuppressWarnings('PMD.ApexDoc') | ||
public interface Triggerable { | ||
void execute(LoggerPlugin__mdt configuration, LoggerTriggerableContext input); | ||
} | ||
|
||
/** | ||
* @description Filters the configured `LoggerPlugin__mdt` records based on a list of `SObjectField` - only records that have a value for 1 or more | ||
* of the specified `populatedFilterFields` will be returned, sorted by the specified `SObjectField` parameter `sortByField` | ||
* @param populatedFilterFields The list of `SObjectField` to check on each `LoggerPlugin__mdt` record - filtering logic checks for a non-null value | ||
* @param sortByField The `SObjectField` to use to sort the list of matches. The method also uses `DeveloperName` as a secondary field for sorting. | ||
* @return The list of matching `LoggerPlugin__mdt` records | ||
*/ | ||
public static List<LoggerPlugin__mdt> getFilteredPluginConfigurations(List<Schema.SObjectField> populatedFilterFields, Schema.SObjectField sortByField) { | ||
List<PluginConfigurationSorter> matchingPluginConfigurationSorters = new List<PluginConfigurationSorter>(); | ||
for (LoggerPlugin__mdt pluginConfiguration : DEVELOPER_NAME_TO_RECORD.values()) { | ||
Boolean matchesFilterFields = false; | ||
for (Schema.SObjectField filterField : populatedFilterFields) { | ||
if (pluginConfiguration.get(filterField) != null) { | ||
matchesFilterFields = true; | ||
break; | ||
} | ||
} | ||
if (matchesFilterFields == true) { | ||
matchingPluginConfigurationSorters.add( | ||
new PluginConfigurationSorter(pluginConfiguration).sortBy(sortByField).sortBy(Schema.LoggerPlugin__mdt.DeveloperName) | ||
); | ||
} | ||
} | ||
matchingPluginConfigurationSorters.sort(); | ||
List<LoggerPlugin__mdt> matchingPluginConfigurations = new List<LoggerPlugin__mdt>(); | ||
for (PluginConfigurationSorter sorter : matchingPluginConfigurationSorters) { | ||
matchingPluginConfigurations.add(sorter.pluginConfiguration); | ||
} | ||
return matchingPluginConfigurations; | ||
} | ||
|
||
/** | ||
* @description Creates an instance of the class `LoggerPlugin.Batchable` based on the provided `LoggerPlugin__mdt` configuration | ||
* @param pluginConfiguration The instance of `LoggerPlugin__mdt` to use to dynamically create an instance of `LoggerPlugin.Batchable` | ||
* @return The dynamically created instance of `LoggerPlugin.Batchable`, | ||
* or null if an instance could not be created based on the provided configuration | ||
*/ | ||
public static Batchable newBatchableInstance(LoggerPlugin__mdt pluginConfiguration) { | ||
return (Batchable) Type.forName(pluginConfiguration?.BatchPurgerApexClass__c)?.newInstance(); | ||
} | ||
|
||
/** | ||
* @description Creates an instance of the class `LoggerPlugin.Triggerable` based on the provided `LoggerPlugin__mdt` configuration | ||
* @param pluginConfiguration The instance of `LoggerPlugin__mdt` to use to dynamically create an instance of `LoggerPlugin.Triggerable` | ||
* @return The dynamically created instance of `LoggerPlugin.Triggerable`, | ||
* or null if an instance could not be created based on the provided configuration | ||
*/ | ||
public static Triggerable newTriggerableInstance(LoggerPlugin__mdt pluginConfiguration) { | ||
return (Triggerable) Type.forName(pluginConfiguration?.SObjectHandlerApexClass__c)?.newInstance(); | ||
} | ||
|
||
private static Map<String, LoggerPlugin__mdt> loadRecords() { | ||
Map<String, LoggerPlugin__mdt> pluginConfigurations = LoggerPlugin__mdt.getAll().clone(); | ||
if (System.Test.isRunningTest() == true) { | ||
pluginConfigurations.clear(); | ||
} | ||
return pluginConfigurations; | ||
} | ||
|
||
@TestVisible | ||
private static void setMock(LoggerPlugin__mdt pluginConfiguration) { | ||
if (String.isBlank(pluginConfiguration.DeveloperName) == true) { | ||
throw new IllegalArgumentException('DeveloperName is required on mock LoggerPlugin__mdt: \n' + JSON.serializePretty(pluginConfiguration)); | ||
} | ||
if (pluginConfiguration.IsEnabled__c == true) { | ||
DEVELOPER_NAME_TO_RECORD.put(pluginConfiguration.DeveloperName, pluginConfiguration); | ||
} | ||
} | ||
|
||
@SuppressWarnings('PMD.ApexDoc') | ||
private class PluginConfigurationSorter implements Comparable { | ||
public LoggerPlugin__mdt pluginConfiguration; | ||
private List<Schema.SObjectField> sortByFields = new List<Schema.SObjectField>(); | ||
|
||
public PluginConfigurationSorter(LoggerPlugin__mdt pluginConfiguration) { | ||
this.pluginConfiguration = pluginConfiguration; | ||
} | ||
|
||
public PluginConfigurationSorter sortBy(Schema.SObjectField field) { | ||
sortByFields.add(field); | ||
return this; | ||
} | ||
|
||
public Integer compareTo(Object compareTo) { | ||
PluginConfigurationSorter that = (PluginConfigurationSorter) compareTo; | ||
|
||
for (Schema.SObjectField field : this.sortByFields) { | ||
// Ugly block to handle numeric comparisons vs string comparisons (based on the field type) | ||
Boolean thisIsGreaterThanThat = false; | ||
if (field.getDescribe().getSoapType() == Schema.SoapType.DOUBLE) { | ||
thisIsGreaterThanThat = (Decimal) this.pluginConfiguration.get(field) > (Decimal) that.pluginConfiguration.get(field); | ||
} else { | ||
thisIsGreaterThanThat = (String) this.pluginConfiguration.get(field) > (String) that.pluginConfiguration.get(field); | ||
} | ||
|
||
// Now, the actual comparisons | ||
if (this.pluginConfiguration.get(field) == that.pluginConfiguration.get(field)) { | ||
continue; | ||
} else if (this.pluginConfiguration.get(field) == null && that.pluginConfiguration.get(field) != null || thisIsGreaterThanThat == true) { | ||
return 1; | ||
} else { | ||
return -1; | ||
} | ||
} | ||
return 0; | ||
} | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.