fix: Missing personal event types in All filter (#23343)
* fix: Missing personal event types in All filter Signed-off-by: Anirban Singha <anirbansingha20@gmail.com> * chore: coderabbit suggestion Signed-off-by: Anirban Singha <anirbansingha20@gmail.com> * chore: export function * chore: Add test file Signed-off-by: Anirban Singha <anirbansingha20@gmail.com> --------- Signed-off-by: Anirban Singha <anirbansingha20@gmail.com> Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
This commit is contained in:
co-authored by
Kartik Saini
parent
84bd4d3d38
commit
f1d7d7f02a
@@ -0,0 +1,199 @@
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
|
||||
import { readonlyPrisma } from "@calcom/prisma";
|
||||
|
||||
import { getEventTypeList } from "../trpc-router";
|
||||
|
||||
vi.mock("@calcom/prisma", () => ({
|
||||
readonlyPrisma: {
|
||||
eventType: {
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
team: {
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
membership: {
|
||||
findFirst: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe("getEventTypeList", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
const mockUser = {
|
||||
id: 1,
|
||||
organizationId: 10,
|
||||
isOwnerAdminOfParentTeam: false,
|
||||
};
|
||||
|
||||
const mockEventTypes = [
|
||||
{
|
||||
id: 1,
|
||||
slug: "personal-event",
|
||||
title: "Personal Event",
|
||||
teamId: null,
|
||||
userId: 1,
|
||||
team: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
slug: "team-event",
|
||||
title: "Team Event",
|
||||
teamId: 5,
|
||||
userId: null,
|
||||
team: { name: "Team A" },
|
||||
},
|
||||
];
|
||||
|
||||
describe("Early return scenarios", () => {
|
||||
it("should return empty array when no teamId, userId, or isAll provided", async () => {
|
||||
const result = await getEventTypeList({
|
||||
prisma: readonlyPrisma,
|
||||
teamId: null,
|
||||
userId: null,
|
||||
isAll: false,
|
||||
user: mockUser,
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(readonlyPrisma.eventType.findMany).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Personal events filtering", () => {
|
||||
it("should return only user's personal events when userId provided", async () => {
|
||||
const personalEvents = [mockEventTypes[0]];
|
||||
vi.mocked(readonlyPrisma.eventType.findMany).mockResolvedValue(personalEvents);
|
||||
|
||||
const result = await getEventTypeList({
|
||||
prisma: readonlyPrisma,
|
||||
teamId: null,
|
||||
userId: 1,
|
||||
isAll: false,
|
||||
user: mockUser,
|
||||
});
|
||||
|
||||
expect(readonlyPrisma.eventType.findMany).toHaveBeenCalledWith({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
userId: mockUser.id,
|
||||
teamId: null,
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(personalEvents);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Organization-wide view (isAll = true)", () => {
|
||||
it("should return team events and user's personal events for owner/admin", async () => {
|
||||
const childTeams = [{ id: 11 }, { id: 12 }];
|
||||
const allEvents = [...mockEventTypes];
|
||||
|
||||
vi.mocked(readonlyPrisma.team.findMany).mockResolvedValue(childTeams);
|
||||
vi.mocked(readonlyPrisma.eventType.findMany).mockResolvedValue(allEvents);
|
||||
|
||||
const ownerUser = { ...mockUser, isOwnerAdminOfParentTeam: true };
|
||||
|
||||
const result = await getEventTypeList({
|
||||
prisma: readonlyPrisma,
|
||||
teamId: null,
|
||||
userId: null,
|
||||
isAll: true,
|
||||
user: ownerUser,
|
||||
});
|
||||
|
||||
expect(readonlyPrisma.eventType.findMany).toHaveBeenCalledWith({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
teamId: {
|
||||
in: [10, 11, 12],
|
||||
},
|
||||
},
|
||||
{
|
||||
userId: ownerUser.id,
|
||||
teamId: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(allEvents);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Team-specific view", () => {
|
||||
it("should return team events for team members", async () => {
|
||||
const membership = { teamId: 5, userId: 1, role: "MEMBER" };
|
||||
vi.mocked(readonlyPrisma.membership.findFirst).mockResolvedValue(membership);
|
||||
vi.mocked(readonlyPrisma.eventType.findMany).mockResolvedValue([mockEventTypes[1]]);
|
||||
|
||||
const result = await getEventTypeList({
|
||||
prisma: readonlyPrisma,
|
||||
teamId: 5,
|
||||
userId: null,
|
||||
isAll: false,
|
||||
user: mockUser,
|
||||
});
|
||||
|
||||
expect(readonlyPrisma.eventType.findMany).toHaveBeenCalledWith({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
teamId: 5,
|
||||
OR: [{ userId: mockUser.id }, { users: { some: { id: mockUser.id } } }],
|
||||
},
|
||||
});
|
||||
expect(result).toEqual([mockEventTypes[1]]);
|
||||
});
|
||||
|
||||
it("should throw error when user is not part of team and not owner/admin", async () => {
|
||||
vi.mocked(readonlyPrisma.membership.findFirst).mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
getEventTypeList({
|
||||
prisma: readonlyPrisma,
|
||||
teamId: 5,
|
||||
userId: null,
|
||||
isAll: false,
|
||||
user: mockUser,
|
||||
})
|
||||
).rejects.toThrow("User is not part of a team/org");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1053,7 +1053,7 @@ export const insightsRouter = router({
|
||||
}),
|
||||
});
|
||||
|
||||
async function getEventTypeList({
|
||||
export async function getEventTypeList({
|
||||
prisma,
|
||||
teamId,
|
||||
userId,
|
||||
@@ -1070,15 +1070,37 @@ async function getEventTypeList({
|
||||
isOwnerAdminOfParentTeam: boolean;
|
||||
};
|
||||
}) {
|
||||
if (!teamId && !userId) {
|
||||
if (!teamId && !userId && !isAll) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const membershipWhereConditional: Prisma.MembershipWhereInput = {};
|
||||
|
||||
let childrenTeamIds: number[] = [];
|
||||
|
||||
if (isAll && teamId && user.organizationId && user.isOwnerAdminOfParentTeam) {
|
||||
if (userId && !teamId && !isAll) {
|
||||
const eventTypeResult = await prisma.eventType.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: null,
|
||||
},
|
||||
});
|
||||
|
||||
return eventTypeResult;
|
||||
}
|
||||
|
||||
if (isAll && user.organizationId && user.isOwnerAdminOfParentTeam) {
|
||||
const childTeams = await prisma.team.findMany({
|
||||
where: {
|
||||
parentId: user.organizationId,
|
||||
@@ -1087,21 +1109,46 @@ async function getEventTypeList({
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (childTeams.length > 0) {
|
||||
childrenTeamIds = childTeams.map((team) => team.id);
|
||||
}
|
||||
membershipWhereConditional["teamId"] = {
|
||||
in: [user.organizationId, ...childrenTeamIds],
|
||||
};
|
||||
|
||||
const eventTypeResult = await prisma.eventType.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
teamId: {
|
||||
in: [user.organizationId, ...childrenTeamIds],
|
||||
},
|
||||
},
|
||||
{
|
||||
userId: user.id,
|
||||
teamId: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
return eventTypeResult;
|
||||
}
|
||||
|
||||
if (teamId && !isAll) {
|
||||
membershipWhereConditional["teamId"] = teamId;
|
||||
membershipWhereConditional["userId"] = user.id;
|
||||
}
|
||||
if (userId) {
|
||||
membershipWhereConditional["userId"] = userId;
|
||||
}
|
||||
|
||||
// I'm not using unique here since when userId comes from input we should look for every
|
||||
// event type that user owns
|
||||
@@ -1114,49 +1161,29 @@ async function getEventTypeList({
|
||||
}
|
||||
|
||||
const eventTypeWhereConditional: Prisma.EventTypeWhereInput = {};
|
||||
if (isAll && childrenTeamIds.length > 0 && user.organizationId && user.isOwnerAdminOfParentTeam) {
|
||||
eventTypeWhereConditional["teamId"] = {
|
||||
in: [user.organizationId, ...childrenTeamIds],
|
||||
};
|
||||
}
|
||||
|
||||
if (teamId && !isAll) {
|
||||
eventTypeWhereConditional["teamId"] = teamId;
|
||||
}
|
||||
if (userId) {
|
||||
eventTypeWhereConditional["userId"] = userId;
|
||||
}
|
||||
let eventTypeResult: Prisma.EventTypeGetPayload<{
|
||||
select: {
|
||||
id: true;
|
||||
slug: true;
|
||||
teamId: true;
|
||||
title: true;
|
||||
team: {
|
||||
select: {
|
||||
name: true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>[] = [];
|
||||
|
||||
let isMember = membership?.role === "MEMBER";
|
||||
if (user.isOwnerAdminOfParentTeam) {
|
||||
isMember = false;
|
||||
}
|
||||
|
||||
if (isMember) {
|
||||
eventTypeWhereConditional["OR"] = [
|
||||
{ userId: user.id },
|
||||
{ users: { some: { id: user.id } } },
|
||||
// @TODO this is not working as expected
|
||||
// hosts: { some: { id: user.id } },
|
||||
];
|
||||
eventTypeWhereConditional["OR"] = [{ userId: user.id }, { users: { some: { id: user.id } } }];
|
||||
// @TODO this is not working as expected
|
||||
// hosts: { some: { id: user.id } },
|
||||
}
|
||||
eventTypeResult = await prisma.eventType.findMany({
|
||||
|
||||
const eventTypeResult = await prisma.eventType.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
teamId: true,
|
||||
userId: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
|
||||
Reference in New Issue
Block a user