feat(webhooks): [APLAT-589] wire PagerDuty webhook end-to-end#51
Conversation
shivanshtamrakar-s1
commented
May 21, 2026
07e13e6 to
ac8ba33
Compare
| fun send(incident: MatchedIncident) { | ||
| val subject = ":pager: Escalation policy $watchedPolicyId added to incident — ${incident.title}" | ||
| val email = Email( | ||
| recipient = InternetAddress(channelEmail), |
There was a problem hiding this comment.
this could throw an exception - better move it into the try block
There was a problem hiding this comment.
moved this to try block
| properties = [ | ||
| "pagerduty.webhook.secret=test-secret", | ||
| "pagerduty.escalation-policy.watch-id=PQ5P8WD", | ||
| "slack.webhook.url=http://slack.test/hook", |
| spring.mail.properties[mail.smtp.starttls.enable]=true | ||
|
|
||
| pagerduty.webhook.secret=${PAGERDUTY_WEBHOOK_SECRET:} | ||
| pagerduty.escalation-policy.watch-id=${WATCH_ID:} |
There was a problem hiding this comment.
| pagerduty.escalation-policy.watch-id=${WATCH_ID:} | |
| pagerduty.escalation-policy.watch-id=${PAGERDUTY_WATCH_ID:} |
for consistency
| pagerduty.webhook.secret=${PAGERDUTY_WEBHOOK_SECRET:} | ||
| pagerduty.escalation-policy.watch-id=${PAGERDUTY_WATCH_ID:} | ||
| slack.channel.email=${SLACK_CHANNEL_EMAIL:} |
There was a problem hiding this comment.
| pagerduty.webhook.secret=${PAGERDUTY_WEBHOOK_SECRET:} | |
| pagerduty.escalation-policy.watch-id=${PAGERDUTY_WATCH_ID:} | |
| slack.channel.email=${SLACK_CHANNEL_EMAIL:} | |
| pagerduty.webhook.secret=${PAGERDUTY_WEBHOOK_SECRET} | |
| pagerduty.escalation-policy.watch-id=${PAGERDUTY_WATCH_ID} | |
| slack.channel.email=${SLACK_CHANNEL_EMAIL} |
fcfbeea to
ce4e0ce
Compare
| private fun sendEmail(subject: String, htmlBody: String, logContext: String) { | ||
| try { | ||
| val email = Email( | ||
| recipient = InternetAddress(channelEmail), |
There was a problem hiding this comment.
It seems a different exception AddressException is thrown here. Maybe we can move it to constructor:
private val channelEmail: InternetAddress = InternetAddress(slackProperties.channel.email)
and throw the error right on the app start
| ) { | ||
|
|
||
| private val keySpec: SecretKeySpec? = if (properties.webhook.secret.isEmpty()) { | ||
| logger.info { "pagerduty.webhook.secret is empty; all PagerDuty webhook deliveries will be rejected with 401" } |
There was a problem hiding this comment.
this is now unreachable code, with @ConfigurationProperties the "missing" value would be ${PAGERDUTY_WEBHOOK_SECRET}. There is a test, but it tests artificial behaviour that doesn't happen in the real world 😄
There was a problem hiding this comment.
my bad , removing this one. thanks
94ef613 to
d0941f7
Compare
Add POST /webhooks/pagerduty/incident — verifies the X-PagerDuty-
Signature, parses the v3 incident.responder.added payload, filters for
the watched escalation policy, and posts a Slack notification when it
matches. Returns 401 on bad/missing signature, 200 on signed bodies
regardless of match (200-on-no-match prevents PagerDuty retry storms).
Components: PagerDutyWebhookController (HTTP entrypoint),
PagerDutyWebhookService (verify-parse-filter-notify orchestration),
WebhookBodySizeFilter (rejects /webhooks/* POSTs >64KB with 413 to
mitigate DoS via large bodies).
Security wiring: WebSecurityConfig CSRF-exempts the single exact path
/webhooks/pagerduty/incident (tighter than a wildcard). Controller
reads raw bytes and decodes UTF-8 explicitly so signature verification
matches what PagerDuty signed regardless of Content-Type charset.
PagerDutySignatureVerifier now logs ERROR (was WARN) when the secret
is unconfigured and rejects all requests instead of throwing during
context initialization.
application.properties adds three properties with empty env-var
defaults so the application context still starts when secrets are not
provisioned locally.
5 web slice tests (200 match, 200 no-match, 401 missing sig, 401 bad
sig, CSRF-exempt path), 3 filter unit tests, plus one new verifier
case covering the empty-secret behavior. Full suite goes from a
pre-existing failure to 81/81 green — the empty-secret tolerance
also fixes SpdreportApplicationTests.contextLoads.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d0941f7 to
9f900a9
Compare