Calendar credential key missing fix (#4645)

This commit is contained in:
Leo Giovanetti 2022-09-21 13:24:02 +00:00 committed by GitHub
parent a1d4589e66
commit e2747f89d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 57 deletions

View File

@ -185,8 +185,8 @@ function ConnectedCalendarsList(props: Props) {
) : (
<Alert
severity="warning"
title={t("calendar_error")}
message={item.error?.message}
title={t("something_went_wrong")}
message={t("calendar_error")}
actions={
<DisconnectIntegration
id={item.credentialId}

View File

@ -1,3 +1,4 @@
import Link from "next/link";
import { Fragment } from "react";
import { useMutation } from "react-query";
@ -153,7 +154,7 @@ function ConnectedCalendarsList(props: Props) {
return null;
}
return (
<List className="flex flex-col gap-6">
<List className="flex flex-col gap-6" noBorderTreatment>
{data.connectedCalendars.map((item) => (
<Fragment key={item.credentialId}>
{item.calendars ? (
@ -193,10 +194,16 @@ function ConnectedCalendarsList(props: Props) {
) : (
<Alert
severity="warning"
title={t("calendar_error")}
message={item.error?.message}
title={t("something_went_wrong")}
message={
<span>
<Link href={"/apps/" + item.integration.slug}>{item.integration.name}</Link>:{" "}
{t("calendar_error")}
</span>
}
iconClassName="h-10 w-10 ml-2 mr-1 mt-0.5"
actions={
<div className="w-32">
<div className="flex w-32 justify-end md:pr-1">
<DisconnectIntegration
credentialId={item.credentialId}
trashIcon
@ -251,7 +258,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
<ShellSubHeading
title={t("calendar")}
subtitle={t("installed_app_calendar_description")}
className="flex items-center"
className="mb-0 flex flex-wrap items-center gap-4 md:mb-3 md:gap-0"
actions={
<div className="flex flex-col xl:flex-row xl:space-x-5">
{!!data.connectedCalendars.length && (
@ -263,7 +270,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
}
/>
<div className="flex justify-between rounded-md border border-gray-200 bg-gray-50 p-4">
<div className="flex w-full items-center gap-4">
<div className="flex w-full flex-col items-start gap-4 md:flex-row md:items-center">
<div className="relative rounded-md border border-gray-200 bg-white p-1.5">
<Icon.FiCalendar className="h-8 w-8" strokeWidth="1" />
<Icon.FiPlus
@ -271,11 +278,11 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
strokeWidth="4"
/>
</div>
<div className="w-6/12">
<div className="md:w-6/12">
<h1 className="text-sm font-semibold">{t("create_events_on")}</h1>
<p className="text-sm font-normal">{t("set_calendar")}</p>
</div>
<div className="flex w-6/12 justify-end">
<div className="flex justify-end md:w-6/12">
<DestinationCalendarSelector
onChange={mutation.mutate}
hidePlaceholder

View File

@ -32,7 +32,7 @@ function IntegrationListItem(props: {
<ListItem
expanded={!!props.children}
className={classNames(
"my-0 flex-col rounded-none border-0 transition-colors duration-500",
"my-0 flex-col rounded-md border transition-colors duration-500",
highlight ? "bg-yellow-100" : ""
)}>
<div className={classNames("flex w-full flex-1 items-center space-x-2 p-4 rtl:space-x-reverse")}>

View File

@ -930,7 +930,7 @@
"credentials_stored_encrypted": "Your credentials will be stored and encrypted.",
"it_stored_encrypted": "It will be stored and encrypted.",
"go_to_app_store": "Go to App Store",
"calendar_error": "Something went wrong, try reconnecting your calendar with all necessary permissions",
"calendar_error": "Try reconnecting your calendar with all necessary permissions",
"set_your_phone_number": "Set a phone number for the meeting",
"calendar_no_busy_slots": "There are no busy slots",
"display_location_label": "Display on booking page",

View File

@ -8,7 +8,7 @@ import appStore from "..";
const log = logger.getChildLogger({ prefix: ["CalendarManager"] });
export const getCalendar = (credential: Credential | null): Calendar | null => {
if (!credential) return null;
if (!credential || !credential.key) return null;
const { type: calendarType } = credential;
const calendarApp = appStore[calendarType.split("_").join("") as keyof typeof appStore];
if (!(calendarApp && "lib" in calendarApp && "CalendarService" in calendarApp.lib)) {

View File

@ -36,10 +36,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (code) {
const token = await oAuth2Client.getToken(code);
key = token.res?.data;
if (!key) res.status(401).json({ message: "Permissions not granted" });
}
await prisma.credential.create({

View File

@ -21,9 +21,7 @@ export const getCalendarCredentials = (credentials: Array<Credential>, userId: n
.flatMap((app) => {
const credentials = app.credentials.flatMap((credential) => {
const calendar = getCalendar(credential);
return app && calendar && app.variant === "calendar"
? [{ integration: app, credential, calendar }]
: [];
return [{ integration: app, credential, calendar }];
});
return credentials.length ? credentials : [];
});
@ -38,40 +36,34 @@ export const getConnectedCalendars = async (
const connectedCalendars = await Promise.all(
calendarCredentials.map(async (item) => {
const { calendar, integration, credential } = item;
const credentialId = credential.id;
try {
const cals = await calendar.listCalendars();
const calendars = _(cals)
.map((cal) => ({
...cal,
readOnly: cal.readOnly || false,
primary: cal.primary || null,
isSelected: selectedCalendars.some((selected) => selected.externalId === cal.externalId),
credentialId,
}))
.sortBy(["primary"])
.value();
const primary = calendars.find((item) => item.primary) ?? calendars[0];
if (!primary) {
throw new Error("No primary calendar found");
}
if (!calendar) {
return {
integration,
credentialId,
primary,
calendars,
};
} catch (_error) {
const error = getErrorFromUnknown(_error);
return {
integration,
credentialId,
error: {
message: error.message,
},
};
}
const cals = await calendar.listCalendars();
const calendars = _(cals)
.map((cal) => ({
...cal,
readOnly: cal.readOnly || false,
primary: cal.primary || null,
isSelected: selectedCalendars.some((selected) => selected.externalId === cal.externalId),
credentialId,
}))
.sortBy(["primary"])
.value();
const primary = calendars.find((item) => item.primary) ?? calendars[0];
if (!primary) {
throw new Error("No primary calendar found");
}
return {
integration,
credentialId,
primary,
calendars,
};
})
);

View File

@ -7,10 +7,11 @@ export interface AlertProps {
message?: ReactNode;
actions?: ReactNode;
className?: string;
iconClassName?: string;
severity: "success" | "warning" | "error" | "info";
}
export function Alert(props: AlertProps) {
const { severity } = props;
const { severity, iconClassName } = props;
return (
<div
@ -22,26 +23,35 @@ export function Alert(props: AlertProps) {
severity === "info" && "border-sky-700 bg-sky-50 text-sky-700",
severity === "success" && "bg-gray-900 text-white"
)}>
<div className="flex">
<div className="relative flex flex-col md:flex-row">
<div className="flex-shrink-0">
{severity === "error" && (
<XCircleIcon className={classNames("h-5 w-5 text-red-400")} aria-hidden="true" />
<XCircleIcon className={classNames("h-5 w-5 text-red-400", iconClassName)} aria-hidden="true" />
)}
{severity === "warning" && (
<ExclamationIcon className={classNames("h-5 w-5 text-yellow-400")} aria-hidden="true" />
<ExclamationIcon
className={classNames("h-5 w-5 text-yellow-400", iconClassName)}
aria-hidden="true"
/>
)}
{severity === "info" && (
<InformationCircleIcon className={classNames("h-5 w-5 text-sky-400")} aria-hidden="true" />
<InformationCircleIcon
className={classNames("h-5 w-5 text-sky-400", iconClassName)}
aria-hidden="true"
/>
)}
{severity === "success" && (
<CheckCircleIcon className={classNames("h-5 w-5 text-gray-400")} aria-hidden="true" />
<CheckCircleIcon
className={classNames("h-5 w-5 text-gray-400", iconClassName)}
aria-hidden="true"
/>
)}
</div>
<div className="ml-3 flex-grow">
<h3 className="text-sm font-medium">{props.title}</h3>
<div className="text-sm">{props.message}</div>
</div>
{props.actions && <div className="text-sm">{props.actions}</div>}
{props.actions && <div className="absolute top-1 right-1 text-sm md:relative">{props.actions}</div>}
</div>
</div>
);

View File

@ -3,12 +3,14 @@ import { createElement } from "react";
import classNames from "@calcom/lib/classNames";
export function List(props: JSX.IntrinsicElements["ul"]) {
export function List(props: JSX.IntrinsicElements["ul"] & { noBorderTreatment?: true }) {
const { noBorderTreatment, ...rest } = props;
return (
<ul
{...props}
{...rest}
className={classNames(
"divide-y divide-neutral-200 rounded-md border border-l border-r sm:mx-0 sm:overflow-hidden",
"sm:mx-0 sm:overflow-hidden",
!noBorderTreatment && "divide-y divide-neutral-200 rounded-md border border-l border-r ",
props.className
)}>
{props.children}