From 868ce8dd1a5ef44664f99cf81bbde3910c17b7cb Mon Sep 17 00:00:00 2001 From: nico Date: Fri, 16 Sep 2022 15:22:19 +0200 Subject: [PATCH 1/2] wip --- api/tp/app/dependencies/__init__.py | 0 api/tp/app/main.py | 36 ++++++++++++++++ api/tp/app/models/__init__.py | 3 ++ api/tp/app/models/database.py | 20 +++++++++ api/tp/app/models/db.py | 9 ++++ api/tp/app/models/post.py | 13 ++++++ api/tp/app/routers/__init__.py | 2 + api/tp/app/routers/health.py | 27 ++++++++++++ api/tp/app/routers/posts.py | 40 +++++++++++++++++ api/tp/app/schemas/__init__.py | 1 + api/tp/app/schemas/posts.py | 16 +++++++ api/tp/app/services/posts.py | 66 +++++++++++++++++++++++++++++ 12 files changed, 233 insertions(+) create mode 100644 api/tp/app/dependencies/__init__.py create mode 100644 api/tp/app/models/__init__.py create mode 100644 api/tp/app/models/database.py create mode 100644 api/tp/app/models/db.py create mode 100644 api/tp/app/models/post.py create mode 100644 api/tp/app/routers/__init__.py create mode 100644 api/tp/app/routers/health.py create mode 100644 api/tp/app/routers/posts.py create mode 100644 api/tp/app/schemas/__init__.py create mode 100644 api/tp/app/schemas/posts.py create mode 100644 api/tp/app/services/posts.py diff --git a/api/tp/app/dependencies/__init__.py b/api/tp/app/dependencies/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/api/tp/app/main.py b/api/tp/app/main.py index f0416a99..fdd1483b 100644 --- a/api/tp/app/main.py +++ b/api/tp/app/main.py @@ -1,11 +1,47 @@ +from typing import Optional from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +import json +import asyncio +import os +from starlette_exporter import PrometheusMiddleware, handle_metrics +from .models import BaseSQL, engine +from . import routers app = FastAPI( title="My title", description="My description", version="0.0.1", ) +origins = [ + "http://localhost:4200", + "http://localhost:3000", + "http://localhost:3001", +] +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +app.include_router(routers.PostRouter) +app.include_router(routers.HealthRouter) + +app.add_middleware(PrometheusMiddleware) +app.add_route("/metrics", handle_metrics) + + +@app.on_event("startup") +async def startup_event(): + BaseSQL.metadata.create_all(bind=engine) + +# @app.get("/api/headers") +# def read_hello(request: Request, x_userinfo: Optional[str] = Header(None, convert_underscores=True), ): +# print(request["headers"]) +# return {"Headers": json.loads(base64.b64decode(x_userinfo))} @app.get("/") def read_root(): diff --git a/api/tp/app/models/__init__.py b/api/tp/app/models/__init__.py new file mode 100644 index 00000000..1c268fd3 --- /dev/null +++ b/api/tp/app/models/__init__.py @@ -0,0 +1,3 @@ +from .post import Post +from .database import BaseSQL +from .db import get_db, engine \ No newline at end of file diff --git a/api/tp/app/models/database.py b/api/tp/app/models/database.py new file mode 100644 index 00000000..2b15ed94 --- /dev/null +++ b/api/tp/app/models/database.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +import os + + +POSTGRES_USER = os.environ.get("POSTGRES_USER") +POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD") +POSTGRES_DB = os.environ.get("POSTGRES_DB") + + +SQLALCHEMY_DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@db/{POSTGRES_DB}" +print(SQLALCHEMY_DATABASE_URL) + +engine = create_engine( + SQLALCHEMY_DATABASE_URL +) +SessionLocal = sessionmaker(autocommit=False, autoflush=True, bind=engine) + +BaseSQL = declarative_base() diff --git a/api/tp/app/models/db.py b/api/tp/app/models/db.py new file mode 100644 index 00000000..7d907548 --- /dev/null +++ b/api/tp/app/models/db.py @@ -0,0 +1,9 @@ +from .database import SessionLocal, engine + +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() \ No newline at end of file diff --git a/api/tp/app/models/post.py b/api/tp/app/models/post.py new file mode 100644 index 00000000..f098451b --- /dev/null +++ b/api/tp/app/models/post.py @@ -0,0 +1,13 @@ +from sqlalchemy import Column, String, DateTime +from sqlalchemy.dialects.postgresql import UUID +from .database import BaseSQL + + +class Post(BaseSQL): + __tablename__ = "posts" + + id = Column(UUID(as_uuid=True), primary_key=True, index=True) + title = Column(String) + description = Column(String) + created_at = Column(DateTime()) + updated_at = Column(DateTime()) diff --git a/api/tp/app/routers/__init__.py b/api/tp/app/routers/__init__.py new file mode 100644 index 00000000..5dab8b48 --- /dev/null +++ b/api/tp/app/routers/__init__.py @@ -0,0 +1,2 @@ +from .posts import router as PostRouter +from .health import router as HealthRouter \ No newline at end of file diff --git a/api/tp/app/routers/health.py b/api/tp/app/routers/health.py new file mode 100644 index 00000000..3b042fdf --- /dev/null +++ b/api/tp/app/routers/health.py @@ -0,0 +1,27 @@ +from fastapi import FastAPI, Header, Request, APIRouter +from typing import Optional +import base64 +import json + +router = APIRouter() + + +@router.get("/") +def read_root(): + return {"Hello": "World"} + + +@router.get("/api") +def read_hello(): + return {"Hello": "Api"} + + +@router.get("/health") +def read_root(): + return {"message": "Api is running fine!"} + + +@router.get("/api/headers") +def read_hello(request: Request, x_userinfo: Optional[str] = Header(None, convert_underscores=True)): + print(request["headers"]) + return {"Headers": json.loads(base64.b64decode(x_userinfo))} diff --git a/api/tp/app/routers/posts.py b/api/tp/app/routers/posts.py new file mode 100644 index 00000000..668b2437 --- /dev/null +++ b/api/tp/app/routers/posts.py @@ -0,0 +1,40 @@ +from fastapi import APIRouter, Depends +from ..services import posts as posts_service +from .. import schemas, models +from sqlalchemy.orm import Session + +router = APIRouter(prefix="/posts") + + +@router.post("/", tags=["posts"]) +async def create_post(post: schemas.Post, db: Session = Depends(models.get_db)): + return posts_service.create_post(post=post, db=db) + + +@router.get("/{post_id}", tags=["posts"]) +async def get_post_by_id(post_id: str, db: Session = Depends(models.get_db)): + return posts_service.get_post_by_id(post_id=post_id, db=db) + + +@router.get("/", tags=["posts"]) +async def get_posts_by_title(title: str = None, db: Session = Depends(models.get_db)): + if title: + return posts_service.get_all_posts(db=db) + else: + return posts_service.get_posts_by_title(title=title, db=db) + + +@router.put("/{post_id}", tags=["posts"]) +async def update_post_by_id(post_id: str, post: schemas.Post, + db: Session = Depends(models.get_db)): + return posts_service.update_post(post_id=post_id, db=db, post=post) + + +@router.delete("/{post_id}", tags=["posts"]) +async def delete_post_by_id(post_id: str, db: Session = Depends(models.get_db)): + return posts_service.delete_post(post_id=post_id, db=db) + + +@router.delete("/", tags=["posts"]) +async def delete_all_posts(db: Session = Depends(models.get_db)): + return posts_service.delete_all_posts(db=db) diff --git a/api/tp/app/schemas/__init__.py b/api/tp/app/schemas/__init__.py new file mode 100644 index 00000000..a406556b --- /dev/null +++ b/api/tp/app/schemas/__init__.py @@ -0,0 +1 @@ +from .posts import Post diff --git a/api/tp/app/schemas/posts.py b/api/tp/app/schemas/posts.py new file mode 100644 index 00000000..78cd3c8e --- /dev/null +++ b/api/tp/app/schemas/posts.py @@ -0,0 +1,16 @@ +from typing import List, Optional +from datetime import datetime +from pydantic import BaseModel, Field +from uuid import uuid4 +from typing_extensions import Annotated + + +class Post(BaseModel): + id: Annotated[str, Field(default_factory=lambda: uuid4().hex)] + title: str + description: Optional[str] + created_at: Annotated[datetime, Field(default_factory=lambda: datetime.now())] + updated_at: Annotated[datetime, Field(default_factory=lambda: datetime.now())] + + class Config: + orm_mode = True diff --git a/api/tp/app/services/posts.py b/api/tp/app/services/posts.py new file mode 100644 index 00000000..76524f40 --- /dev/null +++ b/api/tp/app/services/posts.py @@ -0,0 +1,66 @@ +from typing import List + +from sqlalchemy.orm import Session +from fastapi import HTTPException +from datetime import datetime +from .. import models, schemas + + +def get_all_posts(db: Session, skip: int = 0, limit: int = 10) -> List[models.Post]: + records = db.query(models.Post).filter().offset(skip).limit(limit).all() + for record in records: + record.id = str(record.id) + return records + + +def get_post_by_id(post_id: str, db: Session) -> models.Post: + record = db.query(models.Post).filter(models.Post.id == post_id).first() + if not record: + raise HTTPException(status_code=404, detail="Not Found") + record.id = str(record.id) + return record + + +def get_posts_by_title(title: str, db: Session) -> List[models.Post]: + records = db.query(models.Post).filter(models.Post.title == title).all() + for record in records: + record.id = str(record.id) + return records + + +def update_post(post_id: str, db: Session, post: schemas.Post) -> models.Post: + db_post = get_post_by_id(post_id=post_id, db=db) + for var, value in vars(post).items(): + setattr(db_post, var, value) if value else None + db_post.updated_at = datetime.now() + db.add(db_post) + db.commit() + db.refresh(db_post) + return db_post + + +def delete_post(post_id: str, db: Session) -> models.Post: + db_post = get_post_by_id(post_id=post_id, db=db) + db.delete(db_post) + db.commit() + return db_post + + +def delete_all_posts(db: Session) -> List[models.Post]: + records = db.query(models.Post).filter() + for record in records: + db.delete(record) + db.commit() + return records + + +def create_post(db: Session, post: schemas.Post) -> models.Post: + record = db.query(models.Post).filter(models.Post.id == post.id).first() + if record: + raise HTTPException(status_code=409, detail="Already exists") + db_post = models.Post(**post.dict()) + db.add(db_post) + db.commit() + db.refresh(db_post) + db_post.id = str(db_post.id) + return db_post From 3b486e170484eb3e3b944960d533109c270c41ed Mon Sep 17 00:00:00 2001 From: nico Date: Fri, 16 Sep 2022 16:26:57 +0200 Subject: [PATCH 2/2] wip --- api/tp/app/main.py | 33 +++++++++++++++++++++++++++++++-- api/tp/app/models/db.py | 2 +- api/tp/app/routers/posts.py | 4 ++-- api/tp/app/services/posts.py | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/api/tp/app/main.py b/api/tp/app/main.py index fdd1483b..31f5d9fa 100644 --- a/api/tp/app/main.py +++ b/api/tp/app/main.py @@ -5,8 +5,9 @@ import asyncio import os from starlette_exporter import PrometheusMiddleware, handle_metrics -from .models import BaseSQL, engine -from . import routers +from models import BaseSQL, engine +import routers +from sqlalchemy import insert app = FastAPI( title="My title", @@ -46,3 +47,31 @@ async def startup_event(): @app.get("/") def read_root(): return {"Hello": "World"} + +@app.get("/get_table") +def get_table(): + with engine.connect() as connection: + result = connection.execute("select * from posts") + for row in result: + print("username:", row['username']) + +@app.get("/get_table") +def get_table(): + with engine.connect() as connection: + result = connection.execute("select * from posts") + for row in result: + print("username:", row['username']) + +# @app.get("/insert_table") +# def insert_table(): +# with engine.connect() as conn: +# result = conn.execute( +# insert("INSERT"), +# [ +# {"name": "sandy", "fullname": "Sandy Cheeks"}, +# {"name": "patrick", "fullname": "Patrick Star"} +# ] +# ) +# conn.commit() + + diff --git a/api/tp/app/models/db.py b/api/tp/app/models/db.py index 7d907548..7b6d13d2 100644 --- a/api/tp/app/models/db.py +++ b/api/tp/app/models/db.py @@ -6,4 +6,4 @@ def get_db(): db = SessionLocal() yield db finally: - db.close() \ No newline at end of file + db.close() diff --git a/api/tp/app/routers/posts.py b/api/tp/app/routers/posts.py index 668b2437..591b9ecf 100644 --- a/api/tp/app/routers/posts.py +++ b/api/tp/app/routers/posts.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends -from ..services import posts as posts_service -from .. import schemas, models +from services import posts as posts_service +import schemas, models from sqlalchemy.orm import Session router = APIRouter(prefix="/posts") diff --git a/api/tp/app/services/posts.py b/api/tp/app/services/posts.py index 76524f40..cd28bfaa 100644 --- a/api/tp/app/services/posts.py +++ b/api/tp/app/services/posts.py @@ -3,7 +3,7 @@ from sqlalchemy.orm import Session from fastapi import HTTPException from datetime import datetime -from .. import models, schemas +import models, schemas def get_all_posts(db: Session, skip: int = 0, limit: int = 10) -> List[models.Post]: