Skip to content

Commit b8530e2

Browse files
fix: delegation credential error webhooks + refactor repeated code (calcom#25232)
* fix: delegation credential error webhooks * refactor: extract repeated delegation credential error webhook logic into helper methods - Added private triggerDelegationCredentialError method in Office365CalendarService class - Added triggerDelegationCredentialError helper function in TeamsVideoApiAdapter - Replaced all 4 instances in Office365CalendarService with helper method call - Replaced all 4 instances in TeamsVideoApiAdapter with helper function call - Keeps code DRY by eliminating repeated if statement and webhook trigger logic Co-Authored-By: morgan@cal.com <morgan@cal.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 7f1b94a commit b8530e2

6 files changed

Lines changed: 99 additions & 129 deletions

File tree

packages/app-store/googlecalendar/lib/CalendarAuth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export class CalendarAuth {
140140
delegationError = new CalendarAppDelegationCredentialError("Error authorizing delegation credential");
141141
}
142142

143-
if (user && user.email && this.credential.appId) {
143+
if (user && user.email && this.credential.appId && this.credential.delegatedToId) {
144144
await triggerDelegationCredentialErrorWebhook({
145145
error: delegationError,
146146
credential: {
@@ -152,7 +152,7 @@ export class CalendarAuth {
152152
id: this.credential.userId ?? 0,
153153
email: user.email,
154154
},
155-
orgId: this.credential.teamId,
155+
delegationCredentialId: this.credential.delegatedToId,
156156
});
157157
}
158158

packages/app-store/office365calendar/lib/CalendarService.ts

Lines changed: 27 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -124,28 +124,37 @@ export default class Office365CalendarService implements Calendar {
124124
this.log = logger.getSubLogger({ prefix: [`[[lib] ${this.integrationName}`] });
125125
}
126126

127+
private async triggerDelegationCredentialError(error: Error): Promise<void> {
128+
if (
129+
this.credential.userId &&
130+
this.credential.user &&
131+
this.credential.appId &&
132+
this.credential.delegatedToId
133+
) {
134+
await triggerDelegationCredentialErrorWebhook({
135+
error,
136+
credential: {
137+
id: this.credential.id,
138+
type: this.credential.type,
139+
appId: this.credential.appId,
140+
},
141+
user: {
142+
id: this.credential.userId,
143+
email: this.credential.user.email,
144+
},
145+
delegationCredentialId: this.credential.delegatedToId,
146+
});
147+
}
148+
}
149+
127150
private async getAuthUrl(delegatedTo: boolean, tenantId?: string): Promise<string> {
128151
if (delegatedTo) {
129152
if (!tenantId) {
130153
const error = new CalendarAppDelegationCredentialInvalidGrantError(
131154
"Invalid DelegationCredential Settings: tenantId is missing"
132155
);
133156

134-
if (this.credential.userId && this.credential.user && this.credential.appId) {
135-
await triggerDelegationCredentialErrorWebhook({
136-
error,
137-
credential: {
138-
id: this.credential.id,
139-
type: this.credential.type,
140-
appId: this.credential.appId,
141-
},
142-
user: {
143-
id: this.credential.userId,
144-
email: this.credential.user.email,
145-
},
146-
orgId: this.credential.teamId,
147-
});
148-
}
157+
await this.triggerDelegationCredentialError(error);
149158

150159
throw error;
151160
}
@@ -165,21 +174,7 @@ export default class Office365CalendarService implements Calendar {
165174
"Delegation credential without clientId or Secret"
166175
);
167176

168-
if (this.credential.userId && this.credential.user && this.credential.appId) {
169-
await triggerDelegationCredentialErrorWebhook({
170-
error,
171-
credential: {
172-
id: this.credential.id,
173-
type: this.credential.type,
174-
appId: this.credential.appId,
175-
},
176-
user: {
177-
id: this.credential.userId,
178-
email: this.credential.user.email,
179-
},
180-
orgId: this.credential.teamId,
181-
});
182-
}
177+
await this.triggerDelegationCredentialError(error);
183178

184179
throw error;
185180
}
@@ -207,21 +202,7 @@ export default class Office365CalendarService implements Calendar {
207202
"Delegation credential without clientId or Secret"
208203
);
209204

210-
if (this.credential.userId && this.credential.user && this.credential.appId) {
211-
await triggerDelegationCredentialErrorWebhook({
212-
error,
213-
credential: {
214-
id: this.credential.id,
215-
type: this.credential.type,
216-
appId: this.credential.appId,
217-
},
218-
user: {
219-
id: this.credential.userId,
220-
email: this.credential.user.email,
221-
},
222-
orgId: this.credential.teamId,
223-
});
224-
}
205+
await this.triggerDelegationCredentialError(error);
225206

226207
throw error;
227208
}
@@ -260,21 +241,7 @@ export default class Office365CalendarService implements Calendar {
260241
"User might not exist in Microsoft Azure Active Directory"
261242
);
262243

263-
if (this.credential.userId && this.credential.user && this.credential.appId) {
264-
await triggerDelegationCredentialErrorWebhook({
265-
error,
266-
credential: {
267-
id: this.credential.id,
268-
type: this.credential.type,
269-
appId: this.credential.appId,
270-
},
271-
user: {
272-
id: this.credential.userId ?? 0,
273-
email: this.credential.user.email,
274-
},
275-
orgId: this.credential.teamId,
276-
});
277-
}
244+
await this.triggerDelegationCredentialError(error);
278245

279246
throw error;
280247
}

packages/app-store/office365video/lib/VideoApiAdapter.ts

Lines changed: 22 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
4242
let azureUserId: string | null;
4343
const tokenResponse = oAuthManagerHelper.getTokenObjectFromCredential(credential);
4444

45+
async function triggerDelegationCredentialError(error: Error): Promise<void> {
46+
if (credential.userId && credential.user && credential.appId && credential.delegatedToId) {
47+
await triggerDelegationCredentialErrorWebhook({
48+
error,
49+
credential: {
50+
id: credential.id,
51+
type: credential.type,
52+
appId: credential.appId,
53+
},
54+
user: {
55+
id: credential.userId ?? 0,
56+
email: credential.user.email,
57+
},
58+
delegationCredentialId: credential.delegatedToId,
59+
});
60+
}
61+
}
62+
4563
const auth = new OAuthManager({
4664
credentialSyncVariables: oAuthManagerHelper.credentialSyncVariables,
4765
resourceOwner: {
@@ -68,21 +86,7 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
6886
"Delegation credential without clientId or Secret"
6987
);
7088

71-
if (credential.userId && credential.user && credential.appId) {
72-
await triggerDelegationCredentialErrorWebhook({
73-
error,
74-
credential: {
75-
id: credential.id,
76-
type: credential.type,
77-
appId: credential.appId,
78-
},
79-
user: {
80-
id: credential.userId ?? 0,
81-
email: credential.user.email,
82-
},
83-
orgId: credential.teamId,
84-
});
85-
}
89+
await triggerDelegationCredentialError(error);
8690

8791
throw error;
8892
}
@@ -130,21 +134,7 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
130134
"Invalid DelegationCredential Settings: tenantId is missing"
131135
);
132136

133-
if (credential.userId && credential.user && credential.appId) {
134-
await triggerDelegationCredentialErrorWebhook({
135-
error,
136-
credential: {
137-
id: credential.id,
138-
type: credential.type,
139-
appId: credential.appId,
140-
},
141-
user: {
142-
id: credential.userId ?? 0,
143-
email: credential.user.email,
144-
},
145-
orgId: credential.teamId,
146-
});
147-
}
137+
await triggerDelegationCredentialError(error);
148138

149139
throw error;
150140
}
@@ -179,21 +169,7 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
179169
"Delegation credential without clientId or Secret"
180170
);
181171

182-
if (credential.userId && credential.user && credential.appId) {
183-
await triggerDelegationCredentialErrorWebhook({
184-
error,
185-
credential: {
186-
id: credential.id,
187-
type: credential.type,
188-
appId: credential.appId,
189-
},
190-
user: {
191-
id: credential.userId ?? 0,
192-
email: credential.user.email,
193-
},
194-
orgId: credential.teamId,
195-
});
196-
}
172+
await triggerDelegationCredentialError(error);
197173

198174
throw error;
199175
}
@@ -231,21 +207,7 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
231207
"User might not exist in Microsoft Azure Active Directory"
232208
);
233209

234-
if (credential.userId && credential.user && credential.appId) {
235-
await triggerDelegationCredentialErrorWebhook({
236-
error,
237-
credential: {
238-
id: credential.id,
239-
type: credential.type,
240-
appId: credential.appId,
241-
},
242-
user: {
243-
id: credential.userId ?? 0,
244-
email: credential.user.email,
245-
},
246-
orgId: credential.teamId,
247-
});
248-
}
210+
await triggerDelegationCredentialError(error);
249211

250212
throw error;
251213
}

packages/features/webhooks/lib/dto/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,12 @@ export type DelegationCredentialErrorPayloadType = {
498498
id: number;
499499
type: string;
500500
appId: string;
501+
delegationCredentialId?: string;
501502
};
502503
user: {
503504
id: number;
504505
email: string;
506+
organizationId?: number;
505507
};
506508
};
507509

packages/features/webhooks/lib/repository/WebhookRepository.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,36 @@ export class WebhookRepository implements IWebhookRepository {
261261
});
262262
}
263263

264+
async findByOrgIdAndTrigger({
265+
orgId,
266+
triggerEvent,
267+
}: {
268+
orgId: number;
269+
triggerEvent: WebhookTriggerEvents;
270+
}): Promise<WebhookSubscriber[]> {
271+
return await this.prisma.webhook.findMany({
272+
where: {
273+
teamId: orgId,
274+
platform: false,
275+
eventTriggers: { has: triggerEvent },
276+
},
277+
select: {
278+
id: true,
279+
subscriberUrl: true,
280+
payloadTemplate: true,
281+
active: true,
282+
eventTriggers: true,
283+
secret: true,
284+
teamId: true,
285+
userId: true,
286+
platform: true,
287+
time: true,
288+
timeUnit: true,
289+
appId: true,
290+
},
291+
});
292+
}
293+
264294
async getFilteredWebhooksForUser({ userId, userRole }: { userId: number; userRole?: UserPermissionRole }) {
265295
const user = await this.prisma.user.findUnique({
266296
where: { id: userId },

packages/features/webhooks/lib/triggerDelegationCredentialErrorWebhook.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DelegationCredentialRepository } from "@calcom/features/delegation-credentials/repositories/DelegationCredentialRepository";
12
import { DelegationCredentialErrorPayloadType } from "@calcom/features/webhooks/lib/dto/types";
23
import type { CalendarAppDelegationCredentialError } from "@calcom/lib/CalendarAppError";
34
import logger from "@calcom/lib/logger";
@@ -15,24 +16,30 @@ export async function triggerDelegationCredentialErrorWebhook(params: {
1516
error: CalendarAppDelegationCredentialError;
1617
credential: DelegationCredentialErrorPayloadType["credential"];
1718
user: DelegationCredentialErrorPayloadType["user"];
18-
orgId?: number | null;
19+
delegationCredentialId: string;
1920
}): Promise<void> {
2021
try {
21-
const { error, credential, user, orgId } = params;
22+
const { error, credential, user, delegationCredentialId } = params;
2223

23-
if (!orgId) {
24-
log.debug("Skipping webhook emission - no organization context");
24+
const delegationCredential = await DelegationCredentialRepository.findById({
25+
id: delegationCredentialId,
26+
});
27+
28+
if (!delegationCredential) {
29+
log.debug("No delegation credential found", { delegationCredentialId });
2530
return;
2631
}
2732

2833
const webhookRepository = WebhookRepository.getInstance();
29-
const webhooks = await webhookRepository.getSubscribers({
30-
teamId: orgId,
34+
const webhooks = await webhookRepository.findByOrgIdAndTrigger({
35+
orgId: delegationCredential.organizationId,
3136
triggerEvent: WebhookTriggerEvents.DELEGATION_CREDENTIAL_ERROR,
3237
});
3338

3439
if (webhooks.length === 0) {
35-
log.debug("No webhooks subscribed to DELEGATION_CREDENTIAL_ERROR for organization", { orgId });
40+
log.debug("No webhooks subscribed to DELEGATION_CREDENTIAL_ERROR for organization", {
41+
orgId: delegationCredential.organizationId,
42+
});
3643
return;
3744
}
3845

@@ -45,10 +52,12 @@ export async function triggerDelegationCredentialErrorWebhook(params: {
4552
id: credential.id,
4653
type: credential.type,
4754
appId: credential.appId || "unknown",
55+
delegationCredentialId: delegationCredential.id,
4856
},
4957
user: {
5058
id: user.id,
5159
email: user.email,
60+
organizationId: delegationCredential.organizationId,
5261
},
5362
} satisfies DelegationCredentialErrorPayloadType;
5463

0 commit comments

Comments
 (0)