feat: v1 api connected-calendars endpoint (#14176)
* feat: v1 api connected-calendars endpoint * refactor: flatten return data * refactor: shorten code * refactor: document userId for admins
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const CalendarSchema = z.object({
|
||||
externalId: z.string(),
|
||||
name: z.string(),
|
||||
primary: z.boolean(),
|
||||
readOnly: z.boolean(),
|
||||
});
|
||||
|
||||
const IntegrationSchema = z.object({
|
||||
name: z.string(),
|
||||
appId: z.string(),
|
||||
userId: z.number(),
|
||||
integration: z.string(),
|
||||
calendars: z.array(CalendarSchema),
|
||||
});
|
||||
|
||||
export const schemaConnectedCalendarsReadPublic = z.array(IntegrationSchema);
|
||||
@@ -0,0 +1,146 @@
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import type { UserWithCalendars } from "@calcom/lib/getConnectedDestinationCalendars";
|
||||
import { getConnectedDestinationCalendars } from "@calcom/lib/getConnectedDestinationCalendars";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { extractUserIdsFromQuery } from "~/lib/utils/extractUserIdsFromQuery";
|
||||
import { schemaConnectedCalendarsReadPublic } from "~/lib/validations/connected-calendar";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /connected-calendars:
|
||||
* get:
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: apiKey
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Your API key
|
||||
* - in: query
|
||||
* name: userId
|
||||
* required: false
|
||||
* schema:
|
||||
* type: number
|
||||
* description: Admins can fetch connected calendars for other user e.g. &userId=1 or multiple users e.g. &userId=1&userId=2
|
||||
* summary: Fetch connected calendars
|
||||
* tags:
|
||||
* - connected-calendars
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* appId:
|
||||
* type: string
|
||||
* userId:
|
||||
* type: number
|
||||
* integration:
|
||||
* type: string
|
||||
* calendars:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* properties:
|
||||
* externalId:
|
||||
* type: string
|
||||
* name:
|
||||
* type: string
|
||||
* primary:
|
||||
* type: boolean
|
||||
* readOnly:
|
||||
* type: boolean
|
||||
* examples:
|
||||
* connectedCalendarExample:
|
||||
* value: [
|
||||
* {
|
||||
* "name": "Google Calendar",
|
||||
* "appId": "google-calendar",
|
||||
* "userId": 10,
|
||||
* "integration": "google_calendar",
|
||||
* "calendars": [
|
||||
* {
|
||||
* "externalId": "alice@gmail.com",
|
||||
* "name": "alice@gmail.com",
|
||||
* "primary": true,
|
||||
* "readOnly": false
|
||||
* },
|
||||
* {
|
||||
* "externalId": "addressbook#contacts@group.v.calendar.google.com",
|
||||
* "name": "birthdays",
|
||||
* "primary": false,
|
||||
* "readOnly": true
|
||||
* },
|
||||
* {
|
||||
* "externalId": "en.latvian#holiday@group.v.calendar.google.com",
|
||||
* "name": "Holidays in Narnia",
|
||||
* "primary": false,
|
||||
* "readOnly": true
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* 403:
|
||||
* description: Non admin user trying to fetch other user's connected calendars.
|
||||
*/
|
||||
|
||||
async function getHandler(req: NextApiRequest) {
|
||||
const { userId, isAdmin } = req;
|
||||
|
||||
if (!isAdmin && req.query.userId) throw new HttpError({ statusCode: 403, message: "ADMIN required" });
|
||||
|
||||
const userIds = req.query.userId ? extractUserIdsFromQuery(req) : [userId];
|
||||
|
||||
const usersWithCalendars = await prisma.user.findMany({
|
||||
where: { id: { in: userIds } },
|
||||
include: {
|
||||
selectedCalendars: true,
|
||||
destinationCalendar: true,
|
||||
},
|
||||
});
|
||||
|
||||
return await getConnectedCalendars(usersWithCalendars);
|
||||
}
|
||||
|
||||
async function getConnectedCalendars(users: UserWithCalendars[]) {
|
||||
const connectedDestinationCalendarsPromises = users.map((user) =>
|
||||
getConnectedDestinationCalendars(user, false, prisma).then((connectedCalendarsResult) =>
|
||||
connectedCalendarsResult.connectedCalendars.map((calendar) => ({
|
||||
userId: user.id,
|
||||
...calendar,
|
||||
}))
|
||||
)
|
||||
);
|
||||
const connectedDestinationCalendars = await Promise.all(connectedDestinationCalendarsPromises);
|
||||
|
||||
const flattenedCalendars = connectedDestinationCalendars.flat();
|
||||
|
||||
const mapped = flattenedCalendars.map((calendar) => ({
|
||||
name: calendar.integration.name,
|
||||
appId: calendar.integration.slug,
|
||||
userId: calendar.userId,
|
||||
integration: calendar.integration.type,
|
||||
calendars: (calendar.calendars ?? []).map((c) => ({
|
||||
externalId: c.externalId,
|
||||
name: c.name,
|
||||
primary: c.primary ?? false,
|
||||
readOnly: c.readOnly,
|
||||
})),
|
||||
}));
|
||||
|
||||
return schemaConnectedCalendarsReadPublic.parse(mapped);
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "~/lib/helpers/withMiddleware";
|
||||
|
||||
export default withMiddleware()(
|
||||
defaultHandler({
|
||||
GET: import("./_get"),
|
||||
})
|
||||
);
|
||||
@@ -4,7 +4,7 @@ import type { DestinationCalendar, SelectedCalendar, User } from "@calcom/prisma
|
||||
import { AppCategories } from "@calcom/prisma/enums";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||
|
||||
type UserWithCalendars = Pick<User, "id"> & {
|
||||
export type UserWithCalendars = Pick<User, "id"> & {
|
||||
selectedCalendars: Pick<SelectedCalendar, "externalId" | "integration">[];
|
||||
destinationCalendar: DestinationCalendar | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user