Merge pull request #135 from calcom/feat/availability-teams
Feat/availability teams + pagination for /users
This commit is contained in:
@@ -15,8 +15,13 @@ declare module "next" {
|
||||
session: { user: { id: number } };
|
||||
query: Partial<{ [key: string]: string | string[] }>;
|
||||
isAdmin: boolean;
|
||||
pagination: { take: number; skip: number };
|
||||
}
|
||||
}
|
||||
export const extendRequest: NextMiddleware = async (req, res, next) => {
|
||||
req.pagination = {
|
||||
take: 100,
|
||||
skip: 0,
|
||||
};
|
||||
await next();
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
HTTP_GET_DELETE_PATCH,
|
||||
} from "./httpMethods";
|
||||
import { verifyApiKey } from "./verifyApiKey";
|
||||
import { withPagination } from "./withPagination";
|
||||
|
||||
const withMiddleware = label(
|
||||
{
|
||||
@@ -26,6 +27,7 @@ const withMiddleware = label(
|
||||
verifyApiKey,
|
||||
customPrismaClient,
|
||||
extendRequest,
|
||||
pagination: withPagination,
|
||||
sentry: captureErrors,
|
||||
},
|
||||
// The order here, determines the order of execution, put customPrismaClient before verifyApiKey always.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NextMiddleware } from "next-api-middleware";
|
||||
import z from "zod";
|
||||
|
||||
const withPage = z.object({
|
||||
page: z
|
||||
.string()
|
||||
.min(1)
|
||||
.optional()
|
||||
.default("1")
|
||||
.transform((n) => parseInt(n)),
|
||||
take: z
|
||||
.string()
|
||||
.min(10)
|
||||
.max(100)
|
||||
.optional()
|
||||
.default("10")
|
||||
.transform((n) => parseInt(n)),
|
||||
});
|
||||
|
||||
export const withPagination: NextMiddleware = async (req, _, next) => {
|
||||
const { page, take } = withPage.parse(req.query);
|
||||
const skip = page * take || 0;
|
||||
req.pagination = {
|
||||
take,
|
||||
skip,
|
||||
};
|
||||
await next();
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { withValidation } from "next-validations";
|
||||
import { z } from "zod";
|
||||
|
||||
import { baseApiParams } from "./baseApiParams";
|
||||
|
||||
// Extracted out as utility function so can be reused
|
||||
// at different endpoints that require this validation.
|
||||
export const schemaQueryTeamId = baseApiParams
|
||||
.extend({
|
||||
teamId: z
|
||||
.string()
|
||||
.regex(/^\d+$/)
|
||||
.transform((id) => parseInt(id)),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const withValidQueryTeamId = withValidation({
|
||||
schema: schemaQueryTeamId,
|
||||
type: "Zod",
|
||||
mode: "query",
|
||||
});
|
||||
@@ -8,4 +8,4 @@ const schemaTeamRequiredParams = z.object({});
|
||||
|
||||
export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams);
|
||||
|
||||
export const schemaTeamPublic = Team.omit({});
|
||||
export const schemaTeamReadPublic = Team.omit({});
|
||||
|
||||
@@ -153,6 +153,7 @@ export const schemaUserReadPublic = User.pick({
|
||||
createdDate: true,
|
||||
verified: true,
|
||||
invitedTo: true,
|
||||
role: true,
|
||||
});
|
||||
|
||||
export const schemaUsersReadPublic = z.array(schemaUserReadPublic);
|
||||
|
||||
@@ -11,6 +11,7 @@ const withTM = require("next-transpile-modules")([
|
||||
"@calcom/dayjs",
|
||||
"@calcom/emails",
|
||||
"@calcom/embed-core",
|
||||
"@calcom/dayjs",
|
||||
"@calcom/embed-snippet",
|
||||
]);
|
||||
const { withAxiom } = require("next-axiom");
|
||||
|
||||
@@ -2,28 +2,69 @@ import type { NextApiRequest } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
import { availabilityUserSelect } from "@calcom/prisma";
|
||||
import { stringOrNumber } from "@calcom/prisma/zod-utils";
|
||||
|
||||
const availabilitySchema = z
|
||||
.object({
|
||||
userId: stringOrNumber.optional(),
|
||||
teamId: stringOrNumber.optional(),
|
||||
username: z.string().optional(),
|
||||
dateFrom: z.string(),
|
||||
dateTo: z.string(),
|
||||
eventTypeId: stringOrNumber.optional(),
|
||||
})
|
||||
.refine((data) => !!data.username || !!data.userId, "Either username or userId should be filled in.");
|
||||
.refine(
|
||||
(data) => !!data.username || !!data.userId || !!data.teamId,
|
||||
"Either username or userId or teamId should be filled in."
|
||||
);
|
||||
|
||||
async function handler(req: NextApiRequest) {
|
||||
const { username, userId, eventTypeId, dateTo, dateFrom } = availabilitySchema.parse(req.query);
|
||||
return getUserAvailability({
|
||||
username,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
eventTypeId,
|
||||
userId,
|
||||
const { prisma, isAdmin } = req;
|
||||
const { username, userId, eventTypeId, dateTo, dateFrom, teamId } = availabilitySchema.parse(req.query);
|
||||
if (!teamId)
|
||||
return getUserAvailability({
|
||||
username,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
eventTypeId,
|
||||
userId,
|
||||
});
|
||||
const team = await prisma.team.findUnique({
|
||||
where: { id: teamId },
|
||||
select: { members: true },
|
||||
});
|
||||
if (!team) throw new HttpError({ statusCode: 404, message: "teamId not found" });
|
||||
if (!team.members) throw new HttpError({ statusCode: 404, message: "team has no members" });
|
||||
const allMemberIds = team.members.map((membership) => membership.userId);
|
||||
const members = await prisma.user.findMany({
|
||||
where: { id: { in: allMemberIds } },
|
||||
select: availabilityUserSelect,
|
||||
});
|
||||
if (!isAdmin) throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
const availabilities = members.map(async (user) => {
|
||||
return {
|
||||
userId: user.id,
|
||||
availability: await getUserAvailability(
|
||||
{
|
||||
userId: user.id,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
eventTypeId,
|
||||
},
|
||||
{ user }
|
||||
),
|
||||
};
|
||||
});
|
||||
const settled = await Promise.all(availabilities);
|
||||
if (!settled)
|
||||
throw new HttpError({
|
||||
statusCode: 401,
|
||||
message: "We had an issue retrieving all your members availabilities",
|
||||
});
|
||||
return settled;
|
||||
}
|
||||
|
||||
export default defaultResponder(handler);
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import type { TeamResponse } from "@lib/types";
|
||||
import {
|
||||
schemaQueryIdParseInt,
|
||||
withValidQueryIdTransformParseInt,
|
||||
} from "@lib/validations/shared/queryIdTransformParseInt";
|
||||
import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /teams/{id}:
|
||||
* get:
|
||||
* operationId: getTeamById
|
||||
* summary: Find a team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to get
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* 404:
|
||||
* description: Team was not found
|
||||
* patch:
|
||||
* operationId: editTeamById
|
||||
* summary: Edit an existing team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to edit
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team edited successfuly
|
||||
* 400:
|
||||
* description: Bad request. Team body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* delete:
|
||||
* operationId: removeTeamById
|
||||
* summary: Remove an existing team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to delete
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team removed successfuly
|
||||
* 400:
|
||||
* description: Bad request. Team id is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
export async function teamById(
|
||||
{ method, query, body, userId, prisma }: NextApiRequest,
|
||||
res: NextApiResponse<TeamResponse>
|
||||
) {
|
||||
const safeQuery = schemaQueryIdParseInt.safeParse(query);
|
||||
const safeBody = schemaTeamBodyParams.safeParse(body);
|
||||
if (!safeQuery.success) {
|
||||
res.status(400).json({ message: "Your query was invalid" });
|
||||
return;
|
||||
}
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
//FIXME: This is a hack to get the teamId from the user's membership
|
||||
console.log(userWithMemberships);
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
if (!userTeamIds.includes(safeQuery.data.id)) res.status(401).json({ message: "Unauthorized" });
|
||||
else {
|
||||
switch (method) {
|
||||
case "GET":
|
||||
await prisma.team
|
||||
.findUnique({ where: { id: safeQuery.data.id } })
|
||||
.then((data) => schemaTeamPublic.parse(data))
|
||||
.then((team) => res.status(200).json({ team }))
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
message: `Team with id: ${safeQuery.data.id} not found`,
|
||||
error,
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case "PATCH":
|
||||
if (!safeBody.success) {
|
||||
{
|
||||
res.status(400).json({ message: "Invalid request body" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
await prisma.team
|
||||
.update({ where: { id: safeQuery.data.id }, data: safeBody.data })
|
||||
.then((team) => schemaTeamPublic.parse(team))
|
||||
.then((team) => res.status(200).json({ team }))
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
message: `Team with id: ${safeQuery.data.id} not found`,
|
||||
error,
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case "DELETE":
|
||||
await prisma.team
|
||||
.delete({ where: { id: safeQuery.data.id } })
|
||||
.then(() =>
|
||||
res.status(200).json({
|
||||
message: `Team with id: ${safeQuery.data.id} deleted successfully`,
|
||||
})
|
||||
)
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
message: `Team with id: ${safeQuery.data.id} not found`,
|
||||
error,
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
res.status(405).json({ message: "Method not allowed" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(teamById));
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{teamId}:
|
||||
* delete:
|
||||
* operationId: removeTeamById
|
||||
* summary: Remove an existing team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: teamId
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to delete
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team removed successfuly
|
||||
* 400:
|
||||
* description: Bad request. Team id is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
export async function deleteHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { prisma, isAdmin, userId } = req;
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin && !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
await prisma.team
|
||||
.delete({ where: { id: query.teamId } })
|
||||
.then(() =>
|
||||
res.status(200).json({
|
||||
message: `Team with id: ${query.teamId} deleted successfully`,
|
||||
})
|
||||
)
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
message: `Team with id: ${query.teamId} not found`,
|
||||
error,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export default defaultResponder(deleteHandler);
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
import { schemaTeamReadPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /teams/{teamId}:
|
||||
* get:
|
||||
* operationId: getTeamById
|
||||
* summary: Find a team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: teamId
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to get
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* 404:
|
||||
* description: Team was not found
|
||||
*/
|
||||
export async function getHandler(req: NextApiRequest) {
|
||||
const { prisma, isAdmin, userId } = req;
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin && !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
const data = await prisma.team.findUnique({ where: { id: query.teamId } });
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
return { team };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
import { schemaTeamBodyParams, schemaTeamReadPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /teams/{teamId}:
|
||||
* patch:
|
||||
* operationId: editTeamById
|
||||
* summary: Edit an existing team
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: teamId
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: ID of the team to edit
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team edited successfuly
|
||||
* 400:
|
||||
* description: Bad request. Team body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
export async function patchHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { prisma, isAdmin, userId, body } = req;
|
||||
const safeBody = schemaTeamBodyParams.safeParse(body);
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin || !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
if (!safeBody.success) {
|
||||
{
|
||||
res.status(400).json({ message: "Invalid request body" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
const data = await prisma.team.update({ where: { id: query.teamId }, data: safeBody.data });
|
||||
if (!data) throw new HttpError({ statusCode: 404, message: `Team with id: ${query.teamId} not found` });
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
if (!team) throw new HttpError({ statusCode: 401, message: `Your request body wasn't valid` });
|
||||
return { team };
|
||||
}
|
||||
|
||||
export default defaultResponder(patchHandler);
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
|
||||
export default withMiddleware("HTTP_GET")(
|
||||
defaultHandler({
|
||||
GET: import("@api/availability/_get"),
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { withValidQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
|
||||
export default withMiddleware()(
|
||||
withValidQueryTeamId(
|
||||
defaultHandler({
|
||||
GET: import("./_get"),
|
||||
PATCH: import("./_patch"),
|
||||
DELETE: import("./_delete"),
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaTeamReadPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /teams:
|
||||
* get:
|
||||
* operationId: listTeams
|
||||
* summary: Find all teams
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* 404:
|
||||
* description: No teams were found
|
||||
*/
|
||||
async function getHandler(req: NextApiRequest) {
|
||||
const { userId, prisma, isAdmin } = req;
|
||||
const membershipWhere: Prisma.MembershipWhereInput = {};
|
||||
// If user is not ADMIN, return only his data.
|
||||
if (!isAdmin) membershipWhere.userId = userId;
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: membershipWhere,
|
||||
});
|
||||
const teamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
const teamWhere: Prisma.TeamWhereInput = {};
|
||||
|
||||
if (!isAdmin) teamWhere.id = { in: teamIds };
|
||||
|
||||
const data = await prisma.team.findMany({ where: teamWhere });
|
||||
const teams = schemaTeamReadPublic.parse(data);
|
||||
return { teams };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaMembershipPublic } from "@lib/validations/membership";
|
||||
import { schemaTeamBodyParams, schemaTeamReadPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /teams:
|
||||
* post:
|
||||
* operationId: addTeam
|
||||
* summary: Creates a new team
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team created
|
||||
* 400:
|
||||
* description: Bad request. Team body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
async function postHandler(req: NextApiRequest) {
|
||||
const { prisma, body, userId } = req;
|
||||
const safe = schemaTeamBodyParams.safeParse(body);
|
||||
if (!safe.success) throw new HttpError({ statusCode: 400, message: "Invalid request body" });
|
||||
const data = await prisma.team.create({ data: safe.data });
|
||||
// We're also creating the relation membership of team ownership in this call.
|
||||
const owner = await prisma.membership
|
||||
.create({
|
||||
data: { userId, teamId: data.id, role: "OWNER", accepted: true },
|
||||
})
|
||||
.then((owner) => schemaMembershipPublic.parse(owner));
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
if (!team) throw new HttpError({ statusCode: 400, message: "We were not able to create your team" });
|
||||
req.statusCode = 201;
|
||||
// We are also returning the new ownership relation as owner besides team.
|
||||
return {
|
||||
team,
|
||||
owner,
|
||||
message: "Team created successfully, we also made you the owner of this team",
|
||||
};
|
||||
}
|
||||
|
||||
export default defaultResponder(postHandler);
|
||||
@@ -1,87 +1,10 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { TeamResponse, TeamsResponse } from "@lib/types";
|
||||
import { schemaMembershipPublic } from "@lib/validations/membership";
|
||||
import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team";
|
||||
|
||||
async function createOrlistAllTeams(
|
||||
{ method, body, userId, prisma }: NextApiRequest,
|
||||
res: NextApiResponse<TeamsResponse | TeamResponse>
|
||||
) {
|
||||
if (method === "GET") {
|
||||
/**
|
||||
* @swagger
|
||||
* /teams:
|
||||
* get:
|
||||
* operationId: listTeams
|
||||
* summary: Find all teams
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
* 404:
|
||||
* description: No teams were found
|
||||
*/
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
const teamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
const teams = await prisma.team.findMany({ where: { id: { in: teamIds } } });
|
||||
if (teams) res.status(200).json({ teams });
|
||||
else
|
||||
(error: Error) =>
|
||||
res.status(404).json({
|
||||
message: "No Teams were found",
|
||||
error,
|
||||
});
|
||||
} else if (method === "POST") {
|
||||
/**
|
||||
* @swagger
|
||||
* /teams:
|
||||
* post:
|
||||
* operationId: addTeam
|
||||
* summary: Creates a new team
|
||||
* tags:
|
||||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team created
|
||||
* 400:
|
||||
* description: Bad request. Team body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
const safe = schemaTeamBodyParams.safeParse(body);
|
||||
if (!safe.success) {
|
||||
res.status(400).json({ message: "Invalid request body" });
|
||||
return;
|
||||
}
|
||||
const team = await prisma.team.create({ data: safe.data });
|
||||
// We're also creating the relation membership of team ownership in this call.
|
||||
const membership = await prisma.membership
|
||||
.create({
|
||||
data: { userId, teamId: team.id, role: "OWNER", accepted: true },
|
||||
})
|
||||
.then((membership) => schemaMembershipPublic.parse(membership));
|
||||
const data = schemaTeamPublic.parse(team);
|
||||
// We are also returning the new ownership relation as owner besides team.
|
||||
if (data)
|
||||
res.status(201).json({
|
||||
team: data,
|
||||
owner: membership,
|
||||
message: "Team created successfully, we also made you the owner of this team",
|
||||
});
|
||||
else
|
||||
(error: Error) =>
|
||||
res.status(400).json({
|
||||
message: "Could not create new team",
|
||||
error,
|
||||
});
|
||||
} else res.status(405).json({ message: `Method ${method} not allowed` });
|
||||
}
|
||||
|
||||
export default withMiddleware("HTTP_GET_OR_POST")(createOrlistAllTeams);
|
||||
export default withMiddleware("HTTP_GET_OR_POST")(
|
||||
defaultHandler({
|
||||
GET: import("./_get"),
|
||||
POST: import("./_post"),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { NextApiRequest } from "next";
|
||||
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { schemaUsersReadPublic } from "@lib/validations/user";
|
||||
|
||||
import { Prisma } from ".prisma/client";
|
||||
@@ -23,13 +24,18 @@ import { Prisma } from ".prisma/client";
|
||||
* description: No users were found
|
||||
*/
|
||||
async function getHandler(req: NextApiRequest) {
|
||||
const { userId, prisma, isAdmin } = req;
|
||||
const {
|
||||
userId,
|
||||
prisma,
|
||||
isAdmin,
|
||||
pagination: { take, skip },
|
||||
} = req;
|
||||
const where: Prisma.UserWhereInput = {};
|
||||
// If user is not ADMIN, return only his data.
|
||||
if (!isAdmin) where.id = userId;
|
||||
const data = await prisma.user.findMany({ where });
|
||||
const data = await prisma.user.findMany({ where, take, skip });
|
||||
const users = schemaUsersReadPublic.parse(data);
|
||||
return { users };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
export default withMiddleware("pagination")(defaultResponder(getHandler));
|
||||
|
||||
@@ -5,6 +5,22 @@ import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaUserCreateBodyParams } from "@lib/validations/user";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users:
|
||||
* post:
|
||||
* operationId: addUser
|
||||
* summary: Creates a new user
|
||||
* tags:
|
||||
* - users
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, user created
|
||||
* 400:
|
||||
* description: Bad request. user body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
async function postHandler(req: NextApiRequest) {
|
||||
const { prisma, isAdmin } = req;
|
||||
// If user is not ADMIN, return unauthorized.
|
||||
|
||||
Reference in New Issue
Block a user