@@ -25,6 +25,7 @@ import { sendAlertEmail } from "~/services/email.server";
2525import { logger } from "~/services/logger.server" ;
2626import { decryptSecret } from "~/services/secrets/secretStore.server" ;
2727import { subtle } from "crypto" ;
28+ import { generateErrorGroupWebhookPayload } from "./errorGroupWebhook.server" ;
2829
2930type ErrorAlertClassification = "new_issue" | "regression" | "unignored" ;
3031
@@ -183,58 +184,15 @@ export class DeliverErrorGroupAlertService {
183184 return ;
184185 }
185186
186- const label = this . #classificationLabel( payload . classification ) ;
187- const errorType = payload . error . errorType || "Error" ;
188- const task = payload . error . taskIdentifier ;
189- const envName = payload . error . environmentName ;
190-
191- const emoji =
192- payload . classification === "new_issue"
193- ? ":rotating_light:"
194- : payload . classification === "regression"
195- ? ":warning:"
196- : ":bell:" ;
187+ const message = this . #buildErrorGroupSlackMessage(
188+ payload ,
189+ errorLink ,
190+ channel . project . name
191+ ) ;
197192
198193 await this . #postSlackMessage( integration , {
199194 channel : slackProperties . data . channelId ,
200- text : `${ label } : ${ errorType } in ${ task } [${ envName } ]` ,
201- blocks : [
202- {
203- type : "section" ,
204- text : {
205- type : "mrkdwn" ,
206- text : `${ emoji } *${ label } * in *${ task } * [${ envName } ]` ,
207- } ,
208- } ,
209- {
210- type : "section" ,
211- text : {
212- type : "mrkdwn" ,
213- text : this . #wrapInCodeBlock(
214- payload . error . sampleStackTrace || payload . error . errorMessage
215- ) ,
216- } ,
217- } ,
218- {
219- type : "context" ,
220- elements : [
221- {
222- type : "mrkdwn" ,
223- text : `> *${ task } * | ${ envName } | ${ channel . project . name } \n> ${ payload . error . occurrenceCount } occurrences | ${ this . #formatTimestamp( new Date ( Number ( payload . error . lastSeen ) ) ) } ` ,
224- } ,
225- ] ,
226- } ,
227- {
228- type : "actions" ,
229- elements : [
230- {
231- type : "button" ,
232- text : { type : "plain_text" , text : "Investigate" } ,
233- url : errorLink ,
234- } ,
235- ] ,
236- } ,
237- ] ,
195+ ...message ,
238196 } ) ;
239197 }
240198
@@ -255,36 +213,22 @@ export class DeliverErrorGroupAlertService {
255213 return ;
256214 }
257215
258- const webhookPayload = {
259- type : "alert.error_group" as const ,
216+ const webhookPayload = generateErrorGroupWebhookPayload ( {
260217 classification : payload . classification ,
261- error : {
262- fingerprint : payload . error . fingerprint ,
263- type : payload . error . errorType ,
264- message : payload . error . errorMessage ,
265- stackTrace : payload . error . sampleStackTrace || undefined ,
266- firstSeen : payload . error . firstSeen ,
267- lastSeen : payload . error . lastSeen ,
268- occurrenceCount : payload . error . occurrenceCount ,
269- taskIdentifier : payload . error . taskIdentifier ,
270- } ,
271- environment : {
272- id : payload . error . environmentId ,
273- name : payload . error . environmentName ,
274- } ,
218+ error : payload . error ,
275219 organization : {
276220 id : channel . project . organizationId ,
277221 slug : channel . project . organization . slug ,
278222 name : channel . project . organization . title ,
279223 } ,
280224 project : {
281225 id : channel . project . id ,
282- ref : channel . project . externalRef ,
226+ externalRef : channel . project . externalRef ,
283227 slug : channel . project . slug ,
284228 name : channel . project . name ,
285229 } ,
286230 dashboardUrl : errorLink ,
287- } ;
231+ } ) ;
288232
289233 const rawPayload = JSON . stringify ( webhookPayload ) ;
290234 const hashPayload = Buffer . from ( rawPayload , "utf-8" ) ;
@@ -352,11 +296,90 @@ export class DeliverErrorGroupAlertService {
352296 }
353297 }
354298
299+ #buildErrorGroupSlackMessage(
300+ payload : ErrorAlertPayload ,
301+ errorLink : string ,
302+ projectName : string
303+ ) : Pick < ChatPostMessageArguments , "text" | "blocks" | "attachments" > {
304+ const label = this . #classificationLabel( payload . classification ) ;
305+ const errorType = payload . error . errorType || "Error" ;
306+ const task = payload . error . taskIdentifier ;
307+ const envName = payload . error . environmentName ;
308+
309+ return {
310+ text : `${ label } : ${ errorType } in ${ task } [${ envName } ]` ,
311+ blocks : [
312+ {
313+ type : "section" ,
314+ text : {
315+ type : "mrkdwn" ,
316+ text : `*${ label } in ${ task } [${ envName } ]*` ,
317+ } ,
318+ } ,
319+ ] ,
320+ attachments : [
321+ {
322+ color : "danger" ,
323+ blocks : [
324+ {
325+ type : "section" ,
326+ text : {
327+ type : "mrkdwn" ,
328+ text : this . #wrapInCodeBlock(
329+ payload . error . sampleStackTrace || payload . error . errorMessage
330+ ) ,
331+ } ,
332+ } ,
333+ {
334+ type : "section" ,
335+ fields : [
336+ {
337+ type : "mrkdwn" ,
338+ text : `*Task:*\n${ task } ` ,
339+ } ,
340+ {
341+ type : "mrkdwn" ,
342+ text : `*Environment:*\n${ envName } ` ,
343+ } ,
344+ {
345+ type : "mrkdwn" ,
346+ text : `*Project:*\n${ projectName } ` ,
347+ } ,
348+ {
349+ type : "mrkdwn" ,
350+ text : `*Occurrences:*\n${ payload . error . occurrenceCount } ` ,
351+ } ,
352+ {
353+ type : "mrkdwn" ,
354+ text : `*Last seen:*\n${ this . #formatTimestamp( new Date ( Number ( payload . error . lastSeen ) ) ) } ` ,
355+ } ,
356+ ] ,
357+ } ,
358+ {
359+ type : "actions" ,
360+ elements : [
361+ {
362+ type : "button" ,
363+ text : { type : "plain_text" , text : "Investigate" } ,
364+ url : errorLink ,
365+ style : "primary" ,
366+ } ,
367+ ] ,
368+ } ,
369+ ] ,
370+ } ,
371+ ] ,
372+ } ;
373+ }
374+
355375 #wrapInCodeBlock( text : string , maxLength = 3000 ) {
376+ const wrapperLength = 6 ; // ``` prefix + ``` suffix
377+ const truncationSuffix = "\n\n...truncated — check dashboard for full error" ;
378+ const innerMax = maxLength - wrapperLength ;
379+
356380 const truncated =
357- text . length > maxLength - 10
358- ? text . slice ( 0 , maxLength - 10 - 50 ) +
359- "\n\ntruncated - check dashboard for complete error message"
381+ text . length > innerMax
382+ ? text . slice ( 0 , innerMax - truncationSuffix . length ) + truncationSuffix
360383 : text ;
361384 return `\`\`\`${ truncated } \`\`\`` ;
362385 }
0 commit comments