This commit is contained in:
parent
7258d283ed
commit
31f5bdc254
42 changed files with 21523 additions and 1040 deletions
7
backend/src/recipes/create-recipe.dto.ts
Normal file
7
backend/src/recipes/create-recipe.dto.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
|
||||
// also used for updateRecipe
|
||||
export type CreateRecipeDto = Prisma.recipesCreateInput & {
|
||||
recipeIngredients: Prisma.recipe_ingredientsCreateWithoutRecipesInput[];
|
||||
recipeSteps: Prisma.recipe_stepsCreateWithoutRecipesInput[];
|
||||
};
|
||||
20
backend/src/recipes/recipes.controller.spec.ts
Normal file
20
backend/src/recipes/recipes.controller.spec.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { RecipesController } from './recipes.controller';
|
||||
import { RecipesService } from './recipes.service';
|
||||
|
||||
describe('RecipesController', () => {
|
||||
let controller: RecipesController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [RecipesController],
|
||||
providers: [RecipesService],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<RecipesController>(RecipesController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
45
backend/src/recipes/recipes.controller.ts
Normal file
45
backend/src/recipes/recipes.controller.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
} from '@nestjs/common';
|
||||
import { RecipesService } from './recipes.service';
|
||||
import type { CreateRecipeDto } from './create-recipe.dto';
|
||||
@Controller('recipe')
|
||||
export class RecipesController {
|
||||
constructor(private readonly recipeService: RecipesService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createRecipeDto: CreateRecipeDto) {
|
||||
return this.recipeService.create(createRecipeDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.recipeService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.recipeService.findOne(+id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(@Param('id') id: string, @Body() updateRecipeDto: CreateRecipeDto) {
|
||||
return this.recipeService.update(+id, updateRecipeDto);
|
||||
}
|
||||
|
||||
@Patch(':id/stars')
|
||||
setStars(@Param('id') id: string, @Body('stars') stars: number) {
|
||||
return this.recipeService.setStars(+id, stars);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.recipeService.remove(+id);
|
||||
}
|
||||
}
|
||||
9
backend/src/recipes/recipes.module.ts
Normal file
9
backend/src/recipes/recipes.module.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { RecipesService } from './recipes.service';
|
||||
import { RecipesController } from './recipes.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [RecipesController],
|
||||
providers: [RecipesService],
|
||||
})
|
||||
export class RecipesModule {}
|
||||
18
backend/src/recipes/recipes.service.spec.ts
Normal file
18
backend/src/recipes/recipes.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { RecipesService } from './recipes.service';
|
||||
|
||||
describe('RecipesService', () => {
|
||||
let service: RecipesService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [RecipesService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<RecipesService>(RecipesService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
222
backend/src/recipes/recipes.service.ts
Normal file
222
backend/src/recipes/recipes.service.ts
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import type { CreateRecipeDto } from './create-recipe.dto';
|
||||
|
||||
@Injectable()
|
||||
export class RecipesService {
|
||||
constructor(private readonly databaseService: DatabaseService) {}
|
||||
|
||||
async create(createRecipeDto: CreateRecipeDto) {
|
||||
try {
|
||||
const createdRecipe = await this.databaseService.recipes.create({
|
||||
data: {
|
||||
name: createRecipeDto.name,
|
||||
author: createRecipeDto.author,
|
||||
cuisine: createRecipeDto.cuisine,
|
||||
stars: createRecipeDto.stars,
|
||||
prep_minutes: createRecipeDto.prep_minutes,
|
||||
cook_minutes: createRecipeDto.cook_minutes,
|
||||
recipeIngredients: {
|
||||
create: createRecipeDto.recipeIngredients.map((ingredient) => ({
|
||||
raw: ingredient.raw,
|
||||
})),
|
||||
},
|
||||
recipeSteps: {
|
||||
create: createRecipeDto.recipeSteps || [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Logger.log('New recipe created', {
|
||||
id: createdRecipe.id,
|
||||
name: createdRecipe.name,
|
||||
});
|
||||
|
||||
return createdRecipe;
|
||||
} catch (err) {
|
||||
Logger.error('Error creating recipe', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error('Failed to add recipe');
|
||||
}
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
try {
|
||||
Logger.log('index page view');
|
||||
return await this.databaseService.recipes.findMany({});
|
||||
} catch (err) {
|
||||
Logger.error('Error fetching all recipes', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error(err instanceof Error ? err.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
try {
|
||||
const recipe = await this.databaseService.recipes.findUnique({
|
||||
where: { id },
|
||||
include: { recipeSteps: true, recipeIngredients: true },
|
||||
});
|
||||
if (!recipe) {
|
||||
Logger.warn(`Recipe with id ${id} cannot be found`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = {
|
||||
id: recipe.id,
|
||||
name: recipe.name,
|
||||
author: recipe.author,
|
||||
cuisine: recipe.cuisine,
|
||||
stars: recipe.stars,
|
||||
prep_minutes: recipe.prep_minutes,
|
||||
cook_minutes: recipe.cook_minutes,
|
||||
recipeIngredients: recipe.recipeIngredients.map((ing) => ing.raw),
|
||||
recipeSteps: recipe.recipeSteps.map((step) => ({
|
||||
step_number: step.step_number ?? 0,
|
||||
instruction: step.instruction ?? '',
|
||||
})),
|
||||
};
|
||||
|
||||
Logger.log('Recipe page view', {
|
||||
recipe_id: data.id,
|
||||
recipe_name: data.name,
|
||||
});
|
||||
|
||||
return data;
|
||||
} catch (err) {
|
||||
Logger.error('Error finding recipe', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error(err instanceof Error ? err.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
|
||||
async update(id: number, updateRecipeDto: CreateRecipeDto) {
|
||||
try {
|
||||
const updatedRecipe = await this.databaseService.$transaction(
|
||||
async (prisma) => {
|
||||
await prisma.recipe_ingredients.deleteMany({
|
||||
where: { recipe_id: id },
|
||||
});
|
||||
await prisma.recipe_steps.deleteMany({
|
||||
where: { recipe_id: id },
|
||||
});
|
||||
|
||||
const recipeUpdate = await prisma.recipes.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: updateRecipeDto.name,
|
||||
author: updateRecipeDto.author,
|
||||
cuisine: updateRecipeDto.cuisine,
|
||||
stars: updateRecipeDto.stars,
|
||||
prep_minutes: updateRecipeDto.prep_minutes,
|
||||
cook_minutes: updateRecipeDto.cook_minutes,
|
||||
updated_at: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
const ingredientsData = updateRecipeDto.recipeIngredients.map(
|
||||
(ingredient) => ({
|
||||
raw: ingredient.raw ?? '',
|
||||
quantity: ingredient.quantity ?? null,
|
||||
unit: ingredient.unit ?? null,
|
||||
notes: ingredient.notes ?? null,
|
||||
recipe_id: id,
|
||||
}),
|
||||
);
|
||||
|
||||
const stepsData = updateRecipeDto.recipeSteps.map((step) => ({
|
||||
step_number: step.step_number ?? null,
|
||||
instruction: step.instruction ?? '',
|
||||
recipe_id: id,
|
||||
}));
|
||||
|
||||
if (ingredientsData.length > 0) {
|
||||
await prisma.recipe_ingredients.createMany({
|
||||
data: ingredientsData,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (stepsData.length > 0) {
|
||||
await prisma.recipe_steps.createMany({
|
||||
data: stepsData,
|
||||
skipDuplicates: true,
|
||||
});
|
||||
}
|
||||
|
||||
return recipeUpdate;
|
||||
},
|
||||
);
|
||||
|
||||
Logger.log('Updated Recipe', {
|
||||
id: updatedRecipe.id,
|
||||
name: updatedRecipe.name,
|
||||
});
|
||||
|
||||
return updatedRecipe;
|
||||
} catch (err) {
|
||||
console.error('Update method caught error:', err);
|
||||
Logger.error('Error updating recipe', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
stack: err instanceof Error ? err.stack : 'No stack available',
|
||||
id,
|
||||
updateRecipeDto,
|
||||
});
|
||||
throw new Error('Failed to update recipe');
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteRecipeData(id: number) {
|
||||
try {
|
||||
await this.databaseService.recipe_ingredients.deleteMany({
|
||||
where: { recipe_id: id },
|
||||
});
|
||||
await this.databaseService.recipe_steps.deleteMany({
|
||||
where: { recipe_id: id },
|
||||
});
|
||||
Logger.log(`Recipe data deleted successfully for recipe id: ${id}`);
|
||||
} catch (err) {
|
||||
Logger.error('Error deleting recipe data', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error(err instanceof Error ? err.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
|
||||
async setStars(id: number, stars: number) {
|
||||
try {
|
||||
await this.databaseService.recipes.update({
|
||||
where: { id },
|
||||
data: { stars },
|
||||
});
|
||||
return { message: 'Stars updated' };
|
||||
} catch (err) {
|
||||
Logger.error('Error setting stars', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error(err instanceof Error ? err.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
try {
|
||||
await this.deleteRecipeData(id);
|
||||
const deletedRecipe = await this.databaseService.recipes.delete({
|
||||
where: { id },
|
||||
});
|
||||
Logger.log('Recipe deleted', {
|
||||
id: deletedRecipe.id,
|
||||
name: deletedRecipe.name,
|
||||
});
|
||||
return { message: 'Recipe deleted successfully' };
|
||||
} catch (err) {
|
||||
Logger.error('Error deleting recipe', {
|
||||
message: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
throw new Error(err instanceof Error ? err.message : 'Unknown error');
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue