Skip to content

Commit 7974eef

Browse files
Merge remote-tracking branch 'origin/main' into 15-criação-endpoint-news---post
2 parents 2a56d14 + cb91920 commit 7974eef

File tree

17 files changed

+308
-66
lines changed

17 files changed

+308
-66
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ cython_debug/
182182
.abstra/
183183

184184
# Visual Studio Code
185-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
185+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186186
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
187+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
188188
# you could uncomment the following to ignore the entire vscode folder
189189
# .vscode/
190190

.pre-commit-config.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v2.3.0
4+
hooks:
5+
- id: check-yaml
6+
- id: end-of-file-fixer
7+
- id: trailing-whitespace
8+
- repo: https://github.com/astral-sh/ruff-pre-commit
9+
rev: v0.12.5
10+
hooks:
11+
- id: ruff-check
12+
args: [ --fix , --diff, format]

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@
2929
"justMyCode": false
3030
}
3131
]
32-
}
32+
}

.vscode/settings.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
"source.organizeImports": "explicit"
77
},
88
"editor.defaultFormatter": "charliermarsh.ruff"
9-
}
10-
}
9+
},
10+
"python-envs.defaultEnvManager": "ms-python.python:poetry",
11+
"python-envs.defaultPackageManager": "ms-python.python:poetry",
12+
"python-envs.pythonProjects": []
13+
}

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ WORKDIR $PROJECT_PATH
6060
COPY app app
6161
COPY tests tests
6262

63-
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--lifespan", "on"]
63+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--lifespan", "on"]

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ Serviço de Noticas e Bibliotecas PyNews
44
## 💡 Visão Geral
55

66
## 💻 Tecnologias Utilizadas
7-
- Python
8-
- FastAPI
7+
- Python
8+
- FastAPI
99
- Pydantic
1010
- Poetry
1111
- Sqlite3
1212
- Orjson
1313
- ruff (linter)
1414

1515
## 🚀 Recursos e Funcionalidades
16-
Endpoints para CRUD de noticias selecionadas pela comunidade.
16+
Endpoints para CRUD de noticias selecionadas pela comunidade.
1717

1818
### Schema da API
1919
[Documentação de referencia API Dog](https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/)
2020
<div class="px-2 pb-2 pt-5 os:px-5 os:pb-10 _tree-scroll-container relative h-full w-full overflow-y-auto"><ul class="w-full"><li><div to="" class="_sidebar-tree-node_13jsg_1 cursor-pointer select-none font-600 text-color" title="Authentication"><span class="break-word">Authentication</span><div class="flex-1"></div><div class="flex h-[22px] w-[22px] items-center justify-center"><span role="img" class="appicon app_icon text-disabled" style="font-size:16px"><svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" role="img"><path d="M225.834667 353.834667a42.666667 42.666667 0 0 1 60.330666 0L512 579.669333l225.834667-225.834666a42.666667 42.666667 0 1 1 60.330666 60.330666l-256 256a42.666667 42.666667 0 0 1-60.330666 0l-256-256a42.666667 42.666667 0 0 1 0-60.330666z"></path></svg></span></div></div><ul class="ml-3 border-l border-color-split pl-2"><li><a class="_sidebar-tree-node_13jsg_1 _sidebar-tree-node--selected_13jsg_24 font-600 sidebar-tree-node-apiDetail-15916580" title="Athenticate" data-discover="true" href="https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/athenticate-15916580e0"><span class="break-word">Athenticate</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-orange-6 text-white ml-2 mt-1 max-w-[70px]"><span class="truncate">POST</span></span></a></li></ul></li><li><div to="" class="_sidebar-tree-node_13jsg_1 cursor-pointer select-none text-color" title="News"><span class="break-word">News</span><div class="flex-1"></div><div class="flex h-[22px] w-[22px] items-center justify-center"><span role="img" class="appicon app_icon text-disabled" style="font-size:16px"><svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" role="img"><path d="M225.834667 353.834667a42.666667 42.666667 0 0 1 60.330666 0L512 579.669333l225.834667-225.834666a42.666667 42.666667 0 1 1 60.330666 60.330666l-256 256a42.666667 42.666667 0 0 1-60.330666 0l-256-256a42.666667 42.666667 0 0 1 0-60.330666z"></path></svg></span></div></div><ul class="ml-3 border-l border-color-split pl-2"><li><a class="_sidebar-tree-node_13jsg_1" title="Create" data-discover="true" href="https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/create-15876459e0"><span class="break-word">Create</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-orange-1 text-orange-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">POST</span></span></a></li><li><a class="_sidebar-tree-node_13jsg_1" title="Get" data-discover="true" href="https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/get-15876866e0"><span class="break-word">Get</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-green-1 text-green-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">GET</span></span></a></li><li><a class="_sidebar-tree-node_13jsg_1" title="Update" data-discover="true" href="https://apidog.comhttps://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/update-15878592e0"><span class="break-word">Update</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-blue-1 text-blue-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">PUT</span></span></a></li><li><a class="_sidebar-tree-node_13jsg_1" title="Like" data-discover="true" href="https://apidog.comhttps://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/like-15961454e0"><span class="break-word">Like</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-orange-1 text-orange-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">POST</span></span></a></li></ul></li><li><div to="" class="_sidebar-tree-node_13jsg_1 cursor-pointer select-none text-color" title="Libraries"><span class="break-word">Libraries</span><div class="flex-1"></div><div class="flex h-[22px] w-[22px] items-center justify-center"><span role="img" class="appicon app_icon text-disabled" style="font-size:16px"><svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" role="img"><path d="M225.834667 353.834667a42.666667 42.666667 0 0 1 60.330666 0L512 579.669333l225.834667-225.834666a42.666667 42.666667 0 1 1 60.330666 60.330666l-256 256a42.666667 42.666667 0 0 1-60.330666 0l-256-256a42.666667 42.666667 0 0 1 0-60.330666z"></path></svg></span></div></div><ul class="ml-3 border-l border-color-split pl-2"><li><a class="_sidebar-tree-node_13jsg_1" title="Create Subscription" data-discover="true" href="https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/create-subscription-16489942e0"><span class="break-word">Create Subscription</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-orange-1 text-orange-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">POST</span></span></a></li><li><a class="_sidebar-tree-node_13jsg_1" title="Add new Library" data-discover="true" href="/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/add-new-library-16489959e0"><span class="break-word">Add new Library</span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-orange-1 text-orange-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">POST</span></span></a></li><li><a class="_sidebar-tree-node_13jsg_1" title="GET List of the last 30 days updates " data-discover="true" href="https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/get-list-of-the-last-30-days-updates-16490481e0"><span class="break-word">GET List of the last 30 days updates </span><span class="ui-badge ui-badge-status ui-badge-not-a-wrapper ml-1 opacity-40"><span class="ui-badge-status-dot ui-badge-status-blue"></span></span><div class="flex-1"></div><span class="inline-flex items-center h-[14px] rounded-full px-1 py-0.5 text-xs font-700 leading-[10px] bg-green-1 text-green-6 ml-2 mt-1 max-w-[70px]"><span class="truncate">GET</span></span></a></li></ul></li></ul></div>
2121

2222
---
2323

24-
### Schema do Servidor
24+
### Schema do Servidor
2525
```
2626
fastapi_news_service/
2727
@@ -41,7 +41,7 @@ Serviço de Noticas e Bibliotecas PyNews
4141
│       ├── libraries.py # Definição dos endpoints da API para o módulo de Libraries (/libraries)
4242
│       └── authentication.py # Definição dos endpoints da API para o módulo de Autenticação (/auth)
4343
44-
├── test/ # Diretório para testes unitários
44+
├── test/ # Diretório para testes unitários
4545
│   └── __init__.py
4646
│   └── test_auth.py
4747
│   └── test_news.py
@@ -77,7 +77,7 @@ sequenceDiagram
7777
ServicoNoticias-->>Cliente: Notícia Criada (201 Created)
7878
deactivate ServicoNoticias
7979
deactivate Cliente
80-
80+
8181
8282
```
8383

@@ -245,6 +245,6 @@ poetry run ruff format .
245245
poetry run ruff check . --fix
246246
```
247247

248-
249-
## referencias
248+
249+
## referencias
250250
[Opinion based fastapi best practices](https://github.com/zhanymkanov/fastapi-best-practices)

app/routers/healthcheck/routes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33

44

55
class HealthCheckResponse(BaseModel):
6-
status: str = 'healthy'
7-
version: str = '2.0.0'
6+
status: str = "healthy"
7+
version: str = "2.0.0"
88

99

1010
def setup():
11-
router = APIRouter(prefix='/healthcheck', tags=['healthcheck'])
11+
router = APIRouter(prefix="/healthcheck", tags=["healthcheck"])
1212

1313
@router.get(
14-
'',
14+
"",
1515
response_model=HealthCheckResponse,
1616
status_code=status.HTTP_200_OK,
17-
summary='Health check endpoint',
18-
description='Returns the health status of the API',
17+
summary="Health check endpoint",
18+
description="Returns the health status of the API",
1919
)
2020
async def healthcheck():
2121
"""

app/schemas.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
from pydantic import BaseModel, HttpUrl
21
from datetime import datetime
32

3+
from pydantic import BaseModel, HttpUrl
4+
5+
46
class News(BaseModel):
57
description: str
68
tag: str
79

10+
811
class Library(BaseModel):
912
library_name: str
1013
news: list[News]
1114
logo: HttpUrl
1215
version: str
1316
release_date: datetime
14-
release_doc_url: HttpUrl
17+
release_doc_url: HttpUrl

app/services/database/database.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,88 @@
22
import os
33
from typing import AsyncGenerator
44

5-
from sqlalchemy.ext.asyncio import AsyncEngine
6-
from sqlalchemy.orm import sessionmaker
7-
from sqlmodel import SQLModel, create_engine, Field
5+
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
6+
from sqlmodel import Field, SQLModel
87
from sqlmodel.ext.asyncio.session import AsyncSession
98

10-
from app.services.database import models
9+
from app.services.database import models # noqa F401
1110

1211
logger = logging.getLogger(__name__)
1312

13+
1414
# --- Configuração do Banco de Dados ---
1515
# 'sqlite+aiosqlite' para suporte assíncrono com SQLite
16-
SQLITE_PATH = os.getenv('SQLITE_PATH', 'pynewsdb.db') # Valor padrão se não definida
17-
SQLITE_URL = os.getenv('SQLITE_URL', f'sqlite+aiosqlite:///{SQLITE_PATH}')
16+
SQLITE_PATH = os.getenv(
17+
"SQLITE_PATH", "pynewsdb.db"
18+
) # Valor padrão se não definida
19+
SQLITE_URL = os.getenv("SQLITE_URL", f"sqlite+aiosqlite:///{SQLITE_PATH}")
20+
21+
DATABASE_FILE = SQLITE_PATH # Usamos SQLITE_PATH para o nome do arquivo
22+
DATABASE_URL = SQLITE_URL # Usamos SQLITE_URL para a URL completa
1823

19-
DATABASE_FILE = SQLITE_PATH # Usamos SQLITE_PATH para o nome do arquivo
20-
DATABASE_URL = SQLITE_URL # Usamos SQLITE_URL para a URL completa
24+
# Cria o motor assíncrono
25+
engine = create_async_engine(DATABASE_URL, echo=True, future=True)
2126

22-
# Cria o motor assíncrono
23-
engine: AsyncEngine = AsyncEngine(create_engine(DATABASE_URL, echo=True, future=True))
2427

2528
# --- Fábrica de Sessões Assíncronas ---
2629
# Crie a fábrica de sessões UMA VEZ no escopo global do módulo.
27-
# Esta é a variável é injetada para obter sessões nas chamadas.
28-
AsyncSessionLocal = sessionmaker(
29-
engine, class_=AsyncSession, expire_on_commit=False, echo= True # expire_on_commit=False é importante!
30+
# Esta é a variável é injetada para obter sessões nas chamadas.
31+
AsyncSessionLocal = async_sessionmaker(
32+
engine,
33+
class_=AsyncSession,
34+
expire_on_commit=False,
35+
echo=True, # expire_on_commit=False é importante!
3036
)
3137

38+
3239
# --- Modelo de Teste Temporário (SQLModel) ---
33-
class TestEntry(SQLModel, table=True): # table=True indica que esta classe é um modelo de tabela
40+
class TestEntry(
41+
SQLModel, table=True
42+
): # table=True indica que esta classe é um modelo de tabela
3443
"""
3544
Classe de modelo temporária para testes de conexão com o banco de dados.
36-
Será usada como uma solução provisória antes da implementação dos modelos finais do projeto.
45+
Será usada como uma solução provisória antes da implementação dos
46+
modelos finais do projeto.
3747
"""
38-
__tablename__ = "test_entries"
48+
3949
id: int | None = Field(default=None, primary_key=True)
4050
message: str
51+
__tablename__ = "test_entry"
4152

4253
def __repr__(self):
4354
return f"<TestEntry(id={self.id}, message='{self.message}')>"
44-
55+
4556

4657
# --- Funções de Inicialização e Sessão do Banco de Dados ---
4758
async def init_db():
4859
"""
4960
Inicializa o banco de dados:
5061
1. Verifica se o arquivo do banco de dados existe.
51-
2. Se não existir, cria o arquivo e todas as tabelas definidas nos modelos SQLModel nos imports e acima.
62+
2. Se não existir, cria o arquivo e todas as tabelas definidas
63+
nos modelos SQLModel nos imports e acima.
5264
"""
5365
if not os.path.exists(DATABASE_FILE):
54-
logger.info(f"Arquivo de banco de dados '{DATABASE_FILE}' não encontrado. Criando novo banco de dados e tabelas.")
66+
logger.info(
67+
f"Arquivo de banco de dados '{DATABASE_FILE}' não encontrado."
68+
f"Criando novo banco de dados e tabelas."
69+
)
5570
async with engine.begin() as conn:
56-
# SQLModel.metadata.create_all é síncrono e precisa ser executado via run_sync
71+
# SQLModel.metadata.create_all é síncrono e precisa
72+
# ser executado via run_sync
5773
await conn.run_sync(SQLModel.metadata.create_all)
5874
logger.info("Tabelas criadas com sucesso.")
5975
else:
60-
logger.info(f"Arquivo de banco de dados '{DATABASE_FILE}' já existe. Conectando.")
76+
logger.info(
77+
f"Arquivo de banco de dados '{DATABASE_FILE}'"
78+
f"já existe. Conectando."
79+
)
80+
6181

6282
async def get_session() -> AsyncGenerator[AsyncSession, None]:
6383
"""
6484
Função utilitária que fornece uma sessão de banco de dados assíncrona.
65-
Injeção de dependência no app no lifespan.
85+
Injeção de dependência no app no lifespan.
6686
"""
6787
async with AsyncSessionLocal() as session:
6888
yield session
69-
# chamada do session.close() acontece ao final do bloco async with().
89+
# chamada do session.close() acontece ao final do bloco async with().
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from app.services.database.models.communities import Community
22
from app.services.database.models.libraries import Library
33

4-
__all__ = ["Community", "Library"]
4+
__all__ = ["Community", "Library"]

0 commit comments

Comments
 (0)