Skip to content

Instantly share code, notes, and snippets.

@jongpie
Last active July 25, 2024 21:55
Show Gist options
  • Save jongpie/2e84dc855f128769ab826d3ea33734c6 to your computer and use it in GitHub Desktop.
Save jongpie/2e84dc855f128769ab826d3ea33734c6 to your computer and use it in GitHub Desktop.
Nebula Logger - managed package REST resource prototype
@RestResource(urlMapping='/logger/*')
global class LoggerRestResource {
public class LogRequest {
public String parentLogTransactionId;
public List<LogEntryRequest> logEntries = new List<LogEntryRequest>();
}
public class LogEntryRequest {
public String loggingLevel;
public String message;
public String relatedRecordId;
public Datetime timestamp;
public List<String> tags = new List<String>();
}
public class LogResponse {
public String transactionId;
}
@HttpPost
global static void handlePost() {
LogResponse logResponse = new LogRequestSaver(System.RestContext.request).saveLogRequest();
System.RestContext.response = new System.RestResponse();
System.RestContext.response.responseBody = System.Blob.valueOf(System.JSON.serialize(logResponse));
}
private class LogRequestSaver {
private final LogRequest logRequest;
public LogRequestSaver(System.RestRequest restRequest) {
this.logRequest = this.deserializeLogRequest(restRequest);
}
public LogRequest getLogRequest() {
return this.logRequest;
}
public LogResponse saveLogRequest() {
Nebula.Logger.setParentLogTransactionId(this.getLogRequest().parentLogTransactionId);
for (LogEntryRequest logEntryRequest : this.getLogRequest().logEntries) {
System.LoggingLevel loggingLevel = Nebula.Logger.getLoggingLevel(logEntryRequest.loggingLevel);
Nebula.Logger.newEntry(loggingLevel, logEntryRequest.message).setRecord(logEntryRequest.relatedRecordId).addTags(logEntryRequest.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();
LogResponse logResponse = new LogResponse();
logResponse.transactionId = Nebula.Logger.getTransactionId();
return logResponse;
}
private LogRequest deserializeLogRequest(System.RestRequest restRequest) {
if (String.isBlank(restRequest?.requestBody?.toString())) {
throw new System.IllegalArgumentException('No data provided');
}
LogRequest logRequest = (LogRequest) System.JSON.deserialize(restRequest.requestBody.toString(), LogRequest.class);
if (logRequest.logEntries == null || logRequest.logEntries.isEmpty()) {
throw new System.IllegalArgumentException('No log entries provided');
}
return logRequest;
}
}
}
@IsTest
private class LoggerRestResource_Tests {
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;
System.Exception thrownException = null;
try {
LoggerRestResource.handlePost();
System.Assert.fail('Exception expected on previous line');
} catch (System.IllegalArgumentException ex) {
thrownException = ex;
}
System.Assert.isNotNull(thrownException);
System.Assert.isInstanceOfType(thrownException, System.IllegalArgumentException.class);
System.Assert.areEqual('No data provided', thrownException.getMessage());
}
@IsTest
static void it_throws_an_exception_when_log_entries_list_is_null() {
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
logRequest.logEntries = null;
System.Assert.isNull(logRequest.logEntries);
System.RestContext.request = new System.RestRequest();
System.RestContext.request.requestURI = REQUEST_URI;
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logRequest));
System.Exception thrownException = null;
try {
LoggerRestResource.handlePost();
System.Assert.fail('Exception expected on previous line');
} catch (System.IllegalArgumentException ex) {
thrownException = ex;
}
System.Assert.isNotNull(thrownException);
System.Assert.isInstanceOfType(thrownException, System.IllegalArgumentException.class);
System.Assert.areEqual('No log entries provided', thrownException.getMessage());
}
@IsTest
static void it_throws_an_exception_when_log_entries_list_is_empty() {
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
System.Assert.isTrue(logRequest.logEntries.isEmpty());
System.RestContext.request = new System.RestRequest();
System.RestContext.request.requestURI = REQUEST_URI;
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logRequest));
System.Exception thrownException = null;
try {
LoggerRestResource.handlePost();
System.Assert.fail('Exception expected on previous line');
} catch (System.IllegalArgumentException ex) {
thrownException = ex;
}
System.Assert.isNotNull(thrownException);
System.Assert.isInstanceOfType(thrownException, System.IllegalArgumentException.class);
System.Assert.areEqual('No log entries provided', thrownException.getMessage());
}
@IsTest
static void it_should_successsfully_save_log_request_with_log_entries() {
LoggerRestResource.LogEntryRequest firstLogEntryRequest = new LoggerRestResource.LogEntryRequest();
firstLogEntryRequest.loggingLevel = System.LoggingLevel.INFO.name();
firstLogEntryRequest.message = 'some message for INFO';
firstLogEntryRequest.timestamp = System.now().addDays(-1);
LoggerRestResource.LogEntryRequest secondLogEntryRequest = new LoggerRestResource.LogEntryRequest();
secondLogEntryRequest.loggingLevel = System.LoggingLevel.WARN.name();
secondLogEntryRequest.message = 'some message for WARN';
secondLogEntryRequest.timestamp = System.now().addDays(-1);
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
logRequest.logEntries.add(firstLogEntryRequest);
logRequest.logEntries.add(secondLogEntryRequest);
System.RestContext.request = new System.RestRequest();
System.RestContext.request.requestURI = REQUEST_URI;
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logRequest));
LoggerRestResource.handlePost();
System.Test.getEventBus().deliver();
LoggerRestResource.LogResponse logResponse = (LoggerRestResource.LogResponse) System.JSON.deserialize(
System.RestContext.response.responseBody.toString(),
LoggerRestResource.LogResponse.class
);
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logResponse.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 = :firstLogEntryRequest.loggingLevel
];
System.Assert.areEqual(firstLogEntryRequest.loggingLevel, firstLogEntry.Nebula__LoggingLevel__c);
System.Assert.areEqual(firstLogEntryRequest.message, firstLogEntry.Nebula__Message__c);
// System.Assert.areEqual(firstLogEntryRequest.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 = :secondLogEntryRequest.loggingLevel
];
System.Assert.areEqual(secondLogEntryRequest.loggingLevel, secondLogEntry.Nebula__LoggingLevel__c);
System.Assert.areEqual(secondLogEntryRequest.message, secondLogEntry.Nebula__Message__c);
// System.Assert.areEqual(secondLogEntryRequest.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.LogEntryRequest logEntryRequest = new LoggerRestResource.LogEntryRequest();
logEntryRequest.loggingLevel = System.LoggingLevel.INFO.name();
logEntryRequest.message = 'some message';
logEntryRequest.timestamp = System.now().addDays(-1);
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
logRequest.logEntries.add(logEntryRequest);
logRequest.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(logRequest));
LoggerRestResource.handlePost();
System.Test.getEventBus().deliver();
LoggerRestResource.LogResponse logResponse = (LoggerRestResource.LogResponse) System.JSON.deserialize(
System.RestContext.response.responseBody.toString(),
LoggerRestResource.LogResponse.class
);
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logResponse.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.LogEntryRequest logEntryRequest = new LoggerRestResource.LogEntryRequest();
logEntryRequest.loggingLevel = System.LoggingLevel.INFO.name();
logEntryRequest.message = 'some message';
logEntryRequest.relatedRecordId = recordId;
logEntryRequest.timestamp = System.now().addDays(-1);
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
logRequest.logEntries.add(logEntryRequest);
System.RestContext.request = new System.RestRequest();
System.RestContext.request.requestURI = REQUEST_URI;
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logRequest));
LoggerRestResource.handlePost();
System.Test.getEventBus().deliver();
LoggerRestResource.LogResponse logResponse = (LoggerRestResource.LogResponse) System.JSON.deserialize(
System.RestContext.response.responseBody.toString(),
LoggerRestResource.LogResponse.class
);
System.Assert.areEqual(Nebula.Logger.getTransactionId(), logResponse.transactionId);
Nebula__LogEntry__c logEntry = [
SELECT Id, Nebula__RecordId__c
FROM Nebula__LogEntry__c
WHERE Nebula__Log__r.Nebula__TransactionId__c = :logResponse.transactionId
];
System.Assert.areEqual(logEntryRequest.relatedRecordId, logEntry.Nebula__RecordId__c);
}
@IsTest
static void it_stores_tags_when_provided() {
LoggerRestResource.LogEntryRequest logEntryRequest = new LoggerRestResource.LogEntryRequest();
logEntryRequest.loggingLevel = System.LoggingLevel.INFO.name();
logEntryRequest.message = 'some message';
logEntryRequest.timestamp = System.now().addDays(-1);
logEntryRequest.tags = new List<String>{'some tag', 'another tag'};
LoggerRestResource.LogRequest logRequest = new LoggerRestResource.LogRequest();
logRequest.logEntries.add(logEntryRequest);
System.RestContext.request = new System.RestRequest();
System.RestContext.request.requestURI = REQUEST_URI;
System.RestContext.request.requestBody = System.Blob.valueOf(System.JSON.serialize(logRequest));
LoggerRestResource.handlePost();
System.Test.getEventBus().deliver();
LoggerRestResource.LogResponse logResponse = (LoggerRestResource.LogResponse) System.JSON.deserialize(
System.RestContext.response.responseBody.toString(),
LoggerRestResource.LogResponse.class
);
Nebula__LogEntry__c logEntry = [
SELECT Id, (SELECT Id, Nebula__Tag__r.Name FROM Nebula__LogEntryTags__r)
FROM Nebula__LogEntry__c
];
System.Assert.areEqual(logEntryRequest.tags.size(), logEntry.Nebula__LogEntryTags__r.size());
Set<String> providedTags = new Set<String>(logEntryRequest.tags);
for (Nebula__LogEntryTag__c logEntryTag : logEntry.Nebula__LogEntryTags__r) {
System.Assert.isTrue(providedTags.contains(logEntryTag.Nebula__Tag__r.Name));
}
}
}
{
"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"]
}
]
}
{
"transactionId": "uuid-returned-from-nebula-logger"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment