Merge branch 'main' into teste2e-managedMultiSelectQuestion
commit
e3fe065296
|
@ -4,6 +4,7 @@ import type { calendar_v3 } from "googleapis";
|
||||||
import { google } from "googleapis";
|
import { google } from "googleapis";
|
||||||
|
|
||||||
import { MeetLocationType } from "@calcom/app-store/locations";
|
import { MeetLocationType } from "@calcom/app-store/locations";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
||||||
import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
|
import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
|
||||||
import type CalendarService from "@calcom/lib/CalendarService";
|
import type CalendarService from "@calcom/lib/CalendarService";
|
||||||
|
@ -369,57 +370,75 @@ export default class GoogleCalendarService implements Calendar {
|
||||||
timeMin: string;
|
timeMin: string;
|
||||||
timeMax: string;
|
timeMax: string;
|
||||||
items: { id: string }[];
|
items: { id: string }[];
|
||||||
}): Promise<calendar_v3.Schema$FreeBusyResponse> {
|
}): Promise<EventBusyDate[] | null> {
|
||||||
const calendar = await this.authedCalendar();
|
const calendar = await this.authedCalendar();
|
||||||
const flags = await getFeatureFlagMap(prisma);
|
const flags = await getFeatureFlagMap(prisma);
|
||||||
|
|
||||||
|
let freeBusyResult: calendar_v3.Schema$FreeBusyResponse = {};
|
||||||
if (!flags["calendar-cache"]) {
|
if (!flags["calendar-cache"]) {
|
||||||
this.log.warn("Calendar Cache is disabled - Skipping");
|
this.log.warn("Calendar Cache is disabled - Skipping");
|
||||||
const { timeMin, timeMax, items } = args;
|
const { timeMin, timeMax, items } = args;
|
||||||
const apires = await calendar.freebusy.query({
|
const apires = await calendar.freebusy.query({
|
||||||
requestBody: { timeMin, timeMax, items },
|
requestBody: { timeMin, timeMax, items },
|
||||||
});
|
});
|
||||||
return apires.data;
|
|
||||||
|
freeBusyResult = apires.data;
|
||||||
|
} else {
|
||||||
|
const { timeMin: _timeMin, timeMax: _timeMax, items } = args;
|
||||||
|
const { timeMin, timeMax } = handleMinMax(_timeMin, _timeMax);
|
||||||
|
const key = JSON.stringify({ timeMin, timeMax, items });
|
||||||
|
const cached = await prisma.calendarCache.findUnique({
|
||||||
|
where: {
|
||||||
|
credentialId_key: {
|
||||||
|
credentialId: this.credential.id,
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
expiresAt: { gte: new Date(Date.now()) },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
freeBusyResult = cached.value as unknown as calendar_v3.Schema$FreeBusyResponse;
|
||||||
|
} else {
|
||||||
|
const apires = await calendar.freebusy.query({
|
||||||
|
requestBody: { timeMin, timeMax, items },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipping await to respond faster
|
||||||
|
await prisma.calendarCache.upsert({
|
||||||
|
where: {
|
||||||
|
credentialId_key: {
|
||||||
|
credentialId: this.credential.id,
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
value: JSON.parse(JSON.stringify(apires.data)),
|
||||||
|
expiresAt: new Date(Date.now() + CACHING_TIME),
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
value: JSON.parse(JSON.stringify(apires.data)),
|
||||||
|
credentialId: this.credential.id,
|
||||||
|
key,
|
||||||
|
expiresAt: new Date(Date.now() + CACHING_TIME),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
freeBusyResult = apires.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const { timeMin: _timeMin, timeMax: _timeMax, items } = args;
|
if (!freeBusyResult.calendars) return null;
|
||||||
const { timeMin, timeMax } = handleMinMax(_timeMin, _timeMax);
|
|
||||||
const key = JSON.stringify({ timeMin, timeMax, items });
|
|
||||||
const cached = await prisma.calendarCache.findUnique({
|
|
||||||
where: {
|
|
||||||
credentialId_key: {
|
|
||||||
credentialId: this.credential.id,
|
|
||||||
key,
|
|
||||||
},
|
|
||||||
expiresAt: { gte: new Date(Date.now()) },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (cached) return cached.value as unknown as calendar_v3.Schema$FreeBusyResponse;
|
const result = Object.values(freeBusyResult.calendars).reduce((c, i) => {
|
||||||
|
i.busy?.forEach((busyTime) => {
|
||||||
const apires = await calendar.freebusy.query({
|
c.push({
|
||||||
requestBody: { timeMin, timeMax, items },
|
start: busyTime.start || "",
|
||||||
});
|
end: busyTime.end || "",
|
||||||
|
});
|
||||||
// Skipping await to respond faster
|
});
|
||||||
await prisma.calendarCache.upsert({
|
return c;
|
||||||
where: {
|
}, [] as Prisma.PromiseReturnType<CalendarService["getAvailability"]>);
|
||||||
credentialId_key: {
|
return result;
|
||||||
credentialId: this.credential.id,
|
|
||||||
key,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
value: JSON.parse(JSON.stringify(apires.data)),
|
|
||||||
expiresAt: new Date(Date.now() + CACHING_TIME),
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
value: JSON.parse(JSON.stringify(apires.data)),
|
|
||||||
credentialId: this.credential.id,
|
|
||||||
key,
|
|
||||||
expiresAt: new Date(Date.now() + CACHING_TIME),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return apires.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailability(
|
async getAvailability(
|
||||||
|
@ -444,22 +463,44 @@ export default class GoogleCalendarService implements Calendar {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const calsIds = await getCalIds();
|
const calsIds = await getCalIds();
|
||||||
const freeBusyData = await this.getCacheOrFetchAvailability({
|
const originalStartDate = dayjs(dateFrom);
|
||||||
timeMin: dateFrom,
|
const originalEndDate = dayjs(dateTo);
|
||||||
timeMax: dateTo,
|
const diff = originalEndDate.diff(originalStartDate, "days");
|
||||||
items: calsIds.map((id) => ({ id })),
|
|
||||||
});
|
// /freebusy from google api only allows a date range of 90 days
|
||||||
if (!freeBusyData?.calendars) throw new Error("No response from google calendar");
|
if (diff <= 90) {
|
||||||
const result = Object.values(freeBusyData.calendars).reduce((c, i) => {
|
const freeBusyData = await this.getCacheOrFetchAvailability({
|
||||||
i.busy?.forEach((busyTime) => {
|
timeMin: dateFrom,
|
||||||
c.push({
|
timeMax: dateTo,
|
||||||
start: busyTime.start || "",
|
items: calsIds.map((id) => ({ id })),
|
||||||
end: busyTime.end || "",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return c;
|
if (!freeBusyData) throw new Error("No response from google calendar");
|
||||||
}, [] as Prisma.PromiseReturnType<CalendarService["getAvailability"]>);
|
|
||||||
return result;
|
return freeBusyData;
|
||||||
|
} else {
|
||||||
|
const busyData = [];
|
||||||
|
|
||||||
|
const loopsNumber = Math.ceil(diff / 90);
|
||||||
|
|
||||||
|
let startDate = originalStartDate;
|
||||||
|
let endDate = originalStartDate.add(90, "days");
|
||||||
|
|
||||||
|
for (let i = 0; i < loopsNumber; i++) {
|
||||||
|
if (endDate.isAfter(originalEndDate)) endDate = originalEndDate;
|
||||||
|
|
||||||
|
busyData.push(
|
||||||
|
...((await this.getCacheOrFetchAvailability({
|
||||||
|
timeMin: startDate.format(),
|
||||||
|
timeMax: endDate.format(),
|
||||||
|
items: calsIds.map((id) => ({ id })),
|
||||||
|
})) || [])
|
||||||
|
);
|
||||||
|
|
||||||
|
startDate = endDate.add(1, "minutes");
|
||||||
|
endDate = startDate.add(90, "days");
|
||||||
|
}
|
||||||
|
return busyData;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log.error("There was an error contacting google calendar service: ", error);
|
this.log.error("There was an error contacting google calendar service: ", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -188,7 +188,7 @@ export async function listBookings(
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
where.eventType = {
|
where.eventType = {
|
||||||
teamId: account.id,
|
OR: [{ teamId: account.id }, { parent: { teamId: account.id } }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ export function Avatar(props: AvatarProps) {
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
data-testid={props?.["data-testid"]}
|
data-testid={props?.["data-testid"]}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"bg-emphasis item-center relative inline-flex aspect-square justify-center rounded-full",
|
"bg-emphasis item-center relative inline-flex aspect-square justify-center rounded-full align-top",
|
||||||
indicator ? "overflow-visible" : "overflow-hidden",
|
indicator ? "overflow-visible" : "overflow-hidden",
|
||||||
props.className,
|
props.className,
|
||||||
sizesPropsBySize[size]
|
sizesPropsBySize[size]
|
||||||
|
|
Loading…
Reference in New Issue