From 2c68a314d27bd0eb909b4830b27687692bc33a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Mon, 30 Oct 2023 17:31:19 -0300 Subject: [PATCH 01/35] Adiciona docker e arquivos iniciais do FastApi --- .gitignore | 1 + Dockerfile | 15 +++++++++++++++ docker-compose.yml | 29 +++++++++++++++++++++++++++++ env.example | 5 +++++ requirements.txt | 35 +++++++++++++++++++++++++++++++++++ src/__init__.py | 8 ++++++++ src/main.py | 13 +++++++++++++ 7 files changed, 106 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 env.example create mode 100644 requirements.txt create mode 100644 src/__init__.py create mode 100644 src/main.py diff --git a/.gitignore b/.gitignore index 68bc17f..06884cf 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,4 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +.vscode/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8860585 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.10.9-slim-buster + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONBUFFERED 1 + +COPY . . + +ENV PYTHONPATH "/app/src" +RUN pip install -r requirements.txt + +EXPOSE 8081 + +CMD [ "uvicorn", "src.main:app", "--host", "0.0.0.0","--port", "8081","--reload"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b0c1014 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3" + +services: + db: + image: "postgres:alpine" + container_name: "db" + env_file: + - .env + volumes: + - postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + + api: + depends_on: + - db + build: + context: ./ + container_name: video-service + links: + - db + ports: + - "8081:8081" + volumes: + - ./:/app + environment: + - DEBUG=True +volumes: + postgres: diff --git a/env.example b/env.example new file mode 100644 index 0000000..234714e --- /dev/null +++ b/env.example @@ -0,0 +1,5 @@ +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_HOST= +POSTGRES_DB= +POSTGRES_PORT= \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2f591c9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,35 @@ +annotated-types==0.6.0 +anyio==3.7.1 +certifi==2023.7.22 +click==8.1.7 +dnspython==2.4.2 +email-validator==2.1.0.post1 +exceptiongroup==1.1.3 +fastapi==0.104.1 +h11==0.14.0 +httpcore==0.18.0 +httptools==0.6.1 +httpx==0.25.0 +idna==3.4 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.3 +mypy==1.6.1 +mypy-extensions==1.0.0 +orjson==3.9.10 +pydantic==2.4.2 +pydantic-extra-types==2.1.0 +pydantic-settings==2.0.3 +pydantic_core==2.10.1 +python-dotenv==1.0.0 +python-multipart==0.0.6 +PyYAML==6.0.1 +sniffio==1.3.0 +starlette==0.27.0 +tomli==2.0.1 +typing_extensions==4.8.0 +ujson==5.8.0 +uvicorn==0.23.2 +uvloop==0.19.0 +watchfiles==0.21.0 +websockets==12.0 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..ee60be1 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": "Hello World"} diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..8dcb36d --- /dev/null +++ b/src/main.py @@ -0,0 +1,13 @@ +import uvicorn +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": "Hello from Video Service"} + + +if __name__ == '__main__': + uvicorn.run('main:app', reload=True) \ No newline at end of file From 7c478347c74cd144a25fae2453321d79207ccf4f Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Mon, 30 Oct 2023 22:51:57 -0300 Subject: [PATCH 02/35] =?UTF-8?q?adiciona=20configura=C3=A7=C3=B5es=20do?= =?UTF-8?q?=20database?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- requirements.txt | 46 +++++++++++++++++++++++++++++++++------------- src/database.py | 22 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/database.py diff --git a/requirements.txt b/requirements.txt index 2f591c9..4bc8491 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,35 +1,55 @@ +aiosmtplib==2.0.2 annotated-types==0.6.0 anyio==3.7.1 +bcrypt==4.0.1 +blinker==1.6.3 certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.0 click==8.1.7 +coverage==7.3.2 +cryptography==41.0.4 +deprecation==2.1.0 dnspython==2.4.2 -email-validator==2.1.0.post1 -exceptiongroup==1.1.3 -fastapi==0.104.1 +docker==6.1.3 +ecdsa==0.18.0 +email-validator==2.0.0.post2 +fastapi==0.104.0 +fastapi-mail==1.4.1 +greenlet==3.0.0 h11==0.14.0 httpcore==0.18.0 httptools==0.6.1 httpx==0.25.0 idna==3.4 -itsdangerous==2.1.2 +iniconfig==2.0.0 Jinja2==3.1.2 MarkupSafe==2.1.3 -mypy==1.6.1 -mypy-extensions==1.0.0 -orjson==3.9.10 +packaging==23.2 +passlib==1.7.4 +pluggy==1.3.0 +psycopg2==2.9.9 +pyasn1==0.5.0 +pycparser==2.21 pydantic==2.4.2 -pydantic-extra-types==2.1.0 pydantic-settings==2.0.3 pydantic_core==2.10.1 +pytest==7.4.2 python-dotenv==1.0.0 -python-multipart==0.0.6 +python-jose==3.3.0 PyYAML==6.0.1 +requests==2.31.0 +rsa==4.9 +six==1.16.0 sniffio==1.3.0 +SQLAlchemy==2.0.22 starlette==0.27.0 -tomli==2.0.1 +testcontainers==3.7.1 typing_extensions==4.8.0 -ujson==5.8.0 +urllib3==2.0.7 uvicorn==0.23.2 -uvloop==0.19.0 +uvloop==0.18.0 watchfiles==0.21.0 -websockets==12.0 +websocket-client==1.6.4 +websockets==11.0.3 +wrapt==1.15.0 \ No newline at end of file diff --git a/src/database.py b/src/database.py new file mode 100644 index 0000000..f849f76 --- /dev/null +++ b/src/database.py @@ -0,0 +1,22 @@ +import os, sys +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, declarative_base + +POSTGRES_USER = os.getenv("POSTGRES_USER") +POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD") +POSTGRES_HOST = os.getenv("POSTGRES_HOST") +POSTGRES_DB = os.getenv("POSTGRES_DB") +POSTGRES_PORT = os.getenv("POSTGRES_PORT") + +engine = create_engine(f'postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}/{POSTGRES_DB}') + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file From 5849188ab1e4a396802b09a3a80e362e7701edbf Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Mon, 30 Oct 2023 22:52:34 -0300 Subject: [PATCH 03/35] adiciona model de comentario MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- src/model/__init__.py | 0 src/model/commentModel.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/model/__init__.py create mode 100644 src/model/commentModel.py diff --git a/src/model/__init__.py b/src/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/model/commentModel.py b/src/model/commentModel.py new file mode 100644 index 0000000..30e4add --- /dev/null +++ b/src/model/commentModel.py @@ -0,0 +1,17 @@ +# Referencia: https://fastapi.tiangolo.com/tutorial/sql-databases/#create-the-database-models + +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Date +from sqlalchemy.orm import relationship +from datetime import datetime + +from database import Base + +class Comment(Base): + __tablename__ = "comments" + + id = Column(Integer, primary_key=True, index=True) + video_id = Column(Integer, nullable=False) + user_id = Column(Integer, nullable=False) + content = Column(String, nullable=False) + created_at = Column(Date, nullable=False, default=datetime.now()) + \ No newline at end of file From 1ed8e539a3907c6f18b9693f5f909bad9a8dffa0 Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Mon, 30 Oct 2023 22:53:33 -0300 Subject: [PATCH 04/35] =?UTF-8?q?adiciona=20reposit=C3=B3rio=20de=20coment?= =?UTF-8?q?ario?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- src/repository/__init__.py | 0 src/repository/commentRepository.py | 32 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/repository/__init__.py create mode 100644 src/repository/commentRepository.py diff --git a/src/repository/__init__.py b/src/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/repository/commentRepository.py b/src/repository/commentRepository.py new file mode 100644 index 0000000..74c212c --- /dev/null +++ b/src/repository/commentRepository.py @@ -0,0 +1,32 @@ +# Referencia: https://fastapi.tiangolo.com/tutorial/sql-databases/#crud-utils +from sqlalchemy.orm import Session + +from domain import commentSchema +from model import commentModel + +def get_comment_by_id(db: Session, video_id: int): + return db.query(commentModel.Comment).filter(commentModel.Comment.video_id == video_id) + +def get_comments(db: Session, skip: int = 0, limit: int = 100): + return db.query(commentModel.Comment).offset(skip).limit(limit).all() + +def create_comment(db: Session, video_id, user_id, content): + db_comment = commentModel.Comment(video_id=video_id, user_id=user_id, content=content) + db.add(db_comment) + db.commit() + db.refresh(db_comment) + return db_comment + +def update_comment(db: Session, db_comment: commentSchema.Comment, content: commentSchema.CommentUpdate): + comment_data = content.dict(exclude_unset=True) + for key, value in comment_data.items(): + setattr(db_comment, key, value) + + db.add(db_comment) + db.commit() + db.refresh(db_comment) + return db_comment + +def delete_comment(db: Session, db_comment: commentSchema.Comment): + db.delete(db_comment) + db.commit() \ No newline at end of file From 30e4ce73d0460e20d254a52def11753234471743 Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Mon, 30 Oct 2023 22:54:03 -0300 Subject: [PATCH 05/35] adiciona domain e controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- src/controller/__init__.py | 0 src/controller/commentController.py | 18 ++++++++++++++++++ src/domain/__init__.py | 0 src/domain/commentSchema.py | 14 ++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 src/controller/__init__.py create mode 100644 src/controller/commentController.py create mode 100644 src/domain/__init__.py create mode 100644 src/domain/commentSchema.py diff --git a/src/controller/__init__.py b/src/controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/commentController.py b/src/controller/commentController.py new file mode 100644 index 0000000..a6cbfa2 --- /dev/null +++ b/src/controller/commentController.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, HTTPException, Response, status, Depends +from database import get_db +from sqlalchemy.orm import Session + +from domain import commentSchema +from repository import commentRepository + +comment = APIRouter( + prefix="/comments" +) + +@comment.get("/{video_id}", response_model=commentSchema.Comment) +def read_comment(video_id: int, db: Session = Depends(get_db)): + comment = commentRepository.get_comment_by_id(db, video_id) + if not comment: + raise HTTPException(status_code=404, detail="erro") + return comment + diff --git a/src/domain/__init__.py b/src/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/domain/commentSchema.py b/src/domain/commentSchema.py new file mode 100644 index 0000000..775007e --- /dev/null +++ b/src/domain/commentSchema.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel, ConfigDict + +class CommentUpdate(BaseModel): + #user_id: str | None = None + video_id: int | None = None + content: str | None = None + +class Comment(BaseModel): + model_config = ConfigDict(from_attributes = True) + id: int + user_id: str + video_id: int + content: str + \ No newline at end of file From cf10ffa02832c9f872861d85b51214b4db6ed6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:10:00 -0300 Subject: [PATCH 06/35] =?UTF-8?q?Corre=C3=A7=C3=B5es=20para=20conectar=20F?= =?UTF-8?q?ast=20api=20ao=20postgres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 3 +++ docker-compose.yml | 7 +++++++ requirements.txt | 1 + 3 files changed, 11 insertions(+) diff --git a/Dockerfile b/Dockerfile index 8860585..a11d8a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,9 @@ ENV PYTHONBUFFERED 1 COPY . . +RUN apt-get update +RUN apt-get -y install libpq-dev gcc + ENV PYTHONPATH "/app/src" RUN pip install -r requirements.txt diff --git a/docker-compose.yml b/docker-compose.yml index b0c1014..f5c5fba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: - postgres:/var/lib/postgresql/data ports: - "5432:5432" + networks: + - simple-network api: depends_on: @@ -23,7 +25,12 @@ services: - "8081:8081" volumes: - ./:/app + networks: + - simple-network environment: - DEBUG=True +networks: + simple-network: + driver: bridge volumes: postgres: diff --git a/requirements.txt b/requirements.txt index 4bc8491..0e321d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,7 @@ MarkupSafe==2.1.3 packaging==23.2 passlib==1.7.4 pluggy==1.3.0 +psycopg2-binary==2.9.9 psycopg2==2.9.9 pyasn1==0.5.0 pycparser==2.21 From 075180f23aa82187209a021c41d9f371529336ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:29:04 -0300 Subject: [PATCH 07/35] Adiciona .env ao docker compose --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index f5c5fba..9c3d493 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,8 @@ services: container_name: video-service links: - db + env_file: + - .env ports: - "8081:8081" volumes: From adf92d504313cde1d6e99d66b64c77e7443cba7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:29:38 -0300 Subject: [PATCH 08/35] Adiciona a porta a url de acesso ao banco --- src/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database.py b/src/database.py index f849f76..2decd8d 100644 --- a/src/database.py +++ b/src/database.py @@ -8,7 +8,7 @@ POSTGRES_DB = os.getenv("POSTGRES_DB") POSTGRES_PORT = os.getenv("POSTGRES_PORT") -engine = create_engine(f'postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}/{POSTGRES_DB}') +engine = create_engine(f'postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}') SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) From a9429dbf73acaa7a8592b8f65e90439c2d9f87d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:30:12 -0300 Subject: [PATCH 09/35] Adiciona schema de criar comentario --- src/domain/commentSchema.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/domain/commentSchema.py b/src/domain/commentSchema.py index 775007e..5f87489 100644 --- a/src/domain/commentSchema.py +++ b/src/domain/commentSchema.py @@ -1,3 +1,4 @@ +from datetime import date from pydantic import BaseModel, ConfigDict class CommentUpdate(BaseModel): @@ -8,7 +9,14 @@ class CommentUpdate(BaseModel): class Comment(BaseModel): model_config = ConfigDict(from_attributes = True) id: int - user_id: str + user_id: int + video_id: int + content: str + created_at: date + +class CommentCreate(BaseModel): + model_config = ConfigDict(from_attributes = True) + user_id: int video_id: int content: str \ No newline at end of file From e02bb521252d58d91ac16d12805fe1861836f553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:30:55 -0300 Subject: [PATCH 10/35] Corrige metodo de busca comentarios por video_id --- src/repository/commentRepository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository/commentRepository.py b/src/repository/commentRepository.py index 74c212c..a5a86cc 100644 --- a/src/repository/commentRepository.py +++ b/src/repository/commentRepository.py @@ -5,7 +5,7 @@ from model import commentModel def get_comment_by_id(db: Session, video_id: int): - return db.query(commentModel.Comment).filter(commentModel.Comment.video_id == video_id) + return db.query(commentModel.Comment).filter(commentModel.Comment.video_id == video_id).all() def get_comments(db: Session, skip: int = 0, limit: int = 100): return db.query(commentModel.Comment).offset(skip).limit(limit).all() From 2d8038c438821da0ea96fa53936494792445d5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:32:02 -0300 Subject: [PATCH 11/35] Adiciona rota para criar comentario --- src/controller/commentController.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controller/commentController.py b/src/controller/commentController.py index a6cbfa2..ca2f878 100644 --- a/src/controller/commentController.py +++ b/src/controller/commentController.py @@ -9,10 +9,15 @@ prefix="/comments" ) -@comment.get("/{video_id}", response_model=commentSchema.Comment) + +@comment.get("/{video_id}", response_model=list[commentSchema.Comment]) def read_comment(video_id: int, db: Session = Depends(get_db)): - comment = commentRepository.get_comment_by_id(db, video_id) + comment = commentRepository.get_comment_by_id(db, video_id=video_id) if not comment: raise HTTPException(status_code=404, detail="erro") return comment +@comment.post("/", response_model=commentSchema.Comment) +def create_comment(comment: commentSchema.CommentCreate, db: Session = Depends(get_db)): + print(comment) + return commentRepository.create_comment(db=db, video_id=comment.video_id, user_id=comment.user_id, content=comment.content) From fcfb23ae08cb96915e31010cd98d7ea9040e50aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Tue, 31 Oct 2023 10:33:09 -0300 Subject: [PATCH 12/35] Adiciona cors middleware e rota de comentarios --- src/main.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main.py b/src/main.py index 8dcb36d..a68e073 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,29 @@ import uvicorn from fastapi import FastAPI +from controller import commentController +from database import SessionLocal, engine +from model import commentModel +from fastapi.middleware.cors import CORSMiddleware + +commentModel.Base.metadata.create_all(bind=engine) app = FastAPI() +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + @app.get("/") async def root(): return {"message": "Hello from Video Service"} +app.include_router(prefix="/api", router=commentController.comment) + if __name__ == '__main__': uvicorn.run('main:app', reload=True) \ No newline at end of file From ca673db73dda832ce366038fc7d952cced08b3fe Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Wed, 1 Nov 2023 16:10:52 -0300 Subject: [PATCH 13/35] =?UTF-8?q?corrige=20fun=C3=A7=C3=B5es=20de=20busca?= =?UTF-8?q?=20de=20video?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/repository/commentRepository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/repository/commentRepository.py b/src/repository/commentRepository.py index a5a86cc..f8e7a7b 100644 --- a/src/repository/commentRepository.py +++ b/src/repository/commentRepository.py @@ -4,11 +4,11 @@ from domain import commentSchema from model import commentModel -def get_comment_by_id(db: Session, video_id: int): +def get_comments_by_video_id(db: Session, video_id: int): return db.query(commentModel.Comment).filter(commentModel.Comment.video_id == video_id).all() -def get_comments(db: Session, skip: int = 0, limit: int = 100): - return db.query(commentModel.Comment).offset(skip).limit(limit).all() +def get_comment_by_id(db: Session, id: int): + return db.query(commentModel.Comment).filter(commentModel.Comment.id == id).first() def create_comment(db: Session, video_id, user_id, content): db_comment = commentModel.Comment(video_id=video_id, user_id=user_id, content=content) From 6a40579a1ccc84d2719052d98efffa9a80f16871 Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Wed, 1 Nov 2023 16:11:32 -0300 Subject: [PATCH 14/35] =?UTF-8?q?adiciona=20mensagens=20de=20erro=20e=20fu?= =?UTF-8?q?n=C3=A7=C3=A3o=20delete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- .gitignore | 3 ++- src/constants/__init__.py | 0 src/constants/errorMessages.py | 6 ++++++ src/controller/commentController.py | 19 +++++++++++++------ 4 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 src/constants/__init__.py create mode 100644 src/constants/errorMessages.py diff --git a/.gitignore b/.gitignore index 06884cf..1828ab5 100644 --- a/.gitignore +++ b/.gitignore @@ -158,4 +158,5 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -.vscode/ \ No newline at end of file +.vscode/ +.vs/ \ No newline at end of file diff --git a/src/constants/__init__.py b/src/constants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/constants/errorMessages.py b/src/constants/errorMessages.py new file mode 100644 index 0000000..e733a3f --- /dev/null +++ b/src/constants/errorMessages.py @@ -0,0 +1,6 @@ +USER_ID_REQUIRED = "USER ID FIELD REQUIRED" +VIDEO_ID_REQUIRED = "VIDEO ID FIELD REQUIRED" +CONTENT_REQUIRED = "CONTENT FIELD REQUIRED" +COMMENT_NOT_FOUND = "COMMENT NOT FOUND" +MISSING_ENV_VALUES = "SOME ENVIRONMENT VALUES WERE NOT DEFINED" +INVALID_REQUEST = "INVALID_REQUEST" \ No newline at end of file diff --git a/src/controller/commentController.py b/src/controller/commentController.py index ca2f878..02980ee 100644 --- a/src/controller/commentController.py +++ b/src/controller/commentController.py @@ -4,20 +4,27 @@ from domain import commentSchema from repository import commentRepository +from constants import errorMessages comment = APIRouter( prefix="/comments" ) - @comment.get("/{video_id}", response_model=list[commentSchema.Comment]) def read_comment(video_id: int, db: Session = Depends(get_db)): - comment = commentRepository.get_comment_by_id(db, video_id=video_id) - if not comment: - raise HTTPException(status_code=404, detail="erro") + comment = commentRepository.get_comments_by_video_id(db, video_id=video_id) return comment @comment.post("/", response_model=commentSchema.Comment) def create_comment(comment: commentSchema.CommentCreate, db: Session = Depends(get_db)): - print(comment) - return commentRepository.create_comment(db=db, video_id=comment.video_id, user_id=comment.user_id, content=comment.content) + return commentRepository.create_comment(db=db, video_id=comment.video_id, + user_id=comment.user_id, content=comment.content) + +@comment.delete("/{id}", response_model=commentSchema.Comment) +def delete_comment(id: int, db: Session = Depends(get_db)): + comment = commentRepository.get_comment_by_id(db, id) + if not comment: + raise HTTPException(status_code=404, detail=errorMessages.COMMENT_NOT_FOUND) + commentRepository.delete_comment(db,comment) + return comment + From e43d8f7803b6f5549dfbe0364b830efb94e4ccb1 Mon Sep 17 00:00:00 2001 From: Marcos Castilhos <221008300@aluno.unb.br> Date: Wed, 1 Nov 2023 16:26:22 -0300 Subject: [PATCH 15/35] =?UTF-8?q?adiciona=20fun=C3=A7=C3=A3o=20de=20atuali?= =?UTF-8?q?zar=20coment=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Victor --- src/controller/commentController.py | 7 +++++++ src/domain/commentSchema.py | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controller/commentController.py b/src/controller/commentController.py index 02980ee..0910cd5 100644 --- a/src/controller/commentController.py +++ b/src/controller/commentController.py @@ -28,3 +28,10 @@ def delete_comment(id: int, db: Session = Depends(get_db)): commentRepository.delete_comment(db,comment) return comment +@comment.patch("/{id}", response_model=commentSchema.Comment) +def update_comment(id: int, data: commentSchema.CommentUpdate,db: Session = Depends(get_db)): + comment = commentRepository.get_comment_by_id(db, id) + if not comment: + raise HTTPException(status_code=404, detail=errorMessages.COMMENT_NOT_FOUND) + updated_comment = commentRepository.update_comment(db,comment,data) + return updated_comment diff --git a/src/domain/commentSchema.py b/src/domain/commentSchema.py index 5f87489..4600629 100644 --- a/src/domain/commentSchema.py +++ b/src/domain/commentSchema.py @@ -2,8 +2,6 @@ from pydantic import BaseModel, ConfigDict class CommentUpdate(BaseModel): - #user_id: str | None = None - video_id: int | None = None content: str | None = None class Comment(BaseModel): From b83665b92cd2d33122c07ff497020af4f7f84c01 Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Sun, 12 Nov 2023 21:21:47 -0300 Subject: [PATCH 16/35] Adicionado web scraping do site da unb Co-authored-by: cansancaojennifer Co-authored-by: RaisSabeAndrade --- src/controller/gradeController.py | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/controller/gradeController.py diff --git a/src/controller/gradeController.py b/src/controller/gradeController.py new file mode 100644 index 0000000..6b3d565 --- /dev/null +++ b/src/controller/gradeController.py @@ -0,0 +1,32 @@ +from fastapi import APIRouter, HTTPException, Response, status, Depends +from database import get_db +from sqlalchemy.orm import Session + +import requests +from bs4 import BeautifulSoup + +grade = APIRouter( + prefix="/grade" +) + +@grade.get("/{video_id}") +def read_comment(video_id: int, db: Session = Depends(get_db)): + re = requests.get('https://unbtv.unb.br/grade') + html = re.text + soup = BeautifulSoup(html, 'html.parser') + table = soup.find("table") + + programacao = {} + dia_atual = None + + for row in table.find_all("tr"): + if len(row.find_all("td")) == 1: + cells = row.find_all("td")[0] + programacao[cells.text] = [] + dia_atual = cells.text + else: + detalhe = {"horario": "", "nome": "", "producao": "", "descricao": ""} + for index, td in enumerate(row.find_all("td")): + detalhe[list(detalhe.keys())[index]] = td.text.replace('\xa0', '') + + programacao[dia_atual].append(detalhe) From 5fb9a12241452d3d2355392c7523c0eef3ad6232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Mon, 13 Nov 2023 21:53:26 -0300 Subject: [PATCH 17/35] =?UTF-8?q?Adiciona=20nome=20de=20usu=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marcos Castilhos <221008300@aluno.unb.br> --- src/controller/commentController.py | 2 +- src/domain/commentSchema.py | 4 +++- src/model/commentModel.py | 1 + src/repository/commentRepository.py | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/controller/commentController.py b/src/controller/commentController.py index 0910cd5..aa1dde9 100644 --- a/src/controller/commentController.py +++ b/src/controller/commentController.py @@ -18,7 +18,7 @@ def read_comment(video_id: int, db: Session = Depends(get_db)): @comment.post("/", response_model=commentSchema.Comment) def create_comment(comment: commentSchema.CommentCreate, db: Session = Depends(get_db)): return commentRepository.create_comment(db=db, video_id=comment.video_id, - user_id=comment.user_id, content=comment.content) + user_id=comment.user_id, user_name= comment.user_name ,content=comment.content) @comment.delete("/{id}", response_model=commentSchema.Comment) def delete_comment(id: int, db: Session = Depends(get_db)): diff --git a/src/domain/commentSchema.py b/src/domain/commentSchema.py index 4600629..719cb20 100644 --- a/src/domain/commentSchema.py +++ b/src/domain/commentSchema.py @@ -8,6 +8,7 @@ class Comment(BaseModel): model_config = ConfigDict(from_attributes = True) id: int user_id: int + user_name: str video_id: int content: str created_at: date @@ -15,6 +16,7 @@ class Comment(BaseModel): class CommentCreate(BaseModel): model_config = ConfigDict(from_attributes = True) user_id: int + user_name: str video_id: int - content: str + content: str \ No newline at end of file diff --git a/src/model/commentModel.py b/src/model/commentModel.py index 30e4add..aed969a 100644 --- a/src/model/commentModel.py +++ b/src/model/commentModel.py @@ -12,6 +12,7 @@ class Comment(Base): id = Column(Integer, primary_key=True, index=True) video_id = Column(Integer, nullable=False) user_id = Column(Integer, nullable=False) + user_name = Column(String, nullable=False) content = Column(String, nullable=False) created_at = Column(Date, nullable=False, default=datetime.now()) \ No newline at end of file diff --git a/src/repository/commentRepository.py b/src/repository/commentRepository.py index f8e7a7b..c8d23f7 100644 --- a/src/repository/commentRepository.py +++ b/src/repository/commentRepository.py @@ -10,8 +10,8 @@ def get_comments_by_video_id(db: Session, video_id: int): def get_comment_by_id(db: Session, id: int): return db.query(commentModel.Comment).filter(commentModel.Comment.id == id).first() -def create_comment(db: Session, video_id, user_id, content): - db_comment = commentModel.Comment(video_id=video_id, user_id=user_id, content=content) +def create_comment(db: Session, video_id, user_id, user_name,content): + db_comment = commentModel.Comment(video_id=video_id, user_id=user_id,user_name=user_name, content=content) db.add(db_comment) db.commit() db.refresh(db_comment) From edd05a6fa9d305b3d97decbcbf23fbbb3cecc8bd Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Wed, 15 Nov 2023 18:33:36 -0300 Subject: [PATCH 18/35] Adiciona filtro na busca de grade e realiza alguns gerais --- requirements.txt | 7 ++++ src/constants/__init__.py | 0 src/constants/errorMessages.py | 2 + src/controller/__init__.py | 0 src/controller/gradeController.py | 32 ---------------- src/controller/scheduleController.py | 55 ++++++++++++++++++++++++++++ src/domain/__init__.py | 0 src/main.py | 3 ++ src/utils/enumeration.py | 14 +++++++ 9 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 src/constants/__init__.py create mode 100644 src/constants/errorMessages.py create mode 100644 src/controller/__init__.py delete mode 100644 src/controller/gradeController.py create mode 100644 src/controller/scheduleController.py create mode 100644 src/domain/__init__.py create mode 100644 src/utils/enumeration.py diff --git a/requirements.txt b/requirements.txt index 2f591c9..0c12c72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,9 @@ annotated-types==0.6.0 anyio==3.7.1 +beautifulsoup4==4.12.2 +bs4==0.0.1 certifi==2023.7.22 +charset-normalizer==3.3.2 click==8.1.7 dnspython==2.4.2 email-validator==2.1.0.post1 @@ -24,11 +27,15 @@ pydantic_core==2.10.1 python-dotenv==1.0.0 python-multipart==0.0.6 PyYAML==6.0.1 +requests==2.31.0 sniffio==1.3.0 +soupsieve==2.5 starlette==0.27.0 tomli==2.0.1 typing_extensions==4.8.0 ujson==5.8.0 +Unidecode==1.3.7 +urllib3==2.1.0 uvicorn==0.23.2 uvloop==0.19.0 watchfiles==0.21.0 diff --git a/src/constants/__init__.py b/src/constants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/constants/errorMessages.py b/src/constants/errorMessages.py new file mode 100644 index 0000000..719e6b1 --- /dev/null +++ b/src/constants/errorMessages.py @@ -0,0 +1,2 @@ +INVALID_SCHEDULE_DAY = "INVALID SCHEDULE DAY" +ERROR_RETRIEVING_SCHEDULE = "ERROR RETRIEVING SCHEDULE" \ No newline at end of file diff --git a/src/controller/__init__.py b/src/controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/gradeController.py b/src/controller/gradeController.py deleted file mode 100644 index 6b3d565..0000000 --- a/src/controller/gradeController.py +++ /dev/null @@ -1,32 +0,0 @@ -from fastapi import APIRouter, HTTPException, Response, status, Depends -from database import get_db -from sqlalchemy.orm import Session - -import requests -from bs4 import BeautifulSoup - -grade = APIRouter( - prefix="/grade" -) - -@grade.get("/{video_id}") -def read_comment(video_id: int, db: Session = Depends(get_db)): - re = requests.get('https://unbtv.unb.br/grade') - html = re.text - soup = BeautifulSoup(html, 'html.parser') - table = soup.find("table") - - programacao = {} - dia_atual = None - - for row in table.find_all("tr"): - if len(row.find_all("td")) == 1: - cells = row.find_all("td")[0] - programacao[cells.text] = [] - dia_atual = cells.text - else: - detalhe = {"horario": "", "nome": "", "producao": "", "descricao": ""} - for index, td in enumerate(row.find_all("td")): - detalhe[list(detalhe.keys())[index]] = td.text.replace('\xa0', '') - - programacao[dia_atual].append(detalhe) diff --git a/src/controller/scheduleController.py b/src/controller/scheduleController.py new file mode 100644 index 0000000..a05826d --- /dev/null +++ b/src/controller/scheduleController.py @@ -0,0 +1,55 @@ +import requests +from typing import Optional +from fastapi import APIRouter +from bs4 import BeautifulSoup +from unidecode import unidecode +from starlette.responses import JSONResponse + +from utils import enumeration +from constants import errorMessages + +schedule = APIRouter( + prefix="/schedule" +) + +@schedule.get("/") +async def read_comment(day: Optional[str] = None): + if day: + day = unidecode(day).upper() + if not enumeration.ScheduleDaysEnum.has_value(day): + return JSONResponse(status_code=400, content={ "error": errorMessages.INVALID_SCHEDULE_DAY }) + + try: + re = requests.get('https://unbtv.unb.br/grade') + html = re.text + + soup = BeautifulSoup(html, 'html.parser') + tables = soup.find_all("table") + + schedule_data = {} + current_day = None + + for table in tables: + for row in table.find_all("tr"): + if len(row.find_all("td")) == 1: + cell = row.find_all("td")[0] + schedule_day = unidecode(cell.text.replace("-FEIRA", "")) + + if day: + if current_day: + return schedule_data + if day == schedule_day: + current_day = schedule_day + schedule_data[schedule_day] = [] + else: + current_day = schedule_day + schedule_data[schedule_day] = [] + else: + if current_day: + day_schedule = [item.text for item in row.find_all("td")[:2]] + if (day_schedule[0].strip() != "" and day_schedule[1].strip() != ""): + schedule_data[current_day].append({ "time": day_schedule[0], "activity": day_schedule[1] }) + + return schedule_data + except: + return JSONResponse(status_code=400, content={ "error": errorMessages.ERROR_RETRIEVING_SCHEDULE }) diff --git a/src/domain/__init__.py b/src/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main.py b/src/main.py index 8dcb36d..435667e 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,12 @@ import uvicorn from fastapi import FastAPI +from controller import scheduleController app = FastAPI() +app.include_router(prefix="/api", router=scheduleController.schedule) + @app.get("/") async def root(): return {"message": "Hello from Video Service"} diff --git a/src/utils/enumeration.py b/src/utils/enumeration.py new file mode 100644 index 0000000..30ac49a --- /dev/null +++ b/src/utils/enumeration.py @@ -0,0 +1,14 @@ +from enum import Enum + +class ScheduleDaysEnum(Enum): + SEGUNDA = "SEGUNDA" + TERCA = "TERCA" + QUARTA = "QUARTA" + QUINTA = "QUINTA" + SEXTA = "SEXTA" + SABADO = "SABADO" + DOMINGO = "DOMINGO" + + @classmethod + def has_value(cls, value): + return value in cls._value2member_map_ \ No newline at end of file From 34978796243d420899594bece96788c635b1a3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Wed, 15 Nov 2023 18:52:33 -0300 Subject: [PATCH 19/35] Padroniza docker files --- Dockerfile | 12 ++++---- docker-compose.yml | 73 ++++++++++++++++++++++++++-------------------- requirements.txt | 23 +++++++++++++-- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/Dockerfile b/Dockerfile index a11d8a6..c2c1ef8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /app ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONBUFFERED 1 -COPY . . +COPY requirements.txt /app/requirements.txt -RUN apt-get update -RUN apt-get -y install libpq-dev gcc +RUN pip3 install --no-cache-dir -r /app/requirements.txt -ENV PYTHONPATH "/app/src" -RUN pip install -r requirements.txt +COPY . /app/ -EXPOSE 8081 +WORKDIR src -CMD [ "uvicorn", "src.main:app", "--host", "0.0.0.0","--port", "8081","--reload"] \ No newline at end of file +CMD [ "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9c3d493..c255572 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,38 +1,49 @@ -version: "3" - services: - db: - image: "postgres:alpine" - container_name: "db" - env_file: - - .env - volumes: - - postgres:/var/lib/postgresql/data + app: + build: . ports: - - "5432:5432" + - 8001:8001 + volumes: + - .:/app/ + env_file: + - ./.env + environment: + - POSTGRES_HOST=db + depends_on: + db: + condition: service_healthy + restart: always networks: - - simple-network + - backend_videos - api: - depends_on: - - db - build: - context: ./ - container_name: video-service - links: - - db - env_file: - - .env - ports: - - "8081:8081" + db: + image: postgres volumes: - - ./:/app - networks: - - simple-network + - postgres_data:/var/lib/postgresql/data/ + expose: + - 5432 environment: - - DEBUG=True -networks: - simple-network: - driver: bridge + - POSTGRES_HOST=${POSTGRES_HOST} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_DB=${POSTGRES_DB} + env_file: + - ./.env + restart: always + networks: + - backend_videos + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -d ${POSTGRES_DB} -U ${POSTGRES_USER}", + ] + interval: 10s + timeout: 5s + retries: 5 + volumes: - postgres: + postgres_data: + +networks: + backend_videos: diff --git a/requirements.txt b/requirements.txt index 0e321d9..79c2127 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ aiosmtplib==2.0.2 annotated-types==0.6.0 anyio==3.7.1 +astroid==3.0.1 bcrypt==4.0.1 blinker==1.6.3 certifi==2023.7.22 @@ -9,13 +10,17 @@ charset-normalizer==3.3.0 click==8.1.7 coverage==7.3.2 cryptography==41.0.4 +DateTime==5.2 deprecation==2.1.0 +dill==0.3.7 dnspython==2.4.2 docker==6.1.3 ecdsa==0.18.0 email-validator==2.0.0.post2 fastapi==0.104.0 +fastapi-filter==1.0.0 fastapi-mail==1.4.1 +fastapi-sso==0.7.2 greenlet==3.0.0 h11==0.14.0 httpcore==0.18.0 @@ -23,21 +28,32 @@ httptools==0.6.1 httpx==0.25.0 idna==3.4 iniconfig==2.0.0 +isort==5.12.0 Jinja2==3.1.2 MarkupSafe==2.1.3 +mccabe==0.7.0 +oauthlib==3.2.2 packaging==23.2 passlib==1.7.4 +platformdirs==3.11.0 pluggy==1.3.0 psycopg2-binary==2.9.9 -psycopg2==2.9.9 pyasn1==0.5.0 pycparser==2.21 pydantic==2.4.2 pydantic-settings==2.0.3 pydantic_core==2.10.1 +pylint==3.0.2 +PyMySQL==1.1.0 pytest==7.4.2 +pytest-asyncio==0.21.1 +pytest-html==4.1.1 +pytest-metadata==3.0.0 +pytest-mock==3.12.0 python-dotenv==1.0.0 python-jose==3.3.0 +python-multipart==0.0.6 +pytz==2023.3.post1 PyYAML==6.0.1 requests==2.31.0 rsa==4.9 @@ -46,11 +62,12 @@ sniffio==1.3.0 SQLAlchemy==2.0.22 starlette==0.27.0 testcontainers==3.7.1 +tomlkit==0.12.2 typing_extensions==4.8.0 urllib3==2.0.7 uvicorn==0.23.2 -uvloop==0.18.0 watchfiles==0.21.0 websocket-client==1.6.4 websockets==11.0.3 -wrapt==1.15.0 \ No newline at end of file +wrapt==1.15.0 +zope.interface==6.1 \ No newline at end of file From b6958f7b10b8925aa7a2b72ff13c3d9f7db6c29c Mon Sep 17 00:00:00 2001 From: geraldovictor Date: Wed, 15 Nov 2023 22:37:23 -0300 Subject: [PATCH 20/35] Adiciona arquivos para sonar --- sonar-project.properties | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 sonar-project.properties diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..f75fadf --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,21 @@ +sonar.projectKey=fga-eps-mds_2023.2-UnB-TV-Admin +sonar.organization=fga-eps-mds-1 + +sonar.sources=src +sonar.tests=tests + +sonar.exclusions=__pycache__, tests + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=2023.2-UnB-TV-Admin +#sonar.projectVersion=1.0 + + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. + +# Encoding of the source code. Default is default system encoding +sonar.sourceEncoding=UTF-8 + +sonar.python.version=3.11.5 +sonar.python.xunit.reportPath=junit.xml +sonar.python.coverage.reportPaths=coverage.xml \ No newline at end of file From 210a0997b7ce3568ca12781d0c2c6b0a0b71ab3a Mon Sep 17 00:00:00 2001 From: geraldovictor Date: Wed, 15 Nov 2023 22:38:33 -0300 Subject: [PATCH 21/35] Adiciona arquivos para deploys --- Procfile | 1 + runtime.txt | 1 + src/main.py | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Procfile create mode 100644 runtime.txt diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..0ef4803 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: python src/main.py ${PORT} \ No newline at end of file diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..e3d06d7 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.10.12 \ No newline at end of file diff --git a/src/main.py b/src/main.py index a68e073..31c9d54 100644 --- a/src/main.py +++ b/src/main.py @@ -4,6 +4,7 @@ from database import SessionLocal, engine from model import commentModel from fastapi.middleware.cors import CORSMiddleware +import sys commentModel.Base.metadata.create_all(bind=engine) @@ -26,4 +27,8 @@ async def root(): app.include_router(prefix="/api", router=commentController.comment) if __name__ == '__main__': - uvicorn.run('main:app', reload=True) \ No newline at end of file + port = 8081 + if (len(sys.argv) == 2): + port = sys.argv[1] + + uvicorn.run('main:app', reload=True, port=int(port), host="0.0.0.0") \ No newline at end of file From 52f0fcf6afc12bb85737d28e2ce1ef2aa7cf9c3f Mon Sep 17 00:00:00 2001 From: geraldovictor Date: Wed, 15 Nov 2023 23:34:47 -0300 Subject: [PATCH 22/35] Atualiza porta --- src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 31c9d54..3727a28 100644 --- a/src/main.py +++ b/src/main.py @@ -27,7 +27,7 @@ async def root(): app.include_router(prefix="/api", router=commentController.comment) if __name__ == '__main__': - port = 8081 + port = 8001 if (len(sys.argv) == 2): port = sys.argv[1] From fc91dff920f9ba6dcf5214085d4e6ccb639b65ed Mon Sep 17 00:00:00 2001 From: geraldovictor Date: Thu, 16 Nov 2023 00:03:35 -0300 Subject: [PATCH 23/35] Adiciona templates de pr, user stories,features request e bug_report --- .github/templates/bug_report.md | 30 ++++++++++++++++++++++ .github/templates/feature_request.md | 21 +++++++++++++++ .github/templates/pull_request_template.md | 26 +++++++++++++++++++ .github/templates/user_stories.md | 24 +++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 .github/templates/bug_report.md create mode 100644 .github/templates/feature_request.md create mode 100644 .github/templates/pull_request_template.md create mode 100644 .github/templates/user_stories.md diff --git a/.github/templates/bug_report.md b/.github/templates/bug_report.md new file mode 100644 index 0000000..59b4075 --- /dev/null +++ b/.github/templates/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Coloque os detalhes do bug +title: "[BUG] nome da tarefa no verbo infinitivo" +labels: bug +assignees: '' + +--- + +**Descrição do bug** + + +**Para reproduzir o bug** + + +**Comportamento esperado** + + +**Tarefas** + + +**Screenshots** + + +**Adicional** + diff --git a/.github/templates/feature_request.md b/.github/templates/feature_request.md new file mode 100644 index 0000000..6231f15 --- /dev/null +++ b/.github/templates/feature_request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Coloque os detalhes da issue +title: '[Doc] ou [Feature] e o nome da tarefa no verbo infinitivo' +labels: '' +assignees: '' + +--- + +**Descreva sua issue** + + +**Tarefas** + + +**Critérios de Aceitação** + + + +**Contexto adicional** + diff --git a/.github/templates/pull_request_template.md b/.github/templates/pull_request_template.md new file mode 100644 index 0000000..c227171 --- /dev/null +++ b/.github/templates/pull_request_template.md @@ -0,0 +1,26 @@ + + +## Descrição + + +## _Issue_ Relacionada + + + + + + + +## Como Isso Foi Testado? + + + + +## Capturas de Tela (se apropriado): + +## Tipos de Mudanças + +- [ ] _Bug fix_ (alteração que corrige uma _issue_ e não altera funcionalidades já existentes); +- [ ] Nova _feature_ (alteração que adiciona uma funcionalidade e não altera funcionalidades já existentes); +- [ ] Alteração disruptiva (_Breaking change_) (Correção ou funcionalidade que causa alteração nas funcionalidades existentes); +- [ ] Documentação. \ No newline at end of file diff --git a/.github/templates/user_stories.md b/.github/templates/user_stories.md new file mode 100644 index 0000000..2a0ae6b --- /dev/null +++ b/.github/templates/user_stories.md @@ -0,0 +1,24 @@ +--- +name: User Stories +about: Coloque os detalhes da US +title: 'USX' +labels: ux +assignees: '' + +--- + +**Descrição da Issue** + + +**Tarefas** + + +**Protótipo** + + +**Critérios de Aceitação** + From 1cb9f32b1b7c2b9ae2be964c83a72c18507a447d Mon Sep 17 00:00:00 2001 From: geraldovictor Date: Thu, 16 Nov 2023 00:03:47 -0300 Subject: [PATCH 24/35] Configura sonar --- .github/workflows/code-analysis.yml | 57 +++++++++++++++ .github/workflows/release.yml | 40 +++++++++++ scripts/consts.js | 26 +++++++ scripts/package.json | 13 ++++ scripts/release.js | 103 ++++++++++++++++++++++++++++ sonar-project.properties | 13 ++++ 6 files changed, 252 insertions(+) create mode 100644 .github/workflows/code-analysis.yml create mode 100644 .github/workflows/release.yml create mode 100644 scripts/consts.js create mode 100644 scripts/package.json create mode 100644 scripts/release.js create mode 100644 sonar-project.properties diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml new file mode 100644 index 0000000..fb14b7b --- /dev/null +++ b/.github/workflows/code-analysis.yml @@ -0,0 +1,57 @@ +name: Análise de Código +on: push + +jobs: + sonarcloud: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:alpine + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Creating env file + run: | + echo "${{ vars.ENV_FILE }}" > .env + + - name: Setup virtual environment + run: | + python -m venv venv + source venv/bin/activate + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Executa Pytest + run: PYTHONPATH=src python -m coverage run -m pytest --continue-on-collection-errors --junitxml=./junit.xml + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_HOST: localhost + POSTGRES_DB: postgres + POSTGRES_PORT: 5432 + + - name: Gera arquivos de testes no formato .xml + run: python3 -m coverage xml + + - name: Executa SonarCloud Scan + if: ${{ always() }} + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.API_TOKEN_GITHUB }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cd1250b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: Release + +on: + pull_request: + branches: + - main + - develop + types: [ closed ] + +jobs: + release: + if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'NOT RELEASE') == false + runs-on: "ubuntu-latest" + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Cria arquivo .env + run: | + touch ./scripts/.env + echo TOKEN=${{ secrets.API_TOKEN_GITHUB }} >> ./scripts/.env + echo RELEASE_MAJOR=${{ contains(github.event.pull_request.labels.*.name, 'MAJOR RELEASE') }} >> ./scripts/.env + echo RELEASE_MINOR=${{ contains(github.event.pull_request.labels.*.name, 'MINOR RELEASE') }} >> ./scripts/.env + echo RELEASE_FIX=${{ contains(github.event.pull_request.labels.*.name, 'FIX RELEASE') }} >> ./scripts/.env + echo DEVELOP=${{ contains(github.event.pull_request.labels.*.name, 'DEVELOP') }} >> ./scripts/.env + + - name: Gera release e envia métricas para repositório de DOC + run: | + cd scripts && yarn install && node release.js + git config --global user.email "${{secrets.GIT_USER_EMAIL}}" + git config --global user.name "${{secrets.GIT_USER_NAME}}" + git clone --single-branch --branch main "https://x-access-token:${{secrets.API_TOKEN_GITHUB}}@github.com/fga-eps-mds/${{secrets.GIT_DOC_REPO}}" ${{secrets.GIT_DOC_REPO}} + mkdir -p ${{secrets.GIT_DOC_REPO}}/analytics-raw-data + cp -R analytics-raw-data/*.json ${{secrets.GIT_DOC_REPO}}/analytics-raw-data + cd ${{secrets.GIT_DOC_REPO}} + git add . + git commit -m "Adicionando métricas do repositório ${{ github.event.repository.name }} ${{ github.ref_name }}" + git push \ No newline at end of file diff --git a/scripts/consts.js b/scripts/consts.js new file mode 100644 index 0000000..c57cf32 --- /dev/null +++ b/scripts/consts.js @@ -0,0 +1,26 @@ +const REPO = '2023.2-UnB-TV-VideoService'; // Nome do repositório +const OWNER = 'fga-eps-mds'; +const SONAR_ID = 'fga-eps-mds_2023.2-UnB-TV-VideoService'; // Id do projeto no SonarCloud +const METRIC_LIST = [ + 'files', + 'functions', + 'complexity', + 'comment_lines_density', + 'duplicated_lines_density', + 'coverage', + 'ncloc', + 'tests', + 'test_errors', + 'test_failures', + 'test_execution_time', + 'security_rating', +]; +const SONAR_URL = `https://sonarcloud.io/api/measures/component_tree?component=${SONAR_ID}&metricKeys=${METRIC_LIST.join( + ',' +)}`; + +module.exports = { + SONAR_URL, + REPO, + OWNER, +}; diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..9443de6 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,13 @@ +{ + "name": "scripts", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@octokit/core": "^3.4.0", + "axios": "^0.21.1", + "dotenv": "^8.2.0", + "fs": "^0.0.1-security", + "gh-release-assets": "^2.0.0" + } +} \ No newline at end of file diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..c373926 --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,103 @@ +const { Octokit } = require('@octokit/core'); +const ghReleaseAssets = require('gh-release-assets'); +const axios = require('axios'); +const fs = require('fs'); +require('dotenv').config(); + +const { SONAR_URL, REPO, OWNER } = require('./consts.js'); + +const { TOKEN, RELEASE_MAJOR, RELEASE_MINOR, RELEASE_FIX, DEVELOP } = process.env; + +const octokit = new Octokit({ auth: TOKEN }); + +const now = new Date(); +const year = now.getFullYear().toString(); +const month = (now.getMonth() + 1).toString().padStart(2, '0'); +const day = now.getDate().toString().padStart(2, '0'); +const hours = now.getHours().toString().padStart(2, '0'); +const minutes = now.getMinutes().toString().padStart(2, '0'); +const seconds = now.getSeconds().toString().padStart(2, '0'); + +const getLatestRelease = async () => { + const releases = await octokit.request('GET /repos/{owner}/{repo}/releases', { + owner: OWNER, + repo: REPO, + }); + if (releases?.data.length > 0) { + return releases?.data?.[0]?.tag_name; + } + return '0.0.0'; +}; + +const newTagName = async () => { + let oldTag = await getLatestRelease(); + oldTag = oldTag.split('.'); + + if (RELEASE_MAJOR === 'true') { + const majorTagNum = parseInt(oldTag[0]) + 1; + return `${majorTagNum}.0.0`; + } + if (RELEASE_MINOR === 'true') { + const minorTagNum = parseInt(oldTag[1]) + 1; + return `${oldTag[0]}.${minorTagNum}.0`; + } + if (RELEASE_FIX === 'true') { + const fixTagNum = parseInt(oldTag[2]) + 1; + return `${oldTag[0]}.${oldTag[1]}.${fixTagNum}`; + } + if (DEVELOP === 'true') { + return `develop`; + } + // Caso não tenha nenhuma flag de release, é feito um release de fix + const fixTagNum = parseInt(oldTag[2]) + 1; + return `${oldTag[0]}.${oldTag[1]}.${fixTagNum}`; +}; + +const createRelease = async () => { + const tag = await newTagName(); + const res = await octokit.request('POST /repos/{owner}/{repo}/releases', { + owner: OWNER, + repo: REPO, + tag_name: tag, + name: tag, + }); + return [res?.data?.upload_url, tag]; +}; + +const saveSonarFile = async (tag) => { + const dirPath = './analytics-raw-data/'; + let filePath = `${dirPath}fga-eps-mds-${REPO}-${month}-${day}-${year}-${hours}-${minutes}-${seconds}-v${tag}.json`; + fs.mkdirSync(dirPath); + if(tag === 'develop') { + filePath = `${dirPath}fga-eps-mds-${REPO}-${month}-${day}-${year}-${hours}-${minutes}-${seconds}-${tag}.json`; + } + await axios.get(SONAR_URL).then((res) => { + fs.writeFileSync(filePath, JSON.stringify(res?.data)); + }); +}; + +const uploadSonarFile = async (release) => { + await saveSonarFile(release[1]); + ghReleaseAssets({ + url: release[0], + token: [TOKEN], + assets: [ + `./analytics-raw-data/fga-eps-mds-${REPO}-${month}-${day}-${year}-${hours}-${minutes}-${seconds}-v${release[1]}.json`, + { + name: `fga-eps-mds-${REPO}-${month}-${day}-${year}-${hours}-${minutes}-${seconds}-v${release[1]}.json`, + path: '', + }, + ], + }); +}; + +const script = async () => { + if(DEVELOP === 'true') { + await saveSonarFile('develop'); + return; + } + const release = await createRelease(); + await uploadSonarFile(release); +}; + +script(); diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..98f03fe --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=fga-eps-mds_2023.2-UnB-TV-VideoService +sonar.organization=fga-eps-mds-1 + +sonar.sources=src +sonar.tests=tests + +sonar.exclusions=__pycache__, tests + +sonar.sourceEncoding=UTF-8 + +sonar.python.version=3.11.5 +sonar.python.xunit.reportPath=junit.xml +sonar.python.coverage.reportPaths=coverage.xml \ No newline at end of file From b0bf41b47f565d526daf8fac502ffd916925225d Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Thu, 16 Nov 2023 09:44:04 -0300 Subject: [PATCH 25/35] Adiciona testes --- requirements.txt | 5 +++++ src/controller/scheduleController.py | 4 ++-- tests/__init__.py | 0 tests/test_schedule.py | 29 ++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_schedule.py diff --git a/requirements.txt b/requirements.txt index 0c12c72..6c97151 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ bs4==0.0.1 certifi==2023.7.22 charset-normalizer==3.3.2 click==8.1.7 +coverage==7.3.2 dnspython==2.4.2 email-validator==2.1.0.post1 exceptiongroup==1.1.3 @@ -14,16 +15,20 @@ httpcore==0.18.0 httptools==0.6.1 httpx==0.25.0 idna==3.4 +iniconfig==2.0.0 itsdangerous==2.1.2 Jinja2==3.1.2 MarkupSafe==2.1.3 mypy==1.6.1 mypy-extensions==1.0.0 orjson==3.9.10 +packaging==23.2 +pluggy==1.3.0 pydantic==2.4.2 pydantic-extra-types==2.1.0 pydantic-settings==2.0.3 pydantic_core==2.10.1 +pytest==7.4.3 python-dotenv==1.0.0 python-multipart==0.0.6 PyYAML==6.0.1 diff --git a/src/controller/scheduleController.py b/src/controller/scheduleController.py index a05826d..9a955a0 100644 --- a/src/controller/scheduleController.py +++ b/src/controller/scheduleController.py @@ -13,11 +13,11 @@ ) @schedule.get("/") -async def read_comment(day: Optional[str] = None): +async def get_schedule_day(day: Optional[str] = None): if day: day = unidecode(day).upper() if not enumeration.ScheduleDaysEnum.has_value(day): - return JSONResponse(status_code=400, content={ "error": errorMessages.INVALID_SCHEDULE_DAY }) + return JSONResponse(status_code=400, content={ "detail": errorMessages.INVALID_SCHEDULE_DAY }) try: re = requests.get('https://unbtv.unb.br/grade') diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_schedule.py b/tests/test_schedule.py new file mode 100644 index 0000000..d6ec4b4 --- /dev/null +++ b/tests/test_schedule.py @@ -0,0 +1,29 @@ +import pytest +from fastapi.testclient import TestClient + +from src.main import app +from src.constants import errorMessages + +client = TestClient(app) + +class TestSchedule: + def test_schedule_get_schedule_day(self, mocker): + response = client.get("/api/schedule/") + data = response.json() + assert response.status_code == 200 + assert len(list(data.keys())) == 7 + assert all([a == b for a, b in zip(list(data.keys()), ['SEGUNDA', 'TERCA', 'QUARTA', 'QUINTA', 'SEXTA', 'SABADO', 'DOMINGO'])]) + + def test_schedule_get_schedule_specific_day_invalid(self): + params = { 'day': 'INVALID' } + response = client.get("/api/schedule/", params=params) + data = response.json() + assert response.status_code == 400 + assert data['detail'] == errorMessages.INVALID_SCHEDULE_DAY + + def test_schedule_get_schedule_specific_day(self): + params = { 'day': 'segunda' } + response = client.get("/api/schedule/", params=params) + data = response.json() + assert response.status_code == 200 + assert len(data[params['day'].upper()]) > 0 \ No newline at end of file From 6b177cff4d3b2eec1de4a86cda3116fac1bb97ae Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Thu, 16 Nov 2023 11:40:56 -0300 Subject: [PATCH 26/35] =?UTF-8?q?Adiciona=20CORS=20e=20ajusta=20inicializa?= =?UTF-8?q?=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 20 +++++++++++++++++--- tests/test_schedule.py | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index 435667e..757e5ac 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,20 @@ -import uvicorn +import uvicorn, sys from fastapi import FastAPI from controller import scheduleController +from fastapi.middleware.cors import CORSMiddleware app = FastAPI() +origins = ["*"] + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + expose_headers=["*"] +) app.include_router(prefix="/api", router=scheduleController.schedule) @@ -11,6 +22,9 @@ async def root(): return {"message": "Hello from Video Service"} - if __name__ == '__main__': - uvicorn.run('main:app', reload=True) \ No newline at end of file + port = 8081 + if (len(sys.argv) == 2): + port = sys.argv[1] + + uvicorn.run('main:app', reload=True, port=int(port), host="0.0.0.0") \ No newline at end of file diff --git a/tests/test_schedule.py b/tests/test_schedule.py index d6ec4b4..e45753b 100644 --- a/tests/test_schedule.py +++ b/tests/test_schedule.py @@ -7,7 +7,7 @@ client = TestClient(app) class TestSchedule: - def test_schedule_get_schedule_day(self, mocker): + def test_schedule_get_schedule_day(self): response = client.get("/api/schedule/") data = response.json() assert response.status_code == 200 @@ -26,4 +26,4 @@ def test_schedule_get_schedule_specific_day(self): response = client.get("/api/schedule/", params=params) data = response.json() assert response.status_code == 200 - assert len(data[params['day'].upper()]) > 0 \ No newline at end of file + assert len(data) > 0 \ No newline at end of file From c9daf6e3d0c87eb58c872cc60eaca23a6ba4f13b Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Thu, 16 Nov 2023 15:41:42 -0300 Subject: [PATCH 27/35] =?UTF-8?q?Adiciona=20testes=20da=20cria=C3=A7=C3=A3?= =?UTF-8?q?o=20de=20coment=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/commentController.py | 3 +- src/database.py | 6 ++- src/main.py | 11 +++-- src/model/commentModel.py | 1 + tests/test_comments.py | 75 +++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tests/test_comments.py diff --git a/src/controller/commentController.py b/src/controller/commentController.py index aa1dde9..44f850e 100644 --- a/src/controller/commentController.py +++ b/src/controller/commentController.py @@ -17,8 +17,7 @@ def read_comment(video_id: int, db: Session = Depends(get_db)): @comment.post("/", response_model=commentSchema.Comment) def create_comment(comment: commentSchema.CommentCreate, db: Session = Depends(get_db)): - return commentRepository.create_comment(db=db, video_id=comment.video_id, - user_id=comment.user_id, user_name= comment.user_name ,content=comment.content) + return commentRepository.create_comment(db=db, video_id=comment.video_id, user_id=comment.user_id, user_name= comment.user_name ,content=comment.content) @comment.delete("/{id}", response_model=commentSchema.Comment) def delete_comment(id: int, db: Session = Depends(get_db)): diff --git a/src/database.py b/src/database.py index 2decd8d..923804f 100644 --- a/src/database.py +++ b/src/database.py @@ -6,9 +6,11 @@ POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD") POSTGRES_HOST = os.getenv("POSTGRES_HOST") POSTGRES_DB = os.getenv("POSTGRES_DB") -POSTGRES_PORT = os.getenv("POSTGRES_PORT") +POSTGRES_PORT = os.getenv("POSTGRES_PORT", default=5432) -engine = create_engine(f'postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}') +POSTGRES_URI = f'postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}' + +engine = create_engine(POSTGRES_URI) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/src/main.py b/src/main.py index 3727a28..9af903e 100644 --- a/src/main.py +++ b/src/main.py @@ -1,10 +1,13 @@ -import uvicorn +import uvicorn, sys from fastapi import FastAPI +from dotenv import load_dotenv +from fastapi.middleware.cors import CORSMiddleware + +load_dotenv() + from controller import commentController from database import SessionLocal, engine from model import commentModel -from fastapi.middleware.cors import CORSMiddleware -import sys commentModel.Base.metadata.create_all(bind=engine) @@ -26,7 +29,7 @@ async def root(): app.include_router(prefix="/api", router=commentController.comment) -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover port = 8001 if (len(sys.argv) == 2): port = sys.argv[1] diff --git a/src/model/commentModel.py b/src/model/commentModel.py index aed969a..0c5fd42 100644 --- a/src/model/commentModel.py +++ b/src/model/commentModel.py @@ -8,6 +8,7 @@ class Comment(Base): __tablename__ = "comments" + __table_args__ = {'extend_existing': True} id = Column(Integer, primary_key=True, index=True) video_id = Column(Integer, nullable=False) diff --git a/tests/test_comments.py b/tests/test_comments.py new file mode 100644 index 0000000..9d38ae1 --- /dev/null +++ b/tests/test_comments.py @@ -0,0 +1,75 @@ +import pytest +from fastapi.testclient import TestClient + +from src.main import app +from src.model import commentModel +from src.database import engine +from src.constants import errorMessages + +client = TestClient(app) + +comment = { + 'user_id': 1, + 'user_name': 'Lucas', + 'video_id': 1, + 'content': 'Comentario' +} + +comment_update = { + 'content': 'Comentario Atualizado' +} + +class TestVideoComment: + @pytest.fixture(scope="session", autouse=True) + def setup(self): + response = client.post('/api/comments/', json=comment) + print(response) + + yield + + commentModel.Base.metadata.drop_all(bind=engine) + + def test_root(self, setup): + response = client.get('/') + data = response.json() + assert response.status_code == 200 + assert data["message"] == "Hello from Video Service" + + def test_comment_read_comment(self, setup): + response = client.get('/api/comments/1') + data = response.json() + assert response.status_code == 200 + assert len(data) == 1 + assert data[0]['user_id'] == comment['user_id'] + assert data[0]['user_name'] == comment['user_name'] + assert data[0]['video_id'] == comment['video_id'] + assert data[0]['content'] == comment['content'] + + def test_comment_update_comment_not_found(self, setup): + response = client.patch('/api/comments/2', json=comment_update) + data = response.json() + assert response.status_code == 404 + assert data['detail'] == errorMessages.COMMENT_NOT_FOUND + + def test_comment_update_comment(self, setup): + response = client.patch('/api/comments/1', json=comment_update) + data = response.json() + assert response.status_code == 200 + assert data['user_id'] == comment['user_id'] + assert data['user_name'] == comment['user_name'] + assert data['video_id'] == comment['video_id'] + assert data['content'] == comment_update['content'] + + def test_comment_delete_comment_not_found(self, setup): + response = client.delete('/api/comments/2') + data = response.json() + assert response.status_code == 404 + assert data['detail'] == errorMessages.COMMENT_NOT_FOUND + + def test_comment_delete_comment(self, setup): + response = client.delete('/api/comments/1') + data = response.json() + assert response.status_code == 200 + assert data['user_id'] == comment['user_id'] + assert data['user_name'] == comment['user_name'] + assert data['video_id'] == comment['video_id'] \ No newline at end of file From 4520632c958d826265dea9af6b2506c918fa8fc4 Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Thu, 16 Nov 2023 15:43:47 -0300 Subject: [PATCH 28/35] Limpa __init__.py --- src/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index ee60be1..e69de29 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,8 +0,0 @@ -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -async def root(): - return {"message": "Hello World"} From e55e40f7097cae82694eee74191a49b5b226a53f Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Thu, 16 Nov 2023 15:45:42 -0300 Subject: [PATCH 29/35] =?UTF-8?q?Atualiza=20teste=20de=20cria=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20comentario?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_comments.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_comments.py b/tests/test_comments.py index 9d38ae1..9fb4c60 100644 --- a/tests/test_comments.py +++ b/tests/test_comments.py @@ -23,7 +23,12 @@ class TestVideoComment: @pytest.fixture(scope="session", autouse=True) def setup(self): response = client.post('/api/comments/', json=comment) - print(response) + data = response.json() + assert response.status_code == 200 + assert data['user_id'] == comment['user_id'] + assert data['user_name'] == comment['user_name'] + assert data['video_id'] == comment['video_id'] + assert data['content'] == comment['content'] yield From dee51c22c69b656c0431edaf79a6831abfa7bb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Fri, 17 Nov 2023 16:10:28 -0300 Subject: [PATCH 30/35] Fix deploy --- src/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.py b/src/main.py index 9af903e..09e708d 100644 --- a/src/main.py +++ b/src/main.py @@ -13,6 +13,7 @@ app = FastAPI() +origins = ["*"] app.add_middleware( CORSMiddleware, From 012fa04e81e4283d4561c368c93d1e42a6f118a8 Mon Sep 17 00:00:00 2001 From: lcsAndrade Date: Mon, 20 Nov 2023 09:34:58 -0300 Subject: [PATCH 31/35] Adiciona controller de grade --- src/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.py b/src/main.py index 09e708d..98c0647 100644 --- a/src/main.py +++ b/src/main.py @@ -5,7 +5,7 @@ load_dotenv() -from controller import commentController +from controller import commentController, scheduleController from database import SessionLocal, engine from model import commentModel @@ -23,13 +23,13 @@ allow_headers=["*"], ) +app.include_router(prefix="/api", router=commentController.comment) +app.include_router(prefix="/api", router=scheduleController.schedule) + @app.get("/") async def root(): return {"message": "Hello from Video Service"} - -app.include_router(prefix="/api", router=commentController.comment) - if __name__ == '__main__': # pragma: no cover port = 8001 if (len(sys.argv) == 2): From 14a032543df73ce52eee8c5cc97875c2e43aea5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Wed, 22 Nov 2023 20:07:00 -0300 Subject: [PATCH 32/35] Teste release --- src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 09e708d..b111b79 100644 --- a/src/main.py +++ b/src/main.py @@ -25,7 +25,7 @@ @app.get("/") async def root(): - return {"message": "Hello from Video Service"} + return {"message": "Hello from Video Service!"} app.include_router(prefix="/api", router=commentController.comment) From 18c70fc37517a3e6766493a12d00e6c37b636802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Wed, 22 Nov 2023 20:12:43 -0300 Subject: [PATCH 33/35] Resolve teste --- src/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index 9ce494d..09e708d 100644 --- a/src/main.py +++ b/src/main.py @@ -5,7 +5,7 @@ load_dotenv() -from controller import commentController, scheduleController +from controller import commentController from database import SessionLocal, engine from model import commentModel @@ -23,12 +23,12 @@ allow_headers=["*"], ) -app.include_router(prefix="/api", router=commentController.comment) -app.include_router(prefix="/api", router=scheduleController.schedule) - @app.get("/") async def root(): - return {"message": "Hello from Video Service!"} + return {"message": "Hello from Video Service"} + + +app.include_router(prefix="/api", router=commentController.comment) if __name__ == '__main__': # pragma: no cover port = 8001 From 9e8da2b9f02bec7246eb310bbb1ab3157542caf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Wed, 22 Nov 2023 20:18:14 -0300 Subject: [PATCH 34/35] Resolve teste --- src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index 9ce494d..98c0647 100644 --- a/src/main.py +++ b/src/main.py @@ -28,7 +28,7 @@ @app.get("/") async def root(): - return {"message": "Hello from Video Service!"} + return {"message": "Hello from Video Service"} if __name__ == '__main__': # pragma: no cover port = 8001 From 8839f71c1f55abfbd0ef0420670969f02d78cdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor?= Date: Wed, 22 Nov 2023 20:21:54 -0300 Subject: [PATCH 35/35] Corrige commit feito errado --- src/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.py b/src/main.py index 09e708d..98c0647 100644 --- a/src/main.py +++ b/src/main.py @@ -5,7 +5,7 @@ load_dotenv() -from controller import commentController +from controller import commentController, scheduleController from database import SessionLocal, engine from model import commentModel @@ -23,13 +23,13 @@ allow_headers=["*"], ) +app.include_router(prefix="/api", router=commentController.comment) +app.include_router(prefix="/api", router=scheduleController.schedule) + @app.get("/") async def root(): return {"message": "Hello from Video Service"} - -app.include_router(prefix="/api", router=commentController.comment) - if __name__ == '__main__': # pragma: no cover port = 8001 if (len(sys.argv) == 2):