Skip to content

Commit 55cc1ae

Browse files
Udit-takkaremrysal
andauthored
perf: optimize listWithTeamHandler query (calcom#22069)
* perf: optimize listWithTeamHandler query * chore: update query * chore: add accepted true * Update packages/trpc/server/routers/viewer/eventTypes/listWithTeam.handler.ts * test: add integration test for the sql query --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>
1 parent ae38316 commit 55cc1ae

2 files changed

Lines changed: 173 additions & 28 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import type { User, Team, EventType } from "@prisma/client";
2+
import { describe, it, expect, afterAll, beforeAll } from "vitest";
3+
4+
import prisma from "@calcom/prisma";
5+
import { MembershipRole } from "@calcom/prisma/enums";
6+
7+
import { listWithTeamHandler } from "./listWithTeam.handler";
8+
9+
let user1: User;
10+
let user2: User;
11+
let team1: Team;
12+
let team2: Team;
13+
let eventType1: EventType;
14+
let eventType2: EventType;
15+
let eventType3: EventType;
16+
let eventType4: EventType;
17+
18+
describe("listWithTeamHandler", () => {
19+
beforeAll(async () => {
20+
// Create users, teams and event types
21+
user1 = await prisma.user.create({
22+
data: {
23+
username: "testuser-lwt-1",
24+
email: "testuser-lwt-1@example.com",
25+
name: "Test User 1",
26+
},
27+
});
28+
user2 = await prisma.user.create({
29+
data: {
30+
username: "testuser-lwt-2",
31+
email: "testuser-lwt-2@example.com",
32+
name: "Test User 2",
33+
},
34+
});
35+
36+
team1 = await prisma.team.create({
37+
data: {
38+
name: "Team 1 lwt",
39+
slug: "team-1-lwt",
40+
members: {
41+
create: {
42+
userId: user1.id,
43+
role: MembershipRole.MEMBER,
44+
accepted: true,
45+
},
46+
},
47+
},
48+
});
49+
50+
team2 = await prisma.team.create({
51+
data: {
52+
name: "Team 2 lwt",
53+
slug: "team-2-lwt",
54+
},
55+
});
56+
57+
eventType1 = await prisma.eventType.create({
58+
data: {
59+
title: "User 1 Event",
60+
slug: "user1-event-lwt",
61+
length: 30,
62+
userId: user1.id,
63+
},
64+
});
65+
66+
eventType2 = await prisma.eventType.create({
67+
data: {
68+
title: "Team 1 Event",
69+
slug: "team1-event-lwt",
70+
length: 30,
71+
teamId: team1.id,
72+
userId: user1.id,
73+
},
74+
});
75+
76+
eventType3 = await prisma.eventType.create({
77+
data: {
78+
title: "User 2 Event",
79+
slug: "user2-event-lwt",
80+
length: 30,
81+
userId: user2.id,
82+
},
83+
});
84+
85+
eventType4 = await prisma.eventType.create({
86+
data: {
87+
title: "Team 2 Event",
88+
slug: "team2-event-lwt",
89+
length: 30,
90+
teamId: team2.id,
91+
userId: user2.id,
92+
},
93+
});
94+
});
95+
96+
afterAll(async () => {
97+
await prisma.eventType.deleteMany({
98+
where: {
99+
id: {
100+
in: [eventType1.id, eventType2.id, eventType3.id, eventType4.id],
101+
},
102+
},
103+
});
104+
await prisma.team.deleteMany({
105+
where: {
106+
id: {
107+
in: [team1.id, team2.id],
108+
},
109+
},
110+
});
111+
await prisma.user.deleteMany({
112+
where: {
113+
id: {
114+
in: [user1.id, user2.id],
115+
},
116+
},
117+
});
118+
});
119+
120+
it("should return user's own event types and event types of teams they are a member of", async () => {
121+
const result = await listWithTeamHandler({
122+
ctx: {
123+
// we only need the id from the user object
124+
user: { id: user1.id } as any,
125+
},
126+
});
127+
128+
expect(result).toHaveLength(2);
129+
130+
const resultIds = result.map((e) => e.id);
131+
expect(resultIds).toContain(eventType1.id);
132+
expect(resultIds).toContain(eventType2.id);
133+
134+
const eventType1Result = result.find((e) => e.id === eventType1.id);
135+
expect(eventType1Result).toBeDefined();
136+
expect(eventType1Result?.title).toBe(eventType1.title);
137+
expect(eventType1Result?.slug).toBe(eventType1.slug);
138+
expect(eventType1Result?.team).toBeNull();
139+
140+
const eventType2Result = result.find((e) => e.id === eventType2.id);
141+
expect(eventType2Result).toBeDefined();
142+
expect(eventType2Result?.title).toBe(eventType2.title);
143+
expect(eventType2Result?.slug).toBe(eventType2.slug);
144+
expect(eventType2Result?.team).toBeDefined();
145+
expect(eventType2Result?.team?.id).toBe(team1.id);
146+
expect(eventType2Result?.team?.name).toBe(team1.name);
147+
});
148+
});
Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { prisma } from "@calcom/prisma";
1+
import { Prisma } from "@prisma/client";
2+
3+
import db from "@calcom/prisma";
24

35
import type { TrpcSessionUser } from "../../../types";
46

@@ -9,31 +11,26 @@ type ListWithTeamOptions = {
911
};
1012

1113
export const listWithTeamHandler = async ({ ctx }: ListWithTeamOptions) => {
12-
return await prisma.eventType.findMany({
13-
where: {
14-
OR: [
15-
{ userId: ctx.user.id },
16-
{
17-
team: {
18-
members: {
19-
some: {
20-
userId: ctx.user.id,
21-
},
22-
},
23-
},
24-
},
25-
],
26-
},
27-
select: {
28-
id: true,
29-
team: {
30-
select: {
31-
id: true,
32-
name: true,
33-
},
34-
},
35-
title: true,
36-
slug: true,
37-
},
38-
});
14+
const userId = ctx.user.id;
15+
const query = Prisma.sql`SELECT "public"."EventType"."id", "public"."EventType"."teamId", "public"."EventType"."title", "public"."EventType"."slug", "j1"."name" as "teamName"
16+
FROM "public"."EventType"
17+
LEFT JOIN "public"."Team" AS "j1" ON ("j1"."id") = ("public"."EventType"."teamId")
18+
WHERE "public"."EventType"."userId" = ${userId}
19+
UNION
20+
SELECT "public"."EventType"."id", "public"."EventType"."teamId", "public"."EventType"."title", "public"."EventType"."slug", "j1"."name" as "teamName"
21+
FROM "public"."EventType"
22+
INNER JOIN "public"."Team" AS "j1" ON ("j1"."id") = ("public"."EventType"."teamId")
23+
INNER JOIN "public"."Membership" AS "t2" ON "t2"."teamId" = "j1"."id"
24+
WHERE "t2"."userId" = ${userId} AND "t2"."accepted" = true`;
25+
26+
const result = await db.$queryRaw<
27+
{ id: number; teamId: number | null; title: string; slug: string; teamName: string | null }[]
28+
>(query);
29+
30+
return result.map((row) => ({
31+
id: row.id,
32+
team: row.teamId ? { id: row.teamId, name: row.teamName || "" } : null,
33+
title: row.title,
34+
slug: row.slug,
35+
}));
3936
};

0 commit comments

Comments
 (0)