fix: managed event type bug (#27047)
This commit is contained in:
1 parent
cb6b52b334
commit
6c3b10672c
3 files changed
+198
No files matched your search
@@ -240,6 +240,9 @@ describe("handleChildrenEventTypes", () => {
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.findMany.mockResolvedValue([{ userId: 4, metadata: {} }]);
|
||||
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.update.mockResolvedValue({ id: 100 });
|
||||
|
||||
const result = await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
oldEventType: { children: [{ userId: 4 }], team: { name: "" } },
|
||||
@@ -313,6 +316,9 @@ describe("handleChildrenEventTypes", () => {
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.findMany.mockResolvedValue([{ userId: 4, metadata: {} }]);
|
||||
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.update.mockResolvedValue({ id: 101 });
|
||||
|
||||
const result = await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
oldEventType: { children: [{ userId: 4 }, { userId: 1 }], team: { name: "" } },
|
||||
@@ -450,6 +456,9 @@ describe("handleChildrenEventTypes", () => {
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.findMany.mockResolvedValue([{ userId: 4, metadata: {} }]);
|
||||
|
||||
// @ts-expect-error - partial mock for test purposes
|
||||
prismaMock.eventType.update.mockResolvedValue({ id: 102 });
|
||||
|
||||
prismaMock.eventType.deleteMany.mockResolvedValue([123] as unknown as Prisma.BatchPayload);
|
||||
const result = await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
@@ -960,4 +969,98 @@ describe("handleChildrenEventTypes", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("CalVideoSettings propagation", () => {
|
||||
it("Creates CalVideoSettings for new children when parent has settings", async () => {
|
||||
const { schedulingType, teamId, timeZone, ...evType } = mockFindFirstEventType({
|
||||
id: 123,
|
||||
metadata: { managedEventConfig: {} },
|
||||
locations: [],
|
||||
});
|
||||
|
||||
setupTransactionMock();
|
||||
prismaMock.eventType.createManyAndReturn.mockResolvedValue([
|
||||
{ ...evType, id: 456, userId: 4, schedulingType, teamId, timeZone } as unknown as EventType,
|
||||
]);
|
||||
|
||||
await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
oldEventType: { children: [], team: { name: "" } },
|
||||
children: [{ hidden: false, owner: { id: 4, name: "", email: "", eventTypeSlugs: [] } }],
|
||||
updatedEventType: { schedulingType: "MANAGED", slug: "something" },
|
||||
currentUserId: 1,
|
||||
prisma: prismaMock,
|
||||
profileId: null,
|
||||
updatedValues: {},
|
||||
calVideoSettings: {
|
||||
enableAutomaticRecordingForOrganizer: true,
|
||||
enableAutomaticTranscription: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(prismaMock.calVideoSettings.createMany).toHaveBeenCalledWith({
|
||||
data: [
|
||||
expect.objectContaining({
|
||||
eventTypeId: 456,
|
||||
enableAutomaticRecordingForOrganizer: true,
|
||||
enableAutomaticTranscription: true,
|
||||
}),
|
||||
],
|
||||
skipDuplicates: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("Syncs/deletes CalVideoSettings for existing children based on parent settings", async () => {
|
||||
const mockExistingChildUpdate = () => {
|
||||
mockFindFirstEventType({ metadata: { managedEventConfig: {} }, locations: [] });
|
||||
// @ts-expect-error - partial mock
|
||||
prismaMock.eventType.findMany.mockResolvedValue([{ userId: 4, metadata: {} }]);
|
||||
prismaMock.eventType.update.mockResolvedValue({
|
||||
id: 789,
|
||||
userId: 4,
|
||||
schedulingType: SchedulingType.MANAGED,
|
||||
} as unknown as EventType);
|
||||
};
|
||||
|
||||
const baseParams = {
|
||||
eventTypeId: 1,
|
||||
oldEventType: { children: [{ userId: 4 }], team: { name: "" } },
|
||||
children: [{ hidden: false, owner: { id: 4, name: "", email: "", eventTypeSlugs: [] } }],
|
||||
updatedEventType: { schedulingType: "MANAGED" as const, slug: "something" },
|
||||
currentUserId: 1,
|
||||
prisma: prismaMock,
|
||||
profileId: null,
|
||||
updatedValues: {},
|
||||
};
|
||||
|
||||
// Test upsert when calVideoSettings provided
|
||||
mockExistingChildUpdate();
|
||||
await updateChildrenEventTypes({
|
||||
...baseParams,
|
||||
calVideoSettings: { enableAutomaticRecordingForOrganizer: true },
|
||||
});
|
||||
expect(prismaMock.calVideoSettings.upsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { eventTypeId: 789 },
|
||||
update: expect.objectContaining({ enableAutomaticRecordingForOrganizer: true }),
|
||||
create: expect.objectContaining({ eventTypeId: 789, enableAutomaticRecordingForOrganizer: true }),
|
||||
})
|
||||
);
|
||||
|
||||
// Test deleteMany when calVideoSettings is null
|
||||
vi.clearAllMocks();
|
||||
mockExistingChildUpdate();
|
||||
await updateChildrenEventTypes({ ...baseParams, calVideoSettings: null });
|
||||
expect(prismaMock.calVideoSettings.deleteMany).toHaveBeenCalledWith({
|
||||
where: { eventTypeId: { in: [789] } },
|
||||
});
|
||||
|
||||
// Test no-op when calVideoSettings is undefined
|
||||
vi.clearAllMocks();
|
||||
mockExistingChildUpdate();
|
||||
await updateChildrenEventTypes({ ...baseParams, calVideoSettings: undefined });
|
||||
expect(prismaMock.calVideoSettings.upsert).not.toHaveBeenCalled();
|
||||
expect(prismaMock.calVideoSettings.deleteMany).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -40,6 +40,16 @@ interface handleChildrenEventTypesProps {
|
||||
| undefined;
|
||||
prisma: PrismaClient | DeepMockProxy<PrismaClient>;
|
||||
updatedValues: Prisma.EventTypeUpdateInput;
|
||||
calVideoSettings?: {
|
||||
disableRecordingForGuests?: boolean | null;
|
||||
disableRecordingForOrganizer?: boolean | null;
|
||||
enableAutomaticTranscription?: boolean | null;
|
||||
enableAutomaticRecordingForOrganizer?: boolean | null;
|
||||
disableTranscriptionForGuests?: boolean | null;
|
||||
disableTranscriptionForOrganizer?: boolean | null;
|
||||
redirectUrlOnExit?: string | null;
|
||||
requireEmailForGuests?: boolean | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
const sendAllSlugReplacementEmails = async (
|
||||
@@ -138,6 +148,7 @@ export default async function handleChildrenEventTypes({
|
||||
prisma,
|
||||
profileId,
|
||||
updatedValues: _updatedValues,
|
||||
calVideoSettings,
|
||||
}: handleChildrenEventTypesProps) {
|
||||
// Check we are dealing with a managed event type
|
||||
if (updatedEventType?.schedulingType !== SchedulingType.MANAGED)
|
||||
@@ -269,6 +280,26 @@ export default async function handleChildrenEventTypes({
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Create CalVideoSettings for new children if parent has settings
|
||||
if (calVideoSettings && createdEvents.length > 0) {
|
||||
const calVideoSettingsToCreate = createdEvents.map((event) => ({
|
||||
eventTypeId: event.id,
|
||||
disableRecordingForGuests: calVideoSettings.disableRecordingForGuests ?? false,
|
||||
disableRecordingForOrganizer: calVideoSettings.disableRecordingForOrganizer ?? false,
|
||||
enableAutomaticTranscription: calVideoSettings.enableAutomaticTranscription ?? false,
|
||||
enableAutomaticRecordingForOrganizer: calVideoSettings.enableAutomaticRecordingForOrganizer ?? false,
|
||||
disableTranscriptionForGuests: calVideoSettings.disableTranscriptionForGuests ?? false,
|
||||
disableTranscriptionForOrganizer: calVideoSettings.disableTranscriptionForOrganizer ?? false,
|
||||
redirectUrlOnExit: calVideoSettings.redirectUrlOnExit ?? null,
|
||||
requireEmailForGuests: calVideoSettings.requireEmailForGuests ?? false,
|
||||
}));
|
||||
|
||||
await tx.calVideoSettings.createMany({
|
||||
data: calVideoSettingsToCreate,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -422,6 +453,58 @@ export default async function handleChildrenEventTypes({
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sync CalVideoSettings to existing children
|
||||
if (oldEventTypes.length > 0) {
|
||||
const childEventTypeIds = oldEventTypes.map((e) => e.id);
|
||||
const BATCH_SIZE = 50;
|
||||
|
||||
if (calVideoSettings) {
|
||||
// Parent has CalVideoSettings - upsert for all children
|
||||
for (let i = 0; i < childEventTypeIds.length; i += BATCH_SIZE) {
|
||||
const batch = childEventTypeIds.slice(i, i + BATCH_SIZE);
|
||||
await Promise.all(
|
||||
batch.map((eventTypeId) =>
|
||||
prisma.calVideoSettings.upsert({
|
||||
where: { eventTypeId },
|
||||
update: {
|
||||
disableRecordingForGuests: calVideoSettings.disableRecordingForGuests ?? false,
|
||||
disableRecordingForOrganizer: calVideoSettings.disableRecordingForOrganizer ?? false,
|
||||
enableAutomaticTranscription: calVideoSettings.enableAutomaticTranscription ?? false,
|
||||
enableAutomaticRecordingForOrganizer:
|
||||
calVideoSettings.enableAutomaticRecordingForOrganizer ?? false,
|
||||
disableTranscriptionForGuests: calVideoSettings.disableTranscriptionForGuests ?? false,
|
||||
disableTranscriptionForOrganizer: calVideoSettings.disableTranscriptionForOrganizer ?? false,
|
||||
redirectUrlOnExit: calVideoSettings.redirectUrlOnExit ?? null,
|
||||
requireEmailForGuests: calVideoSettings.requireEmailForGuests ?? false,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
create: {
|
||||
eventTypeId,
|
||||
disableRecordingForGuests: calVideoSettings.disableRecordingForGuests ?? false,
|
||||
disableRecordingForOrganizer: calVideoSettings.disableRecordingForOrganizer ?? false,
|
||||
enableAutomaticTranscription: calVideoSettings.enableAutomaticTranscription ?? false,
|
||||
enableAutomaticRecordingForOrganizer:
|
||||
calVideoSettings.enableAutomaticRecordingForOrganizer ?? false,
|
||||
disableTranscriptionForGuests: calVideoSettings.disableTranscriptionForGuests ?? false,
|
||||
disableTranscriptionForOrganizer: calVideoSettings.disableTranscriptionForOrganizer ?? false,
|
||||
redirectUrlOnExit: calVideoSettings.redirectUrlOnExit ?? null,
|
||||
requireEmailForGuests: calVideoSettings.requireEmailForGuests ?? false,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if (calVideoSettings === null) {
|
||||
// Parent's CalVideoSettings were removed - delete from all children
|
||||
await prisma.calVideoSettings.deleteMany({
|
||||
where: {
|
||||
eventTypeId: { in: childEventTypeIds },
|
||||
},
|
||||
});
|
||||
}
|
||||
// Note: If calVideoSettings is undefined, don't touch children's settings
|
||||
}
|
||||
}
|
||||
|
||||
// Old users deleted
|
||||
|
||||
@@ -716,6 +716,17 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Determine calVideoSettings to pass to children:
|
||||
// - If calVideoSettings provided in input, sync to children
|
||||
// - If Cal Video location removed, delete from children (pass null)
|
||||
// - Otherwise, leave children's settings untouched (pass undefined)
|
||||
let calVideoSettingsForChildren: typeof calVideoSettings | null | undefined = undefined;
|
||||
if (calVideoSettings !== undefined) {
|
||||
calVideoSettingsForChildren = calVideoSettings;
|
||||
} else if (eventType.calVideoSettings && !isCalVideoLocationActive) {
|
||||
calVideoSettingsForChildren = null;
|
||||
}
|
||||
|
||||
// Handling updates to children event types (managed events types)
|
||||
await updateChildrenEventTypes({
|
||||
eventTypeId: id,
|
||||
@@ -726,6 +737,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||
profileId: ctx.user.profile.id,
|
||||
prisma: ctx.prisma,
|
||||
updatedValues,
|
||||
calVideoSettings: calVideoSettingsForChildren,
|
||||
});
|
||||
|
||||
// Clean up empty host groups
|
||||
|
||||
Reference in new issue
Block a user