Exercice - Créer une application Quarkus

Terminée

Dans cette unité, vous créez une application Quarkus basique. Vous utilisez Maven pour démarrer l’application et un environnement de développement intégré (IDE) de votre choix pour modifier le code. Utilisez l’environnement Bash configuré dans les conditions préalables pour exécuter le code. Vous utilisez Docker pour démarrer une base de données PostgreSQL locale afin de pouvoir exécuter et tester l’application localement.

Générer l’application Quarkus à l’aide de Maven

Il existe plusieurs façons pour créer une structure de projet Quarkus. Vous pouvez utiliser l'interface web Quarkus, un plug-in IDE ou le plug-in Maven Quarkus. Utilisons le plug-in Maven pour générer la structure du projet.

Vous générez votre application avec plusieurs dépendances :

  • Dépendance resteasy pour exposer un point de terminaison REST
  • Dépendance jackson pour sérialiser et désérialiser JSON
  • La dépendance hibernate pour interagir avec la base de données
  • Dépendance postgresql pour se connecter à la base de données PostgreSQL
  • Dépendance quarkus-container-image-docker pour générer une image Docker

Vous n’avez pas besoin de spécifier des dépendances Azure, car vous exécutez votre application localement, puis déployez une version conteneurisée sur Azure Container Apps.

Remarque

Les commandes de ce module utilisent la syntaxe Bash. Ne les collez pas inchangés dans PowerShell ou l’invite de commandes Windows. Sur Windows, exécutez les commandes Bash dans la distribution WSL configurée. Utilisez un autre terminal Bash local pour les requêtes HTTP pendant l’exécution de l’application.

Avant de générer le projet, vérifiez que votre environnement Bash utilise Java 17 et Maven 3.9.9 :

java -version
mvn --version

La commande initiale du générateur utilise votre installation de Maven. Le wrapper Maven du projet est créé par la suite. Il ne remplace donc pas ce prérequis. Sélectionnez les versions requises Java et Maven avant de continuer.

À partir du répertoire dans lequel vous souhaitez créer le projet, générez l’application to-do :

mvn -U io.quarkus:quarkus-maven-plugin:3.19.0:create \
    -DplatformVersion=3.18.4 \
    -DprojectGroupId=com.example.demo \
    -DprojectArtifactId=todo \
    -DclassName="com.example.demo.TodoResource" \
    -Dpath="/api/todos" \
    -DjavaVersion=17 \
    -Dextensions="resteasy-jackson, hibernate-orm-panache, jdbc-postgresql, io.quarkus:quarkus-container-image-docker"

Cette commande crée un nouveau projet Quarkus dans un répertoire nommé todo. Il génère une structure de répertoires Maven (src/main/java pour le code source et src/test/java pour les tests). Il crée des classes Java, des tests et des fichiers Dockerfiles. Il génère également un fichier pom.xml avec toutes les dépendances nécessaires (Hibernate, RESTEasy, Jackson, PostgreSQL et Docker) :

  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-orm-panache</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-container-image-docker</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-orm</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

Remarque

Toutes les dépendances dans le fichier pom.xml sont définies dans le boM de Quarkus (facture de matériaux) io.quarkus.platform:quarkus-bom.

Coder l’application

Ensuite, renommez la classe MyEntity.java générée en Todo.java (située dans le même dossier que le fichier TodoResource.java). Remplacez le code existant par le code Java suivant. Il utilise l’API de persistance Java (jakarta.persistence.* package) pour stocker et récupérer des données à partir de votre serveur PostgreSQL. Il utilise également Hibernate ORM avec Panache (hérite de io.quarkus.hibernate.orm.panache.PanacheEntity) pour simplifier la couche de persistance.

Vous utilisez une entité JPA (@Entity) pour mapper l’objet java Todo directement à la table Todo PostgreSQL. Le point de terminaison REST TodoResource crée ensuite une classe d’entité Todo et la conserve. Cette classe est un modèle de domaine qui est mappé à la table Todo. La table est créée automatiquement par JPA.

L’extension de PanacheEntity vous offre un certain nombre de méthodes génériques de création, lecture, mise à jour et suppression (CRUD) pour votre type. Vous pouvez donc effectuer des opérations telles que l’enregistrement et la suppression d’objets Todo dans une seule ligne de code Java.

Ajoutez le code suivant à l’entité Todo :

package com.example.demo;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

import jakarta.persistence.Entity;
import java.time.Instant;

@Entity
public class Todo extends PanacheEntity {

    public String description;

    public String details;

    public boolean done;

    public Instant createdAt = Instant.now();

    @Override
    public String toString() {
        return "Todo{" +
                "id=" + id + '\'' +
                ", description='" + description + '\'' +
                ", details='" + details + '\'' +
                ", done=" + done +
                ", createdAt=" + createdAt +
                '}';
    }
}

Pour gérer cette classe, mettez à jour le TodoResource afin qu’il puisse publier des interfaces REST pour stocker et récupérer des données à l’aide de HTTP. Ouvrez la classe TodoResource et remplacez le code par les éléments suivants :

package com.example.demo;

import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo;
import org.jboss.logging.Logger;

import java.util.List;

@Path("/api/todos")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public class TodoResource {

    @Inject
    Logger logger;

    @Inject
    UriInfo uriInfo;

    @POST
    @Transactional
    public Response createTodo(Todo todo) {
        logger.info("Creating todo: " + todo);
        Todo.persist(todo);
        UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder().path(todo.id.toString());
        return Response.created(uriBuilder.build()).entity(todo).build();
    }

    @GET
    public List<Todo> getTodos() {
        logger.info("Getting all todos");
        return Todo.listAll();
    }

    @GET
    @Path("/{id}")
    public Todo getTodo(@PathParam("id") Long id) {
        Todo todo = id == null ? null : Todo.findById(id);
        if (todo == null) {
            throw new NotFoundException();
        }
        return todo;
    }
}

La méthode POST retourne l’URI du nouvel élément créé dans l’en-tête Location. La méthode GET de l’élément sert cet URI et retourne HTTP 404 si l’élément est introuvable. La protection Null gère également les valeurs de chemin d’accès que RESTEasy convertit en identificateur Null, sans passer la valeur Null à Panache.

Exécuter l’application

Lorsque vous exécutez l’application en mode développement, Docker doit être en cours d’exécution. Cela est dû au fait que Dockerus détecte que vous avez besoin d’une base de données PostgreSQL (en raison de la dépendance PostgreSQL quarkus-jdbc-postgresql déclarée dans le fichier pom.xml), télécharge l’image Docker PostgreSQL et démarre un conteneur avec la base de données. Il crée ensuite automatiquement la table Todo dans la base de données.

Remarque

La plateforme Quarkus 3.18.4 épinglée utilise Testcontainers 1.20.4, dont le client Docker Java utilise par défaut l’API 1.32. Docker Engine 29.x n’accepte plus cette version de l’API par défaut. Si vous utilisez Engine 29.x, exécutez docker version et vérifiez que la plage d’API prise en charge du serveur inclut la version 1.44. Uniquement si c’est le cas, exécutez la commande suivante dans cet interpréteur de commandes Bash avant de démarrer Quarkus ou de tester :

export JDK_JAVA_OPTIONS="${JDK_JAVA_OPTIONS:+$JDK_JAVA_OPTIONS }-Dapi.version=1.44"

Conservez ce paramètre dans le même interpréteur de commandes pour les commandes de test et de création de package ultérieures. JDK_JAVA_OPTIONS fournit la propriété système Java aux processus du lanceur Java, sans modifier les versions de Quarkus fixées. Le paramètre DOCKER_API_VERSION seul ne configure pas cette version du client Docker Java. Ne forcez pas l’API 1.44 sur un serveur dont la plage prise en charge l’exclut ; utilisez plutôt une configuration Docker et client compatible, validée séparément.

Vérifiez que Docker est en cours d’exécution et accessible à partir de votre environnement Bash configuré. Dans le terminal où vous avez généré le projet, entrez dans le répertoire todo et démarrez l’application :

cd todo
./mvnw quarkus:dev

Exécutez toutes les commandes Maven suivantes à partir de ce répertoire généré todo .

L’application Quarkus doit démarrer et se connecter à votre base de données. La sortie suivante doit s’afficher :

2025-02-28 08:38:33,418 INFO  [io.qua.dat.dep.dev.DevServicesDatasourceProcessor] (build-28) Dev Services for default datasource (postgresql) started - container ID is ce37977203b0
2025-02-28 08:38:33,421 INFO  [io.qua.hib.orm.dep.dev.HibernateOrmDevServicesProcessor] (build-6) Setting quarkus.hibernate-orm.database.generation=drop-and-create to initialize Dev Services managed database
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2025-02-28 08:38:35,278 INFO  [io.quarkus] (Quarkus Main Thread) todo 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.18.4) started in 5.367s. Listening on: http://localhost:8080

2025-02-28 08:38:35,280 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2025-02-28 08:38:35,280 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, narayana-jta, resteasy, resteasy-jackson, smallrye-context-propagation, vertx]

--
Tests paused
Press [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>

Pour tester l’application, vous pouvez utiliser cURL.

Dans un deuxième terminal Bash local, créez un élément to-do dans la base de données avec la commande suivante. Vous devez voir le log dans la console Quarkus.

curl --header "Content-Type: application/json" \
    --request POST \
    --data '{"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done": "true"}' \
    http://127.0.0.1:8080/api/todos

Cette commande doit retourner l’élément créé (avec un identificateur) :

{"id":1,"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done":true,"createdAt":"2025-02-26T07:27:30.093447Z"}

Créez une deuxième to-do à l’aide de la commande cURL suivante :

curl --header "Content-Type: application/json" \
    --request POST \
    --data '{"description":"Take Azure Container Apps MS Learn","details":"Take the ACA Learn module","done": "false"}' \
    http://127.0.0.1:8080/api/todos

Ensuite, récupérez les données à l’aide d’une nouvelle requête cURL :

curl http://127.0.0.1:8080/api/todos

Cette commande retourne la liste des éléments to-do, y compris les éléments que vous avez créés. Chaque élément inclut createdAtégalement ; l’exemple suivant omet ces horodatages pour la concision :

[ 
  {"id":1,"description":"Take Quarkus MS Learn","details":"Take the MS Learn on deploying Quarkus to Azure Container Apps","done":true},
  {"id":2,"description":"Take Azure Container Apps MS Learn","details":"Take the ACA Learn module","done":false}
]

Tester l’application

Une fois les vérifications HTTP effectuées, revenez au terminal exécutant Quarkus, puis appuyez sur Ctrl+C pour arrêter le mode de développement. Laissez cet interpréteur de commandes Bash ouvert et restez dans le répertoire todo pour la commande de test.

Pour tester l’application, vous pouvez utiliser la classe TodoResourceTest existante. Il doit tester le point de terminaison REST. Pour tester le point de terminaison, il utilise RESTAssured. Les tests conservent la vérification de collection, suivent le Location retourné par POST et vérifient les identificateurs d’élément manquants et non valides. Remplacez le code dans la classe TodoResourceTest par le code suivant :

package com.example.demo;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;
import static io.restassured.RestAssured.given;
import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;

@QuarkusTest
class TodoResourceTest {

    @Test
    void shouldGetAllTodos() {
        given()
                .when().get("/api/todos")
                .then()
                .statusCode(200);
    }

    @Test
    void shouldCreateATodo() {
        Todo todo = new Todo();
        todo.description = "Take Quarkus MS Learn";
        todo.details = "Take the MS Learn on deploying Quarkus to Azure Container Apps";
        todo.done = true;

        Response created = given().body(todo)
                .header(CONTENT_TYPE, APPLICATION_JSON)
                .when().post("/api/todos")
                .then()
                .statusCode(201)
                .extract().response();

        String location = created.getHeader("Location");
        assertNotNull(location);
        long createdId = created.jsonPath().getLong("id");

        Response retrieved = given()
                .when().get(location)
                .then()
                .statusCode(200)
                .extract().response();

        assertEquals(createdId, retrieved.jsonPath().getLong("id"));
        assertEquals(todo.description, retrieved.jsonPath().getString("description"));
    }

    @Test
    void shouldReturnNotFoundForMissingTodo() {
        given()
                .when().get("/api/todos/-1")
                .then().statusCode(404);
    }

    @Test
    void shouldRejectInvalidTodoIds() {
        given()
                .when().get("/api/todos/not-a-number")
                .then().statusCode(404);

        given().urlEncodingEnabled(false)
                .when().get("/api/todos/%20")
                .then().statusCode(404);
    }
}

Le test de création lit l’identificateur généré à partir de la réponse POST et compare les deux identificateurs comme valeurs long. Le test d’identificateur non valide désactive l’encodage automatique d’URL pour %20 afin que la requête contienne l’espace délibérément encodé.

Lorsque vous testez l’application, Docker doit être en cours d’exécution et accessible à partir du même environnement Bash, car Quarkus démarre un service de développement PostgreSQL distinct à des fins de test. Dans le terminal où vous avez arrêté Quarkus, testez l’application à l’aide de cette commande :

./mvnw clean test

Vous devriez voir un résultat qui devrait ressembler à ceci :

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.TodoResourceTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Gardez cet interpréteur de commandes Bash ouvert pour l’exercice de base de données. Vous allez définir AZ_POSTGRES_JDBC_URL, AZ_POSTGRES_USERNAMEet AZ_POSTGRES_PASSWORD là, puis redémarrer Quarkus à partir de cet interpréteur de commandes. Arrêtez le processus de développement à distance avec Ctrl+C avant les étapes de développement ultérieures ./mvnw clean package et de déploiement. Conservez les variables et ne répétez pas la configuration du nom de ressource et du mot de passe entre les unités.

Un processus hérite de son environnement au démarrage. L’exportation de variables dans un autre terminal ne met pas à jour un processus Quarkus déjà en cours d’exécution. Modifier application.properties et s'appuyer sur le rechargement en direct ne suffisent pas pour passer à Azure ; redémarrez l'application depuis le terminal où vous définissez les variables.