diff --git a/dev/scripts/app_routes_gen.py b/dev/scripts/app_routes_gen.py index c15267d90..aa52c1da9 100644 --- a/dev/scripts/app_routes_gen.py +++ b/dev/scripts/app_routes_gen.py @@ -1,12 +1,10 @@ import json import re from pathlib import Path -from typing import Optional import slugify from jinja2 import Template from mealie.app import app -from pydantic import BaseModel CWD = Path(__file__).parent OUT_FILE = CWD.joinpath("output", "app_routes.py") diff --git a/dev/scripts/output/app_routes.py b/dev/scripts/output/app_routes.py index 6c43040c9..e2c82cb5b 100644 --- a/dev/scripts/output/app_routes.py +++ b/dev/scripts/output/app_routes.py @@ -1,26 +1,34 @@ + class AppRoutes: def __init__(self) -> None: - self.prefix = "/api" + self.prefix = '/api' - self.users_sign_ups = "/api/users/sign-ups" self.auth_token = "/api/auth/token" self.auth_token_long = "/api/auth/token/long" self.auth_refresh = "/api/auth/refresh" + self.users_sign_ups = "/api/users/sign-ups" self.users = "/api/users" self.users_self = "/api/users/self" + self.users_api_tokens = "/api/users-tokens" self.groups = "/api/groups" self.groups_self = "/api/groups/self" - self.recipes = "/api/recipes" + self.recipes_summary = "/api/recipes/summary" + self.recipes_summary_untagged = "/api/recipes/summary/untagged" + self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized" self.recipes_category = "/api/recipes/category" self.recipes_tag = "/api/recipes/tag" - self.categories = "/api/categories" - self.recipes_tags = "/api/recipes/tags/" self.recipes_create = "/api/recipes/create" self.recipes_create_url = "/api/recipes/create-url" + self.categories = "/api/categories" + self.categories_empty = "/api/categories/empty" + self.tags = "/api/tags" + self.tags_empty = "/api/tags/empty" + self.about_events = "/api/about/events" self.meal_plans_all = "/api/meal-plans/all" self.meal_plans_create = "/api/meal-plans/create" self.meal_plans_this_week = "/api/meal-plans/this-week" self.meal_plans_today = "/api/meal-plans/today" + self.meal_plans_today_image = "/api/meal-plans/today/image" self.site_settings_custom_pages = "/api/site-settings/custom-pages" self.site_settings = "/api/site-settings" self.site_settings_webhooks_test = "/api/site-settings/webhooks/test" @@ -30,8 +38,12 @@ class AppRoutes: self.backups_export_database = "/api/backups/export/database" self.backups_upload = "/api/backups/upload" self.migrations = "/api/migrations" + self.debug = "/api/debug" + self.debug_statistics = "/api/debug/statistics" self.debug_version = "/api/debug/version" self.debug_last_recipe_json = "/api/debug/last-recipe-json" + self.debug_log = "/api/debug/log" + self.utils_download = "/api/utils/download" def users_sign_ups_token(self, token): return f"{self.prefix}/users/sign-ups/{token}" @@ -48,21 +60,36 @@ class AppRoutes: def users_id_password(self, id): return f"{self.prefix}/users/{id}/password" + def users_api_tokens_token_id(self, token_id): + return f"{self.prefix}/users-tokens/{token_id}" + def groups_id(self, id): return f"{self.prefix}/groups/{id}" - def categories_category(self, category): - return f"{self.prefix}/categories/{category}" - - def recipes_tags_tag(self, tag): - return f"{self.prefix}/recipes/tags/{tag}" - def recipes_recipe_slug(self, recipe_slug): return f"{self.prefix}/recipes/{recipe_slug}" def recipes_recipe_slug_image(self, recipe_slug): return f"{self.prefix}/recipes/{recipe_slug}/image" + def recipes_recipe_slug_assets(self, recipe_slug): + return f"{self.prefix}/recipes/{recipe_slug}/assets" + + def categories_category(self, category): + return f"{self.prefix}/categories/{category}" + + def tags_tag(self, tag): + return f"{self.prefix}/tags/{tag}" + + def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}" + + def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}" + + def about_events_id(self, id): + return f"{self.prefix}/about/events/{id}" + def meal_plans_plan_id(self, plan_id): return f"{self.prefix}/meal-plans/{plan_id}" @@ -72,8 +99,8 @@ class AppRoutes: def site_settings_custom_pages_id(self, id): return f"{self.prefix}/site-settings/custom-pages/{id}" - def themes_theme_name(self, theme_name): - return f"{self.prefix}/themes/{theme_name}" + def themes_id(self, id): + return f"{self.prefix}/themes/{id}" def backups_file_name_download(self, file_name): return f"{self.prefix}/backups/{file_name}/download" @@ -84,14 +111,14 @@ class AppRoutes: def backups_file_name_delete(self, file_name): return f"{self.prefix}/backups/{file_name}/delete" - def migrations_type_file_name_import(self, type, file_name): - return f"{self.prefix}/migrations/{type}/{file_name}/import" + def migrations_import_type_file_name_import(self, import_type, file_name): + return f"{self.prefix}/migrations/{import_type}/{file_name}/import" - def migrations_type_file_name_delete(self, type, file_name): - return f"{self.prefix}/migrations/{type}/{file_name}/delete" + def migrations_import_type_file_name_delete(self, import_type, file_name): + return f"{self.prefix}/migrations/{import_type}/{file_name}/delete" - def migrations_type_upload(self, type): - return f"{self.prefix}/migrations/{type}/upload" + def migrations_import_type_upload(self, import_type): + return f"{self.prefix}/migrations/{import_type}/upload" def debug_log_num(self, num): return f"{self.prefix}/debug/log/{num}" diff --git a/mealie/db/models/recipe/recipe.py b/mealie/db/models/recipe/recipe.py index 842579b27..10ae5e7b7 100644 --- a/mealie/db/models/recipe/recipe.py +++ b/mealie/db/models/recipe/recipe.py @@ -93,7 +93,6 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): recipe_category: list[str] = None, tags: list[str] = None, date_added: datetime.date = None, - date_updated: datetime.datetime = None, notes: list[dict] = None, rating: int = None, org_url: str = None, diff --git a/mealie/db/models/users.py b/mealie/db/models/users.py index 3945e59bb..ecce12d84 100644 --- a/mealie/db/models/users.py +++ b/mealie/db/models/users.py @@ -40,6 +40,8 @@ class User(SqlAlchemyBase, BaseMixins): group: str = settings.DEFAULT_GROUP, admin=False, id=None, + *args, + **kwargs ) -> None: group = group or settings.DEFAULT_GROUP @@ -49,7 +51,7 @@ class User(SqlAlchemyBase, BaseMixins): self.admin = admin self.password = password - def update(self, full_name, email, group, admin, session=None, id=None, password=None): + def update(self, full_name, email, group, admin, session=None, id=None, password=None, *args, **kwargs): self.full_name = full_name self.email = email self.group = Group.get_ref(session, group) diff --git a/tests/app_routes.py b/tests/app_routes.py index 311017269..40e4b2c16 100644 --- a/tests/app_routes.py +++ b/tests/app_routes.py @@ -2,25 +2,32 @@ class AppRoutes: def __init__(self) -> None: self.prefix = "/api" - self.users_sign_ups = "/api/users/sign-ups" self.auth_token = "/api/auth/token" self.auth_token_long = "/api/auth/token/long" self.auth_refresh = "/api/auth/refresh" + self.users_sign_ups = "/api/users/sign-ups" self.users = "/api/users" self.users_self = "/api/users/self" + self.users_api_tokens = "/api/users-tokens" self.groups = "/api/groups" self.groups_self = "/api/groups/self" - self.recipes = "/api/recipes" + self.recipes_summary = "/api/recipes/summary" + self.recipes_summary_untagged = "/api/recipes/summary/untagged" + self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized" self.recipes_category = "/api/recipes/category" self.recipes_tag = "/api/recipes/tag" - self.categories = "/api/categories" - self.recipes_tags = "/api/recipes/tags/" self.recipes_create = "/api/recipes/create" self.recipes_create_url = "/api/recipes/create-url" + self.categories = "/api/categories" + self.categories_empty = "/api/categories/empty" + self.tags = "/api/tags" + self.tags_empty = "/api/tags/empty" + self.about_events = "/api/about/events" self.meal_plans_all = "/api/meal-plans/all" self.meal_plans_create = "/api/meal-plans/create" self.meal_plans_this_week = "/api/meal-plans/this-week" self.meal_plans_today = "/api/meal-plans/today" + self.meal_plans_today_image = "/api/meal-plans/today/image" self.site_settings_custom_pages = "/api/site-settings/custom-pages" self.site_settings = "/api/site-settings" self.site_settings_webhooks_test = "/api/site-settings/webhooks/test" @@ -30,8 +37,12 @@ class AppRoutes: self.backups_export_database = "/api/backups/export/database" self.backups_upload = "/api/backups/upload" self.migrations = "/api/migrations" + self.debug = "/api/debug" + self.debug_statistics = "/api/debug/statistics" self.debug_version = "/api/debug/version" self.debug_last_recipe_json = "/api/debug/last-recipe-json" + self.debug_log = "/api/debug/log" + self.utils_download = "/api/utils/download" def users_sign_ups_token(self, token): return f"{self.prefix}/users/sign-ups/{token}" @@ -48,21 +59,36 @@ class AppRoutes: def users_id_password(self, id): return f"{self.prefix}/users/{id}/password" + def users_api_tokens_token_id(self, token_id): + return f"{self.prefix}/users-tokens/{token_id}" + def groups_id(self, id): return f"{self.prefix}/groups/{id}" - def categories_category(self, category): - return f"{self.prefix}/categories/{category}" - - def recipes_tags_tag(self, tag): - return f"{self.prefix}/recipes/tags/{tag}" - def recipes_recipe_slug(self, recipe_slug): return f"{self.prefix}/recipes/{recipe_slug}" def recipes_recipe_slug_image(self, recipe_slug): return f"{self.prefix}/recipes/{recipe_slug}/image" + def recipes_recipe_slug_assets(self, recipe_slug): + return f"{self.prefix}/recipes/{recipe_slug}/assets" + + def categories_category(self, category): + return f"{self.prefix}/categories/{category}" + + def tags_tag(self, tag): + return f"{self.prefix}/tags/{tag}" + + def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}" + + def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}" + + def about_events_id(self, id): + return f"{self.prefix}/about/events/{id}" + def meal_plans_plan_id(self, plan_id): return f"{self.prefix}/meal-plans/{plan_id}" @@ -72,8 +98,8 @@ class AppRoutes: def site_settings_custom_pages_id(self, id): return f"{self.prefix}/site-settings/custom-pages/{id}" - def themes_theme_name(self, theme_name): - return f"{self.prefix}/themes/{theme_name}" + def themes_id(self, id): + return f"{self.prefix}/themes/{id}" def backups_file_name_download(self, file_name): return f"{self.prefix}/backups/{file_name}/download" @@ -84,14 +110,14 @@ class AppRoutes: def backups_file_name_delete(self, file_name): return f"{self.prefix}/backups/{file_name}/delete" - def migrations_source_file_name_import(self, source, file_name): - return f"{self.prefix}/migrations/{source}/{file_name}/import" + def migrations_import_type_file_name_import(self, import_type, file_name): + return f"{self.prefix}/migrations/{import_type}/{file_name}/import" - def migrations_source_file_name_delete(self, source, file_name): - return f"{self.prefix}/migrations/{source}/{file_name}/delete" + def migrations_import_type_file_name_delete(self, import_type, file_name): + return f"{self.prefix}/migrations/{import_type}/{file_name}/delete" - def migrations_source_upload(self, source): - return f"{self.prefix}/migrations/{source}/upload" + def migrations_import_type_upload(self, import_type): + return f"{self.prefix}/migrations/{import_type}/upload" def debug_log_num(self, num): return f"{self.prefix}/debug/log/{num}" diff --git a/tests/integration_tests/test_long_live_tokens.py b/tests/integration_tests/test_long_live_tokens.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration_tests/test_migration_routes.py b/tests/integration_tests/test_migration_routes.py index d9c0e84cf..b9c9abecb 100644 --- a/tests/integration_tests/test_migration_routes.py +++ b/tests/integration_tests/test_migration_routes.py @@ -23,7 +23,7 @@ def chowdown_zip(): def test_upload_chowdown_zip(api_client: TestClient, api_routes: AppRoutes, chowdown_zip: Path, token): - upload_url = api_routes.migrations_source_upload("chowdown") + upload_url = api_routes.migrations_import_type_upload("chowdown") response = api_client.post(upload_url, files={"archive": chowdown_zip.open("rb")}, headers=token) assert response.status_code == 200 @@ -36,7 +36,7 @@ def test_import_chowdown_directory(api_client: TestClient, api_routes: AppRoutes api_client.delete(delete_url, headers=token) # TODO: Manage Test Data better selection = chowdown_zip.name - import_url = api_routes.migrations_source_file_name_import("chowdown", selection) + import_url = api_routes.migrations_import_type_file_name_import("chowdown", selection) response = api_client.post(import_url, headers=token) assert response.status_code == 200 @@ -49,7 +49,7 @@ def test_import_chowdown_directory(api_client: TestClient, api_routes: AppRoutes def test_delete_chowdown_migration_data(api_client: TestClient, api_routes: AppRoutes, chowdown_zip: Path, token): selection = chowdown_zip.name - delete_url = api_routes.migrations_source_file_name_delete("chowdown", selection) + delete_url = api_routes.migrations_import_type_file_name_delete("chowdown", selection) response = api_client.delete(delete_url, headers=token) assert response.status_code == 200 @@ -71,7 +71,7 @@ def nextcloud_zip(): def test_upload_nextcloud_zip(api_client: TestClient, api_routes: AppRoutes, nextcloud_zip, token): - upload_url = api_routes.migrations_source_upload("nextcloud") + upload_url = api_routes.migrations_import_type_upload("nextcloud") response = api_client.post(upload_url, files={"archive": nextcloud_zip.open("rb")}, headers=token) assert response.status_code == 200 @@ -81,7 +81,7 @@ def test_upload_nextcloud_zip(api_client: TestClient, api_routes: AppRoutes, nex def test_import_nextcloud_directory(api_client: TestClient, api_routes: AppRoutes, nextcloud_zip, token): selection = nextcloud_zip.name - import_url = api_routes.migrations_source_file_name_import("nextcloud", selection) + import_url = api_routes.migrations_import_type_file_name_import("nextcloud", selection) response = api_client.post(import_url, headers=token) assert response.status_code == 200 @@ -93,7 +93,7 @@ def test_import_nextcloud_directory(api_client: TestClient, api_routes: AppRoute def test_delete__nextcloud_migration_data(api_client: TestClient, api_routes: AppRoutes, nextcloud_zip: Path, token): selection = nextcloud_zip.name - delete_url = api_routes.migrations_source_file_name_delete("nextcloud", selection) + delete_url = api_routes.migrations_import_type_file_name_delete("nextcloud", selection) response = api_client.delete(delete_url, headers=token) assert response.status_code == 200 diff --git a/tests/integration_tests/test_settings_routes.py b/tests/integration_tests/test_settings_routes.py index c6e4bbd62..0b68c5885 100644 --- a/tests/integration_tests/test_settings_routes.py +++ b/tests/integration_tests/test_settings_routes.py @@ -55,7 +55,7 @@ def test_update_settings(api_client: TestClient, api_routes: AppRoutes, default_ def test_default_theme(api_client: TestClient, api_routes: AppRoutes, default_theme): - response = api_client.get(api_routes.themes_theme_name(1)) + response = api_client.get(api_routes.themes_id(1)) assert response.status_code == 200 assert json.loads(response.content) == default_theme @@ -65,7 +65,7 @@ def test_create_theme(api_client: TestClient, api_routes: AppRoutes, new_theme, response = api_client.post(api_routes.themes_create, json=new_theme, headers=token) assert response.status_code == 201 - response = api_client.get(api_routes.themes_theme_name(new_theme.get("id")), headers=token) + response = api_client.get(api_routes.themes_id(new_theme.get("id")), headers=token) assert response.status_code == 200 assert json.loads(response.content) == new_theme @@ -80,7 +80,7 @@ def test_read_all_themes(api_client: TestClient, api_routes: AppRoutes, default_ def test_read_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme): for theme in [default_theme, new_theme]: - response = api_client.get(api_routes.themes_theme_name(theme.get("id"))) + response = api_client.get(api_routes.themes_id(theme.get("id"))) assert response.status_code == 200 assert json.loads(response.content) == theme @@ -97,14 +97,14 @@ def test_update_theme(api_client: TestClient, api_routes: AppRoutes, token, defa } new_theme["colors"] = theme_colors - response = api_client.put(api_routes.themes_theme_name(new_theme.get("id")), json=new_theme, headers=token) + response = api_client.put(api_routes.themes_id(new_theme.get("id")), json=new_theme, headers=token) assert response.status_code == 200 - response = api_client.get(api_routes.themes_theme_name(new_theme.get("id"))) + response = api_client.get(api_routes.themes_id(new_theme.get("id"))) assert json.loads(response.content) == new_theme def test_delete_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme, token): for theme in [default_theme, new_theme]: - response = api_client.delete(api_routes.themes_theme_name(theme.get("id")), headers=token) + response = api_client.delete(api_routes.themes_id(theme.get("id")), headers=token) assert response.status_code == 200 diff --git a/tests/integration_tests/test_user_routes.py b/tests/integration_tests/test_user_routes.py index aebd42332..5d9b96d8e 100644 --- a/tests/integration_tests/test_user_routes.py +++ b/tests/integration_tests/test_user_routes.py @@ -10,12 +10,12 @@ from tests.app_routes import AppRoutes @fixture(scope="session") def default_user(): - return UserOut(id=1, fullName="Change Me", email="changeme@email.com", group="Home", admin=True) + return UserOut(id=1, fullName="Change Me", email="changeme@email.com", group="Home", admin=True, tokens=[]) @fixture(scope="session") def new_user(): - return UserOut(id=3, fullName="My New User", email="newuser@email.com", group="Home", admin=False) + return UserOut(id=3, fullName="My New User", email="newuser@email.com", group="Home", admin=False, tokens=[]) def test_superuser_login(api_client: TestClient, api_routes: AppRoutes, token): @@ -45,6 +45,7 @@ def test_create_user(api_client: TestClient, api_routes: AppRoutes, token, new_u "password": "MyStrongPassword", "group": "Home", "admin": False, + "tokens": [], } response = api_client.post(api_routes.users, json=create_data, headers=token)