Skip to content

Commit 7a4a3ae

Browse files
authored
Merge pull request #56 from PythonFloripa/implements_scanapi
Implements scanapi
2 parents a2be9d1 + ca9a41d commit 7a4a3ae

25 files changed

+5860
-55
lines changed

.env.example

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
# Environment variables for PyNews Server
2-
3-
# API Configuration
41
PYTHONPATH=/server
2+
BASE_URL=http://localhost:8000
53

64
# SQLite Database Configuration
75
SQLITE_PATH=/app/data/pynewsdb.db

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ celerybeat.pid
136136

137137
# Environments
138138
.env
139+
.env.test
139140
.envrc
140141
.venv
141142
env/

Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,20 @@ COPY tests tests
6262
COPY scripts scripts
6363

6464
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--lifespan", "on"]
65+
66+
67+
FROM builder-base AS scanapi-test
68+
69+
WORKDIR $PYSETUP_PATH
70+
71+
RUN poetry install --no-root --no-interaction
72+
73+
WORKDIR $PROJECT_PATH
74+
75+
COPY poetry.lock pyproject.toml ./
76+
77+
COPY app app
78+
COPY scanapi scanapi
79+
COPY scanapi.conf ./
80+
81+
CMD ["poetry", "run", "scanapi", "run"]

Makefile

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ help: ## Mostra esta mensagem de ajuda
1111
@echo ""
1212
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-15s$(NC) %s\n", $$1, $$2}'
1313

14+
install: ## Instala dependências com Poetry
15+
@echo "$(YELLOW)Instalando dependências...$(NC)"
16+
poetry install
17+
1418
build: ## Constrói as imagens Docker
1519
@echo "$(YELLOW)Construindo imagens Docker...$(NC)"
1620
docker-compose build
@@ -26,6 +30,19 @@ down: ## Para os serviços
2630
logs: ## Mostra os logs dos serviços
2731
docker-compose logs -f pynews-api
2832

33+
restart: ## Reinicia os serviços
34+
@echo "$(YELLOW)Reiniciando serviços...$(NC)"
35+
docker-compose restart
36+
37+
dev: build up ## Ambiente de desenvolvimento completo
38+
@echo "$(GREEN)Ambiente de desenvolvimento iniciado!$(NC)"
39+
@echo "API: http://localhost:8000"
40+
@echo "Docs: http://localhost:8000/docs"
41+
42+
prod: ## Inicia em modo produção
43+
@echo "$(YELLOW)Iniciando em modo produção...$(NC)"
44+
docker-compose -f docker-compose.yaml up -d
45+
2946
test: ## Executa os testes
3047
@echo "$(YELLOW)Executando testes...$(NC)"
3148
poetry run pytest
@@ -34,6 +51,12 @@ test-cov: ## Executa os testes com coverage
3451
@echo "$(YELLOW)Executando testes com coverage...$(NC)"
3552
poetry run pytest --cov=app --cov-report=html
3653

54+
docker-test:
55+
docker exec -e PYTHONPATH=/app $(API_CONTAINER_NAME) pytest -s --cov-report=term-missing --cov-report html --cov-report=xml --cov=app tests/
56+
57+
scanapi-test: # Executa testes com scanapi e gera report acessado na porta 8080 no path {url}/scanapi-report.html
58+
docker-compose run --rm scanapi-tests
59+
3760
lint: ## Verifica o código com ruff
3861
@echo "$(YELLOW)Verificando código...$(NC)"
3962
poetry run ruff check .
@@ -42,24 +65,6 @@ format: ## Formata o código
4265
@echo "$(YELLOW)Formatando código...$(NC)"
4366
poetry run ruff format .
4467

45-
clean: ## Remove containers, volumes e imagens
46-
@echo "$(YELLOW)Limpando containers e volumes...$(NC)"
47-
docker-compose down -v --remove-orphans
48-
docker system prune -f
49-
50-
dev: build up ## Ambiente de desenvolvimento completo
51-
@echo "$(GREEN)Ambiente de desenvolvimento iniciado!$(NC)"
52-
@echo "API: http://localhost:8000"
53-
@echo "Docs: http://localhost:8000/docs"
54-
55-
prod: ## Inicia em modo produção
56-
@echo "$(YELLOW)Iniciando em modo produção...$(NC)"
57-
docker-compose -f docker-compose.yaml up -d
58-
59-
restart: ## Reinicia os serviços
60-
@echo "$(YELLOW)Reiniciando serviços...$(NC)"
61-
docker-compose restart
62-
6368
health: ## Verifica o health check da API
6469
@echo "$(YELLOW)Verificando saúde da API...$(NC)"
6570
curl -f http://localhost:8000/api/healthcheck || echo "API não está respondendo"
@@ -110,6 +115,11 @@ install: ## Instala dependências com Poetry
110115
shell: ## Entra no shell do container
111116
docker-compose exec pynews-api bash
112117

118+
clean: ## Remove containers, volumes e imagens
119+
@echo "$(YELLOW)Limpando containers e volumes...$(NC)"
120+
docker-compose down -v --remove-orphans
121+
docker system prune -f
122+
113123
setup: install build up ## Setup completo do projeto
114124
@echo "$(GREEN)Setup completo realizado!$(NC)"
115125
@echo "$(GREEN)Acesse: http://localhost:8000/docs$(NC)"

README.md

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# pynewsserver
2+
23
Serviço de Noticas e Bibliotecas PyNews
34

45
## 💡 Visão Geral
56

67
## 💻 Tecnologias Utilizadas
8+
79
- Python
810
- FastAPI
911
- Pydantic
@@ -13,15 +15,19 @@ Serviço de Noticas e Bibliotecas PyNews
1315
- ruff (linter)
1416

1517
## 🚀 Recursos e Funcionalidades
18+
1619
Endpoints para CRUD de noticias selecionadas pela comunidade.
1720

1821
### Schema da API
22+
1923
[Documentação de referencia API Dog](https://apidog.com/apidoc/shared/70418cab-ddba-4c7d-97a4-8b70b43a7946/)
24+
2025
<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>
2126

2227
---
2328

2429
### Schema do Servidor
30+
2531
```
2632
fastapi_news_service/
2733
@@ -84,24 +90,28 @@ sequenceDiagram
8490
## ⚙️ Como Rodar
8591

8692
### 📋 Pré-requisitos
93+
8794
- Docker e Docker Compose instalados
8895
- Git (para clonar o repositório)
8996

9097
### 🚀 Início Rápido
9198

9299
1. **Clone o repositório:**
100+
93101
```bash
94102
git clone <repository-url>
95103
cd PyNewsServer
96104
```
97105

98106
2. **Configure as variáveis de ambiente (opcional):**
107+
99108
```bash
100109
cp .env.example .env
101110
# Edite o arquivo .env conforme necessário
102111
```
103112

104113
3. **Inicie o serviço:**
114+
105115
```bash
106116
docker-compose up -d
107117
```
@@ -126,6 +136,7 @@ Para mais detalhes sobre configuração do SQLite, consulte: [docs/sqlite-setup.
126136
### ▶️ Guia de Execução para Desenvolvimento
127137

128138
#### Usando Docker (Recomendado)
139+
129140
```bash
130141
# Construir e iniciar em modo desenvolvimento
131142
docker-compose up --build
@@ -138,6 +149,7 @@ docker-compose down
138149
```
139150

140151
#### Usando Poetry (Local)
152+
141153
```bash
142154
# Instalar dependências
143155
poetry install
@@ -179,42 +191,68 @@ docker-compose up -d --force-recreate
179191
### 🔧 Comandos Úteis
180192

181193
#### Usando Makefile (Recomendado)
194+
182195
```bash
183196
# Ver todos os comandos disponíveis
184197
make help
185198

186-
# Setup completo do projeto
187-
make setup
199+
# Instalar dependências com Poetry
200+
make install
188201

189-
# Ambiente de desenvolvimento
190-
make dev
202+
# Setup completo do projeto (instala, constrói e sobe containers)
203+
make setup
191204

192-
# Construir e iniciar
205+
# Construir imagens Docker
193206
make build
207+
208+
# Iniciar serviços
194209
make up
195210

196-
# Ver logs
211+
# Parar serviços
212+
make down
213+
214+
# Reiniciar serviços
215+
make restart
216+
217+
# Ver logs da API
197218
make logs
198219

199-
# Executar testes
220+
# Acessar shell dentro do container da API
221+
make shell
222+
223+
# Executar testes locais
200224
make test
225+
226+
# Executar testes com coverage
201227
make test-cov
202228

203-
# Linting e formatação
229+
# Executar testes dentro do container Docker
230+
make docker-test
231+
232+
# Executar testes com ScanAPI e gerar report que pode ser acessado na porta 8080 no path {url}/scanapi-report.html
233+
make scanapi-test
234+
235+
# Verificar código com Ruff
204236
make lint
237+
238+
# Formatar código com Ruff
205239
make format
206240

207241
# Verificar saúde da API
208242
make health
209243

210-
# Parar serviços
211-
make down
244+
# Ambiente de desenvolvimento completo
245+
make dev
212246

213-
# Limpeza completa
247+
# Iniciar em modo produção
248+
make prod
249+
250+
# Limpeza completa de containers, volumes e imagens
214251
make clean
215252
```
216253

217254
#### Comandos Docker Diretos
255+
218256
```bash
219257
# Entrar no container
220258
docker-compose exec pynews-api bash
@@ -232,6 +270,7 @@ docker-compose down -v
232270
### 🛠️ Desenvolvimento
233271

234272
#### Estrutura de Testes
273+
235274
```bash
236275
# Rodar todos os testes
237276
poetry run pytest
@@ -243,7 +282,12 @@ poetry run pytest --cov=app
243282
poetry run pytest tests/test_auth.py
244283
```
245284

285+
##### Requisitos para utilizar [ScanAPI](https://scanapi.dev/)
286+
287+
- Criar .env.test seguindo o exemplo em .env.example com as credenciais necessárias
288+
246289
#### Linting e Formatação
290+
247291
```bash
248292
# Verificar código
249293
poetry run ruff check .
@@ -255,6 +299,6 @@ poetry run ruff format .
255299
poetry run ruff check . --fix
256300
```
257301

258-
259302
## referencias
303+
260304
[Opinion based fastapi best practices](https://github.com/zhanymkanov/fastapi-best-practices)

app/routers/admin/routes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616

1717

1818
async def create_admin(session: AsyncSession):
19-
ADMIN_USER = os.getenv("ADMIN_USER")
20-
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")
21-
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL")
19+
ADMIN_USER = os.getenv("ADMIN_USER", "")
20+
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "")
21+
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
2222
password = ADMIN_PASSWORD
2323
hashed_password = auth.hash_password(password)
2424
community = DBCommunity(

app/routers/libraries/routes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class SubscribeLibraryResponse(BaseModel):
3030
status: str = "Subscribed in libraries successfully"
3131

3232

33+
class LibraryRequestResponse(BaseModel):
34+
status: str = "Library requested successfully"
35+
36+
3337
def setup():
3438
router = APIRouter(prefix="/libraries", tags=["libraries"])
3539

@@ -163,7 +167,7 @@ async def subscribe_libraries(
163167

164168
@router.post(
165169
"/request",
166-
response_model=LibraryResponse,
170+
response_model=LibraryRequestResponse,
167171
status_code=status.HTTP_200_OK,
168172
summary="Request a library",
169173
description="Request a library to follow",
@@ -189,7 +193,7 @@ async def request_library(
189193
library_request, request.app.db_session_factory
190194
)
191195

192-
return LibraryResponse()
196+
return LibraryRequestResponse()
193197
except HTTPException as e:
194198
raise e
195199
except Exception as e:

docker-compose.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,35 @@ services:
4040
"
4141
restart: "no"
4242

43+
scanapi-tests:
44+
build:
45+
context: .
46+
dockerfile: Dockerfile
47+
target: scanapi-test
48+
container_name: scanapi-tests
49+
env_file:
50+
- .env
51+
environment:
52+
- BASE_URL=http://pynews-server:8000
53+
volumes:
54+
- report-data:/server/scanapi
55+
depends_on:
56+
pynews-api:
57+
condition: service_healthy
58+
command: poetry run scanapi run
59+
60+
scanapi-report-viewer:
61+
image: nginx:alpine
62+
container_name: scanapi-report-viewer
63+
ports:
64+
- "8080:80"
65+
volumes:
66+
- report-data:/usr/share/nginx/html:ro
67+
depends_on:
68+
- scanapi-tests
69+
4370
volumes:
71+
report-data:
4472
sqlite_data:
4573
driver: local
4674
driver_opts:

0 commit comments

Comments
 (0)