cal.com icon indicating copy to clipboard operation
cal.com copied to clipboard

Improve

Open github-actions[bot] opened this issue 2 years ago • 1 comments

We don't want to pass the full schedule for just a set as default update

but in the current logic, this wipes the existing availability.

Return early to prevent this from happening.

https://api.github.com/calcom/cal.com/blob/1eeb91a793e577b10c3037a2158d7ad052659b20/packages/trpc/server/routers/viewer/availability/schedule/update.handler.ts#L56


import { getAvailabilityFromSchedule } from "@calcom/lib/availability";
import { prisma } from "@calcom/prisma";

import { TRPCError } from "@trpc/server";

import type { TrpcSessionUser } from "../../../../trpc";
import { convertScheduleToAvailability, setupDefaultSchedule } from "../util";
import type { TUpdateInputSchema } from "./update.schema";

type UpdateOptions = {
  ctx: {
    user: NonNullable<TrpcSessionUser>;
  };
  input: TUpdateInputSchema;
};

export const updateHandler = async ({ input, ctx }: UpdateOptions) => {
  const { user } = ctx;
  const availability = input.schedule
    ? getAvailabilityFromSchedule(input.schedule)
    : (input.dateOverrides || []).map((dateOverride) => ({
        startTime: dateOverride.start,
        endTime: dateOverride.end,
        date: dateOverride.start,
        days: [],
      }));

  // Not able to update the schedule with userId where clause, so fetch schedule separately and then validate
  // Bug: https://github.com/prisma/prisma/issues/7290
  const userSchedule = await prisma.schedule.findUnique({
    where: {
      id: input.scheduleId,
    },
    select: {
      userId: true,
      name: true,
      id: true,
    },
  });

  if (userSchedule?.userId !== user.id) throw new TRPCError({ code: "UNAUTHORIZED" });

  if (!userSchedule || userSchedule.userId !== user.id) {
    throw new TRPCError({
      code: "UNAUTHORIZED",
    });
  }

  let updatedUser;
  if (input.isDefault) {
    const setupDefault = await setupDefaultSchedule(user.id, input.scheduleId, prisma);
    updatedUser = setupDefault;
  }

  if (!input.name) {
    // TODO: Improve
    // We don't want to pass the full schedule for just a set as default update
    // but in the current logic, this wipes the existing availability.
    // Return early to prevent this from happening.
    return {
      schedule: userSchedule,
      isDefault: updatedUser
        ? updatedUser.defaultScheduleId === input.scheduleId
        : user.defaultScheduleId === input.scheduleId,
    };
  }

  const schedule = await prisma.schedule.update({
    where: {
      id: input.scheduleId,
    },
    data: {
      timeZone: input.timeZone,
      name: input.name,
      availability: {
        deleteMany: {
          scheduleId: {
            equals: input.scheduleId,
          },
        },
        createMany: {
          data: [
            ...availability,
            ...(input.dateOverrides || []).map((override) => ({
              date: override.start,
              startTime: override.start,
              endTime: override.end,
            })),
          ],
        },
      },
    },
    select: {
      id: true,
      userId: true,
      name: true,
      availability: true,
      timeZone: true,
      eventType: {
        select: {
          _count: true,
          id: true,
          eventName: true,
        },
      },
    },
  });

  const userAvailability = convertScheduleToAvailability(schedule);

  return {
    schedule,
    availability: userAvailability,
    timeZone: schedule.timeZone || user.timeZone,
    isDefault: updatedUser
      ? updatedUser.defaultScheduleId === schedule.id
      : user.defaultScheduleId === schedule.id,
    prevDefaultId: user.defaultScheduleId,
    currentDefaultId: updatedUser ? updatedUser.defaultScheduleId : user.defaultScheduleId,
  };
};

github-actions[bot] avatar Apr 25 '23 22:04 github-actions[bot]

I can help on that.

thealoneshadow avatar Apr 30 '23 13:04 thealoneshadow