-
-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added TrelloLogPusher batch class to handle pushing logs to Slack * Added TrelloLogPushScheduler schedulable class to schedule pushing logs to Slack * Added Trello record to LoggerIntegration__mdt custom metadata type * Added new fields ApiKey__c & UrlParameters__c to LoggerIntegration__mdt.object * Added new Trello fields PushToTrello__c and PushedToTrelloDate__c on Log__c object * Added remote site setting for Trello API * Added batch size of 100 for Loggly & Slack schedulers to avoid hitting callout limits & limit JSON size * Added exceptions in Slack & Loggly when status code >= 400
- Loading branch information
Showing
20 changed files
with
394 additions
and
10 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/************************************************************************************************* | ||
* 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. * | ||
*************************************************************************************************/ | ||
public without sharing class TrelloLogPushScheduler implements System.Schedulable { | ||
|
||
public static void scheduleEveryXMinutes(Integer x) { | ||
for(Integer i = 0; i < 60; i += x) { | ||
scheduleHourly(i); | ||
} | ||
} | ||
|
||
public static void scheduleHourly(Integer startingMinuteInHour) { | ||
String minuteString = String.valueOf(startingMinuteInHour); | ||
minuteString = minuteString.leftPad(2, '0'); | ||
scheduleHourly(startingMinuteInHour, 'Trello Log Sync: Every Hour at ' + minuteString); | ||
} | ||
|
||
public static void scheduleHourly(Integer startingMinuteInHour, String jobName) { | ||
System.schedule(jobName, '0 ' + startingMinuteInHour + ' * * * ?', new TrelloLogPushScheduler()); | ||
} | ||
|
||
public void execute(SchedulableContext sc) { | ||
// Salesforce has a limit of 5 running batch jobs | ||
// If there are already 5 jobs running, then don't run this job | ||
// Any records that need to be processed will be processed the next time the job executes | ||
if(this.getNumberOfRunningBatchJobs() >= 5) return; | ||
|
||
Database.executebatch(new TrelloLogPusher(), 100); | ||
} | ||
|
||
private Integer getNumberOfRunningBatchJobs() { | ||
return [SELECT COUNT() FROM AsyncApexJob WHERE JobType='BatchApex' AND Status IN ('Processing', 'Preparing', 'Queued')]; | ||
} | ||
|
||
} |
This file contains 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
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>43.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/************************************************************************************************* | ||
* 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. * | ||
*************************************************************************************************/ | ||
@isTest | ||
private class TrelloLogPushScheduler_Tests { | ||
|
||
@isTest | ||
static void it_should_schedule_the_batch_job() { | ||
String cronExpression = '0 0 0 15 3 ? 2022'; | ||
Integer numberOfScheduledJobs = [SELECT COUNT() FROM CronTrigger]; | ||
System.assertEquals(0, numberOfScheduledJobs); | ||
|
||
Test.startTest(); | ||
String jobId = System.schedule('TrelloLogPushScheduler', cronExpression, new TrelloLogPushScheduler()); | ||
Test.stopTest(); | ||
|
||
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :jobId]; | ||
System.assertEquals(cronExpression, ct.CronExpression); | ||
} | ||
|
||
@isTest | ||
static void it_should_schedule_the_batch_job_schedule_every_5_minutes() { | ||
Integer numberOfScheduledJobs = [SELECT COUNT() FROM CronTrigger]; | ||
System.assertEquals(0, numberOfScheduledJobs); | ||
|
||
Test.startTest(); | ||
TrelloLogPushScheduler.scheduleEveryXMinutes(5); | ||
Test.stopTest(); | ||
} | ||
|
||
@isTest | ||
static void it_should_schedule_the_batch_job_schedule_hourly() { | ||
Integer numberOfScheduledJobs = [SELECT COUNT() FROM CronTrigger]; | ||
System.assertEquals(0, numberOfScheduledJobs); | ||
|
||
Test.startTest(); | ||
TrelloLogPushScheduler.scheduleHourly(0); | ||
Test.stopTest(); | ||
} | ||
|
||
} |
This file contains 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
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>43.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/************************************************************************************************* | ||
* 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. * | ||
*************************************************************************************************/ | ||
public without sharing class TrelloLogPusher implements Database.AllowsCallouts, Database.Batchable<Log__c> { | ||
|
||
private static final LoggerIntegration__mdt SETTINGS = [SELECT ApiKey__c, ApiToken__c, BaseUrl__c, UrlParameters__c FROM LoggerIntegration__mdt WHERE DeveloperName = 'Trello']; | ||
|
||
public List<Log__c> start(Database.BatchableContext batchableContext) { | ||
return [ | ||
SELECT | ||
LoggedBy__r.Name, LoggedBy__c, Name, TransactionId__c, | ||
// Get the most recent exception entry - this will be used to created the Trello card | ||
( | ||
SELECT ExceptionStackTrace__c, ExceptionType__c, Message__c, Timestamp__c | ||
FROM LogEntries__r | ||
WHERE Type__c = 'Exception' | ||
ORDER BY Timestamp__c | ||
DESC LIMIT 1 | ||
), | ||
(SELECT Topic.Name FROM TopicAssignments) | ||
FROM Log__c | ||
WHERE PushToTrello__c = true | ||
AND PushedToTrelloDate__c = null | ||
AND TotalExceptionLogEntries__c > 0 | ||
]; | ||
} | ||
|
||
public void execute(Database.BatchableContext batchableContext, List<Log__c> logs) { | ||
for(Log__c log : logs) { | ||
LogEntry__c lastException = log.LogEntries__r[0]; | ||
|
||
String cardName = log.Name + ' logged by ' + log.LoggedBy__r.Name; | ||
|
||
List<String> topicNames = new List<String>(); | ||
for(TopicAssignment topicAssignment : log.TopicAssignments) { | ||
topicNames.add('#' + topicAssignment.Topic.Name); | ||
} | ||
if(!topicNames.isEmpty()) cardName += '\n\n' + String.join(topicNames, ' '); | ||
|
||
String cardDescription = | ||
'**Transaction ID:** ' + log.TransactionId__c | ||
+ '\n\n**Timestamp:** ' + this.getFormattedTimestamp(lastException.Timestamp__c) | ||
+ '\n\n**Exception Type:** ' + lastException.ExceptionType__c | ||
+ '\n\n**Message:** ' + lastException.Message__c | ||
+ '\n\n**Stack Trace:** ' + lastException.ExceptionStackTrace__c; | ||
|
||
String urlParameters = SETTINGS.UrlParameters__c == null ? '' : '&' + SETTINGS.UrlParameters__c.replace('\n', '&').replace('\r', '').replace(' ', ''); | ||
String newTrelloCardEndpoint = SETTINGS.BaseUrl__c + '/1/cards' | ||
+ '?name=' + EncodingUtil.urlEncode(cardName, 'UTF-8').replace('+', '%20') | ||
+ '&desc=' + EncodingUtil.urlEncode(cardDescription, 'UTF-8').replace('+', '%20') | ||
+ '&urlSource=' + EncodingUtil.urlEncode(Url.getSalesforceBaseUrl().toExternalForm() + '/' + log.Id, 'UTF-8') | ||
+ '&key=' + SETTINGS.ApiKey__c | ||
+ '&token=' + SETTINGS.ApiToken__c | ||
+ urlParameters; | ||
|
||
HttpRequest request = new HttpRequest(); | ||
request.setEndpoint(newTrelloCardEndpoint); | ||
request.setMethod('POST'); | ||
request.setHeader('Content-Type', 'text/plain'); | ||
HttpResponse response = new Http().send(request); | ||
|
||
if(response.getStatusCode() >= 400) throw new TrelloApiException(response.getBody()); | ||
|
||
log.PushedToTrelloDate__c = System.now(); | ||
} | ||
update logs; | ||
} | ||
|
||
public void finish(Database.BatchableContext batchableContext) {} | ||
|
||
private String getFormattedTimestamp(Datetime timestamp) { | ||
return timestamp.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time'); | ||
} | ||
|
||
private class TrelloApiException extends Exception {} | ||
|
||
} |
This file contains 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
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>43.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/************************************************************************************************* | ||
* 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. * | ||
*************************************************************************************************/ | ||
@isTest | ||
private class TrelloLogPusher_Tests { | ||
|
||
public class SuccessCalloutMock implements HttpCalloutMock { | ||
public HttpResponse respond(HttpRequest request) { | ||
HttpResponse response = new HttpResponse(); | ||
response.setBody(request.getBody()); | ||
response.setStatusCode(200); | ||
return response; | ||
} | ||
} | ||
|
||
public class FailureCalloutMock implements HttpCalloutMock { | ||
public HttpResponse respond(HttpRequest request) { | ||
HttpResponse response = new HttpResponse(); | ||
response.setBody(request.getBody()); | ||
response.setStatusCode(400); | ||
return response; | ||
} | ||
} | ||
|
||
static void verifyLogEntryCountEquals(Integer expectedCount) { | ||
List<LogEntry__c> existingLogEntries = [SELECT Id FROM LogEntry__c]; | ||
System.assertEquals(expectedCount, existingLogEntries.size()); | ||
} | ||
|
||
@isTest | ||
static void it_should_push_a_debug_log_entry_and_update_push_date() { | ||
verifyLogEntryCountEquals(0); | ||
Logger.addDebugEntry(LoggingLevel.DEBUG, 'testing', 'MyClass.myMethodName()'); | ||
Logger.saveLog(); | ||
Test.getEventBus().deliver(); | ||
verifyLogEntryCountEquals(1); | ||
|
||
// To trigger Trello push, we need to update a field on the parent Log__c | ||
Log__c log = [SELECT Id, PushToTrello__c FROM Log__c]; | ||
log.PushToTrello__c = true; | ||
update log; | ||
|
||
LogEntry__c logEntry = [SELECT Id, Log__r.PushToTrello__c, Log__r.PushedToTrelloDate__c FROM LogEntry__c]; | ||
System.assertEquals(true, logEntry.Log__r.PushToTrello__c); | ||
System.assertEquals(null, logEntry.Log__r.PushedToTrelloDate__c); | ||
|
||
Test.startTest(); | ||
Test.setMock(HttpCalloutMock.class, new SuccessCalloutMock()); | ||
|
||
Database.executeBatch(new TrelloLogPusher()); | ||
|
||
Test.stopTest(); | ||
|
||
logEntry = [SELECT Id, Log__r.PushToTrello__c, Log__r.PushedToTrelloDate__c FROM LogEntry__c]; | ||
System.assertEquals(true, logEntry.Log__r.PushToTrello__c); | ||
System.assertEquals(System.today(), logEntry.Log__r.PushedToTrelloDate__c.date()); | ||
} | ||
|
||
@isTest | ||
static void it_should_not_push_a_debug_log_entry_when_push_field_is_false() { | ||
verifyLogEntryCountEquals(0); | ||
Logger.addDebugEntry(LoggingLevel.DEBUG, 'testing', 'MyClass.myMethodName()'); | ||
Logger.saveLog(); | ||
Test.getEventBus().deliver(); | ||
verifyLogEntryCountEquals(1); | ||
|
||
// To make sure that Trello push is not triggered, we need to update a field on the parent Log__c | ||
Log__c log = [SELECT Id, PushToTrello__c FROM Log__c]; | ||
log.PushToTrello__c = false; | ||
update log; | ||
|
||
LogEntry__c logEntry = [SELECT Id, Log__r.PushToTrello__c, Log__r.PushedToTrelloDate__c FROM LogEntry__c]; | ||
System.assertEquals(false, logEntry.Log__r.PushToTrello__c); | ||
System.assertEquals(null, logEntry.Log__r.PushedToTrelloDate__c); | ||
|
||
Test.startTest(); | ||
Test.setMock(HttpCalloutMock.class, new SuccessCalloutMock()); | ||
|
||
Database.executeBatch(new TrelloLogPusher()); | ||
|
||
Test.stopTest(); | ||
|
||
logEntry = [SELECT Id, Log__r.PushToTrello__c, Log__r.PushedToTrelloDate__c FROM LogEntry__c]; | ||
System.assertEquals(false, logEntry.Log__r.PushToTrello__c); | ||
System.assertEquals(null, logEntry.Log__r.PushedToTrelloDate__c); | ||
} | ||
|
||
} |
This file contains 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
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>43.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
Oops, something went wrong.