feat: refactor inventory and recipe services for improved error handling and code reuse; add systematic backend review plan
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -31,6 +31,31 @@ export class RecipesService {
|
||||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private throwRecipeNotFound(id: number): never {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
|
||||
private async findRecipeByIdOrThrow(id: number) {
|
||||
const recipe = await this.prisma.recipe.findUnique({ where: { id } });
|
||||
if (!recipe) {
|
||||
this.throwRecipeNotFound(id);
|
||||
}
|
||||
return recipe;
|
||||
}
|
||||
|
||||
private assertRecipeEditableByUser(recipe: { ownerId: number | null }, userId: number, id: number) {
|
||||
// Legacy behavior: ownerless recipes are editable to preserve existing semantics.
|
||||
if (recipe.ownerId !== null && recipe.ownerId !== userId) {
|
||||
this.throwRecipeNotFound(id);
|
||||
}
|
||||
}
|
||||
|
||||
private assertRecipeOwnedByUser(recipe: { ownerId: number | null }, userId: number, id: number) {
|
||||
if (recipe.ownerId !== userId) {
|
||||
this.throwRecipeNotFound(id);
|
||||
}
|
||||
}
|
||||
|
||||
async getInventoryPreview(id: number, userId: number) {
|
||||
const recipe = await this.prisma.recipe.findFirst({
|
||||
where: {
|
||||
@@ -218,19 +243,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async update(id: number, updateRecipeDto: CreateRecipeDto, userId: number) {
|
||||
// Verifiera att receptet finns och att användaren äger det
|
||||
const existingRecipe = await this.prisma.recipe.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!existingRecipe) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
|
||||
// Tillåt uppdatering om användaren är ägare ELLER om receptet är publikt utan ägare
|
||||
if (existingRecipe.ownerId !== null && existingRecipe.ownerId !== userId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const existingRecipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeEditableByUser(existingRecipe, userId, id);
|
||||
|
||||
// Ta bort gamla ingredienser
|
||||
await this.prisma.recipeIngredient.deleteMany({
|
||||
@@ -269,17 +283,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async remove(id: number, userId: number) {
|
||||
const existingRecipe = await this.prisma.recipe.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!existingRecipe) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
|
||||
if (existingRecipe.ownerId !== null && existingRecipe.ownerId !== userId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const existingRecipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeEditableByUser(existingRecipe, userId, id);
|
||||
|
||||
await this.prisma.recipeIngredient.deleteMany({ where: { recipeId: id } });
|
||||
await this.prisma.recipe.delete({ where: { id } });
|
||||
@@ -295,13 +300,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async updateImage(id: number, sourceUrl: string, userId: number) {
|
||||
const existingRecipe = await this.prisma.recipe.findUnique({ where: { id } });
|
||||
if (!existingRecipe) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
if (existingRecipe.ownerId !== userId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const existingRecipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeOwnedByUser(existingRecipe, userId, id);
|
||||
|
||||
const imageUrl = await downloadAndOptimizeImage(sourceUrl, IMAGE_DEST_DIR);
|
||||
|
||||
@@ -317,10 +317,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async setVisibility(id: number, userId: number, isPublic: boolean) {
|
||||
const existingRecipe = await this.prisma.recipe.findUnique({ where: { id } });
|
||||
if (!existingRecipe || existingRecipe.ownerId !== userId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const existingRecipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeOwnedByUser(existingRecipe, userId, id);
|
||||
|
||||
if (isPublic) {
|
||||
const owner = await this.prisma.user.findUnique({
|
||||
@@ -344,13 +342,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async shareWithUser(id: number, ownerId: number, username: string) {
|
||||
const recipe = await this.prisma.recipe.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, ownerId: true },
|
||||
});
|
||||
if (!recipe || recipe.ownerId !== ownerId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const recipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeOwnedByUser(recipe, ownerId, id);
|
||||
|
||||
const owner = await this.prisma.user.findUnique({
|
||||
where: { id: ownerId },
|
||||
@@ -381,13 +374,8 @@ export class RecipesService {
|
||||
}
|
||||
|
||||
async unshareWithUser(id: number, ownerId: number, username: string) {
|
||||
const recipe = await this.prisma.recipe.findUnique({
|
||||
where: { id },
|
||||
select: { id: true, ownerId: true },
|
||||
});
|
||||
if (!recipe || recipe.ownerId !== ownerId) {
|
||||
throw new NotFoundException(`Recipe with id ${id} not found`);
|
||||
}
|
||||
const recipe = await this.findRecipeByIdOrThrow(id);
|
||||
this.assertRecipeOwnedByUser(recipe, ownerId, id);
|
||||
|
||||
const targetUser = await this.prisma.user.findUnique({
|
||||
where: { username },
|
||||
|
||||
Reference in New Issue
Block a user