backend changes

This commit is contained in:
Martin Bauer 2021-08-29 17:57:25 +02:00
parent e5a5e141a9
commit 0d34959e08
9 changed files with 162 additions and 128 deletions

31
backend/requirements.txt Normal file
View File

@ -0,0 +1,31 @@
aiosqlite==0.17.0
alembic==1.6.5
asgiref==3.4.1
bcrypt==3.2.0
cffi==1.14.6
click==8.0.1
databases==0.4.3
Deprecated==1.2.12
dnspython==2.1.0
email-validator==1.1.3
fastapi==0.68.0
fastapi-users==6.1.2
h11==0.12.0
idna==3.2
makefun==1.11.3
Mako==1.1.4
MarkupSafe==2.0.1
ormar==0.10.16
passlib==1.7.4
pycparser==2.20
pydantic==1.8.2
PyJWT==2.1.0
python-dateutil==2.8.2
python-editor==1.0.4
python-multipart==0.0.5
six==1.16.0
SQLAlchemy==1.3.23
starlette==0.14.2
typing-extensions==3.7.4.3
uvicorn==0.15.0
wrapt==1.12.1

View File

@ -1,19 +1,20 @@
from .db import database, engine, Base, DbSession
from .users import add_user_routers, User
from fastapi import FastAPI
from users import add_user_routers, User
from db import database, engine, Base, DbSession
from .routes import create_api_router
from starlette.requests import Request
from routes import router as api_router
import models
def get_app() -> FastAPI:
def get_app(database) -> FastAPI:
application = FastAPI(title="swimtracker", debug=True, version="0.1")
application.include_router(api_router)
add_user_routers(application)
fastapi_users = add_user_routers(application, database)
current_user = fastapi_users.current_user(active=True, verified=True)
current_superuser = fastapi_users.current_user(active=True, superuser=True, verified=True)
application.include_router(create_api_router(current_user))
return application
app = get_app()
app = get_app(database)
@app.middleware("http")

View File

@ -1,7 +1,6 @@
import databases
import sqlalchemy
from starlette import requests
from config import DATABASE_URL
from .config import DATABASE_URL
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
from sqlalchemy.orm import sessionmaker
from starlette.requests import Request

View File

@ -1,6 +1,5 @@
from db import Base
from .db import Base
from sqlalchemy import Column, Integer, Index, LargeBinary, ForeignKey, and_, or_
from typing import Tuple
class Session(Base):

View File

@ -1,24 +1,25 @@
import base64
from . import models
from . import schemas
from .db import get_db
from fastapi import APIRouter, Depends, HTTPException
from typing import List
import schemas
from users import User, UserDB, UserTable, current_user
from db import get_db
import models
from sqlalchemy.orm import Session as DbSession, lazyload
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import IntegrityError
from fastapi import status
from sqlalchemy.sql import select
router = APIRouter()
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session as DbSession
from sqlalchemy.orm.exc import NoResultFound
from typing import List
from .users import User, UserTable
import base64
@router.post("/sessions",
def create_api_router(current_user):
router = APIRouter()
@router.post("/sessions",
response_model=schemas.Session,
tags=["sessions"],
status_code=status.HTTP_201_CREATED)
def create_session(session: schemas.SessionBase,
def create_session(session: schemas.SessionBase,
db: DbSession = Depends(get_db),
user: User = Depends(current_user)):
session_props = session.dict()
@ -30,8 +31,8 @@ def create_session(session: schemas.SessionBase,
return db_obj
@router.get("/sessions", response_model=List[schemas.Session], tags=["sessions"])
def list_sessions(skip=0,
@router.get("/sessions", response_model=List[schemas.Session], tags=["sessions"])
def list_sessions(skip=0,
limit=100,
db: DbSession = Depends(get_db),
user: User = Depends(current_user)):
@ -39,8 +40,8 @@ def list_sessions(skip=0,
models.Session.start_time.desc()).offset(skip).limit(limit).all()
@router.post("/friends/request_friendship/{user_id}", tags=["friends"])
def create_friend_request(other_user_id: str,
@router.post("/friends/request_friendship/{user_id}", tags=["friends"])
def create_friend_request(other_user_id: str,
db: DbSession = Depends(get_db),
user: User = Depends(current_user)):
if models.Friendship.are_friends(db, other_user_id, user.id):
@ -63,8 +64,8 @@ def create_friend_request(other_user_id: str,
detail="Friend request already exists")
@router.post("/friends/accept_friendship/{user_id}", tags=["friends"])
def accept_friend_request(other_user_id: str,
@router.post("/friends/accept_friendship/{user_id}", tags=["friends"])
def accept_friend_request(other_user_id: str,
db: DbSession = Depends(get_db),
user: User = Depends(current_user)):
FR = models.FriendRequest
@ -80,13 +81,13 @@ def accept_friend_request(other_user_id: str,
return {"msg": "ok"}
@router.get("/friends", tags=["friends"], response_model=schemas.FriendsInfo)
def list_friends_info(db: DbSession = Depends(get_db), user: User = Depends(current_user)):
@router.get("/friends", tags=["friends"], response_model=schemas.FriendsInfo)
def list_friends_info(db: DbSession = Depends(get_db), user: User = Depends(current_user)):
user_obj = db.query(UserTable).filter(UserTable.id == user.id).one()
return schemas.FriendsInfo(incoming_requests=user_obj.friend_requests_in,
outgoing_requests=user_obj.friend_requests_out)
return router
# todo: remove friend requests
# todo: remove friendship
# todo: search user by email

View File

@ -20,9 +20,6 @@ class UserInfo(BaseModel):
id: UUID4
email: EmailStr
class Config:
orm_mode = True
class Session(SessionBase):
user: UUID4

View File

@ -1,12 +1,10 @@
from fastapi_users import FastAPIUsers, models
from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
from fastapi_users.authentication import JWTAuthentication
from config import JWT_SECRET
from .config import JWT_SECRET
from .db import Base
from fastapi import Request
from db import database, Base
from fastapi_users import FastAPIUsers, models
from fastapi_users.authentication import JWTAuthentication
from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
from sqlalchemy.orm import relationship, backref
from sqlalchemy import Integer, Column
from fastapi_users.models import BaseUser
class User(models.BaseUser):
@ -40,36 +38,30 @@ class UserTable(Base, SQLAlchemyBaseUserTable):
secondaryjoin=("UserTable.id == Friendship.friend_id"))
user_db = SQLAlchemyUserDatabase(UserDB, database, UserTable.__table__)
jwt_authentication = JWTAuthentication(secret=JWT_SECRET,
def add_user_routers(app, database):
user_db = SQLAlchemyUserDatabase(UserDB, database, UserTable.__table__)
jwt_authentication = JWTAuthentication(secret=JWT_SECRET,
lifetime_seconds=60 * 60 * 8,
tokenUrl="auth/jwt/login")
fastapi_users = FastAPIUsers(
fastapi_users = FastAPIUsers(
user_db,
[jwt_authentication],
User,
UserCreate,
UserUpdate,
UserDB,
)
current_user = fastapi_users.current_user(active=True, verified=True)
current_superuser = fastapi_users.current_user(active=True, superuser=True, verified=True)
)
def on_after_register(user: UserDB, request: Request):
def on_after_register(user: UserDB, request: Request):
print(f"User {user.id} has registered.")
def on_after_forgot_password(user: UserDB, token: str, request: Request):
def on_after_forgot_password(user: UserDB, token: str, request: Request):
print(f"User {user.id} has forgot their password. Reset token: {token}")
def after_verification_request(user: UserDB, token: str, request: Request):
def after_verification_request(user: UserDB, token: str, request: Request):
print(f"Verification requested for user {user.id}. Verification token: {token}")
def add_user_routers(app):
app.include_router(fastapi_users.get_auth_router(jwt_authentication),
prefix="/auth/jwt",
tags=["auth"])
@ -90,3 +82,4 @@ def add_user_routers(app):
tags=["auth"],
)
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])
return fastapi_users

View File

@ -2,8 +2,9 @@ import os
from typing import Any, Generator
import pytest
import databases
from src.db import Base, get_db
from src.main import app as _app
from src import get_app
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
@ -14,9 +15,7 @@ from sqlalchemy.orm import sessionmaker
# database engines
SQLALCHEMY_DATABASE_URL = os.getenv('TEST_DATABASE_URL', "sqlite://")
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@ -26,7 +25,9 @@ def app() -> Generator[FastAPI, Any, None]:
"""
Create a fresh database on each test case.
"""
print(list(Base.metadata.tables.keys()))
Base.metadata.create_all(engine) # Create the tables.
_app = get_app(databases.Database(SQLALCHEMY_DATABASE_URL))
yield _app
Base.metadata.drop_all(engine)
@ -61,7 +62,6 @@ def client(app: FastAPI, db_session: Session) -> Generator[TestClient, Any, None
Create a new FastAPI TestClient that uses the `db_session` fixture to override
the `get_db` dependency that is injected into routes.
"""
def _get_test_db():
try:
yield db_session

View File

@ -1,11 +1,24 @@
from backend.src.db import DbSession
from src.db import DbSession
from src.schemas import Session
from fastapi import FastAPI
from src.db import DbSession
from fastapi.testclient import TestClient
from src.users import UserCreate, User
from fastapi.encoders import jsonable_encoder
#----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
def test_session_create(app: FastAPI, db_session: DbSession, client: TestClient):
pass
# Tests to write
# - User Flow: register, verify, login
# - create, list, delete session
# - friendship: query user by mail, create friend request, accept
def test_register_user(app: FastAPI, db_session: DbSession, client: TestClient):
req_data = jsonable_encoder(UserCreate(email="test@abc.com", password="password"))
response = client.post("/auth/register", json=req_data)
print(response.json())
resp_user = User(**response.json())
assert response.status_code == 201
assert resp_user.is_active
assert not resp_user.is_superuser
assert not resp_user.is_verified