Use Case: When a assigned Sev 1 / Sev 2 Case for the assignment groups say
Test-INTEL,Test-CORE-99, SLA resolution and response is crossed by 50% and 75% and breached , we have to get notified on slack channel #test-core-client-internal with below message
Example - Ticket number - Customer - Short Description - Response SLA clock is ticking(“%”SLA already Crossed) on this “Severity” ticket in “Engineer’s Name” name. Please address quickly.
Solution
Steps:
We have to get the webhook of the Slack channel where we want to post.
Webhook looks like below:
https://hooks.slack.com/services/AC3R7L2XY/U01ECPSG484/39XLtLLqtXtOEQMNpjEZJlXI
We have an OOTB workflow “Default SLA workflow” that emits two events:
- sla.warning
- sla.warning.breach
From the above workflow we can observe that when sla reaches 50% and 75% “sla.warning” events are getting triggered. Also when SLA breaches then “sla.warning.breach” is getting triggered.
We are going to listen to these events and write two script actions to send notification is slack channel.
Script Action
Slack Notif-SLA-50/75-Core/Client:
Send notification to slack when sla reaches 50% and 75%
For 50% and 75% ,sla are using same event(sla.warning) so that we are create one script action for 50% and 75%
//gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);
var isPlainTextRequired = false ;
var emoji = " stopwatch " ;
var record = getCaseDetails ( current . task );
var thresholdTolerance = 10 ;
var targetType = current . sla . getDisplayValue (). split ( " - " )[ 0 ];
var slackLinkByGroup = {};
//Slack-Group Mapping
slackLinkByGroup [ ' TEST-CORE-SERVICES-INTERNAL ' ] = " https://hooks.slack.com/services/Tdc3fghjGM/A0fhbnjVDUG/yCFNpZCdfdd8MJIuerdf " ;
slackLinkByGroup [ ' TEST-CORE-CLIENT-INTERNAL ' ] = " https://hooks.slack.com/services/erdfgresrM2GM/B01FCfgh4666/44cvbgfdrertztOEQEYtyghff " ;
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TC3R7M2GM/yuhjgfde0B7JB/LZopuiylkZ5nJsshM";
var coreServiceGroups = [ " TEST-CORE-DATABASE " , " TEST-CORE-Backup " ];
var coreClientGroups = [ " TEST-CORE-UNIX " , " TEST-CORE-INTEL " ];
var priorityList = [ " 1 " , " 2 " ];
var allowedInstances = [ " https://dev456.service-now.com/ " ];
var instanceURL = gs . getProperty ( ' glide.servlet.uri ' );
if ( record . next ()) {
var threshold = calculatedThreshold ( parseInt ( current . percentage ));
if ( isPriorityAllowed ( record . priority . toString ()) &&
! isProcessed ( threshold , current . sys_id ) &&
isAllowedInstance () &&
current . stage == " in_progress " ) {
var colorCode = evaluateColorCode ( threshold );
var thresholdText = threshold != 0 ? threshold + " % SLA already Crossed " : " Reopened " ;
var assignedTo = record . assigned_to . getDisplayValue () == "" ? " Not Assigned " : record . assigned_to . getDisplayValue ();
var headingText = targetType + " SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record . priority + " ticket. " + " Assignee - " + assignedTo + " . Please address quickly. " ;
var ticketNumber = record . number ;
var customer = record . account . getDisplayValue ();
var shortDescription = record . short_description ;
var incidentLink = instanceURL + " sn_customerservice_case.do?sys_id= " + record . sys_id ;
var slacklink = evaluateSlackLink ( record . assignment_group . getDisplayValue ());
var fallBackText = ticketNumber + " - " + customer + " - " + shortDescription + " - " + headingText ;
var plainTextMessage = " { \" text \" : \" " + fallBackText + " \" } " ;
var assignmentGroup = record . assignment_group . getDisplayValue ();
var template = " { \" attachments \" :[ " ;
template = template + " { \" fallback \" : \" %fallBackText% \" , " ;
template = template + " \" color \" : \" %colorCode% \" , " ;
template = template + " \" fields \" : [ " ;
template = template + " { \" title \" : \" :%emoji%: %headingText% \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Ticket number : `%ticketNumber%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Customer : `%customer%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Assignment Group : `%assignmentGroup%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Short Description: `%shortDescription%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" _Click here to view it_ : <%incidentLink%|*incident*> \" , \" short \" : false } " ;
template = template + " ]} " ;
template = template + " ]} " ;
template = template . replace ( " %fallBackText% " , fallBackText );
template = template . replace ( " %colorCode% " , colorCode );
template = template . replace ( " %headingText% " , headingText );
template = template . replace ( " %ticketNumber% " , ticketNumber );
template = template . replace ( " %customer% " , customer );
template = template . replace ( " %assignmentGroup% " , assignmentGroup );
template = template . replace ( " %shortDescription% " , shortDescription );
template = template . replace ( " %incidentLink% " , incidentLink );
template = template . replace ( " %emoji% " , emoji );
var finalMessage = isPlainTextRequired ? plainTextMessage : template ;
try {
//Used to prevent duplicate message getting sent for 100%, please dont remove
gs . log ( " Threshold for SLA --> " + threshold + ' - ' + current . sys_id , threshold + " - " + current . sys_id );
var request = new sn_ws . RESTMessageV2 ();
request . setHttpMethod ( ' post ' );
request . setRequestBody ( finalMessage );
request . setEndpoint ( slacklink );
var response = request . execute ();
} catch ( ex ) {
//Nothing to do
}
}
}
function getCaseDetails ( caseSysId ) {
var record = new GlideRecord ( ' sn_customerservice_case ' );
record . addQuery ( ' sys_id ' , caseSysId );
record . query ();
return record ;
}
function isProcessed ( threshold , taskId ) {
var taskList = new GlideRecord ( ' syslog ' );
taskList . addQuery ( ' source ' , threshold + ' - ' + taskId );
taskList . addEncodedQuery ( " sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes() " );
taskList . query ();
if ( taskList . next ()) {
return true ;
} else {
return false ;
}
}
function calculatedThreshold ( percentage ) {
if ( percentage >= 50 && percentage < ( 50 + thresholdTolerance ))
return 50 ;
else if ( percentage >= 75 && percentage < ( 75 + thresholdTolerance ))
return 75 ;
else if ( percentage >= 100 && percentage < ( 100 + thresholdTolerance ))
return 100 ;
//Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
else
return 0 ;
}
function isEventForManager ( eventParam2 ) {
return eventParam2 == " manager " ;
}
function evaluateColorCode ( threshold ) {
/**
#F1C40F --> yellow
#E67E22 --> orange
#A93226 --> red
**/
var code = "" ;
switch ( threshold ) {
case 50 :
code = " #F1C40F " ;
break ;
case 75 :
code = " #E67E22 " ;
break ;
default :
code = " #A93226 " ;
}
return code ;
}
function evaluateSlackLink ( groupName ) {
if ( coreServiceGroups . indexOf ( groupName ) !== - 1 ) {
return slackLinkByGroup [ ' TEST-CORE-SERVICES-INTERNAL ' ];
}
if ( coreClientGroups . indexOf ( groupName ) !== - 1 ) {
return slackLinkByGroup [ ' TEST-CORE-CLIENT-INTERNAL ' ];
}
}
function isPriorityAllowed ( priority ) {
return priorityList . indexOf ( priority ) !== - 1 ;
}
function isAllowedInstance () {
return allowedInstances . indexOf ( instanceURL ) !== - 1 ;
}
Script Action
Slack Notif-SLA Breach-Core/Client
Send notification to slack when sla reaches 100%
For 100%, Sla are using different event so we are creating different script action.
//gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);
//Fallback to plain text if rich text format doesnt support
var isPlainTextRequired = false ;
//Emoji icon to use.This text needs to be compatible with Slack emojis
var emoji = " stopwatch " ;
//Get case details from task sla
var record = getCaseDetails ( current . task );
// This tolerance will define the range to take floor value from the range of values
// For example - percentage is 53.6 then assume 50.
var thresholdTolerance = 10 ;
//Extracts SLA type response/resolution from sla name
var targetType = current . sla . getDisplayValue (). split ( " - " )[ 0 ];
//Captures event paramter 2
var eventParam2 = event . parm2 ;
//List of all slack webhooks.Add new entry below and map it to a name
var slackLinkByGroup = {};
//Slack-Group Mapping
slackLinkByGroup [ ' TEST-CORE-SERVICES-INTERNAL ' ] = " https://hooks.slack.com/services/DE4567YHJK2GM/DSF54RTHG/yFDSERTWS56TG " ;
slackLinkByGroup [ ' TEST-CORE-CLIENT-INTERNAL ' ] = " https://hooks.slack.com/services/GHTR567EDHGF/39YBtCYqYUIOJK895GHFD " ;
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TCfdfghjk01ENVIKIKJ/JH4567YHJGHRTS";
//List of Groups.The releationship of these groups to slack link is done in evaluateSlackLink function
var coreServiceGroups = [ " TEST-CORE-DATABASE " , " TEST-CORE-Backup " ];
var coreClientGroups = [ " TEST-CORE-UNIX " , " TEST-CORE-INTEL " ];
var priorityList = [ " 1 " , " 2 " ];
var allowedInstances = [ " https://dev567.service-now.com/ " ];
var instanceURL = gs . getProperty ( ' glide.servlet.uri ' );
if ( record . next ()) {
//Calculate the threshold based on the current percentage
var threshold = calculatedThreshold ( parseInt ( current . percentage ));
var thresholdText = threshold != 0 ? threshold + " % SLA already Crossed " : " Reopened " ;
//Send message only if the event is not fired for a manager and is not already send and with allowed priority.
if ( ! isEventForManager ( eventParam2 ) &&
! isProcessed ( threshold , current . sys_id ) &&
isPriorityAllowed ( record . priority . toString ()) &&
isAllowedInstance () &&
current . stage == " in_progress " ) {
//Determines the colorcode based on threshold
var colorCode = evaluateColorCode ( threshold );
//All below variables are constructed before applied to the template
var assignedTo = record . assigned_to . getDisplayValue () == "" ? " Not Assigned " : record . assigned_to . getDisplayValue ();
var headingText = targetType + " SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record . priority + " ticket. " + " Assignee - " + assignedTo + " . Please address quickly. " ;
var ticketNumber = record . number ;
var customer = record . account . getDisplayValue ();
var shortDescription = record . short_description ;
var incidentLink = instanceURL + " sn_customerservice_case.do?sys_id= " + record . sys_id ;
var slacklink = evaluateSlackLink ( record . assignment_group . getDisplayValue ());
var fallBackText = ticketNumber + " - " + customer + " - " + shortDescription + " - " + headingText ;
var plainTextMessage = " { \" text \" : \" " + fallBackText + " \" } " ;
var assignmentGroup = record . assignment_group . getDisplayValue ();
//The template for slack message
var template = " { \" attachments \" :[ " ;
template = template + " { \" fallback \" : \" %fallBackText% \" , " ;
template = template + " \" color \" : \" %colorCode% \" , " ;
template = template + " \" fields \" : [ " ;
template = template + " { \" title \" : \" :%emoji%: %headingText% \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Ticket number : `%ticketNumber%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Customer : `%customer%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Assignment Group : `%assignmentGroup%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" Short Description: `%shortDescription%` \" , \" short \" : false }, " ;
template = template + " { \" value \" : \" _Click here to view it_ : <%incidentLink%|*incident*> \" , \" short \" : false } " ;
template = template + " ]} " ;
template = template + " ]} " ;
//Fill all the values in the template
template = template . replace ( " %fallBackText% " , fallBackText );
template = template . replace ( " %colorCode% " , colorCode );
template = template . replace ( " %headingText% " , headingText );
template = template . replace ( " %ticketNumber% " , ticketNumber );
template = template . replace ( " %customer% " , customer );
template = template . replace ( " %assignmentGroup% " , assignmentGroup );
template = template . replace ( " %shortDescription% " , shortDescription );
template = template . replace ( " %incidentLink% " , incidentLink );
template = template . replace ( " %emoji% " , emoji );
var finalMessage = isPlainTextRequired ? plainTextMessage : template ;
try {
//Used to prevent duplicate message getting sent for 100%, please dont remove
gs . log ( " Threshold for SLA --> " + threshold + ' - ' + current . sys_id , threshold + " - " + current . sys_id );
//Send message to slack
var request = new sn_ws . RESTMessageV2 ();
request . setHttpMethod ( ' post ' );
request . setRequestBody ( finalMessage );
request . setEndpoint ( slacklink );
var response = request . execute ();
} catch ( ex ) {
//Nothing to do
}
}
}
function getCaseDetails ( caseSysId ) {
var record = new GlideRecord ( ' sn_customerservice_case ' );
record . addQuery ( ' sys_id ' , caseSysId );
record . query ();
return record ;
}
function isProcessed ( threshold , taskId ) {
var taskList = new GlideRecord ( ' syslog ' );
taskList . addQuery ( ' source ' , threshold + ' - ' + taskId );
taskList . addEncodedQuery ( " sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes() " );
taskList . query ();
if ( taskList . next ()) {
return true ;
} else {
return false ;
}
}
function calculatedThreshold ( percentage ) {
if ( percentage >= 50 && percentage < ( 50 + thresholdTolerance ))
return 50 ;
else if ( percentage >= 75 && percentage < ( 75 + thresholdTolerance ))
return 75 ;
else if ( percentage >= 100 && percentage < ( 100 + thresholdTolerance ))
return 100 ;
//Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
else
return 0 ;
}
function isEventForManager ( eventParam2 ) {
return eventParam2 == " manager " ;
}
function evaluateColorCode ( threshold ) {
/**
#F1C40F --> yellow
#E67E22 --> orange
#A93226 --> red
**/
var code = "" ;
switch ( threshold ) {
case 50 :
code = " #F1C40F " ;
break ;
case 75 :
code = " #E67E22 " ;
break ;
default :
code = " #A93226 " ;
}
return code ;
}
function evaluateSlackLink ( groupName ) {
if ( coreServiceGroups . indexOf ( groupName ) !== - 1 ) {
return slackLinkByGroup [ ' TEST-CORE-SERVICES-INTERNAL ' ];
}
if ( coreClientGroups . indexOf ( groupName ) !== - 1 ) {
return slackLinkByGroup [ ' TEST-CORE-CLIENT-INTERNAL ' ];
}
}
function isPriorityAllowed ( priority ) {
return priorityList . indexOf ( priority ) !== - 1 ;
}
function isAllowedInstance () {
return allowedInstances . indexOf ( instanceURL ) !== - 1 ;
}
Comments