pager_tree-integrations 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/pager_tree/integrations/live_call_routing/twilio/v3_controller.rb +20 -0
  3. data/app/jobs/pager_tree/integrations/live_call_routing/twilio/v3/max_wait_time_job.rb +13 -0
  4. data/app/models/pager_tree/integrations/additional_datum.rb +2 -2
  5. data/app/models/pager_tree/integrations/alert.rb +2 -2
  6. data/app/models/pager_tree/integrations/aws_cloudwatch/v3.rb +15 -5
  7. data/app/models/pager_tree/integrations/boolean_store_accessor.rb +2 -2
  8. data/app/models/pager_tree/integrations/channel/hangouts/v3.rb +23 -2
  9. data/app/models/pager_tree/integrations/channel/mattermost/v3.rb +4 -0
  10. data/app/models/pager_tree/integrations/channel/microsoft_teams/v3.rb +74 -29
  11. data/app/models/pager_tree/integrations/channel/microsoft_teams/v4.rb +261 -0
  12. data/app/models/pager_tree/integrations/channel/slack/v3.rb +30 -3
  13. data/app/models/pager_tree/integrations/cloudflare/v3.rb +75 -0
  14. data/app/models/pager_tree/integrations/custom_webhook/v3.rb +206 -0
  15. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/datadog.json +12 -0
  16. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/datadog.yaml +30 -0
  17. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/generic_rules_definition.json +9 -0
  18. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/generic_rules_example.yaml +27 -0
  19. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/grafana.json +25 -0
  20. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/grafana.yaml +33 -0
  21. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/new_relic.json +40 -0
  22. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/new_relic.yaml +32 -0
  23. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/pingdom.json +30 -0
  24. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/pingdom.yaml +23 -0
  25. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_cloudwatch.json +12 -0
  26. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_cloudwatch.yaml +48 -0
  27. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_health_event.json +21 -0
  28. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_health_event.yaml +43 -0
  29. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_lambda_monitor.json +31 -0
  30. data/app/models/pager_tree/integrations/custom_webhook/v3_examples/sns_lambda_monitor.yaml +24 -0
  31. data/app/models/pager_tree/integrations/datadog/v3.rb +10 -3
  32. data/app/models/pager_tree/integrations/dynatrace/v3.rb +67 -0
  33. data/app/models/pager_tree/integrations/echoes_hq/v3.rb +138 -0
  34. data/app/models/pager_tree/integrations/elast_alert/v3.rb +24 -0
  35. data/app/models/pager_tree/integrations/email/v3.rb +272 -22
  36. data/app/models/pager_tree/integrations/email/v3_examples/example.yml +24 -0
  37. data/app/models/pager_tree/integrations/form/v3.rb +15 -3
  38. data/app/models/pager_tree/integrations/freshdesk/v3.rb +7 -5
  39. data/app/models/pager_tree/integrations/freshservice/v3.rb +7 -5
  40. data/app/models/pager_tree/integrations/grafana/v3.rb +2 -2
  41. data/app/models/pager_tree/integrations/hetrix_tools/v3.rb +160 -0
  42. data/app/models/pager_tree/integrations/honeybadger/v3.rb +1 -1
  43. data/app/models/pager_tree/integrations/hydrozen/v3.rb +6 -0
  44. data/app/models/pager_tree/integrations/integration.rb +52 -9
  45. data/app/models/pager_tree/integrations/jira_server/v3.rb +12 -2
  46. data/app/models/pager_tree/integrations/kapacitor/v3.rb +1 -1
  47. data/app/models/pager_tree/integrations/live_call_routing/twilio/v3.rb +257 -22
  48. data/app/models/pager_tree/integrations/logic_monitor/v3.rb +193 -3
  49. data/app/models/pager_tree/integrations/mattermost/outgoing_webhook/v3.rb +1 -1
  50. data/app/models/pager_tree/integrations/meta/workplace/v3.rb +230 -0
  51. data/app/models/pager_tree/integrations/new_relic/v3.rb +9 -5
  52. data/app/models/pager_tree/integrations/outgoing_event.rb +8 -0
  53. data/app/models/pager_tree/integrations/outgoing_webhook/v3.rb +108 -13
  54. data/app/models/pager_tree/integrations/outgoing_webhook_delivery/hook_relay.rb +43 -33
  55. data/app/models/pager_tree/integrations/outgoing_webhook_delivery.rb +6 -5
  56. data/app/models/pager_tree/integrations/prtg/v3.rb +2 -1
  57. data/app/models/pager_tree/integrations/pulsetic/v3.rb +70 -0
  58. data/app/models/pager_tree/integrations/scom/pagertree.ps1 +153 -0
  59. data/app/models/pager_tree/integrations/scom/v3.rb +58 -0
  60. data/app/models/pager_tree/integrations/sentry/v3.rb +477 -0
  61. data/app/models/pager_tree/integrations/server_guard24/v3.rb +1 -1
  62. data/app/models/pager_tree/integrations/sixty_six_uptime/v3.rb +118 -0
  63. data/app/models/pager_tree/integrations/slack/webhook/v3.rb +1 -1
  64. data/app/models/pager_tree/integrations/solar_winds/v3.rb +66 -2
  65. data/app/models/pager_tree/integrations/status_gator/v3.rb +69 -0
  66. data/app/models/pager_tree/integrations/uptime_kuma/v3.rb +76 -0
  67. data/app/models/pager_tree/integrations/uptime_robot/v3.rb +6 -0
  68. data/app/models/pager_tree/integrations/webhook/v3.rb +25 -7
  69. data/app/models/pager_tree/integrations/zabbix/v3.rb +95 -0
  70. data/app/models/pager_tree/integrations/zendesk/v3.rb +2 -0
  71. data/app/views/pager_tree/integrations/channel/hangouts/v3/_form_options.html.erb +7 -0
  72. data/app/views/pager_tree/integrations/channel/hangouts/v3/_show_options.html.erb +10 -0
  73. data/app/views/pager_tree/integrations/channel/microsoft_teams/v3/_form_options.html.erb +7 -0
  74. data/app/views/pager_tree/integrations/channel/microsoft_teams/v3/_show_options.html.erb +14 -0
  75. data/app/views/pager_tree/integrations/channel/microsoft_teams/v4/_form_options.html.erb +41 -0
  76. data/app/views/pager_tree/integrations/channel/microsoft_teams/v4/_show_options.html.erb +54 -0
  77. data/app/views/pager_tree/integrations/channel/slack/v3/_form_options.html.erb +6 -3
  78. data/app/views/pager_tree/integrations/channel/slack/v3/_show_options.html.erb +1 -1
  79. data/app/views/pager_tree/integrations/cloudflare/v3/_form_options.html.erb +7 -0
  80. data/app/views/pager_tree/integrations/cloudflare/v3/_show_options.html.erb +12 -0
  81. data/app/views/pager_tree/integrations/custom_webhook/v3/_form_options.html.erb +8 -0
  82. data/app/views/pager_tree/integrations/custom_webhook/v3/_show_options.html.erb +0 -0
  83. data/app/views/pager_tree/integrations/datadog/v3/_form_options.html.erb +7 -0
  84. data/app/views/pager_tree/integrations/datadog/v3/_show_options.html.erb +12 -0
  85. data/app/views/pager_tree/integrations/dynatrace/v3/_form_options.html.erb +0 -0
  86. data/app/views/pager_tree/integrations/dynatrace/v3/_show_options.html.erb +0 -0
  87. data/app/views/pager_tree/integrations/echoes_hq/v3/_form_options.html.erb +10 -0
  88. data/app/views/pager_tree/integrations/echoes_hq/v3/_show_options.html.erb +12 -0
  89. data/app/views/pager_tree/integrations/email/v3/_form_options.html.erb +41 -11
  90. data/app/views/pager_tree/integrations/email/v3/_show_options.html.erb +18 -0
  91. data/app/views/pager_tree/integrations/form/v3/_form_options.html.erb +1 -5
  92. data/app/views/pager_tree/integrations/hetrix_tools/v3/_form_options.html.erb +7 -0
  93. data/app/views/pager_tree/integrations/hetrix_tools/v3/_show_options.html.erb +12 -0
  94. data/app/views/pager_tree/integrations/hydrozen/v3/_form_options.html.erb +0 -0
  95. data/app/views/pager_tree/integrations/hydrozen/v3/_show_options.html.erb +0 -0
  96. data/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_form_options.html.erb +44 -31
  97. data/app/views/pager_tree/integrations/live_call_routing/twilio/v3/_show_options.html.erb +93 -8
  98. data/app/views/pager_tree/integrations/logic_monitor/v3/_form_options.html.erb +31 -0
  99. data/app/views/pager_tree/integrations/logic_monitor/v3/_show_options.html.erb +60 -0
  100. data/app/views/pager_tree/integrations/meta/workplace/v3/_form_options.html.erb +44 -0
  101. data/app/views/pager_tree/integrations/meta/workplace/v3/_show_options.html.erb +68 -0
  102. data/app/views/pager_tree/integrations/outgoing_webhook/v3/_form_options.html.erb +17 -3
  103. data/app/views/pager_tree/integrations/outgoing_webhook/v3/_show_options.html.erb +20 -0
  104. data/app/views/pager_tree/integrations/pulsetic/v3/_form_options.html.erb +0 -0
  105. data/app/views/pager_tree/integrations/pulsetic/v3/_show_options.html.erb +0 -0
  106. data/app/views/pager_tree/integrations/scom/v3/_form_options.html.erb +0 -0
  107. data/app/views/pager_tree/integrations/scom/v3/_show_options.html.erb +0 -0
  108. data/app/views/pager_tree/integrations/sentry/v3/_form_options.html.erb +9 -0
  109. data/app/views/pager_tree/integrations/sentry/v3/_show_options.html.erb +15 -0
  110. data/app/views/pager_tree/integrations/sixty_six_uptime/v3/_form_options.html.erb +0 -0
  111. data/app/views/pager_tree/integrations/sixty_six_uptime/v3/_show_options.html.erb +0 -0
  112. data/app/views/pager_tree/integrations/solar_winds/v3/_form_options.html.erb +40 -0
  113. data/app/views/pager_tree/integrations/solar_winds/v3/_show_options.html.erb +66 -0
  114. data/app/views/pager_tree/integrations/status_gator/v3/_form_options.html.erb +13 -0
  115. data/app/views/pager_tree/integrations/status_gator/v3/_show_options.html.erb +24 -0
  116. data/app/views/pager_tree/integrations/uptime_kuma/v3/_form_options.html.erb +0 -0
  117. data/app/views/pager_tree/integrations/uptime_kuma/v3/_show_options.html.erb +0 -0
  118. data/app/views/pager_tree/integrations/webhook/v3/_form_options.html.erb +8 -0
  119. data/app/views/pager_tree/integrations/webhook/v3/_show_options.html.erb +15 -0
  120. data/app/views/pager_tree/integrations/zabbix/v3/_form_options.html.erb +7 -0
  121. data/app/views/pager_tree/integrations/zabbix/v3/_show_options.html.erb +12 -0
  122. data/config/locales/en.yml +158 -10
  123. data/config/routes.rb +1 -0
  124. data/lib/pager_tree/integrations/format_converters/yaml_json_converter.rb +67 -0
  125. data/lib/pager_tree/integrations/version.rb +1 -1
  126. data/lib/pager_tree/integrations.rb +11 -3
  127. metadata +84 -8
@@ -41,12 +41,20 @@ module PagerTree::Integrations
41
41
  false
42
42
  end
43
43
 
44
+ def adapter_supports_auto_aggregate?
45
+ false
46
+ end
47
+
48
+ def adapter_supports_auto_resolve?
49
+ false
50
+ end
51
+
44
52
  def adapter_outgoing_interest?(event_name)
45
53
  try("option_#{event_name}") || false
46
54
  end
47
55
 
48
56
  def adapter_process_outgoing
49
- url = adapter_outgoing_event.outgoing_rules_data.dig("webhook_url") || self.option_incoming_webhook_url
57
+ url = adapter_outgoing_event.outgoing_rules_data.dig("webhook_url") || option_incoming_webhook_url
50
58
  body = _blocks.merge(adapter_outgoing_event.outgoing_rules_data.except("webhook_url"))
51
59
 
52
60
  outgoing_webhook_delivery = OutgoingWebhookDelivery.factory(
@@ -60,6 +68,10 @@ module PagerTree::Integrations
60
68
  outgoing_webhook_delivery
61
69
  end
62
70
 
71
+ def _alert_created_at_timestamp_formatted
72
+ "<!date^#{_alert.created_at.utc.to_i}^{date_num} {time_secs}|#{_alert.created_at.utc.to_i}>"
73
+ end
74
+
63
75
  private
64
76
 
65
77
  def _alert
@@ -91,7 +103,7 @@ module PagerTree::Integrations
91
103
  },
92
104
  {
93
105
  title: "Created",
94
- value: "<!date^#{_alert.created_at.utc.to_i}^{date_num} {time_secs}|#{_alert.created_at.utc.to_i}>",
106
+ value: _alert_created_at_timestamp_formatted,
95
107
  short: "true"
96
108
  },
97
109
  {
@@ -101,7 +113,22 @@ module PagerTree::Integrations
101
113
  },
102
114
  {
103
115
  title: "Destinations",
104
- value: _alert.alert_destinations&.map { |d| d.destination.name }&.join(", "),
116
+ value: _alert.alert_destinations&.map { |d| d.destination.name }&.join(", ").presence || "-",
117
+ short: "false"
118
+ },
119
+ {
120
+ title: "Acknowledged By",
121
+ value: _alert.alert_responders&.map { |r| r.account_user.name }&.join(", ").presence || "-",
122
+ short: "false"
123
+ },
124
+ {
125
+ title: "Actions",
126
+ value: [
127
+ "<#{Rails.application.routes.url_helpers.try(:alert_url, _alert, script_name: "/#{_alert.account_id}")}|View>",
128
+ ["open", "dropped"].include?(_alert.status) ? "<#{Rails.application.routes.url_helpers.try(:acknowledge_alert_url, _alert, script_name: "/#{_alert.account_id}")}|Acknowledge>" : nil,
129
+ ["open"].include?(_alert.status) ? "<#{Rails.application.routes.url_helpers.try(:reject_alert_url, _alert, script_name: "/#{_alert.account_id}")}|Reject>" : nil,
130
+ ["acknowledged"].include?(_alert.status) ? "<#{Rails.application.routes.url_helpers.try(:resolve_alert_url, _alert, script_name: "/#{_alert.account_id}")}|Resolve>" : nil
131
+ ].compact.join(" | "),
105
132
  short: "false"
106
133
  }
107
134
  ]
@@ -0,0 +1,75 @@
1
+ module PagerTree::Integrations
2
+ class Cloudflare::V3 < Integration
3
+ OPTIONS = [
4
+ {key: :webhook_secret, type: :string, default: nil}
5
+ ]
6
+ store_accessor :options, *OPTIONS.map { |x| x[:key] }.map(&:to_s), prefix: "option"
7
+
8
+ after_initialize do
9
+ self.option_webhook_secret ||= nil
10
+ end
11
+
12
+ def adapter_should_block_incoming?(request)
13
+ option_webhook_secret.present? && (request.headers["cf-webhook-auth"] != option_webhook_secret)
14
+ end
15
+
16
+ def adapter_supports_incoming?
17
+ true
18
+ end
19
+
20
+ def adapter_supports_outgoing?
21
+ false
22
+ end
23
+
24
+ def adapter_incoming_can_defer?
25
+ true
26
+ end
27
+
28
+ def adapter_thirdparty_id
29
+ @adapter_thirdparty_id ||= SecureRandom.uuid
30
+ end
31
+
32
+ def adapter_action
33
+ :create
34
+ end
35
+
36
+ def adapter_process_create
37
+ Alert.new(
38
+ title: _title,
39
+ description: _description,
40
+ thirdparty_id: adapter_thirdparty_id,
41
+ dedup_keys: [],
42
+ additional_data: _additional_datums
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ def _title
49
+ adapter_incoming_request_params.dig("text")&.truncate(70) || "CF Event #{adapter_incoming_request_params.dig("text")&.titleize}"
50
+ end
51
+
52
+ def _description
53
+ ActionController::Base.helpers.simple_format(adapter_incoming_request_params.dig("text"))
54
+ end
55
+
56
+ def _additional_datums
57
+ timestamp = begin
58
+ Time.at(adapter_incoming_request_params.dig("ts")).utc.to_datetime
59
+ rescue
60
+ nil
61
+ end
62
+
63
+ datums = []
64
+ datums << AdditionalDatum.new(format: "text", label: "Account Name", value: adapter_incoming_request_params.dig("data", "account_name"))
65
+ datums << AdditionalDatum.new(format: "text", label: "Zone Name", value: adapter_incoming_request_params.dig("data", "zone_name"))
66
+ datums << AdditionalDatum.new(format: "link", label: "Dashboard Link", value: adapter_incoming_request_params.dig("data", "dashboard_link"))
67
+ datums << AdditionalDatum.new(format: "datetime", label: "Timestamp", value: timestamp)
68
+ datums << AdditionalDatum.new(format: "text", label: "Alert Type", value: adapter_incoming_request_params.dig("alert_type"))
69
+ datums << AdditionalDatum.new(format: "text", label: "Account ID", value: adapter_incoming_request_params.dig("account_id"))
70
+ datums << AdditionalDatum.new(format: "text", label: "Policy ID", value: adapter_incoming_request_params.dig("policy_id"))
71
+
72
+ datums.filter { |datum| datum.value.present? }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,206 @@
1
+ module PagerTree::Integrations
2
+ class CustomWebhook::V3 < Integration
3
+ extend ::PagerTree::Integrations::Env
4
+
5
+ # the source log (if created) - Its what shows on the integration page (different from deferred request)
6
+ attribute :adapter_source_log
7
+
8
+ OPTIONS = [
9
+ {key: :custom_definition, type: :string, default: nil}
10
+ ]
11
+ store_accessor :options, *OPTIONS.map { |x| x[:key] }.map(&:to_s), prefix: "option"
12
+
13
+ validates :option_custom_definition, presence: true
14
+
15
+ def self.custom_webhook_v3_service_url
16
+ ::PagerTree::Integrations.integration_custom_webhook_v3_service_url.presence ||
17
+ find_value_by_name("integration_custom_webhook_v3", "service_url")
18
+ end
19
+
20
+ def adapter_supports_incoming?
21
+ true
22
+ end
23
+
24
+ def adapter_supports_outgoing?
25
+ false
26
+ end
27
+
28
+ def adapter_incoming_can_defer?
29
+ true
30
+ end
31
+
32
+ def adapter_thirdparty_id
33
+ custom_response_result.dig("thirdparty_id")
34
+ end
35
+
36
+ def adapter_action
37
+ case custom_response_result.dig("type")&.downcase
38
+ when "create"
39
+ :create
40
+ when "acknowledge"
41
+ :acknowledge
42
+ when "resolve"
43
+ :resolve
44
+ else
45
+ :other
46
+ end
47
+ end
48
+
49
+ def adapter_process_create
50
+ Alert.new(
51
+ title: _title,
52
+ description: _description,
53
+ urgency: _urgency,
54
+ thirdparty_id: adapter_thirdparty_id,
55
+ dedup_keys: _dedup_keys,
56
+ incident: _incident,
57
+ incident_severity: _incident_severity,
58
+ incident_message: _incident_message,
59
+ tags: _tags,
60
+ meta: _meta,
61
+ additional_data: _additional_datums
62
+ )
63
+ end
64
+
65
+ def custom_response_processed_log_data
66
+ _custom_response_processed_log_data || {}
67
+ end
68
+
69
+ private
70
+
71
+ def _custom_response
72
+ return {} unless adapter_incoming_deferred_request.present?
73
+
74
+ @_custom_response ||= begin
75
+ log_hash = {
76
+ remote_ip: adapter_incoming_deferred_request.remote_ip,
77
+ url: adapter_incoming_deferred_request.url,
78
+ method: adapter_incoming_deferred_request.method,
79
+ headers: adapter_incoming_deferred_request.headers,
80
+ data: adapter_incoming_deferred_request.params.presence || adapter_incoming_deferred_request.body,
81
+ params: adapter_incoming_deferred_request.params,
82
+ body: adapter_incoming_deferred_request.body
83
+ }
84
+
85
+ body_hash = {
86
+ log: log_hash,
87
+ config: JSON.parse(PagerTree::Integrations::FormatConverters::YamlJsonConverter.convert_to_json(option_custom_definition))
88
+ }
89
+
90
+ response = HTTParty.post(
91
+ self.class.custom_webhook_v3_service_url,
92
+ body: body_hash.to_json,
93
+ headers: {"Content-Type" => "application/json"},
94
+ timeout: 2
95
+ )
96
+
97
+ unless response.success?
98
+ if response.parsed_response.dig("error").present?
99
+ adapter_source_log&.sublog({
100
+ message: "Custom Webhook Service Error:",
101
+ parsed_response: response.parsed_response
102
+ })
103
+ adapter_source_log&.save
104
+ end
105
+ raise "Custom Webhook Service HTTP error: #{response.code} - #{response.message} - #{response.body}"
106
+ end
107
+
108
+ adapter_source_log&.sublog({
109
+ message: "Custom Webhook Service Response:",
110
+ parsed_response: response.parsed_response
111
+ })
112
+ adapter_source_log&.save
113
+
114
+ response.parsed_response
115
+ rescue JSON::ParserError => e
116
+ Rails.logger.error("Custom Webhook YAML to JSON conversion error: #{e.message}")
117
+ adapter_source_log&.sublog("Custom Webhook YAML to JSON conversion error: #{e.message}")
118
+ adapter_source_log&.save
119
+ raise "Invalid YAML configuration: #{e.message}"
120
+ rescue HTTParty::Error, SocketError, Net::OpenTimeout, Net::ReadTimeout => e
121
+ Rails.logger.error("Custom Webhook Service error: #{e.message}")
122
+ adapter_source_log&.sublog("Custom Webhook Service error: #{e.message}")
123
+ adapter_source_log&.save
124
+ raise "Custom Webhook Service error: #{e.message}"
125
+ rescue => e
126
+ Rails.logger.error("Unexpected error in Custom Webhook: #{e.message}")
127
+ adapter_source_log&.sublog("Unexpected error in Custom Webhook: #{e.message}")
128
+ adapter_source_log&.save
129
+ raise e
130
+ end
131
+ end
132
+
133
+ def _custom_response_status
134
+ @_custom_response_status ||= _custom_response.dig("status")
135
+ end
136
+
137
+ def custom_response_result
138
+ @_custom_response_result ||= _custom_response.dig("results")&.first || {}
139
+ end
140
+
141
+ def _custom_response_processed_log_data
142
+ @_custom_response_processed_log_data ||= _custom_response.dig("processedLogData")
143
+ end
144
+
145
+ def _title
146
+ custom_response_result.dig("title")&.to_s&.presence || "Untitled Alert"
147
+ end
148
+
149
+ def _description
150
+ custom_response_result.dig("description")&.to_s || ""
151
+ end
152
+
153
+ def _urgency
154
+ custom_response_result.dig("urgency")&.to_s&.presence
155
+ end
156
+
157
+ def _dedup_keys
158
+ keys = custom_response_result.dig("dedup_keys")
159
+ keys = keys.split(",") if keys.is_a?(String)
160
+ Array(keys).compact_blank.map(&:to_s).uniq
161
+ end
162
+
163
+ def _incident
164
+ ActiveModel::Type::Boolean.new.cast(custom_response_result.dig("incident"))
165
+ end
166
+
167
+ def _incident_severity
168
+ custom_response_result.dig("incident_severity")&.to_s&.presence
169
+ end
170
+
171
+ def _incident_message
172
+ custom_response_result.dig("incident_message")&.to_s&.presence
173
+ end
174
+
175
+ def _tags
176
+ tags = custom_response_result.dig("tags")
177
+ tags = tags.split(",") if tags.is_a?(String)
178
+ Array(tags).compact_blank.map(&:to_s).uniq
179
+ end
180
+
181
+ def _meta
182
+ meta = custom_response_result.dig("meta")
183
+ meta.is_a?(Hash) ? meta : {}
184
+ end
185
+
186
+ def _additional_datums
187
+ @_additional_datums ||= begin
188
+ items = custom_response_result.dig("additional_data") || []
189
+ items = [items] unless items.is_a?(Array)
190
+
191
+ items.each_with_object([]) do |ad, result|
192
+ next unless ad.is_a?(Hash)
193
+
194
+ format = ad["format"].to_s
195
+ next unless PagerTree::Integrations::AdditionalDatum::FORMATS.include?(format)
196
+
197
+ result << AdditionalDatum.new(
198
+ format: format,
199
+ label: ad["label"].to_s.presence || "Untitled",
200
+ value: ad["value"]
201
+ )
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "generic-alert-id",
3
+ "title": "High CPU Usage",
4
+ "text": "CPU usage exceeded 90% for more than 5 minutes",
5
+ "priority": "high",
6
+ "tags": ["env:prod", "service:web"],
7
+ "host": "example-host",
8
+ "alert_type": "error",
9
+ "aggregation_key": "cpu-alert",
10
+ "source": "datadog",
11
+ "event_type": "trigger"
12
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ rules:
3
+ - match:
4
+ log.data.event_type: "trigger"
5
+ log.data.priority: "high"
6
+ actions:
7
+ - type: create
8
+ title: "{{log.data.title}}"
9
+ description: "{{log.data.text}}"
10
+ thirdparty_id: "{{log.data.id}}"
11
+ urgency: "high"
12
+ tags: "{{log.data.tags}}"
13
+ additional_data:
14
+ - format: text
15
+ label: Host
16
+ value: "{{log.data.host}}"
17
+ - format: text
18
+ label: Source
19
+ value: "{{log.data.source}}"
20
+
21
+ - match:
22
+ log.data.event_type: "resolve"
23
+ actions:
24
+ - type: resolve
25
+ thirdparty_id: "{{log.data.id}}"
26
+
27
+ default:
28
+ actions:
29
+ - type: ignore
30
+ reason: "Unhandled Datadog alert"
@@ -0,0 +1,9 @@
1
+ {
2
+ "monitorID": "12345",
3
+ "alertTypeFriendlyName": "Down",
4
+ "monitorFriendlyName": "My Website",
5
+ "alertDetails": "Connection timeout",
6
+ "monitorURL": "https://example.com",
7
+ "urgency": "high",
8
+ "alertDateTime": 1733126400
9
+ }
@@ -0,0 +1,27 @@
1
+ ---
2
+ rules:
3
+ - match:
4
+ log.data.alertTypeFriendlyName: { $regex: "^down$", $options: "i" }
5
+ actions:
6
+ - type: create
7
+ title: "{{log.data.monitorFriendlyName}} is DOWN"
8
+ description: "{{log.data.monitorFriendlyName}} is DOWN because {{log.data.alertDetails}}"
9
+ thirdparty_id: "{{log.data.monitorID}}"
10
+ additional_data:
11
+ - format: link
12
+ label: Monitor URL
13
+ value: "{{log.data.monitorURL}}"
14
+ - format: datetime
15
+ label: Triggered At
16
+ value: "{{unix-timezone log.data.alertDateTime 'UTC' 'YYYY-MM-DD HH:mm:ss'}}"
17
+
18
+ - match:
19
+ log.data.alertTypeFriendlyName: { $regex: "^up$", $options: "i" }
20
+ actions:
21
+ - type: resolve
22
+ thirdparty_id: "{{log.data.monitorID}}"
23
+
24
+ default:
25
+ actions:
26
+ - type: ignore
27
+ reason: "Unhandled UptimeRobot alert type: {{log.data.alertTypeFriendlyName}}"
@@ -0,0 +1,25 @@
1
+ {
2
+ "dashboardId": 1,
3
+ "evalMatches": [
4
+ {
5
+ "value": 100,
6
+ "metric": "High value",
7
+ "tags": null
8
+ },
9
+ {
10
+ "value": 8,
11
+ "metric": "Lower value",
12
+ "tags": null
13
+ }
14
+ ],
15
+ "imageUrl": "https://example.com/image/generic.png",
16
+ "message": "Someone is testing the alert notification within Grafana.",
17
+ "orgId": 1,
18
+ "panelId": 2,
19
+ "ruleId": 1,
20
+ "ruleName": "Panel Title alert",
21
+ "ruleUrl": "https://example.com/d/generic-dashboard/alerting/generic-rule/edit",
22
+ "state": "alerting",
23
+ "tags": {},
24
+ "title": "[Alerting] Panel Title alert"
25
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ rules:
3
+ - match:
4
+ log.data.state: "alerting"
5
+ actions:
6
+ - type: create
7
+ title: "{{log.data.title}}"
8
+ description: "{{log.data.message}}"
9
+ thirdparty_id: "{{log.data.ruleId}}"
10
+ additional_data:
11
+ - format: text
12
+ label: Dashboard ID
13
+ value: "{{log.data.dashboardId}}"
14
+ - format: link
15
+ label: Rule URL
16
+ value: "{{log.data.ruleUrl}}"
17
+ - format: img
18
+ label: Alert Image
19
+ value: "{{log.data.imageUrl}}"
20
+ - format: text
21
+ label: Eval Matches
22
+ value: "{{log.data.evalMatches.[0].metric}}: {{log.data.evalMatches.[0].value}}"
23
+
24
+ - match:
25
+ log.data.state: "ok"
26
+ actions:
27
+ - type: resolve
28
+ thirdparty_id: "{{log.data.ruleId}}"
29
+
30
+ default:
31
+ actions:
32
+ - type: ignore
33
+ reason: "Unhandled Grafana alert"
@@ -0,0 +1,40 @@
1
+ {
2
+ "accumulations": {
3
+ "policyName": [
4
+ "Example Policy"
5
+ ],
6
+ "conditionName": [
7
+ "High Error Rate"
8
+ ]
9
+ },
10
+ "annotations": {
11
+ "description": "Error rate > 5% for 5 minutes",
12
+ "reason": "Threshold exceeded"
13
+ },
14
+ "closedViolations": [],
15
+ "currentState": "open",
16
+ "details": "Error rate is 7.2%",
17
+ "eventType": "INCIDENT",
18
+ "incidentId": "generic-incident-id",
19
+ "incidentUrl": "https://example.com/alerts/generic-id",
20
+ "openViolations": [
21
+ {
22
+ "createdAt": 1733126400000,
23
+ "label": "High Error Rate",
24
+ "violationId": "generic-violation-id"
25
+ }
26
+ ],
27
+ "owner": "alerts@example.com",
28
+ "policyUrl": "https://example.com/policies/generic-policy",
29
+ "severity": "CRITICAL",
30
+ "targets": [
31
+ {
32
+ "id": "generic-entity-id",
33
+ "name": "Example App",
34
+ "type": "Application",
35
+ "link": "https://example.com/entities/generic-entity"
36
+ }
37
+ ],
38
+ "timestamp": 1733126400000,
39
+ "version": 3
40
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ rules:
3
+ - match:
4
+ log.data.currentState: "open"
5
+ log.data.severity: "CRITICAL"
6
+ actions:
7
+ - type: create
8
+ title: "{{log.data.accumulations.policyName.[0]}} - {{log.data.accumulations.conditionName.[0]}}"
9
+ description: "{{log.data.annotations.description}} - Details: {{log.data.details}}"
10
+ thirdparty_id: "{{log.data.incidentId}}"
11
+ urgency: "high"
12
+ additional_data:
13
+ - format: link
14
+ label: Incident URL
15
+ value: "{{log.data.incidentUrl}}"
16
+ - format: link
17
+ label: Policy URL
18
+ value: "{{log.data.policyUrl}}"
19
+ - format: text
20
+ label: Targets
21
+ value: "{{log.data.targets.[0].name}} ({{log.data.targets.[0].type}})"
22
+
23
+ - match:
24
+ log.data.currentState: "closed"
25
+ actions:
26
+ - type: resolve
27
+ thirdparty_id: "{{log.data.incidentId}}"
28
+
29
+ default:
30
+ actions:
31
+ - type: ignore
32
+ reason: "Unhandled New Relic alert"
@@ -0,0 +1,30 @@
1
+ {
2
+ "client": null,
3
+ "client_url": null,
4
+ "contexts": [
5
+ {
6
+ "href": "https://my.pingdom.com/newchecks/checks",
7
+ "text": "View in Pingdom",
8
+ "type": "link"
9
+ }
10
+ ],
11
+ "description": "Redirection error",
12
+ "event_type": "trigger",
13
+ "incident_key": "generic-incident-key",
14
+ "service_key": "generic-service-key",
15
+ "details": {
16
+ "check_id": "generic-check-id",
17
+ "check_name": "example.com check HTTP",
18
+ "full_url": "https://example.com/",
19
+ "header": "User-Agent:Pingdom.com_bot_version_1.4[](http://www.pingdom.com/)",
20
+ "hostname": "example.com",
21
+ "long_description": "Could not find redirect location",
22
+ "tags": [
23
+ "environment:prod",
24
+ "generic:monitoring",
25
+ "owner:devops",
26
+ "http"
27
+ ],
28
+ "url": "/"
29
+ }
30
+ }
@@ -0,0 +1,23 @@
1
+ ---
2
+ rules:
3
+ - match:
4
+ log.data.details.check_name: { $regex: "example.com check HTTP" }
5
+ log.data.event_type: "trigger"
6
+ actions:
7
+ - type: create
8
+ title: "Pingdom Alert: {{log.data.description}}"
9
+ description: "{{log.data.details.long_description}} for {{log.data.details.full_url}}"
10
+ thirdparty_id: "{{log.data.details.check_id}}"
11
+ tags: "{{log.data.details.tags}}"
12
+ additional_data:
13
+ - format: text
14
+ label: Hostname
15
+ value: "{{log.data.details.hostname}}"
16
+ - format: link
17
+ label: View in Pingdom
18
+ value: "https://my.pingdom.com/newchecks/checks"
19
+
20
+ default:
21
+ actions:
22
+ - type: ignore
23
+ reason: "Unhandled Pingdom alert"
@@ -0,0 +1,12 @@
1
+ {
2
+ "Type": "Notification",
3
+ "MessageId": "[REDACTED]",
4
+ "TopicArn": "arn:aws:sns:us-west-2:[ACCOUNT_ID]:[TOPIC_NAME]",
5
+ "Subject": "ALARM: \"[ALARM_NAME]\" in US West (Oregon)",
6
+ "Message": "{\"AlarmName\":\"[ALARM_NAME]\",\"AlarmDescription\":null,\"AWSAccountId\":\"[ACCOUNT_ID]\",\"AlarmConfigurationUpdatedTimestamp\":\"2025-12-05T18:57:39.128+0000\",\"NewStateValue\":\"OK\",\"NewStateReason\":\"Threshold Crossed: no datapoints were received for 1 period and 1 missing datapoint was treated as [Breaching].\",\"StateChangeTime\":\"2025-12-05T18:59:34.547+0000\",\"Region\":\"US West (Oregon)\",\"AlarmArn\":\"arn:aws:cloudwatch:us-west-2:[ACCOUNT_ID]:alarm:[ALARM_NAME]\",\"OldStateValue\":\"OK\",\"OKActions\":[\"arn:aws:sns:us-west-2:[ACCOUNT_ID]:[TOPIC_NAME]\"],\"AlarmActions\":[\"arn:aws:sns:us-west-2:[ACCOUNT_ID]:[TOPIC_NAME]\"],\"InsufficientDataActions\":[],\"Trigger\":{\"MetricName\":\"CallCount\",\"Namespace\":\"AWS/Usage\",\"StatisticType\":\"Statistic\",\"Statistic\":\"AVERAGE\",\"Unit\":null,\"Dimensions\":[{\"value\":\"API\",\"name\":\"Type\"},{\"value\":\"SendLast24Hours\",\"name\":\"Resource\"},{\"value\":\"SES\",\"name\":\"Service\"},{\"value\":\"None\",\"name\":\"Class\"}],\"Period\":60,\"EvaluationPeriods\":1,\"DatapointsToAlarm\":1,\"ComparisonOperator\":\"GreaterThanThreshold\",\"Threshold\":1.0,\"TreatMissingData\":\"breaching\",\"EvaluateLowSampleCountPercentile\":\"\"}}",
7
+ "Timestamp": "2025-12-05T18:59:34.596Z",
8
+ "SignatureVersion": "1",
9
+ "Signature": "[REDACTED]",
10
+ "SigningCertURL": "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-[REDACTED].pem",
11
+ "UnsubscribeURL": "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:[ACCOUNT_ID]:[TOPIC_NAME]:[SUBSCRIPTION_ID]"
12
+ }