Dernière modification : Dec 08 , 2024

Nous allons apprendre à faire communiquer notre frontend Quarkus en faisant un appel REST sur le backend.

Objectifs

  • Déclarer l'adresse de notre backend dans la configuration du frontend Quarkus
  • Savoir utiliser la notion de profil pour faire changer l'adresse en fonction de l'environnement.
  • Déclarer une resource REST dans le backend contenant le message de bienvenue
  • Tester la resource REST
  • Récupérer le paramètre contenant l'adresse du backend dans le frontend
  • Ajouter un code pour communiquer en REST dans le frontend
  • Désérialiser la donnée de JSON en objet Java
  • Tester la communication

Architecture de notre application.

Architecture initiale

Créer le Endpoint dans le backend.

Ouvrez le projet backend-monster-adoption-store.

Nous allons ajouter l'extension rest-jackson afin d'ajouter la capacité à Quarkus de convertir ( sérialiser ) des objects Java en JSON.

Pour cela, tapez la commande suivante :

mvn quarkus:add-extension -Dextension=rest-jackson

Créer un fichier java GreetingResource.java dans le package com.byoskill du projet.

Ajoutez ou modifier le code pour avoir quelque chose de similaire :

package com.byoskill;

import java.util.Map;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/motd")
public class GreetingResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, Object> motd() {

        return Map.of("message","Bienvenue sur notre site d'adoption");
    }
}

Tester notre nouvel endpoint

Tester le endpoint en ouvrant votre navigateur à l'adresse http://localhost:8090.

Test du endpoint

Nous avons écrit notre premier endpoint mais nous ne l'avons tester que manuellement.

Ecrivons un test unitaire pour vérifier notre code.

Test unitaire du endpoint

Créer un fichier GreetingResourceTest.java dans le répertoire src/test/java du projet.

Copier coller le contenu suivant

package com.byoskill;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.*;
import static io.restassured.RestAssured.given;

@QuarkusTest
class GreetingResourceTest {
    @Test
    void testHelloEndpoint() {
        given()
          .when().get("/motd")
          .then()
             .statusCode(200)
             .body("message", equalTo("Bienvenue sur notre site d'adoption"));
    }
}

Nous utilisons la commande body pour tester une propriété particulière de notre message JSON en se référant au chemin pour accéder à la variable. Plus d'information sur Documentation officielle de RESTAssured et sur la documentation de JsonPath.

Plus de détails sur le framework ici : Lien vers comment tester avec Quarkus.

Test d'intégration du endpoint

Notre premier test était un test unitaire qui ne nécessite pas le démarrage de l'application Quarkus complet. Toutefois, parfois il est nécessaire d'avoir le contexte de l'application complet afin d'utiliser l'accès à la bse de données ou d'autres resources.

Pour créer un test d'intégration, il faut utiliser le template suivant :

package com.byoskill;

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
class GreetingResourceIT  {
    // Execute the same tests but in packaged mode.
}

Il est alors possible de créer ses tests en utilisant JUnit + RestAssured + Hamcrest.

Lorsque l'application est testée à l'aide de @ QuarkusIntegrationTest, elle est lancée à l'aide du profil de configuration prod, mais cela peut être modifié à l'aide de quarkus.test.integration-test-profile.

Lors de l'ajout de propriétés de configuration spécifiques au test à l'aide de src/test/resources/application.properties (notez qu'il y a test, pas main) sont possibles pour les tests unitaires, ce n'est pas possible pour les tests d'intégration.

Créer le code pour se connecter au backend

Nous allons ajouter le code dans la ressource REST que nous avons créé pour la page principale du site. A chaque chargement de la page, nous interrogerons le backend pour obtenir le message d'accueil du site.

Les étapes sont les suivantes :

  • ajouter l'extension pour utiliser le client REST Quarkus
  • création d'une classe "Modéle" qui permettra de convertir le payload JSON en objet Java
  • création de l'interface du client REST
  • ajout du code d'initialisation du client
  • ajout du code faisant appel au client pour récupérer l'information
  • injecter le résultat dans la page web

Organisation du code

Nous allons considérer que le code relatif à la récupération du message de bienvenue est lié à la fonctionnalité "communication" de notre produit. Par conséquent le code sera placé dans le package com.byoskill.communication.

Les classes définissant les données échangées ou manipulées dans le cadre des fonctionnalités liées à la communication seront placées dans le package com.byoskill.communication.model.

Les classes techniques liées à la communication REST seront distinguées entre la partie interface et la partie implémentation. Cette distinction à l'avantage de pouvoir changer l'implémentation sans modifier l'interface. Pour plus d'information se référer aux articles liés à l'architecture hexagonale.

Ajout de l'extension REST-Client pour Quarkus

Ouvrez un terminal dans le projet frontend-monster-adoption-store et tapez le code suivant :

quarkus extension add rest-client-reactive-jackson

Création du modèle

Nous allons créer une classe Java qui permet de traduire la réponse JSON de notre backend en objet Java.

Créons donc la classe WelcomeMessage.java dans le package com.byoskill.communication.model qui contiendra le code suivant :

package com.byoskill.communication.model;

public class WelcomeMessage {
    private String message;

    public WelcomeMessage() {
    }

    public String getMessage() {
        return message;
    }
}

Notez qu'il est possible d'écrire le même contenu en utilisant une structure record Java.

Voici le code utilisant un Record :

package com.byoskill.communication.model;

public record WelcomeMessage(String message) {

}

Création du connecteur REST

Nous devons déclarer l interface du connecteur REST.

Créons donc la classe WelcomeMessageClient.java dans le package com.byoskill.communication.client qui contiendra le code suivant :

package com.byoskill.communication.client;
import com.byoskill.communication.model.WelcomeMessage;


import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;


@Path("/motd")
@RegisterRestClient
public interface CommunicationMessageClient {

    @GET
    WelcomeMessage getWelcomeMessage();
}

Définir l'adresse du backend

Pour lire les informations du backend, le frontend doit réaliser des appels REST avec l'adresse du backend.

Ouvrir le fichier application.properties dans le projet frontend-monster-adoption-store.

Nous allons déclarer l'adresse du backend quarkus dans la configuration du frontend Quarkus.

# Default address of the backend
quarkus.rest-client."com.byoskill.communication.client.CommunicationMessageClient".url=http://localhost:8090

La chaîne de caractère com.byoskill.communication.client.CommunicationMessageClient est le nom de l'interface du client REST. Toutefois ce n'est pas très pratique de nommer de manière précise le nom de la classe et son package. Il est possible de donner un identifiant unique à notre client REST.

Pour cela il faut modifier le code du client REST au niveau de la ligne avec @RegisterRestClient et remplacer avec le code suivant :

@RegisterRestClient(configKey="communication-api")

Et modifier le fichier application.properties avec le code suivant :

quarkus.rest-client.communication-api.url=http://localhost:8090

Redémarrez le service et vérifiez que le code compile et démarre bien.

Ajouter le code pour communiquer avec le backend

Ouvrez le fichier HomePage.java dans le répertoire src/main/java du projet.

Localisez la ligne déclarant la classe, et insérer dans le corps de la classe, le contenu suivant :

   @RestClient
   CommunicationMessageClient CommunicationMessageClient;

Le stub client est injecté avec l'annotation @RestClient au lieu du CDI @Inject habituel;

Modifiez la méthode get() pour ajouter le code afin d'appeler le client, comme par exemple dans le code ci-dessous :

    @Blocking
    @GET
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get() {
        WelcomeMessage welcomeMessage = CommunicationMessageClient.getWelcomeMessage();
        return index.data("motd", welcomeMessage.message());
    }

Vous remarquerez que nous utilisons l'annotation @Blocking. En effet, par définition Quarkus a une architecture réactive et attend que nous faisions des appels REST asynchrones. Toutefois pour écrire un code simple, nous générons notre page web de manière synchrone et les appels au backend sont également synchrones.

Tester notre code

Il est facilement vérifiable que le code fonctionne en ouvrant la page web.

Affichage du résultat

Toutefois nous souhaitons écrire un test unitaire pour vérifier ce code. Il va falloir mocker le comportement du client REST pour permettre l'exécution sans lancer le backend.

Ajouter le code suivant au fichier pom.xml du frontend afin d'utiliser Quarkus JUnit5 Mockito :

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5-mockito</artifactId>
    <scope>test</scope>
</dependency>

Ouvrez le fichier HomePageTest.java dans le répertoire src/test/java du projet.

Copiez le code suivant :

package com.byoskill;



import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;

import static org.mockito.Mockito.when;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import com.byoskill.communication.client.CommunicationMessageClient;
import com.byoskill.communication.model.WelcomeMessage;

import static io.restassured.RestAssured.given;

@QuarkusTest
class HomePageTest {

    public static final String DEFAULT_WELCOME_MESSAGE = "Welcome to our website";


    @InjectMock()
    @RestClient
    CommunicationMessageClient CommunicationMessageClient;

    @Test
    void testHelloEndpoint() {
        when(CommunicationMessageClient.getWelcomeMessage()).thenReturn(new WelcomeMessage(DEFAULT_WELCOME_MESSAGE));

        given()
          .when().get("/")
          .then()
             .statusCode(200)
             .body(Matchers.containsString(DEFAULT_WELCOME_MESSAGE));
    }

}

Nous remplaçons à l'exécution le code de notre client Rest par un Mock, qui va nous permettre de surcharger le comportement du client REST. Nous allons en particulier lui faire retourner un message de bienvenu exact. Ainsi nous pouvons vérifier le contenu de la page HTML et que ce message par défaut est bien affiché.

Conclusion

Dans cet exercice, nous avons couvert beaucoup d'aspect de Quarkus.

Notamment, nous avons créé une simple ressource REST dans notre backend Quarkus afin de retourner un message de bienvenue pour notre site web. Ce message est retourné dans le format JSON et Quarkus via Jackson se charge de la sérialisation.

Nous avons alors modifié le frontend pour qu'il puisse joindre le backend et lui demander le message de bienvenue. Pour cela, il nous a été nécessaire de créer un client REST.

  • Le client REST nécessite une configuration qui a été ajoutée dans le fichier application.properties.
  • Le client REST est défini par une interface Java et est injecté dans le frontend via l'annotation @RestClient.
  • le client REST Quarkus supporte les appels synchrones et asynchrones.

Les ressources REST qui sont déclarées sont par définitions réactives et donc asynchrones. Si vous créez un client REST avec des méthodes bloquantes, il vous sera nécessaire de déclarer votre ressource REST comment étant synchrone via l'annotation @Blocking. Si vous oubliez l'annotation, l'appel à la ressource sera rejeté et une exception levée.

Plus de documentation sur le client REST dans Quarkus à l'adresse suivante Documentation.