-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: onboard pagerduty destination (#1736)
* feat: onboard pagerduty destination * feat: settings.json file removed * feat: desttype change to processor * fix: code review changes
- Loading branch information
1 parent
424bce9
commit a947b10
Showing
10 changed files
with
1,286 additions
and
14 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const BASE_ENDPOINT = "https://events.pagerduty.com/v2"; | ||
|
||
const { getMappingConfig } = require("../../util"); | ||
|
||
const CONFIG_CATEGORIES = { | ||
ALERT_EVENT: { | ||
name: "pagerdutyAlertEventConfig", | ||
type: "track", | ||
endpoint: `${BASE_ENDPOINT}/enqueue` | ||
}, | ||
CHANGE_EVENT: { | ||
name: "pagerdutyChangeEventConfig", | ||
type: "track", | ||
endpoint: `${BASE_ENDPOINT}/change/enqueue` | ||
} | ||
}; | ||
|
||
const EVENT_ACTIONS = ["trigger", "acknowledge", "resolve"]; | ||
const DEFAULT_EVENT_ACTION = "trigger"; | ||
|
||
const SEVERITIES = ["critical", "warning", "error", "info"]; | ||
const DEFAULT_SEVERITY = "critical"; | ||
|
||
const MAPPING_CONFIG = getMappingConfig(CONFIG_CATEGORIES, __dirname); | ||
|
||
module.exports = { | ||
SEVERITIES, | ||
BASE_ENDPOINT, | ||
EVENT_ACTIONS, | ||
MAPPING_CONFIG, | ||
DEFAULT_SEVERITY, | ||
CONFIG_CATEGORIES, | ||
DEFAULT_EVENT_ACTION | ||
}; |
74 changes: 74 additions & 0 deletions
74
src/v0/destinations/pagerduty/data/pagerdutyAlertEventConfig.json
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,74 @@ | ||
[ | ||
{ | ||
"destKey": "payload.summary", | ||
"sourceKeys": "event", | ||
"required": true | ||
}, | ||
{ | ||
"destKey": "payload.timestamp", | ||
"sourceKeys": "timestamp", | ||
"sourceFromGenericMap": true, | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.severity", | ||
"sourceKeys": "properties.severity", | ||
"metadata": { | ||
"defaultValue": "critical" | ||
}, | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.source", | ||
"sourceKeys": "properties.source", | ||
"required": true | ||
}, | ||
{ | ||
"destKey": "event_action", | ||
"sourceKeys": "properties.action", | ||
"metadata": { | ||
"defaultValue": "trigger" | ||
}, | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "links", | ||
"sourceKeys": "properties.linkURLs", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "images", | ||
"sourceKeys": "properties.imageURLs", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.group", | ||
"sourceKeys": "properties.group", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.class", | ||
"sourceKeys": "properties.class", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.component", | ||
"sourceKeys": "properties.component", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "client", | ||
"sourceKeys": "properties.client", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "client_url", | ||
"sourceKeys": "properties.clientUrl", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.custom_details", | ||
"sourceKeys": "properties.customDetails", | ||
"required": false | ||
} | ||
] |
33 changes: 33 additions & 0 deletions
33
src/v0/destinations/pagerduty/data/pagerdutyChangeEventConfig.json
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,33 @@ | ||
[ | ||
{ | ||
"destKey": "payload.summary", | ||
"sourceKeys": "event", | ||
"required": true | ||
}, | ||
{ | ||
"destKey": "payload.timestamp", | ||
"sourceKeys": "timestamp", | ||
"sourceFromGenericMap": true, | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.source", | ||
"sourceKeys": "properties.source", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "payload.custom_details", | ||
"sourceKeys": "properties.customDetails", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "links", | ||
"sourceKeys": "properties.linkURLs", | ||
"required": false | ||
}, | ||
{ | ||
"destKey": "images", | ||
"sourceKeys": "properties.imageURLs", | ||
"required": false | ||
} | ||
] |
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,66 @@ | ||
const { EventType } = require("../../../constants"); | ||
const { | ||
defaultRequestConfig, | ||
simpleProcessRouterDest, | ||
defaultPostRequestConfig, | ||
removeUndefinedAndNullValues | ||
} = require("../../util"); | ||
const { | ||
ConfigurationError, | ||
TransformationError, | ||
InstrumentationError | ||
} = require("../../util/errorTypes"); | ||
const { trackEventPayloadBuilder } = require("./util"); | ||
|
||
const responseBuilder = (payload, endpoint) => { | ||
if (payload) { | ||
const response = defaultRequestConfig(); | ||
response.endpoint = endpoint; | ||
response.headers = { | ||
"Content-Type": "application/json" | ||
}; | ||
response.method = defaultPostRequestConfig.requestMethod; | ||
response.body.JSON = removeUndefinedAndNullValues(payload); | ||
return response; | ||
} | ||
// fail-safety for developer error | ||
throw new TransformationError( | ||
"Something went wrong while constructing the payload" | ||
); | ||
}; | ||
|
||
const trackResponseBuilder = (message, Config) => { | ||
if (!message.event) { | ||
throw new InstrumentationError("Event name is required"); | ||
} | ||
const builder = trackEventPayloadBuilder(message, Config); | ||
const { payload, endpoint } = builder; | ||
return responseBuilder(payload, endpoint); | ||
}; | ||
|
||
const process = event => { | ||
const { message, destination } = event; | ||
const { Config } = destination; | ||
|
||
if (!Config.routingKey) { | ||
throw new ConfigurationError("Routing Key Is Required"); | ||
} | ||
|
||
if (!message.type) { | ||
throw new InstrumentationError("Event type is required"); | ||
} | ||
const messageType = message.type.toLowerCase(); | ||
|
||
if (messageType === EventType.TRACK) { | ||
return trackResponseBuilder(message, Config); | ||
} | ||
|
||
throw new InstrumentationError(`Event type ${messageType} is not supported`); | ||
}; | ||
|
||
const processRouterDest = async (inputs, reqMetadata) => { | ||
const respList = await simpleProcessRouterDest(inputs, process, reqMetadata); | ||
return respList; | ||
}; | ||
|
||
module.exports = { process, processRouterDest }; |
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,172 @@ | ||
const get = require("get-value"); | ||
const moment = require("moment"); | ||
const { | ||
SEVERITIES, | ||
EVENT_ACTIONS, | ||
MAPPING_CONFIG, | ||
DEFAULT_SEVERITY, | ||
CONFIG_CATEGORIES, | ||
DEFAULT_EVENT_ACTION | ||
} = require("./config"); | ||
const { constructPayload, getIntegrationsObj } = require("../../util"); | ||
const { InstrumentationError } = require("../../util/errorTypes"); | ||
|
||
/** | ||
* Validates the timestamp | ||
* @param {*} payload | ||
* @returns | ||
*/ | ||
const validateTimeStamp = payload => { | ||
if (payload.payload?.timestamp) { | ||
const start = moment.unix(moment(payload.payload.timestamp).format("X")); | ||
const current = moment.unix(moment().format("X")); | ||
// calculates past event in hours | ||
const deltaDay = Math.ceil(moment.duration(current.diff(start)).asHours()); | ||
if (deltaDay > 90) { | ||
throw new InstrumentationError( | ||
"Events must be sent within ninety days of their occurrence" | ||
); | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Returns the valid links array | ||
* @param {*} links | ||
* @returns | ||
*/ | ||
const getValidLinks = links => { | ||
if (typeof links === "string") { | ||
return [{ href: links }]; | ||
} | ||
// Removing the objects where href key is not present | ||
return links.filter(link => link.href); | ||
}; | ||
|
||
/** | ||
* Returns the valid images array | ||
* @param {*} images | ||
* @returns | ||
*/ | ||
const getValidImages = images => { | ||
if (typeof images === "string") { | ||
return [{ src: images }]; | ||
} | ||
// Removing the objects where src key is not present | ||
return images.filter(image => image.src); | ||
}; | ||
|
||
/** | ||
* Returns the valid severity value | ||
* @param {*} payload | ||
* @returns | ||
*/ | ||
const getValidSeverity = payload => { | ||
if ( | ||
payload.payload?.severity && | ||
SEVERITIES.includes(payload.payload.severity) | ||
) { | ||
return payload.payload.severity; | ||
} | ||
// If severity is not found in payload then fallback to default value which is "critical" | ||
return DEFAULT_SEVERITY; | ||
}; | ||
|
||
/** | ||
* Returns the valid eventAction value | ||
* @param {*} message | ||
* @returns | ||
*/ | ||
const getValidEventAction = message => { | ||
const eventAction = get(message.properties, "action"); | ||
if (eventAction && EVENT_ACTIONS.includes(eventAction)) { | ||
return eventAction; | ||
} | ||
// If action is not found in payload then fallback to default value which is "trigger" | ||
return DEFAULT_EVENT_ACTION; | ||
}; | ||
|
||
/** | ||
* Returns the alert event payload | ||
* Ref: https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgx-send-an-alert-event | ||
* @param {*} message | ||
* @param {*} Config | ||
* @returns | ||
*/ | ||
const prepareAlertEventPayload = (message, Config) => { | ||
const eventAction = getValidEventAction(message); | ||
|
||
let dedupKey; | ||
if (Config.dedupKeyFieldIdentifier) { | ||
dedupKey = get(message, Config.dedupKeyFieldIdentifier); | ||
} | ||
|
||
if (eventAction === "acknowledge" || eventAction === "resolve") { | ||
// dedup_key is required if you want to acknowledge or resolve an incident | ||
if (!dedupKey) { | ||
throw new InstrumentationError( | ||
`dedup_key required for ${eventAction} events` | ||
); | ||
} | ||
|
||
return { | ||
dedup_key: dedupKey, | ||
event_action: eventAction | ||
}; | ||
} | ||
|
||
const payload = constructPayload( | ||
message, | ||
MAPPING_CONFIG[CONFIG_CATEGORIES.ALERT_EVENT.name] | ||
); | ||
|
||
// If dedup_key is not found when incident is triggered then fallback to messageId | ||
payload.dedup_key = dedupKey || message.messageId; | ||
payload.event_action = eventAction; | ||
payload.payload.severity = getValidSeverity(payload); | ||
|
||
return payload; | ||
}; | ||
|
||
/** | ||
* Returns the track call payload | ||
* @param {*} message | ||
* @param {*} Config | ||
* @returns | ||
*/ | ||
const trackEventPayloadBuilder = (message, Config) => { | ||
const integrationsObj = getIntegrationsObj(message, "pagerduty"); | ||
|
||
let payload; | ||
let endpoint; | ||
|
||
if ( | ||
integrationsObj && | ||
integrationsObj.type && | ||
integrationsObj.type === "changeEvent" | ||
) { | ||
payload = constructPayload( | ||
message, | ||
MAPPING_CONFIG[CONFIG_CATEGORIES.CHANGE_EVENT.name] | ||
); | ||
endpoint = CONFIG_CATEGORIES.CHANGE_EVENT.endpoint; | ||
} else { | ||
payload = prepareAlertEventPayload(message, Config); | ||
endpoint = CONFIG_CATEGORIES.ALERT_EVENT.endpoint; | ||
} | ||
|
||
validateTimeStamp(payload); | ||
if (payload.links) { | ||
payload.links = getValidLinks(payload.links); | ||
} | ||
if (payload.images) { | ||
payload.images = getValidImages(payload.images); | ||
} | ||
const { routingKey } = Config; | ||
payload.routing_key = routingKey; | ||
return { payload, endpoint }; | ||
}; | ||
|
||
module.exports = { | ||
trackEventPayloadBuilder | ||
}; |
Oops, something went wrong.