diff --git a/dev/scripts/gen_error_messages.py b/dev/scripts/gen_error_messages.py new file mode 100644 index 000000000000..58c939e91e76 --- /dev/null +++ b/dev/scripts/gen_error_messages.py @@ -0,0 +1,159 @@ +import json +import re +from dataclasses import dataclass +from pathlib import Path + +from slugify import slugify + +CWD = Path(__file__).parent + +PROJECT_BASE = CWD.parent.parent + +server_side_msgs = PROJECT_BASE / "mealie" / "utils" / "error_messages.py" +en_us_msgs = PROJECT_BASE / "frontend" / "lang" / "errors" / "en-US.json" +client_side_msgs = PROJECT_BASE / "frontend" / "utils" / "error-messages.ts" + +GENERATE_MESSAGES = [ + # User Related + "user", + "webhook", + "token", + # Group Related + "group", + "cookbook", + "mealplan", + # Recipe Related + "scraper", + "recipe", + "ingredient", + "food", + "unit", + # Admin Related + "backup", + "migration", + "event", +] + + +class ErrorMessage: + def __init__(self, prefix, verb) -> None: + self.message = f"{prefix.title()} {verb.title()} Failed" + self.snake = slugify(self.message, separator="_") + self.kabab = slugify(self.message, separator="-") + + def factory(prefix) -> list["ErrorMessage"]: + verbs = ["Create", "Update", "Delete"] + return [ErrorMessage(prefix, verb) for verb in verbs] + + +@dataclass +class CodeGenLines: + start: int + end: int + + indentation: str + text: list[str] + + _next_line = None + + def purge_lines(self) -> None: + start = self.start + 1 + end = self.end + del self.text[start:end] + + def push_line(self, string: str) -> None: + self._next_line = self._next_line or self.start + 1 + self.text.insert(self._next_line, self.indentation + string) + self._next_line += 1 + + +def find_start(file_text: list[str], gen_id: str): + for x, line in enumerate(file_text): + if "CODE_GEN_ID:" in line and gen_id in line: + return x, line + + return None + + +def find_end(file_text: list[str], gen_id: str): + for x, line in enumerate(file_text): + if f"END {gen_id}" in line: + return x, line + return None + + +def get_indentation_of_string(line: str): + return re.sub(r"#.*", "", line).removesuffix("\n") + + +def get_messages(message_prefix: str) -> str: + prefix = message_prefix.lower() + + return [ + f'{prefix}_create_failure = "{prefix}-create-failure"\n', + f'{prefix}_update_failure = "{prefix}-update-failure"\n', + f'{prefix}_delete_failure = "{prefix}-delete-failure"\n', + ] + + +def code_gen_factory(file_path: Path) -> CodeGenLines: + with open(file_path, "r") as file: + text = file.readlines() + start_num, line = find_start(text, "ERROR_MESSAGE_ENUMS") + indentation = get_indentation_of_string(line) + end_num, line = find_end(text, "ERROR_MESSAGE_ENUMS") + + return CodeGenLines( + start=start_num, + end=end_num, + indentation=indentation, + text=text, + ) + + +def write_to_locals(messages: list[ErrorMessage]) -> None: + with open(en_us_msgs, "r") as f: + existing_msg = json.loads(f.read()) + + for msg in messages: + if msg.kabab in existing_msg: + continue + + existing_msg[msg.kabab] = msg.message + print(f"Added Key {msg.kabab} to 'en-US.json'") + + with open(en_us_msgs, "w") as f: + f.write(json.dumps(existing_msg, indent=4)) + + +def main(): + print("Starting...") + GENERATE_MESSAGES.sort() + + code_gen = code_gen_factory(server_side_msgs) + code_gen.purge_lines() + + messages = [] + for msg_type in GENERATE_MESSAGES: + messages += get_messages(msg_type) + messages.append("\n") + + for msg in messages: + code_gen.push_line(msg) + + with open(server_side_msgs, "w") as file: + file.writelines(code_gen.text) + + # Locals + + local_msgs = [] + for msg_type in GENERATE_MESSAGES: + local_msgs += ErrorMessage.factory(msg_type) + + write_to_locals(local_msgs) + + print("Done!") + + +if __name__ == "__main__": + main() diff --git a/dev/scripts/openapi.json b/dev/scripts/openapi.json deleted file mode 100644 index 2787ed7bc665..000000000000 --- a/dev/scripts/openapi.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi": "3.0.2", "info": {"title": "Mealie", "description": "A place for all your recipes", "version": "v0.5.2"}, "paths": {"/api/auth/token": {"post": {"tags": ["Authentication"], "summary": "Get Token", "operationId": "get_token_api_auth_token_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_get_token_api_auth_token_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/auth/token/long": {"post": {"tags": ["Authentication"], "summary": "Get Token", "operationId": "get_token_api_auth_token_long_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_get_token_api_auth_token_long_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/auth/refresh": {"get": {"tags": ["Authentication"], "summary": "Refresh Token", "description": "Use a valid token to get another token", "operationId": "refresh_token_api_auth_refresh_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/sign-ups/{token}": {"post": {"tags": ["User Signup"], "summary": "Create User With Token", "description": "Creates a user with a valid sign up token ", "operationId": "create_user_with_token_api_users_sign_ups__token__post", "parameters": [{"required": true, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["User Signup"], "summary": "Delete Token", "description": "Removed a token from the database ", "operationId": "delete_token_api_users_sign_ups__token__delete", "parameters": [{"required": true, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/sign-ups": {"get": {"tags": ["User Signup"], "summary": "Get All Open Sign Ups", "description": "Returns a list of open sign up links ", "operationId": "get_all_open_sign_ups_api_users_sign_ups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Open Sign Ups Api Users Sign Ups Get", "type": "array", "items": {"$ref": "#/components/schemas/SignUpOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["User Signup"], "summary": "Create User Sign Up Key", "description": "Generates a Random Token that a new user can sign up with ", "operationId": "create_user_sign_up_key_api_users_sign_ups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignUpIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignUpToken"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/image": {"get": {"tags": ["Users"], "summary": "Get User Image", "description": "Returns a users profile picture ", "operationId": "get_user_image_api_users__id__image_get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Users"], "summary": "Update User Image", "description": "Updates a User Image ", "operationId": "update_user_image_api_users__id__image_post", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_update_user_image_api_users__id__image_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/self": {"get": {"tags": ["Users"], "summary": "Get Logged In User", "operationId": "get_logged_in_user_api_users_self_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/reset-password": {"put": {"tags": ["Users"], "summary": "Reset User Password", "operationId": "reset_user_password_api_users__id__reset_password_put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}": {"get": {"tags": ["Users"], "summary": "Get User By Id", "operationId": "get_user_by_id_api_users__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Users"], "summary": "Update User", "operationId": "update_user_api_users__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/mealie__schema__user__user__UserBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Users"], "summary": "Delete User", "description": "Removes a user from the database. Must be the current user or a super user", "operationId": "delete_user_api_users__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/password": {"put": {"tags": ["Users"], "summary": "Update Password", "description": "Resets the User Password", "operationId": "update_password_api_users__id__password_put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChangePassword"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/favorites": {"get": {"tags": ["Users"], "summary": "Get Favorites", "description": "Get user's favorite recipes ", "operationId": "get_favorites_api_users__id__favorites_get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserFavorites"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/favorites/{slug}": {"post": {"tags": ["Users"], "summary": "Add Favorite", "description": "Adds a Recipe to the users favorites ", "operationId": "add_favorite_api_users__id__favorites__slug__post", "parameters": [{"required": true, "schema": {"title": "Slug", "type": "string"}, "name": "slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Users"], "summary": "Remove Favorite", "description": "Adds a Recipe to the users favorites ", "operationId": "remove_favorite_api_users__id__favorites__slug__delete", "parameters": [{"required": true, "schema": {"title": "Slug", "type": "string"}, "name": "slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users": {"get": {"tags": ["Users"], "summary": "Get All Users", "operationId": "get_all_users_api_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Users Api Users Get", "type": "array", "items": {"$ref": "#/components/schemas/UserOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users"], "summary": "Create User", "operationId": "create_user_api_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/api-tokens": {"post": {"tags": ["User API Tokens"], "summary": "Create Api Token", "description": "Create api_token in the Database ", "operationId": "create_api_token_api_users_api_tokens_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LoingLiveTokenIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/api-tokens/{token_id}": {"delete": {"tags": ["User API Tokens"], "summary": "Delete Api Token", "description": "Delete api_token from the Database ", "operationId": "delete_api_token_api_users_api_tokens__token_id__delete", "parameters": [{"required": true, "schema": {"title": "Token Id", "type": "integer"}, "name": "token_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups": {"get": {"tags": ["Groups administration"], "summary": "Get All Groups", "description": "Returns a list of all groups in the database ", "operationId": "get_all_groups_api_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Groups Api Groups Get", "type": "array", "items": {"$ref": "#/components/schemas/GroupInDB"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Groups administration"], "summary": "Create Group", "description": "Creates a Group in the Database ", "operationId": "create_group_api_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/GroupBase"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GroupInDB"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups/{id}": {"put": {"tags": ["Groups administration"], "summary": "Update Group Data", "description": "Updates a User Group ", "operationId": "update_group_data_api_groups__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateGroup"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Groups administration"], "summary": "Delete User Group", "description": "Removes a user group from the database ", "operationId": "delete_user_group_api_groups__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups/self": {"get": {"tags": ["Groups"], "summary": "Get Current User Group", "description": "Returns the Group Data for the Current User ", "operationId": "get_current_user_group_api_groups_self_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GroupInDB"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/shopping-lists": {"post": {"tags": ["Shopping Lists"], "summary": "Create Shopping List", "description": "Create Shopping List in the Database ", "operationId": "create_shopping_list_api_shopping_lists_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/shopping-lists/{id}": {"get": {"tags": ["Shopping Lists"], "summary": "Get Shopping List", "description": "Get Shopping List from the Database ", "operationId": "get_shopping_list_api_shopping_lists__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Shopping Lists"], "summary": "Update Shopping List", "description": "Update Shopping List in the Database ", "operationId": "update_shopping_list_api_shopping_lists__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Shopping Lists"], "summary": "Delete Shopping List", "description": "Delete Shopping List from the Database ", "operationId": "delete_shopping_list_api_shopping_lists__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes": {"get": {"tags": ["Query All Recipes"], "summary": "Get Recipe Summary", "description": "Returns key the recipe summary data for recipes in the database. You can perform\nslice operations to set the skip/end amounts for recipes. All recipes are sorted by the added date.\n\n**Query Parameters**\n- skip: The database entry to start at. (0 Indexed)\n- end: The number of entries to return.\n\nskip=2, end=10 will return entries", "operationId": "get_recipe_summary_api_recipes_get", "parameters": [{"required": false, "schema": {"title": "Start", "default": 0}, "name": "start", "in": "query"}, {"required": false, "schema": {"title": "Limit", "default": 9999}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Recipe CRUD"], "summary": "Create From Name", "description": "Takes in a JSON string and loads data into the database as a new entry", "operationId": "create_from_name_api_recipes_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateRecipe"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Create From Name Api Recipes Post", "type": "string"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/summary/untagged": {"get": {"tags": ["Query All Recipes"], "summary": "Get Untagged Recipes", "operationId": "get_untagged_recipes_api_recipes_summary_untagged_get", "parameters": [{"required": false, "schema": {"title": "Count", "type": "boolean", "default": false}, "name": "count", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get Untagged Recipes Api Recipes Summary Untagged Get", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/summary/uncategorized": {"get": {"tags": ["Query All Recipes"], "summary": "Get Uncategorized Recipes", "operationId": "get_uncategorized_recipes_api_recipes_summary_uncategorized_get", "parameters": [{"required": false, "schema": {"title": "Count", "type": "boolean", "default": false}, "name": "count", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get Uncategorized Recipes Api Recipes Summary Uncategorized Get", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/category": {"post": {"tags": ["Query All Recipes"], "summary": "Filter By Category", "description": "pass a list of categories and get a list of recipes associated with those categories ", "operationId": "filter_by_category_api_recipes_category_post", "requestBody": {"content": {"application/json": {"schema": {"title": "Categories", "type": "array", "items": {}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true}}, "/api/recipes/tag": {"post": {"tags": ["Query All Recipes"], "summary": "Filter By Tags", "description": "pass a list of tags and get a list of recipes associated with those tags", "operationId": "filter_by_tags_api_recipes_tag_post", "requestBody": {"content": {"application/json": {"schema": {"title": "Tags", "type": "array", "items": {}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "deprecated": true}}, "/api/recipes/{recipe_slug}": {"get": {"tags": ["Recipe CRUD"], "summary": "Get Recipe", "description": "Takes in a recipe slug, returns all data for a recipe ", "operationId": "get_recipe_api_recipes__recipe_slug__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Recipe CRUD"], "summary": "Update Recipe", "description": "Updates a recipe by existing slug and data. ", "operationId": "update_recipe_api_recipes__recipe_slug__put", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe CRUD"], "summary": "Delete Recipe", "description": "Deletes a recipe by slug ", "operationId": "delete_recipe_api_recipes__recipe_slug__delete", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "patch": {"tags": ["Recipe CRUD"], "summary": "Patch Recipe", "description": "Updates a recipe by existing slug and data. ", "operationId": "patch_recipe_api_recipes__recipe_slug__patch", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}/zip": {"get": {"tags": ["Recipe CRUD"], "summary": "Get Recipe As Zip", "description": "Get a Recipe and It's Original Image as a Zip File ", "operationId": "get_recipe_as_zip_api_recipes__recipe_slug__zip_get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/create": {"post": {"tags": ["Recipe CRUD"], "summary": "Create From Json", "description": "Takes in a JSON string and loads data into the database as a new entry", "operationId": "create_from_json_api_recipes_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Create From Json Api Recipes Create Post", "type": "string"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/test-scrape-url": {"post": {"tags": ["Recipe CRUD"], "summary": "Test Parse Recipe Url", "operationId": "test_parse_recipe_url_api_recipes_test_scrape_url_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeURLIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/create-url": {"post": {"tags": ["Recipe CRUD"], "summary": "Parse Recipe Url", "description": "Takes in a URL and attempts to scrape data and load it into the database ", "operationId": "parse_recipe_url_api_recipes_create_url_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeURLIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Parse Recipe Url Api Recipes Create Url Post", "type": "string"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/create-from-zip": {"post": {"tags": ["Recipe CRUD"], "summary": "Create Recipe From Zip", "description": "Create recipe from archive ", "operationId": "create_recipe_from_zip_api_recipes_create_from_zip_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_create_recipe_from_zip_api_recipes_create_from_zip_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}/image": {"put": {"tags": ["Recipe CRUD"], "summary": "Update Recipe Image", "description": "Removes an existing image and replaces it with the incoming file. ", "operationId": "update_recipe_image_api_recipes__recipe_slug__image_put", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_update_recipe_image_api_recipes__recipe_slug__image_put"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Recipe CRUD"], "summary": "Scrape Image Url", "description": "Removes an existing image and replaces it with the incoming file. ", "operationId": "scrape_image_url_api_recipes__recipe_slug__image_post", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeURLIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}/assets": {"post": {"tags": ["Recipe CRUD"], "summary": "Upload Recipe Asset", "description": "Upload a file to store as a recipe asset ", "operationId": "upload_recipe_asset_api_recipes__recipe_slug__assets_post", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeAsset"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/categories": {"get": {"tags": ["Recipe Categories"], "summary": "Get All Recipe Categories", "description": "Returns a list of available categories in the database ", "operationId": "get_all_recipe_categories_api_categories_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "post": {"tags": ["Recipe Categories"], "summary": "Create Recipe Category", "description": "Creates a Category in the database ", "operationId": "create_recipe_category_api_categories_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategoryIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/categories/empty": {"get": {"tags": ["Recipe Categories"], "summary": "Get Empty Categories", "description": "Returns a list of categories that do not contain any recipes", "operationId": "get_empty_categories_api_categories_empty_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/categories/{category}": {"get": {"tags": ["Recipe Categories"], "summary": "Get All Recipes By Category", "description": "Returns a list of recipes associated with the provided category. ", "operationId": "get_all_recipes_by_category_api_categories__category__get", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeCategoryResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Recipe Categories"], "summary": "Update Recipe Category", "description": "Updates an existing Tag in the database ", "operationId": "update_recipe_category_api_categories__category__put", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategoryIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeCategoryResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe Categories"], "summary": "Delete Recipe Category", "description": "Removes a recipe category from the database. Deleting a\ncategory does not impact a recipe. The category will be removed\nfrom any recipes that contain it", "operationId": "delete_recipe_category_api_categories__category__delete", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/tags/{tag}": {"get": {"tags": ["Recipe Tags"], "summary": "Get All Recipes By Tag", "description": "Returns a list of recipes associated with the provided tag. ", "operationId": "get_all_recipes_by_tag_api_tags__tag__get", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeTagResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Recipe Tags"], "summary": "Update Recipe Tag", "description": "Updates an existing Tag in the database ", "operationId": "update_recipe_tag_api_tags__tag__put", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TagIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeTagResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe Tags"], "summary": "Delete Recipe Tag", "description": "Removes a recipe tag from the database. Deleting a\ntag does not impact a recipe. The tag will be removed\nfrom any recipes that contain it", "operationId": "delete_recipe_tag_api_tags__tag__delete", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/tags": {"get": {"tags": ["Recipe Tags"], "summary": "Get All Recipe Tags", "description": "Returns a list of available tags in the database ", "operationId": "get_all_recipe_tags_api_tags_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "post": {"tags": ["Recipe Tags"], "summary": "Create Recipe Tag", "description": "Creates a Tag in the database ", "operationId": "create_recipe_tag_api_tags_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TagIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/tags/empty": {"get": {"tags": ["Recipe Tags"], "summary": "Get Empty Tags", "description": "Returns a list of tags that do not contain any recipes", "operationId": "get_empty_tags_api_tags_empty_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/recipes/{slug}/comments": {"post": {"tags": ["Recipe Comments"], "summary": "Create Comment", "description": "Create comment in the Database ", "operationId": "create_comment_api_recipes__slug__comments_post", "parameters": [{"required": true, "schema": {"title": "Slug", "type": "string"}, "name": "slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CommentIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{slug}/comments/{id}": {"put": {"tags": ["Recipe Comments"], "summary": "Update Comment", "description": "Update comment in the Database ", "operationId": "update_comment_api_recipes__slug__comments__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CommentIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe Comments"], "summary": "Delete Comment", "description": "Delete comment from the Database ", "operationId": "delete_comment_api_recipes__slug__comments__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/media/recipes/{recipe_slug}/images/{file_name}": {"get": {"tags": ["Site Media"], "summary": "Get Recipe Img", "description": "Takes in a recipe slug, returns the static image. This route is proxied in the docker image\nand should not hit the API in production", "operationId": "get_recipe_img_api_media_recipes__recipe_slug__images__file_name__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}, {"required": true, "schema": {"$ref": "#/components/schemas/ImageType"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/media/recipes/{recipe_slug}/assets/{file_name}": {"get": {"tags": ["Site Media"], "summary": "Get Recipe Asset", "description": "Returns a recipe asset ", "operationId": "get_recipe_asset_api_media_recipes__recipe_slug__assets__file_name__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/about/events": {"get": {"tags": ["App Events"], "summary": "Get Events", "description": "Get event from the Database ", "operationId": "get_events_api_about_events_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EventsOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["App Events"], "summary": "Delete Events", "description": "Get event from the Database ", "operationId": "delete_events_api_about_events_delete", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/{id}": {"delete": {"tags": ["App Events"], "summary": "Delete Event", "description": "Delete event from the Database ", "operationId": "delete_event_api_about_events__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications": {"get": {"tags": ["App Events"], "summary": "Get All Event Notification", "description": "Get all event_notification from the Database ", "operationId": "get_all_event_notification_api_about_events_notifications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Event Notification Api About Events Notifications Get", "type": "array", "items": {"$ref": "#/components/schemas/EventNotificationOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["App Events"], "summary": "Create Event Notification", "description": "Create event_notification in the Database ", "operationId": "create_event_notification_api_about_events_notifications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EventNotificationIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications/test": {"post": {"tags": ["App Events"], "summary": "Test Notification Route", "description": "Create event_notification in the Database ", "operationId": "test_notification_route_api_about_events_notifications_test_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TestEvent"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications/{id}": {"put": {"tags": ["App Events"], "summary": "Update Event Notification", "description": "Update event_notification in the Database ", "operationId": "update_event_notification_api_about_events_notifications__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["App Events"], "summary": "Delete Event Notification", "description": "Delete event_notification from the Database ", "operationId": "delete_event_notification_api_about_events_notifications__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/recipes/defaults": {"get": {"tags": ["About Recipes"], "summary": "Get Recipe Settings Defaults", "description": "Returns the Default Settings for Recieps as set by ENV variables ", "operationId": "get_recipe_settings_defaults_api_about_recipes_defaults_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/meal-plans/all": {"get": {"tags": ["Meal Plan"], "summary": "Get All Meals", "description": "Returns a list of all available Meal Plan ", "operationId": "get_all_meals_api_meal_plans_all_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Meals Api Meal Plans All Get", "type": "array", "items": {"$ref": "#/components/schemas/MealPlanOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/this-week": {"get": {"tags": ["Meal Plan"], "summary": "Get This Week", "description": "Returns the meal plan data for this week ", "operationId": "get_this_week_api_meal_plans_this_week_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/today": {"get": {"tags": ["Meal Plan", "Meal Plan"], "summary": "Get Today", "description": "Returns the recipe slug for the meal scheduled for today.\nIf no meal is scheduled nothing is returned", "operationId": "get_today_api_meal_plans_today_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/{id}": {"get": {"tags": ["Meal Plan"], "summary": "Get Meal Plan", "description": "Returns a single Meal Plan from the Database ", "operationId": "get_meal_plan_api_meal_plans__id__get", "parameters": [{"required": true, "schema": {"title": "Id"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/create": {"post": {"tags": ["Meal Plan"], "summary": "Create Meal Plan", "description": "Creates a meal plan database entry ", "operationId": "create_meal_plan_api_meal_plans_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/{plan_id}": {"put": {"tags": ["Meal Plan"], "summary": "Update Meal Plan", "description": "Updates a meal plan based off ID ", "operationId": "update_meal_plan_api_meal_plans__plan_id__put", "parameters": [{"required": true, "schema": {"title": "Plan Id", "type": "string"}, "name": "plan_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Meal Plan"], "summary": "Delete Meal Plan", "description": "Removes a meal plan from the database ", "operationId": "delete_meal_plan_api_meal_plans__plan_id__delete", "parameters": [{"required": true, "schema": {"title": "Plan Id"}, "name": "plan_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/today/image": {"get": {"tags": ["Meal Plan", "Meal Plan"], "summary": "Get Todays Image", "description": "Returns the image for todays meal-plan.", "operationId": "get_todays_image_api_meal_plans_today_image_get", "parameters": [{"required": false, "schema": {"title": "Group Name", "type": "string", "default": "Home"}, "name": "group_name", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/meal-plans/{id}/shopping-list": {"get": {"tags": ["Meal Plan"], "summary": "Get Shopping List", "operationId": "get_shopping_list_api_meal_plans__id__shopping_list_get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/custom-pages": {"get": {"tags": ["Settings"], "summary": "Get Custom Pages", "description": "Returns the sites custom pages ", "operationId": "get_custom_pages_api_site_settings_custom_pages_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Multiple Pages", "description": "Update multiple custom pages ", "operationId": "update_multiple_pages_api_site_settings_custom_pages_put", "requestBody": {"content": {"application/json": {"schema": {"title": "Pages", "type": "array", "items": {"$ref": "#/components/schemas/CustomPageOut"}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Settings"], "summary": "Create New Page", "description": "Creates a new Custom Page ", "operationId": "create_new_page_api_site_settings_custom_pages_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomPageBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/custom-pages/{id}": {"get": {"tags": ["Settings"], "summary": "Get Single Page", "description": "Removes a custom page from the database ", "operationId": "get_single_page_api_site_settings_custom_pages__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "anyOf": [{"type": "integer"}, {"type": "string"}]}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Single Page", "description": "Removes a custom page from the database ", "operationId": "update_single_page_api_site_settings_custom_pages__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomPageOut"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Settings"], "summary": "Delete Custom Page", "description": "Removes a custom page from the database ", "operationId": "delete_custom_page_api_site_settings_custom_pages__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings": {"get": {"tags": ["Settings"], "summary": "Get Main Settings", "description": "Returns basic site settings ", "operationId": "get_main_settings_api_site_settings_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Settings", "description": "Returns Site Settings ", "operationId": "update_settings_api_site_settings_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/webhooks/test": {"post": {"tags": ["Settings"], "summary": "Test Webhooks", "description": "Run the function to test your webhooks ", "operationId": "test_webhooks_api_site_settings_webhooks_test_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/themes": {"get": {"tags": ["Themes"], "summary": "Get All Themes", "description": "Returns all site themes ", "operationId": "get_all_themes_api_themes_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/themes/{id}": {"get": {"tags": ["Themes"], "summary": "Get Single Theme", "description": "Returns a named theme ", "operationId": "get_single_theme_api_themes__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Themes"], "summary": "Update Theme", "description": "Update a theme database entry ", "operationId": "update_theme_api_themes__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteTheme"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Themes"], "summary": "Delete Theme", "description": "Deletes theme from the database ", "operationId": "delete_theme_api_themes__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/themes/create": {"post": {"tags": ["Themes"], "summary": "Create Theme", "description": "Creates a site color theme database entry ", "operationId": "create_theme_api_themes_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteTheme"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/available": {"get": {"tags": ["Backups"], "summary": "Available Imports", "description": "Returns a list of avaiable .zip files for import into Mealie.", "operationId": "available_imports_api_backups_available_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Imports"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/export/database": {"post": {"tags": ["Backups"], "summary": "Export Database", "description": "Generates a backup of the recipe database in json format.", "operationId": "export_database_api_backups_export_database_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/BackupJob"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/upload": {"post": {"tags": ["Backups"], "summary": "Upload Backup File", "description": "Upload a .zip File to later be imported into Mealie ", "operationId": "upload_backup_file_api_backups_upload_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_backup_file_api_backups_upload_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/download": {"get": {"tags": ["Backups"], "summary": "Download Backup File", "description": "Returns a token to download a file ", "operationId": "download_backup_file_api_backups__file_name__download_get", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/import": {"post": {"tags": ["Backups"], "summary": "Import Database", "description": "Import a database backup file generated from Mealie. ", "operationId": "import_database_api_backups__file_name__import_post", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ImportJob"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/delete": {"delete": {"tags": ["Backups"], "summary": "Delete Backup", "description": "Removes a database backup from the file system ", "operationId": "delete_backup_api_backups__file_name__delete_delete", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations": {"get": {"tags": ["Migration"], "summary": "Get All Migration Options", "description": "Returns a list of avaiable directories that can be imported into Mealie ", "operationId": "get_all_migration_options_api_migrations_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Migration Options Api Migrations Get", "type": "array", "items": {"$ref": "#/components/schemas/Migrations"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/{file_name}/import": {"post": {"tags": ["Migration"], "summary": "Import Migration", "description": "Imports all the recipes in a given directory ", "operationId": "import_migration_api_migrations__import_type___file_name__import_post", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/{file_name}/delete": {"delete": {"tags": ["Migration"], "summary": "Delete Migration Data", "description": "Removes migration data from the file system ", "operationId": "delete_migration_data_api_migrations__import_type___file_name__delete_delete", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/upload": {"post": {"tags": ["Migration"], "summary": "Upload Nextcloud Zipfile", "description": "Upload a .zip File to later be imported into Mealie ", "operationId": "upload_nextcloud_zipfile_api_migrations__import_type__upload_post", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/version": {"get": {"tags": ["Debug"], "summary": "Get Mealie Version", "description": "Returns the current version of mealie", "operationId": "get_mealie_version_api_debug_version_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/debug": {"get": {"tags": ["Debug"], "summary": "Get Debug Info", "description": "Returns general information about the application for debugging ", "operationId": "get_debug_info_api_debug_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/statistics": {"get": {"tags": ["Debug"], "summary": "Get App Statistics", "operationId": "get_app_statistics_api_debug_statistics_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/last-recipe-json": {"get": {"tags": ["Debug"], "summary": "Get Last Recipe Json", "description": "Returns a token to download a file ", "operationId": "get_last_recipe_json_api_debug_last_recipe_json_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/log/{num}": {"get": {"tags": ["Debug"], "summary": "Get Log", "description": "Doc Str ", "operationId": "get_log_api_debug_log__num__get", "parameters": [{"required": true, "schema": {"title": "Num", "type": "integer"}, "name": "num", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/log": {"get": {"tags": ["Debug"], "summary": "Get Log File", "description": "Returns a token to download a file ", "operationId": "get_log_file_api_debug_log_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/utils/download": {"get": {"tags": ["Utils"], "summary": "Download File", "description": "Uses a file token obtained by an active user to retrieve a file from the operating\nsystem.", "operationId": "download_file_api_utils_download_get", "parameters": [{"required": false, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"BackupJob": {"title": "BackupJob", "required": ["options"], "type": "object", "properties": {"tag": {"title": "Tag", "type": "string"}, "options": {"$ref": "#/components/schemas/BackupOptions"}, "templates": {"title": "Templates", "type": "array", "items": {"type": "string"}}}, "example": {"tag": "July 23rd 2021", "options": {"recipes": true, "settings": true, "pages": true, "themes": true, "groups": true, "users": true, "notifications": true}, "template": ["recipes.md"]}}, "BackupOptions": {"title": "BackupOptions", "type": "object", "properties": {"recipes": {"title": "Recipes", "type": "boolean", "default": true}, "settings": {"title": "Settings", "type": "boolean", "default": true}, "pages": {"title": "Pages", "type": "boolean", "default": true}, "themes": {"title": "Themes", "type": "boolean", "default": true}, "groups": {"title": "Groups", "type": "boolean", "default": true}, "users": {"title": "Users", "type": "boolean", "default": true}, "notifications": {"title": "Notifications", "type": "boolean", "default": true}}, "example": {"recipes": true, "settings": true, "themes": true, "groups": true, "users": true}}, "Body_create_recipe_from_zip_api_recipes_create_from_zip_post": {"title": "Body_create_recipe_from_zip_api_recipes_create_from_zip_post", "required": ["archive"], "type": "object", "properties": {"archive": {"title": "Archive", "type": "string", "format": "binary"}}}, "Body_get_token_api_auth_token_long_post": {"title": "Body_get_token_api_auth_token_long_post", "required": ["username", "password"], "type": "object", "properties": {"grant_type": {"title": "Grant Type", "pattern": "password", "type": "string"}, "username": {"title": "Username", "type": "string"}, "password": {"title": "Password", "type": "string"}, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": {"title": "Client Id", "type": "string"}, "client_secret": {"title": "Client Secret", "type": "string"}}}, "Body_get_token_api_auth_token_post": {"title": "Body_get_token_api_auth_token_post", "required": ["username", "password"], "type": "object", "properties": {"grant_type": {"title": "Grant Type", "pattern": "password", "type": "string"}, "username": {"title": "Username", "type": "string"}, "password": {"title": "Password", "type": "string"}, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": {"title": "Client Id", "type": "string"}, "client_secret": {"title": "Client Secret", "type": "string"}}}, "Body_update_recipe_image_api_recipes__recipe_slug__image_put": {"title": "Body_update_recipe_image_api_recipes__recipe_slug__image_put", "required": ["image", "extension"], "type": "object", "properties": {"image": {"title": "Image", "type": "string", "format": "binary"}, "extension": {"title": "Extension", "type": "string"}}}, "Body_update_user_image_api_users__id__image_post": {"title": "Body_update_user_image_api_users__id__image_post", "required": ["profile_image"], "type": "object", "properties": {"profile_image": {"title": "Profile Image", "type": "string", "format": "binary"}}}, "Body_upload_backup_file_api_backups_upload_post": {"title": "Body_upload_backup_file_api_backups_upload_post", "required": ["archive"], "type": "object", "properties": {"archive": {"title": "Archive", "type": "string", "format": "binary"}}}, "Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post": {"title": "Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post", "required": ["archive"], "type": "object", "properties": {"archive": {"title": "Archive", "type": "string", "format": "binary"}}}, "Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post": {"title": "Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post", "required": ["name", "icon", "extension", "file"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "icon": {"title": "Icon", "type": "string"}, "extension": {"title": "Extension", "type": "string"}, "file": {"title": "File", "type": "string", "format": "binary"}}}, "CategoryBase": {"title": "CategoryBase", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}}}, "CategoryIn": {"title": "CategoryIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "ChangePassword": {"title": "ChangePassword", "required": ["currentPassword", "newPassword"], "type": "object", "properties": {"currentPassword": {"title": "Currentpassword", "type": "string"}, "newPassword": {"title": "Newpassword", "type": "string"}}}, "Colors": {"title": "Colors", "type": "object", "properties": {"primary": {"title": "Primary", "type": "string", "default": "#E58325"}, "accent": {"title": "Accent", "type": "string", "default": "#00457A"}, "secondary": {"title": "Secondary", "type": "string", "default": "#973542"}, "success": {"title": "Success", "type": "string", "default": "#43A047"}, "info": {"title": "Info", "type": "string", "default": "#1976D2"}, "warning": {"title": "Warning", "type": "string", "default": "#FF6F00"}, "error": {"title": "Error", "type": "string", "default": "#EF5350"}}}, "CommentIn": {"title": "CommentIn", "required": ["text"], "type": "object", "properties": {"text": {"title": "Text", "type": "string"}}}, "CommentOut": {"title": "CommentOut", "required": ["text", "id", "uuid", "recipeSlug", "dateAdded", "user"], "type": "object", "properties": {"text": {"title": "Text", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "uuid": {"title": "Uuid", "type": "string"}, "recipeSlug": {"title": "Recipeslug", "type": "string"}, "dateAdded": {"title": "Dateadded", "type": "string", "format": "date-time"}, "user": {"$ref": "#/components/schemas/mealie__schema__recipe__comments__UserBase"}}}, "CreateRecipe": {"title": "CreateRecipe", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "CustomPageBase": {"title": "CustomPageBase", "required": ["name", "position"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string"}, "position": {"title": "Position", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/RecipeCategoryResponse"}, "default": []}}}, "CustomPageOut": {"title": "CustomPageOut", "required": ["name", "position", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string"}, "position": {"title": "Position", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/RecipeCategoryResponse"}, "default": []}, "id": {"title": "Id", "type": "integer"}}}, "DeclaredTypes": {"title": "DeclaredTypes", "enum": ["General", "Discord", "Gotify", "Pushover", "Home Assistant"], "type": "string", "description": "An enumeration."}, "Event": {"title": "Event", "required": ["title", "text"], "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string"}, "timeStamp": {"title": "Timestamp", "type": "string", "format": "date-time"}, "category": {"allOf": [{"$ref": "#/components/schemas/EventCategory"}], "default": "general"}}}, "EventCategory": {"title": "EventCategory", "enum": ["general", "recipe", "backup", "scheduled", "migration", "group", "user"], "type": "string", "description": "An enumeration."}, "EventNotificationIn": {"title": "EventNotificationIn", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": ""}, "type": {"allOf": [{"$ref": "#/components/schemas/DeclaredTypes"}], "default": "General"}, "general": {"title": "General", "type": "boolean", "default": true}, "recipe": {"title": "Recipe", "type": "boolean", "default": true}, "backup": {"title": "Backup", "type": "boolean", "default": true}, "scheduled": {"title": "Scheduled", "type": "boolean", "default": true}, "migration": {"title": "Migration", "type": "boolean", "default": true}, "group": {"title": "Group", "type": "boolean", "default": true}, "user": {"title": "User", "type": "boolean", "default": true}, "notificationUrl": {"title": "Notificationurl", "type": "string", "default": ""}}}, "EventNotificationOut": {"title": "EventNotificationOut", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": ""}, "type": {"allOf": [{"$ref": "#/components/schemas/DeclaredTypes"}], "default": "General"}, "general": {"title": "General", "type": "boolean", "default": true}, "recipe": {"title": "Recipe", "type": "boolean", "default": true}, "backup": {"title": "Backup", "type": "boolean", "default": true}, "scheduled": {"title": "Scheduled", "type": "boolean", "default": true}, "migration": {"title": "Migration", "type": "boolean", "default": true}, "group": {"title": "Group", "type": "boolean", "default": true}, "user": {"title": "User", "type": "boolean", "default": true}}}, "EventsOut": {"title": "EventsOut", "required": ["total", "events"], "type": "object", "properties": {"total": {"title": "Total", "type": "integer"}, "events": {"title": "Events", "type": "array", "items": {"$ref": "#/components/schemas/Event"}}}}, "GroupBase": {"title": "GroupBase", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "GroupInDB": {"title": "GroupInDB", "required": ["name", "id", "webhookEnable"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}, "webhookUrls": {"title": "Webhookurls", "type": "array", "items": {"type": "string"}, "default": []}, "webhookTime": {"title": "Webhooktime", "type": "string", "default": "00:00"}, "webhookEnable": {"title": "Webhookenable", "type": "boolean"}, "users": {"title": "Users", "type": "array", "items": {"$ref": "#/components/schemas/UserOut"}}, "mealplans": {"title": "Mealplans", "type": "array", "items": {"$ref": "#/components/schemas/MealPlanOut"}}, "shoppingLists": {"title": "Shoppinglists", "type": "array", "items": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "HTTPValidationError": {"title": "HTTPValidationError", "type": "object", "properties": {"detail": {"title": "Detail", "type": "array", "items": {"$ref": "#/components/schemas/ValidationError"}}}}, "ImageType": {"title": "ImageType", "enum": ["original.webp", "min-original.webp", "tiny-original.webp"], "type": "string", "description": "An enumeration."}, "ImportJob": {"title": "ImportJob", "required": ["name"], "type": "object", "properties": {"recipes": {"title": "Recipes", "type": "boolean", "default": true}, "settings": {"title": "Settings", "type": "boolean", "default": true}, "pages": {"title": "Pages", "type": "boolean", "default": true}, "themes": {"title": "Themes", "type": "boolean", "default": true}, "groups": {"title": "Groups", "type": "boolean", "default": true}, "users": {"title": "Users", "type": "boolean", "default": true}, "notifications": {"title": "Notifications", "type": "boolean", "default": true}, "name": {"title": "Name", "type": "string"}, "force": {"title": "Force", "type": "boolean", "default": false}, "rebase": {"title": "Rebase", "type": "boolean", "default": false}}, "example": {"name": "my_local_backup.zip", "recipes": true, "settings": true, "themes": true, "groups": true, "users": true}}, "Imports": {"title": "Imports", "required": ["imports", "templates"], "type": "object", "properties": {"imports": {"title": "Imports", "type": "array", "items": {"$ref": "#/components/schemas/LocalBackup"}}, "templates": {"title": "Templates", "type": "array", "items": {"type": "string"}}}, "example": {"imports": [{"name": "AutoBackup_12-1-2020.zip", "date": "2021-08-13T20:36:49.548929"}], "templates": ["recipes.md", "custom_template.md"]}}, "ListItem": {"title": "ListItem", "type": "object", "properties": {"title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string", "default": ""}, "quantity": {"title": "Quantity", "type": "integer", "default": 1}, "checked": {"title": "Checked", "type": "boolean", "default": false}}}, "LocalBackup": {"title": "LocalBackup", "required": ["name", "date"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "date": {"title": "Date", "type": "string", "format": "date-time"}}}, "LoingLiveTokenIn": {"title": "LoingLiveTokenIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "LongLiveTokenOut": {"title": "LongLiveTokenOut", "required": ["name", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}}}, "MealDayIn": {"title": "MealDayIn", "required": ["meals"], "type": "object", "properties": {"date": {"title": "Date", "type": "string", "format": "date"}, "meals": {"title": "Meals", "type": "array", "items": {"$ref": "#/components/schemas/MealIn"}}}}, "MealIn": {"title": "MealIn", "type": "object", "properties": {"slug": {"title": "Slug", "type": "string"}, "name": {"title": "Name", "type": "string"}, "description": {"title": "Description", "type": "string"}}}, "MealPlanIn": {"title": "MealPlanIn", "required": ["group", "startDate", "endDate", "planDays"], "type": "object", "properties": {"group": {"title": "Group", "type": "string"}, "startDate": {"title": "Startdate", "type": "string", "format": "date"}, "endDate": {"title": "Enddate", "type": "string", "format": "date"}, "planDays": {"title": "Plandays", "type": "array", "items": {"$ref": "#/components/schemas/MealDayIn"}}}}, "MealPlanOut": {"title": "MealPlanOut", "required": ["group", "startDate", "endDate", "planDays", "uid"], "type": "object", "properties": {"group": {"title": "Group", "type": "string"}, "startDate": {"title": "Startdate", "type": "string", "format": "date"}, "endDate": {"title": "Enddate", "type": "string", "format": "date"}, "planDays": {"title": "Plandays", "type": "array", "items": {"$ref": "#/components/schemas/MealDayIn"}}, "uid": {"title": "Uid", "type": "integer"}, "shoppingList": {"title": "Shoppinglist", "type": "integer"}}}, "Migration": {"title": "Migration", "enum": ["nextcloud", "chowdown"], "type": "string", "description": "The class defining the supported types of migrations for Mealie. Pass the\nclass attribute of the class instead of the string when using."}, "MigrationFile": {"title": "MigrationFile", "required": ["name", "date"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "date": {"title": "Date", "type": "string", "format": "date-time"}}}, "Migrations": {"title": "Migrations", "required": ["type"], "type": "object", "properties": {"type": {"title": "Type", "type": "string"}, "files": {"title": "Files", "type": "array", "items": {"$ref": "#/components/schemas/MigrationFile"}, "default": []}}}, "Nutrition": {"title": "Nutrition", "type": "object", "properties": {"calories": {"title": "Calories", "type": "string"}, "fatContent": {"title": "Fatcontent", "type": "string"}, "proteinContent": {"title": "Proteincontent", "type": "string"}, "carbohydrateContent": {"title": "Carbohydratecontent", "type": "string"}, "fiberContent": {"title": "Fibercontent", "type": "string"}, "sodiumContent": {"title": "Sodiumcontent", "type": "string"}, "sugarContent": {"title": "Sugarcontent", "type": "string"}}}, "Recipe": {"title": "Recipe", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string", "default": ""}, "image": {"title": "Image"}, "description": {"title": "Description", "type": "string"}, "recipeCategory": {"title": "Recipecategory", "type": "array", "items": {"type": "string"}, "default": []}, "tags": {"title": "Tags", "type": "array", "items": {"type": "string"}, "default": []}, "rating": {"title": "Rating", "type": "integer"}, "dateAdded": {"title": "Dateadded", "type": "string", "format": "date"}, "dateUpdated": {"title": "Dateupdated", "type": "string", "format": "date-time"}, "recipeYield": {"title": "Recipeyield", "type": "string"}, "recipeIngredient": {"title": "Recipeingredient", "type": "array", "items": {"$ref": "#/components/schemas/RecipeIngredient"}, "default": []}, "recipeInstructions": {"title": "Recipeinstructions", "type": "array", "items": {"$ref": "#/components/schemas/RecipeStep"}, "default": []}, "nutrition": {"$ref": "#/components/schemas/Nutrition"}, "tools": {"title": "Tools", "type": "array", "items": {"type": "string"}, "default": []}, "totalTime": {"title": "Totaltime", "type": "string"}, "prepTime": {"title": "Preptime", "type": "string"}, "performTime": {"title": "Performtime", "type": "string"}, "settings": {"title": "Settings", "allOf": [{"$ref": "#/components/schemas/RecipeSettings"}], "default": {"public": false, "show_nutrition": false, "show_assets": false, "landscape_view": false, "disable_comments": false, "disable_amount": false}}, "assets": {"title": "Assets", "type": "array", "items": {"$ref": "#/components/schemas/RecipeAsset"}, "default": []}, "notes": {"title": "Notes", "type": "array", "items": {"$ref": "#/components/schemas/RecipeNote"}, "default": []}, "orgURL": {"title": "Orgurl", "type": "string"}, "extras": {"title": "Extras", "type": "object", "default": {}}, "comments": {"title": "Comments", "type": "array", "items": {"$ref": "#/components/schemas/CommentOut"}, "default": []}}, "example": {"name": "Chicken and Rice With Leeks and Salsa Verde", "description": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.", "image": "chicken-and-rice-with-leeks-and-salsa-verde.jpg", "recipe_yield": "4 Servings", "recipe_ingredient": ["1 1/2 lb. skinless, boneless chicken thighs (4-8 depending on size)", "Kosher salt, freshly ground pepper", "3 Tbsp. unsalted butter, divided"], "recipe_instructions": [{"text": "Season chicken with salt and pepper."}], "slug": "chicken-and-rice-with-leeks-and-salsa-verde", "tags": ["favorite", "yummy!"], "recipe_category": ["Dinner", "Pasta"], "notes": [{"title": "Watch Out!", "text": "Prep the day before!"}], "org_url": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde", "rating": 3, "extras": {"message": "Don't forget to defrost the chicken!"}}}, "RecipeAsset": {"title": "RecipeAsset", "required": ["name", "icon"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "icon": {"title": "Icon", "type": "string"}, "fileName": {"title": "Filename", "type": "string"}}}, "RecipeCategoryResponse": {"title": "RecipeCategoryResponse", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}, "recipes": {"title": "Recipes", "type": "array", "items": {"$ref": "#/components/schemas/Recipe"}}}, "example": {"id": 1, "name": "dinner", "recipes": [{}]}}, "RecipeIngredient": {"title": "RecipeIngredient", "type": "object", "properties": {"title": {"title": "Title", "type": "string"}, "note": {"title": "Note", "type": "string"}, "unit": {"$ref": "#/components/schemas/RecipeIngredientUnit"}, "food": {"$ref": "#/components/schemas/RecipeIngredientFood"}, "disableAmount": {"title": "Disableamount", "type": "boolean", "default": true}, "quantity": {"title": "Quantity", "type": "integer", "default": 1}}}, "RecipeIngredientFood": {"title": "RecipeIngredientFood", "type": "object", "properties": {"name": {"title": "Name", "type": "string", "default": ""}, "description": {"title": "Description", "type": "string", "default": ""}}}, "RecipeIngredientUnit": {"title": "RecipeIngredientUnit", "type": "object", "properties": {"name": {"title": "Name", "type": "string", "default": ""}, "description": {"title": "Description", "type": "string", "default": ""}}}, "RecipeNote": {"title": "RecipeNote", "required": ["title", "text"], "type": "object", "properties": {"title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string"}}}, "RecipeSettings": {"title": "RecipeSettings", "type": "object", "properties": {"public": {"title": "Public", "type": "boolean", "default": false}, "showNutrition": {"title": "Shownutrition", "type": "boolean", "default": false}, "showAssets": {"title": "Showassets", "type": "boolean", "default": false}, "landscapeView": {"title": "Landscapeview", "type": "boolean", "default": false}, "disableComments": {"title": "Disablecomments", "type": "boolean", "default": false}, "disableAmount": {"title": "Disableamount", "type": "boolean", "default": false}}}, "RecipeStep": {"title": "RecipeStep", "required": ["text"], "type": "object", "properties": {"title": {"title": "Title", "type": "string", "default": ""}, "text": {"title": "Text", "type": "string"}}}, "RecipeSummary": {"title": "RecipeSummary", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string", "default": ""}, "image": {"title": "Image"}, "description": {"title": "Description", "type": "string"}, "recipeCategory": {"title": "Recipecategory", "type": "array", "items": {"type": "string"}, "default": []}, "tags": {"title": "Tags", "type": "array", "items": {"type": "string"}, "default": []}, "rating": {"title": "Rating", "type": "integer"}, "dateAdded": {"title": "Dateadded", "type": "string", "format": "date"}, "dateUpdated": {"title": "Dateupdated", "type": "string", "format": "date-time"}}}, "RecipeTagResponse": {"title": "RecipeTagResponse", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}, "recipes": {"title": "Recipes", "type": "array", "items": {"$ref": "#/components/schemas/Recipe"}}}, "example": {"id": 1, "name": "dinner", "recipes": [{}]}}, "RecipeURLIn": {"title": "RecipeURLIn", "required": ["url"], "type": "object", "properties": {"url": {"title": "Url", "type": "string"}}, "example": {"url": "https://myfavoriterecipes.com/recipes"}}, "ShoppingListIn": {"title": "ShoppingListIn", "required": ["name", "items"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "group": {"title": "Group", "type": "string"}, "items": {"title": "Items", "type": "array", "items": {"$ref": "#/components/schemas/ListItem"}}}}, "ShoppingListOut": {"title": "ShoppingListOut", "required": ["name", "items", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "group": {"title": "Group", "type": "string"}, "items": {"title": "Items", "type": "array", "items": {"$ref": "#/components/schemas/ListItem"}}, "id": {"title": "Id", "type": "integer"}}}, "SignUpIn": {"title": "SignUpIn", "required": ["name", "admin"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}}}, "SignUpOut": {"title": "SignUpOut", "required": ["name", "admin", "token", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "token": {"title": "Token", "type": "string"}, "id": {"title": "Id", "type": "integer"}}}, "SignUpToken": {"title": "SignUpToken", "required": ["name", "admin", "token"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "token": {"title": "Token", "type": "string"}}}, "SiteSettings": {"title": "SiteSettings", "type": "object", "properties": {"language": {"title": "Language", "type": "string", "default": "en-US"}, "firstDayOfWeek": {"title": "Firstdayofweek", "type": "integer", "default": 0}, "showRecent": {"title": "Showrecent", "type": "boolean", "default": true}, "cardsPerSection": {"title": "Cardspersection", "type": "integer", "default": 9}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}}, "example": {"language": "en", "firstDayOfWeek": 0, "showRecent": true, "categories": [{"id": 1, "name": "thanksgiving", "slug": "thanksgiving"}, {"id": 2, "name": "homechef", "slug": "homechef"}, {"id": 3, "name": "potatoes", "slug": "potatoes"}]}}, "SiteTheme": {"title": "SiteTheme", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": "default"}, "colors": {"title": "Colors", "allOf": [{"$ref": "#/components/schemas/Colors"}], "default": {"primary": "#E58325", "accent": "#00457A", "secondary": "#973542", "success": "#43A047", "info": "#1976D2", "warning": "#FF6F00", "error": "#EF5350"}}}, "example": {"name": "default", "colors": {"primary": "#E58325", "accent": "#00457A", "secondary": "#973542", "success": "#5AB1BB", "info": "#4990BA", "warning": "#FF4081", "error": "#EF5350"}}}, "TagIn": {"title": "TagIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "TestEvent": {"title": "TestEvent", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "testUrl": {"title": "Testurl", "type": "string"}}}, "UpdateGroup": {"title": "UpdateGroup", "required": ["name", "id", "webhookEnable"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}, "webhookUrls": {"title": "Webhookurls", "type": "array", "items": {"type": "string"}, "default": []}, "webhookTime": {"title": "Webhooktime", "type": "string", "default": "00:00"}, "webhookEnable": {"title": "Webhookenable", "type": "boolean"}}}, "UserFavorites": {"title": "UserFavorites", "required": ["email", "admin"], "type": "object", "properties": {"username": {"title": "Username", "type": "string"}, "fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "favoriteRecipes": {"title": "Favoriterecipes", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}, "default": []}}}, "UserIn": {"title": "UserIn", "required": ["email", "admin", "password"], "type": "object", "properties": {"username": {"title": "Username", "type": "string"}, "fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "favoriteRecipes": {"title": "Favoriterecipes", "type": "array", "items": {"type": "string"}, "default": []}, "password": {"title": "Password", "type": "string"}}}, "UserOut": {"title": "UserOut", "required": ["email", "admin", "group", "id"], "type": "object", "properties": {"username": {"title": "Username", "type": "string"}, "fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "favoriteRecipes": {"title": "Favoriterecipes", "type": "array", "items": {"type": "string"}, "default": []}, "id": {"title": "Id", "type": "integer"}, "tokens": {"title": "Tokens", "type": "array", "items": {"$ref": "#/components/schemas/LongLiveTokenOut"}}}}, "ValidationError": {"title": "ValidationError", "required": ["loc", "msg", "type"], "type": "object", "properties": {"loc": {"title": "Location", "type": "array", "items": {"type": "string"}}, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}}}, "mealie__schema__recipe__comments__UserBase": {"title": "UserBase", "required": ["id", "admin"], "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "username": {"title": "Username", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}}}, "mealie__schema__user__user__UserBase": {"title": "UserBase", "required": ["email", "admin"], "type": "object", "properties": {"username": {"title": "Username", "type": "string"}, "fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "favoriteRecipes": {"title": "Favoriterecipes", "type": "array", "items": {"type": "string"}, "default": []}}}}, "securitySchemes": {"OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "/api/auth/token"}}}}}} \ No newline at end of file diff --git a/frontend/lang/errors/en-US.json b/frontend/lang/errors/en-US.json new file mode 100644 index 000000000000..14f4133c0ab0 --- /dev/null +++ b/frontend/lang/errors/en-US.json @@ -0,0 +1,44 @@ +{ + "backup-create-failed": "Backup Create Failed", + "backup-update-failed": "Backup Update Failed", + "backup-delete-failed": "Backup Delete Failed", + "cookbook-create-failed": "Cookbook Create Failed", + "cookbook-update-failed": "Cookbook Update Failed", + "cookbook-delete-failed": "Cookbook Delete Failed", + "event-create-failed": "Event Create Failed", + "event-update-failed": "Event Update Failed", + "event-delete-failed": "Event Delete Failed", + "food-create-failed": "Food Create Failed", + "food-update-failed": "Food Update Failed", + "food-delete-failed": "Food Delete Failed", + "group-create-failed": "Group Create Failed", + "group-update-failed": "Group Update Failed", + "group-delete-failed": "Group Delete Failed", + "ingredient-create-failed": "Ingredient Create Failed", + "ingredient-update-failed": "Ingredient Update Failed", + "ingredient-delete-failed": "Ingredient Delete Failed", + "mealplan-create-failed": "Mealplan Create Failed", + "mealplan-update-failed": "Mealplan Update Failed", + "mealplan-delete-failed": "Mealplan Delete Failed", + "migration-create-failed": "Migration Create Failed", + "migration-update-failed": "Migration Update Failed", + "migration-delete-failed": "Migration Delete Failed", + "recipe-create-failed": "Recipe Create Failed", + "recipe-update-failed": "Recipe Update Failed", + "recipe-delete-failed": "Recipe Delete Failed", + "scraper-create-failed": "Scraper Create Failed", + "scraper-update-failed": "Scraper Update Failed", + "scraper-delete-failed": "Scraper Delete Failed", + "token-create-failed": "Token Create Failed", + "token-update-failed": "Token Update Failed", + "token-delete-failed": "Token Delete Failed", + "unit-create-failed": "Unit Create Failed", + "unit-update-failed": "Unit Update Failed", + "unit-delete-failed": "Unit Delete Failed", + "user-create-failed": "User Create Failed", + "user-update-failed": "User Update Failed", + "user-delete-failed": "User Delete Failed", + "webhook-create-failed": "Webhook Create Failed", + "webhook-update-failed": "Webhook Update Failed", + "webhook-delete-failed": "Webhook Delete Failed" +} \ No newline at end of file diff --git a/mealie/core/dependencies/grouped.py b/mealie/core/dependencies/grouped.py index 75150779da6b..63a52fa9b0bc 100644 --- a/mealie/core/dependencies/grouped.py +++ b/mealie/core/dependencies/grouped.py @@ -3,12 +3,12 @@ from sqlalchemy.orm.session import Session from mealie.schema.user.user import PrivateUser -from .dependencies import generate_session, get_current_user, is_logged_in +from .dependencies import generate_session, get_admin_user, get_current_user, is_logged_in -class ReadDeps: +class PublicDeps: """ - ReadDeps contains the common dependencies for all read operations through the API. + PublicDeps contains the common dependencies for all read operations through the API. Note: The user object is used to definer what assets the user has access to. Args: @@ -28,9 +28,9 @@ class ReadDeps: self.user: bool = user -class WriteDeps: +class UserDeps: """ - WriteDeps contains the common dependencies for all read operations through the API. + UserDeps contains the common dependencies for all read operations through the API. Note: The user must be logged in or the route will return a 401 error. Args: @@ -48,3 +48,15 @@ class WriteDeps: self.session: Session = session self.bg_task: BackgroundTasks = background_tasks self.user: PrivateUser = user + + +class AdminDeps: + def __init__( + self, + background_tasks: BackgroundTasks, + session: Session = Depends(generate_session), + user=Depends(get_admin_user), + ): + self.session: Session = session + self.bg_task: BackgroundTasks = background_tasks + self.user: PrivateUser = user diff --git a/mealie/services/base_http_service/base_http_service.py b/mealie/services/base_http_service/base_http_service.py index 297e3ea49422..511032a4a749 100644 --- a/mealie/services/base_http_service/base_http_service.py +++ b/mealie/services/base_http_service/base_http_service.py @@ -1,11 +1,11 @@ from abc import ABC, abstractmethod -from typing import Callable, Generic, TypeVar +from typing import Callable, Generic, Type, TypeVar from fastapi import BackgroundTasks, Depends, HTTPException, status from sqlalchemy.orm.session import Session from mealie.core.config import get_app_dirs, get_settings -from mealie.core.dependencies.grouped import ReadDeps, WriteDeps +from mealie.core.dependencies.grouped import PublicDeps, UserDeps from mealie.core.root_logger import get_logger from mealie.db.database import get_database from mealie.db.db_setup import SessionLocal @@ -17,25 +17,25 @@ T = TypeVar("T") D = TypeVar("D") +CLS_DEP = TypeVar("CLS_DEP") # Generic Used for the class method dependencies + + class BaseHttpService(Generic[T, D], ABC): - """The BaseHttpService class is a generic class that can be used to create + """ + The BaseHttpService class is a generic class that can be used to create http services that are injected via `Depends` into a route function. To use, you must define the Generic type arguments: `T`: The type passed into the *_existing functions (e.g. id) which is then passed into assert_existing `D`: Item returned from database layer - - Child Requirements: - Define the following functions: - `assert_existing(self, data: T) -> None:` - - Define the following variables: - `event_func`: A function that is called when an event is created. """ item: D = None - # Function that Generate Corrsesponding Routes through RouterFactor + # Function that Generate Corrsesponding Routes through RouterFactory: + # if the method is defined or != `None` than the corresponding route is defined through the RouterFactory. + # If the method is not defined, then the route will be excluded from creation. This service based articheture + # is being adopted as apart of the v1 migration get_all: Callable = None create_one: Callable = None update_one: Callable = None @@ -75,48 +75,35 @@ class BaseHttpService(Generic[T, D], ABC): self._group_id_cache = group.id return self._group_id_cache - @classmethod - def read_existing(cls, item_id: T, deps: ReadDeps = Depends()): - """ - Used for dependency injection for routes that require an existing recipe. If the recipe doesn't exist - or the user doens't not have the required permissions, the proper HTTP Status code will be raised. - """ - new_class = cls(deps.session, deps.user, deps.bg_task) - new_class.assert_existing(item_id) - return new_class + def _existing_factory(dependency: Type[CLS_DEP]) -> classmethod: + def cls_method(cls, item_id: T, deps: CLS_DEP = Depends(dependency)): + new_class = cls(deps.session, deps.user, deps.bg_task) + new_class.assert_existing(item_id) + return new_class - @classmethod - def write_existing(cls, item_id: T, deps: WriteDeps = Depends()): - """ - Used for dependency injection for routes that require an existing recipe. The only difference between - read_existing and write_existing is that the user is required to be logged in on write_existing method. - """ - new_class = cls(deps.session, deps.user, deps.bg_task) - new_class.assert_existing(item_id) - return new_class + return classmethod(cls_method) - @classmethod - def public(cls, deps: ReadDeps = Depends()): - """ - A Base instance to be used as a router dependency - """ - return cls(deps.session, deps.user, deps.bg_task) + def _class_method_factory(dependency: Type[CLS_DEP]) -> classmethod: + def cls_method(cls, deps: CLS_DEP = Depends(dependency)): + return cls(deps.session, deps.user, deps.bg_task) - @classmethod - def private(cls, deps: WriteDeps = Depends()): - """ - A Base instance to be used as a router dependency - """ - return cls(deps.session, deps.user, deps.bg_task) + return classmethod(cls_method) - @abstractmethod - def populate_item(self) -> None: - ... + # TODO: Refactor to allow for configurable dependencies base on substantiation + read_existing = _existing_factory(PublicDeps) + write_existing = _existing_factory(UserDeps) + + public = _class_method_factory(PublicDeps) + private = _class_method_factory(UserDeps) def assert_existing(self, id: T) -> None: self.populate_item(id) self._check_item() + @abstractmethod + def populate_item(self) -> None: + ... + def _check_item(self) -> None: if not self.item: raise HTTPException(status.HTTP_404_NOT_FOUND) diff --git a/mealie/services/base_http_service/http_services.py b/mealie/services/base_http_service/http_services.py new file mode 100644 index 000000000000..cc6d1fd4a005 --- /dev/null +++ b/mealie/services/base_http_service/http_services.py @@ -0,0 +1,60 @@ +from abc import abstractmethod +from typing import TypeVar + +from mealie.core.dependencies.grouped import AdminDeps, PublicDeps, UserDeps + +from .base_http_service import BaseHttpService + +T = TypeVar("T") +D = TypeVar("D") + + +class PublicHttpService(BaseHttpService[T, D]): + """ + PublicHttpService sets the class methods to PublicDeps for read actions + and UserDeps for write actions which are inaccessible to not logged in users. + """ + + read_existing = BaseHttpService._existing_factory(PublicDeps) + write_existing = BaseHttpService._existing_factory(UserDeps) + + public = BaseHttpService._class_method_factory(PublicDeps) + private = BaseHttpService._class_method_factory(UserDeps) + + @abstractmethod + def populate_item(self) -> None: + ... + + +class UserHttpService(BaseHttpService[T, D]): + """ + UserHttpService sets the class methods to UserDeps which are inaccessible + to not logged in users. + """ + + read_existing = BaseHttpService._existing_factory(UserDeps) + write_existing = BaseHttpService._existing_factory(UserDeps) + + public = BaseHttpService._class_method_factory(UserDeps) + private = BaseHttpService._class_method_factory(UserDeps) + + @abstractmethod + def populate_item(self) -> None: + ... + + +class AdminHttpService(BaseHttpService[T, D]): + """ + AdminHttpService restricts the class methods to AdminDeps which are restricts + all class methods to users who are administrators. + """ + + read_existing = BaseHttpService._existing_factory(AdminDeps) + write_existing = BaseHttpService._existing_factory(AdminDeps) + + public = BaseHttpService._class_method_factory(AdminDeps) + private = BaseHttpService._class_method_factory(AdminDeps) + + @abstractmethod + def populate_item(self) -> None: + ... diff --git a/mealie/services/base_http_service/router_factory.py b/mealie/services/base_http_service/router_factory.py index 4046111b8ac7..cfe656f42f89 100644 --- a/mealie/services/base_http_service/router_factory.py +++ b/mealie/services/base_http_service/router_factory.py @@ -1,3 +1,4 @@ +import inspect from typing import Any, Callable, Optional, Sequence, Type, TypeVar from fastapi import APIRouter @@ -23,15 +24,7 @@ class RouterFactory(APIRouter): update_schema: Type[T] _base_path: str = "/" - def __init__( - self, - service: Type[S], - prefix: Optional[str] = None, - tags: Optional[list[str]] = None, - *args, - **kwargs, - ): - + def __init__(self, service: Type[S], prefix: Optional[str] = None, tags: Optional[list[str]] = None, *_, **kwargs): self.service: Type[S] = service self.schema: Type[T] = service._schema @@ -57,6 +50,7 @@ class RouterFactory(APIRouter): methods=["GET"], response_model=Optional[list[self.schema]], # type: ignore summary="Get All", + description=inspect.cleandoc(self.service.get_all.__doc__ or ""), ) if self.service.create_one: @@ -66,6 +60,7 @@ class RouterFactory(APIRouter): methods=["POST"], response_model=self.schema, summary="Create One", + description=inspect.cleandoc(self.service.create_one.__doc__ or ""), ) if self.service.update_many: @@ -75,6 +70,7 @@ class RouterFactory(APIRouter): methods=["PUT"], response_model=Optional[list[self.schema]], # type: ignore summary="Update Many", + description=inspect.cleandoc(self.service.update_many.__doc__ or ""), ) if self.service.delete_all: @@ -84,6 +80,7 @@ class RouterFactory(APIRouter): methods=["DELETE"], response_model=Optional[list[self.schema]], # type: ignore summary="Delete All", + description=inspect.cleandoc(self.service.delete_all.__doc__ or ""), ) if self.service.populate_item: @@ -93,6 +90,7 @@ class RouterFactory(APIRouter): methods=["GET"], response_model=self.get_one_schema, summary="Get One", + description=inspect.cleandoc(self.service.populate_item.__doc__ or ""), ) if self.service.update_one: @@ -102,15 +100,18 @@ class RouterFactory(APIRouter): methods=["PUT"], response_model=self.schema, summary="Update One", + description=inspect.cleandoc(self.service.update_one.__doc__ or ""), ) if self.service.delete_one: + print(self.service.delete_one.__doc__) self._add_api_route( "/{item_id}", self._delete_one(), methods=["DELETE"], response_model=self.schema, summary="Delete One", + description=inspect.cleandoc(self.service.delete_one.__doc__ or ""), ) def _add_api_route(self, path: str, endpoint: Callable[..., Any], **kwargs: Any) -> None: diff --git a/mealie/services/cookbook/cookbook_service.py b/mealie/services/cookbook/cookbook_service.py index e970425ba5d5..2eee39483586 100644 --- a/mealie/services/cookbook/cookbook_service.py +++ b/mealie/services/cookbook/cookbook_service.py @@ -4,13 +4,13 @@ from fastapi import HTTPException, status from mealie.core.root_logger import get_logger from mealie.schema.cookbook.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook -from mealie.services.base_http_service.base_http_service import BaseHttpService +from mealie.services.base_http_service.http_services import UserHttpService from mealie.services.events import create_group_event logger = get_logger(module=__name__) -class CookbookService(BaseHttpService[int, ReadCookBook]): +class CookbookService(UserHttpService[int, ReadCookBook]): event_func = create_group_event _restrict_by_group = True @@ -19,17 +19,17 @@ class CookbookService(BaseHttpService[int, ReadCookBook]): _update_schema = UpdateCookBook _get_one_schema = RecipeCookBook - def populate_item(self, id: int | str): + def populate_item(self, item_id: int | str): try: - id = int(id) + item_id = int(item_id) except Exception: pass - if isinstance(id, int): - self.item = self.db.cookbooks.get_one(self.session, id, override_schema=RecipeCookBook) + if isinstance(item_id, int): + self.item = self.db.cookbooks.get_one(self.session, item_id, override_schema=RecipeCookBook) else: - self.item = self.db.cookbooks.get_one(self.session, id, key="slug", override_schema=RecipeCookBook) + self.item = self.db.cookbooks.get_one(self.session, item_id, key="slug", override_schema=RecipeCookBook) def get_all(self) -> list[ReadCookBook]: items = self.db.cookbooks.get(self.session, self.group_id, "group_id", limit=999) diff --git a/mealie/services/group/group_service.py b/mealie/services/group/group_service.py index c1dea8ab8f19..1455e56fb0cb 100644 --- a/mealie/services/group/group_service.py +++ b/mealie/services/group/group_service.py @@ -2,7 +2,7 @@ from __future__ import annotations from fastapi import Depends, HTTPException, status -from mealie.core.dependencies.grouped import WriteDeps +from mealie.core.dependencies.grouped import UserDeps from mealie.core.root_logger import get_logger from mealie.schema.recipe.recipe_category import CategoryBase from mealie.schema.user.user import GroupInDB @@ -18,12 +18,12 @@ class GroupSelfService(BaseHttpService[int, str]): item: GroupInDB @classmethod - def read_existing(cls, deps: WriteDeps = Depends()): + def read_existing(cls, deps: UserDeps = Depends()): """Override parent method to remove `item_id` from arguments""" return super().read_existing(item_id=0, deps=deps) @classmethod - def write_existing(cls, deps: WriteDeps = Depends()): + def write_existing(cls, deps: UserDeps = Depends()): """Override parent method to remove `item_id` from arguments""" return super().write_existing(item_id=0, deps=deps) diff --git a/mealie/services/recipe/recipe_service.py b/mealie/services/recipe/recipe_service.py index 3e4e304273e8..ff8a00e1cac6 100644 --- a/mealie/services/recipe/recipe_service.py +++ b/mealie/services/recipe/recipe_service.py @@ -5,7 +5,7 @@ from typing import Union from fastapi import Depends, HTTPException, status from sqlalchemy.exc import IntegrityError -from mealie.core.dependencies.grouped import ReadDeps, WriteDeps +from mealie.core.dependencies.grouped import PublicDeps, UserDeps from mealie.core.root_logger import get_logger from mealie.schema.recipe.recipe import CreateRecipe, Recipe from mealie.services.base_http_service.base_http_service import BaseHttpService @@ -25,11 +25,11 @@ class RecipeService(BaseHttpService[str, Recipe]): event_func = create_recipe_event @classmethod - def write_existing(cls, slug: str, deps: WriteDeps = Depends()): + def write_existing(cls, slug: str, deps: UserDeps = Depends()): return super().write_existing(slug, deps) @classmethod - def read_existing(cls, slug: str, deps: ReadDeps = Depends()): + def read_existing(cls, slug: str, deps: PublicDeps = Depends()): return super().write_existing(slug, deps) def assert_existing(self, slug: str): diff --git a/mealie/utils/error_messages.py b/mealie/utils/error_messages.py new file mode 100644 index 000000000000..c9904b9d22da --- /dev/null +++ b/mealie/utils/error_messages.py @@ -0,0 +1,81 @@ +from dataclasses import dataclass + + +@dataclass +class ErrorMessages: + """ + This enum class holds the text values that represent the errors returned when + something goes wrong on the server side. + + Example: {"details": "general-failure"} + + The items contained within the '#' are automatically generated by a script in the scripts directory. + DO NOT EDIT THE CONTENTS BETWEEN THOSE. If you need to add a custom error message, do so in the lines + above. + + Why Generate This!?!?! If we generate static errors on the backend we can ensure that a constant + set or error messages will be returned to the frontend. As such we can use the "details" key to + look up localized messages in the frontend. as such DO NOT change the generated or manual codes + without making the necessary changes on the client side code. + """ + + general_failure = "general-failure" + + # CODE_GEN_ID: ERROR_MESSAGE_ENUMS + backup_create_failure = "backup-create-failure" + backup_update_failure = "backup-update-failure" + backup_delete_failure = "backup-delete-failure" + + cookbook_create_failure = "cookbook-create-failure" + cookbook_update_failure = "cookbook-update-failure" + cookbook_delete_failure = "cookbook-delete-failure" + + event_create_failure = "event-create-failure" + event_update_failure = "event-update-failure" + event_delete_failure = "event-delete-failure" + + food_create_failure = "food-create-failure" + food_update_failure = "food-update-failure" + food_delete_failure = "food-delete-failure" + + group_create_failure = "group-create-failure" + group_update_failure = "group-update-failure" + group_delete_failure = "group-delete-failure" + + ingredient_create_failure = "ingredient-create-failure" + ingredient_update_failure = "ingredient-update-failure" + ingredient_delete_failure = "ingredient-delete-failure" + + mealplan_create_failure = "mealplan-create-failure" + mealplan_update_failure = "mealplan-update-failure" + mealplan_delete_failure = "mealplan-delete-failure" + + migration_create_failure = "migration-create-failure" + migration_update_failure = "migration-update-failure" + migration_delete_failure = "migration-delete-failure" + + recipe_create_failure = "recipe-create-failure" + recipe_update_failure = "recipe-update-failure" + recipe_delete_failure = "recipe-delete-failure" + + scraper_create_failure = "scraper-create-failure" + scraper_update_failure = "scraper-update-failure" + scraper_delete_failure = "scraper-delete-failure" + + token_create_failure = "token-create-failure" + token_update_failure = "token-update-failure" + token_delete_failure = "token-delete-failure" + + unit_create_failure = "unit-create-failure" + unit_update_failure = "unit-update-failure" + unit_delete_failure = "unit-delete-failure" + + user_create_failure = "user-create-failure" + user_update_failure = "user-update-failure" + user_delete_failure = "user-delete-failure" + + webhook_create_failure = "webhook-create-failure" + webhook_update_failure = "webhook-update-failure" + webhook_delete_failure = "webhook-delete-failure" + + # END ERROR_MESSAGE_ENUMS