diff --git a/.github/workflows/backend-docker-nightly.yml b/.github/workflows/backend-docker-nightly.yml index bca854638728..e40318764294 100644 --- a/.github/workflows/backend-docker-nightly.yml +++ b/.github/workflows/backend-docker-nightly.yml @@ -47,3 +47,12 @@ jobs: docker build --push --no-cache \ --tag hkotel/mealie:api-nightly \ --platform linux/amd64,linux/arm64 . + # + # Build Discord Notification + # + - name: Discord notification + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }} + uses: Ilshidur/action-discord@0.3.2 + with: + args: "🚀 A New Nighlty Build of Mealie is ready" diff --git a/mealie/db/models/_model_utils/auto_init.py b/mealie/db/models/_model_utils/auto_init.py index 2a705384c60f..22e0582a7399 100644 --- a/mealie/db/models/_model_utils/auto_init.py +++ b/mealie/db/models/_model_utils/auto_init.py @@ -1,4 +1,5 @@ from functools import wraps +from uuid import UUID from pydantic import BaseModel, Field from sqlalchemy.orm import MANYTOMANY, MANYTOONE, ONETOMANY, Session @@ -170,9 +171,13 @@ def auto_init(): # sourcery no-metrics if val is None: raise ValueError(f"Expected 'id' to be provided for {key}") - if isinstance(val, (str, int)): + if isinstance(val, (str, int, UUID)): instance = session.query(relation_cls).filter_by(**{get_attr: val}).one_or_none() setattr(self, key, instance) + else: + # If the value is not of the type defined above we assume that it isn't a valid id + # and try a different approach. + pass elif relation_dir == MANYTOMANY: instances = handle_many_to_many(session, get_attr, relation_cls, val) diff --git a/mealie/db/models/recipe/recipe.py b/mealie/db/models/recipe/recipe.py index 6d114618341b..0367748d700b 100644 --- a/mealie/db/models/recipe/recipe.py +++ b/mealie/db/models/recipe/recipe.py @@ -142,7 +142,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): assets: list = None, notes: list[dict] = None, nutrition: dict = None, - recipe_ingredient: list[str] = None, + recipe_ingredient: list[dict] = None, settings: dict = None, **_, ) -> None: diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py index 459aafd6391e..0cc042c59dfc 100644 --- a/mealie/routes/recipe/recipe_crud_routes.py +++ b/mealie/routes/recipe/recipe_crud_routes.py @@ -117,6 +117,25 @@ router = UserAPIRouter(prefix="/recipes", tags=["Recipe: CRUD"]) @controller(router) class RecipeController(BaseRecipeController): + def handle_exceptions(self, ex: Exception) -> None: + match type(ex): + case exceptions.PermissionDenied: + self.deps.logger.error("Permission Denied on recipe controller action") + raise HTTPException(status_code=403, detail=ErrorResponse.respond(message="Permission Denied")) + case exceptions.NoEntryFound: + self.deps.logger.error("No Entry Found on recipe controller action") + raise HTTPException(status_code=404, detail=ErrorResponse.respond(message="No Entry Found")) + case sqlalchemy.exc.IntegrityError: + self.deps.logger.error("SQL Integrity Error on recipe controller action") + raise HTTPException(status_code=400, detail=ErrorResponse.respond(message="Recipe already exists")) + + case _: + self.deps.logger.error("Unknown Error on recipe controller action") + self.deps.logger.exception(ex) + raise HTTPException( + status_code=500, detail=ErrorResponse.respond(message="Unknown Error", exception=str(ex)) + ) + # ======================================================================= # URL Scraping Operations @@ -212,24 +231,6 @@ class RecipeController(BaseRecipeController): except Exception as e: self.handle_exceptions(e) - def handle_exceptions(self, ex: Exception) -> None: - match type(ex): - case exceptions.PermissionDenied: - self.deps.logger.error("Permission Denied on recipe controller action") - raise HTTPException(status_code=403, detail=ErrorResponse.respond(message="Permission Denied")) - case exceptions.NoEntryFound: - self.deps.logger.error("No Entry Found on recipe controller action") - raise HTTPException(status_code=404, detail=ErrorResponse.respond(message="No Entry Found")) - case sqlalchemy.exc.IntegrityError: - self.deps.logger.error("SQL Integrity Error on recipe controller action") - raise HTTPException(status_code=400, detail=ErrorResponse.respond(message="Recipe already exists")) - - case _: - self.deps.logger.error("Unknown Error on recipe controller action") - raise HTTPException( - status_code=500, detail=ErrorResponse.respond(message="Unknown Error", exception=ex) - ) - @router.put("/{slug}") def update_one(self, slug: str, data: Recipe): """Updates a recipe by existing slug and data.""" diff --git a/mealie/services/recipe/recipe_service.py b/mealie/services/recipe/recipe_service.py index 4858f25b9560..33b1a2db2197 100644 --- a/mealie/services/recipe/recipe_service.py +++ b/mealie/services/recipe/recipe_service.py @@ -25,7 +25,7 @@ step_text = """Recipe steps as well as other fields in the recipe page support m [My Link](https://beta.mealie.io) -**Imbed an image** +**Embed an image** Use the `height="100"` or `width="100"` attributes to set the size of the image. @@ -158,6 +158,7 @@ class RecipeService(BaseService): def update_one(self, slug: str, update_data: Recipe) -> Recipe: recipe = self._pre_update_check(slug, update_data) + new_data = self.repos.recipes.update(slug, update_data) self.check_assets(new_data, recipe.slug) return new_data @@ -165,6 +166,7 @@ class RecipeService(BaseService): def patch_one(self, slug: str, patch_data: Recipe) -> Recipe: recipe = self._pre_update_check(slug, patch_data) recipe = self.repos.recipes.by_group(self.group.id).get_one(slug) + new_data = self.repos.recipes.patch(recipe.slug, patch_data) self.check_assets(new_data, recipe.slug)