8000 Merge pull request #2 from code4mk/feature/implement-pundra · code4mk/fastapi-template@ab34ef0 · GitHub
[go: up one dir, main page]

Skip to content

Commit ab34ef0

Browse files
authored
Merge pull request #2 from code4mk/feature/implement-pundra
Feature/implement pundra
2 parents 0b75ef4 + 774808a commit ab34ef0

35 files changed

+700
-309
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,5 @@ docker-compose.override.yml
6363

6464
# Documentation
6565
docs/_build/
66+
67+
app/tests/

Pipfile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ wheel = "*"
1717
beautifulsoup4 = "*"
1818
premailer = "*"
1919
celery-redbeat = "*"
20-
fastapi-pundra = "==0.0.7"
20+
fastapi-pundra = "==0.0.11"
2121

2222
[dev-packages]
23+
pytest = "*"
24+
pytest-asyncio = "*"
25+
pytest-cov = "*"
26+
factory-boy = "*"
27+
httpx = "*"
28+
ruff = "*"
2329

2430
[requires]
2531
python_version = "3.12"

Pipfile.lock

Lines changed: 321 additions & 82 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

alembic/env.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from logging.config import fileConfig
2-
from sqlalchemy import engine_from_config
3-
from sqlalchemy import pool
4-
from alembic import context
5-
from dotenv import load_dotenv
6-
from pathlib import Path
7-
import sys
8-
import os
91
import importlib
2+
import os
103
import pkgutil
4+
import sys
5+
from logging.config import fileConfig
6+
from pathlib import Path
7+
8+
from dotenv import load_dotenv
9+
from sqlalchemy import engine_from_config, pool
10+
11+
from alembic import context
1112

1213
# Add the project root directory to the Python path
1314
project_root = str(Path(__file__).parents[1])
@@ -16,7 +17,6 @@
1617
# After adding the project root to the Python path, we can import the Base class
1718
from app.database.database import Base
1819

19-
2020
# Load environment variables
2121
load_dotenv()
2222

@@ -54,16 +54,20 @@
5454
# my_important_option = config.get_main_option("my_important_option")
5555
# ... etc.
5656

57+
5758
def import_all_models():
58-
models_pkg = importlib.import_module('app.models')
59+
models_pkg = importlib.import_module("app.models")
5960
for _, name, _ in pkgutil.iter_modules(models_pkg.__path__):
60-
importlib.import_module(f'app.models.{name}')
61+
importlib.import_module(f"app.models.{name}")
62+
6163

6264
# Import all models automatically
6365
import_all_models()
6466

67+
6568
def run_migrations_offline() -> None:
66-
"""Run migrations in 'offline' mode.
69+
"""
70+
Run migrations in 'offline' mode.
6771
6872
This configures the context with just a URL
6973
and not an Engine, though an Engine is acceptable
@@ -87,7 +91,8 @@ def run_migrations_offline() -> None:
8791

8892

8993
def run_migrations_online() -> None:
90-
"""Run migrations in 'online' mode.
94+
"""
95+
Run migrations in 'online' mode.
9196
9297
In this scenario we need to create an Engine
9398
and associate a connection with the context.
@@ -100,9 +105,7 @@ def run_migrations_online() -> None:
100105
)
101106

102107
with connectable.connect() as connection:
103-
context.configure(
104-
connection=connection, target_metadata=target_metadata
105-
)
108+
context.configure(connection=connection, target_metadata=target_metadata)
106109

107110
with context.begin_transaction():
108111
context.run_migrations()
@@ -111,4 +114,4 @@ def run_migrations_online() -> None:
111114
if context.is_offline_mode():
112115
run_migrations_offline()
113116
else:
114-
run_migrations_online()
117+
run_migrations_online()

app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""App module."""

app/api/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""API package for the application."""
2+
3+
# Empty file to make this directory a Python package

app/api/health.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
# Create a api router
55
router = APIRouter()
66

7+
78
# Health check route
89
@router.get("/")
9-
async def health_check():
10-
data = {"status": "ok"}
11-
return JSONResponse(content=data, status_code=status.HTTP_200_OK)
10+
async def health_check() -> JSONResponse:
11+
"""Health check route."""
12+
data = {"status": "ok"}
13+
return JSONResponse(content=data, status_code=status.HTTP_200_OK)

app/api/root_index.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
from fastapi import APIRouter, status, Request
1+
from fastapi import APIRouter, status
22
from fastapi.responses import JSONResponse
33

44
# Create a api router
55
router = APIRouter()
66

7+
78
# root index
89
@router.get("/")
9-
async def root_index(request: Request):
10+
async def root_index() -> JSONResponse:
11+
"""Root endpoint that confirms API is running."""
1012
data = {
11-
'message': 'fastapi is running....'
13+
"message": "FastAPI is running...",
1214
}
1315
return JSONResponse(content=data, status_code=status.HTTP_200_OK)
1416

17+
1518
@router.get("/the-index")
16-
async def the_index(request: Request):
19+
async def the_index() -> JSONResponse:
20+
"""Alternative index endpoint that confirms API is running."""
1721
data = {
18-
'message': 'fastapi is running....'
22+
"message": "FastAPI is running...",
1923
}
2024
return JSONResponse(content=data, status_code=status.HTTP_200_OK)
21-

app/api/v1/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""API version 1 package for the application."""
2+
3+
# Empty file to make this directory a Python package

app/api/v1/user.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,65 @@
1-
from fastapi import APIRouter, Request, status, BackgroundTasks
2-
from fastapi.responses import JSONResponse
1+
from __future__ import annotations
2+
3+
from fastapi import APIRouter, BackgroundTasks, Request, status
34
from fastapi.encoders import jsonable_encoder
4-
from app.services.user_service import UserService
5-
from app.schemas.user_schema import UserCreateSchema, UserUpdateSchema
6-
from fastapi_pundra.rest.validation import dto
5+
from fastapi.responses import JSONResponse
76
from fastapi_pundra.rest.helpers import the_query
7+
from fastapi_pundra.rest.validation import dto
88

9+
from app.schemas.user_schema import UserCreateSchema, UserUpdateSchema
10+
from app.services.user_service import UserService
911

1012
# Create a api router
1113
router = APIRouter()
1214

1315
# User service
1416
user_service = UserService()
1517

18+
1619
# Registration route
1720
@router.post("/users/registration")
1821
@dto(UserCreateSchema)
19-
async def registration(request: Request, background_tasks: BackgroundTasks):
20-
22+
async def registration(request: Request, background_tasks: BackgroundTasks) -> JSONResponse:
23+
"""Register a new user."""
2124
# Retrieve data from the request
2225
request_data = await the_query(request)
2326
data = UserCreateSchema(**request_data)
24-
27+
2528
output = await user_service.s_registration(request, data, background_tasks)
2629
return JSONResponse(content=output, status_code=status.HTTP_201_CREATED)
2730

31+
2832
@router.post("/users/login")
29-
async def login(request: Request):
33+
async def login(request: Request) -> JSONResponse:
34+
"""Login a user."""
3035
data = await user_service.s_login(request)
3136
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)
3237

38+
3339
@router.get("/users")
34-
async def get_users(request: Request):
40+
async def get_users(request: Request) -> JSONResponse:
41+
"""Get all users."""
3542
data = await user_service.s_get_users(request)
3643
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)
3744

38-
@router.get("/users/{id}")
39-
async def get_user(request: Request, id: int|str):
40-
data = await user_service.s_get_user_by_id(request,user_id=id)
45+
46+
@router.get("/users/{user_id}")
47+
async def get_user(request: Request, user_id: int | str) -> JSONResponse:
48+
"""Get a user by id."""
49+
data = await user_service.s_get_user_by_id(request, user_id=user_id)
4150
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)
4251

43-
@router.put("/users/{id}/update")
52+
53+
@router.put("/users/{user_id}/update")
4454
@dto(UserUpdateSchema)
45-
async def update_user(request: Request, id: int|str):
46-
data = await user_service.s_update_user(request, user_id=id)
55+
async def update_user(request: Request, user_id: int | str) -> JSONResponse:
56+
"""Update a user by id."""
57+
data = await user_service.s_update_user(request, user_id=user_id)
4758
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)
4859

49-
@router.delete("/users/{id}/delete")
50-
async def delete_user(request: Request, id: int|str):
51-
data = await user_service.s_delete_user(request, user_id=id)
52-
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)
60+
61+
@router.delete("/users/{user_id}/delete")
62+
async def delete_user(request: Request, user_id: int | str) -> JSONResponse:
63+
"""Delete a user by id."""
64+
data = await user_service.s_delete_user(request, user_id=user_id)
65+
return JSONResponse(content=jsonable_encoder(data), status_code=status.HTTP_200_OK)

0 commit comments

Comments
 (0)
0