Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/db #3

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
APP_NAME=
APP_PORT=
APP_PORT=

DB_DRIVER=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=
DB_NAME=
File renamed without changes.
5 changes: 5 additions & 0 deletions apps/jobs/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pkgs.core import create_server
from .container import JobContainer
from .controllers import controllers

app = create_server(JobContainer, controllers)
9 changes: 9 additions & 0 deletions apps/jobs/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dependency_injector.containers import DeclarativeContainer
from dependency_injector.providers import Singleton
from pkgs.jobs import JobLibContainer

from .services import JobService

class JobContainer(DeclarativeContainer):
job_container = JobLibContainer()
job_service = Singleton(JobService, service=job_container.service)
3 changes: 3 additions & 0 deletions apps/jobs/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import jobs

controllers = [jobs]
43 changes: 43 additions & 0 deletions apps/jobs/controllers/jobs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from fastapi import Depends
from fastapi import status
from dependency_injector.wiring import inject, Provide
from pkgs.core import create_router
# from pkgs.core.transformer import transform
# from pkgs.common.transformers import UserTransformer
from ..container import JobContainer
from ..services import JobService
from ..validators import CreateJobDTO, UpdateJobDTO

router = create_router('/api/v1')

@router.post('/jobs')
@inject
async def create_job(data: CreateJobDTO, job_service: JobService = Depends(Provide[JobContainer.job_service])):
job = await job_service.create_job(data)
return job

@router.get('/jobs')
@inject
# @transform(UserTransformer)
async def get_jobs(job_service: JobService = Depends(Provide[JobContainer.job_service])):
jobs = await job_service.get_all_job()
return jobs


@router.get('/jobs/{id}')
# @transform(UserTransformer)
@inject
async def get_job(id: int, job_service: JobService = Depends(Provide[JobContainer.job_service])):
job = await job_service.get_job_by_id(id)
return job.to_dict()


@router.patch('/jobs/{id}', status_code=status.HTTP_204_NO_CONTENT)
@inject
async def update_job(id: int, data: UpdateJobDTO, job_service: JobService = Depends(Provide[JobContainer.job_service])):
await job_service.update_job_by_id(data, { "id": id })

@router.delete('/jobs/{id}', status_code=status.HTTP_204_NO_CONTENT)
@inject
async def delete_job(id: int, job_service: JobService = Depends(Provide[JobContainer.job_service])):
await job_service.delete_job(id)
1 change: 1 addition & 0 deletions apps/jobs/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .jobs import JobService
28 changes: 28 additions & 0 deletions apps/jobs/services/jobs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from dependency_injector.wiring import inject

from uuid import uuid4
from pkgs.jobs.services import JobLibService
from ..validators import CreateJobDTO, UpdateJobDTO

@inject
class JobService:
def __init__(self, service: JobLibService) -> None:
self.service = service

async def create_job(self, data: CreateJobDTO):
payload = data.dict()
payload['uuid'] = uuid4()
return await self.service.repo.create(payload)

async def get_all_job(self):
return await self.service.repo.all()

async def get_job_by_id(self, id: int):
return await self.service.repo.first_where({ "id": id }, relations=['created_by'])

async def update_job_by_id(self, data: UpdateJobDTO, filter):
return await self.service.repo.update_where(filter, data.dict(exclude_none=True))

async def delete_job_by_id(self, id: int):
return await self.service.repo.delete_where({ "id": id })

2 changes: 2 additions & 0 deletions apps/jobs/validators/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .create_job import CreateJobDTO
from .update_job import UpdateJobDTO
6 changes: 6 additions & 0 deletions apps/jobs/validators/create_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel

class CreateJobDTO(BaseModel):
title: str
description: str
created_by_id: int
7 changes: 7 additions & 0 deletions apps/jobs/validators/update_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel

class UpdateJobDTO(BaseModel):
title: Optional[str]
description: Optional[str]
created_by: Optional[int]
5 changes: 4 additions & 1 deletion apps/users/container.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from dependency_injector.containers import DeclarativeContainer
from dependency_injector.providers import Singleton
from pkgs.users import UserLibContainer

from .services import UserService

class UserContainer(DeclarativeContainer):
user_service = Singleton(UserService)
user_container = UserLibContainer()
user_service = Singleton(UserService, service=user_container.service)
36 changes: 31 additions & 5 deletions apps/users/controllers/user.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
from typing import Dict
from fastapi import Depends, HTTPException
from fastapi import Depends
from fastapi import status
from dependency_injector.wiring import inject, Provide
from pkgs.core import create_router
from ..container import UserContainer
from ..services import UserService
from ..validators.user import UserModel
from ..validators import CreateUserDTO, UpdateUserDTO

router = create_router('/api/v1')

@router.post('/users')
@inject
def get_users(user: UserModel, user_service: UserService = Depends(Provide[UserContainer.user_service])) -> Dict:
return user_service.greet()
async def get_users(data: CreateUserDTO, user_service: UserService = Depends(Provide[UserContainer.user_service])):
user = await user_service.create_user(data)
return user

@router.get('/users')
@inject
async def get_users(user_service: UserService = Depends(Provide[UserContainer.user_service])):
user = await user_service.get_all_users()
return user


@router.get('/users/{id}')
@inject
async def get_users(id: int, user_service: UserService = Depends(Provide[UserContainer.user_service])):
user = await user_service.get_user_by_id(id)
return user


@router.patch('/users/{id}', status_code=status.HTTP_204_NO_CONTENT)
@inject
async def get_users(id: int, data: UpdateUserDTO, user_service: UserService = Depends(Provide[UserContainer.user_service])):
await user_service.update_user_by_id(data, { "id": id })

@router.delete('/users/{id}', status_code=status.HTTP_204_NO_CONTENT)
@inject
async def get_users(id: int, user_service: UserService = Depends(Provide[UserContainer.user_service])):
await user_service.delete_user_by_id(id)
29 changes: 26 additions & 3 deletions apps/users/services/users.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
from typing import Dict
from dependency_injector.wiring import inject

from uuid import uuid4
from pkgs.users.services import UserLibService
from ..validators import CreateUserDTO, UpdateUserDTO

@inject
class UserService:
def greet(self) -> Dict:
return {'message': "Hello there!"}
def __init__(self, service: UserLibService) -> None:
self.service = service

async def create_user(self, data: CreateUserDTO):
payload = data.dict()
payload['uuid'] = uuid4()
return await self.service.repo.create(payload)

async def get_all_users(self):
return await self.service.repo.all()

async def get_user_by_id(self, id: int):
return await self.service.repo.first_where({ "id": id })

async def update_user_by_id(self, data: UpdateUserDTO, filter):
return await self.service.repo.update_where(filter, data.dict(exclude_none=True))

async def delete_user_by_id(self, id: int):
return await self.service.repo.delete_where({ "id": id })

2 changes: 2 additions & 0 deletions apps/users/validators/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .create_user import CreateUserDTO
from .update_user import UpdateUserDTO
6 changes: 6 additions & 0 deletions apps/users/validators/create_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel, EmailStr, constr

class CreateUserDTO(BaseModel):
name: str
email: EmailStr
password: constr(max_length=255, min_length=8)
7 changes: 7 additions & 0 deletions apps/users/validators/update_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing import Optional
from pydantic import BaseModel, EmailStr, constr

class UpdateUserDTO(BaseModel):
name: Optional[str]
email: Optional[EmailStr]
password: Optional[constr(max_length=255, min_length=8)]
6 changes: 0 additions & 6 deletions apps/users/validators/user.py

This file was deleted.

4 changes: 3 additions & 1 deletion config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from .app import app
from .database import db

config = {
"app": app
"app": app,
"db": db
}

from .utils.resolve import resolve
2 changes: 1 addition & 1 deletion config/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

app = {
"name": os.environ.get('APP_NAME') or "fastapi-boilerplate",
"port": os.environ.get('APP_PORT') or 8080
"port": int(os.environ.get('APP_PORT')) or 8080
}
10 changes: 10 additions & 0 deletions config/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

db = {
"driver": os.environ.get('DB_DRIVER'),
"user": os.environ.get('DB_USER'),
"password": os.environ.get('DB_PASSWORD'),
"host": os.environ.get('DB_HOST'),
"port": os.environ.get('DB_PORT'),
"name": os.environ.get('DB_NAME'),
}
1 change: 1 addition & 0 deletions config/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .resolve import resolve
2 changes: 1 addition & 1 deletion config/utils/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .. import config

def resolve(key: str):
pattern = r'(\w+.\w+)*(\.\w+)+'
pattern = r'^(\w+(.\w+)+)$'
if not re.match(pattern, key):
raise ValueError("Invalid key")

Expand Down
8 changes: 4 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from glob import glob
import uvicorn
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
from config import resolve
from dotenv import load_dotenv

load_dotenv()

if __name__ == '__main__':
uvicorn.run('apps.users.app:app', host='localhost', port=resolve('app.port'), reload=True)
uvicorn.run('apps.jobs.app:app', host='localhost', port=resolve('app.port'), reload=True)
19 changes: 19 additions & 0 deletions migrations/1676731094599205_create_users_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[up]
CREATE TABLE users (
id SERIAL,
uuid VARCHAR(255),
name VARCHAR(255),
email VARCHAR(255),
password VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TRIGGER update_users_trigger
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE PROCEDURE on_update_timestamp();

[down]
DROP TRIGGER update_users_trigger on users;
DROP TABLE users;
19 changes: 19 additions & 0 deletions migrations/1679077537322008_create_jobs_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[up]
CREATE TABLE jobs (
id SERIAL,
uuid VARCHAR(255),
title VARCHAR(255),
description TEXT,
created_by_id INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TRIGGER update_jobs_trigger
BEFORE UPDATE ON jobs
FOR EACH ROW
EXECUTE PROCEDURE on_update_timestamp();

[down]
DROP TRIGGER update_jobs_trigger on jobs;
DROP TABLE jobs;
Empty file added pkgs/common/__init__.py
Empty file.
11 changes: 11 additions & 0 deletions pkgs/common/transformers/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pkgs.core.transformer import Transformer

class UserTransformer(Transformer):
def transform(self, obj):
return {
"id": obj.get('uuid'),
"name": obj.get('name'),
"email": obj.get('email'),
"created_at": obj.get('created_at'),
"updated_at": obj.get('updated_at')
}
3 changes: 2 additions & 1 deletion pkgs/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .http import *
from .http import *
from .container import core_container
10 changes: 10 additions & 0 deletions pkgs/core/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from dependency_injector.containers import DeclarativeContainer
from dependency_injector.providers import Resource
from .database import init_db

class CoreContainer:
db = Resource(
init_db
)

core_container = CoreContainer()
3 changes: 3 additions & 0 deletions pkgs/core/database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .database import *
from .repository import *
from .basemodel import *
10 changes: 10 additions & 0 deletions pkgs/core/database/basemodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from tortoise.models import Model

class BaseModel(Model):
def to_dict(self):
unwanted_keys = ['_partial', '_saved_in_db', '_custom_generated_pk']
result = self.__dict__
for key in unwanted_keys:
result.pop(key)

return result
Loading