Le projet doit être rendu sous la forme d’un repository (gitlab ou github)
Le projet est à faire maximum en trinôme.
Vous pouvez ajouter un README si vous avez un choix à justifier ou un blocage.
Il doit être rendu au plus tard le 8 février à 23h59
Le but de ce projet est de créer 3 services pour faire de la gestion de stock magasin.
Les trois services sont les suivants :
-
Products : un référentiel de produit.
-
Stores : un référentiel de magasin avec les produits en stock.
-
Gateway : un service qui permet de faire proxy devant les deux autres pour gérer la partie sécurité.
Chaque service a sa propre spécification, des contraintes sont à respecter pour chaque service.
Elles ne sont pas impératives,
par exemple s’il est demandé une base de donnée h2, il vaut mieux une version HashMap en mémoire qu’un service qui ne fonctionne pas.
La liste des APIs doit inclure au minimum les endpoints demandés, mais il est possible d’en ajouter d’autres si nécessaire.
Le service Products est un service qui permet de gérer des familles de produits et les produits associés.
Contraintes :
-
Ce service doit fournir une implémentation de la "base de donnée" :
-
en h2 par default,
-
avec une hashmap si le profile DEV est activé.
-
/!\ Par souci de simplification, ce n’est pas grave si une connexion h2 et son repo jpa sont créées, il faut juste que le code utilise la hashmap
-
-
-
Ce service ne doit pas utiliser les annotations
@Compoment,@Service,@Repository,@Controller.-
@RestController peut-être utilisé, mais uniquement dans le cadre d’un controlleur HTTP
-
-
Ce service doit avec une architecture en couche :
-
La couche
controlleurqui fait la validation des données et la transformation DTO → Format interne. -
La couche
servicequi fait la logique métier. -
La couche
repositoryqui fait la gestion de la base de donnée (ça peut être le repository JPA).
-
Une famille se présente sous la forme suivante :
{
"id": "9a852ad2-4b0a-4f74-8e23-5305b733c263",
"name": "Bike",
"description": "All kind of bikes"
}Son ID est un UUID qui sera choisi par le service lors de la création de la famille, jamais par l’utilisateur.
Le nom d’une famille est unique, mais la description ne l’est pas.
Le nom doit faire entre 3 et 30 caractères.
La description doit faire entre 5 et 100 caractères.
Il faut fournir les endpoints suivants :
L’API doit répondre :
-
201avec la famille créée en body. -
400si les données sont invalides. -
409s’il y a un conflit de nom.
Retourne la liste des familles enregistrées.
L’API doit répondre :
-
200avec la famille correspondante à l’ID si elle existe. -
400si le format de l’id est invalide. -
404si la famille n’existe pas.
L’API doit répondre :
-
200avec la famille mise à jour. -
400si les données sont invalides. -
409s’il y a un conflit de nom.
Un produit se présente sous la forme suivante :
{
"id": "a6efa614-0235-4180-bbe7-0ff30f3bb858",
"name": "RC 500",
"description": "VELO ROUTE CYCLOTOURISTE",
"price": {
"amount": 875,
"currency": "EUR"
},
"family": {
"id": "9a852ad2-4b0a-4f74-8e23-5305b733c263",
"name": "Bike",
"description": "All kind of bikes"
}
}L’ID est un UUID généré par le service lors de la création du produit, jamais pas l’utilisateur.
Le nom d’un produit doit faire entre 2 et 20 caractères.
La description doit faire entre 5 et 100 caractères ou être nulle.
Le prix est un objet avec un montant positif et une devise sur 3 caractères alphabétique majuscule (ex: EUR et non eur).
La famille ne peut pas être nulle, ni modifiée par l’API des produits.
Il faut fournir les endpoints suivants :
L’API doit répondre :
-
201avec le produit créé. -
400si les données sont invalides ou si la famille n’existe pas.
GET /api/v1/products?familyname=Bike&minprice=100&maxprice=200 : Permet de récupérer tous les produits
Les critères de filtrage familyname, minprice et maxprice sont tous optionnels.
Il faut respecter la règle: 0 < minprice < maxprice
L’API doit retourner :
-
200avec la liste des produits correspondants aux critères. -
400si les critères de filtrages sont incohérents.
L’API doit répondre :
-
200avec le produit correspondant à l’ID s’il existe. -
400si le format de l’id est invalide. -
404si le produit n’existe pas.
Permet de mettre à jour un produit.
Ce endpoint permet aussi de changer la famille d’un produit.
L’API doit répondre :
-
200avec le produit mise à jour. -
400si les données sont invalides ou que la nouvelle famille n’existe pas.
Permet de supprimer un produit s’il n’est plus en stock dans aucun magasin (i.e. n’existe pas pour le magasin ou stock=0).
Avant la suppression, tous les stocks à 0 du magasin doivent être supprimés.
-
204si le produit est supprimé. -
400si l’id est invalide. -
409s’il existe encore du stock pour ce produit.
Le service Stores est un service qui permet de gérer les informations de contact, les magasins et leur stock des produits.
Contraintes :
-
Ce service doit fournir une implémentation de la "base de donnée" en h2.
-
Le service ne peut utiliser que le client http
WebClient. -
La gestion des erreurs doit passer par un
ControllerAdvice.
Un contact se présente sous la forme suivante :
{
"id": 1,
"email": "my@email.com",
"phone": "0123456789",
"address": {
"street": "Rue truc",
"city": "Nantes",
"postalCode": "44300"
}
}L’ID est un entier généré par la base de donnée.
L’email doit avoir un format valide.
Le téléphone doit être un numéro de téléphone valide (10 chiffres).
La rue doit faire entre 5 et 50 caractères.
La ville doit faire entre 1 et 30 caractères.
Le code postal doit être un code postal valide (5 chiffres).
Il faut fournir les endpoints suivants :
L’API doit répondre :
-
201avec le contact créé en body. -
400si les données sont invalides.
La liste des contacts optionnellement filtrée par la ville.
L’API doit répondre :
-
200avec le contact correspondant à l’ID s’il existe. -
400si le format de l’id est invalide. -
404si le contact n’existe pas.
Lors d’un update de contact, on ne peut pas changer en même temps l’email et le téléphone.
L’API doit répondre :
-
200avec le contact est mise à jour. -
400si les données sont invalides.
Un magasin se présente sous la forme suivante :
{
"id": 1,
"name": "Atlantis",
"contact": {
"id": 1,
"email": "my@email.com",
"phone": "0123456789",
"address": {
"street": "Rue truc",
"city": "Nantes",
"postalCode": "44300"
}
},
"products": [
{
"id": "e437f62a-432e-4aef-a440-6c86d3b09901",
"name": "RC 500",
"quantity": 1
}
]
}L’ID est un entier généré par la base de donnée.
Le nom doit faire entre 3 et 30 caractères.
Le contact ne peut pas être nul.
La liste de produits ne peut pas être nulle, mais peut être vide.
Elle ne peut pas être initialisée avec le magasin.
Elle ne peut pas contenir de doublons.
Le nom du produit doit être cohérent avec le contenu du service product.
Il faut fournir les endpoints suivants :
Cette API permet de créer un magasin. Si le contact n’existe pas, il est créé. S’il existe, il est utilisé sans mise à jour.
On ne peut pas initialiser la liste de produits avec cette API. Si elle est fournie, elle doit être ignorée.
On ne peut pas mettre à jour un magasin avec cette API.
L’API doit répondre :
-
201avec le magasin créé. -
400si les données sont invalides.
Cette API permet de récupérer la liste des magasins triée par nom croissant (i.e. a→z).
L’API doit répondre :
-
200avec le magasin correspondant à l’ID s’il existe. -
400si le format de l’id est invalide. -
404si le contact n’existe pas.
Cette API permet de mettre à jour les informations d’un magasin, mais pas la liste de produits.
Elle permet de changer le contact du magasin.
L’API doit répondre :
-
200avec le magasin mise à jour. -
400si les données sont invalides.
Il est possible de gérer les stocks des produits dans les magasins avec trois APIs.
POST /api/v1/stores/{storeId}/products/{productId}/add?quantity=2 : Permet d’ajouter une quantité de produit au stock d’un magasin
Le paramètre quantity est optionnel, mais doit être positif s’il est fourni.
Si le paramètre quantity n’est pas fourni, il est initialisé à 1.
Si le produit n’existe pas dans le magasin, il faut vérifier qu’il existe puis l’ajouter.
L’API doit répondre :
-
200avec le produit mis à jour. -
400si les données sont invalides. -
404si le magasin n’existe pas.
POST /api/v1/stores/{storeId}/products/{productId}/remove?quantity=2 : Permet de retirer une quantité de produit du stock d’un magasin
Le paramètre quantity est optionnel, mais doit être positif s’il est fourni.
Si le paramètre quantity n’est pas fourni, il est initialisé à 1.
L’API doit répondre :
-
200avec le produit mis à jour. -
400si les données sont invalides. -
404si le produit n’est pas dans le magasin ou le magasin n’existe pas. -
409si le stock final est inférieur à 0.
Cette API prend en body une liste de produits à retirer du stock.
[
"e437f62a-432e-4aef-a440-6c86d3b09901",
"9a852ad2-4b0a-4f74-8e23-5305b733c263"
]Si un produit n’est pas dans le magasin, il est ignoré.
L’API doit répondre :
-
204si les produits sont retirés ou ignorés. -
400si les données sont invalides ou si un produit est en double dans la liste. -
404si le magasin n’existe pas.
Le service Gateway est un service qui permet de faire proxy devant les deux autres services.
C’est-à-dire qu’il ne fait que rediriger les requêtes vers les services appropriés après avoir vérifié l’authentification de l’utilisateur.
Lors de la redirection, il doit ajouter un header X-User avec le login de l’utilisateur.
Les services Products et Stores doivent filtrer les requêtes,
avec le code le plus commun possible (entre endpoint, voire entre services),
et ne laisser passer que celles avec ce header.
Pour qu’un utilisateur puisse appeler les endpoints autres que les ajouts et suppression de stock,
il doit avoir le role ADMIN
De base le service doit avoir un utilisateur ADMIN au login ADMIN/ADMIN
Le endpoint POST /api/v1/user permet de créer un utilisateur avec le body suivant :
{
"login": "user",
"password": "password",
"isAdmin": false
}Contraintes :
-
Le service doit fournir une gestion du
UserDetail:-
In memory si la property
gateway.security=inmemory. -
En base de donnée sinon.
-