Skip to content

Commit

Permalink
Merge branch 'develop' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
wouerner authored Oct 22, 2024
2 parents 9be1a94 + 85289f8 commit f476242
Show file tree
Hide file tree
Showing 19 changed files with 454 additions and 94 deletions.
5 changes: 3 additions & 2 deletions .docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ FROM python:3.12

WORKDIR /code

COPY ./requirements.txt /code/requirements.txt
# COPY ./requirements.txt /code/requirements.txt
COPY ./requirements_lock.txt /code/requirements_lock.txt

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
RUN pip install --no-cache-dir -r /code/requirements_lock.txt

COPY ./app /code/app

Expand Down
2 changes: 1 addition & 1 deletion .docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ services:

volumes:
mysql_database:
name: mysql_database
name: mysql_database
45 changes: 45 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python


name: Python application

on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v3
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Format with Black
run: black .

- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest and coverage
run: |
pytest --cov=app --cov-report=term --cov-fail-under=100
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM python:3.12

WORKDIR /code

COPY ./requirements.txt /code/requirements.txt

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY ./app /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,100 @@
# stars-api

### Adicionando Verificações de Qualidade de Código

1. **Adicionar Verificação de Formatação com Black e Padrão PEP8:**

O PEP 8 define regras para a indentação, uso de espaços em branco, nomes de variáveis, entre outros aspectos do estilo de código. Ao adicionar a verificação do Black no GitHub Actions, estaremos garantindo que o código esteja formatado de acordo com as recomendações do PEP 8.

- Para instalar o Black, utilize o comando:
```bash
pip install black
```
- Para executar o Black, utilize:
```bash
black <nome_do_seu_arquivo_ou_diretório>
```

2. **Adicionar Verificação do Pytest com 100% de Cobertura:**

O Pytest é uma biblioteca de testes unitários que permite escrever testes simples e escaláveis em Python. Ele fornece suporte para detecção automática de testes, relatórios detalhados e plugins personalizados.

**Cobertura:**
A biblioteca Coverage é usada para medir a cobertura de testes do código-fonte Python. Ela ajuda a identificar áreas do código que não estão sendo testadas, fornecendo relatórios sobre a porcentagem de código coberto pelos testes.

- Para instalar o Pytest e o pytest-cov, utilize o comando:
```bash
pip install pytest pytest-cov
```
- Para executar os testes com o Pytest e calcular a cobertura de código, utilize o pytest-cov diretamente no comando Pytest:
```bash
pytest --cov=.
```
- Para cobrir todo o código ou
```bash
pytest --cov=app
```
- para cobrir apenas o diretório app.

3. **Adicionar Verificação do Flake8:**

O Flake8 é uma ferramenta de verificação de código que combina as funcionalidades de outras ferramentas populares. Ele verifica o estilo do código, identifica problemas potenciais e fornece sugestões de melhoria.

- Para instalar o Flake8, utilize o comando:
```bash
pip install flake8
```
- Para verificar seu código com o Flake8, utilize o seguinte comando:
```bash
flake8
```


# Documentação
## Este projeto é um aplicativo Python com FastApi com todo o ambiente de execução encapsulado em Docker.

### ambiente virtual
python3 -m venv env

### ativação do ambiente
source env/bin/activate

### install nas dependencias
pip install -r requirements.txt

#### Atulização nas dependencias
pip freeze > requirements.txt

#### Comando para criar a imagem docker no projeto
docker build -t backoffice -f .docker/Dockerfile .

#### Configurações de vulnerabilidade da imagem sugerida pelo docker
docker scout cves local://backoffice:latest
docker scout recommendations local://backoffice:latest

### Comando para checar se a imagem foi criada
docker images

### Executar o container e verificar se esta em execução
docker run -d -p 80:80 nome_do_container
docker ps

### Comandos para criar/subir os containers
docker-compose up
docker-compose ps

### Parar containers
docker compose down

### Comandos uteis docker
docker-compose stop
docker-compose start
docker-compose restart

### Porta e swagger
http://localhost:8000/docs

### Parar o servidor
docker stop <seu id> 65d05c5e44806478fd97914e8ecdb61a3a1b530686b20640da7c68e5717ec7a3


2 changes: 1 addition & 1 deletion alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@ def run_migrations_online() -> None:
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
run_migrations_online()
71 changes: 71 additions & 0 deletions app/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from typing import Annotated, Union
from fastapi import Depends, HTTPException, status
from pydantic import BaseModel
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.database import get_db, SessionLocal
from datetime import datetime, timedelta
from app.settings import settings
from app.schemas import UserAuth, TokenData
from app.utils import get_user_by_username

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Configuração de criptografia de senhas
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str):
return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
return pwd_context.hash(password)

def authenticate_user(db: Session, email: str, password: str):
user = get_user_by_username(db, email)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None) -> str:

if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
data.update({"exp": expire})
encoded_jwt = jwt.encode(data, settings.JWT_SECRETE_KEY, algorithm=settings.PASSWORD_HASH_ALGORITHM)
return encoded_jwt

def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)

try:
payload = jwt.decode(token, settings.JWT_SECRETE_KEY, algorithms=[settings.PASSWORD_HASH_ALGORITHM])
email: Union[str, None] = payload.get("sub")
if not email is None:
raise credentials_exception
token_data = TokenData(username=email)
except JWTError:
raise credentials_exception

if not token_data.username is None:
raise credentials_exception

user = get_user_by_username(db, token_data.username) #type:ignore
if not user is None:
raise credentials_exception

return user

def get_current_active_user(current_user: UserAuth = Depends(get_current_user)):
if not current_user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
63 changes: 24 additions & 39 deletions app/crud.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
from sqlalchemy.orm import Session

from sqlalchemy import create_engine, Column, Integer, String, Boolean, func

from . import models, schemas


def get_user(db: Session, user_id: int):
return db.query(models.User).filter(models.User.id == user_id).first()


def get_user_by_email(db: Session, email: str):
return db.query(models.User).filter(models.User.email == email).first()


def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.User).offset(skip).limit(limit).all()
from app.auth import get_password_hash


def create_user(db: Session, user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
hashed_password = get_password_hash(user.password)
db_user = models.User(
username=user.username,
email=user.email,
hashed_password=hashed_password,
is_active=True,
)
db.add(db_user)
db.commit()
db.refresh(db_user)
Expand All @@ -39,15 +31,16 @@ def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):

def get_volunteers(db: Session, skip: int = 0, limit: int = 100):
return db.query(
models.Volunteer.id,
models.Volunteer.name,
func.replace(
models.Volunteer.email,
func.substr(models.Volunteer.email, 1, func.instr(models.Volunteer.email, '@') - 1),
'***').label("masked_email"),
models.Volunteer.is_active,
models.Volunteer.jobtitle_id,
# models.Volunteer.email
models.Volunteer.id,
models.Volunteer.name,
func.replace(
models.Volunteer.email,
func.substr(models.Volunteer.email, 1, func.instr(models.Volunteer.email, '@') - 1),
'***').label("masked_email"),
models.Volunteer.is_active,
models.Volunteer.jobtitle_id,
models.Volunteer.linkedin,
# models.Volunteer.email
).all()
# return db.query(models.Volunteer).offset(skip).limit(limit).all()

Expand All @@ -57,33 +50,25 @@ def get_volunteers_by_email(db: Session, skip: int = 0, limit: int = 100, email:
models.Volunteer.id,
models.Volunteer.name,
func.replace(
models.Volunteer.email,
models.Volunteer.email,
func.substr(models.Volunteer.email, 1, func.instr(models.Volunteer.email, '@') - 1),
'***').label("masked_email"),
models.Volunteer.is_active,
models.Volunteer.jobtitle_id,
).filter(models.Volunteer.email == email).first()

def create_volunteer(db: Session, volunteer: schemas.Volunteer, jobtitle_id: int):
# print(volunteer.jobtitle_id[0].id)
# return

db_volunteer = models.Volunteer(
name=volunteer.name,
email=volunteer.email,
linkedin=volunteer.linkedin,
is_active=volunteer.is_active,
jobtitle_id=jobtitle_id
)
def create_volunteer(db: Session, volunteer: schemas.VolunteerCreate, jobtitle_id: int):
db_volunteer = models.Volunteer(**volunteer.dict())
db.add(db_volunteer)
db.commit()
db.refresh(db_volunteer)
print("db_volunteer",db_volunteer)
return db_volunteer


def get_jobtitles(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.JobTitle).offset(skip).limit(limit).all()

def get_volunteer_by_email(db: Session, email: str):
return db.query(models.Volunteer).filter(models.Volunteer.email == email).first()

def get_volunteer_by_email(db: Session, email: str):
return db.query(models.Volunteer).filter(models.Volunteer.email == email).first()
16 changes: 13 additions & 3 deletions app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from dotenv import load_dotenv
from .settings import settings

load_dotenv()

SQLALCHEMY_DATABASE_URL = f"{os.getenv('DB_DRIVER')}://{os.getenv('DB_USERNAME')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_DATABASE')}"
SQLALCHEMY_DATABASE_URL = (
f"{settings.DB_DRIVER}://{settings.DB_USERNAME}:{settings.DB_PASSWORD}"
f"@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_DATABASE}"
)

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()
metadata = Base.metadata

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Loading

0 comments on commit f476242

Please sign in to comment.