This commit is contained in:
dderbentsov
2023-07-13 01:25:29 +03:00
commit bc339cefb9
25 changed files with 494 additions and 0 deletions

View File

19
src/personal_info/app.py Normal file
View File

@@ -0,0 +1,19 @@
from fastapi import FastAPI
def create_app() -> FastAPI:
app = FastAPI (
title='personal_info',
description='Personal Information Service',
version='1.0'
)
@app.get('/health')
async def health() -> str:
return 'ok'
from personal_info.routers import persons
app.include(persons.router)
return app

View File

@@ -0,0 +1,15 @@
from functools import lru_cache
from typing import Optional
from pydantic import BaseSettings, PostgresDsn
class Settings(BaseSettings):
database_url: Optional[PostgresDsn]
@lru_cache
def get_settings() -> Settings:
settings = Settings() # type: ignore
print('333333333333333333333333333333333333333333')
print(settings)
#CONTINUE
return settings

View File

View File

@@ -0,0 +1,17 @@
import uuid
from typing import Any
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.sql.schema import ForeignKey
Base: Any = declarative_base()
class Person(Base):
__tablename__ = 'people'
id = sa.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
first_name = sa.Column(sa.Text, nullable=False)
last_name = sa.Column(sa.Text, nullable=False)

View File

@@ -0,0 +1 @@
from .person import *

View File

@@ -0,0 +1,16 @@
from datetime import datetime
from typing import List
from pydantic import BaseModel, Field
from pydantic.types import UUID4
class Person(BaseModel):
id: UUID4
first_name: str
last_name: str
class PersonCreate(Person):
pass
class PersonDetail(Person):
pass

View File

@@ -0,0 +1,23 @@
from functools import lru_cache
from typing import Generator
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from personal_info.config import get_settings
engine = create_engine(get_settings().database_url, pool_pre_ping=True)
@lru_cache
def create_session() -> scoped_session:
Session = scoped_session(
sessionmaker(autocommit=False, autoflush=False, bind=engine)
)
return Session
def get_session() -> Generator[scoped_session, None, None]:
Session = create_session()
try:
yield Session
finally:
Session.remove()

View File

@@ -0,0 +1,3 @@
from personal_info.app import create_app
app = create_app()

View File

View File

@@ -0,0 +1,27 @@
from fastapi import Depends
from fastapi.routing import APIRouter
from pydantic.types import UUID4
from personal_info.db import models
from personal_info.db.schemas import Person, PersonDetail
from personal_info.services import PersonService, get_persons_service
router = APIrouter(prefix='/people')
@router.get('/', response_model=List[Person])
async def list_persons(
persons_service: PersonService = Depends(get_persons_service),
) -> List[models.Person]:
return persons_service.list()
@router.get('/{person_id}', response_model=PersonDetail)
async def get_person(
person_id: UUID4, persons_service: PersonService = Depends(get_persons_service)
) -> Optional[models.Person]:
return persons_service.get(person_id)
@router.post('/', status_code=201, response_model=PersonDetail)
async def create_person(
person: PersonCreate, persons_service: PersonService = Depends(get_persons_service)
) -> models.Person:
return persons_service.create(person)

View File

@@ -0,0 +1,11 @@
from fastapi import Depends
from sqlalchemy.orm import Session
from personal_info.db.session import get_session
from .people import PersonService
def get_persons_service(db_session: Session = Depends(get_session)) -> PersonService:
return PersonService(db_session)
__all__ = ('get_persons_service')

View File

@@ -0,0 +1,52 @@
from typing import Any, Generic, List, Optional, Type, TypeVar
import sqlalchemy
from pydantic import BaseModel
from sqlalchemy.orm import Session
from starlette.exceptions import HTTPException
from personal_info.db.models import Base
ModelType = TypeVar('ModelType', bound=Base)
CreateSchemaType = TypeVar('CreateSchemaType', bound=BaseModel)
UpdateSchemaType = TypeVar('UpdateSchemaType', bound=BaseModel)
class BaseService(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
def __init__(self, model: Type[ModelType], db_session: Session):
self.model = model
self.db_session = db_session
def get(self, id: Any) -> Optional[ModelType]:
obj: Optional[ModelType] = self.db_session.get(self.model, id)
if obj is None:
raise HTTPException(status_code=404, detail='NotFound')
return obj
def list(self) -> List[ModelType]:
objs: List[ModelType] = self.db_session.query(self.model).all()
return objs
def create(self, obj: CreateSchemaType) -> ModelType:
db_obj: ModelType = self.model(**obj.dict())
self.db_session.add(db_obj)
try:
self.db_session.commit()
except sqlalchemy.exc.IntegrityError as e:
self.db_session.rollback()
if 'duplicate key' in str(e):
raise HTTPException(status_code=409, detail='Conflict Error')
else:
raise e
return db_obj
def update(self, id: Any, obj: UpdateSchemaType) -> Optional[ModelType]:
db_obj = self.get(id)
for column, value in obj.dict(exclude_unset=True).items():
setattr(db_obj, column, value)
self.db_session.commit()
return db_obj
def delete(self, id: Any) -> None:
db_obj = self.db_session.get(self.model, id)
self.db_session.delete(db_obj)
self.db_session.commit()

View File

@@ -0,0 +1,19 @@
from typing import Any
from sqlalchemy.orm import Session
from personal_info.db.models import Person
from personal_info.db.schemas import PersonCreate
from personal_info.services.base import BaseService
class PersonService(BaseService[Person, PersonCreate, Any]):
def __init__(self, db_session: Session):
super(PersonService, self).__init__(Person, db_session)
def create(self, obj: PersonCreate) -> Person:
person: Person = Person()
self.db_session.add(person)
self.db_session.flush()
self.db_session.commit()
return person