Skip to content

Commit

Permalink
Added the ability to throw fault message exception in Flow invocable …
Browse files Browse the repository at this point in the history
…actions

* Updated Apex classes FlowLogEntry, FlowRecordLogEntry, and FlowCollectionLogEntry to have a new optional Boolean property, shouldThrowFaultMessageException

* Updated FlowLogger to handle throwing the exception for all 3 Flow invocable logging actions when shouldThrowFaultMessageException == true

* Rearranged a few properties in the Flow classes to group some related properties together for readability
  • Loading branch information
JMercie authored Jul 28, 2023
1 parent 4e3d868 commit c4d3987
Show file tree
Hide file tree
Showing 18 changed files with 260 additions and 36 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.

## Unlocked Package - v4.11.0
## Unlocked Package - v4.11.1

[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000023SI6QAM)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000023SI6QAM)
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001TsX4QAK)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001TsX4QAK)
[![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000023SI6QAM`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001TsX4QAK`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000023SI6QAM`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001TsX4QAK`

---

Expand All @@ -25,7 +25,7 @@ The most robust logger for Salesforce. Works with Apex, Lightning Components, Fl

`sf package install --wait 30 --security-type AdminsOnly --package 04t5Y0000023SI1QAM`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000023SI1QAM`
`sfdx force:package:install --wait 30 --securitytype AdminsOnly --package 04t5Y0000023SI1QAM`

---

Expand Down
4 changes: 4 additions & 0 deletions docs/apex/Logger-Engine/FlowCollectionLogEntry.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ Optionally choose the save method to use when 'Save Log' is true

Optionally specify the name to use for the current transaction's scenario

#### `shouldThrowFaultMessageException``Boolean`

Optionally rollback Database operations executed until Apex action was called and save the log entry.

#### `tagsString``String`

Optionally provide a comma-separated String of tags to dynamically assign to the log entry
Expand Down
4 changes: 4 additions & 0 deletions docs/apex/Logger-Engine/FlowLogEntry.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ Optionally choose the save method to use when 'Save Log' is true

Optionally specify the name to use for the current transaction's scenario

#### `shouldThrowFaultMessageException``Boolean`

Optionally rollback Database operations executed until Apex action was called and save the log entry.

#### `tagsString``String`

Optionally provide a comma-separated String of tags to dynamically assign to the log entry
Expand Down
4 changes: 4 additions & 0 deletions docs/apex/Logger-Engine/FlowLogger.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ String name of the instance of Logger.SaveMethod to use when 'Save Log&apos

Optionally specify the scenario to use for the current transaction

###### `shouldThrowFaultMessageException``Boolean`

Optionally throws an exception when value is set to True (Use it on Fault connectors of your flow).

###### `tagsString``String`

Comma-separated string of tags
Expand Down
4 changes: 4 additions & 0 deletions docs/apex/Logger-Engine/FlowRecordLogEntry.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ Optionally choose the save method to use when 'Save Log' is true

Optionally specify the name to use for the current transaction's scenario

#### `shouldThrowFaultMessageException``Boolean`

Optionally rollback Database operations executed until Apex action was called and save the log entry.

#### `tagsString``String`

Optionally provide a comma-separated String of tags to dynamically assign to the log entry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,24 @@ global inherited sharing class FlowCollectionLogEntry {
@InvocableVariable(required=false label='Records')
global List<SObject> records;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally log a Flow fault error message
*/
@InvocableVariable(required=false label='(Optional) Flow Fault Error Message')
global String faultMessage;

/**
* @description Optionally rollback Database operations executed until Apex action was called and save the log entry.
*/
@InvocableVariable(required=false label='(Optional) Throw FlowException for Fault Error Message')
global Boolean shouldThrowFaultMessageException;

/**
* @description Optionally choose to save any pending log entries
*/
Expand All @@ -51,12 +63,6 @@ global inherited sharing class FlowCollectionLogEntry {
@InvocableVariable(required=false label='(Optional) Save Method')
global String saveMethodName;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally specify the name to use for the current transaction's scenario
*/
Expand Down
18 changes: 12 additions & 6 deletions nebula-logger/core/main/logger-engine/classes/FlowLogEntry.cls
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,24 @@ global inherited sharing class FlowLogEntry {
@InvocableVariable(required=true label='Log Entry Message')
global String message;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally log a Flow fault error message
*/
@InvocableVariable(required=false label='(Optional) Flow Fault Error Message')
global String faultMessage;

/**
* @description Optionally rollback Database operations executed until Apex action was called and save the log entry.
*/
@InvocableVariable(required=false label='(Optional) Throw FlowException for Fault Error Message')
global Boolean shouldThrowFaultMessageException;

/**
* @description Optionally choose to save any pending log entries
*/
Expand All @@ -51,12 +63,6 @@ global inherited sharing class FlowLogEntry {
@InvocableVariable(required=false label='(Optional) Record ID')
global Id recordId;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally specify the name to use for the current transaction's scenario
*/
Expand Down
30 changes: 27 additions & 3 deletions nebula-logger/core/main/logger-engine/classes/FlowLogger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,20 @@ public inherited sharing class FlowLogger {
*/
public String message;

/**
* @description String name of the entry's logging level
*/
public String loggingLevelName;

/**
* @description String containing fault message, if applicable
*/
public String faultMessage;

/**
* @description String name of the entry's logging level
* @description Optionally throws an exception when value is set to True (Use it on Fault connectors of your flow).
*/
public String loggingLevelName;
public Boolean shouldThrowFaultMessageException;

/**
* @description Optionally specify the scenario to use for the current transaction
Expand Down Expand Up @@ -140,6 +145,9 @@ public inherited sharing class FlowLogger {
public static List<String> addEntries(List<LogEntry> flowEntries) {
Boolean saveLog = false;
Logger.SaveMethod saveMethod = Logger.getSaveMethod();
Boolean shouldThrowFaultMessageException = false;
String faultMessage = '';

for (LogEntry flowEntry : flowEntries) {
flowEntry.addToLoggerBuffer();

Expand All @@ -148,19 +156,35 @@ public inherited sharing class FlowLogger {
if (String.isNotBlank(flowEntry.saveMethodName) == true) {
saveMethod = Logger.SaveMethod.valueOf(flowEntry.saveMethodName);
}
if (flowEntry.shouldThrowFaultMessageException == true) {
shouldThrowFaultMessageException = flowEntry.shouldThrowFaultMessageException;
}
if (String.isNotBlank(flowEntry.faultMessage) == true) {
faultMessage = flowEntry.faultMessage;
}
}
}

if (saveLog == true) {
Logger.saveLog(saveMethod);
}

// Here we throw an exception so we stop synchronous database updates from happening
// avoiding inconsistencies
if (shouldThrowFaultMessageException == true) {
throw new System.FlowException(faultMessage);
}

return setTransactionIds(flowEntries.size());
}

private static List<String> setTransactionIds(Integer size) {
// Event though it's the same transaction ID, Salesforce expects the returned list...
// to have the same number of items as the initial input.
// When there's a mismatch, Salesforce throws an error:
// FLOW_ELEMENT_ERROR The number of results does not match the number of interviews that were executed in a single bulk execution request.|FlowActionCall
List<String> transactionIds = new List<String>();
for (Integer i = 0; i < flowEntries.size(); i++) {
for (Integer i = 0; i < size; i++) {
transactionIds.add(Logger.getTransactionId());
}
return transactionIds;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,24 @@ global inherited sharing class FlowRecordLogEntry {
@InvocableVariable(required=false label='Record')
global SObject record;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally log a Flow fault error message
*/
@InvocableVariable(required=false label='(Optional) Flow Fault Error Message')
global String faultMessage;

/**
* @description Optionally rollback Database operations executed until Apex action was called and save the log entry.
*/
@InvocableVariable(required=false label='(Optional) Throw FlowException for Fault Error Message')
global Boolean shouldThrowFaultMessageException;

/**
* @description Optionally choose to save any pending log entries
*/
Expand All @@ -50,12 +63,6 @@ global inherited sharing class FlowRecordLogEntry {
@InvocableVariable(required=false label='(Optional) Save Method')
global String saveMethodName;

/**
* @description Optionally specify a logging level - the default is 'DEBUG'
*/
@InvocableVariable(required=false label='(Optional) Logging Level')
global String loggingLevelName;

/**
* @description Optionally specify the name to use for the current transaction's scenario
*/
Expand Down
2 changes: 1 addition & 1 deletion nebula-logger/core/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
global with sharing class Logger {
// There's no reliable way to get the version number dynamically in Apex
@TestVisible
private static final String CURRENT_VERSION_NUMBER = 'v4.11.0';
private static final String CURRENT_VERSION_NUMBER = 'v4.11.1';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final Set<String> IGNORED_APEX_CLASSES = initializeIgnoredApexClasses();
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
Expand Down
2 changes: 1 addition & 1 deletion nebula-logger/core/main/logger-engine/lwc/logger/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { LightningElement, api } from 'lwc';
import { createLoggerService } from './loggerService';

const CURRENT_VERSION_NUMBER = 'v4.11.0';
const CURRENT_VERSION_NUMBER = 'v4.11.1';

export default class Logger extends LightningElement {
#loggerService = createLoggerService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,47 @@ private class FlowCollectionLogEntry_Tests {
System.Assert.areEqual('Flow', publishedLogEntryEvent.OriginType__c);
}

@IsTest
static void it_should_throw_exception_when_shouldThrowFaultMessageException_is_set_to_true() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
String faultMessage = '';
System.LoggingLevel entryLoggingLevel = System.LoggingLevel.ERROR;
Logger.getUserSettings().LoggingLevel__c = entryLoggingLevel.name();
LoggerTestConfigurator.setupMockSObjectHandlerConfigurations();
System.Assert.areEqual(0, Logger.getBufferSize());
System.Assert.areEqual(0, [SELECT COUNT() FROM LogEntry__c]);
FlowCollectionLogEntry flowCollectionEntry = createFlowCollectionLogEntry();
flowCollectionEntry.flowName = 'MyFlow';
flowCollectionEntry.message = 'hello from Flow';
flowCollectionEntry.loggingLevelName = entryLoggingLevel.name();
flowCollectionEntry.saveLog = true;
flowCollectionEntry.faultMessage = 'Exception message';
flowCollectionEntry.shouldThrowFaultMessageException = true;
flowCollectionEntry.timestamp = System.now();
System.Assert.areEqual(0, Logger.saveLogCallCount);
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());

try {
FlowCollectionLogEntry.addFlowCollectionEntries(new List<FlowCollectionLogEntry>{ flowCollectionEntry });

System.Assert.areEqual(0, Logger.getBufferSize());
System.Assert.areEqual(1, Logger.saveLogCallCount);
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0);
System.Assert.areEqual(flowCollectionEntry.loggingLevelName, publishedLogEntryEvent.LoggingLevel__c);
System.Assert.areEqual(flowCollectionEntry.message, publishedLogEntryEvent.Message__c);
System.Assert.areEqual('Flow', publishedLogEntryEvent.OriginType__c);
System.Assert.areEqual(flowCollectionEntry.timestamp, publishedLogEntryEvent.Timestamp__c);
} catch (Exception e) {
faultMessage = e.getMessage();

System.Assert.areEqual(flowCollectionEntry.faultMessage, faultMessage, 'fault message its not expected to be empty');
System.Assert.areEqual('System.FlowException', e.getTypeName(), 'Exception type must match the one we are throwing');
}
}

@IsTest
static void it_should_set_logger_scenario() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,47 @@ private class FlowLogEntry_Tests {
System.Assert.areEqual('Flow', publishedLogEntryEvent.OriginType__c);
}

@IsTest
static void it_should_throw_exception_when_shouldThrowFaultMessageException_is_set_to_true() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
String faultMessage = '';
System.LoggingLevel entryLoggingLevel = System.LoggingLevel.ERROR;
Logger.getUserSettings().LoggingLevel__c = entryLoggingLevel.name();
LoggerTestConfigurator.setupMockSObjectHandlerConfigurations();
System.Assert.areEqual(0, Logger.getBufferSize());
System.Assert.areEqual(0, [SELECT COUNT() FROM LogEntry__c]);
FlowLogEntry flowEntry = createFlowLogEntry();
flowEntry.flowName = 'MyFlow';
flowEntry.message = 'hello from Flow';
flowEntry.loggingLevelName = entryLoggingLevel.name();
flowEntry.saveLog = true;
flowEntry.faultMessage = 'Exception message';
flowEntry.shouldThrowFaultMessageException = true;
flowEntry.timestamp = System.now();
System.Assert.areEqual(0, Logger.saveLogCallCount);
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());

try {
FlowLogEntry.addFlowEntries(new List<FlowLogEntry>{ flowEntry });

System.Assert.areEqual(0, Logger.getBufferSize());
System.Assert.areEqual(1, Logger.saveLogCallCount);
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishCallCount());
System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size());
LogEntryEvent__e publishedLogEntryEvent = (LogEntryEvent__e) LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0);
System.Assert.areEqual(flowEntry.loggingLevelName, publishedLogEntryEvent.LoggingLevel__c);
System.Assert.areEqual(flowEntry.message, publishedLogEntryEvent.Message__c);
System.Assert.areEqual('Flow', publishedLogEntryEvent.OriginType__c);
System.Assert.areEqual(flowEntry.timestamp, publishedLogEntryEvent.Timestamp__c);
} catch (Exception e) {
faultMessage = e.getMessage();

System.Assert.areEqual(flowEntry.faultMessage, faultMessage, 'fault message its not expected to be empty');
System.Assert.areEqual('System.FlowException', e.getTypeName(), 'Exception type must match the one we are throwing');
}
}

@IsTest
static void it_should_set_logger_scenario() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
Expand Down
Loading

0 comments on commit c4d3987

Please sign in to comment.