fix/test that never really passed

This commit is contained in:
hay-kot 2021-03-21 20:27:36 -08:00
commit dcdca9eec7
17 changed files with 256 additions and 176 deletions

0
mealie/core/__init__.py Normal file
View file

View file

@ -19,7 +19,7 @@ ENV = CWD.joinpath(".env") #! I'm Broken Fix Me!
dotenv.load_dotenv(ENV) dotenv.load_dotenv(ENV)
SECRET = "super-secret-key" SECRET = "test-secret-shhh"
# General # General
PRODUCTION = os.environ.get("ENV") PRODUCTION = os.environ.get("ENV")

View file

@ -1,6 +1,33 @@
from datetime import datetime, timedelta
from mealie.schema.user import UserInDB
from jose import jwt
from mealie.core.config import SECRET
from mealie.db.database import db
from passlib.context import CryptContext from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ALGORITHM = "HS256"
def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=120)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET, algorithm=ALGORITHM)
return encoded_jwt
def authenticate_user(session, email: str, password: str) -> UserInDB:
user: UserInDB = db.users.get(session, email, "email")
if not user:
return False
if not verify_password(password, user.password):
return False
return user
def verify_password(plain_password: str, hashed_password: str) -> bool: def verify_password(plain_password: str, hashed_password: str) -> bool:

View file

@ -1,23 +1,35 @@
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from mealie.core.config import SECRET from mealie.core.config import SECRET
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import create_session from mealie.db.db_setup import create_session, generate_session
from fastapi_login import LoginManager from mealie.schema.auth import Token, TokenData
from sqlalchemy.orm.session import Session
from mealie.schema.user import UserInDB from mealie.schema.user import UserInDB
manager = LoginManager(SECRET, "/api/auth/token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
ALGORITHM = "HS256"
@manager.user_loader async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(generate_session)) -> UserInDB:
def query_user(user_email: str, session: Session = None) -> UserInDB: credentials_exception = HTTPException(
""" status_code=status.HTTP_401_UNAUTHORIZED,
Get a user from the db detail="Could not validate credentials",
:param user_id: E-Mail of the user headers={"WWW-Authenticate": "Bearer"},
:return: None or the UserInDB object )
""" try:
payload = jwt.decode(token, SECRET, algorithms=[ALGORITHM])
session = session if session else create_session() username: str = payload.get("sub")
user = db.users.get(session, user_email, "email") if username is None:
session.close() raise credentials_exception
token_data = TokenData(username=username)
print("Login Payload", token_data)
except JWTError:
raise credentials_exception
user = db.users.get(session, token_data.username, "email")
if user is None:
raise credentials_exception
return user return user

View file

@ -1,9 +1,9 @@
from fastapi import APIRouter, Depends
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from mealie.routes.deps import get_current_user
from mealie.routes.deps import manager
from mealie.schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserIn, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/groups", tags=["Groups"]) router = APIRouter(prefix="/api/groups", tags=["Groups"])
@ -11,7 +11,7 @@ router = APIRouter(prefix="/api/groups", tags=["Groups"])
@router.get("", response_model=list[GroupInDB]) @router.get("", response_model=list[GroupInDB])
async def get_all_groups( async def get_all_groups(
current_user=Depends(manager), current_user=Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns a list of all groups in the database """ """ Returns a list of all groups in the database """
@ -21,7 +21,7 @@ async def get_all_groups(
@router.get("/self", response_model=GroupInDB) @router.get("/self", response_model=GroupInDB)
async def get_current_user_group( async def get_current_user_group(
current_user=Depends(manager), current_user: UserInDB =Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns the Group Data for the Current User """ """ Returns the Group Data for the Current User """
@ -33,7 +33,7 @@ async def get_current_user_group(
@router.post("") @router.post("")
async def create_group( async def create_group(
group_data: GroupBase, group_data: GroupBase,
current_user=Depends(manager), current_user=Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Creates a Group in the Database """ """ Creates a Group in the Database """
@ -49,7 +49,7 @@ async def create_group(
async def update_group_data( async def update_group_data(
id: int, id: int,
group_data: UpdateGroup, group_data: UpdateGroup,
current_user=Depends(manager), current_user=Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Updates a User Group """ """ Updates a User Group """
@ -59,7 +59,9 @@ async def update_group_data(
@router.delete("/{id}") @router.delete("/{id}")
async def delete_user_group(id: int, current_user=Depends(manager), session: Session = Depends(generate_session)): async def delete_user_group(
id: int, current_user=Depends(get_current_user), session: Session = Depends(generate_session)
):
""" Removes a user group from the database """ """ Removes a user group from the database """
if id == 1: if id == 1:

View file

@ -3,7 +3,7 @@ import datetime
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.deps import manager from mealie.routes.deps import get_current_user
from mealie.schema.meal import MealPlanIn, MealPlanInDB from mealie.schema.meal import MealPlanIn, MealPlanInDB
from mealie.schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, UserInDB
@ -15,7 +15,7 @@ router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@router.get("/all", response_model=list[MealPlanInDB]) @router.get("/all", response_model=list[MealPlanInDB])
def get_all_meals( def get_all_meals(
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns a list of all available Meal Plan """ """ Returns a list of all available Meal Plan """
@ -27,7 +27,7 @@ def get_all_meals(
def create_meal_plan( def create_meal_plan(
data: MealPlanIn, data: MealPlanIn,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user=Depends(manager), current_user=Depends(get_current_user),
): ):
""" Creates a meal plan database entry """ """ Creates a meal plan database entry """
processed_plan = process_meals(session, data) processed_plan = process_meals(session, data)
@ -58,7 +58,7 @@ def delete_meal_plan(plan_id, session: Session = Depends(generate_session)):
@router.get("/this-week", response_model=MealPlanInDB) @router.get("/this-week", response_model=MealPlanInDB)
def get_this_week( def get_this_week(
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
): ):
""" Returns the meal plan data for this week """ """ Returns the meal plan data for this week """
@ -68,7 +68,7 @@ def get_this_week(
@router.get("/today", tags=["Meal Plan"]) @router.get("/today", tags=["Meal Plan"])
def get_today( def get_today(
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
): ):
""" """
Returns the recipe slug for the meal scheduled for today. Returns the recipe slug for the meal scheduled for today.

View file

@ -7,7 +7,7 @@ from mealie.schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from mealie.utils.post_webhooks import post_webhooks from mealie.utils.post_webhooks import post_webhooks
from mealie.routes.deps import manager from mealie.routes.deps import get_current_user
router = APIRouter(prefix="/api/site-settings", tags=["Settings"]) router = APIRouter(prefix="/api/site-settings", tags=["Settings"])
@ -31,7 +31,7 @@ def update_settings(data: SiteSettings, session: Session = Depends(generate_sess
@router.post("/webhooks/test") @router.post("/webhooks/test")
def test_webhooks( def test_webhooks(
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Run the function to test your webhooks """ """ Run the function to test your webhooks """

View file

@ -1,11 +1,12 @@
from datetime import timedelta from datetime import timedelta
from mealie.core.security import verify_password from fastapi import APIRouter, Depends, status
from mealie.db.db_setup import generate_session from fastapi.exceptions import HTTPException
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from fastapi_login.exceptions import InvalidCredentialsException from mealie.core import security
from mealie.routes.deps import manager, query_user from mealie.core.security import authenticate_user, verify_password
from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user
from mealie.schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import UserInDB from mealie.schema.user import UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -13,6 +14,7 @@ from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/auth", tags=["Authentication"]) router = APIRouter(prefix="/api/auth", tags=["Authentication"])
@router.post("/token/long")
@router.post("/token") @router.post("/token")
def get_token( def get_token(
data: OAuth2PasswordRequestForm = Depends(), data: OAuth2PasswordRequestForm = Depends(),
@ -21,35 +23,16 @@ def get_token(
email = data.username email = data.username
password = data.password password = data.password
user: UserInDB = query_user(email, session) user = authenticate_user(session, email, password)
if not user:
raise InvalidCredentialsException # you can also use your own HTTPException
elif not verify_password(password, user.password):
raise InvalidCredentialsException
access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(hours=2)) if not user:
return SnackResponse.success( raise HTTPException(
"User Successfully Logged In", status_code=status.HTTP_401_UNAUTHORIZED,
{"access_token": access_token, "token_type": "bearer"}, detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
) )
access_token = security.create_access_token(dict(sub=email), timedelta(hours=2))
@router.post("/token/long")
def get_long_token(
data: OAuth2PasswordRequestForm = Depends(),
session: Session = Depends(generate_session),
):
"""Get an Access Token for 1 day"""
email = data.username
password = data.password
user: UserInDB = query_user(email, session)
if not user:
raise InvalidCredentialsException # you can also use your own HTTPException
elif not verify_password(password, user.password):
raise InvalidCredentialsException
access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(days=1))
return SnackResponse.success( return SnackResponse.success(
"User Successfully Logged In", "User Successfully Logged In",
{"access_token": access_token, "token_type": "bearer"}, {"access_token": access_token, "token_type": "bearer"},
@ -57,7 +40,7 @@ def get_long_token(
@router.get("/refresh") @router.get("/refresh")
async def refresh_token(current_user: UserInDB = Depends(manager)): async def refresh_token(current_user: UserInDB = Depends(get_current_user)):
""" Use a valid token to get another token""" """ Use a valid token to get another token"""
access_token = manager.create_access_token(data=dict(sub=current_user.email), expires=timedelta(hours=1)) access_token = security.create_access_token(data=dict(sub=current_user.email), expires_delta=timedelta(hours=1))
return {"access_token": access_token, "token_type": "bearer"} return {"access_token": access_token, "token_type": "bearer"}

View file

@ -1,14 +1,14 @@
import shutil import shutil
from datetime import timedelta from datetime import timedelta
from os import access
from fastapi import APIRouter, Depends, File, UploadFile
from fastapi.responses import FileResponse
from mealie.core import security
from mealie.core.config import USER_DIR from mealie.core.config import USER_DIR
from mealie.core.security import get_password_hash, verify_password from mealie.core.security import get_password_hash, verify_password
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, UploadFile from mealie.routes.deps import get_current_user
from fastapi.responses import FileResponse
from mealie.routes.deps import manager
from mealie.schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut from mealie.schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -19,7 +19,7 @@ router = APIRouter(prefix="/api/users", tags=["Users"])
@router.post("", response_model=UserOut, status_code=201) @router.post("", response_model=UserOut, status_code=201)
async def create_user( async def create_user(
new_user: UserIn, new_user: UserIn,
current_user=Depends(manager), current_user=Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
@ -31,7 +31,7 @@ async def create_user(
@router.get("", response_model=list[UserOut]) @router.get("", response_model=list[UserOut])
async def get_all_users( async def get_all_users(
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
@ -43,7 +43,7 @@ async def get_all_users(
@router.get("/self", response_model=UserOut) @router.get("/self", response_model=UserOut)
async def get_logged_in_user( async def get_logged_in_user(
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
return current_user.dict() return current_user.dict()
@ -52,7 +52,7 @@ async def get_logged_in_user(
@router.get("/{id}", response_model=UserOut) @router.get("/{id}", response_model=UserOut)
async def get_user_by_id( async def get_user_by_id(
id: int, id: int,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
return db.users.get(session, id) return db.users.get(session, id)
@ -62,19 +62,21 @@ async def get_user_by_id(
async def update_user( async def update_user(
id: int, id: int,
new_data: UserBase, new_data: UserBase,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
access_token = None token = None
if current_user.id == id or current_user.admin: if current_user.id == id or current_user.admin:
updated_user: UserInDB = db.users.update(session, id, new_data.dict()) print("Current User")
email = updated_user.email db.users.update(session, id, new_data.dict())
if current_user.id == id: if current_user.id == id:
access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(hours=2)) print(new_data.email)
access_token = {"access_token": access_token, "token_type": "bearer"} access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2))
token = {"access_token": access_token, "token_type": "bearer"}
return SnackResponse.success("User Updated", access_token) print(SnackResponse.success("User Updated", token))
return SnackResponse.success("User Updated", token)
@router.get("/{id}/image") @router.get("/{id}/image")
@ -91,7 +93,7 @@ async def get_user_image(id: str):
async def update_user_image( async def update_user_image(
id: str, id: str,
profile_image: UploadFile = File(...), profile_image: UploadFile = File(...),
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
): ):
""" Updates a User Image """ """ Updates a User Image """
@ -119,7 +121,7 @@ async def update_user_image(
async def update_password( async def update_password(
id: int, id: int,
password_change: ChangePassword, password_change: ChangePassword,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Resets the User Password""" """ Resets the User Password"""
@ -138,7 +140,7 @@ async def update_password(
@router.delete("/{id}") @router.delete("/{id}")
async def delete_user( async def delete_user(
id: int, id: int,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Removes a user from the database. Must be the current user or a super user""" """ Removes a user from the database. Must be the current user or a super user"""

View file

@ -4,7 +4,7 @@ from mealie.core.security import get_password_hash
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from mealie.routes.deps import manager from mealie.routes.deps import get_current_user
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
from mealie.schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from mealie.schema.user import UserIn, UserInDB from mealie.schema.user import UserIn, UserInDB
@ -15,7 +15,7 @@ router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])
@router.get("", response_model=list[SignUpOut]) @router.get("", response_model=list[SignUpOut])
async def get_all_open_sign_ups( async def get_all_open_sign_ups(
current_user=Depends(manager), current_user=Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns a list of open sign up links """ """ Returns a list of open sign up links """
@ -28,7 +28,7 @@ async def get_all_open_sign_ups(
@router.post("", response_model=SignUpToken) @router.post("", response_model=SignUpToken)
async def create_user_sign_up_key( async def create_user_sign_up_key(
key_data: SignUpIn, key_data: SignUpIn,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Generates a Random Token that a new user can sign up with """ """ Generates a Random Token that a new user can sign up with """
@ -75,7 +75,7 @@ async def create_user_with_token(
@router.delete("/{token}") @router.delete("/{token}")
async def delete_token( async def delete_token(
token: str, token: str,
current_user: UserInDB = Depends(manager), current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Removed a token from the database """ """ Removed a token from the database """

10
mealie/schema/auth.py Normal file
View file

@ -0,0 +1,10 @@
from pydantic import BaseModel
from typing import Optional
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None

183
poetry.lock generated
View file

@ -197,6 +197,17 @@ category = "main"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*" python-versions = ">=2.6, !=3.0.*, !=3.1.*"
[[package]]
name = "ecdsa"
version = "0.14.1"
description = "ECDSA cryptographic signature library (pure python)"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[package.dependencies]
six = "*"
[[package]] [[package]]
name = "extruct" name = "extruct"
version = "0.12.0" version = "0.12.0"
@ -249,19 +260,6 @@ python-versions = ">=3.6"
pydantic = "*" pydantic = "*"
pyhumps = "*" pyhumps = "*"
[[package]]
name = "fastapi-login"
version = "1.5.3"
description = ""
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
fastapi = "*"
passlib = "*"
pyjwt = "*"
[[package]] [[package]]
name = "future" name = "future"
version = "0.18.2" version = "0.18.2"
@ -614,6 +612,14 @@ category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyasn1"
version = "0.4.8"
description = "ASN.1 types and codecs"
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "pycparser" name = "pycparser"
version = "2.20" version = "2.20"
@ -653,20 +659,6 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "pyjwt"
version = "2.0.1"
description = "JSON Web Token implementation in Python"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
crypto = ["cryptography (>=3.3.1,<4.0.0)"]
dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
[[package]] [[package]]
name = "pylint" name = "pylint"
version = "2.7.2" version = "2.7.2"
@ -763,6 +755,25 @@ python-versions = "*"
[package.extras] [package.extras]
cli = ["click (>=5.0)"] cli = ["click (>=5.0)"]
[[package]]
name = "python-jose"
version = "3.2.0"
description = "JOSE implementation in Python"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
ecdsa = "<0.15"
pyasn1 = "*"
rsa = "*"
six = "<2.0"
[package.extras]
cryptography = ["cryptography"]
pycrypto = ["pycrypto (>=2.6.0,<2.7.0)", "pyasn1"]
pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"]
[[package]] [[package]]
name = "python-multipart" name = "python-multipart"
version = "0.0.5" version = "0.0.5"
@ -860,6 +871,17 @@ urllib3 = ">=1.21.1,<1.27"
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
[[package]]
name = "rsa"
version = "4.7.2"
description = "Pure-Python RSA implementation"
category = "main"
optional = false
python-versions = ">=3.5, <4"
[package.dependencies]
pyasn1 = ">=0.1.3"
[[package]] [[package]]
name = "scrape-schema-recipe" name = "scrape-schema-recipe"
version = "0.1.3" version = "0.1.3"
@ -1103,7 +1125,7 @@ python-versions = "*"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "adf8ad8e07d1af5c231c936276b57be83dd534e0cf706042ddb26f6ff51c86ca" content-hash = "688326ef0f3bf3b2d2d515b941dbca379f26b08ae83afab66fa0ec95dc2c57ce"
[metadata.files] [metadata.files]
aiofiles = [ aiofiles = [
@ -1264,6 +1286,10 @@ decorator = [
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
] ]
ecdsa = [
{file = "ecdsa-0.14.1-py2.py3-none-any.whl", hash = "sha256:e108a5fe92c67639abae3260e43561af914e7fd0d27bae6d2ec1312ae7934dfe"},
{file = "ecdsa-0.14.1.tar.gz", hash = "sha256:64c613005f13efec6541bb0a33290d0d03c27abab5f15fbab20fb0ee162bdd8e"},
]
extruct = [ extruct = [
{file = "extruct-0.12.0-py2.py3-none-any.whl", hash = "sha256:42c6c9f50b00aa6c17b5c26b5f1b3a337ebc27b427fafc3714f34ce3bbb16c2f"}, {file = "extruct-0.12.0-py2.py3-none-any.whl", hash = "sha256:42c6c9f50b00aa6c17b5c26b5f1b3a337ebc27b427fafc3714f34ce3bbb16c2f"},
{file = "extruct-0.12.0.tar.gz", hash = "sha256:d4a68bb79d1b85ff36d603a42c2666888bb480191a399a659d9daaf735358276"}, {file = "extruct-0.12.0.tar.gz", hash = "sha256:d4a68bb79d1b85ff36d603a42c2666888bb480191a399a659d9daaf735358276"},
@ -1275,10 +1301,6 @@ fastapi = [
fastapi-camelcase = [ fastapi-camelcase = [
{file = "fastapi_camelcase-1.0.2.tar.gz", hash = "sha256:1d852149f6c9e5bb8002839a1e024050af917f1944b9d108d56468d64c6da279"}, {file = "fastapi_camelcase-1.0.2.tar.gz", hash = "sha256:1d852149f6c9e5bb8002839a1e024050af917f1944b9d108d56468d64c6da279"},
] ]
fastapi-login = [
{file = "fastapi-login-1.5.3.tar.gz", hash = "sha256:8e8ef710f1b7107e81d00e205779e73e17be35d5a91d11685ff72f323898e93b"},
{file = "fastapi_login-1.5.3-py3-none-any.whl", hash = "sha256:6c83b74bdb45c34ec0aab22000a7951df96c5d011f02a99a46ca4b2be6b1263c"},
]
future = [ future = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
] ]
@ -1414,39 +1436,43 @@ lunr = [
{file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"},
] ]
lxml = [ lxml = [
{file = "lxml-4.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"}, {file = "lxml-4.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f"},
{file = "lxml-4.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"}, {file = "lxml-4.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d"},
{file = "lxml-4.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"}, {file = "lxml-4.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2"},
{file = "lxml-4.6.2-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"}, {file = "lxml-4.6.2-cp27-cp27m-win32.whl", hash = "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"},
{file = "lxml-4.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"}, {file = "lxml-4.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e"},
{file = "lxml-4.6.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"}, {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2"},
{file = "lxml-4.6.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"}, {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe"},
{file = "lxml-4.6.2-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"}, {file = "lxml-4.6.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388"},
{file = "lxml-4.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"}, {file = "lxml-4.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80"},
{file = "lxml-4.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"}, {file = "lxml-4.6.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b"},
{file = "lxml-4.6.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"}, {file = "lxml-4.6.2-cp35-cp35m-win32.whl", hash = "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8"},
{file = "lxml-4.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"}, {file = "lxml-4.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780"},
{file = "lxml-4.6.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"}, {file = "lxml-4.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af"},
{file = "lxml-4.6.2-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"}, {file = "lxml-4.6.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37"},
{file = "lxml-4.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"}, {file = "lxml-4.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98"},
{file = "lxml-4.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"}, {file = "lxml-4.6.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d"},
{file = "lxml-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"}, {file = "lxml-4.6.2-cp36-cp36m-win32.whl", hash = "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf"},
{file = "lxml-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"}, {file = "lxml-4.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939"},
{file = "lxml-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"}, {file = "lxml-4.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e"},
{file = "lxml-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"}, {file = "lxml-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711"},
{file = "lxml-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"}, {file = "lxml-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089"},
{file = "lxml-4.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"}, {file = "lxml-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01"},
{file = "lxml-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"}, {file = "lxml-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2"},
{file = "lxml-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"}, {file = "lxml-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc"},
{file = "lxml-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"}, {file = "lxml-4.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d"},
{file = "lxml-4.6.2-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"}, {file = "lxml-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3"},
{file = "lxml-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"}, {file = "lxml-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644"},
{file = "lxml-4.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"}, {file = "lxml-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308"},
{file = "lxml-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"}, {file = "lxml-4.6.2-cp38-cp38-win32.whl", hash = "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505"},
{file = "lxml-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"}, {file = "lxml-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a"},
{file = "lxml-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"}, {file = "lxml-4.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931"},
{file = "lxml-4.6.2-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"}, {file = "lxml-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03"},
{file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"}, {file = "lxml-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5"},
{file = "lxml-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a"},
{file = "lxml-4.6.2-cp39-cp39-win32.whl", hash = "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75"},
{file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf"},
{file = "lxml-4.6.2.tar.gz", hash = "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc"},
] ]
markdown = [ markdown = [
{file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"},
@ -1552,6 +1578,21 @@ py = [
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
] ]
pyasn1 = [
{file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
{file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
{file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"},
{file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"},
{file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
{file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"},
{file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"},
{file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"},
{file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"},
{file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"},
{file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"},
{file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"},
{file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
]
pycparser = [ pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
@ -1588,10 +1629,6 @@ pyhumps = [
{file = "pyhumps-1.6.1-py3-none-any.whl", hash = "sha256:58b367b73c57b64e32d211dc769addabd68ff6db07ce64b2e6565f7d5a12291f"}, {file = "pyhumps-1.6.1-py3-none-any.whl", hash = "sha256:58b367b73c57b64e32d211dc769addabd68ff6db07ce64b2e6565f7d5a12291f"},
{file = "pyhumps-1.6.1.tar.gz", hash = "sha256:01612603c5ad73a407299d806d30708a3935052276fdd93776953bccc0724e0a"}, {file = "pyhumps-1.6.1.tar.gz", hash = "sha256:01612603c5ad73a407299d806d30708a3935052276fdd93776953bccc0724e0a"},
] ]
pyjwt = [
{file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"},
{file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"},
]
pylint = [ pylint = [
{file = "pylint-2.7.2-py3-none-any.whl", hash = "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"}, {file = "pylint-2.7.2-py3-none-any.whl", hash = "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"},
{file = "pylint-2.7.2.tar.gz", hash = "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a"}, {file = "pylint-2.7.2.tar.gz", hash = "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a"},
@ -1620,6 +1657,10 @@ python-dotenv = [
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"}, {file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"}, {file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
] ]
python-jose = [
{file = "python-jose-3.2.0.tar.gz", hash = "sha256:4e4192402e100b5fb09de5a8ea6bcc39c36ad4526341c123d401e2561720335b"},
{file = "python_jose-3.2.0-py2.py3-none-any.whl", hash = "sha256:67d7dfff599df676b04a996520d9be90d6cdb7e6dd10b4c7cacc0c3e2e92f2be"},
]
python-multipart = [ python-multipart = [
{file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
] ]
@ -1707,6 +1748,10 @@ requests = [
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
] ]
rsa = [
{file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"},
{file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"},
]
scrape-schema-recipe = [ scrape-schema-recipe = [
{file = "scrape-schema-recipe-0.1.3.tar.gz", hash = "sha256:f5c9bdbdb254ac4ca008e4233afd38308cf9877fc9399643d03087df0d950aea"}, {file = "scrape-schema-recipe-0.1.3.tar.gz", hash = "sha256:f5c9bdbdb254ac4ca008e4233afd38308cf9877fc9399643d03087df0d950aea"},
{file = "scrape_schema_recipe-0.1.3-py2.py3-none-any.whl", hash = "sha256:7a505d7cd94091ffdfcbac0fad21dd583cceee2d9c7ea12366e8fefac8b4da82"}, {file = "scrape_schema_recipe-0.1.3-py2.py3-none-any.whl", hash = "sha256:7a505d7cd94091ffdfcbac0fad21dd583cceee2d9c7ea12366e8fefac8b4da82"},

View file

@ -25,9 +25,10 @@ PyYAML = "^5.3.1"
extruct = "^0.12.0" extruct = "^0.12.0"
scrape-schema-recipe = "^0.1.3" scrape-schema-recipe = "^0.1.3"
python-multipart = "^0.0.5" python-multipart = "^0.0.5"
fastapi-login = "^1.5.3"
fastapi-camelcase = "^1.0.2" fastapi-camelcase = "^1.0.2"
bcrypt = "^3.2.0" bcrypt = "^3.2.0"
python-jose = "^3.2.0"
passlib = "^1.7.4"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pylint = "^2.6.0" pylint = "^2.6.0"

View file

@ -1,12 +1,16 @@
import json import json
import requests import requests
from fastapi.testclient import TestClient
from mealie.app import app from mealie.app import app
from mealie.core.config import SQLITE_DIR from mealie.core.config import SQLITE_DIR
from mealie.db.database import db
from mealie.db.db_setup import generate_session, sql_global_init from mealie.db.db_setup import generate_session, sql_global_init
from mealie.db.init_db import init_db from mealie.db.init_db import init_db
from fastapi.testclient import TestClient from mealie.routes.deps import get_current_user
from mealie.schema.user import UserInDB
from pytest import fixture from pytest import fixture
from sqlalchemy.orm.session import Session
from tests.test_config import TEST_DATA from tests.test_config import TEST_DATA
@ -42,7 +46,7 @@ def test_image():
return TEST_DATA.joinpath("test_image.jpg") return TEST_DATA.joinpath("test_image.jpg")
@fixture(scope="function") @fixture(scope="session")
def token(api_client: requests): def token(api_client: requests):
form_data = {"username": "changeme@email.com", "password": "MyPassword"} form_data = {"username": "changeme@email.com", "password": "MyPassword"}
response = api_client.post(TOKEN_URL, form_data) response = api_client.post(TOKEN_URL, form_data)

View file

@ -23,9 +23,6 @@ def get_meal_plan_template(first=None, second=None):
} }
## Meal Routes
@pytest.fixture @pytest.fixture
def slug_1(api_client): def slug_1(api_client):
# Slug 1 # Slug 1
@ -50,9 +47,7 @@ def slug_2(api_client):
def test_create_mealplan(api_client, slug_1, slug_2, token): def test_create_mealplan(api_client, slug_1, slug_2, token):
meal_plan = get_meal_plan_template() meal_plan = get_meal_plan_template(slug_1, slug_2)
meal_plan["meals"][0]["slug"] = slug_1
meal_plan["meals"][1]["slug"] = slug_2
response = api_client.post(MEALPLAN_CREATE, json=meal_plan, headers=token) response = api_client.post(MEALPLAN_CREATE, json=meal_plan, headers=token)
assert response.status_code == 200 assert response.status_code == 200

View file

@ -17,14 +17,17 @@ def new_user():
return {"id": 2, "fullName": "My New User", "email": "newuser@email.com", "group": "Home", "admin": False} return {"id": 2, "fullName": "My New User", "email": "newuser@email.com", "group": "Home", "admin": False}
def test_superuser_login(api_client: requests): def test_superuser_login(api_client: requests, token):
form_data = {"username": "changeme@email.com", "password": "MyPassword"} form_data = {"username": "changeme@email.com", "password": "MyPassword"}
response = api_client.post(TOKEN_URL, form_data) response = api_client.post(TOKEN_URL, form_data)
assert response.status_code == 200 assert response.status_code == 200
token = json.loads(response.text).get("access_token") new_token = json.loads(response.text).get("access_token")
return {"Authorization": f"Bearer {token}"} response = api_client.get("/api/users/self", headers=token)
assert response.status_code == 200
return {"Authorization": f"Bearer {new_token}"}
def test_init_superuser(api_client: requests, token, default_user): def test_init_superuser(api_client: requests, token, default_user):
@ -59,10 +62,11 @@ def test_get_all_users(api_client: requests, token, new_user, default_user):
def test_update_user(api_client: requests, token): def test_update_user(api_client: requests, token):
update_data = {"id": 1, "fullName": "Updated Name", "email": "updated@email.com", "group": "Home", "admin": True} update_data = {"id": 1, "fullName": "Updated Name", "email": "changeme@email.com", "group": "Home", "admin": True}
response = api_client.put(f"{BASE}/1", headers=token, json=update_data) response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
assert response.status_code == 200 assert response.status_code == 200
print(response.text)
assert json.loads(response.text).get("access_token") assert json.loads(response.text).get("access_token")

View file

@ -4,12 +4,7 @@ import pytest
from mealie.core.config import TEMP_DIR from mealie.core.config import TEMP_DIR
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from mealie.services.image_services import IMG_DIR from mealie.services.image_services import IMG_DIR
from mealie.services.migrations.nextcloud import ( from mealie.services.migrations.nextcloud import cleanup, import_recipes, prep, process_selection
cleanup,
import_recipes,
prep,
process_selection,
)
from tests.test_config import TEST_NEXTCLOUD_DIR from tests.test_config import TEST_NEXTCLOUD_DIR
CWD = Path(__file__).parent CWD = Path(__file__).parent