Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
03a496e
add pyrightconfig and pylintrc files
May 20, 2025
9f906ff
add docker-compose et Dockerfile files
May 20, 2025
b27760d
[FORMAT] Reduce max-line-length from 120 to 88 characters for better …
May 24, 2025
a3f3874
chore(docker): add container name, update port mapping, add env_file …
May 24, 2025
5b088af
feat(docker): optimize Dockerfile with python:3.13-slim base image
May 24, 2025
b121cc9
add routers
May 24, 2025
65a0729
add models schemas and crud files
May 24, 2025
f34b859
add log configurations
May 24, 2025
4e3e841
add mains and tasks files
May 24, 2025
a208177
add migrations folder
May 24, 2025
a51908d
add database folder
May 24, 2025
fa088d2
add import service file
May 24, 2025
3a6e88d
add alembric file
May 24, 2025
21ec2c2
add .gitignore file
May 24, 2025
bed807b
add tests
May 24, 2025
68e9422
Added # type: ignore[call-arg] comments to suppress type-checker errors
May 24, 2025
2b846bf
Add explicit return type annotation `-> None:` to functions for bette…
May 24, 2025
ff8594b
Add requirements with project dependencies
May 24, 2025
659fc70
Add .env file to the repository for testing purposes
May 24, 2025
a4021b7
Add __init__.py to the API module to initialize the package
May 24, 2025
dd683b3
fix(auth): re-raise authentication exceptions to let FastAPI handle them
May 25, 2025
783639b
feat(auth): add /auth route for authentication endpoints
May 25, 2025
335e22b
feat(security): protect all routes with token verification
May 25, 2025
f3f202a
feat(auth): add two new files to handle authentication
May 25, 2025
d2690e5
Add mock authentication to tests
May 25, 2025
23c28c9
Add PyJWT to requirements.txt
May 25, 2025
2f3dfea
update .env file
May 25, 2025
3c138ae
Add CSV file with test data for the API
May 25, 2025
cf0f616
migration version file generated
May 25, 2025
d67ef1b
Add Alembic migration folder and configuration files
May 25, 2025
47bbe1b
Replace unused variable name 'payload' with underscore '_'
May 25, 2025
f627bb6
Add project execution instructions, useful Docker commands, and proje…
May 25, 2025
de9b2d9
Add successful test results output
May 25, 2025
598e7b5
Add IMPROVEMENTS.md with identified enhancement opportunities
May 25, 2025
04b6a62
Update README.md to add link to IMPROVEMENTS.md for enhancement sugge…
May 25, 2025
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
19 changes: 19 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# .env

CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=db+postgresql+psycopg://user:password@database:5432/alpha

POSTGRES_USER=user
POSTGRES_PASSWORD=password
POSTGRES_DB=alpha
POSTGRES_HOST=database
POSTGRES_PORT=5432
POSTGRES_URL=postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}


VALID_CLIENT_ID=UezjwqiRG6cKJRtSdTBjrkLI09HpWPxDLdweOisr
VALID_CLIENT_SECRET=V5tSsR9VSWISVamUl8KweBiydAZ2SKV7Rs4NqZIeTQBB42IMrAGB1SRpoJpmIvEdcKQaGjrt6XLvRmwcUnqWV5CzO7ReHHHWS1x5GwxvxOI6tq87HpV8DlZZStGqZGUL

JWT_SECRET_KEY=your_super_secret_key
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_SECONDS=31536000
43 changes: 43 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
.vscode/
*.vscode
*$py.class
*.ruff_cache/*
.idea
*.idea
.idea/

# Environments
# .env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/
144 changes: 144 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
[MASTER]
ignore=.git,__pycache__,migrations,node_modules,.venv
init-hook='import sys; sys.path.append("api")'
load-plugins=pylint_celery,pylint_pytest
persistent=yes

[MESSAGES CONTROL]
disable=
missing-docstring,
invalid-name,
too-few-public-methods,
too-many-arguments,
duplicate-code,
no-member,
unused-argument,
redefined-outer-name,
import-error,
no-name-in-module,
broad-exception-raised,
logging-fstring-interpolation

[REPORTS]
output-format=text
reports=no
score=no

[BASIC]
good-names=i,j,k,_,db
bad-names=foo,bar,baz
variable-rgx=[a-z_][a-z0-9_]{2,30}$
function-rgx=([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
attr-rgx=[a-z_][a-z0-9_]{2,30}$
class-rgx=[A-Z][a-zA-Z0-9]+$
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$

[FORMAT]
max-line-length=88
indent-string=' '
ignore-long-lines=^\s*(# )?<?https?://\S+>?

[TYPECHECK]
ignore-mixin-members=yes
generated-members=request,user,objects,status_code,celery_app

[DESIGN]
max-args=6
max-returns=10
max-branches=15
max-statements=60
max-locals=20
max-attributes=10

[IMPORTS]
known-standard-library=typing,os,sys
known-third-party=fastapi,pydantic,celery,sqlalchemy,uvicorn,psycopg2,redis
known-first-party=api,services
import-order-style=google

[MISCELLANEOUS]
notes=TODO,FIXME,XXX

[LOGGING]
logging-modules=logging

[VARIABLES]
dummy-variables-rgx=_|dummy

[SIMILARITIES][MASTER]
ignore=.git,__pycache__,migrations,node_modules,.venv
init-hook='import sys; sys.path.append("api")'
load-plugins=pylint_celery,pylint_pytest
persistent=yes

[MESSAGES CONTROL]
disable=
missing-docstring,
invalid-name,
too-few-public-methods,
too-many-arguments,
duplicate-code,
no-member,
unused-argument,
redefined-outer-name,
import-error,
broad-exception-raised,
logging-fstring-interpolation

[REPORTS]
output-format=text
reports=no
score=no

[BASIC]
good-names=i,j,k,_,db
bad-names=foo,bar,baz
variable-rgx=[a-z_][a-z0-9_]{2,30}$
function-rgx=([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$
argument-rgx=[a-z_][a-z0-9_]{2,30}$
attr-rgx=[a-z_][a-z0-9_]{2,30}$
class-rgx=[A-Z][a-zA-Z0-9]+$
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$

[FORMAT]
max-line-length=120
indent-string=' '
ignore-long-lines=^\s*(# )?<?https?://\S+>?

[TYPECHECK]
ignore-mixin-members=yes
generated-members=request,user,objects,status_code,celery_app

[DESIGN]
max-args=6
max-returns=10
max-branches=15
max-statements=60
max-locals=20
max-attributes=10

[MISCELLANEOUS]
notes=TODO,FIXME,XXX

[LOGGING]
logging-modules=logging

[VARIABLES]
dummy-variables-rgx=_|dummy

[SIMILARITIES]
min-similarity-lines=4
ignore-comments=yes
ignore-docstrings=yes
ignore-imports=yes

[EXCEPTIONS]
overgeneral-exceptions=Exception
min-similarity-lines=4
ignore-comments=yes
ignore-docstrings=yes
ignore-imports=yes

[EXCEPTIONS]
overgeneral-exceptions=Exception
135 changes: 135 additions & 0 deletions IMPROVEMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
## Pistes d'amélioration des tests API

Ces améliorations ont été identifiées mais non implémentées par manque de temps. Elles visent à renforcer la couverture des tests, la robustesse du système et la gestion des cas limites.

### Tests sur l’import de fichiers CSV

- [ ] Ajouter un test avec un fichier mal formé (ex : en-têtes manquants, colonnes incorrectes).
- [ ] Ajouter un test avec un fichier d’un type incorrect (ex : `.txt`, `.json`) pour vérifier le filtrage MIME.
- [ ] Tester l'import d'un fichier très volumineux pour valider le comportement en cas de surcharge.

### Tests de sécurité

- [ ] Supprimer ou désactiver temporairement le mock de l’authentification (`fake_get_current_user`) pour tester les comportements réels liés aux tokens.
- [ ] Vérifier le comportement **sans jeton d’authentification** : une requête sans en-tête `Authorization` doit retourner un code 401.
- [ ] Vérifier le comportement **avec un jeton invalide ou expiré** : la route doit refuser l'accès avec un message d'erreur approprié.

### Tests des cas limites et erreurs

- [ ] Tester la route `/api/imports/{job_id}/status` avec un UUID invalide (format incorrect → 422 attendu).
- [ ] Tester `/api/clients` sans pagination pour valider le fallback aux valeurs par défaut.
- [ ] Ajouter un test sur une route inexistante pour vérifier la gestion des 404.




## Améliorations possibles des tests de la tâche d’import

Ces pistes d'amélioration visent à renforcer la robustesse des tests liés à la tâche `run_import_job` et à la fonction `process_csv`. Faute de temps, elles n'ont pas été mises en place, mais elles seraient prioritaires dans un contexte de production.

### Cas fonctionnels et de validation

- [ ] Import de fichiers CSV vides, sans aucune ligne à traiter.
- [ ] Fichiers CSV composés uniquement de lignes invalides (échec total de validation).
- [ ] Données contenant des dates malformées, absentes ou incorrectes.
- [ ] Lignes avec des champs manquants ou vides (ex. email absent).
- [ ] CSV avec des doublons ou incohérences logiques (même email pour plusieurs lignes).
👉 Actuellement, **aucune logique de détection ou de rejet des doublons ou encodage inhabituel dans les données etc** (par exemple sur l'email) n'est implémentée.
Il serait pertinent d’ajouter une **vérification coté schéma/model** pour éviter l’insertion de clients identiques ou données avec accents, emojis par exemple.

### Tests de performance et scalabilité

- [ ] Gestion de très gros fichiers CSV pour tester la montée en charge.
- [ ] Évaluer la consommation mémoire / temps de traitement sur des volumes élevés.

### Cas d’erreurs techniques

- [ ] Cas où la connexion à la base de données échoue ou la session n’est pas accessible.
- [ ] Scénarios où la tâche Celery dépasse le nombre maximal de retries autorisés.
- [ ] Gestion des erreurs inattendues lors du traitement du CSV (ex. crash Pydantic, encodage).

### Tests d’intégration

- [ ] Ajouter des tests d’intégration réels exécutant la fonction `process_csv` sur de vrais fichiers CSV et une base de données de test (sans mocks), afin de valider le comportement complet en environnement réel.


### Améliorations possibles des modèles (`models.py`)

- [ ] Ajouter une contrainte d’unicité sur `Client.email` pour éviter les doublons côté base de données.
👉 Actuellement, aucun contrôle ne garantit qu’un même email ne soit pas inséré plusieurs fois.

- [ ] Ajouter un champ `import_job_id` (clé étrangère) dans `Client` pour tracer l’origine d’un enregistrement.
👉 Cela permettrait de lier chaque client à l’import correspondant pour faciliter l’audit.

- [ ] Ajouter un index composite sur (`email`, `birth_date`) si ces champs sont utilisés fréquemment pour des recherches combinées.

- [ ] Ajouter des contraintes au niveau des champs :
- `String(length)` au lieu de `String` sans taille (par exemple `String(255)` pour `email`)

- [ ] Dans `ImportJob` :
- Ajouter un champ `filename` (ou `source`) pour garder une trace du fichier CSV utilisé.
- Permettre un champ `error_log` (texte ou JSON) pour stocker les lignes rejetées ou les erreurs précises détectées.

### Améliorations possibles des api/crud/base.py

- [ ] Ajouter des méthodes `update` et `delete` pour compléter les opérations CRUD.
👉 La classe actuelle respecte le périmètre demandé (Create et Read uniquement),
mais resterait incomplète dans un contexte CRUD général.

### Améliorations possibles des schémas Pydantic

- [ ] Ajouter des contraintes de validation supplémentaires dans `ClientSchema`, comme :
- `name`: longueur minimale/maximale (`min_length`, `max_length`)
- `birth_date`: validation personnalisée pour interdire les dates futures

### Améliorations des schémas et validations

- [ ] Ajouter un validateur pour interdire les dates de naissance futures.
- [ ] Vérifier que le champ `name` n'est pas vide ou uniquement composé d'espaces.
- [ ] Afficher des messages d'erreur personnalisés pour les emails invalides.
- [ ] Ajouter un validateur racine pour détecter des incohérences multi-champs.
- [ ] Ajouter des tests de validation sur des entrées clairement invalides.
- [ ] Uniformiser l’utilisation de `EmailStr` (utilisé dans `ClientSchema`, mais pas dans `ClientResponse`)
👉 Cela permettrait de garder une validation forte même en sortie.

### Améliorations de l’authentification/ Securité

- [ ] Ajouter un provider de gestion des identifiants (`CredentialProvider`) pour centraliser la logique d’accès aux `client_id` et `client_secret` (ex. depuis un fichier, une base de données ou un gestionnaire de secrets).
- [ ] Gérer l’expiration des tokens côté client en ajoutant un mécanisme de **renouvellement périodique** via un endpoint `/refresh_token`.
- [ ] Implémenter un système de **permissions** et de **rôles** dans les tokens JWT (ex. `"role": "admin"`, `"scopes": ["read", "write"]`) pour une gestion plus fine de l’accès aux routes.
- [ ] Logger toutes les tentatives d’authentification, réussies ou échouées, avec le `client_id` et l’IP (sans journaliser les secrets).
- [ ] Ajouter des tests automatisés couvrant les cas :
- Absence du header `Authorization`
- Format invalide du token
- Expiration du token
- Tentative avec des credentials invalides
- Accès à une route protégée sans le bon rôle ou scope

### Améliorations Securité Limitation de débit (Throttling)

- [ ] Implémenter une limitation de débit globale sur les endpoints sensibles (ex: `/access_token`, `/import`) pour éviter les abus.
- [ ] Implémenter un système de throttling qui reconnaît le rôle de l’utilisateur ex. `"role": "admin"` à partir du token JWT.
- [ ] Retourner une erreur 429 (`Too Many Requests`) lorsque la limite est atteinte.
- [ ] Ajouter des en-têtes HTTP pour informer le client :
- `X-RateLimit-Limit`: nombre max autorisé
- `X-RateLimit-Remaining`: combien de requêtes restantes
- `Retry-After`: temps à attendre avant de retenter

### Caching avec Redis

- [ ] Mettre en place un cache Redis pour améliorer les performances des endpoints fréquents.
- [ ] Éviter de mettre en cache les données sensibles ou personnelles notamment les informations clients (`email`, `birth_date`), pour respecter la confidentialité et les règles de sécurité.
- [ ] Prévoir un mécanisme d’invalidation du cache en cas de modification des données sous-jacentes (signal).


# Notification email via signal après traitement CSV

## Description
- [ ] Mettre en place un **signal** (ou un système d’événement) qui se déclenche une fois que la tâche d’import CSV est terminée.
- [ ] Ce signal appellera une fonction responsable d’envoyer un email de notification au(x) destinataire(s).

## Détails
- [ ] Définir un signal `import_completed`
- [ ] Émettre ce signal à la fin de la fonction/tâche de traitement CSV, en passant un résumé du traitement.
- [ ] Connecter un handler au signal qui envoie l’email de notification.
- [ ] Cette approche découple le traitement CSV et la notification, facilitant la maintenance
Loading