* fix trpc session circle dep * fixes trpc session cirlce dep * fix relative imports * fix more imports to use types and not trpc * fix exports * Fixed types --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com>
124 lines
4.4 KiB
TypeScript
124 lines
4.4 KiB
TypeScript
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/lib/CalendarManager";
|
|
import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials";
|
|
import { DestinationCalendarRepository } from "@calcom/lib/server/repository/destinationCalendar";
|
|
import { prisma } from "@calcom/prisma";
|
|
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
|
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
import type { TSetDestinationCalendarInputSchema } from "./setDestinationCalendar.schema";
|
|
|
|
type SessionUser = NonNullable<TrpcSessionUser>;
|
|
type User = {
|
|
id: SessionUser["id"];
|
|
email: SessionUser["email"];
|
|
userLevelSelectedCalendars: SessionUser["userLevelSelectedCalendars"];
|
|
};
|
|
|
|
type SetDestinationCalendarOptions = {
|
|
ctx: {
|
|
user: User;
|
|
};
|
|
input: TSetDestinationCalendarInputSchema;
|
|
};
|
|
|
|
type ConnectedCalendar = Awaited<ReturnType<typeof getConnectedCalendars>>["connectedCalendars"][number];
|
|
type ConnectedCalendarCalendar = NonNullable<ConnectedCalendar["calendars"]>[number];
|
|
export const getFirstConnectedCalendar = ({
|
|
connectedCalendars,
|
|
matcher,
|
|
}: {
|
|
connectedCalendars: ConnectedCalendar[];
|
|
matcher: (calendar: ConnectedCalendarCalendar) => boolean;
|
|
}) => {
|
|
const calendars = connectedCalendars.flatMap((c) => c.calendars ?? []);
|
|
const matchingCalendars = calendars.filter(matcher);
|
|
const delegationCredentialCalendar = matchingCalendars.find((cal) => !!cal.delegationCredentialId);
|
|
|
|
// Prefer Delegation credential calendar as there could be other one due to existing connections even after DelegationCredential is enabled.
|
|
if (delegationCredentialCalendar) {
|
|
return delegationCredentialCalendar;
|
|
} else {
|
|
return matchingCalendars[0];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* It identifies the destination calendar by externalId, integration and eventTypeId and doesn't consider the `credentialId` or destinationCalendar.id
|
|
* Also, DestinationCalendar doesn't have unique constraint on externalId, integration and eventTypeId, so there could be multiple destinationCalendars with same externalId, integration and eventTypeId in DB.
|
|
* So, it could update any of the destinationCalendar when there are duplicates in DB. Ideally we should have unique constraint on externalId, integration and eventTypeId.
|
|
*
|
|
* With the addition of Delegation credential, it adds another dimension to the problem.
|
|
* A user could have DelegationCredential and non-Delegation credential for the same calendar and he might be selecting Delegation credential connected calendar but it could still be set with nullish destinationCalendar.delegationCredentialId.
|
|
*/
|
|
export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinationCalendarOptions) => {
|
|
const { user } = ctx;
|
|
const { integration, externalId, eventTypeId } = input;
|
|
const credentials = await getUsersCredentials(user);
|
|
const calendarCredentials = getCalendarCredentials(credentials);
|
|
const { connectedCalendars } = await getConnectedCalendars(
|
|
calendarCredentials,
|
|
user.userLevelSelectedCalendars
|
|
);
|
|
|
|
const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat();
|
|
|
|
const firstConnectedCalendar = getFirstConnectedCalendar({
|
|
connectedCalendars,
|
|
matcher: (cal) =>
|
|
cal.externalId === externalId && cal.integration === integration && cal.readOnly === false,
|
|
});
|
|
|
|
const { credentialId, delegationCredentialId } = firstConnectedCalendar || {};
|
|
|
|
let where;
|
|
|
|
if (!credentialId && !delegationCredentialId) {
|
|
throw new TRPCError({ code: "BAD_REQUEST", message: `Could not find calendar ${input.externalId}` });
|
|
}
|
|
|
|
const primaryEmail =
|
|
allCals.find(
|
|
(cal) =>
|
|
cal.primary &&
|
|
(cal.credentialId === credentialId || cal.delegationCredentialId === delegationCredentialId)
|
|
)?.email ?? null;
|
|
|
|
if (eventTypeId) {
|
|
if (
|
|
!(await prisma.eventType.findFirst({
|
|
where: {
|
|
id: eventTypeId,
|
|
userId: user.id,
|
|
},
|
|
}))
|
|
) {
|
|
throw new TRPCError({
|
|
code: "UNAUTHORIZED",
|
|
message: `You don't have access to event type ${eventTypeId}`,
|
|
});
|
|
}
|
|
|
|
where = { eventTypeId };
|
|
} else where = { userId: user.id };
|
|
|
|
await DestinationCalendarRepository.upsert({
|
|
where,
|
|
update: {
|
|
integration,
|
|
externalId,
|
|
primaryEmail,
|
|
credentialId,
|
|
delegationCredentialId,
|
|
},
|
|
create: {
|
|
...where,
|
|
integration,
|
|
externalId,
|
|
primaryEmail,
|
|
credentialId,
|
|
delegationCredentialId,
|
|
},
|
|
});
|
|
};
|