feat: improve api key endpoints, add users endpoint

This commit is contained in:
Agusti Fernandez Pardo
2022-03-25 23:26:22 +01:00
parent d9c34f67e6
commit 084c91f6c1
11 changed files with 228 additions and 7 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import { z } from "zod";
const schemaApiKey = z
.object({
expiresAt: z.date().optional(), // default is 30 days
expiresAt: z.string().transform((date: string) => new Date(date)).optional(), // default is 30 days
note: z.string().min(1).optional(),
})
.strict(); // Adding strict so that we can disallow passing in extra fields
+1 -1
View File
@@ -7,7 +7,7 @@ const schemaQueryId = z
.object({
// since nextjs parses query params as strings,
// we need to cast them to numbers using z.transform() and parseInt()
id: z.string().uuid()
id: z.string()
})
.strict();
+62
View File
@@ -0,0 +1,62 @@
import { withValidation } from "next-validations";
import { schemaEventType } from "./eventType";
// import { schemaCredential } from "./credential";
// import { schemaMembership } from "./membership";
// import { schemaBooking } from "./booking";
// import { schemaSchedule } from "./schedule";
// import { schemaSelectedCalendar } from "./selectedCalendar";
// import { schemaAvailability } from "./availability";
// import { schemaWebhook } from "./webhook";
import { z } from "zod";
import { schemaApiKey } from "./apiKey";
const schemaUser = z
.object({
username: z.string().min(3),
name: z.string().min(3),
email: z.string().email(), // max is a full day.
emailVerified: z.date().optional(),
password: z.string().optional(),
bio: z.string().min(3).optional(),
avatar: z.string().optional(),
timeZone: z.string().default("Europe/London"),
weekStart: z.string().default("Sunday"),
bufferTime: z.number().default(0),
hideBranding: z.boolean().default(false),
theme: z.string().optional(),
trialEndsAt: z.date().optional(),
eventTypes: z.array((schemaEventType)).optional(),
// credentials: z.array((schemaCredentials)).optional(),
// teams: z.array((schemaMembership)).optional(),
// bookings: z.array((schemaBooking)).optional(),
// schedules: z.array((schemaSchedule)).optional(),
defaultScheduleId: z.number().optional(),
// selectedCalendars: z.array((schemaSelectedCalendar)).optional(),
completedOnboarding: z.boolean().default(false),
locale: z.string().optional(),
timeFormat: z.number().optional().default(12),
twoFactorEnabled: z.boolean().default(false),
twoFactorSecret: z.string().optional(),
identityProvider: z.enum(["CAL", "SAML", "GOOGLE"]).optional().default("CAL"),
identityProviderId: z.string().optional(),
// availavility: z.array((schemaAvailavility)).optional(),
invitedTo: z.number().optional(),
plan: z.enum(['FREE', 'TRIAL', 'PRO']).default("TRIAL"),
// webhooks: z.array((schemaWebhook)).optional(),
brandColor: z.string().default("#292929"),
darkBrandColor: z.string().default("#fafafa"),
// destinationCalendar: z.instanceof(schemaEventType).optional(), // FIXME: instanceof doesnt work here
away: z.boolean().default(false),
metadata: z.object({}).optional(),
verified: z.boolean().default(false),
apiKeys: z.array((schemaApiKey)).optional(),
})
.strict(); // Adding strict so that we can disallow passing in extra fields
const withValidUser = withValidation({
schema: schemaUser,
type: "Zod",
mode: "body",
});
export { schemaUser, withValidUser };
+1 -1
View File
@@ -9,7 +9,7 @@ type ResponseData = {
export default async function apiKey(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
try {
const apiKeys = await prisma.apiKey.findMany({ where: { id: `${req.query.eventTypeId}` } });
const apiKeys = await prisma.apiKey.findMany({});
res.status(200).json({ data: { ...apiKeys } });
} catch (error) {
console.log(error);
+10 -3
View File
@@ -16,10 +16,17 @@ async function createApiKey(req: NextApiRequest, res: NextApiResponse<ResponseDa
const safe = schemaApiKey.safeParse(body);
if (safe.success && safe.data) {
await prisma.apiKey
.create({ data: { ...safe.data, user: { connect: { id: 1 } }
}})
.create({
data: {
...safe.data, user: { connect: { id: 1 } }
}
})
.then((apiKey) => res.status(201).json({ data: apiKey }))
.catch((error) => res.status(400).json({ message: "Could not create apiKey", error: error }));
.catch((error) => {
console.log(error);
res.status(400).json({ message: "Could not create apiKey", error: error })
}
)
}
} else {
// Reject any other HTTP method than POST
+1 -1
View File
@@ -9,7 +9,7 @@ type ResponseData = {
export default async function eventType(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
try {
const eventTypes = await prisma.eventType.findMany({ where: { id: Number(req.query.eventTypeId) } });
const eventTypes = await prisma.eventType.findMany();
res.status(200).json({ data: { ...eventTypes } });
} catch (error) {
console.log(error);
+36
View File
@@ -0,0 +1,36 @@
import prisma from "@calcom/prisma";
import type { NextApiRequest, NextApiResponse } from "next";
import { schemaQueryId, withValidQueryIdTransformParseInt } from "@lib/validations/queryIdTransformParseInt";
type ResponseData = {
message?: string;
error?: unknown;
};
export async function user(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
const { query, method } = req;
const safe = await schemaQueryId.safeParse(query);
if (safe.success) {
if (method === "DELETE") {
// DELETE WILL DELETE THE EVENT TYPE
prisma.user
.delete({ where: { id: safe.data.id } })
.then(() => {
// We only remove the user type from the database if there's an existing resource.
res.status(200).json({ message: `user-type with id: ${safe.data.id} deleted successfully` });
})
.catch((error) => {
// This catches the error thrown by prisma.user.delete() if the resource is not found.
res.status(400).json({ message: `Resource with id:${safe.data.id} was not found`, error: error });
});
} else {
// Reject any other HTTP method than POST
res.status(405).json({ message: "Only DELETE Method allowed in /user-types/[id]/delete endpoint" });
}
}
}
export default withValidQueryIdTransformParseInt(user);
+37
View File
@@ -0,0 +1,37 @@
import prisma from "@calcom/prisma";
import { User } from "@calcom/prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { schemaUser, withValidUser } from "@lib/validations/user";
import { schemaQueryId, withValidQueryIdTransformParseInt } from "@lib/validations/queryIdTransformParseInt";
type ResponseData = {
data?: User;
message?: string;
error?: unknown;
};
export async function editUser(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
const { query, body, method } = req;
const safeQuery = await schemaQueryId.safeParse(query);
const safeBody = await schemaUser.safeParse(body);
if (method === "PATCH") {
if (safeQuery.success && safeBody.success) {
await prisma.user.update({
where: { id: safeQuery.data.id },
data: safeBody.data,
}).then(user => {
res.status(200).json({ data: user });
}).catch(error => {
res.status(404).json({ message: `Event type with ID ${safeQuery.data.id} not found and wasn't updated`, error })
});
}
} else {
// Reject any other HTTP method than POST
res.status(405).json({ message: "Only PATCH Method allowed for updating user-types" });
}
}
export default withValidQueryIdTransformParseInt(withValidUser(editUser));
+31
View File
@@ -0,0 +1,31 @@
import prisma from "@calcom/prisma";
import { User } from "@calcom/prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { schemaQueryId, withValidQueryIdTransformParseInt } from "@lib/validations/queryIdTransformParseInt";
type ResponseData = {
data?: User;
message?: string;
error?: unknown;
};
export async function user(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
const { query, method } = req;
const safe = await schemaQueryId.safeParse(query);
if (safe.success) {
if (method === "GET") {
const user = await prisma.user.findUnique({ where: { id: safe.data.id } });
if (user) res.status(200).json({ data: user });
if (!user) res.status(404).json({ message: "Event type not found" });
} else {
// Reject any other HTTP method than POST
res.status(405).json({ message: "Only GET Method allowed" });
}
}
}
export default withValidQueryIdTransformParseInt(user);
+19
View File
@@ -0,0 +1,19 @@
import prisma from "@calcom/prisma";
import { User } from "@calcom/prisma/client";import type { NextApiRequest, NextApiResponse } from "next";
type ResponseData = {
data?: User[];
error?: unknown;
};
export default async function user(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
try {
const users = await prisma.user.findMany();
res.status(200).json({ data: { ...users } });
} catch (error) {
console.log(error);
// FIXME: Add zod for validation/error handling
res.status(400).json({ error: error });
}
}
+29
View File
@@ -0,0 +1,29 @@
import prisma from "@calcom/prisma";
import { User } from "@calcom/prisma/client";import type { NextApiRequest, NextApiResponse } from "next";
import { schemaUser, withValidUser } from "@lib/validations/user";
type ResponseData = {
data?: User;
message?: string;
error?: string;
};
async function createUser(req: NextApiRequest, res: NextApiResponse<ResponseData>) {
const { body, method } = req;
if (method === "POST") {
const safe = schemaUser.safeParse(body);
if (safe.success && safe.data) {
await prisma.user
.create({ data: safe.data })
.then((user) => res.status(201).json({ data: user }))
.catch((error) => res.status(400).json({ message: "Could not create user type", error: error }));
}
} else {
// Reject any other HTTP method than POST
res.status(405).json({ error: "Only POST Method allowed" });
}
}
export default withValidUser(createUser);