Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementação de Testes Automatizados #10

Merged
merged 24 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6af477b
fix: updating project configuration to remove wrong ref to readme
TanookiVerde Jan 5, 2024
801a192
misc: installing depedencies for testing
TanookiVerde Jan 5, 2024
51d7e81
feat: implementing three basic tests: login and user creation (minima…
TanookiVerde Jan 5, 2024
66faa17
misc: documenting test usage and minor changes
TanookiVerde Jan 5, 2024
fcb3586
refactor: creating utils module for testing
TanookiVerde Jan 5, 2024
a7a780f
fix: wrong module importing
TanookiVerde Jan 5, 2024
7bfc6ba
feat: add cd
gabriel-milan Jan 8, 2024
6bb82f3
chore: fix linting issues
gabriel-milan Jan 8, 2024
1648119
fix(cd): add infisical envs
gabriel-milan Jan 8, 2024
6a9f42e
fix(cd): add infisical envs
gabriel-milan Jan 8, 2024
e4b2d61
fix(cd): typo
gabriel-milan Jan 8, 2024
b7a1a53
chore: downgrade tortoise-orm due to import issues
gabriel-milan Jan 8, 2024
a3d903b
fix(cd): add pg instance
gabriel-milan Jan 8, 2024
ffa4d2d
fix(cd): use container for testing
gabriel-milan Jan 8, 2024
a246867
fix(cd): initialize db
gabriel-milan Jan 8, 2024
b911485
chore(cd): ignore aerich errors
gabriel-milan Jan 8, 2024
80e7423
fix(tests): create user before try to login
gabriel-milan Jan 8, 2024
0333e33
chore(tests): minor refactor
gabriel-milan Jan 8, 2024
b8883ca
fix(tests): remove trailing slash
gabriel-milan Jan 8, 2024
ed9fcad
fix(tests): remove trailing slash
gabriel-milan Jan 8, 2024
66cd72b
fix(tests): setup
gabriel-milan Jan 8, 2024
50d368c
chore(cd): remove ingress def
gabriel-milan Jan 8, 2024
fe6a4b7
fix(cd): healthcheck endpoint
gabriel-milan Jan 8, 2024
a778963
fix(cd): liveness probe
gabriel-milan Jan 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions .github/workflows/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: API

on:
push:
paths:
- ".github/workflows/api*"
- "api/**/*"

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.11.x"

- name: Set up dependencies
run: |
cd api/
pip install -U pip poetry
poetry config virtualenvs.create false
poetry install --with dev

- name: Run linters
run: |
cd api/
task lint

tests:
name: Tests
runs-on: ubuntu-latest
container: python:3.11-slim
env:
INFISICAL_ADDRESS: ${{ secrets.INFISICAL_ADDRESS }}
INFISICAL_TOKEN: ${{ secrets.INFISICAL_TOKEN }}
services:
db:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up dependencies
run: |
cd api/
pip install -U pip poetry
poetry config virtualenvs.create false
poetry install --with dev

- name: Set up database
run: |
cd api/
aerich init-db || true
aerich upgrade || true

- name: Run tests
run: |
cd api/
task test

deploy:
name: Build and Deploy
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/development'
needs: [lint, tests]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push image
uses: docker/build-push-action@v2
with:
context: api/
file: api/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.ref == 'refs/heads/main' && 'latest' || github.ref == 'refs/heads/development' && 'latest-dev' || github.sha }}
labels: |
org.opencontainers.image.source=${{ github.event.repository.html_url }}
org.opencontainers.image.revision=${{ github.sha }}
build-args: |
BUILDKIT_INLINE_CACHE=1

- name: Setup Google Cloud CLI
uses: google-github-actions/[email protected]
with:
service_account_key: ${{ secrets.GCP_SA_KEY }}
project_id: ${{ secrets.GCP_PROJECT_ID }}
export_default_credentials: true

- name: Get GKE credentials
uses: google-github-actions/[email protected]
with:
cluster_name: ${{ secrets.GKE_CLUSTER }}
location: ${{ secrets.GKE_ZONE }}
credentials: ${{ secrets.GCP_SA_KEY }}

- name: Set up Kustomize
run: |-
curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_linux_amd64
chmod u+x ./kustomize
mv ./kustomize api/

- name: Deploy (main)
if: github.ref == 'refs/heads/main'
run: |-
cd api/
./kustomize edit set image gcr.io/PROJECT_ID/IMAGE_NAME:TAG=ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}
./kustomize build . | kubectl apply -n unificacao-prontuarios-prod -f -
kubectl rollout status -w -n unificacao-prontuarios-prod deployment/unificacao-prontuarios

- name: Deploy (development)
if: github.ref == 'refs/heads/development'
run: |-
cd api/
./kustomize edit set image gcr.io/PROJECT_ID/IMAGE_NAME:TAG=ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}
./kustomize build . | kubectl apply -n unificacao-prontuarios-dev -f -
kubectl rollout status -w -n unificacao-prontuarios-dev deployment/unificacao-prontuarios
2 changes: 1 addition & 1 deletion .github/workflows/lib-docs.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy docs
name: Lib - Docs

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lib-lint.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI - Lint
name: Lib - Lint

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lib-tests.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI - Tests
name: Lib - Tests

on:
push:
Expand Down
103 changes: 34 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
# FHIR
# Prontuários Integrados API

## Preparação de Ambiente

- Rode `pre-commit install`
- O projeto está na pasta `api/`
- Para rodar o banco de dados é obrigatório utilizar um ambiente virtual docker
- Para rodar a API você pode optar pelo docker ou pelo ambiente

### Utilizando Docker (BD e API)
*Ideal para deploy e subir o banco de dados localmente*

- Esteja na pasta `./api/`
- Garanta instalação de Docker e Docker Compose
- Configure em `docker-compose.yaml` a variável `ENVIRONMENT` para `dev` ou `prod`
- `dev`: Ambiente de Desenvolvimento
- `prod`: Ambiente de Produção
- Rode `docker compose up --build` na raiz do projeto
- Os serviços estão disponíveis em:
- Para subir a API e o banco de dados, rode `docker compose up --build`
- Se quiser subir apenas o banco de dados: `docker compose up db --build`
- Os serviços ficam disponíveis em:

|Serviço|URL|Porta|Usuário|Senha|
|--|--|--|--|--|
|Banco de Dados (Postgres) |localhost|5432|postgres|postgres|
|API (Fast API) | localhost|**8000**|-|-|

### Utilizando Poetry (Apenas API)
*Ideal para debugging da API e execução de testes localmente*
- Na pasta `./api/` rode `poetry shell` e depois `poetry install`
- Para inicializar a API rode: `uvicorn app.main:app --host 0.0.0.0 --port 8001`
- PS.: Recomendado seguir o tutorial de Debugging e executar por lá.
- O serviço estará disponivel em:

|Serviço|URL|Porta|Usuário|Senha|
|--|--|--|--|--|
|Banco de Dados (Postgres) |localhost|8001|postgres|postgres|
|API (Fast API) | localhost|8000|-|-|
|API (Fast API) | localhost|**8001**|-|-|


## Dados Iniciais
- Dois scripts que populam o banco com dados iniciais:
- Crie um usuário próprio: `python scripts/create_user.py --username <USUARIO> --password <SENHA>`
- Crie dados iniciais: `python scripts/database_initial_data.py`
- **Atenção**: Caso precise limpar todos os dados do banco: `python scripts/database_cleanup.py`

## Testes Automatizados

- Os testes estão definidos na pasta `api/tests/`
- Para rodar os testes basta executar no terminal `pytest --disable-warnings`

## Debugging
- Para fazer o debugging da API você vai rodá-la fora do container docker. Dessa forma a configuração é simples.
- Arquivos de configuração disponíveis em `.vscode/launch.json`.
Expand All @@ -36,67 +60,8 @@

### Uso
- **Inicie a depuração**: o VSCode detecta automaticamente o arquivo que configura a depuração. Basta dar "play" na aba de depuração
- O serviço fica disponível em:

## Payloads para Testes

### Entidade `Patient` (Campos Obrigatórios)

```json
{
"active": true,
"birth_date": "1999-12-20",
"gender": "male",
"cpf": "11111111111",
"name": "MANUEL GOMES",
"telecom": [{
"value": "5521123456789"
}]
}
```

### Entidade `Patient` (Todos os Campos)

```json
{
"active": true,
"address": [{
"use": "home",
"type": "physical",
"line": "AV SQN BLOCO M 604 APARTAMENTO ASA NORTE",
"city": "Rio de Janeiro",
"state": "Rio de Janeiro",
"country": "Brasil",
"postal_code": "70752130",
"period": {
"start": "2020-10-01 00:00:00",
"end": "2020-10-02 00:00:00"
}
}],
"birth_city": "Rio de Janeiro",
"birth_country": "Brasil",
"birth_state": "Rio de Janeiro",
"birth_date": "1999-12-20 00:00:00",
"deceased": false,
"gender": "male",
"cpf": "12345678901",
"cns": "1234567890000000",
"name": "GABRIELA INACIO ALVES",
"nationality": "B",
"naturalization": "",
"mother": "MARILIA FARES DA ROCHA ALVES",
"father": "JURACY ALVES",
"protected_person": false,
"race": "Parda",
"ethnicity": "PATAXO",
"telecom": [{
"system": "phone",
"use": "home",
"value": "5521123456789",
"rank": "1",
"period": {
"start": "2020-10-01 00:00:00",
"end": "2020-10-02 00:00:00"
}
}]
}
```
|Serviço|URL|Porta|Usuário|Senha|
|--|--|--|--|--|
|API (Fast API) | localhost|**8001**|-|-|
66 changes: 66 additions & 0 deletions api/.kubernetes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: unificacao-prontuarios
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: unificacao-prontuarios
minReadySeconds: 5
template:
metadata:
labels:
app: unificacao-prontuarios
spec:
containers:
- name: unificacao-prontuarios
image: gcr.io/PROJECT_ID/IMAGE_NAME:TAG
ports:
- containerPort: 80
envFrom:
- secretRef:
name: unificacao-prontuarios-secrets
readinessProbe:
httpGet:
path: /docs
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /docs
port: 80
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1024Mi"
cpu: "500m"
restartPolicy: Always

---
# Service
apiVersion: v1
kind: Service
metadata:
labels:
app: unificacao-prontuarios
name: unificacao-prontuarios
spec:
ports:
- name: "80"
port: 80
targetPort: 80
selector:
app: unificacao-prontuarios
21 changes: 19 additions & 2 deletions api/app/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from os import getenv
from typing import List

from infisical import InfisicalClient
from loguru import logger


Expand Down Expand Up @@ -64,9 +65,25 @@ def getenv_list_or_action(
return []


def inject_environment_variables(environment: str):
"""Inject environment variables from Infisical."""
site_url = getenv_or_action("INFISICAL_ADDRESS", action="raise")
token = getenv_or_action("INFISICAL_TOKEN", action="raise")
infisical_client = InfisicalClient(
token=token,
site_url=site_url,
)
secrets = infisical_client.get_all_secrets(environment=environment, attach_to_os_environ=True)
logger.info(f"Injecting {len(secrets)} environment variables from Infisical:")
for secret in secrets:
logger.info(f" - {secret.secret_name}: {'*' * len(secret.secret_value)}")


environment = getenv_or_action("ENVIRONMENT", action="warn", default="dev")
if environment not in ["dev", "prod"]:
raise ValueError("ENVIRONMENT must be one of 'dev' or 'prod'")
if environment not in ["dev", "staging", "prod"]:
raise ValueError("ENVIRONMENT must be one of 'dev', 'staging' or 'prod'")

inject_environment_variables(environment=environment)

if environment == "dev":
from app.config.dev import * # noqa: F401, F403
Expand Down
Loading
Loading