# TP application gestion d'equipes pour l'Université Sorbonne Paris Nord > L'objectif de ce TP est de développer une application en `fullstack` qui permet de gérer des équipes de Football :soccer: > > Une partie backend/api en Spring boot permettra de gérer les équipes avec une base de données `in memory` `H2` et d'exposer des `endPoints` en REST. > Spring data JPA et Hibernate seront utilisé pour la partie accès aux données > > Une partie front end permettra de gérer les équipes via le framework >Angular 11. > NodeJs, TypeScript, RxJs, Material... seront utilisés avec Angular > > L'application va permettre de lister une liste d'équipe par défaut > D'afficher le détail d'une équipe > D'ajouter une nouvelle équipe en base de donnée via un formulaire Web > De supprimer une équipe existante > Et en option d'afficher, ajouter, supprimer une liste de joueurs pour une équipe ## :memo: Partie backend/API ### Installer l'open JDK 15 selon l'OS sur lequel vous travaillez (Windows, Linux, Mac) https://jdk.java.net/15/ Certains tuto expliquent comment faire selon l'OS : - Windows : https://java.tutorials24x7.com/blog/how-to-install-openjdk-15-on-windows-10 - Linux/Ubuntu : https://techoral.com/blog/java/install-openjdk-15-ubuntu.html Les variables d'environnement pour Java ne sont pas obligatoires pour ce TP l'application Java Spring sera lancé par l'IDE IntelliJ Idea. :rocket: ### Créer la structure du projet Spring Boot Aller sur le lien suivant : https://start.spring.io/ Dans cet interface `Spring Initializr` nous allons pouvoir initialiser une structure de projet avec les dépendances souhaitées. Spring Boot est initialisé avec un outil de `build` comme Maven ou Gradle, dans le cadre de ce TP c'est Maven qui a été choisi. Maven reste à ce jour l'outil de `build` le plus utilisé de l'ecosystème Java. Spring peut également être initialisé avec plusieurs languages, Java, Kotlin et Groovy. Dans le cadre de ce TP, c'est Java qui a été choisi avec la version 15. ![](https://i.imgur.com/foRVpoT.png) Dans la partie droite plusieurs dépendances peuvent être ajoutées au projet avec un champ d'autocomplétion. Les dépendances suivantes doivent être ajoutées : - Spring web - Spring data JPA - H2 ![](https://i.imgur.com/WBg8YZP.png) Dans le bloc `Project Metadata`, vous pouvez mettre : - Group : fr.sorbonne.paris.nord.university - Artifact : tp-teams-handling-api - Name : tp-teams-handling-api - Description : TP for the Sorbonne Paris Nord University that handles the api part - Package name : fr.sorbonne.paris.nord.university.api - Packaging : jar - Java : 15 Cliquez ensuite sur le bouton "generate". ### Lancer l'IDE Dans le cadre de ce TP nous préconisons l'utilisons de l'IDE IntelliJ Idea. Cet IDE ets gratuit pour les étudiants et il est surement le plus populaire dans l'ecosytème Java et aussi le plus utilisé. Ce qui fait sa force c'est qu'il propose également des supports très complets dans quasi tous les languages, dont ceux utilisés dans la partie front (Angular, ES6, Typescipt, CSS, SCSS...) - Lancer l'IDE - Utiliser l'option "Create project from existing source" - Dans la fenêtre qui va s'ouvrir, sélectionner le fichier `pom.xml` du projet Spring boot généré précédement. Ce fichier pom est à la racine du projet. - IntelliJ va initialiser le projet et grâce au fichier pom.xml, va automatiquement détécté qu'il s'agit d'un projet Maven. L'IDE propose également beaucoup de supports pour la partie Spring. - Séléctionnez ensuite l'option `File/Project Structure` - Séléctionnez `Platform settings/SDK` à gauche - Cliquez ensuite sur le bouton `+` - Un explorateur de fichier va s'ouvrir, aller vers le chemin contenant le répertoire d'install du JDK (celui qui a été dezippé suite au téléchargement) - Si cela a bien fonctionné, vous verrez un 15 dans la liste comme sur l'image ci dessous. ![](https://i.imgur.com/OmP69WU.png) - Allez ensuite dans `Project settings/Project` - Choisir le SDK 15 ajouté précédement grâce à la liste déroulante proposée - Dans project language level, séléctionnez Java 15 ![](https://i.imgur.com/tiyF0Ag.png) ### Installer et compiler le projet avec Maven IntelliJ propose une version de Maven intégré `embedded`, donc pas dans le cadre de cet exercice, pas besoin d'installer Maven en global sur la machine. L'IDE propose une fenêtre Maven à droite avec tout le cycle de vie d'un projet Maven `Lifecyle` : ![](https://i.imgur.com/OGO0JUo.png) - Double cliquez sur `clean`, ceci videra le répertoire de travail `target` utilisé par Maven - Double cliquez sur install, ceci va installer toutes les dépendances proposées dans le fichier `pom.xml`, compiler le projet Java et executer les tests s'il y en a dans le projet. Si tout se passe bien, la console dans l'IDE affichera un SUCCESS : ![](https://i.imgur.com/V4gmfxq.png) Si ce n'est pas la cas, vérifiez si le bloc suivant existe dans le fichier pom.xml, dans la section `plugins` : ```xml= <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>--enable-preview</argLine> </configuration> </plugin> ``` Si ce n'est pas le cas, copier coller ce bloc. En effet Java 15 propose des features en cours de validation `enable-preview` et lors des tests unitaire, Maven utilise le plugin `maven-surefire-plugin`. Parfois il y a besoin de configurer ça dans le plugin Maven surefire. Félicitation, vous venez de compiler et installer pour la première fois votre projet backend/api Spring Boot :i_love_you_hand_sign: ### Ajouter votre premier webservice REST Dans cette partie nous allons ajouter la classe qui va contenir notre premier webservice REST. Ce premier Webservice va juste afficher "Hello World" sur une page Web. - Dans le package fr.sorbonne.paris.nord.university.api, ajouter un package `controller` - Y ajouter une nouvelle class Java appelé `TeamController` - Cette classe contient une anotation Spring `@RestController` pour indiquer qu'il s'agit d'un controller pour exposer du Rest - Elle contient une méthode et le type de Webservice, dans cet exemple un `@Get` car nous souhaitons récupérer une resource - L'URL associée à la resource `hello` dans cet exemple ```java= @RestController public class TeamController { @GetMapping("/hello") public String getTeams() { return "Hello World"; } } ``` - Chaque projet Sring boot contient un `main` avec un serveur embarqué, tomcat par défaut pour du Spring Web (Jetty peut aussi être utilisé) - Ouvrir la classe `TpTeamsHandlingApiApplication` à la racine du package api - Lancer le main via IntelliJ (bouton lecture en vert sur la classe ou la méthode main) - Si tout se passe bien Spring n'affichera pas d'erreur dans la console - :warning: spring utilise par défaut le port `8080` dans son serveur embarqué, si ce port est occupé par un autre process sur votre machine, il va falloir soit arrêté le process en question, ou soit changer le port par défaut utilisé par Spring boot, via le fichier `application.properties` ou `application.yaml` - ouvrir un navigateur Web et afficher sur la colone l'URL suivante : `http://localhost:8081/hello` - Normalement le text `hello` devrait s'afficher Félicitations, vous venez de développer votre premier webservice Rest et d'y afficher le résultat. ### Configuration de la base embarquée H2 Dans le répertoire `src/main/resource` ajouter 2 fichiers : - schema.sql : qui va permettre de gérer la structure de notre modèle de donnée ```sql= CREATE TABLE team ( id IDENTITY NOT NULL PRIMARY KEY, name VARCHAR(200), slogan VARCHAR(500) ); ``` - data.sql : qui va permettre d'ingérer de la donnée dans la base ```sql= INSERT INTO team (name, slogan) VALUES ('PSG', 'Revons plus grand'); INSERT INTO team (name, slogan) VALUES ('Real Madrid', 'Les galactiques'); INSERT INTO team (name, slogan) VALUES ('Barcelone', 'La Macia'); INSERT INTO team (name, slogan) VALUES ('Bayern', 'Les puissants en Allemagne'); INSERT INTO team (name, slogan) VALUES ('Manchester United', 'Les red devils'); ``` - Ajouter ensuite de la configuration pour le modèle de données H2, dans le fichier `application.properties` proposé par défaut dans Spring boot : ```properties= spring.h2.console.enabled=true ``` Cette configuration permet de pouvoir afficher la console Web H2, pour y voir le modèle de données. - Gérer les paramètres d'accès à la base de données : ```properties= spring.datasource.username=sa spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.password= spring.datasource.dbname=testdb ``` Ces paramètres permettent de configurer les éléments nécessaires pour accéder à la base de données. - Configuration pour l'ORM Hibernate : ```properties= spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=validate ``` Le premier paramètre `spring.jpa.show-sql` permet d'afficher dans la console les requêtes SQL lancées par Hibernate. Le second paramètre `spring.jpa.hibernate.ddl-auto` permet d'indiquer à Hibernate de valider que les types des objets `entity`, soient coéherents avec les types de colonnes des tables associées. Ainsi le mapping objet relationnel est cohérent car une validation est effectuée. Par défaut Hibernate utilise un mode `create`, qui permet de créer et modifier le model de données à partir des objets entity. Or dans notre cas nous souhaitons déléguer cette tâche à H2. - Relancer l'application Spring boot (la classe avec la méthode main) afin de prendre en compte les nouvelles configurations. - Lancer la console H2 sur depuis un navigateur en tappant l'url suivante : ```text= http://localhost:8080/h2-console ``` Saisir les paramètres d'accès à la base, ajoutés précédement dans le fichier de configuration. ![](https://i.imgur.com/Epi5X8U.png) Cliquez ensuite sur `Connect`, vous verrez ensuite la table `team` avec les données et vous pourrez y executer des requêtes SQL ### Développement de la partie accès aux données - A la racine du package api, ajouter un package `entity` et y ajouter une classe `TeamEntity`. Implémenter le contenu de cette classe avec le mapping objet relationel. Cet objet doit être associé à la table `team` - A la racine du package api, ajouter un package `repository` et y ajouter une interface `TeamRepository` qui etend l'interface `JpaRepository` proposée par Spring Data Jpa. Cette interface propose des méthodes par défaut pour des traitements autour de la donnée. - A la racine du package api, ajouter un package `service` et y ajouter une classe `TeamService` - Injecter l'interface `TeamRepository` dans la classe `TeamService`, grâce au support d'injection de dépendance proposé par Spring. Nous utiliserons l'injection par constructeur, car c'est une pratique recommandée (mockable facilement et évite les dépendances cycliques) - Dans la classe service, implémentez les services suivants : - Récuperer toutes les équipes en base de données - Récuperer une équipe en base à partir de son ID - Insérer une équipe en base - Supprimer une équipe existante de la base de données à partir de son ID Ne pas hésiter à s'aider des documentations sur internet, Spring data Jpa, Hibernate... ### Implementer des tests d'integration pour la classe TeamService - Dans le package src/test/java à la racine du package api, ajouter une classe TeamServiceTest - Dans cette classe, faire un test passant pour chacune des methodes du service - Pour la convention de nommage utilisées, les développeurs sont libres, ils peuvent par exemple utiliser une convention `should` ou une convention en `given when then` ```java= @Test public void shouldReturnTheExpectedTeam_whenGetTeamByExistingId(){ .... } ``` ```java= @Test public void givenExistingId_whenGetTeamById_thenExpectedTeamInResult(){ // Given. // When. // Then. } ``` Pour les assertions et les tests, le framework `JUnit` en version 5 est utilisé. Un framework très populaire dans l'écosystème Java, appelé `AssertJ` est dirèctement apporté par Spring Boot. Dans le cadre de cet execice, nous souhaitons que les assertions se fasse avec AssertJ, en voici un exemple : ```java= import static org.assertj.core.api.Assertions.assertThat; @Test public void given...(){ // Given. // When. String result = ... // Then. assertThat(result).isNotNull().isNotEmpty() } ``` AssertJ permet de faire des assertions en mode `fluent` ainsi qu'une api très fournie pour la partie testing. ### Appeler les service depuis la classe TeamController - Modifier la classe `TeamController` et y ajouter les webservices suivants : - Récuperer la liste des équipes - Récuperer une équipe à partir de son ID - Crée une équipe - Supprimer une équipe existante - Aidez vous de la doc de Spring pour Spring Web et Spring MVC Rest. - Aidez vous également de la documentation sur les bonnes pratiques de Rest, pour utiliser le bon verbe en fonction du Webservice et de la bonne URL. - Dans le cadre de cet exerice, nous ne souhaitons pas directement exposer l'objet `TeamEntity` aux clients de notre api, mais un objet TeamDto. Si notre api évolue, ceci nous permet de controller exactement ce que nous exposons. - L'objet TeamDto peut être un Pojo classique (avec getters/setters) ou une `Record` qui est un concept qui encore en preview dans Java 15. L'avantage des Record est que le `boilerplate code` (code standard) n'a pas besoin d'être écrit. ### Tester unitairement les webservices Rest Après avoir implémenté les webservices Rest dans la class TeamController, nous souhaitons les tester avec du code pour garantir qu'il fonctionne. Prédémment des test d'intégration ont été fait dans les traitements métiers et d'accès à la donnée. L'objectif içi est de tester les statuts de retour de chacun de webservices, sans avoir besoin de redescendre dans la classe service et l'accès aux données. - Créer une classe `TeamControllerTest` dans le package test à la racine de api. - Nous souhaitons pour tester nos webservices, utiliser un librairie populaire dans l'ecosystème Java, appelée `Rest Assured`. Cette librairie utilise une convention en `given/when/then` (comme proposé précédement dans la partie test des services). Spring boot propose un support natif pour Rest, à travers la class : `RestAssuredMockMvc` : Ajouter dans le pom : ```xml= <dependency> <groupId>io.rest-assured</groupId> <artifactId>spring-mock-mvc</artifactId> <scope>test</scope> </dependency> ``` Pour vous guider un peu, voici une partie de code qui doit être ajouté dans la classe `TeamControllerTest` : ```java= @BeforeEach public void initialiseRestAssuredMockMvcStandalone() { RestAssuredMockMvc.standaloneSetup(teamController); } ``` - Faire un test pour chacun des webservice (Get, Post, Put, Delete), en vérifiant qu'il retournent bien un statut 200. - Utilisez l'api `Mockito` pour mocker la classe `TeamService` et les méthodes de cette classe. - Utiliser ensuite Rest assured pour faire les assertions. Pour la suite, nous allons partir dans le principe qu'une nouvelle règle métier a été proposé par l'équipe fonctionnelle du projet. Lors de l'ajout d'une équipe en base de données, si le champ `slogan` de la class `team` est null ou vide, nous souhaitons que l'ajout ne se fasse pas et qu'une exception soit levée. Pour les clients de notre api, nous souhaitons renvoyer un statut Rest 400 (Bad Request). - Ajouter la nouvelle règle dans la classe `TeamService`, si le champ slogan est nul ou vide, alors on `throw` une exception `TeamInvalidException`. Cette classe peut, par example, être ajoutée dans un package exception à la racine du package api. - Dans la classe TeamController, nous souhaitons renvoyer un statut 400 si l'exception est levée depuis la classe service. Trouver un moyen de renvoyer un statut 400 dans la classe controller sans faire de `try catch`. Utilisez la doc de Spring pour voir quelles sont les moyens proposés pour le faire. - Dans la classe `TeamControllerTest`, ajouter un nouveau test pour tester ce cas d'erreur. Pour nous faciliter la vie, nous allons utiliser Mockito pour mocker la méthode `addTeam` de la classe `TeamService`, en simulant le cas ou elle lève une `TeamInvalidException`. Utilisez ensuite Rest Assured, pour tester le `Post` et faire l'assertion sur le statut 400. ## :memo: Partie frontend Après durement travaillé sur la partie backend et vérifier que tout fonctionnait corerctement, nous passons à la partie front. Nous allons utiliser le framework `Angular` version 11. - Installer NodeJs : https://nodejs.org/en/download/ (suivez les instructions selon l'OS) - Node est installé avec le gestionnaire de paquets `NPM`. `NPM` va permettre d'installer des `packages` en global sur la machine mais aussi dans un projet Angular par example. - Pour vérifier que node et npm ont bien été installés, lancer les commandes suivantes, depuis une invite de commande (shell pour Linux et Mac et cmd pour Windows) : ```shell= node --version npm --version ``` Si les installations se sont bien passées, les versions s'afficheront dans l'invite de commande. - Installer le package `Angular cli` en global sur votre machine. Ce package va permettre de créer un projet Angular avec une structure initiale et une application qui fonctionne. Pour vous aider, vous pouvez aller sur la doc officielle de Angular cli : https://angular.io/cli ```shell= npm install -g @angular/cli ``` - Créer ensuite un nouveau projet appelé `tp-teams-handling-app` via la commande suivante : ```shell= ng new tp-teams-handling-app ``` N'hésitez pas à aller sur la doc d'Angular : https://angular.io/guide/setup-local - Lancer ensuite l'application : ```shell= cd tp-teams-handling-app ng serve --open ``` Le `ng serve` va lancer un serveur Node et lancer l'application Angular. L'option --open permet d'ouvrir un navigateur automatiquement. Vous verrez ensuite l'adresse suivante http://localhost:4200/, une page affichant des éléments.