1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-07 06:25:21 +02:00

feature: proper multi-tenant-support (#969)(WIP)

* update naming

* refactor tests to use shared structure

* shorten names

* add tools test case

* refactor to support multi-tenant

* set group_id on creation

* initial refactor for multitenant tags/cats

* spelling

* additional test case for same valued resources

* fix recipe update tests

* apply indexes to foreign keys

* fix performance regressions

* handle unknown exception

* utility decorator for function debugging

* migrate recipe_id to UUID

* GUID for recipes

* remove unused import

* move image functions into package

* move utilities to packages dir

* update import

* linter

* image image and asset routes

* update assets and images to use UUIDs

* fix migration base

* image asset test coverage

* use ids for categories and tag crud functions

* refactor recipe organizer test suite to reduce duplication

* add uuid serlization utility

* organizer base router

* slug routes testing and fixes

* fix postgres error

* adopt UUIDs

* move tags, categories, and tools under "organizers" umbrella

* update composite label

* generate ts types

* fix import error

* update frontend types

* fix type errors

* fix postgres errors

* fix #978

* add null check for title validation

* add note in docs on multi-tenancy
This commit is contained in:
Hayden 2022-02-13 12:23:42 -09:00 committed by GitHub
parent 9a82a172cb
commit c617251f4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
157 changed files with 1866 additions and 1578 deletions

View file

@ -9,7 +9,7 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUni
class ShoppingListItemRecipeRef(CamelModel):
recipe_id: int
recipe_id: UUID4
recipe_quantity: float
@ -30,9 +30,9 @@ class ShoppingListItemCreate(CamelModel):
note: Optional[str] = ""
quantity: float = 1
unit_id: int = None
unit_id: UUID4 = None
unit: Optional[IngredientUnit]
food_id: int = None
food_id: UUID4 = None
food: Optional[IngredientFood]
label_id: Optional[UUID4] = None
@ -58,7 +58,7 @@ class ShoppingListCreate(CamelModel):
class ShoppingListRecipeRefOut(CamelModel):
id: UUID4
shopping_list_id: UUID4
recipe_id: int
recipe_id: UUID4
recipe_quantity: float
recipe: RecipeSummary

View file

@ -26,7 +26,7 @@ class CreatePlanEntry(CamelModel):
entry_type: PlanEntryType = PlanEntryType.breakfast
title: str = ""
text: str = ""
recipe_id: Optional[int]
recipe_id: Optional[UUID]
@validator("recipe_id", always=True)
@classmethod

View file

@ -6,7 +6,7 @@ from pydantic import UUID4
class Category(CamelModel):
id: int
id: UUID4
name: str
slug: str

View file

@ -24,7 +24,7 @@ app_dirs = get_app_dirs()
class RecipeTag(CamelModel):
id: int = 0
id: UUID4 = None
name: str
slug: str
@ -37,7 +37,7 @@ class RecipeCategory(RecipeTag):
class RecipeTool(RecipeTag):
id: int = 0
id: UUID4
on_hand: bool = False
@ -63,7 +63,7 @@ class CreateRecipe(CamelModel):
class RecipeSummary(CamelModel):
id: Optional[int]
id: Optional[UUID4]
user_id: UUID4 = Field(default_factory=uuid4)
group_id: UUID4 = Field(default_factory=uuid4)
@ -96,13 +96,13 @@ class RecipeSummary(CamelModel):
@validator("tags", always=True, pre=True, allow_reuse=True)
def validate_tags(cats: list[Any]): # type: ignore
if isinstance(cats, list) and cats and isinstance(cats[0], str):
return [RecipeTag(name=c, slug=slugify(c)) for c in cats]
return [RecipeTag(id=uuid4(), name=c, slug=slugify(c)) for c in cats]
return cats
@validator("recipe_category", always=True, pre=True, allow_reuse=True)
def validate_categories(cats: list[Any]): # type: ignore
if isinstance(cats, list) and cats and isinstance(cats[0], str):
return [RecipeCategory(name=c, slug=slugify(c)) for c in cats]
return [RecipeCategory(id=uuid4(), name=c, slug=slugify(c)) for c in cats]
return cats
@validator("group_id", always=True, pre=True, allow_reuse=True)
@ -132,12 +132,15 @@ class Recipe(RecipeSummary):
comments: Optional[list[RecipeCommentOut]] = []
@staticmethod
def directory_from_slug(slug) -> Path:
return app_dirs.RECIPE_DATA_DIR.joinpath(slug)
def directory_from_id(recipe_id: UUID4 | str) -> Path:
return app_dirs.RECIPE_DATA_DIR.joinpath(str(recipe_id))
@property
def directory(self) -> Path:
dir = app_dirs.RECIPE_DATA_DIR.joinpath(self.slug)
if not self.id:
raise ValueError("Recipe has no ID")
dir = app_dirs.RECIPE_DATA_DIR.joinpath(str(self.id))
dir.mkdir(exist_ok=True, parents=True)
return dir

View file

@ -1,4 +1,5 @@
from fastapi_camelcase import CamelModel
from pydantic import UUID4
from pydantic.utils import GetterDict
@ -6,8 +7,12 @@ class CategoryIn(CamelModel):
name: str
class CategorySave(CategoryIn):
group_id: UUID4
class CategoryBase(CategoryIn):
id: int
id: UUID4
slug: str
class Config:
@ -20,27 +25,45 @@ class CategoryBase(CategoryIn):
}
class RecipeCategoryResponse(CategoryBase):
recipes: "list[Recipe]" = []
class CategoryOut(CategoryBase):
slug: str
class Config:
orm_mode = True
class RecipeCategoryResponse(CategoryBase):
recipes: "list[RecipeSummary]" = []
class Config:
orm_mode = True
schema_extra = {"example": {"id": 1, "name": "dinner", "recipes": [{}]}}
class TagIn(CategoryIn):
pass
class TagSave(TagIn):
group_id: UUID4
class TagBase(CategoryBase):
pass
class TagOut(TagSave):
id: UUID4
slug: str
class Config:
orm_mode = True
class RecipeTagResponse(RecipeCategoryResponse):
pass
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.recipe.recipe import RecipeSummary
RecipeCategoryResponse.update_forward_refs()
RecipeTagResponse.update_forward_refs()

View file

@ -16,7 +16,7 @@ class UserBase(CamelModel):
class RecipeCommentCreate(CamelModel):
recipe_id: int
recipe_id: UUID4
text: str
@ -31,7 +31,7 @@ class RecipeCommentUpdate(CamelModel):
class RecipeCommentOut(RecipeCommentCreate):
id: UUID
recipe_id: int
recipe_id: UUID4
created_at: datetime
update_at: datetime
user_id: UUID4

View file

@ -14,7 +14,7 @@ class UnitFoodBase(CamelModel):
class CreateIngredientFood(UnitFoodBase):
label_id: UUID4 = None
label_id: Optional[UUID4] = None
class SaveIngredientFood(CreateIngredientFood):
@ -22,7 +22,7 @@ class SaveIngredientFood(CreateIngredientFood):
class IngredientFood(CreateIngredientFood):
id: int
id: UUID4
label: MultiPurposeLabelSummary = None
class Config:
@ -39,7 +39,7 @@ class SaveIngredientUnit(CreateIngredientUnit):
class IngredientUnit(CreateIngredientUnit):
id: int
id: UUID4
class Config:
orm_mode = True

View file

@ -11,7 +11,7 @@ def defaut_expires_at_time() -> datetime:
class RecipeShareTokenCreate(CamelModel):
recipe_id: int
recipe_id: UUID4
expires_at: datetime = Field(default_factory=defaut_expires_at_time)

View file

@ -1,6 +1,7 @@
from typing import List
from fastapi_camelcase import CamelModel
from pydantic import UUID4
class RecipeToolCreate(CamelModel):
@ -8,8 +9,12 @@ class RecipeToolCreate(CamelModel):
on_hand: bool = False
class RecipeToolSave(RecipeToolCreate):
group_id: UUID4
class RecipeTool(RecipeToolCreate):
id: int
id: UUID4
slug: str
class Config: