Skip to content

Commit d94ab29

Browse files
kathiekiwiTrigger.dev RepoOps
authored andcommitted
fix(alerts): deliver legacy Slack alerts through a removed integration again
fix(webapp): restore Slack alert delivery for channels linked to removed integrations Mono-RevId: 941ebb4266026f8b64e9ea3f047a5605f34bc60e
1 parent 98adb66 commit d94ab29

6 files changed

Lines changed: 100 additions & 33 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Alerts sent to Slack channels connected through a removed Slack installation deliver again.

‎apps/webapp/app/v3/services/alerts/alertTransport.server.ts‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,28 @@ const PERMANENT_WEBHOOK_STATUSES = [401, 403, 404, 410];
2424

2525
export async function findSlackIntegration(
2626
prisma: { organizationIntegration: { findFirst: Function } },
27-
params: { integrationId?: string | null; organizationId: string }
27+
params: { integrationId?: string | null; organizationId: string },
28+
options: { includeDeleted?: boolean } = {}
2829
): Promise<OrganizationIntegrationForService<"SLACK"> | null> {
30+
const findLatest = (where: { deletedAt?: null }) =>
31+
prisma.organizationIntegration.findFirst({
32+
where: { service: "SLACK", organizationId: params.organizationId, ...where },
33+
orderBy: { createdAt: "desc" },
34+
include: { tokenReference: true },
35+
});
36+
2937
const integration = params.integrationId
3038
? await prisma.organizationIntegration.findFirst({
3139
where: {
3240
id: params.integrationId,
3341
service: "SLACK",
3442
organizationId: params.organizationId,
35-
deletedAt: null,
43+
...(options.includeDeleted ? {} : { deletedAt: null }),
3644
},
3745
include: { tokenReference: true },
3846
})
39-
: await prisma.organizationIntegration.findFirst({
40-
where: { service: "SLACK", organizationId: params.organizationId, deletedAt: null },
41-
orderBy: { createdAt: "desc" },
42-
include: { tokenReference: true },
43-
});
47+
: ((await findLatest({ deletedAt: null })) ??
48+
(options.includeDeleted ? await findLatest({}) : null));
4449

4550
if (!integration || !isIntegrationForService(integration, "SLACK")) {
4651
return null;

‎apps/webapp/app/v3/services/alerts/deliverAlert.server.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -761,10 +761,14 @@ export class DeliverAlertService extends BaseService {
761761
return;
762762
}
763763

764-
const integration = await findSlackIntegration(this._prisma, {
765-
integrationId: slackProperties.data.integrationId,
766-
organizationId: alert.project.organizationId,
767-
});
764+
const integration = await findSlackIntegration(
765+
this._prisma,
766+
{
767+
integrationId: slackProperties.data.integrationId,
768+
organizationId: alert.project.organizationId,
769+
},
770+
{ includeDeleted: true }
771+
);
768772

769773
if (!integration) {
770774
logger.error("[DeliverAlert] Slack integration not found", {

‎apps/webapp/app/v3/services/alerts/deliverDashboardAgentWatchAlert.server.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,14 @@ export class DeliverDashboardAgentWatchChannelAlertService {
277277
return;
278278
}
279279

280-
const integration = await findSlackIntegration(prisma, {
281-
integrationId: slackProperties.data.integrationId,
282-
organizationId: payload.organizationId,
283-
});
280+
const integration = await findSlackIntegration(
281+
prisma,
282+
{
283+
integrationId: slackProperties.data.integrationId,
284+
organizationId: payload.organizationId,
285+
},
286+
{ includeDeleted: true }
287+
);
284288

285289
if (!integration) {
286290
logger.error("[DeliverDashboardAgentWatchAlert] Slack integration not found");

‎apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts‎

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
buildErrorGroupSlackMessage,
2929
type ErrorAlertPayload,
3030
} from "./errorGroupSlackMessage.server";
31+
import { findSlackIntegration } from "./alertTransport.server";
3132
import { safeWebhookFetch } from "./safeWebhookFetch.server";
3233

3334
class SkipRetryError extends Error {}
@@ -143,24 +144,14 @@ export class DeliverErrorGroupAlertService {
143144
return;
144145
}
145146

146-
const integration = slackProperties.data.integrationId
147-
? await prisma.organizationIntegration.findFirst({
148-
where: {
149-
id: slackProperties.data.integrationId,
150-
organizationId: channel.project.organizationId,
151-
deletedAt: null,
152-
},
153-
include: { tokenReference: true },
154-
})
155-
: await prisma.organizationIntegration.findFirst({
156-
where: {
157-
service: "SLACK",
158-
organizationId: channel.project.organizationId,
159-
deletedAt: null,
160-
},
161-
orderBy: { createdAt: "desc" },
162-
include: { tokenReference: true },
163-
});
147+
const integration = await findSlackIntegration(
148+
prisma,
149+
{
150+
integrationId: slackProperties.data.integrationId,
151+
organizationId: channel.project.organizationId,
152+
},
153+
{ includeDeleted: true }
154+
);
164155

165156
if (!integration || !isIntegrationForService(integration, "SLACK")) {
166157
logger.error("[DeliverErrorGroupAlert] Slack integration not found");

‎apps/webapp/app/v3/services/alerts/findSlackIntegration.server.test.ts‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,60 @@ postgresTest("ignores a soft-deleted Slack integration", async ({ prisma }) => {
4747
).toBeNull();
4848
expect(await findSlackIntegration(prisma, { organizationId: organization.id })).toBeNull();
4949
});
50+
51+
postgresTest(
52+
"resolves a soft-deleted integration by id when includeDeleted is set",
53+
async ({ prisma }) => {
54+
const { organization, integration } = await seed(prisma, true);
55+
56+
const byId = await findSlackIntegration(
57+
prisma,
58+
{ integrationId: integration.id, organizationId: organization.id },
59+
{ includeDeleted: true }
60+
);
61+
62+
expect(byId?.id).toBe(integration.id);
63+
}
64+
);
65+
66+
postgresTest(
67+
"resolves a soft-deleted integration by organization when includeDeleted is set",
68+
async ({ prisma }) => {
69+
const { organization, integration } = await seed(prisma, true);
70+
71+
const byOrg = await findSlackIntegration(
72+
prisma,
73+
{ organizationId: organization.id },
74+
{ includeDeleted: true }
75+
);
76+
77+
expect(byOrg?.id).toBe(integration.id);
78+
}
79+
);
80+
81+
postgresTest(
82+
"prefers an older live integration over a newer soft-deleted one when includeDeleted is set",
83+
async ({ prisma }) => {
84+
const { organization, integration: live } = await seed(prisma, false);
85+
const secret = await prisma.secretReference.create({ data: { key: "slack-lookup-token-2" } });
86+
await prisma.organizationIntegration.create({
87+
data: {
88+
friendlyId: generateFriendlyId("org_integration"),
89+
service: "SLACK",
90+
integrationData: {},
91+
tokenReferenceId: secret.id,
92+
organizationId: organization.id,
93+
createdAt: new Date(Date.now() + 60_000),
94+
deletedAt: new Date(),
95+
},
96+
});
97+
98+
const byOrg = await findSlackIntegration(
99+
prisma,
100+
{ organizationId: organization.id },
101+
{ includeDeleted: true }
102+
);
103+
104+
expect(byOrg?.id).toBe(live.id);
105+
}
106+
);

0 commit comments

Comments
 (0)