Skip to content

Commit

Permalink
[WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
jongpie committed Sep 15, 2024
1 parent 396014e commit a7f31d2
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 8 deletions.
3 changes: 0 additions & 3 deletions config/scratch-orgs/advanced-scratch-def.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@
"flowSettings": {
"canDebugFlowAsAnotherUser": true
},
"lightningExperienceSettings": {
"enableLightningPreviewPref": true
},
"pathAssistantSettings": {
"pathAssistantEnabled": true
},
Expand Down
20 changes: 20 additions & 0 deletions docs/apex/Log-Management/LogManagementDataSelector.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,26 @@ List<Log\_\_c>

The list of matching `Log__c` records

#### `getOmniProcessProxies(List<Id> omniProcessIds)``Map<Id, LoggerSObjectProxy.OmniProcess>`

Returns a list of matching `Schema.OmniProcess` records based on the provided list of OmniProcess IDs

##### Parameters

| Param | Description |
| ---------------- | --------------------------------------------- |
| `omniProcessIds` | The list of `Schema.OmniProcess` IDs to query |

##### Return

**Type**

Map&lt;Id, LoggerSObjectProxy.OmniProcess&gt;

**Description**

The instance of `Map&lt;Id, SObject&gt;` containing any matching `Schema.OmniProcess` records

#### `getProfilesById(List<Id> profileIds)``List<Schema.Profile>`

Returns a `List&lt;Schema.Profile&gt;` of records with the specified profile IDs
Expand Down
51 changes: 51 additions & 0 deletions nebula-logger/core/main/log-management/classes/LogEntryHandler.cls
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public without sharing class LogEntryHandler extends LoggerSObjectHandler {
this.setComponentFields();
this.setFlowDefinitionFields();
this.setFlowVersionFields();
this.setOmniProcessFields();
this.setRecordNames();
this.setCheckboxFields();
}
Expand Down Expand Up @@ -219,6 +220,56 @@ public without sharing class LogEntryHandler extends LoggerSObjectHandler {
}
}

private void setOmniProcessFields() {
// TODO consider overriding some fields like to be OmniStudio specific:
// - OriginType__c
// - OriginLocation__c
// - OriginSourceApiName__c
// - OriginSourceMetadataType__c
// etc....
// TODO need to figure if/when to query, and if strongly-typed references are available in all orgs (if not, new Proxy?)
List<String> omniProcessIds = new List<String>();
List<LogEntry__c> omniProcessLogEntries = new List<LogEntry__c>();
for (LogEntry__c logEntry : this.logEntries) {
if (logEntry.OriginType__c == 'OmniStudio' && String.isNotBlank(logEntry.OriginSourceId__c)) {
omniProcessIds.add(logEntry.OriginSourceId__c);
omniProcessLogEntries.add(logEntry);
}
}

if (omniProcessIds.isEmpty()) {
return;
}

Map<Id, LoggerSObjectProxy.OmniProcess> omniProcessIdToProxy = LogManagementDataSelector.getInstance().getOmniProcessProxies(omniProcessIds);
for (LogEntry__c logEntry : omniProcessLogEntries) {
LoggerSObjectProxy.OmniProcess omniProcessProxy = omniProcessIdToProxy.get(logEntry.OriginSourceId__c);

if (omniProcessProxy == null) {
continue;
}

String originSourceMetadataType;
switch on omniProcessProxy.OmniProcessType {
when 'Integration Procedure' {
originSourceMetadataType = 'OmniIntegrationProcedure';
}
when 'OmniScript' {
originSourceMetadataType = 'OmniScript';
}
}

logEntry.OriginSourceApiName__c = omniProcessProxy.UniqueName;
logEntry.OriginSourceCreatedById__c = omniProcessProxy.CreatedById;
logEntry.OriginSourceCreatedByUsername__c = omniProcessProxy.CreatedBy?.Username;
logEntry.OriginSourceCreatedDate__c = omniProcessProxy.CreatedDate;
logEntry.OriginSourceLastModifiedById__c = omniProcessProxy.LastModifiedById;
logEntry.OriginSourceLastModifiedByUsername__c = omniProcessProxy.LastModifiedBy?.Username;
logEntry.OriginSourceLastModifiedDate__c = omniProcessProxy.LastModifiedDate;
logEntry.OriginSourceMetadataType__c = originSourceMetadataType;
}
}

@SuppressWarnings('PMD.OperationWithLimitsInLoop')
private void setRecordNames() {
if (LoggerParameter.QUERY_RELATED_RECORD_DATA == false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,29 @@ public without sharing virtual class LogManagementDataSelector {
return [SELECT Id, OwnerId, UniqueId__c FROM LoggerScenario__c WHERE Id IN :logScenarioIds];
}

/**
* @description Returns a list of matching `Schema.OmniProcess` records based on the provided list of OmniProcess IDs
* @param omniProcessIds The list of `Schema.OmniProcess` IDs to query
* @return The instance of `Map<Id, SObject>` containing any matching `Schema.OmniProcess` records
*/
public virtual Map<Id, LoggerSObjectProxy.OmniProcess> getOmniProcessProxies(List<Id> omniProcessIds) {
if (LoggerParameter.QUERY_OMNI_PROCESS_DATA == false) {
return new Map<Id, LoggerSObjectProxy.OmniProcess>();
}

// OmniStudio may not be enabled in the org, and the Schema.OmniProcess object may not exist,
// so run everything dynamically
Map<Id, LoggerSObjectProxy.OmniProcess> omniProcessIdToOmniProcessProxy = new Map<Id, LoggerSObjectProxy.OmniProcess>();
String query =
'SELECT CreatedBy.Username, CreatedById, CreatedDate, Id, IsIntegrationProcedure, LastModifiedBy.Username, LastModifiedById, LastModifiedDate, OmniProcessType, UniqueName' +
' FROM OmniProcess WHERE Id IN :omniProcessIds';
for (SObject omniProcessRecord : System.Database.query(String.escapeSingleQuotes(query))) {
LoggerSObjectProxy.OmniProcess omniProcessProxy = new LoggerSObjectProxy.OmniProcess(omniProcessRecord);
omniProcessIdToOmniProcessProxy.put(omniProcessProxy.Id, omniProcessProxy);
}
return omniProcessIdToOmniProcessProxy;
}

/**
* @description Returns a `List<Schema.Profile>` of records with the specified profile IDs
* @param profileIds The list of `ID` of the `Schema.Profile` records to query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ global without sharing class CallableLogger implements System.Callable {
}
if (input.containsKey(OMNISTUDIO_ARGUMENT_INPUT_OMNI_PROCESS_ID) || Logger.getCurrentQuiddity() == System.Quiddity.REMOTE_ACTION) {
LogEntryEvent__e logEntryEvent = logEntryEventBuilder.getLogEntryEvent();
logEntryEvent.OriginLocation__c = null;
logEntryEvent.OriginSourceId__c = (String) input.get(OMNISTUDIO_ARGUMENT_INPUT_OMNI_PROCESS_ID);
logEntryEvent.OriginType__c = 'OmniStudio';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,27 @@ public without sharing class LoggerSObjectProxy {
*/
@SuppressWarnings('PMD.FieldNamingConventions, PMD.VariableNamingConventions')
public class OmniProcess {
public Id CreatedById;
public Schema.User CreatedBy;
public Datetime CreatedDate;
public String Id;
public Boolean IsIntegrationProcedure;
public Id LastModifiedById;
public Schema.User LastModifiedBy;
public Datetime LastModifiedDate;
public String OmniProcessType;
public String UniqueName;

@SuppressWarnings('PMD.ApexDoc')
public OmniProcess(SObject omniProcess) {
if (omniProcess != null) {
this.CreatedById = (String) omniProcess.get('CreatedById');
this.CreatedBy = (Schema.User) omniProcess.getSObject('CreatedBy');
this.CreatedDate = (Datetime) omniProcess.get('CreatedDate');
this.Id = (String) omniProcess.get('Id');
this.IsIntegrationProcedure = (Boolean) omniProcess.get('IsIntegrationProcedure');
this.LastModifiedById = (String) omniProcess.get('LastModifiedById');
this.LastModifiedBy = (Schema.User) omniProcess.getSObject('LastModifiedBy');
this.LastModifiedDate = (Datetime) omniProcess.get('LastModifiedDate');
this.OmniProcessType = (String) omniProcess.get('OmniProcessType');
this.UniqueName = (String) omniProcess.get('UniqueName');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1258,13 +1258,86 @@ private class LogEntryHandler_Tests {
System.Assert.isNull(logEntry.OriginSourceSnippet__c);
}

@IsTest
static void it_should_set_origin_omni_process_details() {
Schema.User currentUser = new Schema.User(Id = System.UserInfo.getUserId(), Username = System.UserInfo.getUsername());
SObject mockOmniProcessRecord = (SObject) (System.Type.forName('Schema.OmniProcess').newInstance());
LoggerSObjectProxy.OmniProcess mockOmniProcessProxy = new LoggerSObjectProxy.OmniProcess(mockOmniProcessRecord);
mockOmniProcessProxy.CreatedById = System.UserInfo.getUserId();
mockOmniProcessProxy.CreatedBy = currentUser;
mockOmniProcessProxy.CreatedDate = System.now().addDays(-7);
mockOmniProcessProxy.Id = LoggerMockDataCreator.createId(mockOmniProcessRecord.getSObjectType());
mockOmniProcessProxy.LastModifiedById = currentUser.Id;
mockOmniProcessProxy.LastModifiedBy = currentUser;
mockOmniProcessProxy.LastModifiedDate = System.now().addDays(-1);
mockOmniProcessProxy.OmniProcessType = 'Integration Procedure';
mockOmniProcessProxy.UniqueName = 'Mock_OmniScript_English_1';
MockLogManagementDataSelector mockSelector = new MockLogManagementDataSelector();
mockSelector.setMockOmniProcess(mockOmniProcessProxy);
LogManagementDataSelector.setMock(mockSelector);
Log__c log = [SELECT Id FROM Log__c LIMIT 1];
LogEntry__c logEntry = new LogEntry__c(Log__c = log.Id, OriginSourceId__c = mockOmniProcessProxy.Id, OriginType__c = 'OmniStudio');
LoggerMockDataCreator.createDataBuilder(logEntry).populateRequiredFields().getRecord();

LoggerDataStore.getDatabase().insertRecord(logEntry);

System.Assert.areEqual(
2,
LoggerSObjectHandler.getExecutedHandlers().get(Schema.LogEntry__c.SObjectType).size(),
'Handler class should have executed two times - once for BEFORE_INSERT and once for AFTER_INSERT'
);
logEntry = [
SELECT
Id,
OriginSourceActionName__c,
OriginSourceApiName__c,
OriginSourceApiVersion__c,
OriginSourceCreatedById__c,
OriginSourceCreatedByUsername__c,
OriginSourceCreatedDate__c,
OriginSourceId__c,
OriginSourceLastModifiedById__c,
OriginSourceLastModifiedByUsername__c,
OriginSourceLastModifiedDate__c
FROM LogEntry__c
WHERE Id = :logEntry.Id
];
System.Assert.isNull(logEntry.OriginSourceActionName__c);
System.Assert.areEqual(mockOmniProcessProxy.UniqueName, logEntry.OriginSourceApiName__c);
System.Assert.isNull(logEntry.OriginSourceApiVersion__c);
System.Assert.areEqual(mockOmniProcessProxy.CreatedById, logEntry.OriginSourceCreatedById__c);
System.Assert.areEqual(mockOmniProcessProxy.CreatedBy.Username, logEntry.OriginSourceCreatedByUsername__c);
System.Assert.areEqual(mockOmniProcessProxy.CreatedDate, logEntry.OriginSourceCreatedDate__c);
System.Assert.areEqual(mockOmniProcessProxy.Id, logEntry.OriginSourceId__c);
System.Assert.areEqual(mockOmniProcessProxy.LastModifiedById, logEntry.OriginSourceLastModifiedById__c);
System.Assert.areEqual(mockOmniProcessProxy.LastModifiedBy.Username, logEntry.OriginSourceLastModifiedByUsername__c);
System.Assert.areEqual(mockOmniProcessProxy.LastModifiedDate, logEntry.OriginSourceLastModifiedDate__c);
}

@IsTest
static void it_should_set_skip_setting_origin_process_details_when_origin_source_id_is_null() {
System.Assert.fail('TODO');
}

private static String getNamespacePrefix() {
String className = LogEntryHandler_Tests.class.getName();
String namespacePrefix = className.contains('.') ? className.substringBefore('.') : '';

return namespacePrefix;
}

private class MockLogManagementDataSelector extends LogManagementDataSelector {
private LoggerSObjectProxy.OmniProcess mockOmniProcessProxy;

public override Map<Id, LoggerSObjectProxy.OmniProcess> getOmniProcessProxies(List<Id> omniProcessIds) {
return new Map<Id, LoggerSObjectProxy.OmniProcess>{ mockOmniProcessProxy.Id => mockOmniProcessProxy };
}

public void setMockOmniProcess(LoggerSObjectProxy.OmniProcess omniProcessProxy) {
this.mockOmniProcessProxy = omniProcessProxy;
}
}

// Helper class for testing stack trace parsing & Schema.ApexClass querying for an inner class
private class SomeInnerClass {
public LoggerStackTrace getLoggerStackTrace() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
@SuppressWarnings('PMD.ApexDoc, PMD.CyclomaticComplexity, PMD.MethodNamingConventions')
@IsTest(IsParallel=false)
private class LogManagementDataSelector_Tests {
private static final Boolean IS_OMNISTUDIO_ENABLED = System.Type.forName('Schema.OmniProcess') != null;

@IsTest
static void it_dynamically_queries_all_records_for_specified_sobject_type_and_fields() {
Schema.SObjectType targetSObjectType = Schema.Organization.SObjectType;
Expand Down Expand Up @@ -357,6 +359,57 @@ private class LogManagementDataSelector_Tests {
System.Assert.areEqual(logs.size(), returnedResults.size());
}

@IsTest
static void it_returns_logger_scenarios_for_specified_ids() {
LoggerSObjectHandler.shouldExecute(false);
List<LoggerScenario__c> loggerScenarios = new List<LoggerScenario__c>();
for (Integer i = 0; i < 5; i++) {
LoggerScenario__c loggerScenario = (LoggerScenario__c) LoggerMockDataCreator.createDataBuilder(Schema.LoggerScenario__c.SObjectType)
.populateRequiredFields()
.getRecord();
loggerScenario.Name = 'some fake scenario ' + i;
loggerScenarios.add(loggerScenario);
}
insert loggerScenarios;
List<String> loggerScenarioIds = new List<Id>(new Map<Id, SObject>(loggerScenarios).keySet());

List<LoggerScenario__c> returnedResults = LogManagementDataSelector.getInstance().getLoggerScenariosById(loggerScenarioIds);

System.Assert.areEqual(loggerScenarios.size(), returnedResults.size());
}

@IsTest
static void it_returns_omni_processes_for_specified_ids() {
// No need to fail the test if it's running in an org that does not have OmniStudio enabled
if (IS_OMNISTUDIO_ENABLED == false) {
// return;
}
// Fun fact: in an anonymous Apex script, you can easily create an OmniProcess record...
// ...but when you create one in a test class, you get a gack error 🥲
// Because of this platform limitation, this test is not great - it doesn't create records (because it can't),
// and thus, it can't validate that only the correct records are returned. But, it does validate that the the
// query string is valid & can successfully be executed.
// TODO revisit to see if there is any other way to create OmniProcess records to improve this test.
List<String> omniProcessIds = new List<Id>();

List<SObject> returnedResults = LogManagementDataSelector.getInstance().getLoggerScenariosById(omniProcessIds);

System.Assert.isNotNull(returnedResults);
}

@IsTest
static void it_does_not_query_omni_processes_when_disabled_via_logger_parameter() {
// The IDs used in the query don't particularly matter here - the main concern is checking that the query does not execute at all
List<Id> targetOmniProcessIds = new List<Id>{ System.UserInfo.getUserId() };
Integer originalQueryCount = System.Limits.getQueries();
LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryOmniProcessData', Value__c = String.valueOf(false)));

Map<Id, LoggerSObjectProxy.OmniProcess> returnedResults = LogManagementDataSelector.getInstance().getOmniProcessProxies(targetOmniProcessIds);

System.Assert.areEqual(originalQueryCount, System.Limits.getQueries());
System.Assert.areEqual(0, returnedResults.size());
}

@IsTest
static void it_returns_profiles_for_specified_profile_ids() {
List<Schema.Profile> expectedResults = [SELECT Id, Name FROM Profile LIMIT 10];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,37 @@ private class LoggerSObjectProxy_Tests {
if (IS_OMNISTUDIO_ENABLED == false) {
return;
}
Schema.User currentUser = new Schema.User(Id = System.UserInfo.getUserId(), Username = System.UserInfo.getUsername());
String createdByFieldName = 'CreatedBy';
String createdByIdFieldName = 'CreatedById';
String createdDateFieldName = 'CreatedDate';
String idFieldName = 'Id';
String isIntegrationProcedureFieldName = 'IsIntegrationProcedure';
String lastModifiedByFieldName = 'LastModifiedBy';
String lastModifiedByIdFieldName = 'LastModifiedById';
String lastModifiedDateFieldName = 'LastModifiedDate';
String omniProcessTypeFieldName = 'OmniProcessType';
String uniqueFieldName = 'UniqueName';
SObject mockOmniProcessRecord = (SObject) (System.Type.forName('Schema.OmniProcess').newInstance());
mockOmniProcessRecord.put(createdByIdFieldName, currentUser.Id);
mockOmniProcessRecord.putSObject(createdByFieldName, currentUser);
mockOmniProcessRecord.put(createdDateFieldName, System.now().addDays(-7));
mockOmniProcessRecord.put(idFieldName, LoggerMockDataCreator.createId(mockOmniProcessRecord.getSObjectType()));
mockOmniProcessRecord.put(isIntegrationProcedureFieldName, false);
mockOmniProcessRecord.put(lastModifiedByIdFieldName, currentUser.Id);
mockOmniProcessRecord.putSObject(lastModifiedByFieldName, currentUser);
mockOmniProcessRecord.put(lastModifiedDateFieldName, System.now().addDays(-1));
mockOmniProcessRecord.put(omniProcessTypeFieldName, 'Integration Procedure');
mockOmniProcessRecord.put(uniqueFieldName, 'Mock_OmniScript_English_1');

LoggerSObjectProxy.OmniProcess omniProcessProxy = new LoggerSObjectProxy.OmniProcess(mockOmniProcessRecord);

System.Assert.areEqual((Schema.User) mockOmniProcessRecord.get(createdByFieldName), omniProcessProxy.CreatedBy);
System.Assert.areEqual((String) mockOmniProcessRecord.get(createdByIdFieldName), omniProcessProxy.CreatedById);
System.Assert.areEqual((Datetime) mockOmniProcessRecord.get(createdDateFieldName), omniProcessProxy.CreatedDate);
System.Assert.areEqual((Id) mockOmniProcessRecord.get(idFieldName), omniProcessProxy.Id);
System.Assert.areEqual((Boolean) mockOmniProcessRecord.get(isIntegrationProcedureFieldName), omniProcessProxy.IsIntegrationProcedure);
System.Assert.areEqual((Schema.User) mockOmniProcessRecord.get(lastModifiedByFieldName), omniProcessProxy.LastModifiedBy);
System.Assert.areEqual((String) mockOmniProcessRecord.get(lastModifiedByIdFieldName), omniProcessProxy.LastModifiedById);
System.Assert.areEqual((Datetime) mockOmniProcessRecord.get(lastModifiedByFieldName), omniProcessProxy.LastModifiedDate);
System.Assert.areEqual((String) mockOmniProcessRecord.get(omniProcessTypeFieldName), omniProcessProxy.OmniProcessType);
System.Assert.areEqual((String) mockOmniProcessRecord.get(uniqueFieldName), omniProcessProxy.UniqueName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//------------------------------------------------------------------------------------------------//
// 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. //
//------------------------------------------------------------------------------------------------//

// This class intentionally does nothing - it's here to ensure that any references
// in Nebula Logger's codebase use `Schema.OmniProcess` instead of just `OmniProcess`
@SuppressWarnings('PMD.ApexDoc, PMD.EmptyStatementBlock')
public without sharing class OmniProcess {
}
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>61.0</apiVersion>
<status>Active</status>
</ApexClass>

0 comments on commit a7f31d2

Please sign in to comment.