Skip to content

Commit

Permalink
project structure refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
wuttinanhi committed Sep 19, 2022
1 parent 4252396 commit 2cea217
Show file tree
Hide file tree
Showing 29 changed files with 157 additions and 81 deletions.
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
{
"configurations": [
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "main.py",
"FLASK_DEBUG": "1"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true,
"justMyCode": true
},
{
"name": "Docker: Python - Flask",
"type": "docker",
Expand Down
6 changes: 4 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
"editor.mouseWheelZoom": true,
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "autopep8",
"python.linting.lintOnSave": true,
"python.languageServer": "Pylance",
"[python]": {
"editor.wordBasedSuggestions": false,
"editor.defaultFormatter": "ms-python.python",
"editor.formatOnSave": true
"editor.formatOnSave": true,
},
"files.exclude": {
"**/.git": true,
Expand Down
9 changes: 9 additions & 0 deletions auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
auth module
"""

from auth.blueprint import blueprint as auth_blueprint
from auth.decorator import login_required
from auth.function import GetUser

__all__ = [auth_blueprint, login_required, GetUser]
23 changes: 12 additions & 11 deletions blueprints/auth.py → auth/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import os

from flask import Blueprint, request
from jwt_wrapper.service import JwtService
from marshmallow import Schema, ValidationError, fields, validate
from services.auth.decorator import login_required
from services.auth.function import GetUser
from services.jwt_wrapper.jwt_wrapper import JwtWrapper
from services.user.user import UserService
from user.service import UserService
from util.validate_request import ValidateRequest

bp = Blueprint("auth", __name__, url_prefix="/auth")
from auth.decorator import login_required
from auth.function import GetUser

blueprint = Blueprint("auth", __name__, url_prefix="/auth")


class LoginDto(Schema):
Expand All @@ -28,7 +29,7 @@ class RegisterDto(LoginDto):
pass


@bp.route('/login', methods=['POST'])
@blueprint.route('/login', methods=['POST'])
def login():
data = ValidateRequest(LoginDto, request)
check = UserService.login(data.email, data.password)
Expand All @@ -38,28 +39,28 @@ def login():
jwt = {"user_id": user.id}
token: str
if os.getenv("ENV") != "production":
token = JwtWrapper.encode(jwt, 86400) # 1 day
token = JwtService.encode(jwt, 86400) # 1 day
else:
token = JwtWrapper.encode(jwt, 60*5) # 5 minutes
token = JwtService.encode(jwt, 60*5) # 5 minutes
return {'token': token}, 200
return {"message": "Invalid login!"}, 401


@bp.route('/register', methods=['POST'])
@blueprint.route('/register', methods=['POST'])
def register():
data = ValidateRequest(LoginDto, request)
UserService.register(data.email, data.password)
return {'message': "Successfully registered"}, 201


@bp.route('/user', methods=['GET'])
@blueprint.route('/user', methods=['GET'])
@login_required
def user():
user = GetUser()
return user.email


@bp.errorhandler(Exception)
@blueprint.errorhandler(Exception)
def error_handle(err: Exception):
if err.__class__ is ValidationError:
return str(err), 400
Expand Down
8 changes: 4 additions & 4 deletions services/auth/decorator.py → auth/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from functools import wraps

from flask import request
from services.jwt_wrapper.jwt_wrapper import JwtWrapper
from services.user.user import UserService
from jwt_wrapper.service import JwtService
from user.service import UserService


def login_required(f):
Expand All @@ -17,10 +17,10 @@ def decorated_function(*args, **kwargs):
return {"message": "Unauthorized!"}, 401

jwt_token = auth_header.split("Bearer ")[1]
check = JwtWrapper.validate(jwt_token)
check = JwtService.validate(jwt_token)

if check:
decoded = JwtWrapper.decode(jwt_token)
decoded = JwtService.decode(jwt_token)
user = UserService.find_by_id(decoded["user_id"])
request.user = user
return f(*args, **kwargs)
Expand Down
2 changes: 1 addition & 1 deletion services/auth/function.py → auth/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

from flask import request
from services.user.user import User
from user.model import User


def GetUser() -> User:
Expand Down
7 changes: 7 additions & 0 deletions bcrypt_wrapper/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
bcrypt wrapper module
"""

from bcrypt_wrapper.service import BcryptService

__all__ = [BcryptService]
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
import bcrypt


class BcryptWrapper:
class BcryptService:
__salt_round = os.getenv("BCRYPT_SALT") or 10
__salt = bcrypt.gensalt(rounds=__salt_round)

@staticmethod
def hash(value: str):
return bcrypt.hashpw(value.encode("utf-8"), BcryptWrapper.__salt)
return bcrypt.hashpw(value.encode("utf-8"), BcryptService.__salt)

@staticmethod
def validate(value: str, hash: str):
Expand Down
Empty file removed blueprints/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions car/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
car module
"""

from car.blueprint import blueprint as car_blueprint
from car.model import Car
from car.service import CarService

__all__ = [Car, car_blueprint, CarService]
21 changes: 11 additions & 10 deletions blueprints/car.py → car/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
'''


from http.client import BAD_REQUEST, CREATED, FORBIDDEN, NOT_FOUND, OK
from xmlrpc.client import INTERNAL_ERROR
from http.client import (BAD_REQUEST, CREATED, FORBIDDEN,
INTERNAL_SERVER_ERROR, NOT_FOUND, OK)

from auth.decorator import login_required
from auth.function import GetUser
from flask import Blueprint, request
from marshmallow import Schema, ValidationError, fields, validate
from services.auth.decorator import login_required
from services.auth.function import GetUser
from services.car.car import CarService
from util.validate_request import ValidateRequest

bp = Blueprint("car", __name__, url_prefix="/car")
from car.service import CarService

blueprint = Blueprint("car", __name__, url_prefix="/car")


class CarAddDto(Schema):
Expand All @@ -31,7 +32,7 @@ class CarDeleteDto(Schema):
car_id = fields.Int(required=True)


@bp.route('/add', methods=['POST'])
@blueprint.route('/add', methods=['POST'])
@login_required
def add_car():
user = GetUser()
Expand All @@ -40,7 +41,7 @@ def add_car():
return {"message": "Car add."}, CREATED


@bp.route('/remove', methods=['DELETE'])
@blueprint.route('/remove', methods=['DELETE'])
@login_required
def remove_car():
user = GetUser()
Expand All @@ -55,8 +56,8 @@ def remove_car():
return {"message": "Car not found!"}, NOT_FOUND


@bp.errorhandler(Exception)
@blueprint.errorhandler(Exception)
def error_handle(err: Exception):
if err.__class__ is ValidationError:
return str(err), BAD_REQUEST
return {'message': "Internal server exception!", "error": str(err)}, INTERNAL_ERROR
return {'message': "Internal server exception!"}, INTERNAL_SERVER_ERROR
19 changes: 19 additions & 0 deletions car/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from database import Base
from sqlalchemy import Column, Integer, String


class Car(Base):
__tablename__ = 'cars'

id = Column(Integer, primary_key=True)
car_license_plate = Column(String(100), unique=True)
car_owner_id = Column(Integer())
car_type = Column(String(100))

def __init__(self, car_license_plate: str, car_owner_id: str, car_type: str):
self.car_license_plate = car_license_plate
self.car_owner_id = car_owner_id
self.car_type = car_type

def __repr__(self):
return f'<Car {self.id}>'
22 changes: 3 additions & 19 deletions services/car/car.py → car/service.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
from services.database import Base, db_session
from services.user.user import User
from sqlalchemy import Column, Integer, String
from database.database import db_session
from sqlalchemy.exc import IntegrityError
from user.model import User


class Car(Base):
__tablename__ = 'cars'

id = Column(Integer, primary_key=True)
car_license_plate = Column(String(100), unique=True)
car_owner_id = Column(Integer())
car_type = Column(String(100))

def __init__(self, car_license_plate: str, car_owner_id: str, car_type: str):
self.car_license_plate = car_license_plate
self.car_owner_id = car_owner_id
self.car_type = car_type

def __repr__(self):
return f'<Car {self.id}>'
from car.model import Car


class CarService:
Expand Down
5 changes: 5 additions & 0 deletions database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""
database module
"""

from database.database import *
File renamed without changes.
7 changes: 7 additions & 0 deletions jwt_wrapper/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
jwt wrapper module
"""

from jwt_wrapper.service import JwtService

__all__ = [JwtService]
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
import jwt


class JwtWrapper:
class JwtService:
__jwt_secret = os.getenv("BCRYPT_SALT") or "dev-secret"

def encode(value: any, duration: int):
value["iat"] = datetime.now(tz=timezone.utc)
value["exp"] = value["iat"] + timedelta(seconds=duration)
return jwt.encode(value, JwtWrapper.__jwt_secret, "HS256")
return jwt.encode(value, JwtService.__jwt_secret, "HS256")

def validate(value: str):
try:
jwt.decode(value, JwtWrapper.__jwt_secret, algorithms=["HS256"])
jwt.decode(value, JwtService.__jwt_secret, algorithms=["HS256"])
return True
except:
return False

def decode(value: str):
return jwt.decode(value, JwtWrapper.__jwt_secret, algorithms=["HS256"])
return jwt.decode(value, JwtService.__jwt_secret, algorithms=["HS256"])
10 changes: 5 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
from dotenv import load_dotenv
from flask import Flask

import blueprints.auth as auth
import blueprints.car as car
from services.database import db_session, init_db
from auth import auth_blueprint
from car import car_blueprint
from database.database import db_session, init_db

load_dotenv()


app = Flask(__name__)


app.register_blueprint(auth.bp)
app.register_blueprint(car.bp)
app.register_blueprint(auth_blueprint)
app.register_blueprint(car_blueprint)


@app.teardown_appcontext
Expand Down
6 changes: 3 additions & 3 deletions mock/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@


if __name__ == '__main__':
from services.car.car import CarService
from services.database import Base, db_session, engine, init_db
from services.user.user import UserService
from car.service import CarService
from database.database import Base, db_session, engine, init_db
from user.service import UserService

# drop all
Base.metadata.drop_all(bind=engine)
Expand Down
Empty file removed services/__init__.py
Empty file.
Empty file removed services/auth/__init__.py
Empty file.
Empty file.
Empty file removed services/car/__init__.py
Empty file.
Empty file removed services/jwt_wrapper/__init__.py
Empty file.
Empty file removed services/user/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion thunder-tests/thunderclient.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"method": "DELETE",
"sortNum": 20000,
"created": "2022-09-19T13:00:30.444Z",
"modified": "2022-09-19T13:13:57.167Z",
"modified": "2022-09-19T18:45:43.093Z",
"headers": [],
"params": [],
"body": {
Expand Down
8 changes: 8 additions & 0 deletions user/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
user module
"""

from user.model import User
from user.service import UserService

__all__ = [User, UserService]
21 changes: 21 additions & 0 deletions user/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
user model
"""

from database.database import Base
from sqlalchemy import Column, Integer, String


class User(Base):
__tablename__ = 'users'

id = Column(Integer, primary_key=True)
email = Column(String(100), unique=True)
password = Column(String(100))

def __init__(self, email: str, password: str):
self.email = email
self.password = password

def __repr__(self):
return f'<User {self.email!r}>'
Loading

0 comments on commit 2cea217

Please sign in to comment.