sh# job-ms

Description
-----------
Ce microservice Spring Boot gère des « jobs » persistés en base et leur ordonnancement via un registre de tâches cron.

Composants clés
---------------
- `JobEntity` : l'entité JPA représentant un job (id, nom, expression cron, payload, type, enabled ...).
- `JobRepository` : Spring Data JPA pour l'accès persistant aux jobs.
- `JobService` : logique métier principale — CRUD, activation/désactivation, et coordination avec le planificateur (`CronRegistry`).
- `CronRegistry` : composant responsable de l'enregistrement, reprogrammation et annulation des jobs côté planificateur.
- `JobController` : exposition HTTP des opérations (CRUD + toggle).

Comportement principal
----------------------
- Création : si `enabled == true` après sauvegarde, le job est planifié via `cronRegistry.scheduleJob(job)`.
- Mise à jour : l'entité est mise à jour en base puis `cronRegistry.rescheduleJob(job)` est appelée pour refléter les changements d'expression cron ou d'état.
- Suppression : avant suppression en base, `cronRegistry.cancelJob(id)` est appelée pour retirer la tâche du planificateur.
- Activation / désactivation : le flag `enabled` est basculé en base ; on appelle `scheduleJob` si activation, `cancelJob` si désactivation.
- Liste des jobs actifs : méthode utilitaire exposée par `JobService` qui retourne les jobs avec `enabled = true`.

API REST (attendue)
-------------------
Les routes exposées typiquement par le contrôleur sont :
- `GET /jobs` – lister tous les jobs
- `GET /jobs/{id}` – récupérer un job
- `POST /jobs` – créer un job
- `PUT /jobs/{id}` – mettre à jour un job
- `DELETE /jobs/{id}` – supprimer un job
- `POST /jobs/{id}/toggle?enable=true|false` – activer/désactiver un job

Exemple JSON d'un job
---------------------
{
  "id": 1,
  "name": "my-job",
  "cronExpression": "0 0/5 * * * ?",
  "enabled": true,
  "payload": "{...}",
  "type": "SOME_TYPE"
}

Contraintes et comportements d'erreur
-------------------------------------
- Les méthodes de modification sont annotées `@Transactional` — en cas d'erreur, les changements en base seront annulés.
- Si un job n'est pas trouvé lors d'une opération, le service lance actuellement `IllegalArgumentException("Job not found")`.
  Il est recommandé de remplacer cela par une exception métier plus explicite (ou `EntityNotFoundException`) pour un mapping HTTP approprié.
- Il faut s'assurer que l'état de la base et celui du planificateur sont synchrones au démarrage (resynchronisation possible).

Exigences & dépendances
-----------------------
- Java 17+ (vérifier `pom.xml` pour la version ciblée)
- Spring Boot
- Spring Data JPA
- Base de données relationnelle (H2, Postgres, MySQL...) configurée dans `application.yml`

Build & exécution
------------------
Compiler l'application :

```bash
mvn clean package -DskipTests
```

Lancer en développement :

```bash
mvn spring-boot:run
```

Ou exécuter le jar produit :

```bash
java -jar target/job-ms-*.jar
```

Tests
-----
- Les tests unitaires doivent moquer `JobRepository` et `CronRegistry` pour vérifier que `scheduleJob`, `rescheduleJob` et `cancelJob` sont appelés aux bons moments.
- Ajouter un test d'intégration démarrant le contexte Spring avec une base en mémoire pour valider le flux complet.

Suggestions d'amélioration
--------------------------
- Remplacer `IllegalArgumentException` par une exception métier plus explicite et mapper correctement en 404/400 au niveau du contrôleur.
- Ajouter une initialisation au démarrage qui resynchronise les jobs `enabled=true` en base avec le `CronRegistry` (utile après redémarrage).
- Ajouter des métriques et des logs structurés lors des opérations de scheduling/rescheduling/cancel.
- Tests unitaires et d'intégration plus complets (cas d'erreurs de parsing de cron, payload invalides, collisions d'ID, etc.).

Dépannage rapide
----------------
- Si un job n'est pas exécuté alors qu'il est `enabled=true` : vérifier que `CronRegistry` contient bien l'entrée correspondante (logs au démarrage / endpoint d'inspection si présent), contrôler la validité de l'expression cron.
- Si l'application perd la connexion à la base : vérifier `application.yml` et la disponibilité de la base ; envisager une stratégie de reconnexion/backoff.

Contact
-------
Pour plus d'informations ou évolution, regardez les classes clés :
- `hugebrain.job_ms.service.JobService`
- `hugebrain.job_ms.config.SchedulerConfig`
- `hugebrain.job_ms.service.CronRegistry` (implémentation)

Exemple d'utilisation dans un microservice
-------------------------------
```java
@GetMapping
public ResponseEntity<List<PlanningMenageDto>> list() {

    JobRequest jobRequest = new JobRequest();
    jobRequest.setName("Liste plannings");
    jobRequest.setEnabled(true);
    jobRequest.setType("CUSTOM_HTTP");
    jobRequest.setCronExpression("0 0/1 * * * ?");
    jobRequest.setPayload("http://localhost:8080/api/v1/plannings-menage");

    new RestTemplate().postForObject("http://localhost:9900/api/jobs", jobRequest, Void.class);

    return ResponseEntity.ok(service.findAll());
}
```


