diff --git a/data/ingredients.json b/data/ingredients.json new file mode 100644 index 0000000..dd47a70 --- /dev/null +++ b/data/ingredients.json @@ -0,0 +1,667 @@ +[ + { + "id": "ingredient_flour", + "name": "Flour", + "price": 0.0015, + "unit_id": "weight_g", + "category": "baking" + }, + { + "id": "ingredient_sugar", + "name": "Sugar", + "price": 0.0018, + "unit_id": "weight_g", + "category": "baking" + }, + { + "id": "ingredient_butter", + "name": "Butter", + "price": 0.003, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_eggs", + "name": "Eggs", + "price": 0.25, + "unit_id": "count_pc", + "category": "dairy" + }, + { + "id": "ingredient_garlic", + "name": "Garlic", + "price": 0.01, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_tomatoes", + "name": "Tomatoes", + "price": 0.003, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_chicken_breast", + "name": "Chicken Breast", + "price": 0.008, + "unit_id": "weight_g", + "category": "meat" + }, + { + "id": "ingredient_rice", + "name": "Rice", + "price": 0.0025, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_basil", + "name": "Basil", + "price": 0.025, + "unit_id": "weight_g", + "category": "herbs" + }, + { + "id": "ingredient_cheddar_cheese", + "name": "Cheddar Cheese", + "price": 0.01, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_avocado", + "name": "Avocado", + "price": 2.00, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_mushrooms", + "name": "Mushrooms", + "price": 0.005, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_spinach", + "name": "Spinach", + "price": 0.002, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_carrots", + "name": "Carrots", + "price": 0.0018, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_potatoes", + "name": "Potatoes", + "price": 0.001, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_lemons", + "name": "Lemons", + "price": 0.50, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_bananas", + "name": "Bananas", + "price": 0.30, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_cheese", + "name": "Cheese", + "price": 0.008, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_yogurt", + "name": "Yogurt", + "price": 0.0032, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_milk", + "name": "Milk", + "price": 0.0012, + "unit_id": "volume_ml", + "category": "dairy" + }, + { + "id": "ingredient_bread", + "name": "Bread", + "price": 1.00, + "unit_id": "count_pc", + "category": "bakery" + }, + { + "id": "ingredient_onions", + "name": "Onions", + "price": 0.002, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_cucumber", + "name": "Cucumber", + "price": 0.004, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_yogurt_natural", + "name": "Natural Yogurt", + "price": 0.003, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_chocolate", + "name": "Chocolate", + "price": 0.015, + "unit_id": "weight_g", + "category": "sweets" + }, + { + "id": "ingredient_coconut_milk", + "name": "Coconut Milk", + "price": 0.007, + "unit_id": "volume_ml", + "category": "oils" + }, + { + "id": "ingredient_peanut_butter", + "name": "Peanut Butter", + "price": 0.02, + "unit_id": "weight_g", + "category": "oils" + }, + { + "id": "ingredient_salmon", + "name": "Salmon", + "price": 0.02, + "unit_id": "weight_g", + "category": "meat" + }, + { + "id": "ingredient_pasta", + "name": "Pasta", + "price": 0.003, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_bacon", + "name": "Bacon", + "price": 0.01, + "unit_id": "weight_g", + "category": "meat" + }, + { + "id": "ingredient_avocado_oil", + "name": "Avocado Oil", + "price": 0.008, + "unit_id": "volume_ml", + "category": "oils" + }, + { + "id": "ingredient_paprika", + "name": "Paprika", + "price": 0.015, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_salt", + "name": "Salt", + "price": 0.0005, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_black_pepper", + "name": "Black Pepper", + "price": 0.025, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_honey", + "name": "Honey", + "price": 0.02, + "unit_id": "weight_g", + "category": "sweets" + }, + { + "id": "ingredient_kale", + "name": "Kale", + "price": 0.005, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_zucchini", + "name": "Zucchini", + "price": 0.003, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_lentils", + "name": "Lentils", + "price": 0.002, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_cilantro", + "name": "Cilantro", + "price": 0.02, + "unit_id": "weight_g", + "category": "herbs" + }, + { + "id": "ingredient_parsley", + "name": "Parsley", + "price": 0.015, + "unit_id": "weight_g", + "category": "herbs" + }, + { + "id": "ingredient_oats", + "name": "Oats", + "price": 0.002, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_coriander", + "name": "Coriander", + "price": 0.02, + "unit_id": "weight_g", + "category": "herbs" + }, + { + "id": "ingredient_feta_cheese", + "name": "Feta Cheese", + "price": 0.015, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_sesame_seeds", + "name": "Sesame Seeds", + "price": 0.025, + "unit_id": "weight_g", + "category": "seeds" + }, + { + "id": "ingredient_pineapple", + "name": "Pineapple", + "price": 0.75, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_mango", + "name": "Mango", + "price": 1.00, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_asparagus", + "name": "Asparagus", + "price": 0.02, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_broccoli", + "name": "Broccoli", + "price": 0.01, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_peas", + "name": "Peas", + "price": 0.005, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_raisins", + "name": "Raisins", + "price": 0.01, + "unit_id": "weight_g", + "category": "sweets" + }, + { + "id": "ingredient_tuna", + "name": "Tuna", + "price": 0.02, + "unit_id": "weight_g", + "category": "meat" + }, + { + "id": "ingredient_apple", + "name": "Apple", + "price": 0.25, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_papaya", + "name": "Papaya", + "price": 1.20, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_sweet_potatoes", + "name": "Sweet Potatoes", + "price": 0.006, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_chickpeas", + "name": "Chickpeas", + "price": 0.003, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_mustard", + "name": "Mustard", + "price": 0.02, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_cucumber_pickles", + "name": "Cucumber Pickles", + "price": 0.04, + "unit_id": "weight_g", + "category": "condiments" + }, + { + "id": "ingredient_sunflower_oil", + "name": "Sunflower Oil", + "price": 0.006, + "unit_id": "volume_ml", + "category": "oils" + }, + { + "id": "ingredient_balsamic_vinegar", + "name": "Balsamic Vinegar", + "price": 0.02, + "unit_id": "volume_ml", + "category": "condiments" + }, + { + "id": "ingredient_brown_sugar", + "name": "Brown Sugar", + "price": 0.0015, + "unit_id": "weight_g", + "category": "baking" + }, + { + "id": "ingredient_white_wine_vinegar", + "name": "White Wine Vinegar", + "price": 0.015, + "unit_id": "volume_ml", + "category": "condiments" + }, + { + "id": "ingredient_cabbage", + "name": "Cabbage", + "price": 0.003, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_lime", + "name": "Lime", + "price": 0.50, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_coconut_flakes", + "name": "Coconut Flakes", + "price": 0.02, + "unit_id": "weight_g", + "category": "sweets" + }, + { + "id": "ingredient_mint", + "name": "Mint", + "price": 0.03, + "unit_id": "weight_g", + "category": "herbs" + }, + { + "id": "ingredient_cheese_parmesan", + "name": "Parmesan Cheese", + "price": 0.03, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_ginger", + "name": "Ginger", + "price": 0.02, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_mushroom_dried", + "name": "Dried Mushrooms", + "price": 0.08, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_pumpkin", + "name": "Pumpkin", + "price": 0.005, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_cranberries", + "name": "Cranberries", + "price": 0.02, + "unit_id": "weight_g", + "category": "fruits" + }, + { + "id": "ingredient_tofu", + "name": "Tofu", + "price": 0.02, + "unit_id": "weight_g", + "category": "protein" + }, + { + "id": "ingredient_milk_powder", + "name": "Milk Powder", + "price": 0.02, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_sweet_chili_sauce", + "name": "Sweet Chili Sauce", + "price": 0.03, + "unit_id": "volume_ml", + "category": "sauces" + }, + { + "id": "ingredient_black_olive", + "name": "Black Olives", + "price": 0.05, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_mayonnaise", + "name": "Mayonnaise", + "price": 0.01, + "unit_id": "volume_ml", + "category": "condiments" + }, + { + "id": "ingredient_peach", + "name": "Peach", + "price": 0.80, + "unit_id": "count_pc", + "category": "fruits" + }, + { + "id": "ingredient_paprika_smoked", + "name": "Smoked Paprika", + "price": 0.025, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_poppy_seeds", + "name": "Poppy Seeds", + "price": 0.04, + "unit_id": "weight_g", + "category": "seeds" + }, + { + "id": "ingredient_butter_unsalted", + "name": "Unsalted Butter", + "price": 0.015, + "unit_id": "weight_g", + "category": "dairy" + }, + { + "id": "ingredient_saffron", + "name": "Saffron", + "price": 0.50, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_olive_oil", + "name": "Olive Oil", + "price": 0.008, + "unit_id": "volume_ml", + "category": "oils" + }, + { + "id": "ingredient_cashew_nuts", + "name": "Cashew Nuts", + "price": 0.02, + "unit_id": "weight_g", + "category": "nuts" + }, + { + "id": "ingredient_sunflower_seeds", + "name": "Sunflower Seeds", + "price": 0.01, + "unit_id": "weight_g", + "category": "seeds" + }, + { + "id": "ingredient_fennel", + "name": "Fennel", + "price": 0.015, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_lentils_red", + "name": "Red Lentils", + "price": 0.002, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_ketchup", + "name": "Ketchup", + "price": 0.01, + "unit_id": "volume_ml", + "category": "sauces" + }, + { + "id": "ingredient_pine_nuts", + "name": "Pine Nuts", + "price": 0.10, + "unit_id": "weight_g", + "category": "nuts" + }, + { + "id": "ingredient_tahini", + "name": "Tahini", + "price": 0.04, + "unit_id": "weight_g", + "category": "oils" + }, + { + "id": "ingredient_garlic_powder", + "name": "Garlic Powder", + "price": 0.02, + "unit_id": "weight_g", + "category": "spices" + }, + { + "id": "ingredient_dried_tomatoes", + "name": "Dried Tomatoes", + "price": 0.05, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_arugula", + "name": "Arugula", + "price": 0.02, + "unit_id": "weight_g", + "category": "vegetables" + }, + { + "id": "ingredient_lemon_zest", + "name": "Lemon Zest", + "price": 0.02, + "unit_id": "weight_g", + "category": "fruits" + }, + { + "id": "ingredient_bacon_bits", + "name": "Bacon Bits", + "price": 0.02, + "unit_id": "weight_g", + "category": "meat" + }, + { + "id": "ingredient_quinoa", + "name": "Quinoa", + "price": 0.004, + "unit_id": "weight_g", + "category": "grains" + }, + { + "id": "ingredient_white_chocolate", + "name": "White Chocolate", + "price": 0.02, + "unit_id": "weight_g", + "category": "sweets" + } +] \ No newline at end of file diff --git a/data/measure-units.json b/data/measure-units.json new file mode 100644 index 0000000..ec12346 --- /dev/null +++ b/data/measure-units.json @@ -0,0 +1,122 @@ +[ + { + "id": "weight_g", + "unit_name": "Gram", + "unit_symbol": "g", + "measurement_type": "weight", + "conversion_factor": 1, + "base_unit_id": null + }, + { + "id": "weight_kg", + "unit_name": "Kilogram", + "unit_symbol": "kg", + "measurement_type": "weight", + "conversion_factor": 1000, + "base_unit_id": "weight_g" + }, + { + "id": "weight_mg", + "unit_name": "Milligram", + "unit_symbol": "mg", + "measurement_type": "weight", + "conversion_factor": 0.001, + "base_unit_id": "weight_g" + }, + { + "id": "volume_ml", + "unit_name": "Milliliter", + "unit_symbol": "ml", + "measurement_type": "volume", + "conversion_factor": 1, + "base_unit_id": null + }, + { + "id": "volume_cl", + "unit_name": "Centiliter", + "unit_symbol": "cl", + "measurement_type": "volume", + "conversion_factor": 10, + "base_unit_id": "volume_ml" + }, + { + "id": "volume_L", + "unit_name": "Liter", + "unit_symbol": "L", + "measurement_type": "volume", + "conversion_factor": 1000, + "base_unit_id": "volume_ml" + }, + { + "id": "volume_cup", + "unit_name": "Cup", + "unit_symbol": "cup", + "measurement_type": "volume", + "conversion_factor": 240, + "base_unit_id": "volume_ml" + }, + { + "id": "weight_tsp", + "unit_name": "Teaspoon", + "unit_symbol": "tsp", + "measurement_type": "weight", + "conversion_factor": 4.9, + "base_unit_id": "weight_g" + }, + { + "id": "volume_tsp", + "unit_name": "Teaspoon", + "unit_symbol": "tsp", + "measurement_type": "volume", + "conversion_factor": 5, + "base_unit_id": "volume_ml" + }, + { + "id": "weight_tbsp", + "unit_name": "Tablespoon", + "unit_symbol": "tbsp", + "measurement_type": "weight", + "conversion_factor": 14.8, + "base_unit_id": "weight_g" + }, + { + "id": "volume_tbsp", + "unit_name": "Tablespoon", + "unit_symbol": "tbsp", + "measurement_type": "volume", + "conversion_factor": 15, + "base_unit_id": "volume_ml" + }, + { + "id": "length_cm", + "unit_name": "Centimeter", + "unit_symbol": "cm", + "measurement_type": "length", + "conversion_factor": 1, + "base_unit_id": null + }, + { + "id": "length_mm", + "unit_name": "Millimeter", + "unit_symbol": "mm", + "measurement_type": "length", + "conversion_factor": 0.1, + "base_unit_id": "length_cm" + }, + { + "id": "length_m", + "unit_name": "Meter", + "unit_symbol": "m", + "measurement_type": "length", + "conversion_factor": 100, + "base_unit_id": "length_cm" + }, + { + "id": "count_pc", + "unit_name": "Piece", + "unit_symbol": "pc", + "measurement_type": "count", + "conversion_factor": 1, + "base_unit_id": null + } +] diff --git a/data/recipes.json b/data/recipes.json new file mode 100644 index 0000000..de6c1f7 --- /dev/null +++ b/data/recipes.json @@ -0,0 +1,1756 @@ +[ + { + "id": "recipe_pancakes", + "name": "Pancakes", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_flour", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 75, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Dans un bol, mélanger la farine et le lait jusqu'à obtenir une pâte lisse.", + "Ajouter l'œuf et le beurre fondu, puis fouetter bien.", + "Faire chauffer une poêle antiadhésive à feu moyen. Verser une petite louche de pâte et cuire chaque pancake environ 2-3 minutes de chaque côté, jusqu'à ce qu'il soit doré." + ] + }, + { + "id": "recipe_omelette", + "name": "Omelette au fromage", + "preparation_time": 10, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_eggs", + "quantity": 2, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 0.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 0.25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Dans un bol, battre les œufs avec le sel et le poivre.", + "Faire chauffer une poêle avec un peu d'huile ou de beurre à feu moyen.", + "Verser les œufs battus et cuire 2-3 minutes sans remuer jusqu'à ce que l'omelette soit prise.", + "Ajouter le fromage râpé, plier l'omelette en deux, et servir immédiatement." + ] + }, + { + "id": "recipe_french_toast", + "name": "Pain perdu", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_bread", + "quantity": 2, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 50, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Dans un bol, mélanger le lait, l'œuf et le sucre.", + "Tremper les tranches de pain dans le mélange jusqu'à ce qu'elles soient bien imbibées.", + "Faire fondre le beurre dans une poêle à feu moyen et faire dorer les tranches de pain pendant 2 minutes de chaque côté. Servir chaud." + ] + }, + { + "id": "recipe_ratatouille", + "name": "Ratatouille", + "preparation_time": 45, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 75, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_zucchini", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bell_pepper", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 7.5, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 2.5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper tous les légumes en dés.", + "Faire chauffer l'huile d'olive dans une casserole à feu moyen.", + "Ajouter les oignons et l'ail, puis faire revenir pendant 5 minutes jusqu'à ce qu'ils soient tendres.", + "Ajouter les autres légumes, couvrir et cuire à feu doux pendant 30 minutes en remuant de temps en temps. Servir chaud." + ] + }, + { + "id": "recipe_mashed_potatoes", + "name": "Purée de pommes de terre", + "preparation_time": 25, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_potatoes", + "quantity": 125, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 25, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 12.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 0.5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Éplucher et couper les pommes de terre en morceaux.", + "Les faire cuire dans de l'eau bouillante salée pendant 20 minutes jusqu'à ce qu'elles soient tendres.", + "Égoutter et écraser les pommes de terre, puis ajouter le lait chaud et le beurre. Mélanger jusqu'à obtenir une purée lisse." + ] + }, + { + "id": "recipe_quiche", + "name": "Quiche au fromage", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_flour", + "quantity": 62.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 31.25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 50, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Mélanger la farine et le beurre pour former une pâte.", + "Étaler la pâte dans un moule à tarte.", + "Dans un bol, fouetter l'œuf avec le lait et le fromage.", + "Verser le mélange dans le moule et cuire au four à 180°C pendant 30 minutes." + ] + }, + { + "id": "recipe_rice_pilaf", + "name": "Riz Pilaf", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_rice", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 12.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 0.25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire revenir les oignons dans l'huile d'olive dans une casserole.", + "Ajouter le riz et remuer pendant 2 minutes.", + "Ajouter de l'eau (environ 75 ml), assaisonner de sel et laisser mijoter à couvert pendant 15 minutes, jusqu'à ce que le riz soit tendre." + ] + }, + { + "id": "recipe_chocolate_mousse", + "name": "Mousse au chocolat", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_chocolate", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire fondre le chocolat au bain-marie.", + "Séparer le blanc et le jaune d'œuf.", + "Incorporer le jaune au chocolat fondu. Battre le blanc en neige avec le sucre, puis l'incorporer délicatement au mélange chocolaté.", + "Réfrigérer pendant au moins 2 heures avant de servir." + ] + }, + { + "id": "recipe_vegetable_soup", + "name": "Soupe de légumes", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_carrots", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_potatoes", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 12.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 1.25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Éplucher et couper les légumes en morceaux.", + "Dans une grande casserole, faire revenir l'ail et l'oignon dans un peu d'huile.", + "Ajouter les autres légumes et couvrir d'eau.", + "Laisser mijoter pendant 30 minutes à feu moyen, puis mixer pour une consistance lisse si désiré." + ] + }, + { + "id": "recipe_caesar_salad", + "name": "Salade César", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_lettuce", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_chicken_breast", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 12.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bread", + "quantity": 0.25, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 0.25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Griller le poulet et le couper en tranches.", + "Déchirer la laitue, ajouter des croûtons faits avec le pain grillé et parsemer de fromage râpé.", + "Ajouter les morceaux de poulet grillé, mélanger avec une vinaigrette César." + ] + }, + { + "id": "recipe_gratin_dauphinois", + "name": "Gratin Dauphinois", + "preparation_time": 60, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_potatoes", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 50, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 2, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Éplucher et couper les pommes de terre en fines rondelles.", + "Frotter un plat à gratin avec une gousse d'ail, puis le beurrer.", + "Disposer les rondelles de pommes de terre dans le plat, verser le lait et saupoudrer de fromage.", + "Cuire au four pendant 45 minutes jusqu'à ce que le dessus soit doré." + ] + }, + { + "id": "recipe_pasta_bolognese", + "name": "Pâtes à la bolognaise", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_pasta", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 2, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_beef", + "quantity": 50, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire chauffer l'huile dans une poêle et y faire revenir l'oignon et l'ail.", + "Ajouter la viande hachée et cuire jusqu'à ce qu'elle soit dorée.", + "Ajouter les tomates, assaisonner et laisser mijoter 15 minutes.", + "Cuire les pâtes dans de l'eau bouillante salée, égoutter, et servir avec la sauce." + ] + }, + { + "id": "recipe_salade_niçoise", + "name": "Salade Niçoise", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_lettuce", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_black_olive", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tuna", + "quantity": 50, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Cuire l'œuf dur pendant 10 minutes, puis le couper en quartiers.", + "Disposer la laitue, les tomates, l'œuf, les olives et le thon dans une assiette.", + "Assaisonner avec une vinaigrette au choix." + ] + }, + { + "id": "recipe_ratatouille_tartine", + "name": "Tartine de ratatouille", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_bread", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_zucchini", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bell_pepper", + "quantity": 25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper les légumes en petits dés et les faire revenir dans une poêle avec un peu d'huile pendant 10 minutes.", + "Toaster le pain et disposer les légumes cuits dessus.", + "Servir chaud avec un peu de sel et de poivre." + ] + }, + { + "id": "recipe_soupe_oignon", + "name": "Soupe à l'oignon", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_onions", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bread", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 20, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Émincer les oignons et les faire revenir dans du beurre jusqu'à ce qu'ils soient dorés.", + "Ajouter la farine, bien mélanger, puis ajouter de l'eau et laisser mijoter 30 minutes.", + "Servir avec une tranche de pain grillée et du fromage râpé." + ] + }, + { + "id": "recipe_crepes", + "name": "Crêpes", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_flour", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 100, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Dans un bol, mélanger la farine, le lait et l'œuf jusqu'à obtenir une pâte lisse.", + "Faire chauffer une poêle et y verser une petite quantité de pâte.", + "Cuire chaque crêpe 1-2 minutes de chaque côté." + ] + }, + { + "id": "recipe_moules_frites", + "name": "Moules-frites", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_mussels", + "quantity": 200, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_potatoes", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Nettoyer les moules et les cuire à feu vif avec de l'ail et un peu d'huile.", + "Couper les pommes de terre en frites et les faire frire jusqu'à ce qu'elles soient dorées.", + "Servir les moules avec les frites." + ] + }, + { + "id": "recipe_gratin_legumes", + "name": "Gratin de légumes", + "preparation_time": 35, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_zucchini", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Couper les légumes en rondelles et les disposer dans un plat à gratin.", + "Saupoudrer de fromage râpé et arroser d'un filet d'huile d'olive.", + "Cuire au four pendant 25 minutes." + ] + }, + { + "id": "recipe_blanc_poulet_salade", + "name": "Blanc de poulet et salade", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_chicken_breast", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_lettuce", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Cuire le blanc de poulet à la poêle pendant 10 minutes jusqu'à ce qu'il soit bien doré.", + "Servir avec la salade de laitue et de tomates assaisonnée d'huile d'olive." + ] + }, + { + "id": "recipe_boeuf_bourguignon", + "name": "Bœuf Bourguignon", + "preparation_time": 180, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_beef", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_carrots", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 2, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_red_wine", + "quantity": 50, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Faire revenir le bœuf avec les oignons, l'ail et les carottes dans une cocotte.", + "Ajouter le vin rouge et laisser mijoter pendant 2 heures 30 à feu doux.", + "Servir chaud avec des pommes de terre ou du pain." + ] + }, + { + "id": "recipe_potage_poireaux_pommes_terre", + "name": "Potage aux poireaux et pommes de terre", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_potatoes", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_leek", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Éplucher et couper les pommes de terre et le poireau.", + "Faire fondre le beurre dans une casserole, ajouter l'oignon, le poireau et faire revenir pendant 5 minutes.", + "Ajouter les pommes de terre et couvrir d'eau. Laisser mijoter 20 minutes, puis mixer le potage." + ] + }, + { + "id": "recipe_poulet_roti", + "name": "Poulet rôti", + "preparation_time": 60, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_chicken_breast", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 200°C.", + "Placer le blanc de poulet dans un plat, ajouter l'ail, le sel et l'huile d'olive.", + "Cuire au four pendant 45 minutes en arrosant le poulet avec son jus à mi-cuisson." + ] + }, + { + "id": "recipe_tartiflette", + "name": "Tartiflette", + "preparation_time": 50, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_potatoes", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bacon", + "quantity": 30, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 50, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Faire cuire les pommes de terre dans de l'eau bouillante pendant 15 minutes, puis les couper en rondelles.", + "Faire revenir les oignons et les lardons à la poêle.", + "Disposer les pommes de terre dans un plat, ajouter les oignons et lardons, puis le fromage.", + "Cuire au four pendant 20 minutes jusqu'à ce que le fromage soit fondu et doré." + ] + }, + { + "id": "recipe_risotto_champignons", + "name": "Risotto aux champignons", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_rice", + "quantity": 80, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_mushrooms", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 15, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Faire chauffer l'huile d'olive dans une poêle et y faire revenir l'oignon et les champignons.", + "Ajouter le riz et remuer jusqu'à ce qu'il soit translucide.", + "Ajouter de l'eau progressivement en remuant, jusqu'à ce que le riz soit cuit et crémeux.", + "Incorporer le fromage à la fin pour un risotto onctueux." + ] + }, + { + "id": "recipe_clafoutis_cerises", + "name": "Clafoutis aux cerises", + "preparation_time": 35, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_cherries", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 50, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 15, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Dans un bol, mélanger la farine, le lait, l'œuf et le sucre jusqu'à obtenir une pâte lisse.", + "Verser la pâte dans un moule, ajouter les cerises et cuire pendant 25 minutes." + ] + }, + { + "id": "recipe_croque_monsieur", + "name": "Croque-Monsieur", + "preparation_time": 10, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_bread", + "quantity": 2, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_ham", + "quantity": 30, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Beurrer une tranche de pain, ajouter le jambon et le fromage, puis recouvrir avec l'autre tranche.", + "Enfourner pendant 5-10 minutes jusqu'à ce que le fromage soit fondu." + ] + }, + { + "id": "recipe_roquefort_poire_noix", + "name": "Salade roquefort, poire et noix", + "preparation_time": 10, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_lettuce", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_pear", + "quantity": 1, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_walnuts", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper la poire en tranches fines.", + "Dans un bol, mélanger la laitue, les morceaux de poire, le roquefort émietté et les noix.", + "Servir avec une vinaigrette de votre choix." + ] + }, + { + "id": "recipe_soupe_poisson", + "name": "Soupe de poisson", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_fish", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 2, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Faire revenir l'oignon et l'ail dans l'huile d'olive.", + "Ajouter le poisson et les tomates, couvrir d'eau.", + "Laisser mijoter pendant 30 minutes, puis mixer la soupe." + ] + }, + { + "id": "recipe_tarte_aux_pommes", + "name": "Tarte aux pommes", + "preparation_time": 45, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_apples", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Préparer une pâte avec la farine, le beurre et une pincée de sel.", + "Étaler la pâte, ajouter les pommes en tranches fines et saupoudrer de sucre.", + "Cuire au four pendant 30 minutes." + ] + }, + { + "id": "recipe_gratin_fenouil", + "name": "Gratin de fenouil", + "preparation_time": 35, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_fennel", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Couper le fenouil en tranches fines et les disposer dans un plat à gratin.", + "Arroser d'huile d'olive, ajouter du sel et parsemer de fromage.", + "Cuire au four pendant 25 minutes." + ] + }, + { + "id": "recipe_blanquette_de_veau", + "name": "Blanquette de veau", + "preparation_time": 90, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_veal", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_carrots", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_mushrooms", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire revenir le veau dans une cocotte avec du beurre jusqu'à ce qu'il soit doré.", + "Ajouter les carottes et les oignons en morceaux, couvrir d'eau et laisser mijoter 1h.", + "Ajouter les champignons, et laisser cuire encore 15 minutes.", + "Servir avec du riz ou des pommes de terre." + ] + }, + { + "id": "recipe_raclette", + "name": "Raclette", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_cheese", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_potatoes", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_ham", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 0.5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Cuire les pommes de terre à l'eau pendant 20 minutes jusqu'à ce qu'elles soient tendres.", + "Faire fondre le fromage à raclette dans un appareil à raclette.", + "Servir les pommes de terre avec le fromage fondu, le jambon et du poivre." + ] + }, + { + "id": "recipe_beurre_noisette_poisson", + "name": "Poisson au beurre noisette", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_fish", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_lemon_zest", + "quantity": 1, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire cuire le poisson à la poêle 3-4 minutes de chaque côté.", + "Dans une autre poêle, faire fondre le beurre jusqu'à ce qu'il devienne noisette.", + "Ajouter le zeste de citron, verser le beurre sur le poisson et servir chaud." + ] + }, + { + "id": "recipe_risotto_asperges", + "name": "Risotto aux asperges", + "preparation_time": 35, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_rice", + "quantity": 80, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_asparagus", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 15, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Faire revenir l'oignon et les asperges dans l'huile d'olive.", + "Ajouter le riz, remuer, puis ajouter de l'eau progressivement en remuant jusqu'à ce que le riz soit crémeux.", + "Incorporer le fromage et servir immédiatement." + ] + }, + { + "id": "recipe_flan_patissier", + "name": "Flan pâtissier", + "preparation_time": 50, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_milk", + "quantity": 100, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 15, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_eggs", + "quantity": 1, + "unit_id": "count_pc" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Mélanger la farine, le sucre, l'œuf et le lait pour obtenir une pâte lisse.", + "Verser dans un moule et cuire pendant 30-35 minutes." + ] + }, + { + "id": "recipe_melon_jambon", + "name": "Melon au jambon", + "preparation_time": 5, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_melon", + "quantity": 0.5, + "unit_id": "count_pc" + }, + { + "ingredient_id": "ingredient_ham", + "quantity": 50, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper le melon en deux, retirer les graines et couper la chair en tranches.", + "Disposer les tranches de melon sur une assiette et ajouter les tranches de jambon par-dessus.", + "Servir frais." + ] + }, + { + "id": "recipe_tarte_tatin", + "name": "Tarte Tatin", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_apples", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 15, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 50, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Dans une poêle, faire caraméliser le sucre avec le beurre, puis ajouter les pommes.", + "Préparer une pâte avec la farine et l'eau, étaler sur les pommes et enfourner 25 minutes.", + "Retourner la tarte avant de servir." + ] + }, + { + "id": "recipe_choucroute", + "name": "Choucroute", + "preparation_time": 90, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_cabbage", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_sausage", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_potatoes", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire cuire le chou dans de l'eau pendant 30 minutes.", + "Ajouter les pommes de terre et les saucisses, cuire encore 30 minutes.", + "Servir chaud avec du poivre." + ] + }, + { + "id": "recipe_crumble_pommes", + "name": "Crumble aux pommes", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_apples", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_flour", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 15, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_sugar", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Disposer les pommes en morceaux dans un plat à gratin.", + "Mélanger la farine, le beurre et le sucre pour former un crumble.", + "Saupoudrer le crumble sur les pommes et cuire 20 minutes." + ] + }, + { + "id": "recipe_endives_jambon", + "name": "Endives au jambon", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_endive", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_ham", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Cuire les endives à l'eau pendant 20 minutes.", + "Enrouler chaque endive dans une tranche de jambon, disposer dans un plat à gratin.", + "Saupoudrer de fromage, ajouter des morceaux de beurre et cuire au four à 180°C pendant 15 minutes." + ] + }, + { + "id": "recipe_steak_frites", + "name": "Steak frites", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_beef", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_potatoes", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 1, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 0.5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Éplucher et couper les pommes de terre en bâtonnets pour faire des frites.", + "Faire frire les pommes de terre jusqu'à ce qu'elles soient dorées et croustillantes.", + "Cuire le steak dans une poêle chaude pendant 3-4 minutes de chaque côté, assaisonner avec du sel et du poivre.", + "Servir le steak avec les frites." + ] + }, + { + "id": "recipe_boeuf_carottes", + "name": "Bœuf aux carottes", + "preparation_time": 120, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_beef", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_carrots", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire revenir le bœuf, l'oignon et l'ail dans une cocotte jusqu'à ce qu'ils soient dorés.", + "Ajouter les carottes en rondelles et de l'eau pour couvrir.", + "Laisser mijoter à feu doux pendant 2 heures, en remuant de temps en temps." + ] + }, + { + "id": "recipe_ratatouille_fraiche", + "name": "Ratatouille fraîche", + "preparation_time": 45, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_tomatoes", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_zucchini", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_eggplant", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bell_pepper", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper les légumes en dés.", + "Faire revenir l'oignon dans une casserole avec un peu d'huile d'olive.", + "Ajouter les autres légumes et cuire à feu doux pendant 30 minutes, en remuant régulièrement." + ] + }, + { + "id": "recipe_potatoes_gratin", + "name": "Gratin de pommes de terre", + "preparation_time": 45, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_potatoes", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_milk", + "quantity": 50, + "unit_id": "volume_ml" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 20, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Éplucher et couper les pommes de terre en fines rondelles.", + "Disposer les pommes de terre dans un plat à gratin avec le lait, le fromage et l'ail.", + "Cuire au four pendant 35 minutes jusqu'à ce que le dessus soit doré." + ] + }, + { + "id": "recipe_soupe_carottes", + "name": "Soupe de carottes", + "preparation_time": 30, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_carrots", + "quantity": 150, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_olive_oil", + "quantity": 5, + "unit_id": "volume_ml" + } + ], + "instructions": [ + "Faire revenir l'oignon et l'ail dans une casserole avec l'huile d'olive.", + "Ajouter les carottes coupées en rondelles et couvrir d'eau.", + "Laisser mijoter pendant 20 minutes, puis mixer la soupe." + ] + }, + { + "id": "recipe_tartare_boeuf", + "name": "Tartare de bœuf", + "preparation_time": 15, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_beef", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 0.5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_salt", + "quantity": 1, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper le bœuf en petits dés.", + "Ajouter l'oignon haché, le sel et le poivre, puis bien mélanger.", + "Servir immédiatement avec une salade." + ] + }, + { + "id": "recipe_poelee_legumes", + "name": "Poêlée de légumes", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_zucchini", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_bell_pepper", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_onions", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Couper tous les légumes en dés.", + "Faire revenir l'oignon et l'ail dans une poêle avec un peu d'huile d'olive.", + "Ajouter les autres légumes et cuire à feu moyen pendant 15 minutes en remuant de temps en temps." + ] + }, + { + "id": "recipe_coquilles_saint_jacques", + "name": "Coquilles Saint-Jacques", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_scallops", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_garlic", + "quantity": 5, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_parsley", + "quantity": 2, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Faire fondre le beurre dans une poêle et y ajouter l'ail et le persil.", + "Ajouter les Saint-Jacques et cuire 2-3 minutes de chaque côté.", + "Servir chaud avec du pain." + ] + }, + { + "id": "recipe_boudin_aux_pommes", + "name": "Boudin aux pommes", + "preparation_time": 20, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_blood_sausage", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_apples", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_black_pepper", + "quantity": 0.5, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Éplucher et couper les pommes en quartiers.", + "Faire fondre le beurre dans une poêle, y faire revenir les pommes jusqu'à ce qu'elles soient dorées.", + "Cuire le boudin à feu moyen dans la même poêle, assaisonner et servir ensemble." + ] + }, + { + "id": "recipe_endives_gratin", + "name": "Endives au gratin", + "preparation_time": 40, + "servings": 1, + "ingredients": [ + { + "ingredient_id": "ingredient_endive", + "quantity": 100, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_cheese", + "quantity": 25, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_ham", + "quantity": 50, + "unit_id": "weight_g" + }, + { + "ingredient_id": "ingredient_butter", + "quantity": 10, + "unit_id": "weight_g" + } + ], + "instructions": [ + "Préchauffer le four à 180°C.", + "Cuire les endives dans de l'eau bouillante pendant 15 minutes, puis les égoutter.", + "Enrouler chaque endive dans une tranche de jambon et disposer dans un plat à gratin.", + "Parsemer de fromage râpé, ajouter du beurre et cuire au four pendant 20 minutes." + ] + } +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f06965a..e13114d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,16 @@ "license": "UNLICENSED", "dependencies": { "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", + "@nestjs/mongoose": "^10.1.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^8.0.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "dotenv": "^16.4.5", + "mongodb": "^6.10.0", + "mongoose": "^8.8.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, @@ -1569,6 +1576,15 @@ "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", "license": "MIT" }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@nestjs/cli": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.7.tgz", @@ -1644,6 +1660,21 @@ } } }, + "node_modules/@nestjs/config": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", + "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.5", + "dotenv-expand": "10.0.0", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" + } + }, "node_modules/@nestjs/core": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.7.tgz", @@ -1702,6 +1733,18 @@ } } }, + "node_modules/@nestjs/mongoose": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.1.0.tgz", + "integrity": "sha512-1ExAnZUfh2QffEaGjqYGgVPy/sYBQCVLCLqVgkcClKx/BCd0QNgND8MB70lwyobp3nm/+nbGQqBpu9F3/hgOCw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", + "rxjs": "^7.0.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.7.tgz", @@ -2845,6 +2888,27 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/validator": { + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==", + "license": "MIT" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3852,6 +3916,15 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.9.0.tgz", + "integrity": "sha512-X9hJeyeM0//Fus+0pc5dSUMhhrrmWwQUtdavaQeF3Ta6m69matZkGWV/MrBcnwUeLC8W9kwwc2hfkZgUuCX3Ig==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -4054,6 +4127,23 @@ "dev": true, "license": "MIT" }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/clean-stack": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", @@ -4640,7 +4730,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4835,6 +4924,27 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -7805,6 +7915,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7849,6 +7968,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.13.tgz", + "integrity": "sha512-LIJmXxgs7o1njVZPcX5fkbtcFgDnXXPvJQQBH5Ho/8+r6BFlJaEbJ+bAiaUGaChWUhFtvawwdmXIOz4wZBANCg==", + "license": "MIT" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -8172,6 +8297,12 @@ "node": ">= 4.0.0" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -8337,6 +8468,139 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mongodb": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mongoose": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.0.tgz", + "integrity": "sha512-KluvgwnQB1GPOYZZXUHJRjS1TW6xxwTlf/YgjWExuuNanIe3W7VcR7dDXQVCIRk8L7NYge8EnoTcu2grWtN+XQ==", + "license": "MIT", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "~6.10.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -12380,7 +12644,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13387,6 +13650,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -13567,6 +13836,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -14730,6 +15008,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 3535124..e110a0f 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,16 @@ }, "dependencies": { "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", + "@nestjs/mongoose": "^10.1.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^8.0.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "dotenv": "^16.4.5", + "mongodb": "^6.10.0", + "mongoose": "^8.8.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1" }, diff --git a/src/app.module.ts b/src/app.module.ts index 8662803..8ab0383 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,10 +1,49 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { MongooseModule } from '@nestjs/mongoose'; +import { IngredientsController } from './services/ingredient/ingredient/ingredients.controller'; +import { IngredientsService } from './services/ingredient/ingredient/ingredients.service'; +import { + Ingredient, + IngredientSchema, +} from './schemas/ingredient/ingredient.schema'; +import { Recipe, RecipeSchema } from './schemas/recipe/recipe.schema'; +import { RecipesController } from './services/recipe/recipes.controller'; +import { RecipesService } from './services/recipe/recipes.service'; +import { + Measureunit, + MeasureunitSchema, +} from './schemas/measureunit/measureunit.schema'; +import { MeasureunitsController } from './services/measureunit/measureunits/measureunits.controller'; +import { MeasureunitsService } from './services/measureunit/measureunits/measureunits.service'; +import { ConfigModule } from '@nestjs/config'; @Module({ - imports: [], - controllers: [AppController], - providers: [AppService], + imports: [ + ConfigModule.forRoot(), + MongooseModule.forRoot( + process.env.MongoURI, + ), + MongooseModule.forFeature([ + { name: Ingredient.name, schema: IngredientSchema }, + ]), + MongooseModule.forFeature([{ name: Recipe.name, schema: RecipeSchema }]), + MongooseModule.forFeature([ + { name: Measureunit.name, schema: MeasureunitSchema }, + ]), + ], + controllers: [ + AppController, + IngredientsController, + RecipesController, + MeasureunitsController, + ], + providers: [ + AppService, + IngredientsService, + RecipesService, + MeasureunitsService, + ], }) export class AppModule {} diff --git a/src/dtos/ingredient/ingredient.dto.ts b/src/dtos/ingredient/ingredient.dto.ts new file mode 100644 index 0000000..61766fa --- /dev/null +++ b/src/dtos/ingredient/ingredient.dto.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNumber, IsOptional, IsPositive } from 'class-validator'; +import { Type } from 'class-transformer'; +import { PartialType } from '@nestjs/swagger'; + +export class CreateIngredientDto { + @ApiProperty() + @IsString() + id: string; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + price: number; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + quantity: number; + + @ApiProperty() + @IsString() + unit_id: string; + + @ApiProperty() + @IsString() + @IsOptional() + category?: string; +} + +export class UpdateIngredientDto extends PartialType(CreateIngredientDto) {} diff --git a/src/dtos/measureunit/measureunit.dto.ts b/src/dtos/measureunit/measureunit.dto.ts new file mode 100644 index 0000000..ee61805 --- /dev/null +++ b/src/dtos/measureunit/measureunit.dto.ts @@ -0,0 +1,34 @@ +import { IsString, IsNumber, IsOptional, IsPositive } from 'class-validator'; +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; + +export class CreateMeasureunitDto { + @ApiProperty() + @IsString() + id: string; + + @ApiProperty() + @IsString() + unit_name: string; + + @ApiProperty() + @IsString() + unit_symbol: string; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + measurement_type: number; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + conversion_factor: number; + + @ApiProperty({ required: false, nullable: true }) + @IsOptional() + @IsString() + base_unit_id?: string; +} + +export class UpdateMeasureunitDto extends PartialType(CreateMeasureunitDto) {} diff --git a/src/dtos/recipe/recipe.dto.ts b/src/dtos/recipe/recipe.dto.ts new file mode 100644 index 0000000..bc32bf5 --- /dev/null +++ b/src/dtos/recipe/recipe.dto.ts @@ -0,0 +1,57 @@ +import { IsString, IsNumber, IsArray, ValidateNested, IsPositive } from 'class-validator'; +import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; +import { PartialType } from '@nestjs/swagger'; + +class IngredientDetailDto { + @ApiProperty() + @IsString() + ingredient_id: string; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + quantity: number; + + @ApiProperty() + @IsString() + unit_id: string; +} + +export class CreateRecipeDto { + @ApiProperty() + @IsString() + id: string; + + @ApiProperty() + @IsString() + name: string; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + preparation_time: number; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + servings: number; + + @ApiProperty() + @IsPositive() + @Type(() => Number) + cost: number; + + @ApiProperty({ type: [IngredientDetailDto] }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => IngredientDetailDto) + ingredients: IngredientDetailDto[]; + + @ApiProperty({ type: [String] }) + @IsArray() + @IsString({ each: true }) + instructions: string[]; +} + +export class UpdateRecipeDto extends PartialType(CreateRecipeDto) {} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index fdf6db1..5db3070 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,11 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.useGlobalPipes(new ValidationPipe); const config = new DocumentBuilder() .setTitle('EcoMeal API Documentation') .setDescription('Enjoy using this swaager ;-)') diff --git a/src/schemas/ingredient/ingredient.schema.ts b/src/schemas/ingredient/ingredient.schema.ts new file mode 100644 index 0000000..3e7eaee --- /dev/null +++ b/src/schemas/ingredient/ingredient.schema.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; + +export type IngredientDocument = HydratedDocument; + +@Schema({ strict: false, timestamps: true }) +export class Ingredient { + @Prop({ required: true, unique: true }) + id: string; + + @Prop({ required: true }) + name: string; + + @Prop({ required: true }) + price: number; + + @Prop({ required: true }) + quantity: number; + + @Prop({ required: true }) + unit_id: string; + + @Prop({ required: false }) + category: string; +} + +export const IngredientSchema = SchemaFactory.createForClass(Ingredient); diff --git a/src/schemas/measureunit/measureunit.schema.ts b/src/schemas/measureunit/measureunit.schema.ts new file mode 100644 index 0000000..f3b211f --- /dev/null +++ b/src/schemas/measureunit/measureunit.schema.ts @@ -0,0 +1,27 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; + +export type MeasureunitDocument = HydratedDocument; + +@Schema({ strict: false, timestamps: true }) +export class Measureunit { + @Prop({ required: true, unique: true, type: String }) + id: string; + + @Prop({ required: true, type: String }) + unit_name: string; + + @Prop({ required: true, type: String }) + unit_symbol: string; + + @Prop({ required: true, type: Number }) + measurement_type: number; + + @Prop({ required: true, type: Number }) + conversion_factor: number; + + @Prop({ required: true, type: String, default: null }) + base_unit_id: string | null; +} + +export const MeasureunitSchema = SchemaFactory.createForClass(Measureunit); diff --git a/src/schemas/recipe/recipe.schema.ts b/src/schemas/recipe/recipe.schema.ts new file mode 100644 index 0000000..5c19e79 --- /dev/null +++ b/src/schemas/recipe/recipe.schema.ts @@ -0,0 +1,42 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; + +export type RecipeDocument = HydratedDocument; + +@Schema({ timestamps: true }) +export class IngredientDetail { + @Prop({ required: true }) + ingredient_id: string; + + @Prop({ required: true, type: Number }) + quantity: number; + + @Prop({ required: true }) + unit_id: string; +} + +@Schema({ strict: false, timestamps: true }) +export class Recipe { + @Prop({ required: true, unique: true }) + id: string; + + @Prop({ required: true }) + name: string; + + @Prop({ required: true, type: Number }) + preparation_time: number; + + @Prop({ required: true, type: Number }) + servings: number; + + @Prop({ required: true, type: Number }) + cost: number; + + @Prop({ required: true, type: [IngredientDetail] }) + ingredients: IngredientDetail[]; + + @Prop({ required: true, type: [String] }) + instructions: string[]; +} + +export const RecipeSchema = SchemaFactory.createForClass(Recipe); diff --git a/src/services/ingredient/ingredient/ingredients.controller.spec.ts b/src/services/ingredient/ingredient/ingredients.controller.spec.ts new file mode 100644 index 0000000..44d8b60 --- /dev/null +++ b/src/services/ingredient/ingredient/ingredients.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { IngredientsController } from './ingredients.controller'; + +describe('IngredientsController', () => { + let controller: IngredientsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [IngredientsController], + }).compile(); + + controller = module.get(IngredientsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/services/ingredient/ingredient/ingredients.controller.ts b/src/services/ingredient/ingredient/ingredients.controller.ts new file mode 100644 index 0000000..cb69603 --- /dev/null +++ b/src/services/ingredient/ingredient/ingredients.controller.ts @@ -0,0 +1,93 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Put, + Delete, + HttpStatus, + HttpException, +} from '@nestjs/common'; +import { IngredientsService } from './ingredients.service'; +import { CreateIngredientDto, UpdateIngredientDto } from 'src/dtos/ingredient/ingredient.dto'; +import { Ingredient } from 'src/schemas/ingredient/ingredient.schema'; +import { ApiTags } from '@nestjs/swagger'; + +@ApiTags('ingredients') +@Controller('ingredients') +export class IngredientsController { + constructor(private readonly IngredientsService: IngredientsService) {} + + @Post() + async create( + @Body() createIngredientDto: CreateIngredientDto, + ): Promise { + try { + const newIngredient = + await this.IngredientsService.create(createIngredientDto); + return newIngredient; + } catch (err) { + throw new HttpException( + { + status: HttpStatus.BAD_REQUEST, + error: 'Error: Ingredient not created!', + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + @Get() + async findAll(): Promise { + return this.IngredientsService.findAll(); + } + + @Get(':id') + async findOne(@Param('id') id: string): Promise { + try { + return this.IngredientsService.findOne(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Ingredient with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Put(':id') + async update( + @Param('id') id: string, + @Body() updateIngredientDto: UpdateIngredientDto, + ): Promise { + try { + return this.IngredientsService.update(id, updateIngredientDto); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Ingredient with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Delete(':id') + async remove(@Param('id') id: string): Promise { + try { + return this.IngredientsService.remove(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Ingredient with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } +} diff --git a/src/services/ingredient/ingredient/ingredients.service.spec.ts b/src/services/ingredient/ingredient/ingredients.service.spec.ts new file mode 100644 index 0000000..46af327 --- /dev/null +++ b/src/services/ingredient/ingredient/ingredients.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { IngredientsService } from './ingredients.service'; + +describe('IngredientsService', () => { + let service: IngredientsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [IngredientsService], + }).compile(); + + service = module.get(IngredientsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/services/ingredient/ingredient/ingredients.service.ts b/src/services/ingredient/ingredient/ingredients.service.ts new file mode 100644 index 0000000..3f1159f --- /dev/null +++ b/src/services/ingredient/ingredient/ingredients.service.ts @@ -0,0 +1,60 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { + Ingredient, + IngredientDocument, +} from 'src/schemas/ingredient/ingredient.schema'; +import { + CreateIngredientDto, + UpdateIngredientDto, +} from 'src/dtos/ingredient/ingredient.dto'; + +@Injectable() +export class IngredientsService { + + constructor( + @InjectModel(Ingredient.name) + private ingredientModel: Model, + ) {} + + async create(createIngredientDto: CreateIngredientDto): Promise { + const createdIngredient = new this.ingredientModel(createIngredientDto); + return createdIngredient.save(); + } + + async findAll(): Promise { + return this.ingredientModel.find().exec(); + } + + async findOne(id: string): Promise { + const ingredient = await this.ingredientModel.findOne({ id }).exec(); + if (!ingredient) { + throw new NotFoundException(`Ingredient with ID ${id} not found`); + } + return ingredient; + } + + async update( + id: string, + updateIngredientDto: UpdateIngredientDto, + ): Promise { + const updatedIngredient = await this.ingredientModel + .findOneAndUpdate({ id }, updateIngredientDto, { new: true }) + .exec(); + if (!updatedIngredient) { + throw new NotFoundException(`Ingredient with ID ${id} not found`); + } + return updatedIngredient; + } + + async remove(id: string): Promise { + const deletedIngredient = await this.ingredientModel + .findOneAndDelete({ id }) + .exec(); + if (!deletedIngredient) { + throw new NotFoundException(`Ingredient with ID ${id} not found`); + } + return deletedIngredient; + } +} diff --git a/src/services/measureunit/measureunits/measureunits.controller.spec.ts b/src/services/measureunit/measureunits/measureunits.controller.spec.ts new file mode 100644 index 0000000..ee7b25a --- /dev/null +++ b/src/services/measureunit/measureunits/measureunits.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MeasureunitsController } from './measureunits.controller'; + +describe('MeasureunitsController', () => { + let controller: MeasureunitsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MeasureunitsController], + }).compile(); + + controller = module.get(MeasureunitsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/services/measureunit/measureunits/measureunits.controller.ts b/src/services/measureunit/measureunits/measureunits.controller.ts new file mode 100644 index 0000000..d63adea --- /dev/null +++ b/src/services/measureunit/measureunits/measureunits.controller.ts @@ -0,0 +1,94 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Put, + Delete, + HttpStatus, + HttpException, +} from '@nestjs/common'; +import { MeasureunitsService } from './measureunits.service'; +import { ApiTags } from '@nestjs/swagger'; +import { + CreateMeasureunitDto, + UpdateMeasureunitDto, +} from 'src/dtos/measureunit/measureunit.dto'; +import { Measureunit } from 'src/schemas/measureunit/measureunit.schema'; + +@ApiTags('Measureunits') +@Controller('measureunits') +export class MeasureunitsController { + constructor(private readonly measureunitsService: MeasureunitsService) {} + + @Post() + async create( + @Body() createMeasureunitDto: CreateMeasureunitDto, + ): Promise { + try { + return await this.measureunitsService.create(createMeasureunitDto); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.BAD_REQUEST, + error: 'Error: Measureunit not created!', + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + @Get() + async findAll(): Promise { + return this.measureunitsService.findAll(); + } + + @Get(':id') + async findOne(@Param('id') id: string): Promise { + try { + return await this.measureunitsService.findOne(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Measureunit with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Put(':id') + async update( + @Param('id') id: string, + @Body() updateMeasureunitDto: UpdateMeasureunitDto, + ): Promise { + try { + return await this.measureunitsService.update(id, updateMeasureunitDto); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Measureunit with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Delete(':id') + async remove(@Param('id') id: string): Promise { + try { + return await this.measureunitsService.remove(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Measureunit with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } +} diff --git a/src/services/measureunit/measureunits/measureunits.service.spec.ts b/src/services/measureunit/measureunits/measureunits.service.spec.ts new file mode 100644 index 0000000..29816d4 --- /dev/null +++ b/src/services/measureunit/measureunits/measureunits.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MeasureunitsService } from './measureunits.service'; + +describe('MeasureunitsService', () => { + let service: MeasureunitsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MeasureunitsService], + }).compile(); + + service = module.get(MeasureunitsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/services/measureunit/measureunits/measureunits.service.ts b/src/services/measureunit/measureunits/measureunits.service.ts new file mode 100644 index 0000000..0474788 --- /dev/null +++ b/src/services/measureunit/measureunits/measureunits.service.ts @@ -0,0 +1,55 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { CreateMeasureunitDto, UpdateMeasureunitDto } from 'src/dtos/measureunit/measureunit.dto'; +import { Measureunit, MeasureunitDocument } from 'src/schemas/measureunit/measureunit.schema'; + +@Injectable() +export class MeasureunitsService { + constructor( + @InjectModel(Measureunit.name) + private measureunitModel: Model, + ) {} + + async create( + createMeasureunitDto: CreateMeasureunitDto, + ): Promise { + const createdMeasureunit = new this.measureunitModel(createMeasureunitDto); + return createdMeasureunit.save(); + } + + async findAll(): Promise { + return this.measureunitModel.find().exec(); + } + + async findOne(id: string): Promise { + const measureunit = await this.measureunitModel.findOne({ id }).exec(); + if (!measureunit) { + throw new NotFoundException(`Measureunit with ID ${id} not found`); + } + return measureunit; + } + + async update( + id: string, + updateMeasureunitDto: UpdateMeasureunitDto, + ): Promise { + const updatedMeasureunit = await this.measureunitModel + .findOneAndUpdate({ id }, updateMeasureunitDto, { new: true }) + .exec(); + if (!updatedMeasureunit) { + throw new NotFoundException(`Measureunit with ID ${id} not found`); + } + return updatedMeasureunit; + } + + async remove(id: string): Promise { + const deletedMeasureunit = await this.measureunitModel + .findOneAndDelete({ id }) + .exec(); + if (!deletedMeasureunit) { + throw new NotFoundException(`Measureunit with ID ${id} not found`); + } + return deletedMeasureunit; + } +} diff --git a/src/services/recipe/recipes.controller.spec.ts b/src/services/recipe/recipes.controller.spec.ts new file mode 100644 index 0000000..173b996 --- /dev/null +++ b/src/services/recipe/recipes.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { RecipesController } from './recipes.controller'; + +describe('RecipesController', () => { + let controller: RecipesController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [RecipesController], + }).compile(); + + controller = module.get(RecipesController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/services/recipe/recipes.controller.ts b/src/services/recipe/recipes.controller.ts new file mode 100644 index 0000000..7d49473 --- /dev/null +++ b/src/services/recipe/recipes.controller.ts @@ -0,0 +1,89 @@ +import { + Controller, + Get, + Post, + Body, + Param, + Put, + Delete, + HttpStatus, + HttpException, +} from '@nestjs/common'; +import { RecipesService } from './recipes.service'; +import { CreateRecipeDto, UpdateRecipeDto } from 'src/dtos/recipe/recipe.dto'; +import { Recipe } from 'src/schemas/recipe/recipe.schema'; +import { ApiTags } from '@nestjs/swagger'; + +@ApiTags('recipes') +@Controller('recipes') +export class RecipesController { + constructor(private readonly RecipesService: RecipesService) {} + + @Post() + async create(@Body() createRecipeDto: CreateRecipeDto): Promise { + try { + return await this.RecipesService.create(createRecipeDto); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.BAD_REQUEST, + error: 'Error: Recipe not created!', + }, + HttpStatus.BAD_REQUEST, + ); + } + } + + @Get() + async findAll(): Promise { + return this.RecipesService.findAll(); + } + + @Get(':id') + async findOne(@Param('id') id: string): Promise { + try { + return await this.RecipesService.findOne(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Recipe with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Put(':id') + async update( + @Param('id') id: string, + @Body() updateRecipeDto: UpdateRecipeDto, + ): Promise { + try { + return await this.RecipesService.update(id, updateRecipeDto); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Recipe with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } + + @Delete(':id') + async remove(@Param('id') id: string): Promise { + try { + return await this.RecipesService.remove(id); + } catch (err) { + throw new HttpException( + { + status: HttpStatus.NOT_FOUND, + error: `Recipe with ID ${id} not found`, + }, + HttpStatus.NOT_FOUND, + ); + } + } +} diff --git a/src/services/recipe/recipes.service.spec.ts b/src/services/recipe/recipes.service.spec.ts new file mode 100644 index 0000000..7fcefd9 --- /dev/null +++ b/src/services/recipe/recipes.service.spec.ts @@ -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); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/services/recipe/recipes.service.ts b/src/services/recipe/recipes.service.ts new file mode 100644 index 0000000..d78cac4 --- /dev/null +++ b/src/services/recipe/recipes.service.ts @@ -0,0 +1,49 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Recipe, RecipeDocument } from 'src/schemas/recipe/recipe.schema'; +import { CreateRecipeDto, UpdateRecipeDto } from 'src/dtos/recipe/recipe.dto'; + +@Injectable() +export class RecipesService { + constructor( + @InjectModel(Recipe.name) private recipeModel: Model, + ) {} + + async create(createRecipeDto: CreateRecipeDto): Promise { + const createdRecipe = new this.recipeModel(createRecipeDto); + return createdRecipe.save(); + } + + async findAll(): Promise { + return this.recipeModel.find().exec(); + } + + async findOne(id: string): Promise { + const recipe = await this.recipeModel.findOne({ id }).exec(); + if (!recipe) { + throw new NotFoundException(`Recipe with ID ${id} not found`); + } + return recipe; + } + + async update(id: string, updateRecipeDto: UpdateRecipeDto): Promise { + const updatedRecipe = await this.recipeModel + .findOneAndUpdate({ id }, updateRecipeDto, { new: true }) + .exec(); + if (!updatedRecipe) { + throw new NotFoundException(`Recipe with ID ${id} not found`); + } + return updatedRecipe; + } + + async remove(id: string): Promise { + const deletedRecipe = await this.recipeModel + .findOneAndDelete({ id }) + .exec(); + if (!deletedRecipe) { + throw new NotFoundException(`Recipe with ID ${id} not found`); + } + return deletedRecipe; + } +}