diff --git a/mealie/repos/repository_group.py b/mealie/repos/repository_group.py index 37115df77e49..a6a24eb2c23d 100644 --- a/mealie/repos/repository_group.py +++ b/mealie/repos/repository_group.py @@ -1,3 +1,5 @@ +from typing import Union + from sqlalchemy.orm.session import Session from mealie.db.models.group import Group @@ -22,3 +24,9 @@ class RepositoryGroup(RepositoryGeneric[GroupInDB, Group]): group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none() return group.mealplans + + def get_by_name(self, name: str, limit=1) -> Union[GroupInDB, Group, None]: + dbgroup = self.session.query(self.sql_model).filter_by(**{"name": name}).one_or_none() + if dbgroup is None: + return None + return self.schema.from_orm(dbgroup) diff --git a/mealie/repos/repository_recipes.py b/mealie/repos/repository_recipes.py index f8913f78baa6..488cb8284efc 100644 --- a/mealie/repos/repository_recipes.py +++ b/mealie/repos/repository_recipes.py @@ -1,5 +1,5 @@ from random import randint -from typing import Any +from typing import Any, Optional from uuid import UUID from sqlalchemy import and_, func @@ -143,3 +143,13 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): .order_by(func.random()) # Postgres and SQLite specific .limit(limit) ] + + def get_by_slug(self, group_id: UUID, slug: str, limit=1) -> Optional[Recipe]: + dbrecipe = ( + self.session.query(RecipeModel) + .filter(RecipeModel.group_id == group_id, RecipeModel.slug == slug) + .one_or_none() + ) + if dbrecipe is None: + return None + return self.schema.from_orm(dbrecipe) diff --git a/mealie/repos/repository_users.py b/mealie/repos/repository_users.py index 78eb1b079a18..7dd5429cf79f 100644 --- a/mealie/repos/repository_users.py +++ b/mealie/repos/repository_users.py @@ -1,5 +1,6 @@ import random import shutil +from typing import Optional from mealie.assets import users as users_assets from mealie.schema.user.user import PrivateUser, User @@ -34,3 +35,9 @@ class RepositoryUsers(RepositoryGeneric[PrivateUser, User]): # Delete the user's directory shutil.rmtree(PrivateUser.get_directory(id)) return entry + + def get_by_username(self, username: str, limit=1) -> Optional[User]: + dbuser = self.session.query(User).filter(User.username == username).one_or_none() + if dbuser is None: + return None + return self.schema.from_orm(dbuser) diff --git a/mealie/routes/__init__.py b/mealie/routes/__init__.py index dd5e3ba5a275..b2b49f1cd675 100644 --- a/mealie/routes/__init__.py +++ b/mealie/routes/__init__.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from . import admin, app, auth, comments, groups, organizers, parser, recipe, shared, unit_and_foods, users +from . import admin, app, auth, comments, groups, organizers, parser, recipe, shared, unit_and_foods, users, validators router = APIRouter(prefix="/api") @@ -15,3 +15,4 @@ router.include_router(comments.router) router.include_router(parser.router) router.include_router(unit_and_foods.router) router.include_router(admin.router) +router.include_router(validators.router) diff --git a/mealie/routes/validators/__init__.py b/mealie/routes/validators/__init__.py new file mode 100644 index 000000000000..a133c15a713f --- /dev/null +++ b/mealie/routes/validators/__init__.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter + +from . import validators + +prefix = "/validators" + +router = APIRouter() + +router.include_router(validators.router, prefix=prefix, tags=["Validators"]) diff --git a/mealie/routes/validators/validators.py b/mealie/routes/validators/validators.py new file mode 100644 index 000000000000..c6a31070c4dd --- /dev/null +++ b/mealie/routes/validators/validators.py @@ -0,0 +1,34 @@ +from uuid import UUID + +from fastapi import APIRouter, Depends +from sqlalchemy.orm.session import Session + +from mealie.db.db_setup import generate_session +from mealie.repos.all_repositories import get_repositories +from mealie.schema.response import ValidationResponse + +router = APIRouter() + + +@router.get("/user/{name}", response_model=ValidationResponse) +def validate_user(name: str, session: Session = Depends(generate_session)): + """Checks if a user with the given name exists""" + db = get_repositories(session) + existing_element = db.users.get_by_username(name) + return ValidationResponse(valid=existing_element is None) + + +@router.get("/group/{name}", response_model=ValidationResponse) +def validate_group(name: str, session: Session = Depends(generate_session)): + """Checks if a group with the given name exists""" + db = get_repositories(session) + existing_element = db.groups.get_by_name(name) + return ValidationResponse(valid=existing_element is None) + + +@router.get("/recipe/{group_id}/{slug}", response_model=ValidationResponse) +def validate_recipe(group_id: UUID, slug: str, session: Session = Depends(generate_session)): + """Checks if a group with the given slug exists""" + db = get_repositories(session) + existing_element = db.recipes.get_by_slug(group_id, slug) + return ValidationResponse(valid=existing_element is None) diff --git a/mealie/schema/response/__init__.py b/mealie/schema/response/__init__.py index 95d4cd1fb2fb..919bdcffef28 100644 --- a/mealie/schema/response/__init__.py +++ b/mealie/schema/response/__init__.py @@ -1,2 +1,3 @@ # GENERATED CODE - DO NOT MODIFY BY HAND from .responses import * +from .validation import * diff --git a/mealie/schema/response/validation.py b/mealie/schema/response/validation.py new file mode 100644 index 000000000000..882725d193d8 --- /dev/null +++ b/mealie/schema/response/validation.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class ValidationResponse(BaseModel): + valid: bool = False diff --git a/tests/fixtures/fixture_admin.py b/tests/fixtures/fixture_admin.py index ef9ee9b02d99..640262dee21d 100644 --- a/tests/fixtures/fixture_admin.py +++ b/tests/fixtures/fixture_admin.py @@ -33,6 +33,7 @@ def admin_user(api_client: TestClient, api_routes: utils.AppRoutes): yield utils.TestUser( _group_id=user_data.get("groupId"), user_id=user_data.get("id"), + username=user_data.get("username"), email=user_data.get("email"), token=token, ) diff --git a/tests/fixtures/fixture_users.py b/tests/fixtures/fixture_users.py index 6a30902abf00..1277ebfe9a5c 100644 --- a/tests/fixtures/fixture_users.py +++ b/tests/fixtures/fixture_users.py @@ -28,6 +28,7 @@ def build_unique_user(group: str, api_client: requests) -> utils.TestUser: _group_id=user_data.get("groupId"), user_id=user_data.get("id"), email=user_data.get("email"), + username=user_data.get("username"), token=token, ) @@ -69,6 +70,7 @@ def g2_user(admin_token, api_client: TestClient, api_routes: utils.AppRoutes): _group_id=group_id, token=token, email=create_data["email"], + username=create_data.get("username"), ) finally: # TODO: Delete User after test @@ -93,6 +95,7 @@ def unique_user(api_client: TestClient, api_routes: utils.AppRoutes): _group_id=user_data.get("groupId"), user_id=user_data.get("id"), email=user_data.get("email"), + username=user_data.get("username"), token=token, ) finally: @@ -142,6 +145,7 @@ def user_tuple(admin_token, api_client: requests, api_routes: utils.AppRoutes) - utils.TestUser( _group_id=user_data.get("groupId"), user_id=user_data.get("id"), + username=user_data.get("username"), email=user_data.get("email"), token=token, ) diff --git a/tests/integration_tests/test_validators.py b/tests/integration_tests/test_validators.py new file mode 100644 index 000000000000..f5c708e30ee0 --- /dev/null +++ b/tests/integration_tests/test_validators.py @@ -0,0 +1,46 @@ +from fastapi.testclient import TestClient + +from mealie.db.db_setup import create_session +from mealie.schema.recipe.recipe import Recipe +from tests.utils.fixture_schemas import TestUser + + +class Routes: + user = "/api/validators/user" + recipe = "/api/validators/recipe" + + +def test_validators_user(api_client: TestClient, unique_user: TestUser): + session = create_session() + + # Test existing user + response = api_client.get(Routes.user + f"/{unique_user.username}") + assert response.status_code == 200 + response_data = response.json() + assert not response_data["valid"] + + # Test non-existing user + response = api_client.get(Routes.user + f"/{unique_user.username}2") + assert response.status_code == 200 + response_data = response.json() + assert response_data["valid"] + + session.close() + + +def test_validators_recipe(api_client: TestClient, random_recipe: Recipe): + session = create_session() + + # Test existing user + response = api_client.get(Routes.recipe + f"/{random_recipe.group_id}/{random_recipe.slug}") + assert response.status_code == 200 + response_data = response.json() + assert not response_data["valid"] + + # Test non-existing user + response = api_client.get(Routes.recipe + f"/{random_recipe.group_id}/{random_recipe.slug}-test") + assert response.status_code == 200 + response_data = response.json() + assert response_data["valid"] + + session.close() diff --git a/tests/utils/fixture_schemas.py b/tests/utils/fixture_schemas.py index 1e84934b635d..c00023d83989 100644 --- a/tests/utils/fixture_schemas.py +++ b/tests/utils/fixture_schemas.py @@ -7,6 +7,7 @@ from uuid import UUID class TestUser: email: str user_id: UUID + username: str _group_id: UUID token: Any