Skip to content

Instantly share code, notes, and snippets.

@jongpie
Last active August 2, 2024 05:00
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 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;
}
}
}
@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));
}
}
}
{
"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"]
}
]
}
{
"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