Skip to content

Commit

Permalink
[WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
jongpie committed Sep 12, 2024
1 parent 68c63e8 commit e3c6b95
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 105 deletions.
145 changes: 46 additions & 99 deletions nebula-logger/core/main/logger-engine/classes/CallableLogger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ global without sharing class CallableLogger implements System.Callable {
private static final String OMNISTUDIO_ARGUMENT_OUTPUT = 'output';
private static final String OMNISTUDIO_ARGUMENT_OPTIONS = 'options';
private static final String OUTPUT_ARGUMENT_CALL_ERROR_MESSAGE = 'errorMessage';
private static final String OUTPUT_ARGUMENT_CALL_ERROR_TYPE = 'errorType';
private static final String OUTPUT_ARGUMENT_CALL_IS_SUCCESS = 'isSuccess';

/**
Expand All @@ -39,33 +40,26 @@ global without sharing class CallableLogger implements System.Callable {
*/
global Object call(String action, Map<String, Object> arguments) {
arguments = arguments ?? new Map<String, Object>();
arguments.remove(null);

CallableHandler handler;
Map<String, Object> input;
Map<String, Object> output;
// if (isOmniStudioCall(arguments)) {
if (arguments.containsKey(OMNISTUDIO_ARGUMENT_INPUT) || arguments.containsKey(OMNISTUDIO_ARGUMENT_OUTPUT)) {
handler = new OmniStudioCallableHandler();
input = (Map<String, Object>) arguments.get(OMNISTUDIO_ARGUMENT_INPUT);
output = (Map<String, Object>) arguments.get(OMNISTUDIO_ARGUMENT_OUTPUT);
} else {
handler = new StandardCallableHandler();
input = arguments;
output = new Map<String, Object>();
// Relevant OmniStudio docs for using the Callable interface: https://help.salesforce.com/s/articleView?id=sf.os_callable_implementations.htm&type=5
Map<String, Object> input = (Map<String, Object>) arguments.get(OMNISTUDIO_ARGUMENT_INPUT) ?? arguments;
Map<String, Object> output = (Map<String, Object>) arguments.get(OMNISTUDIO_ARGUMENT_OUTPUT) ?? new Map<String, Object>();
// System.Assert.fail(input.toString());
try {
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
new CallableHandler().handleCall(action, input, output);
} catch(System.Exception thrownException) {
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, false);
output.put(OUTPUT_ARGUMENT_CALL_ERROR_MESSAGE, thrownException.getMessage());
output.put(OUTPUT_ARGUMENT_CALL_ERROR_TYPE, thrownException.getTypeName());
}

handler.handleCall(action, input, output);

return output;
}

@SuppressWarnings('PMD.ApexDoc')
private abstract class CallableHandler {
public virtual void handleCall(String action, Map<String, Object> input, Map<String, Object> output) {
Boolean isSuccess = true;
String errorMessage;

private class CallableHandler {
public void handleCall(String action, Map<String, Object> input, Map<String, Object> output) {
switch on action {
// Methods for transaction IDs / parent transaction IDs
when 'getTransactionId' {
Expand All @@ -87,103 +81,56 @@ global without sharing class CallableLogger implements System.Callable {
when 'endScenario' {
Logger.endScenario((String) input.get(ARGUMENT_SCENARIO));
}
// Methods for adding & saving log entries
when 'newEntry' {
this.newEntry(input);
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when 'saveLog' {
this.saveLog(input);
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when else {
isSuccess = false;
errorMessage = 'Unknown action: ' + action;
throw new System.IllegalArgumentException('Unknown action: ' + action);
}
}

output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, isSuccess);
if (String.isNotBlank(errorMessage)) {
output.put(OUTPUT_ARGUMENT_CALL_ERROR_MESSAGE, errorMessage);
}
}

protected LogEntryEventBuilder newEntry(Map<String, Object> arguments) {
// The value of loggingLevel could be either a string name or enum value,
// so coerce it to a string for consistency
String loggingLevelName = '' + arguments.get(ARGUMENT_LOGGING_LEVEL);
private void newEntry(Map<String, Object> input) {
// The value of loggingLevel could be either a string name or an enum value from System.LoggingLevel,
// so always first convert it to a string for consistency
String loggingLevelName = input.get(ARGUMENT_LOGGING_LEVEL)?.toString();
System.LoggingLevel loggingLevel = Logger.getLoggingLevel(loggingLevelName);
String message = (String) arguments.get(ARGUMENT_MESSAGE);
String message = (String) input.get(ARGUMENT_MESSAGE);

LogEntryEventBuilder logEntryEventBuilder = Logger.newEntry(loggingLevel, message);

if (arguments.containsKey(ARGUMENT_EXCEPTION)) {
logEntryEventBuilder.setExceptionDetails((System.Exception) arguments.get(ARGUMENT_EXCEPTION));
if (input.containsKey(ARGUMENT_EXCEPTION)) {
logEntryEventBuilder.setExceptionDetails((System.Exception) input.get(ARGUMENT_EXCEPTION));
}
if (arguments.containsKey(ARGUMENT_RECORD_ID)) {
logEntryEventBuilder.setRecord((String) arguments.get(ARGUMENT_RECORD_ID));
if (input.containsKey(ARGUMENT_RECORD_ID)) {
logEntryEventBuilder.setRecord((String) input.get(ARGUMENT_RECORD_ID));
}
if (arguments.containsKey(ARGUMENT_RECORD)) {
logEntryEventBuilder.setRecord((SObject) arguments.get(ARGUMENT_RECORD));
if (input.containsKey(ARGUMENT_RECORD)) {
logEntryEventBuilder.setRecord((SObject) input.get(ARGUMENT_RECORD));
}
if (arguments.containsKey(ARGUMENT_RECORD_LIST)) {
logEntryEventBuilder.setRecord((List<SObject>) arguments.get(ARGUMENT_RECORD_LIST));
if (input.containsKey(ARGUMENT_RECORD_LIST)) {
logEntryEventBuilder.setRecord((List<SObject>) input.get(ARGUMENT_RECORD_LIST));
}
if (arguments.containsKey(ARGUMENT_RECORD_MAP)) {
logEntryEventBuilder.setRecord((Map<Id, SObject>) arguments.get(ARGUMENT_RECORD_MAP));
if (input.containsKey(ARGUMENT_RECORD_MAP)) {
logEntryEventBuilder.setRecord((Map<Id, SObject>) input.get(ARGUMENT_RECORD_MAP));
}
if (arguments.containsKey(ARGUMENT_TAGS)) {
logEntryEventBuilder.addTags((List<String>) arguments.get(ARGUMENT_TAGS));
if (input.containsKey(ARGUMENT_TAGS)) {
logEntryEventBuilder.addTags((List<String>) input.get(ARGUMENT_TAGS));
}

return logEntryEventBuilder;
}

protected void saveLog(Map<String, Object> arguments) {
String saveMethodName = ((String) arguments.get(ARGUMENT_SAVE_METHOD_NAME)) ?? Logger.getUserSettings().DefaultSaveMethod__c;
private void saveLog(Map<String, Object> input) {
// The value of saveMethodName could be either a string name or an enum value from Logger.SaveMethod,
// so always first convert it to a string for consistency
// System.Assert.fail(input.toString());
String saveMethodName = input.get(ARGUMENT_SAVE_METHOD_NAME)?.toString() ?? Logger.getUserSettings().DefaultSaveMethod__c;
Logger.saveLog(saveMethodName);
}
}

// Handler used when Apex developers/ISVs/package creators are directly creating
// interacting with `CallableLogger` (i.e., anyone not using this for OmniStudio)
@SuppressWarnings('PMD.ApexDoc')
private class StandardCallableHandler extends CallableHandler {
public override void handleCall(String action, Map<String, Object> input, Map<String, Object> output) {
switch on action {
// Methods for adding entries & saving
when 'newEntry' {
this.newEntry(input);
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when 'saveLog' {
this.saveLog(input);
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when else {
super.handleCall(action, input, output);
}
}
}
}

// Handler used when OmniStudio designers & developers are using `CallableLogger` for logging in OmniStudio
@SuppressWarnings('PMD.ApexDoc')
private class OmniStudioCallableHandler extends CallableHandler {
public override void handleCall(String action, Map<String, Object> input, Map<String, Object> output) {
switch on action {
// TODO revisit if using 'addnewentry' and ''addnewentry' is the best approach for OmniStudio:
// - For omniscripts, there's no state, so calling saveLog() doesn't make sense
// - For integration procedures, there IS state, so devs may want to call newEntry() and saveLog()
when 'addNewEntry' {
this.newEntry(input);
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when 'saveNewEntry' {
this.newEntry(input);
Logger.saveLog();
output.put(OUTPUT_ARGUMENT_CALL_IS_SUCCESS, true);
}
when else {
super.handleCall(action, input, output);
}
}
}
}

// OmniStudio / OmniScript docs https://help.salesforce.com/s/articleView?id=sf.os_callable_implementations.htm&type=5
private Boolean isOmniStudioCall(Map<String, Object> arguments) {
return arguments.containsKey(OMNISTUDIO_ARGUMENT_INPUT) || arguments.containsKey(OMNISTUDIO_ARGUMENT_OUTPUT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ private class CallableLogger_Tests {
System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call(fakeActionName, null);

System.Assert.areEqual(2, returnedOutput.size());
System.Assert.areEqual(3, returnedOutput.size());
System.Assert.areEqual(false, returnedOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + returnedOutput);
System.Assert.areEqual('Unknown action: ' + fakeActionName, returnedOutput.get('errorMessage'));
System.Assert.areEqual(System.IllegalArgumentException.class.getName(), returnedOutput.get('errorType'));
}

@IsTest
Expand All @@ -26,9 +27,10 @@ private class CallableLogger_Tests {
System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
callableLoggerInstance.call(fakeActionName, new Map<String, Object>{ 'output' => omnistudioOutput });

System.Assert.areEqual(2, omnistudioOutput.size());
System.Assert.areEqual(3, omnistudioOutput.size());
System.Assert.areEqual(false, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + omnistudioOutput);
System.Assert.areEqual('Unknown action: ' + fakeActionName, omnistudioOutput.get('errorMessage'));
System.Assert.areEqual(System.IllegalArgumentException.class.getName(), omnistudioOutput.get('errorType'));
}

@IsTest
Expand All @@ -41,9 +43,10 @@ private class CallableLogger_Tests {
System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call(incorrectlyCasedActionName, null);

System.Assert.areEqual(2, returnedOutput.size());
System.Assert.areEqual(3, returnedOutput.size());
System.Assert.areEqual(false, returnedOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + returnedOutput);
System.Assert.areEqual('Unknown action: ' + incorrectlyCasedActionName, returnedOutput.get('errorMessage'));
System.Assert.areEqual(System.IllegalArgumentException.class.getName(), returnedOutput.get('errorType'));
}

@IsTest
Expand Down Expand Up @@ -401,7 +404,7 @@ private class CallableLogger_Tests {
Map<String, Object> omnistudioOutput = new Map<String, Object>();

System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
callableLoggerInstance.call('saveLog', new Map<String, Object>{ 'output' => omnistudioOutput });
callableLoggerInstance.call('saveLog', new Map<String, Object>{ 'output' => omnistudioOutput });

System.Assert.areEqual(1, omnistudioOutput.size(), omnistudioOutput.toString());
System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput);
Expand All @@ -416,7 +419,10 @@ private class CallableLogger_Tests {
String targetSaveMethodName = Logger.SaveMethod.SYNCHRONOUS_DML.name();

System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call('saveLog', new Map<String, Object>{ 'saveMethodName' => targetSaveMethodName });
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call(
'saveLog',
new Map<String, Object>{ 'saveMethodName' => targetSaveMethodName }
);

System.Assert.areEqual(1, returnedOutput.size());
System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput);
Expand All @@ -432,7 +438,10 @@ private class CallableLogger_Tests {
String targetSaveMethodName = Logger.SaveMethod.SYNCHRONOUS_DML.name();

System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance();
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call('saveLog', new Map<String, Object>{ 'saveMethodName' => targetSaveMethodName });
Map<String, Object> returnedOutput = (Map<String, Object>) callableLoggerInstance.call(
'saveLog',
new Map<String, Object>{ 'saveMethodName' => targetSaveMethodName }
);

System.Assert.areEqual(1, returnedOutput.size());
System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput);
Expand Down

0 comments on commit e3c6b95

Please sign in to comment.