-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
120 lines (105 loc) · 4.45 KB
/
Copy pathapp.py
File metadata and controls
120 lines (105 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from flask import Flask, request, jsonify
from database.database import get_connection
from database.models import Expense
from database.repositories.expense_repo import ExpenseRepo
from database.repositories.user_repo import UserRepo
from exceptions import AppError, ValidationError, NotFoundError
from services import user_service, expense_service
from services.auth_service import login, get_current_user_id
from services.expense_service import add_expense
from utils.date_filter import filter_date
app = Flask(__name__)
def _require_json_object():
data = request.get_json(silent=True)
if not isinstance(data, dict):
raise ValidationError("Request body must be a JSON object.")
return data
def _require_fields(data: dict, fields: tuple[str, ...]) -> None:
missing = [field for field in fields if field not in data]
if missing:
raise ValidationError({field: "This field is required." for field in missing})
@app.errorhandler(AppError)
def handle_app_error(e: AppError):
"""Catches ANY AppError raised anywhere in the app and formats it."""
return jsonify({"error": e.errors}), e.status_code
@app.route("/signup", methods=["POST"])
def signup_route():
data = _require_json_object()
repo = UserRepo(get_connection())
user = user_service.signup(
repo,
username=data.get("username"),
email=data.get("email"),
firstname=data.get("first_name"),
lastname=data.get("last_name"),
plain_password=data.get("password"),
)
return jsonify(user.to_public_dict()), 201
@app.route("/login", methods=["POST"])
def login_route():
data = _require_json_object()
repo = UserRepo(get_connection())
token = login(repo, data.get("identifier"), data.get("password"))
return jsonify({"token": token}), 200
@app.route ("/expenses", methods=["GET"])
def get_expenses_route():
auth_header = request.headers.get("Authorization")
user_id = get_current_user_id(auth_header)
filter_type = request.args.get("filter")
start_date, end_date = None, None
if filter_type is not None:
start_date, end_date = filter_date(request, filter_type)
limit_str = request.args.get("limit")
limit = None
if limit_str is not None:
try:
limit = int(limit_str)
except ValueError:
raise ValidationError("limit must be a valid integer.")
if limit < 1:
raise ValidationError("limit must be greater than 0.")
repo = ExpenseRepo(get_connection())
expenses = repo.get_expenses(user_id, start_date, end_date, limit)
return jsonify([expense.to_dict() for expense in expenses]), 200
@app.route ("/expenses", methods=["POST"])
def create_expense_route():
auth_header = request.headers.get("Authorization")
user_id = get_current_user_id(auth_header)
data = _require_json_object()
_require_fields(data, ("cost", "category_id"))
repo = ExpenseRepo(get_connection())
expense = add_expense(
repo=repo,
cost = data.get("cost"),
description = data.get("description"),
category_id= data.get("category_id"),
user_id = user_id
)
return jsonify(expense.to_dict()), 201
@app.route ("/expenses/<int:expense_id>", methods=["DELETE"])
def delete_expense_route(expense_id):
auth_header = request.headers.get("Authorization")
user_id = get_current_user_id(auth_header)
repo = ExpenseRepo(get_connection())
expense_service.delete_expense(repo, user_id, expense_id)
return "", 204
@app.route("/expenses/<int:expense_id>", methods=["PUT"])
def update_expense_route(expense_id):
auth_header = request.headers.get("Authorization")
user_id = get_current_user_id(auth_header)
data = _require_json_object()
repo = ExpenseRepo(get_connection())
existing_expense = None
if "cost" not in data or "category_id" not in data or "description" not in data:
existing_expense = repo.get_expense_by_id(expense_id)
if existing_expense is None:
raise NotFoundError("Expense doesn't exist.")
updated_expense = Expense(
id=expense_id,
user_id=user_id,
cost=data["cost"] if "cost" in data else existing_expense.cost,
description=data["description"] if "description" in data else existing_expense.description,
category_id=data["category_id"] if "category_id" in data else existing_expense.category_id,
)
result = expense_service.update_expense(repo, updated_expense)
return jsonify(result.to_dict()), 200