Refactor: update beer style, brewery post services

This commit is contained in:
Aaron William Po
2023-12-14 22:18:59 -05:00
parent 0de4697e77
commit 70a168df92
26 changed files with 671 additions and 633 deletions

View File

@@ -1,34 +0,0 @@
import { z } from 'zod';
import DBClient from '@/prisma/DBClient';
import BeerStyleQueryResult from './schema/BeerStyleQueryResult';
interface DeleteBeerStyleByIdArgs {
beerStyleId: string;
}
const deleteBeerStyleById = async ({
beerStyleId,
}: DeleteBeerStyleByIdArgs): Promise<z.infer<typeof BeerStyleQueryResult> | null> => {
const deleted = await DBClient.instance.beerStyle.delete({
where: { id: beerStyleId },
select: {
id: true,
name: true,
createdAt: true,
updatedAt: true,
abvRange: true,
ibuRange: true,
description: true,
postedBy: { select: { id: true, username: true } },
glassware: { select: { id: true, name: true } },
},
});
/**
* Prisma does not support tuples, so we have to typecast the ibuRange and abvRange
* fields to [number, number] in order to satisfy the zod schema.
*/
return deleted as Awaited<ReturnType<typeof deleteBeerStyleById>>;
};
export default deleteBeerStyleById;

View File

@@ -1,30 +0,0 @@
import DBClient from '@/prisma/DBClient';
import { z } from 'zod';
import BeerStyleQueryResult from './schema/BeerStyleQueryResult';
const editBeerStyleById = async (
id: string,
): Promise<z.infer<typeof BeerStyleQueryResult> | null> => {
const beerStyle = await DBClient.instance.beerStyle.findUnique({
where: { id },
select: {
id: true,
name: true,
postedBy: { select: { id: true, username: true } },
createdAt: true,
updatedAt: true,
abvRange: true,
ibuRange: true,
description: true,
glassware: { select: { id: true, name: true } },
},
});
/**
* Prisma does not support tuples, so we have to typecast the ibuRange and abvRange
* fields to [number, number] in order to satisfy the zod schema.
*/
return beerStyle as Awaited<ReturnType<typeof editBeerStyleById>>;
};
export default editBeerStyleById;

View File

@@ -1,37 +0,0 @@
import DBClient from '@/prisma/DBClient';
import { z } from 'zod';
import BeerStyleQueryResult from './schema/BeerStyleQueryResult';
interface GetAllBeerStylesArgs {
pageNum: number;
pageSize: number;
}
const getAllBeerStyles = async ({
pageNum,
pageSize,
}: GetAllBeerStylesArgs): Promise<z.infer<typeof BeerStyleQueryResult>[]> => {
const beerStyles = await DBClient.instance.beerStyle.findMany({
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: {
id: true,
name: true,
postedBy: { select: { id: true, username: true } },
createdAt: true,
updatedAt: true,
abvRange: true,
ibuRange: true,
description: true,
glassware: { select: { id: true, name: true } },
},
});
/**
* Prisma does not support tuples, so we have to typecast the ibuRange and abvRange
* fields to [number, number] in order to satisfy the zod schema.
*/
return beerStyles as Awaited<ReturnType<typeof getAllBeerStyles>>;
};
export default getAllBeerStyles;

View File

@@ -1,30 +0,0 @@
import DBClient from '@/prisma/DBClient';
import { z } from 'zod';
import BeerStyleQueryResult from './schema/BeerStyleQueryResult';
const getBeerStyleById = async (
id: string,
): Promise<z.infer<typeof BeerStyleQueryResult> | null> => {
const beerStyle = await DBClient.instance.beerStyle.findUnique({
where: { id },
select: {
id: true,
name: true,
postedBy: { select: { id: true, username: true } },
createdAt: true,
updatedAt: true,
abvRange: true,
ibuRange: true,
description: true,
glassware: { select: { id: true, name: true } },
},
});
/**
* Prisma does not support tuples, so we have to typecast the ibuRange and abvRange
* fields to [number, number] in order to satisfy the zod schema.
*/
return beerStyle as Awaited<ReturnType<typeof getBeerStyleById>>;
};
export default getBeerStyleById;

View File

@@ -0,0 +1,182 @@
import DBClient from '@/prisma/DBClient';
import ServerError from '@/config/util/ServerError';
import type {
CreateBeerStyle,
DeleteBeerStyleById,
EditBeerStyleById,
GetAllBeerStyles,
GetBeerStyleById,
} from '@/services/posts/beer-style-post/types';
/**
* The select object for retrieving beer styles.
*
* Satisfies the BeerStyleQueryResult zod schema.
*
* @remarks
* Prisma does not support tuples, so we have to typecast the ibuRange and abvRange fields
* to satisfy the zod schema.
* @example
* const beerStyles = await DBClient.instance.beerStyle.findMany({
* select: beerStyleSelect,
* });
*/
const beerStyleSelect = {
id: true,
name: true,
createdAt: true,
updatedAt: true,
abvRange: true,
ibuRange: true,
description: true,
postedBy: { select: { id: true, username: true } },
glassware: { select: { id: true, name: true } },
} as const;
/**
* Deletes a beer style by id.
*
* @param args - The arguments for the service.
* @param args.beerStyleId - The id of the beer style to delete.
* @returns The deleted beer style.
*/
export const deleteBeerStyleService: DeleteBeerStyleById = async ({ beerStyleId }) => {
const deleted = await DBClient.instance.beerStyle.delete({
where: { id: beerStyleId },
select: beerStyleSelect,
});
return deleted as Awaited<ReturnType<typeof deleteBeerStyleService>>;
};
/**
* Edits a beer style by id.
*
* @param args - The arguments for the service.
* @param args.beerStyleId - The id of the beer style to edit.
* @param args.body - The data to update the beer style with.
* @param args.body.abvRange - The abv range of the beer style.
* @param args.body.description - The description of the beer style.
* @param args.body.glasswareId - The id of the glassware to connect to the beer style.
* @param args.body.ibuRange - The ibu range of the beer style.
* @param args.body.name - The name of the beer style.
* @returns The updated beer style.
*/
export const editBeerStyleService: EditBeerStyleById = async ({ beerStyleId, body }) => {
const { abvRange, description, glasswareId, ibuRange, name } = body;
const glassware = await DBClient.instance.glassware.findUnique({
where: { id: glasswareId },
select: { id: true },
});
if (!glassware) {
throw new ServerError(
'A glassware with that id does not exist and cannot be connected.',
404,
);
}
const updated = await DBClient.instance.beerStyle.update({
where: { id: beerStyleId },
data: {
abvRange,
description,
ibuRange,
name,
glassware: { connect: { id: glasswareId } },
},
select: beerStyleSelect,
});
return updated as Awaited<ReturnType<typeof editBeerStyleService>>;
};
/**
* Gets all beer styles with pagination.
*
* @param args - The arguments for the service.
* @param args.pageNum - The page number of the results.
* @param args.pageSize - The page size of the results.
* @returns The beer styles and the total count of beer styles.
*/
export const getAllBeerStylesService: GetAllBeerStyles = async ({
pageNum,
pageSize,
}) => {
const beerStyles = await DBClient.instance.beerStyle.findMany({
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: beerStyleSelect,
});
const beerStyleCount = await DBClient.instance.beerStyle.count();
return {
beerStyles: beerStyles as Awaited<
ReturnType<typeof getAllBeerStylesService>
>['beerStyles'],
beerStyleCount,
};
};
/**
* Gets a beer style by id.
*
* @param args - The arguments for the service.
* @param args.beerStyleId - The id of the beer style to get.
* @returns The beer style.
*/
export const getBeerStyleByIdService: GetBeerStyleById = async ({ beerStyleId }) => {
const beerStyle = await DBClient.instance.beerStyle.findUnique({
where: { id: beerStyleId },
select: beerStyleSelect,
});
return beerStyle as Awaited<ReturnType<typeof getBeerStyleByIdService>>;
};
/**
* Creates a beer style.
*
* @param args - The arguments for the service.
* @param args.body - The data to create the beer style with.
* @param args.body.abvRange - The abv range of the beer style.
* @param args.body.description - The description of the beer style.
* @param args.body.glasswareId - The id of the glassware to connect to the beer style.
* @param args.body.ibuRange - The ibu range of the beer style.
* @param args.body.name - The name of the beer style.
* @param args.glasswareId - The id of the glassware to connect to the beer style.
* @param args.postedById - The id of the user who posted the beer style.
*/
export const createBeerStyleService: CreateBeerStyle = async ({
body: { abvRange, description, ibuRange, name },
glasswareId,
postedById,
}) => {
const glassware = await DBClient.instance.glassware.findUnique({
where: { id: glasswareId },
select: { id: true },
});
if (!glassware) {
throw new ServerError(
'A glassware with that id does not exist and cannot be connected.',
404,
);
}
const beerStyle = await DBClient.instance.beerStyle.create({
data: {
name,
description,
abvRange,
ibuRange,
glassware: { connect: { id: glasswareId } },
postedBy: { connect: { id: postedById } },
},
select: beerStyleSelect,
});
return beerStyle as Awaited<ReturnType<typeof createBeerStyleService>>;
};

View File

@@ -0,0 +1,39 @@
import { z } from 'zod';
import BeerStyleQueryResult from '../schema/BeerStyleQueryResult';
type BeerStyle = z.infer<typeof BeerStyleQueryResult>;
export type GetBeerStyleById = (args: {
beerStyleId: string;
}) => Promise<BeerStyle | null>;
export type DeleteBeerStyleById = (args: {
beerStyleId: string;
}) => Promise<BeerStyle | null>;
export type EditBeerStyleById = (args: {
beerStyleId: string;
body: {
name: string;
description: string;
abvRange: [number, number];
ibuRange: [number, number];
glasswareId: string;
};
}) => Promise<BeerStyle | null>;
export type GetAllBeerStyles = (args: { pageNum: number; pageSize: number }) => Promise<{
beerStyles: BeerStyle[];
beerStyleCount: number;
}>;
export type CreateBeerStyle = (args: {
body: {
name: string;
description: string;
abvRange: [number, number];
ibuRange: [number, number];
};
glasswareId: string;
postedById: string;
}) => Promise<BeerStyle>;

View File

@@ -1,64 +0,0 @@
import DBClient from '@/prisma/DBClient';
import { z } from 'zod';
import CreateBreweryPostSchema from './schema/CreateBreweryPostSchema';
import BreweryPostQueryResult from './schema/BreweryPostQueryResult';
const CreateNewBreweryPostWithUserAndLocationSchema = CreateBreweryPostSchema.omit({
address: true,
city: true,
country: true,
stateOrProvince: true,
}).extend({
userId: z.string().cuid(),
locationId: z.string().cuid(),
});
const createNewBreweryPost = async ({
dateEstablished,
description,
locationId,
name,
userId,
}: z.infer<typeof CreateNewBreweryPostWithUserAndLocationSchema>): Promise<
z.infer<typeof BreweryPostQueryResult>
> => {
const post = (await DBClient.instance.breweryPost.create({
data: {
name,
description,
dateEstablished,
location: { connect: { id: locationId } },
postedBy: { connect: { id: userId } },
},
select: {
id: true,
name: true,
description: true,
createdAt: true,
dateEstablished: true,
postedBy: { select: { id: true, username: true } },
breweryImages: {
select: {
path: true,
caption: true,
id: true,
alt: true,
createdAt: true,
updatedAt: true,
},
},
location: {
select: {
city: true,
address: true,
coordinates: true,
country: true,
stateOrProvince: true,
},
},
},
})) as Awaited<ReturnType<typeof createNewBreweryPost>>;
return post;
};
export default createNewBreweryPost;

View File

@@ -1,55 +0,0 @@
import DBClient from '@/prisma/DBClient';
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
import { z } from 'zod';
const prisma = DBClient.instance;
const getAllBreweryPosts = async ({
pageNum,
pageSize,
}: {
pageNum: number;
pageSize: number;
}): Promise<z.infer<typeof BreweryPostQueryResult>[]> => {
const breweryPosts = await prisma.breweryPost.findMany({
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: {
id: true,
location: {
select: {
city: true,
address: true,
coordinates: true,
country: true,
stateOrProvince: true,
},
},
description: true,
name: true,
postedBy: { select: { username: true, id: true } },
breweryImages: {
select: {
path: true,
caption: true,
id: true,
alt: true,
createdAt: true,
updatedAt: true,
},
},
createdAt: true,
dateEstablished: true,
},
orderBy: { createdAt: 'desc' },
});
/**
* Prisma does not support tuples, so we have to typecast the coordinates field to
* [number, number] in order to satisfy the zod schema.
*/
return breweryPosts as Awaited<ReturnType<typeof getAllBreweryPosts>>;
};
export default getAllBreweryPosts;

View File

@@ -1,58 +0,0 @@
import DBClient from '@/prisma/DBClient';
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
import { z } from 'zod';
const prisma = DBClient.instance;
const getAllBreweryPostsByPostedById = async ({
pageNum,
pageSize,
postedById,
}: {
pageNum: number;
pageSize: number;
postedById: string;
}): Promise<z.infer<typeof BreweryPostQueryResult>[]> => {
const breweryPosts = await prisma.breweryPost.findMany({
where: { postedBy: { id: postedById } },
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: {
id: true,
location: {
select: {
city: true,
address: true,
coordinates: true,
country: true,
stateOrProvince: true,
},
},
description: true,
name: true,
postedBy: { select: { username: true, id: true } },
breweryImages: {
select: {
path: true,
caption: true,
id: true,
alt: true,
createdAt: true,
updatedAt: true,
},
},
createdAt: true,
dateEstablished: true,
},
orderBy: { createdAt: 'desc' },
});
/**
* Prisma does not support tuples, so we have to typecast the coordinates field to
* [number, number] in order to satisfy the zod schema.
*/
return breweryPosts as Awaited<ReturnType<typeof getAllBreweryPostsByPostedById>>;
};
export default getAllBreweryPostsByPostedById;

View File

@@ -1,46 +0,0 @@
import DBClient from '@/prisma/DBClient';
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
import { z } from 'zod';
const prisma = DBClient.instance;
const getBreweryPostById = async (id: string) => {
const breweryPost = await prisma.breweryPost.findFirst({
select: {
id: true,
location: {
select: {
city: true,
address: true,
coordinates: true,
country: true,
stateOrProvince: true,
},
},
description: true,
name: true,
breweryImages: {
select: {
path: true,
caption: true,
id: true,
alt: true,
createdAt: true,
updatedAt: true,
},
},
postedBy: { select: { username: true, id: true } },
createdAt: true,
dateEstablished: true,
},
where: { id },
});
/**
* Prisma does not support tuples, so we have to typecast the coordinates field to
* [number, number] in order to satisfy the zod schema.
*/
return breweryPost as z.infer<typeof BreweryPostQueryResult> | null;
};
export default getBreweryPostById;

View File

@@ -0,0 +1,207 @@
import DBClient from '@/prisma/DBClient';
import {
CreateBreweryPostLocation,
CreateNewBreweryPost,
GetAllBreweryPosts,
GetAllBreweryPostsByPostedById,
GetBreweryPostById,
GetMapBreweryPosts,
} from './types';
/**
* The select object to use when querying for brewery posts.
*
* @remarks
* Prisma does not support tuples, so we have to typecast the coordinates field to
* [number, number] in order to satisfy the zod schema.
*/
const breweryPostSelect = {
id: true,
name: true,
description: true,
createdAt: true,
dateEstablished: true,
postedBy: { select: { id: true, username: true } },
breweryImages: {
select: {
path: true,
caption: true,
id: true,
alt: true,
createdAt: true,
updatedAt: true,
},
},
location: {
select: {
city: true,
address: true,
coordinates: true,
country: true,
stateOrProvince: true,
},
},
} as const;
/**
* Creates a new brewery post.
*
* @param args - The arguments to create a new brewery post.
* @param args.name - The name of the brewery.
* @param args.description - The description of the brewery.
* @param args.dateEstablished - The date the brewery was established.
* @param args.userId - The id of the user who created the brewery post.
* @param args.locationId - The id of the location of the brewery.
* @returns The newly created brewery post.
*/
export const createNewBreweryPostService: CreateNewBreweryPost = async ({
dateEstablished,
description,
locationId,
name,
userId,
}) => {
const post = (await DBClient.instance.breweryPost.create({
data: {
name,
description,
dateEstablished,
location: { connect: { id: locationId } },
postedBy: { connect: { id: userId } },
},
select: breweryPostSelect,
})) as Awaited<ReturnType<typeof createNewBreweryPostService>>;
return post;
};
/**
* Retrieves all brewery posts paginated.
*
* @param args - The arguments to get all brewery posts.
* @param args.pageNum - The page number of the brewery posts to get.
* @param args.pageSize - The number of brewery posts to get per page.
* @returns All brewery posts.
*/
export const getAllBreweryPostsService: GetAllBreweryPosts = async ({
pageNum,
pageSize,
}) => {
const breweryPosts = (await DBClient.instance.breweryPost.findMany({
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: breweryPostSelect,
orderBy: { createdAt: 'desc' },
})) as Awaited<ReturnType<typeof getAllBreweryPostsService>>['breweryPosts'];
const count = await DBClient.instance.breweryPost.count();
return { breweryPosts, count };
};
/**
* Retrieves a brewery post by ID.
*
* @param args - The arguments to get a brewery post by ID.
* @param args.breweryPostId - The ID of the brewery post to get.
* @returns The brewery post.
*/
export const getBreweryPostByIdService: GetBreweryPostById = async ({
breweryPostId,
}) => {
const breweryPost = await DBClient.instance.breweryPost.findFirst({
select: breweryPostSelect,
where: { id: breweryPostId },
});
return breweryPost as Awaited<ReturnType<typeof getBreweryPostByIdService>>;
};
/**
* Retrieves all brewery posts by posted by ID.
*
* @param args - The arguments to get all brewery posts by posted by ID.
* @param args.pageNum - The page number of the brewery posts to get.
* @param args.pageSize - The number of brewery posts to get per page.
* @param args.postedById - The ID of the user who posted the brewery posts.
*/
export const getAllBreweryPostsByPostedByIdService: GetAllBreweryPostsByPostedById =
async ({ pageNum, pageSize, postedById }) => {
const breweryPosts = (await DBClient.instance.breweryPost.findMany({
where: { postedBy: { id: postedById } },
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: breweryPostSelect,
orderBy: { createdAt: 'desc' },
})) as Awaited<
ReturnType<typeof getAllBreweryPostsByPostedByIdService>
>['breweryPosts'];
const count = await DBClient.instance.breweryPost.count({
where: { postedBy: { id: postedById } },
});
return { breweryPosts, count };
};
/**
* Creates a brewery post location.
*
* @param args - The arguments to create a brewery post location.
* @param args.body - The body of the request.
* @param args.body.address - The address of the brewery.
* @param args.body.city - The city of the brewery.
* @param args.body.country - The country of the brewery.
* @param args.body.stateOrProvince - The state or province of the brewery.
* @param args.body.coordinates - The coordinates of the brewery in an array of [latitude,
* longitude].
* @param args.postedById - The ID of the user who posted the brewery post.
* @returns The newly created brewery post location.
*/
export const createBreweryPostLocationService: CreateBreweryPostLocation = async ({
body: { address, city, country, stateOrProvince, coordinates },
postedById,
}) => {
const [latitude, longitude] = coordinates;
return DBClient.instance.breweryLocation.create({
data: {
address,
city,
country,
stateOrProvince,
coordinates: [latitude, longitude],
postedBy: { connect: { id: postedById } },
},
select: { id: true },
});
};
/**
* Gets all brewery posts for the post map.
*
* @param args - The arguments to get all brewery posts for the post map.
* @param args.pageNum - The page number of the brewery posts to get.
* @param args.pageSize - The number of brewery posts to get per page.
* @returns All brewery posts for the post map.
*/
export const getMapBreweryPostsService: GetMapBreweryPosts = async ({
pageNum,
pageSize,
}) => {
const breweryPosts = await DBClient.instance.breweryPost.findMany({
take: pageSize,
skip: (pageNum - 1) * pageSize,
select: {
id: true,
name: true,
location: {
select: { coordinates: true, city: true, country: true, stateOrProvince: true },
},
},
orderBy: { createdAt: 'desc' },
});
const count = await DBClient.instance.breweryPost.count();
return { breweryPosts, count };
};

View File

@@ -0,0 +1,14 @@
import { z } from 'zod';
import CreateBreweryPostSchema from './CreateBreweryPostSchema';
const CreateNewBreweryPostWithoutLocationSchema = CreateBreweryPostSchema.omit({
address: true,
city: true,
country: true,
stateOrProvince: true,
}).extend({
userId: z.string().cuid(),
locationId: z.string().cuid(),
});
export default CreateNewBreweryPostWithoutLocationSchema;

View File

@@ -0,0 +1,14 @@
import { z } from 'zod';
const GetMapBreweryPostsSchema = z.object({
name: z.string(),
id: z.string().cuid(),
location: z.object({
city: z.string(),
country: z.string().nullable(),
stateOrProvince: z.string().nullable(),
coordinates: z.array(z.number(), z.number()),
}),
});
export default GetMapBreweryPostsSchema;

View File

@@ -0,0 +1,48 @@
import { z } from 'zod';
import BreweryPostQueryResult from '../schema/BreweryPostQueryResult';
import CreateNewBreweryPostWithoutLocationSchema from '../schema/CreateNewBreweryPostWithoutLocationSchema';
import BreweryPostMapQueryResult from '../schema/BreweryPostMapQueryResult';
export type CreateNewBreweryPost = (
args: z.infer<typeof CreateNewBreweryPostWithoutLocationSchema>,
) => Promise<z.infer<typeof BreweryPostQueryResult>>;
export type GetAllBreweryPosts = (args: {
pageNum: number;
pageSize: number;
}) => Promise<{
breweryPosts: z.infer<typeof BreweryPostQueryResult>[];
count: number;
}>;
export type GetBreweryPostById = (args: {
breweryPostId: string;
}) => Promise<z.infer<typeof BreweryPostQueryResult> | null>;
export type GetAllBreweryPostsByPostedById = (args: {
pageNum: number;
pageSize: number;
postedById: string;
}) => Promise<{
breweryPosts: z.infer<typeof BreweryPostQueryResult>[];
count: number;
}>;
export type CreateBreweryPostLocation = (args: {
body: {
address: string;
city: string;
country?: string;
stateOrProvince?: string;
coordinates: [number, number];
};
postedById: string;
}) => Promise<{ id: string }>;
export type GetMapBreweryPosts = (args: {
pageNum: number;
pageSize: number;
}) => Promise<{
breweryPosts: z.infer<typeof BreweryPostMapQueryResult>[];
count: number;
}>;