Send Custom Notifications Using REST API
前言
個人需求
當某條記錄被創建後或者修改後,如果裏面當內容不符合要求,那麼爲了增強用戶體驗,需要給用戶發送消息提醒(Custom Notifications ),在salseforce上其實是可以使用Process Builder來發送的,也十分簡單,如果有小夥伴不清楚,可以留言,我會盡快恢復。本文章使用REST API來發送Notifications,雖然代碼量增多些,但是難免以後不會遇到相似的問題,因此留個筆記…
核心代碼
在這之前你需要知道的是怎麼才能數據肯定是以json方式發送過去,那麼customNotifition的數據格式是怎樣的呢?
{
"inputs" :
[
{
"customNotifTypeId" : "0MLB0000000PAwjOAG",
"recipientIds" : ["005B0000005lewTIAQ"],
"title" : "opportunity Closed!",
"body" : "Your Opportunity has been Closed.",
"targetId" : "006B00000058gHEIAY"
}
]
}
1、Get Resource From Org Use HttpRequest
/*
*Get Resource From Org Use HttpRequest
*/
public class OrgREST {// inherited sharing
/*
*get resource
*/
public static String retriveResult(String strResourceName, String strMethod, String strBody) {
String response;
String strEndPonitURL = URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v46.0/' + strResourceName;
if(String.isNotBlank(strResourceName)) {
HttpRequest httpRequest = new HttpRequest();
httpRequest.setMethod(strMethod);
httpRequest.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());
httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
if(String.isNotBlank(strBody)) {
httpRequest.setBody(strBody);
httpRequest.setHeader('Content-Type', 'application/json');
httpRequest.setHeader('Accept', 'application/json');
}
httpRequest.setEndpoint(strEndPonitURL);
try {
Http http = new Http();
HttpResponse httpResponse = http.send(httpRequest);
if (httpResponse.getStatusCode() == 200 ) {
response = httpResponse.getBody();
}
else {
throw new CalloutException(httpResponse.getBody());
}
}
catch(Exception ex) {
throw ex;
}
}
return response;
}
}
使用該class裏面的retriveResult方法,你可以使用Get方式來獲取你想要的資源,使用POST向服務器發送消息
2、Send CustomNotificationRESTAPI Use Last Code
public class CustomNotificationRESTAPI {//inherited sharing
public static void (String strNotifiTypeDevName, list<sObject> lstObjects, list<Id> lstRecipients) {
//List<sObject> lstObjects = [select Id from User where Id IN:lstObjectsId];
System.debug('lstObjects' + lstObjects);
String strResponse = null;
System.debug(strNotifiTypeDevName + ':' + strNotifiTypeDevName + ':' + lstRecipients);
if(String.isNotBlank(strNotifiTypeDevName)) {
// Tooling API query
String strResourceQuery = 'tooling/query/?q=select+id,customNotifTypeName+from+CustomNotificationType+where+DeveloperName=\''+ strNotifiTypeDevName +'\'';
// OrgREST Custom Class returns the response as string
strResponse = OrgREST.retriveResult(strResourceQuery, 'GET', null);
System.debug('strResponse:' + strResponse);
//get CustomNotificationType
}
map<String, Object> objData;
if(String.isNotBlank(strResponse)) {
// Deserializeing the response body
map<String, Object> mapJsonData = (map<String, Object>)JSON.deserializeUntyped(strResponse);
// Getting records
list<Object> lstRecordsData = (list<Object>)mapJsonData.get('records');
if(!lstRecordsData.isEmpty()) {
// Converting each object to readable map to get the data
objData = (map<String, Object>)lstRecordsData[0];
}
System.debug('Id ====> '+(String)objData.get('Id'));
}
/*
* Generating JSON body.
* Below code work for bulk record process.
* Recipients are User, Owner, Group or Queue Ids
*/
if(!lstObjects.isEmpty() && !lstRecipients.isEmpty()) {
JSONGenerator jsGen = JSON.createGenerator(true);
jsGen.writeStartObject();
jsGen.writeFieldName('inputs');
jsGen.writeStartArray();
for(sObject obj : lstObjects) {
jsGen.writeStartObject();
// Custom Notification type id
jsGen.writeStringField('customNotifTypeId', (String)objData.get('Id'));
jsGen.writeFieldName('recipientIds');
jsGen.writeStartArray();
// adding mulitiple recipients if any
for(Id idRecipient : lstRecipients) {
jsGen.writeId(idRecipient);
}
jsGen.writeEndArray();
// Notification titile
jsGen.writeStringField('title','Time Hour Contains Taboo');
// Notification body
jsGen.writeStringField('body', 'Your Time Hour Contains Taboo!');
jsGen.writeStringField('targetId', (String)obj.get('Id'));
jsGen.writeEndObject();
}
jsGen.writeEndArray();
jsGen.writeEndObject();
// Converting JSON as String
String strJson = jsGen.getAsString();
System.debug('strJson ====> '+strJson);
// POST URL to send custom notification
String strPOSTURL = 'actions/standard/customNotificationAction';
// Sending post request to send custom notification
String strPostResponse = OrgREST.retriveResult(strPOSTURL, 'POST', strJson);
}
}
}
這裏相當於就是先通過GET方式去獲取你要的那一個消息提醒(參數strNotifiTypeDevName就是你自定義custom Notification的label name),然後封裝你的POST數據,最後還是調用上一個Class中的方法去發送數據到你的salesfroce服務器
注意事項
1、Tigger不能直接調用REST API
前言也說了,發送消息提醒的話一般是處於某種條件下,所以Process Builder和Trigger就經常會遇到類似的問題。但是如果你直接在你的Trigger裏面調用REST API的話你會遇到:System.CalloutException: Callout from triggers are currently not supported
意思就是現在還不支持在Trigger裏面直接調用REST API!!
So,怎麼辦!答案就是你必須將調用REST API的操作放到另一個事務中
trigger TimeHourNotificationTrigger on TimeHour__c (after insert) {
for(TimeHour__c selfTimeHour : Trigger.New){
String timeBrief = selfTimeHour.Brief__c;
//List<Id> lstObjectsId = new List<Id>();
//lstObjectsId.add(selfTimeHour.User__c);
List<sObject> lstObjects = new List<sObject>();
lstObjects.add(selfTimeHour);
if(timeBrief.containsIgnoreCase('bug') == true || timeBrief.containsIgnoreCase('bugs') == true || timeBrief.containsIgnoreCase('internal') == true){
System.debug('begin');
//solution:1 CustomNotificationCallout.sendCustomNotification('Time_Hour_Notification', lstObjectsId, new list<Id>{UserInfo.getUserId()});
//solution:1 System.enqueueJob(new CustomNotificationCallout('Time_Hour_Notification', lstObjects, new list<Id>{UserInfo.getUserId()}));
System.debug('end');
}
}
solution 1 :在Future裏面調用REST API
但是Future有個很不好的地方就是參數只能是原始類型的不能接受對象,所以你只能傳ID然後再查詢一邊,個人推薦實現Queueable Job
solution 2 : 實現Queueable Job
下面的代碼是使用Queueable來做的,另一種方法被註釋調,如有需要就請自己動手改一改哈。當然別忘了如果用future的話CustomNotificationRESTAPI.sendCustomNotification那個方法的參數和裏面還有以及查詢也要改喲。
public with sharing class CustomNotificationCallout implements Queueable,Database.AllowsCallouts {//
public String strNotifiTypeDevName;
//public list<Id> lstObjectsId;
List<sObject> lstObjects = new List<sObject>();
public list<Id> lstRecipients;
public CustomNotificationCallout(String strNotifiTypeDevName, List<sObject> lstObjects, list<Id> lstRecipients){
this.strNotifiTypeDevName = strNotifiTypeDevName;
//this.lstObjectsId = lstObjectsId;
this.lstObjects = lstObjects;
this.lstRecipients = lstRecipients;
System.debug('constror');
}
public void execute(QueueableContext context) {
// Awesome processing logic here
// Chain this job to next job by submitting the next job
CustomNotificationRESTAPI.sendCustomNotification(strNotifiTypeDevName, lstObjects, lstRecipients);
}
/*
@future(callout=true)
public static void sendCustomNotification(String strNotifiTypeDevName, list<Id> lstObjectsId, list<Id> lstRecipients){
System.debug('future');
CustomNotificationRESTAPI.sendCustomNotification(strNotifiTypeDevName, lstObjectsId, lstRecipients);
System.debug(' end future');
} */
}
2、使用Queueable必須實現(implement Database.AllowsCallouts)
如果你的classpublic with sharing class CustomNotificationCallout implements Queueable,Database.AllowsCallouts
只實現了Queueable的話是代碼、運行都不會報錯,但是是無法發送消息通知的,就像它本身就代表着是否允許Callouts.
PS :如有敘述不清或者錯誤的地方,懇請指正,留言交流,謝謝!
附上外文網站:https://www.salesforcecodecrack.com/2019/06/send-custom-notifications-using-rest-api.html