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 #10

Merged
merged 71 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
83a89a6
Created api folder. Added there a new file gift.ts. Added some basic …
anttiasmala Jan 26, 2024
6eca4ff
Created a switch for every request method. Created as well a handler …
anttiasmala Jan 26, 2024
cbb31e1
Changed PUT and DELETE spots in case-switch. Created a placeholder fu…
anttiasmala Jan 26, 2024
4ab759b
Changed removeGift function name to deleGift. Changed file name from …
anttiasmala Jan 26, 2024
0f4f823
Fixed imports due to new name of file giftRequests.ts
anttiasmala Jan 26, 2024
74e9d8b
Added a new variable queryId and gave it a type of string due to ESLi…
anttiasmala Jan 26, 2024
811c8cd
Changed removeGift function to deleteGift to make things working. Cha…
anttiasmala Jan 26, 2024
1b1f106
Added comments after variable queryId to remember to check the commen…
anttiasmala Jan 26, 2024
750ba21
ESLint fixes
anttiasmala Jan 29, 2024
4f160e9
Merged main branch into antti/api
anttiasmala Feb 2, 2024
a5f8014
Changed a few lines of JSDoc in giftRequests.ts
anttiasmala Feb 2, 2024
fa04b84
Fixed a merge conflict that was not saved for some reason
anttiasmala Feb 2, 2024
7a73272
Created handlePATCH function. Added PATCH into Switch statement
anttiasmala Feb 2, 2024
0df5a97
Added a response if method used in HTTP request is not found in Switc…
anttiasmala Feb 2, 2024
3ba7a4f
Added a try/catch block into handleDELETE
anttiasmala Feb 2, 2024
b32eaba
Fixed patch request
anttiasmala Feb 2, 2024
74e1463
Created a new function catchError that is used in all of the handleRE…
anttiasmala Feb 2, 2024
ca33ad2
Added try/catch blocks to rest of the handlers
anttiasmala Feb 2, 2024
f1e8bdc
Added a new error if the server is down and cannot connect. Added try…
anttiasmala Feb 4, 2024
f1a95d3
Enable some rules which are difficult for beginners
samuliasmala Feb 5, 2024
da7d388
Changed switch statement to use Record. Changed localhost URL to baseURL
anttiasmala Feb 7, 2024
e0c94eb
Added types for returns so they work as they should work
anttiasmala Feb 7, 2024
e1d4018
Created a new folder called gifts. Created a new file [id].ts that wo…
anttiasmala Feb 7, 2024
6cba371
Changed status code 400 -> 405 to make status code correct
anttiasmala Feb 7, 2024
fb91d72
Added a check if the request is GET
anttiasmala Feb 7, 2024
2fbaf75
Added a try/catch block
anttiasmala Feb 7, 2024
a12db8a
Created a variable for baseURL. Added a get request and added a conso…
anttiasmala Feb 7, 2024
5d3e84d
Deleted console.logs. Added responses for error and successful gift r…
anttiasmala Feb 7, 2024
da3fcbb
Copied catchError function from gifts.ts. Changed functio name catchE…
anttiasmala Feb 7, 2024
25ee352
Changed the way getGift requests for the gift. Uses now a path instea…
anttiasmala Feb 7, 2024
4076b8b
Renamed gifts.ts -> index.ts and moved it into gifts folder
anttiasmala Feb 7, 2024
14d2973
Added handler for methods and created a handler for GET request
anttiasmala Feb 7, 2024
e27585f
Added a handler for POST request
anttiasmala Feb 7, 2024
87b2e5d
Added a handler for PATCH request
anttiasmala Feb 7, 2024
aba83d7
Added a handler for PUT request
anttiasmala Feb 7, 2024
d9f6e51
Added a handler for DELETE request. PUT and DELETE handlers were insi…
anttiasmala Feb 7, 2024
c8deff4
Changed rest of the requests inside giftRequests.ts to use instead th…
anttiasmala Feb 7, 2024
2f649f2
Changed /api/gifts/index.ts file drastically. Deleted handlers that a…
anttiasmala Feb 7, 2024
cc23804
Added checks for the ID that it is a string. Added a custom error cod…
anttiasmala Feb 7, 2024
50163b9
Moved functions and variables a bit to make them more clear
anttiasmala Feb 7, 2024
decebfd
Deleted one as Type Assersion. Added a string check for handlePUT fun…
anttiasmala Feb 7, 2024
59bb5b3
Changed the way how type check does its check. Now it looks if the re…
anttiasmala Feb 7, 2024
3c3a081
Changed handleGET return's type from Gift[] -> Gift to make it right
anttiasmala Feb 7, 2024
01b475f
Created a new function errorFound. Added try/catch block everywhere w…
anttiasmala Feb 7, 2024
1eca2c7
Deleted localStorageKeyID type from Gift. localStorage is not used an…
anttiasmala Feb 7, 2024
ef96b0e
ESLint fixes
anttiasmala Feb 7, 2024
e24a8f2
Added a new useState for errors. Created function errorFound and adde…
anttiasmala Feb 7, 2024
a94503d
Added an error for only AxiosError
anttiasmala Feb 7, 2024
6581d91
Deleted console.log that was used for debugging and testing purposes
anttiasmala Feb 7, 2024
5141a1d
Added .env line to .gitignore file. .env*.local did not seem to ignor…
anttiasmala Feb 8, 2024
9bf6780
Fixed POST request so it will call the correct endpoint
anttiasmala Feb 8, 2024
f5c3484
Moved POST request back to api/gifts/index.ts
anttiasmala Feb 8, 2024
777eb4d
Deleted errorFound function and imported it from the [id].ts file. Ch…
anttiasmala Feb 8, 2024
5811abf
ESLint fixes
anttiasmala Feb 9, 2024
c27061e
Deleted generatedUUID variable. Kept the id property in newGift so id…
anttiasmala Feb 12, 2024
e88aeca
Changed the way how handlePOST works. It will overwrite the id that w…
anttiasmala Feb 12, 2024
e7792d5
.env file will be used for frontend env-variables. .env.local will be…
anttiasmala Feb 13, 2024
2770713
Changed the URL to match with other requests. Added a return statement
anttiasmala Feb 13, 2024
b34a6d2
Added return statements to updateGift and deleteGift functions
anttiasmala Feb 13, 2024
b3da19a
Moved the query ID string check before running a handler. Now only ne…
anttiasmala Feb 13, 2024
3cd5bbf
json-server generates ID on its own. Now returns the data of the crea…
anttiasmala Feb 13, 2024
7100a3f
Changed type from Gift to CreateGift to make it work properly
anttiasmala Feb 13, 2024
5de268e
Created a new type CreateGift and made it exportable. Deleted ID prop…
anttiasmala Feb 13, 2024
8a84529
Added type Gift into createdGift variable
anttiasmala Feb 13, 2024
201c30c
Created a "global" variable queryID that stores the ID request was ma…
anttiasmala Feb 13, 2024
9c1ef15
Deleted global queryId variable. Created new type HandlerParams where…
anttiasmala Feb 13, 2024
2b5b7f6
PATCH and PUT returns now Axios request's data. Delete request return…
anttiasmala Feb 13, 2024
1cde780
Made frontend's PATCH and POST requests to return data. Fixed the cre…
anttiasmala Feb 13, 2024
fea57e2
deleteGift function in giftRequests.ts does not return the axios request
anttiasmala Feb 14, 2024
59a117e
Deleted currentGiftList variable. updatedGiftList variable concats gi…
anttiasmala Feb 14, 2024
d352812
handleDELETE does not return the body anymore
anttiasmala Feb 14, 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
12 changes: 6 additions & 6 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@
"node/no-missing-import": "off",

// Disable some rules which are difficult for beginners
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-member-access": "warn",
"@typescript-eslint/no-unsafe-argument": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unsafe-return": "warn",
"@typescript-eslint/no-unsafe-call": "warn",

// Use warn instead of error for some rules
"prefer-const": "warn",
Expand Down
4 changes: 2 additions & 2 deletions components/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import React, { Dispatch, SetStateAction } from 'react';
import { Gift } from '~/pages';
import { Modal } from './Modal';
import { Button } from './Button';
import { removeGift } from '~/utils/jsonServerFunctions';
import { SvgCheckMarkIcon } from '~/icons/CheckMarkIcon';
import { SvgDeclineIcon } from '~/icons/DeclineIcon';
import { deleteGift } from '~/utils/giftRequests';
import { isAxiosError } from 'axios';

type DeleteModal = {
Expand All @@ -21,7 +21,7 @@ export function DeleteModal({
}: DeleteModal) {
async function handleDeletion() {
try {
await removeGift(gift.id);
await deleteGift(gift.id);
Copy link
Collaborator Author

@anttiasmala anttiasmala Feb 7, 2024

Choose a reason for hiding this comment

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

Tällaisia tuskin tarvitsee kommentoida, mutta laitan kuitenkin. Vaihdoin removeGift -> deleteGift. Remove kuulostaa "väliaikaiselta", kuin taas delete pysyvältä ratkaisulta

Edit: typo korjattu

} catch (e) {
if (isAxiosError(e) && e.response?.status === 404) {
console.error('Lahjaa ei löytynyt palvelimelta!');
Expand Down
2 changes: 1 addition & 1 deletion components/EditModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Gift } from '~/pages';
import { Modal } from './Modal';
import { TitleText } from './TitleText';
import { Button } from './Button';
import { updateGift } from '~/utils/jsonServerFunctions';
import { updateGift } from '~/utils/giftRequests';
import { Input } from './Input';
import { SvgCheckMarkIcon } from '~/icons/CheckMarkIcon';
import { SvgDeclineIcon } from '~/icons/DeclineIcon';
Expand Down
80 changes: 80 additions & 0 deletions pages/api/gifts/[id].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import axios, { isAxiosError } from 'axios';
import { NextApiRequest, NextApiResponse } from 'next';
import { Gift } from '~/pages';

const baseURL = 'http://localhost:3001/gifts';

type HandlerParams = {
req: NextApiRequest;
res: NextApiResponse;
queryId: string;
};

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

export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
try {
const reqHandler = req.method !== undefined && HANDLERS[req.method];
if (reqHandler) {
if (typeof req.query.id !== 'string') {
throw new Error('Invalid ID', { cause: 'idError' });
}
const queryId = req.query.id;

await reqHandler({ req, res, queryId });
} 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, queryId }: HandlerParams) {
const giftRequest = await axios.get(`${baseURL}/${queryId}`);
return res.status(giftRequest.status).send(giftRequest.data as Gift);
}

async function handlePATCH({ req, res, queryId }: HandlerParams) {
const patchRequest = await axios.patch(`${baseURL}/${queryId}`, req.body);
return res.status(patchRequest.status).json(patchRequest.data as Gift);
}

async function handlePUT({ req, res, queryId }: HandlerParams) {
const putRequest = await axios.put(`${baseURL}/${queryId}`, req.body);
return res.status(putRequest.status).json(putRequest.data as Gift);
}

async function handleDELETE({ res, queryId }: HandlerParams) {
const deleteRequest = await axios.delete(`${baseURL}/${queryId}`);
res.status(deleteRequest.status).end();
return;
}

export function errorFound(res: NextApiResponse, e: unknown) {
if (isAxiosError(e) && e.response?.status === 404) {
return res
.status(e.response.status)
.send('Lahjaa ei löytynyt palvelimelta!');
} else if (isAxiosError(e) && e.code === 'ECONNREFUSED') {
return res.status(500).send('Palvelin virhe!');
} else if (e instanceof Error) {
if (e.cause === 'idError') return res.status(400).send('Virheellinen ID!');

return res.status(500).send('Odottamaton virhe tapahtui!');
} else {
return res.status(500).send('Odottamaton virhe tapahtui!');
}
}
44 changes: 44 additions & 0 deletions pages/api/gifts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from 'axios';
import { NextApiRequest, NextApiResponse } from 'next';
import { Gift } from '../..';
import { errorFound } from './[id]';

const baseURL = 'http://localhost:3001/gifts';

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

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

async function handleGET(req: NextApiRequest, res: NextApiResponse) {
const giftRequest = await axios.get(`${baseURL}`);
return res.status(giftRequest.status).json(giftRequest.data as Gift[]);
}

async function handlePOST(req: NextApiRequest, res: NextApiResponse) {
const postRequest = await axios.post(baseURL, req.body);
return res.status(postRequest.status).json(postRequest.data as Gift);
}
119 changes: 81 additions & 38 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import { TitleText } from '~/components/TitleText';
import { Input } from '../components/Input';
import { DeleteModal } from '~/components/DeleteModal';
import { EditModal } from '~/components/EditModal';
import { createGift, getAllGifts } from '~/utils/jsonServerFunctions';
import { createGift, getAllGifts } from '~/utils/giftRequests';
import { isAxiosError } from 'axios';

const inter = Inter({ subsets: ['latin'] });

export type Gift = {
name: string;
gift: string;
id: string;
localStorageKeyID?: string;
createdDate: number;
};

export type CreateGift = Omit<Gift, 'id'>;

export default function Home() {
const [isAnyKindOfError, setIsAnyKindOfError] = useState(false);
const [isAnyKindOfErrorMessage, setIsAnyKindOfErrorMessage] = useState('');
const [giftData, setGiftData] = useState<Gift[]>([]);
const [giftNameError, setGiftNameError] = useState(false);
const [receiverError, setReceiverError] = useState(false);
Expand All @@ -31,53 +35,81 @@ export default function Home() {
useEffect(() => {
console.log('effect');
async function fetchGifts() {
const gifts = await getAllGifts();
setGiftData(gifts);
try {
const gifts = await getAllGifts();
setGiftData(gifts);
} catch (e) {
errorFound(e);
}
}
fetchGifts().catch((e) => {
console.error(e);
});
void fetchGifts();
}, []);

async function handleSubmit(e: FormEvent<HTMLElement>) {
e.preventDefault();
setGiftNameError(false);
setReceiverError(false);
// this variable is used for checking both inputs
// could use return statement instead of errorFound, but it would not give an error message to all invalid inputs. Only the first invalid input.
let errorFound = false;
try {
e.preventDefault();
setGiftNameError(false);
setReceiverError(false);
// this variable is used for checking both inputs
// could use return statement instead of errorFound, but it would not give an error message to all invalid inputs. Only the first invalid input.
let errorFound = false;

if (typeof newGiftName !== 'string' || newGiftName.length === 0) {
setGiftNameError(true);
errorFound = true;
}
if (typeof newReceiver !== 'string' || newReceiver.length === 0) {
setReceiverError(true);
errorFound = true;
}
if (errorFound) {
return;
}
if (typeof newGiftName !== 'string' || newGiftName.length === 0) {
setGiftNameError(true);
errorFound = true;
}
if (typeof newReceiver !== 'string' || newReceiver.length === 0) {
setReceiverError(true);
errorFound = true;
}
if (errorFound) {
return;
}

const generatedUUID = crypto.randomUUID();
const newGift: Gift = {
name: newReceiver,
gift: newGiftName,
id: generatedUUID,
createdDate: new Date().getTime(),
};
const newGift: CreateGift = {
name: newReceiver,
gift: newGiftName,
createdDate: new Date().getTime(),
};

const currentGiftList = await getAllGifts();
const updatedGiftList = currentGiftList.concat(newGift);
const createdGift = await createGift(newGift);
const updatedGiftList = giftData.concat(createdGift);

await createGift(newGift);
setGiftData(updatedGiftList);
setNewGiftName('');
setNewReceiver('');
setGiftData(updatedGiftList);
setNewGiftName('');
setNewReceiver('');
} catch (e) {
errorFound(e);
}
}

async function refreshGiftList() {
setGiftData(await getAllGifts());
try {
setGiftData(await getAllGifts());
} catch (e) {
errorFound(e);
}
}

function errorFound(e: unknown) {
if (isAxiosError(e) && e.code === 'ERR_BAD_RESPONSE') {
if (e.response !== undefined && typeof e.response.data === 'string') {
setIsAnyKindOfError(true);
setIsAnyKindOfErrorMessage(e.response.data);
} else {
setIsAnyKindOfError(true);
setIsAnyKindOfErrorMessage('Palvelin virhe!');
}
} else if (isAxiosError(e)) {
setIsAnyKindOfError(true);
setIsAnyKindOfErrorMessage(e.message);
} else if (e instanceof Error) {
setIsAnyKindOfError(true);
setIsAnyKindOfErrorMessage(e.message);
} else {
setIsAnyKindOfError(true);
setIsAnyKindOfErrorMessage('Odottamaton virhe tapahtui!');
}
}

return (
Expand Down Expand Up @@ -176,6 +208,17 @@ export default function Home() {
setIsModalOpen={setIsDeleteModalOpen}
/>
)}

{isAnyKindOfError && (
<>
<div className="fixed flex z-[98] justify-center items-center left-0 bottom-0 w-full">
<div className="bg-red-600 text-center p-10 z-[99] w-full" />
<span className="animate-bounce fixed z-[99] text-5xl">
{isAnyKindOfErrorMessage}
</span>
</div>
</>
)}
</div>
</div>
</div>
Expand Down
47 changes: 47 additions & 0 deletions utils/giftRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios from 'axios';
import { CreateGift, Gift } from '~/pages';

const baseURL = '/api/gifts';

/**
*
* @returns an array that contains all the gifts as objects
*/
export async function getAllGifts() {
return (await axios.get(baseURL)).data as Gift[];
}

/**
*
* @param id should be given a string that contains the id that is wanted to be searched.
* @returns an object of a gift if it was found. Else it will return null
*/
export async function getGift(id: string) {
return (await axios.get(`${baseURL}/${id}`)).data as Gift;
}

/**
*
* @param newObject a new object of a gift with type Gift that will added to the server
*/
export async function createGift(newObject: CreateGift) {
return (await axios.post(`${baseURL}`, newObject)).data as Gift;
}

/**
*
* @param id should be given a string that contains the id of the gift that is wanted to be updated
* @param newObject should be given parts of Gift object type that are wanted to be updated
*/
export async function updateGift(id: string, newObject: Partial<Gift>) {
return (await axios.patch(`${baseURL}/${id}`, newObject)).data as Gift;
}

/**
*
* @param id should be given the id of the gift that is wanted to be deleted
*/
export async function deleteGift(id: string) {
await axios.delete(`${baseURL}/${id}`);
return;
}
Loading
Loading