adding .unix

This commit is contained in:
2026-02-07 02:17:02 -06:00
parent afc7aa99c0
commit ea7144c3eb
15 changed files with 270 additions and 270 deletions

View File

@@ -1,31 +1,31 @@
I was too lazy to write this in a markdown file. And lazy enough to write this in English as well. I was too lazy to write this in a markdown file. And lazy enough to write this in English as well.
------------------------------------------------ ------------------------------------------------
Welp. Easy RESTful API and stuff. Welp. Easy RESTful API and stuff.
Endpoints: Endpoints:
- GET | / | Sample root endpoint. - GET | / | Sample root endpoint.
- GET | /docs | Swagger UI for testing the endpoints. - GET | /docs | Swagger UI for testing the endpoints.
- GET | /users | List all users. - GET | /users | List all users.
- GET | /users/{id} | Gets a user by its id in database. - GET | /users/{id} | Gets a user by its id in database.
- POST | /users | Adds a new user to the database. - POST | /users | Adds a new user to the database.
Dependencies JIC you want to make your own virtual python environment: Dependencies JIC you want to make your own virtual python environment:
- uvicorn | To run API apps - uvicorn | To run API apps
- fastapi | This project's API Framework - fastapi | This project's API Framework
- pydantic | Data validation library - pydantic | Data validation library
- sqlalchemy | Framework to operate databases with abstractions - sqlalchemy | Framework to operate databases with abstractions
Extra implementations: Extra implementations:
- Database | SQLite3 Database file. /src/users.db - Database | SQLite3 Database file. /src/users.db
- Windows & UNIX| For Windows or Linux servers, this API includes both .env directories for windows and UNIX. /.env & /.env-unix - Windows & UNIX| For Windows or Linux servers, this API includes both .env directories for windows and UNIX. /.env & /.env-unix
Tested on: Tested on:
- Windows - Windows
- Linux (WSL & Native) - Using Arch Linux. - Linux (WSL & Native) - Using Arch Linux.
Not tested on: Not tested on:
- BSD - FreeBSD and/or NetBSD. - BSD - FreeBSD and/or NetBSD.
- Docker. - Docker.
- Micropython - ESP32. - Micropython - ESP32.
- Other Python compatible systems. - Other Python compatible systems.

View File

@@ -1,8 +1,8 @@
{ {
"dependencies": { "dependencies": {
"1" : "uviconrn", "1" : "uviconrn",
"2" : "fastapi", "2" : "fastapi",
"3" : "pydantic", "3" : "pydantic",
"4" : "sqlalchemy" "4" : "sqlalchemy"
} }
} }

View File

@@ -1,2 +1,2 @@
class UserAlreadyExists(Exception): class UserAlreadyExists(Exception):
pass pass

View File

@@ -1,13 +1,13 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from domain.users import User from domain.users import User
class UserRepository(ABC): class UserRepository(ABC):
@abstractmethod @abstractmethod
def save(self, user: User): def save(self, user: User):
... ...
@abstractmethod @abstractmethod
def viewAll(self) -> list[User]: def viewAll(self) -> list[User]:
... ...
@abstractmethod @abstractmethod
def viewById(self, user_id:int): def viewById(self, user_id:int):
... ...

View File

@@ -1,33 +1,33 @@
from domain.users import User from domain.users import User
from application.ports.user_repository import UserRepository from application.ports.user_repository import UserRepository
class CreateUser: class CreateUser:
def __init__(self, repo: UserRepository): def __init__(self, repo: UserRepository):
if not isinstance(repo, UserRepository): if not isinstance(repo, UserRepository):
raise TypeError("repo must implement UserRepository") raise TypeError("repo must implement UserRepository")
self.repo = repo self.repo = repo
def execute(self, name:str, email:str, phone:str): def execute(self, name:str, email:str, phone:str):
user = User(user_id=0, email=email, name=name, phone=phone) user = User(user_id=0, email=email, name=name, phone=phone)
self.repo.save(user) self.repo.save(user)
return user return user
class ViewUsers: class ViewUsers:
def __init__(self, repo: UserRepository): def __init__(self, repo: UserRepository):
if not isinstance(repo, UserRepository): if not isinstance(repo, UserRepository):
raise TypeError("repo must implement UserRepository") raise TypeError("repo must implement UserRepository")
self.repo = repo self.repo = repo
def execute(self): def execute(self):
users = self.repo.viewAll() users = self.repo.viewAll()
return users return users
class ViewUserById: class ViewUserById:
def __init__(self, repo: UserRepository): def __init__(self, repo: UserRepository):
if not isinstance(repo, UserRepository): if not isinstance(repo, UserRepository):
raise TypeError("repo must implement UserRepository") raise TypeError("repo must implement UserRepository")
self.repo = repo self.repo = repo
def execute(self, user_id: int) -> User | None: def execute(self, user_id: int) -> User | None:
user = self.repo.viewById(user_id) user = self.repo.viewById(user_id)
return user return user

View File

@@ -1,8 +1,8 @@
from dataclasses import dataclass from dataclasses import dataclass
@dataclass @dataclass
class User: class User:
user_id: int user_id: int
name: str name: str
email: str email: str
phone: str phone: str

View File

@@ -1,5 +1,5 @@
from infrastructure.adapters.persistence.db import engine from infrastructure.adapters.persistence.db import engine
from infrastructure.adapters.persistence.models import Base from infrastructure.adapters.persistence.models import Base
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)

View File

@@ -1,17 +1,17 @@
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "sqlite:///./users.db" DATABASE_URL = "sqlite:///./users.db"
engine = create_engine( engine = create_engine(
DATABASE_URL, DATABASE_URL,
connect_args={"check_same_thread": False} connect_args={"check_same_thread": False}
) )
SessionLocal = sessionmaker( SessionLocal = sessionmaker(
bind=engine, bind=engine,
autocommit=False, autocommit=False,
autoflush=False autoflush=False
) )
Base = declarative_base() Base = declarative_base()

View File

@@ -1,10 +1,10 @@
from sqlalchemy import Column, Integer, String from sqlalchemy import Column, Integer, String
from infrastructure.adapters.persistence.db import Base from infrastructure.adapters.persistence.db import Base
class UserModel(Base): class UserModel(Base):
__tablename__ = "users" __tablename__ = "users"
user_id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=False, index=True) name = Column(String, unique=False, index=True)
email = Column(String, unique=True, index=True) email = Column(String, unique=True, index=True)
phone = Column(String, unique=True, index=True) phone = Column(String, unique=True, index=True)

View File

@@ -1,61 +1,61 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from domain.users import User from domain.users import User
from application.ports.user_repository import UserRepository from application.ports.user_repository import UserRepository
from infrastructure.adapters.persistence.db import SessionLocal from infrastructure.adapters.persistence.db import SessionLocal
from infrastructure.adapters.persistence.models import UserModel from infrastructure.adapters.persistence.models import UserModel
from application.exceptions import UserAlreadyExists from application.exceptions import UserAlreadyExists
class SqlUserRepository(UserRepository): class SqlUserRepository(UserRepository):
def __init__(self): def __init__(self):
self.db: Session = SessionLocal() self.db: Session = SessionLocal()
def save(self, user: User): def save(self, user: User):
model = UserModel( model = UserModel(
name= user.name, name= user.name,
email=user.email, email=user.email,
phone=user.phone phone=user.phone
) )
self.db.add(model) self.db.add(model)
try: try:
self.db.commit() self.db.commit()
except IntegrityError: except IntegrityError:
self.db.rollback() self.db.rollback()
raise UserAlreadyExists("A user with the same phone number or name does already exist.") raise UserAlreadyExists("A user with the same phone number or name does already exist.")
self.db.refresh(model) self.db.refresh(model)
return User( return User(
user_id = model.user_id, user_id = model.user_id,
name = model.name, name = model.name,
email = model.email, email = model.email,
phone = model.phone phone = model.phone
) )
def viewAll(self) -> list[User]: def viewAll(self) -> list[User]:
models = self.db.query(UserModel).all() models = self.db.query(UserModel).all()
return [ return [
User( User(
user_id=model.user_id, user_id=model.user_id,
name = model.name, name = model.name,
email = model.email, email = model.email,
phone = model.phone phone = model.phone
) for model in models ) for model in models
] ]
def viewById(self, user_id: int) -> User | None: def viewById(self, user_id: int) -> User | None:
model = self.db.query(UserModel).filter(UserModel.user_id == user_id).first() model = self.db.query(UserModel).filter(UserModel.user_id == user_id).first()
if not model: if not model:
return None return None
return User( return User(
user_id=model.user_id, user_id=model.user_id,
name = model.name, name = model.name,
email = model.email, email = model.email,
phone = model.phone phone = model.phone
) )

View File

@@ -1,7 +1,7 @@
from fastapi import FastAPI from fastapi import FastAPI
from infrastructure.api.users.router import router from infrastructure.api.users.router import router
def create_app() -> FastAPI: def create_app() -> FastAPI:
app = FastAPI(title="Users service") app = FastAPI(title="Users service")
app.include_router(router) app.include_router(router)
return app return app

View File

@@ -1,10 +1,10 @@
from fastapi import APIRouter from fastapi import APIRouter
router = APIRouter() router = APIRouter()
@router.get("/") @router.get("/")
def root(): def root():
return { return {
"Status": "Running", "Status": "Running",
"Docs": "/docs" "Docs": "/docs"
} }

View File

@@ -1,17 +1,17 @@
from fastapi import APIRouter from fastapi import APIRouter
from infrastructure.api.users.users import router as users_router from infrastructure.api.users.users import router as users_router
from infrastructure.api.users.root import router as root_router from infrastructure.api.users.root import router as root_router
router = APIRouter() router = APIRouter()
router.include_router( router.include_router(
users_router, users_router,
prefix="/users", prefix="/users",
tags=["users"] tags=["users"]
) )
router.include_router( router.include_router(
root_router, root_router,
prefix='', prefix='',
tags=["root"] tags=["root"]
) )

View File

@@ -1,44 +1,44 @@
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from application.services.user_services import CreateUser, ViewUsers, ViewUserById from application.services.user_services import CreateUser, ViewUsers, ViewUserById
from infrastructure.adapters.persistence.user_repository_sql import SqlUserRepository from infrastructure.adapters.persistence.user_repository_sql import SqlUserRepository
from application.exceptions import UserAlreadyExists from application.exceptions import UserAlreadyExists
router = APIRouter() router = APIRouter()
@router.post("/") @router.post("/")
def create_user(name:str, email:str, phone:str): def create_user(name:str, email:str, phone:str):
service = CreateUser(SqlUserRepository()) service = CreateUser(SqlUserRepository())
try: try:
return service.execute(name,email,phone) return service.execute(name,email,phone)
except UserAlreadyExists as e: except UserAlreadyExists as e:
raise HTTPException ( raise HTTPException (
status_code=409, status_code=409,
detail=str(e) detail=str(e)
) )
@router.get("/") @router.get("/")
def view_all_users(): def view_all_users():
service = ViewUsers(SqlUserRepository()) service = ViewUsers(SqlUserRepository())
try: try:
return service.execute() return service.execute()
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=e) detail=e)
@router.get("/{id}") @router.get("/{id}")
def view_user_by_id(user_id: int): def view_user_by_id(user_id: int):
service = ViewUserById(SqlUserRepository()) service = ViewUserById(SqlUserRepository())
result = service.execute(user_id) result = service.execute(user_id)
if result is None: if result is None:
raise HTTPException( raise HTTPException(
status_code=404, status_code=404,
detail="User not found") detail="User not found")
return result return result

View File

@@ -1,16 +1,16 @@
from infrastructure.api.users.app import create_app from infrastructure.api.users.app import create_app
app = create_app() app = create_app()
def run(): def run():
import uvicorn import uvicorn
uvicorn.run( uvicorn.run(
"main:app", "main:app",
host="0.0.0.0", host="0.0.0.0",
port=8000, port=8000,
reload=True, reload=True,
log_level="info", log_level="info",
) )
if __name__ == "__main__": if __name__ == "__main__":
run() run()