Last active
August 2, 2024 05:00
-
-
Save jongpie/2e84dc855f128769ab826d3ea33734c6 to your computer and use it in GitHub Desktop.
Nebula Logger - managed package REST resource prototype
This file contains hidden or 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
@RestResource(urlMapping='/logger/*') | |
global class LoggerRestResource { | |
public class LogCreateRequest { | |
public String parentLogTransactionId; | |
public List<LogEntryCreateRequest> logEntries = new List<LogEntryCreateRequest>(); | |
} | |
public class LogEntryCreateRequest { | |
public String loggingLevel; | |
public String message; | |
public String relatedRecordId; | |
public Datetime timestamp; | |
public List<String> tags = new List<String>(); | |
} | |
public class LogCreateResponse { | |
public String requestId; | |
public String transactionId; | |
} | |
@HttpPost | |
global static void handlePost() { | |
// TODO revisit this line | |
System.RestContext.response = System.RestContext.response ?? new System.RestResponse(); | |
try { | |
LogCreateResponse logCreateResponse = new LogCreateRequestSaver().saveLogRequest(System.RestContext.request); | |
System.RestContext.response.statusCode = 200; | |
System.RestContext.response.responseBody = System.Blob.valueOf(System.JSON.serialize(logCreateResponse)); | |
} catch (Exception ex) { | |
Nebula.Logger.error('Failed to save external log', ex) | |
.setRestRequestDetails(System.RestContext.request) | |
.setRestResponseDetails(System.RestContext.response); | |
Nebula.Logger.saveLog(); | |
System.RestContext.response.responseBody = System.Blob.valueOf('{}'); | |
System.RestContext.response.statusCode = 500; | |
System.RestContext.response.responseBody = System.Blob.valueOf(ex.getMessage()); | |
} | |
} | |
private class LogCreateRequestSaver { | |
public LogCreateResponse saveLogRequest(System.RestRequest restRequest) { | |
LogCreateRequest logCreateRequest = this.deserializeLogRequest(restRequest); | |
Nebula.Logger.setParentLogTransactionId(logCreateRequest.parentLogTransactionId); | |
for (LogEntryCreateRequest logEntryCreateRequest : logCreateRequest.logEntries) { | |
System.LoggingLevel loggingLevel = Nebula.Logger.getLoggingLevel(logEntryCreateRequest.loggingLevel); | |
Nebula.Logger.newEntry(loggingLevel, logEntryCreateRequest.message) | |
.setRecord(logEntryCreateRequest.relatedRecordId) | |
.addTags(logEntryCreateRequest.tags); | |
// FIXME The managed package currently doesn't have the .setTimestamp() builder method, | |
// so there's not a great way to correctly set the Timestamp__c field yet | |
} | |
Nebula.Logger.saveLog(); | |
LogCreateResponse logCreateResponse = new LogCreateResponse(); | |
logCreateResponse.requestId = System.Request.getCurrent().getRequestId(); | |
logCreateResponse.transactionId = Nebula.Logger.getTransactionId(); | |
return logCreateResponse; | |
} | |
private LogCreateRequest deserializeLogRequest(System.RestRequest restRequest) { | |
if (String.isBlank(restRequest?.requestBody?.toString())) { | |
throw new System.IllegalArgumentException('No data provided'); | |
} | |
LogCreateRequest logCreateRequest = (LogCreateRequest) System.JSON.deserialize(restRequest.requestBody.toString(), LogCreateRequest.class); | |
if (logCreateRequest.logEntries == null || logCreateRequest.logEntries.isEmpty()) { | |
throw new System.IllegalArgumentException('No log entries provided'); | |
} | |
return logCreateRequest; | |
} | |
} | |
} |
This file contains hidden or 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
@IsTest | |
private class LoggerRestResource_Tests { | |
// TODO revisit this value / confirm how it will behave with a namespace | |
private static final String REQUEST_URI = '/services/apexrest/logger'; | |
@IsTest | |
static void it_throws_an_exception_when_no_json_data_is_posted() { | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = null; | |
LoggerRestResource.handlePost(); | |
System.Assert.areEqual(500, System.RestContext.response.statusCode); | |
System.Assert.areEqual('No data provided', System.RestContext.response.responseBody?.toString()); | |
} | |
@IsTest | |
static void it_throws_an_exception_when_log_entries_list_is_null() { | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
logCreateRequest.logEntries = null; | |
System.Assert.isNull(logCreateRequest.logEntries); | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Assert.areEqual(500, System.RestContext.response.statusCode); | |
System.Assert.areEqual('No log entries provided', System.RestContext.response.responseBody?.toString()); | |
} | |
@IsTest | |
static void it_throws_an_exception_when_log_entries_list_is_empty() { | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
System.Assert.isTrue(logCreateRequest.logEntries.isEmpty()); | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Assert.areEqual(500, System.RestContext.response.statusCode); | |
System.Assert.areEqual('No log entries provided', System.RestContext.response.responseBody?.toString()); | |
} | |
@IsTest | |
static void it_should_successsfully_save_log_request_with_log_entries() { | |
LoggerRestResource.LogEntryCreateRequest firstLogEntryCreateRequest = new LoggerRestResource.LogEntryCreateRequest(); | |
firstLogEntryCreateRequest.loggingLevel = System.LoggingLevel.INFO.name(); | |
firstLogEntryCreateRequest.message = 'some message for INFO'; | |
firstLogEntryCreateRequest.timestamp = System.now().addDays(-1); | |
LoggerRestResource.LogEntryCreateRequest secondLogEntryCreateRequest = new LoggerRestResource.LogEntryCreateRequest(); | |
secondLogEntryCreateRequest.loggingLevel = System.LoggingLevel.WARN.name(); | |
secondLogEntryCreateRequest.message = 'some message for WARN'; | |
secondLogEntryCreateRequest.timestamp = System.now().addDays(-1); | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
logCreateRequest.logEntries.add(firstLogEntryCreateRequest); | |
logCreateRequest.logEntries.add(secondLogEntryCreateRequest); | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Test.getEventBus().deliver(); | |
System.Assert.areEqual(200, System.RestContext.response.statusCode); | |
System.Assert.isNotNull(System.RestContext.response.responseBody); | |
LoggerRestResource.LogCreateResponse logCreateResponse = (LoggerRestResource.LogCreateResponse) System.JSON.deserialize( | |
System.RestContext.response.responseBody.toString(), | |
LoggerRestResource.LogCreateResponse.class | |
); | |
System.Assert.areEqual(System.Request.getCurrent().getRequestId(), logCreateResponse.requestId); | |
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logCreateResponse.transactionId); | |
Nebula__Log__c log = [SELECT Id, Nebula__TransactionId__c FROM Nebula__Log__c]; | |
System.Assert.areEqual(Nebula.Logger.getTransactionId(), log.Nebula__TransactionId__c); | |
Nebula__LogEntry__c firstLogEntry = [ | |
SELECT Id, Nebula__LoggingLevel__c, Nebula__Message__c, Nebula__Timestamp__c, Nebula__TransactionEntryNumber__c | |
FROM Nebula__LogEntry__c | |
WHERE Nebula__Log__c = :log.Id AND Nebula__LoggingLevel__c = :firstLogEntryCreateRequest.loggingLevel | |
]; | |
System.Assert.areEqual(firstLogEntryCreateRequest.loggingLevel, firstLogEntry.Nebula__LoggingLevel__c); | |
System.Assert.areEqual(firstLogEntryCreateRequest.message, firstLogEntry.Nebula__Message__c); | |
// System.Assert.areEqual(firstLogEntryCreateRequest.timestamp, firstLogEntry.Nebula__Timestamp__c); | |
System.Assert.areEqual(1, firstLogEntry.Nebula__TransactionEntryNumber__c); | |
Nebula__LogEntry__c secondLogEntry = [ | |
SELECT Id, Nebula__LoggingLevel__c, Nebula__Message__c, Nebula__Timestamp__c, Nebula__TransactionEntryNumber__c | |
FROM Nebula__LogEntry__c | |
WHERE Nebula__Log__c = :log.Id AND Nebula__LoggingLevel__c = :secondLogEntryCreateRequest.loggingLevel | |
]; | |
System.Assert.areEqual(secondLogEntryCreateRequest.loggingLevel, secondLogEntry.Nebula__LoggingLevel__c); | |
System.Assert.areEqual(secondLogEntryCreateRequest.message, secondLogEntry.Nebula__Message__c); | |
// System.Assert.areEqual(secondLogEntryCreateRequest.timestamp, secondLogEntry.Nebula__Timestamp__c); | |
System.Assert.areEqual(2, secondLogEntry.Nebula__TransactionEntryNumber__c); | |
} | |
@IsTest | |
static void it_sets_parent_transaction_id_when_provided() { | |
Nebula__Log__c parentLog = new Nebula__Log__c(Nebula__TransactionId__c = 'some fake parent transaction id'); | |
insert parentLog; | |
LoggerRestResource.LogEntryCreateRequest logEntryCreateRequest = new LoggerRestResource.LogEntryCreateRequest(); | |
logEntryCreateRequest.loggingLevel = System.LoggingLevel.INFO.name(); | |
logEntryCreateRequest.message = 'some message'; | |
logEntryCreateRequest.timestamp = System.now().addDays(-1); | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
logCreateRequest.logEntries.add(logEntryCreateRequest); | |
logCreateRequest.parentLogTransactionId = parentLog.Nebula__TransactionId__c; | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Test.getEventBus().deliver(); | |
System.Assert.areEqual(200, System.RestContext.response.statusCode); | |
System.Assert.isNotNull(System.RestContext.response.responseBody); | |
LoggerRestResource.LogCreateResponse logCreateResponse = (LoggerRestResource.LogCreateResponse) System.JSON.deserialize( | |
System.RestContext.response.responseBody.toString(), | |
LoggerRestResource.LogCreateResponse.class | |
); | |
System.Assert.areEqual(System.Request.getCurrent().getRequestId(), logCreateResponse.requestId); | |
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logCreateResponse.transactionId); | |
Nebula__Log__c log = [SELECT Id, Nebula__ParentLog__c, Nebula__TransactionId__c FROM Nebula__Log__c WHERE Id != :parentLog.Id]; | |
System.Assert.areEqual(parentLog.Id, log.Nebula__ParentLog__c); | |
System.Assert.areEqual(Nebula.Logger.getTransactionId(), log.Nebula__TransactionId__c); | |
} | |
@IsTest | |
static void it_sets_related_record_id_when_provided() { | |
String recordId = System.UserInfo.getUserId(); | |
LoggerRestResource.LogEntryCreateRequest logEntryCreateRequest = new LoggerRestResource.LogEntryCreateRequest(); | |
logEntryCreateRequest.loggingLevel = System.LoggingLevel.INFO.name(); | |
logEntryCreateRequest.message = 'some message'; | |
logEntryCreateRequest.relatedRecordId = recordId; | |
logEntryCreateRequest.timestamp = System.now().addDays(-1); | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
logCreateRequest.logEntries.add(logEntryCreateRequest); | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Test.getEventBus().deliver(); | |
System.Assert.areEqual(200, System.RestContext.response.statusCode); | |
System.Assert.isNotNull(System.RestContext.response.responseBody); | |
LoggerRestResource.LogCreateResponse logCreateResponse = (LoggerRestResource.LogCreateResponse) System.JSON.deserialize( | |
System.RestContext.response.responseBody.toString(), | |
LoggerRestResource.LogCreateResponse.class | |
); | |
System.Assert.areEqual(System.Request.getCurrent().getRequestId(), logCreateResponse.requestId); | |
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logCreateResponse.transactionId); | |
Nebula__LogEntry__c logEntry = [ | |
SELECT Id, Nebula__RecordId__c | |
FROM Nebula__LogEntry__c | |
WHERE Nebula__Log__r.Nebula__TransactionId__c = :logCreateResponse.transactionId | |
]; | |
System.Assert.areEqual(logEntryCreateRequest.relatedRecordId, logEntry.Nebula__RecordId__c); | |
} | |
@IsTest | |
static void it_stores_tags_when_provided() { | |
LoggerRestResource.LogEntryCreateRequest logEntryCreateRequest = new LoggerRestResource.LogEntryCreateRequest(); | |
logEntryCreateRequest.loggingLevel = System.LoggingLevel.INFO.name(); | |
logEntryCreateRequest.message = 'some message'; | |
logEntryCreateRequest.timestamp = System.now().addDays(-1); | |
logEntryCreateRequest.tags = new List<String>{ 'some tag', 'another tag' }; | |
LoggerRestResource.LogCreateRequest logCreateRequest = new LoggerRestResource.LogCreateRequest(); | |
logCreateRequest.logEntries.add(logEntryCreateRequest); | |
System.RestContext.request = new System.RestRequest(); | |
System.RestContext.request.requestURI = REQUEST_URI; | |
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logCreateRequest)); | |
LoggerRestResource.handlePost(); | |
System.Test.getEventBus().deliver(); | |
System.Assert.areEqual(200, System.RestContext.response.statusCode); | |
System.Assert.isNotNull(System.RestContext.response.responseBody); | |
LoggerRestResource.LogCreateResponse logCreateResponse = (LoggerRestResource.LogCreateResponse) System.JSON.deserialize( | |
System.RestContext.response.responseBody.toString(), | |
LoggerRestResource.LogCreateResponse.class | |
); | |
Nebula__LogEntry__c logEntry = [ | |
SELECT Id, (SELECT Id, Nebula__Tag__r.Name FROM Nebula__LogEntryTags__r) | |
FROM Nebula__LogEntry__c | |
]; | |
System.Assert.areEqual(logEntryCreateRequest.tags.size(), logEntry.Nebula__LogEntryTags__r.size()); | |
Set<String> providedTags = new Set<String>(logEntryCreateRequest.tags); | |
for (Nebula__LogEntryTag__c logEntryTag : logEntry.Nebula__LogEntryTags__r) { | |
System.Assert.isTrue(providedTags.contains(logEntryTag.Nebula__Tag__r.Name)); | |
} | |
} | |
} |
This file contains hidden or 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
{ | |
"parentLogTransactionId": "null-or-a-uuid-returned-from-nebula-logger", | |
"logEntries": [ | |
{ | |
"loggingLevel": "ERROR", | |
"message": "some log entry message" | |
}, | |
{ | |
"loggingLevel": "INFO", | |
"message": "another log entry message", | |
"relatedRecordId": "0058G0000072V5BQAU", | |
}, | |
{ | |
"loggingLevel": "FINEST", | |
"message": "yet another log entry message", | |
"tags": ["some tag", "another tag"] | |
} | |
] | |
} |
This file contains hidden or 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
{ | |
"requestId": "value-returned-from-salesforce", | |
"transactionId": "uuid-returned-from-nebula-logger" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment