From c3dc3894737f9ebdd94e4f3bdbed4483d840e046 Mon Sep 17 00:00:00 2001 From: Walt Jones Date: Thu, 23 Apr 2020 16:16:06 -0700 Subject: [PATCH 1/2] feat: enable sending occurrences from triggers, futures, etc via platform events --- .../main/default/classes/DataBuilder.cls | 27 ++++------ force-app/main/default/classes/Notifier.cls | 51 +++++++++++++++---- force-app/main/default/classes/Rollbar.cls | 35 ++++++++++--- force-app/main/default/classes/SendMethod.cls | 1 + .../default/classes/SendMethod.cls-meta.xml | 5 ++ .../RollbarEvent__e.object-meta.xml | 8 +++ .../fields/Data1__c.field-meta.xml | 14 +++++ .../main/default/tests/DataBuilderTest.cls | 16 +++--- force-app/main/default/tests/NotifierTest.cls | 22 ++++---- .../RollbarApiVerifyRequestCalloutMock.cls | 17 +++++++ ...arApiVerifyRequestCalloutMock.cls-meta.xml | 5 ++ force-app/main/default/tests/RollbarTest.cls | 30 ++++++++++- .../main/default/triggers/EventSend.trigger | 7 +++ .../triggers/EventSend.trigger-meta.xml | 5 ++ manifest/package.xml | 8 +++ sfdx-project.json | 2 +- 16 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 force-app/main/default/classes/SendMethod.cls create mode 100644 force-app/main/default/classes/SendMethod.cls-meta.xml create mode 100644 force-app/main/default/objects/RollbarEvent__e/RollbarEvent__e.object-meta.xml create mode 100644 force-app/main/default/objects/RollbarEvent__e/fields/Data1__c.field-meta.xml create mode 100644 force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls create mode 100644 force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls-meta.xml create mode 100644 force-app/main/default/triggers/EventSend.trigger create mode 100644 force-app/main/default/triggers/EventSend.trigger-meta.xml diff --git a/force-app/main/default/classes/DataBuilder.cls b/force-app/main/default/classes/DataBuilder.cls index 29e93f0..a438ecb 100644 --- a/force-app/main/default/classes/DataBuilder.cls +++ b/force-app/main/default/classes/DataBuilder.cls @@ -3,19 +3,14 @@ public with sharing class DataBuilder { this.config = config; } - public Map buildPayload(String level, String message) + public Map buildPayload(String level, String message, Map custom) { - return buildPayloadStructure(level, buildMessageBody(message), null); + return buildPayloadStructure(level, buildMessageBody(message), custom); } - public Map buildPayload(Exception exc) + public Map buildPayload(String level, Exception exc, Map custom) { - return buildPayloadStructure('error', buildExceptionBody(exc), null); - } - - public Map buildPayload(Exception exc, Map custom) - { - return buildPayloadStructure('error', buildExceptionBody(exc), custom); + return buildPayloadStructure(level, buildExceptionBody(exc), custom); } public Map buildPayload(ExceptionData exData) @@ -52,14 +47,14 @@ public with sharing class DataBuilder { } private Map buildPayloadStructure( - String level, - Map body, + String level, + Map body, Map custom ) { Map data = this.buildDataStructure( - level, - this.config.environment(), - body, + level, + this.config.environment(), + body, custom ); @@ -75,7 +70,7 @@ public with sharing class DataBuilder { Map body, Map custom ) { - + Map notifierMap = new Map(); notifierMap.put('name', Notifier.NAME); notifierMap.put('version', Notifier.VERSION); @@ -118,7 +113,7 @@ public with sharing class DataBuilder { Map excMap = new Map(); excMap.put('class', exData.className()); excMap.put('message', exData.message()); - + return buildTraceStructure(excMap, framesList); } diff --git a/force-app/main/default/classes/Notifier.cls b/force-app/main/default/classes/Notifier.cls index 8203263..a5cce9d 100644 --- a/force-app/main/default/classes/Notifier.cls +++ b/force-app/main/default/classes/Notifier.cls @@ -9,32 +9,61 @@ public with sharing class Notifier this.dataBuilder = new DataBuilder(this.config); } - public HttpResponse log(String level, String message) + public HttpResponse log(String level, String message, Map custom, SendMethod method) { - return send(dataBuilder.buildPayload(level, message)); + return send(dataBuilder.buildPayload(level, message, custom), method); } - public HttpResponse log(Exception exc) + public HttpResponse log(String level, Exception exc, Map custom, SendMethod method) { - return send(dataBuilder.buildPayload(exc)); + return send(dataBuilder.buildPayload(level, exc, custom), method); } - public HttpResponse log(Exception exc, Map custom) + public HttpResponse log(ExceptionData exData, SendMethod method) { - return send(dataBuilder.buildPayload(exc, custom)); + return send(dataBuilder.buildPayload(exData), method); } - public HttpResponse log(ExceptionData exData) + public HttpResponse send(Map payload, SendMethod method) { - return send(dataBuilder.buildPayload(exData)); + return methodSend(JSON.serialize(payload), method); } - private HttpResponse send(Map payload) + public HttpResponse methodSend(String payload, SendMethod method) { - return send(JSON.serialize(payload)); + HttpResponse response = null; + + switch on method { + when SYNC { + response = syncSend(payload); + } + when FUTURE { + Notifier.futureSend(payload); + } + when else { + eventSend(payload); + } + } + + return response; + } + + private void eventSend(String payload) + { + RollbarEvent__e event = new RollbarEvent__e(Data1__c = payload); + + EventBus.publish(event); + } + + @future (callout=true) + private static void futureSend(String payload) + { + Notifier notifier = Rollbar.initializedInstance().notifier(); + + notifier.syncSend(payload); } - private HttpResponse send(String payload) + private HttpResponse syncSend(String payload) { HttpRequest request = new HttpRequest(); request.setEndpoint(this.config.endpoint()); diff --git a/force-app/main/default/classes/Rollbar.cls b/force-app/main/default/classes/Rollbar.cls index 070410c..0feb560 100644 --- a/force-app/main/default/classes/Rollbar.cls +++ b/force-app/main/default/classes/Rollbar.cls @@ -41,26 +41,45 @@ global with sharing class Rollbar { } global static HttpResponse log(String level, String message) { + return log(level, message, null, null); + } + + global static HttpResponse log(String level, String message, Map custom) { + return log(level, message, custom, null); + } + + global static HttpResponse log(String level, String message, SendMethod method) { + return log(level, message, null, method); + } + + global static HttpResponse log(String level, String message, Map custom, SendMethod method) { Rollbar instance = initializedInstance(); - return instance.notifier.log(level, message); + return instance.notifier.log(level, message, custom, method); } global static HttpResponse log(Exception exc) { - Rollbar instance = initializedInstance(); - return instance.notifier.log(exc); + return log(exc, null, null); + } + + global static HttpResponse log(Exception exc, SendMethod method) { + return log(exc, null, method); } global static HttpResponse log(Exception exc, Map custom) { + return log(exc, custom, null); + } + + global static HttpResponse log(Exception exc, Map custom, SendMethod method) { Rollbar instance = initializedInstance(); - return instance.notifier.log(exc, custom); + return instance.notifier.log('error', exc, custom, method); } global static HttpResponse log(ExceptionData exData) { Rollbar instance = initializedInstance(); - return instance.notifier.log(exData); + return instance.notifier.log(exData, SendMethod.SYNC); } - private static Rollbar initializedInstance() + public static Rollbar initializedInstance() { Rollbar instance = Rollbar.instance(); if (!instance.initialized) { @@ -73,6 +92,10 @@ global with sharing class Rollbar { private Rollbar() { } + public Notifier notifier() { + return notifier; + } + private static Rollbar instance = null; private Config config = null; private Notifier notifier = null; diff --git a/force-app/main/default/classes/SendMethod.cls b/force-app/main/default/classes/SendMethod.cls new file mode 100644 index 0000000..b263c65 --- /dev/null +++ b/force-app/main/default/classes/SendMethod.cls @@ -0,0 +1 @@ +global enum SendMethod {SYNC, FUTURE, EVENT} diff --git a/force-app/main/default/classes/SendMethod.cls-meta.xml b/force-app/main/default/classes/SendMethod.cls-meta.xml new file mode 100644 index 0000000..ddd3e52 --- /dev/null +++ b/force-app/main/default/classes/SendMethod.cls-meta.xml @@ -0,0 +1,5 @@ + + + 47.0 + Active + diff --git a/force-app/main/default/objects/RollbarEvent__e/RollbarEvent__e.object-meta.xml b/force-app/main/default/objects/RollbarEvent__e/RollbarEvent__e.object-meta.xml new file mode 100644 index 0000000..59a1afb --- /dev/null +++ b/force-app/main/default/objects/RollbarEvent__e/RollbarEvent__e.object-meta.xml @@ -0,0 +1,8 @@ + + + Deployed + HighVolume + + Rollbar Events + PublishImmediately + diff --git a/force-app/main/default/objects/RollbarEvent__e/fields/Data1__c.field-meta.xml b/force-app/main/default/objects/RollbarEvent__e/fields/Data1__c.field-meta.xml new file mode 100644 index 0000000..6834a1b --- /dev/null +++ b/force-app/main/default/objects/RollbarEvent__e/fields/Data1__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Data1__c + false + false + false + false + + false + LongTextArea + 128000 + 3 + false + diff --git a/force-app/main/default/tests/DataBuilderTest.cls b/force-app/main/default/tests/DataBuilderTest.cls index 39f6183..a889dab 100644 --- a/force-app/main/default/tests/DataBuilderTest.cls +++ b/force-app/main/default/tests/DataBuilderTest.cls @@ -1,5 +1,5 @@ @isTest -public class DataBuilderTest +public class DataBuilderTest { @isTest static void testBuildMessagePayload() @@ -7,10 +7,10 @@ public class DataBuilderTest DataBuilder subject = new DataBuilder(new Config('foo', 'bar')); String expected = 'Message built in DataBuilderTest'; - Map result = subject.buildPayload('info', expected); + Map result = subject.buildPayload('info', expected, null); Map data = (Map)result.get('data'); - + Map notifierMap = (Map)data.get('notifier'); System.assertEquals(Notifier.NAME, (String)notifierMap.get('name')); @@ -40,7 +40,7 @@ public class DataBuilderTest expectedClass = exc.getTypeName(); throw exc; } catch(Exception exc) { - Map result = subject.buildPayload(exc); + Map result = subject.buildPayload('error', exc, null); Map data = (Map)result.get('data'); @@ -71,7 +71,7 @@ public class DataBuilderTest DataBuilderTestException exc = new DataBuilderTestException('foobar'); - Map result = subject.buildPayload(exc, expectedCustom); + Map result = subject.buildPayload('error', exc, expectedCustom); Map data = (Map)result.get('data'); Map custom = (Map)data.get('custom'); @@ -98,7 +98,7 @@ public class DataBuilderTest } catch(Exception exc1) { expectedClass1 = exc1.getTypeName(); - Map result = subject.buildPayload(exc1); + Map result = subject.buildPayload('error', exc1, null); Map data = (Map)result.get('data'); @@ -135,7 +135,7 @@ public class DataBuilderTest expected.put('context', 'Exception context'); expected.put('line', 14); expected.put('column', 12); - + ExceptionData exData = ExceptionData.fromMap(expected); Map result = subject.buildPayload(exData); @@ -145,7 +145,7 @@ public class DataBuilderTest Map custom = (Map)data.get('custom'); System.assertEquals((String)expected.get('context'), (String)custom.get('context')); - + Map notifierMap = (Map)data.get('notifier'); System.assertEquals(Notifier.NAME, (String)notifierMap.get('name')); System.assertEquals(Notifier.VERSION, (String)notifierMap.get('version')); diff --git a/force-app/main/default/tests/NotifierTest.cls b/force-app/main/default/tests/NotifierTest.cls index b1bf836..a5a59c9 100644 --- a/force-app/main/default/tests/NotifierTest.cls +++ b/force-app/main/default/tests/NotifierTest.cls @@ -8,14 +8,14 @@ public class NotifierTest { Notifier subject = new Notifier(config); - HttpResponse response = subject.log('info', 'Message from the Apex SDK'); + HttpResponse response = subject.log('info', 'Message from the Apex SDK', null, SendMethod.SYNC); System.assertEquals(200, response.getStatusCode()); - + JSONParser parser = JSON.createParser(response.getBody()); Integer err = null; while (parser.nextToken() != null) { - if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && + if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'err')) { parser.nextToken(); err = parser.getIntegerValue(); @@ -35,14 +35,14 @@ public class NotifierTest { DataBuilderTestException exc = new DataBuilderTestException(); - HttpResponse response = subject.log(exc); + HttpResponse response = subject.log('error', exc, null, SendMethod.SYNC); System.assertEquals(200, response.getStatusCode()); - + JSONParser parser = JSON.createParser(response.getBody()); Integer err = null; while (parser.nextToken() != null) { - if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && + if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'err')) { parser.nextToken(); err = parser.getIntegerValue(); @@ -69,17 +69,17 @@ public class NotifierTest { expected.put('context', 'Exception context'); expected.put('line', 14); expected.put('column', 12); - + ExceptionData exData = ExceptionData.fromMap(expected); - HttpResponse response = subject.log(exData); + HttpResponse response = subject.log(exData, SendMethod.SYNC); System.assertEquals(200, response.getStatusCode()); - + JSONParser parser = JSON.createParser(response.getBody()); Integer err = null; while (parser.nextToken() != null) { - if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && + if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'err')) { parser.nextToken(); err = parser.getIntegerValue(); @@ -88,4 +88,4 @@ public class NotifierTest { System.assertEquals(0, err); } -} \ No newline at end of file +} diff --git a/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls b/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls new file mode 100644 index 0000000..c58f195 --- /dev/null +++ b/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls @@ -0,0 +1,17 @@ +@isTest +global class RollbarApiVerifyRequestCalloutMock implements HttpCalloutMock { + global HTTPResponse respond(HTTPRequest req) { + String body = req.getBody(); + Map payload = (Map) JSON.deserializeUntyped(body); + Map data = (Map)payload.get('data'); + + Map notifierMap = (Map)data.get('notifier'); + System.assertEquals(Notifier.NAME, (String)notifierMap.get('name')); + System.assertEquals(Notifier.VERSION, (String)notifierMap.get('version')); + + HttpResponse response = new HttpResponse(); + response.setStatusCode(200); + response.setBody('{"err":0,"result":{"id":null,"uuid":"e5ea9bee-08e6-41cc-a850-1863980dc224"}}'); + return response; + } +} diff --git a/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls-meta.xml b/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls-meta.xml new file mode 100644 index 0000000..252fbfd --- /dev/null +++ b/force-app/main/default/tests/RollbarApiVerifyRequestCalloutMock.cls-meta.xml @@ -0,0 +1,5 @@ + + + 47.0 + Active + diff --git a/force-app/main/default/tests/RollbarTest.cls b/force-app/main/default/tests/RollbarTest.cls index cee4e29..8c95c7e 100644 --- a/force-app/main/default/tests/RollbarTest.cls +++ b/force-app/main/default/tests/RollbarTest.cls @@ -5,17 +5,43 @@ public class RollbarTest { Test.setMock(HttpCalloutMock.class, new RollbarApiCalloutMock()); Rollbar.init('test-token', 'test-env'); - HttpResponse response = Rollbar.log('info', 'Message from the Apex SDK'); + HttpResponse response = Rollbar.log('info', 'Message from the Apex SDK', SendMethod.SYNC); System.assertEquals(200, response.getStatusCode()); } + @isTest + public static void testLogMessageFuture() { + Test.setMock(HttpCalloutMock.class, new RollbarApiVerifyRequestCalloutMock()); + + insert new RollbarSettings__c(AccessToken__c = 'test-token'); + + Test.startTest(); + Rollbar.log('info', 'Message from the Apex SDK', SendMethod.FUTURE); + Test.stopTest(); + + // Asserts valid payload in the mock. + } + + @isTest + public static void testLogMessageEvent() { + Test.setMock(HttpCalloutMock.class, new RollbarApiVerifyRequestCalloutMock()); + + insert new RollbarSettings__c(AccessToken__c = 'test-token'); + + Test.startTest(); + Rollbar.log('info', 'Message from the Apex SDK', SendMethod.EVENT); + Test.stopTest(); + + // Asserts valid payload in the mock. + } + @isTest public static void testLogException() { Test.setMock(HttpCalloutMock.class, new RollbarApiCalloutMock()); Rollbar.init('test-token', 'test-env'); - HttpResponse response = Rollbar.log(new DataBuilderTestException()); + HttpResponse response = Rollbar.log(new DataBuilderTestException(), SendMethod.SYNC); System.assertEquals(200, response.getStatusCode()); } diff --git a/force-app/main/default/triggers/EventSend.trigger b/force-app/main/default/triggers/EventSend.trigger new file mode 100644 index 0000000..0a51b3b --- /dev/null +++ b/force-app/main/default/triggers/EventSend.trigger @@ -0,0 +1,7 @@ +trigger EventSend on RollbarEvent__e (after insert) { + Notifier notifier = Rollbar.initializedInstance().notifier(); + + for (RollbarEvent__e event : Trigger.New) { + notifier.methodSend(event.Data1__c, SendMethod.FUTURE); + } +} diff --git a/force-app/main/default/triggers/EventSend.trigger-meta.xml b/force-app/main/default/triggers/EventSend.trigger-meta.xml new file mode 100644 index 0000000..4051361 --- /dev/null +++ b/force-app/main/default/triggers/EventSend.trigger-meta.xml @@ -0,0 +1,5 @@ + + + 47.0 + Active + diff --git a/manifest/package.xml b/manifest/package.xml index 0671346..408e64f 100644 --- a/manifest/package.xml +++ b/manifest/package.xml @@ -18,6 +18,7 @@ RollbarInstaller EmailServiceInstaller RollbarNotInitializedException + SendMethod ConfigTest @@ -30,6 +31,7 @@ RollbarApiCalloutMock + RollbarApiVerifyRequestCalloutMock DataBuilderTestException @@ -41,5 +43,11 @@ CustomObject RollbarSettings__c + RollbarEvent__e + + + + ApexTrigger + EventSend diff --git a/sfdx-project.json b/sfdx-project.json index 76dc617..d6c646b 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -7,5 +7,5 @@ ], "namespace": "rollbar", "sfdcLoginUrl": "https://login.salesforce.com", - "sourceApiVersion": "45.0" + "sourceApiVersion": "47.0" } From 9fddb4bb17198d9731ffe478376cd21ebab8688f Mon Sep 17 00:00:00 2001 From: Walt Jones Date: Thu, 23 Apr 2020 18:46:02 -0700 Subject: [PATCH 2/2] fix: use synchronous callout for tokan verification --- force-app/main/default/classes/RollbarInstaller.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/force-app/main/default/classes/RollbarInstaller.cls b/force-app/main/default/classes/RollbarInstaller.cls index 78da849..5c808ac 100644 --- a/force-app/main/default/classes/RollbarInstaller.cls +++ b/force-app/main/default/classes/RollbarInstaller.cls @@ -176,7 +176,7 @@ public with sharing class RollbarInstaller { public static Boolean accessTokenCorrect(Config config) { Rollbar.init(config); - System.HttpResponse response = Rollbar.log('info', 'Rollbar Apex SDK installed correctly in ' + UserInfo.getOrganizationName()); + System.HttpResponse response = Rollbar.log('info', 'Rollbar Apex SDK installed correctly in ' + UserInfo.getOrganizationName(), SendMethod.SYNC); return response.getStatusCode() == 200; }