Refactor: update beer post api requests

This commit is contained in:
Aaron William Po
2023-12-27 20:42:37 -05:00
parent 2b90bb2e5d
commit 7c4a4bde80
12 changed files with 208 additions and 159 deletions

View File

@@ -1,18 +1,20 @@
import { FunctionComponent } from 'react';
import router from 'next/router';
import { zodResolver } from '@hookform/resolvers/zod';
import { BeerStyle } from '@prisma/client';
import router from 'next/router';
import { FunctionComponent } from 'react';
import toast from 'react-hot-toast';
import { useForm, SubmitHandler, FieldError } from 'react-hook-form';
import { z } from 'zod';
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
import sendCreateBeerPostRequest from '@/requests/posts/beer-post/sendCreateBeerPostRequest';
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
import sendUploadBeerImagesRequest from '@/requests/images/beer-image/sendUploadBeerImageRequest';
import toast from 'react-hot-toast';
import createErrorToast from '@/util/createErrorToast';
import { sendCreateBeerPostRequest } from '@/requests/posts/beer-post';
import sendUploadBeerImagesRequest from '@/requests/images/beer-image/sendUploadBeerImageRequest';
import Button from './ui/forms/Button';
import FormError from './ui/forms/FormError';
import FormInfo from './ui/forms/FormInfo';
@@ -53,7 +55,16 @@ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
try {
const loadingToast = toast.loading('Creating beer post...');
const beerPost = await sendCreateBeerPostRequest(data);
const beerPost = await sendCreateBeerPostRequest({
body: {
name: data.name,
description: data.description,
abv: data.abv,
ibu: data.ibu,
},
breweryId: data.breweryId,
styleId: data.styleId,
});
await sendUploadBeerImagesRequest({ beerPost, images: data.images });
await router.push(`/beers/${beerPost.id}`);
toast.dismiss(loadingToast);

View File

@@ -6,10 +6,13 @@ import { z } from 'zod';
import { useForm, SubmitHandler } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import deleteBeerPostRequest from '@/requests/posts/beer-post/deleteBeerPostRequest';
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
import sendEditBeerPostRequest from '@/requests/posts/beer-post/sendEditBeerPostRequest';
import createErrorToast from '@/util/createErrorToast';
import {
sendEditBeerPostRequest,
sendDeleteBeerPostRequest,
} from '@/requests/posts/beer-post';
import Button from './ui/forms/Button';
import FormError from './ui/forms/FormError';
import FormInfo from './ui/forms/FormInfo';
@@ -35,7 +38,15 @@ const EditBeerPostForm: FC<EditBeerPostFormProps> = ({ previousValues }) => {
const onSubmit: SubmitHandler<EditBeerPostSchema> = async (data) => {
try {
const loadingToast = toast.loading('Editing beer post...');
await sendEditBeerPostRequest(data);
await sendEditBeerPostRequest({
beerPostId: data.id,
body: {
name: data.name,
abv: data.abv,
ibu: data.ibu,
description: data.description,
},
});
await router.push(`/beers/${data.id}`);
toast.success('Edited beer post.');
toast.dismiss(loadingToast);
@@ -48,7 +59,7 @@ const EditBeerPostForm: FC<EditBeerPostFormProps> = ({ previousValues }) => {
const onDelete = async () => {
try {
const loadingToast = toast.loading('Deleting beer post...');
await deleteBeerPostRequest(previousValues.id);
await sendDeleteBeerPostRequest({ beerPostId: previousValues.id });
toast.dismiss(loadingToast);
await router.push('/beers');
toast.success('Deleted beer post.');

View File

@@ -20,7 +20,7 @@ export const processBeerImageData = async (
}
const beerImages = await addBeerImagesService({
beerPostId: req.query.id,
beerPostId: req.query.postId,
userId: user!.id,
body,
files,

View File

@@ -18,7 +18,7 @@ export const processBreweryImageData = async (
}
const breweryImages = await addBreweryImagesService({
breweryPostId: req.query.id,
breweryPostId: req.query.postId,
userId: user!.id,
body,
files,

View File

@@ -4,7 +4,7 @@ import { z } from 'zod';
export interface UploadImagesRequest extends UserExtendedNextApiRequest {
files?: Express.Multer.File[];
query: { id: string };
query: { postId: string };
body: z.infer<typeof ImageMetadataValidationSchema>;
}

View File

@@ -167,9 +167,9 @@ export const checkIfBreweryPostOwner = async (
next: NextHandler,
) => {
const user = req.user!;
const { id } = req.query;
const { postId } = req.query;
const breweryPost = await getBreweryPostByIdService({ breweryPostId: id });
const breweryPost = await getBreweryPostByIdService({ breweryPostId: postId });
if (!breweryPost) {
throw new ServerError('Brewery post not found', 404);
}
@@ -187,10 +187,10 @@ export const editBreweryPost = async (
) => {
const {
body,
query: { id },
query: { postId },
} = req;
await updateBreweryPostService({ breweryPostId: id, body });
await updateBreweryPostService({ breweryPostId: postId, body });
res.status(200).json({
message: 'Brewery post updated successfully',
@@ -203,8 +203,8 @@ export const deleteBreweryPost = async (
req: BreweryPostRequest,
res: NextApiResponse,
) => {
const { id } = req.query;
const deleted = await deleteBreweryPostService({ breweryPostId: id });
const { postId } = req.query;
const deleted = await deleteBreweryPostService({ breweryPostId: postId });
if (!deleted) {
throw new ServerError('Brewery post not found', 404);

View File

@@ -14,7 +14,7 @@ export interface CreateBreweryPostRequest extends UserExtendedNextApiRequest {
}
export interface BreweryPostRequest extends UserExtendedNextApiRequest {
query: { id: string };
query: { postId: string };
}
export interface EditBreweryPostRequest extends BreweryPostRequest {

View File

@@ -1,29 +0,0 @@
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
/**
* Sends a DELETE request to the server to delete a beer post with the given ID.
*
* @param id The ID of the beer post to delete.
* @returns A Promise that resolves to the parsed API response.
* @throws An error if the response fails or the API response is invalid.
*/
const deleteBeerPostRequest = async (id: string) => {
const response = await fetch(`/api/beers/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('Could not successfully parse the response.');
}
return parsed;
};
export default deleteBeerPostRequest;

View File

@@ -0,0 +1,134 @@
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import {
SendCreateBeerPostRequest,
SendDeleteBeerPostRequest,
SendEditBeerPostRequest,
} from './types';
/**
* Sends a POST request to create a new beer post.
*
* @example
* const beerPost = await sendCreateBeerPostRequest({
* body: {
* abv: 5.5,
* description: 'A golden delight with a touch of citrus.',
* ibu: 30,
* name: 'Yerb Sunshine Ale',
* },
* styleId: 'clqmteqxc000008jphoy31wqw',
* breweryId: 'clqmtexfb000108jp3nsg26c6',
* });
*
* @param data - The data to send in the request.
* @param data.body - The body of the request.
* @param data.body.abv - The ABV of the beer.
* @param data.body.description - The description of the beer.
* @param data.body.ibu - The IBU of the beer.
* @param data.body.name - The name of the beer.
* @param data.styleId - The ID of the style of the beer.
* @param data.breweryId - The ID of the brewery of the beer.
* @returns The created beer post.
* @throws An error if the request fails or the response is invalid.
*/
export const sendCreateBeerPostRequest: SendCreateBeerPostRequest = async ({
body: { abv, description, ibu, name },
styleId,
breweryId,
}) => {
const response = await fetch('/api/beers/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ abv, description, ibu, name, styleId, breweryId }),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('Invalid API response');
}
const { payload, success, message } = parsed.data;
if (!success) {
throw new Error(message);
}
const parsedPayload = BeerPostQueryResult.safeParse(payload);
if (!parsedPayload.success) {
throw new Error('Invalid API response payload');
}
return parsedPayload.data;
};
/**
* Sends a DELETE request to delete a beer post.
*
* @example
* const response = await sendDeleteBeerPostRequest({
* beerPostId: 'clqmtexfb000108jp3nsg26c6',
* });
*
* @param args - The arguments to send in the request.
* @param args.beerPostId - The ID of the beer post to delete.
* @returns The response from the server.
* @throws An error if the request fails or the response is invalid.
*/
export const sendDeleteBeerPostRequest: SendDeleteBeerPostRequest = async ({
beerPostId,
}) => {
const response = await fetch(`/api/beers/${beerPostId}`, { method: 'DELETE' });
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('Could not successfully parse the response.');
}
return parsed.data;
};
/**
* Sends a PUT request to edit a beer post.
*
* @example
* const response = await sendEditBeerPostRequest({
* beerPostId: 'clqmtexfb000108jp3nsg26c6',
* body: {
* abv: 5.5,
* description: 'A golden delight with a touch of citrus.',
* ibu: 30,
* name: 'Yerb Sunshine Ale',
* },
* });
*
* @param args - The arguments to send in the request.
* @param args.beerPostId - The ID of the beer post to edit.
* @param args.body - The body of the request.
* @param args.body.abv - The ABV of the beer.
* @param args.body.description - The description of the beer.
* @param args.body.ibu - The IBU of the beer.
* @param args.body.name - The name of the beer.
* @returns The response from the server.
* @throws An error if the request fails or the response is invalid.
*/
export const sendEditBeerPostRequest: SendEditBeerPostRequest = async ({
beerPostId,
body: { abv, description, ibu, name },
}) => {
const response = await fetch(`/api/beers/${beerPostId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ abv, description, ibu, name }),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('Invalid API response');
}
return parsed.data;
};

View File

@@ -1,67 +0,0 @@
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod';
/**
* Sends a POST request to the server to create a new beer post.
*
* @param data Data containing the beer post information to be sent to the server.
* @param abv The alcohol by volume of the beer.
* @param breweryId The ID of the brewery that produces the beer.
* @param description The description of the beer.
* @param ibu The International Bitterness Units of the beer.
* @param name The name of the beer.
* @param styleId The ID of the beer style.
* @returns A Promise that resolves to the created beer post.
* @throws An error if the request fails, the API response is invalid, or the API response
* payload is invalid.
*/
const sendCreateBeerPostRequest = async ({
abv,
breweryId,
description,
ibu,
name,
styleId,
}: z.infer<typeof CreateBeerPostValidationSchema>) => {
const response = await fetch('/api/beers/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
abv,
breweryId,
description,
ibu,
name,
styleId,
}),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('Invalid API response');
}
const { payload, success, message } = parsed.data;
if (!success) {
throw new Error(message);
}
const parsedPayload = BeerPostQueryResult.safeParse(payload);
if (!parsedPayload.success) {
throw new Error('Invalid API response payload');
}
return parsedPayload.data;
};
export default sendCreateBeerPostRequest;

View File

@@ -1,42 +0,0 @@
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod';
/**
* Sends a PUT request to the server to update a beer post.
*
* @param data Data containing the updated beer post information to be sent to the server.
* @param data.abv The updated ABV of the beer.
* @param data.description The updated description of the beer.
* @param data.ibu The updated IBU of the beer.
* @param data.id The ID of the beer post to be updated.
* @param data.name The updated name of the beer.
* @param data.styleId The updated style ID of the beer.
* @throws If the response status is not ok or the API response is not successful.
*/
const sendEditBeerPostRequest = async ({
abv,
description,
ibu,
id,
name,
}: z.infer<typeof EditBeerPostValidationSchema>) => {
const response = await fetch(`/api/beers/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ abv, description, ibu, name, id }),
});
if (!response.ok) {
throw new Error(`${response.status}: ${response.statusText}`);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error(parsed.error.message);
}
};
export default sendEditBeerPostRequest;

View File

@@ -0,0 +1,31 @@
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod';
type BeerPostQueryResultT = z.infer<typeof BeerPostQueryResult>;
type APIResponse = z.infer<typeof APIResponseValidationSchema>;
export type SendCreateBeerPostRequest = (data: {
body: {
abv: number;
description: string;
ibu: number;
name: string;
};
styleId: string;
breweryId: string;
}) => Promise<BeerPostQueryResultT>;
export type SendDeleteBeerPostRequest = (args: {
beerPostId: string;
}) => Promise<APIResponse>;
export type SendEditBeerPostRequest = (args: {
beerPostId: string;
body: {
abv: number;
description: string;
ibu: number;
name: string;
};
}) => Promise<APIResponse>;