Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Antti | api/user #18

Merged
merged 40 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8cb4efd
Created a new folder 'users' inside the api folder. Added the index f…
anttiasmala Mar 5, 2024
dbd9c58
Created [uuid].ts file into api/users
anttiasmala Mar 5, 2024
08d2f7e
Copied request handlers from 'gifts'
anttiasmala Mar 5, 2024
100c393
Created types for user and creating user
anttiasmala Mar 5, 2024
486b359
Added user to not required field on CreateGift type
anttiasmala Mar 5, 2024
ea88381
Rewrote handleGET
anttiasmala Mar 5, 2024
5745929
Rewrote handlePOST
anttiasmala Mar 5, 2024
309784a
Copied this file from api/gifts/[uuid].ts
anttiasmala Mar 5, 2024
70251f9
Rewrote handleGET
anttiasmala Mar 5, 2024
6515f50
Rewrote handlePATCH
anttiasmala Mar 5, 2024
d6a5e94
Rewrote handlePUT
anttiasmala Mar 5, 2024
c3f4d4c
Rewrote handleDELETE
anttiasmala Mar 5, 2024
9f9f078
Created a new function for checking if email can be used for creating…
anttiasmala Mar 5, 2024
c72e3db
Changed regex pattern. AI made that for me
anttiasmala Mar 5, 2024
b1277f3
Lint fixes
anttiasmala Mar 5, 2024
92d3785
Installed Bcrypt and types for Bcrypt
anttiasmala Mar 5, 2024
af38e19
Added an import for bcrypt. Created function for hashing the password…
anttiasmala Mar 5, 2024
6d40868
Added some test requests into api.rest
anttiasmala Mar 5, 2024
2404aba
Changed 'gifts' to 'users' in the GET request's URL
anttiasmala Mar 11, 2024
25c9d27
Changed a full bcrypt import to only import 'hash'
anttiasmala Mar 11, 2024
bd8b9de
Changed email to be added as lowercased to the database
anttiasmala Mar 11, 2024
0aa81ae
Deleted isEmailFound check due to no need. Prisma handles this
anttiasmala Mar 11, 2024
e93272d
Added known Prisma errors
anttiasmala Mar 11, 2024
d1e570e
Added a DELETE request to api.rest
anttiasmala Mar 11, 2024
a03f714
Removed the IDs from requests due to they're not same for everyone
anttiasmala Mar 11, 2024
b89bc79
Added some comments to Requests
anttiasmala Mar 11, 2024
e00dc25
Changed PATCH request to PUT and added 'gift' field
anttiasmala Mar 11, 2024
77cfb55
Added a placeholder email field to User's PATCH request
anttiasmala Mar 11, 2024
1ff9c66
Added a DELETE request for Gift
anttiasmala Mar 11, 2024
6c97067
Changed PATCH request's email change to be lowercased
anttiasmala Mar 11, 2024
143cfcb
Changed how PUT request works a bit
anttiasmala Mar 11, 2024
81a110c
Added PUT requests to api.rest for future use
anttiasmala Mar 11, 2024
0eba44f
Changed isEmailValid function to not be a Promise anymore
anttiasmala Mar 11, 2024
1160168
Added values for PATCH request, were missing for some reason
anttiasmala Mar 11, 2024
4ebabcc
Fixed Invalid JSON error
anttiasmala Mar 11, 2024
5c973c1
Merge branch 'main' into antti/user-api
anttiasmala Mar 11, 2024
f2e7d08
Deleted unneccessary comments in api.rest
anttiasmala Mar 13, 2024
ffae3d5
Added mark to separate User requests and Gift requests
anttiasmala Mar 13, 2024
c2ea747
Added placeholder ID's to requests
anttiasmala Mar 13, 2024
b896e73
Made req.body as User to be fully optional. Made email to have condit…
anttiasmala Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions pages/api/users/[uuid].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { User } from '~/shared/types';
import { NextApiRequest, NextApiResponse } from 'next';
import prisma from '~/prisma';
import { errorFound } from '.';

type HandlerParams<ResponseType = unknown> = {
req: NextApiRequest;
res: NextApiResponse<ResponseType>;
queryUUID: string;
};

const HANDLERS: Record<string, (params: HandlerParams) => Promise<void>> = {
GET: handleGET,
PATCH: handlePATCH,
PUT: handlePUT,
DELETE: handleDELETE,
};

export default async function handlePrisma(
req: NextApiRequest,
res: NextApiResponse,
) {
try {
const reqHandler = req.method !== undefined && HANDLERS[req.method];
if (reqHandler) {
if (typeof req.query.uuid !== 'string') {
throw new Error('Invalid ID', { cause: 'idError' });
}
const queryUUID = req.query.uuid;
await reqHandler({ req, res, queryUUID });
} else {
return res
.status(405)
.send(
`${req.method} is not a valid method. GET, PATCH, PUT and DELETE request are valid.`,
);
}
} catch (e) {
return errorFound(res, e);
}
}

async function handleGET({ res, queryUUID }: HandlerParams<User>) {
const user = await prisma.user.findUniqueOrThrow({
where: {
uuid: queryUUID,
},
select: {
uuid: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
updatedAt: true,
},
});
return res.status(200).json(user);
}

async function handlePATCH({ req, res, queryUUID }: HandlerParams<User>) {
const newUserDetails = req.body as User;

const updatedUser = await prisma.user.update({
where: {
uuid: queryUUID,
},
data: {
email: newUserDetails.email,
firstName: newUserDetails.firstName,
lastName: newUserDetails.lastName,
},
select: {
uuid: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
updatedAt: true,
},
});

return res.status(200).json(updatedUser);
}

async function handlePUT({ req, res, queryUUID }: HandlerParams<User>) {
const newUserDetails = req.body as User;

const updatedUser = await prisma.user.update({
where: {
uuid: queryUUID,
},
data: newUserDetails,
select: {
uuid: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
updatedAt: true,
},
});

return res.status(200).json(updatedUser);
}

async function handleDELETE({ res, queryUUID }: HandlerParams) {
await prisma.user.delete({
where: {
uuid: queryUUID,
},
});

res.status(200).end();
return;
}
112 changes: 112 additions & 0 deletions pages/api/users/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { CreateUser, User } from '~/shared/types';
import prisma from '~/prisma';

const HANDLER: Record<
string,
(req: NextApiRequest, res: NextApiResponse) => Promise<void>
> = {
GET: handleGET,
POST: handlePOST,
};

export default async function handlePrisma(
req: NextApiRequest,
res: NextApiResponse,
) {
try {
const reqHandler = req.method !== undefined && HANDLER[req.method];
if (reqHandler) {
await reqHandler(req, res);
} else {
return res
.status(405)
.send(
`${req.method} is not a valid method. Only GET and POST requests are valid!`,
);
}
} catch (e) {
return errorFound(res, e);
}
}

async function handleGET(req: NextApiRequest, res: NextApiResponse<User[]>) {
const users = await prisma.user.findMany({
select: {
uuid: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
updatedAt: true,
},
});

return res.status(200).json(users);
}

async function handlePOST(req: NextApiRequest, res: NextApiResponse<User>) {
const userDetails = req.body as CreateUser;
await isEmailValid(userDetails.email);

const addedUser = await prisma.user.create({
data: {
email: userDetails.email,
firstName: userDetails.firstName,
lastName: userDetails.lastName,
password: userDetails.password,
},
select: {
uuid: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
updatedAt: true,
},
});

return res.status(200).json(addedUser);
}

export function errorFound(res: NextApiResponse, e: unknown) {
if (e instanceof Error) {
if (e.message.toLowerCase() === 'no gift found') {
return res.status(400).send('Gift was not found!');
}
if (e.message === 'Invalid email!') {
return res.status(400).send(e.message);
}
if (e.message === 'Email is used already!') {
return res.status(400).send(e.message);
}
if (e.cause === 'idError') return res.status(400).send('Invalid ID!');
return res.status(500).send('Server error!');
}

return res.status(500).send('Unexpected error occurred!');
}

async function isEmailValid(emailAddress: string): Promise<boolean> {
const checkedEmailAddress = emailAddress
.toLowerCase()
.match(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/);
if (checkedEmailAddress === null) {
throw new Error('Invalid email!');
}

// checking if email exists in database
const isEmailFound = await prisma.user.findUnique({
where: {
email: emailAddress,
},
});

// email exists already
if (isEmailFound) {
throw new Error('Email is used already!');
}

// email is ready to be used
return true;
}
10 changes: 8 additions & 2 deletions shared/types.ts
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nämä tulevat antti/user-model-branchista, käytin sitä pohjana vaikka siitäkin on PR #16

Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Prisma, Gift as PrismaGift } from '@prisma/client';
import { Prisma, Gift as PrismaGift, User as PrismaUser } from '@prisma/client';

export type Gift = Omit<PrismaGift, 'id' | 'userUUID'>;
export type CreateGift = Omit<
Prisma.GiftCreateInput,
'uuid' | 'createdAt' | 'updatedAt'
'uuid' | 'createdAt' | 'updatedAt' | 'user'
>;

export type User = Omit<PrismaUser, 'id' | 'password'>;
export type CreateUser = Omit<
Prisma.UserCreateInput,
'uuid' | 'createdAt' | 'updatedAt' | 'gift'
>;
Loading