Exercício – Criar um aplicativo do Quarkus

Concluído

Nesta unidade, você criará um aplicativo quarkus básico. Você usa o Maven para inicializar o aplicativo e um IDE (ambiente de desenvolvimento integrado) de sua escolha para editar o código. Use um terminal de sua preferência para executar o código. Você usa o Docker para iniciar um banco de dados PostgreSQL local para que possa executar e testar o aplicativo localmente.

Gerar o aplicativo Quarkus usando o Maven

Há várias maneiras de gerar uma estrutura de projeto do Quarkus. Você pode usar a Interface da Web do Quarkus, um plug-in IDE ou o plug-in Quarkus Maven. Vamos usar o plug-in do Maven para gerar a estrutura do projeto.

Você gera seu aplicativo com várias dependências:

  • A dependência resteasy para expor um ponto de extremidade REST
  • A dependência jackson para serializar e desserializar o JSON
  • A dependência hibernate para interagir com o banco de dados
  • A dependência postgresql para se conectar ao banco de dados PostgreSQL
  • A dependência docker para criar uma imagem do Docker

Você não precisa especificar dependências do Azure porque executa seu aplicativo localmente primeiro e, em seguida, implanta uma versão em contêineres dele nos Aplicativos de Contêiner do Azure.

Em um prompt de comando, gere o aplicativo da lista de pendências:

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, docker"

Esse comando cria um novo projeto do Quarkus. Ele gera uma estrutura de diretório maven (src/main/java para código-fonte e src/test/java para testes). Ele cria algumas classes Java, alguns testes e alguns Dockerfiles. Ele também gera um arquivo pom.xml com todas as dependências necessárias (Hibernar, RESTEasy, Jackson, PostgreSQL e 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>

Observação

Todas as dependências no arquivo pom.xml são definidas no quarkus BOM (fatura de materiais) io.quarkus.platform:quarkus-bom.

Codificar o aplicativo

Em seguida, renomeie a classe MyEntity.java gerada para Todo.java (localizada na mesma pasta que o arquivo TodoResource.java). Substitua o código existente pelo código Java a seguir. Ele usa a API de Persistência do Java (jakarta.persistence.* pacote) para armazenar e recuperar dados do seu servidor PostgreSQL. Ele também usa Hibernate ORM com o Panache (herdando de io.quarkus.hibernate.orm.panache.PanacheEntity) para simplificar a camada de persistência.

Você usa uma entidade JPA (@Entity) para mapear o objeto Todo Java diretamente para a tabela Todo PostgreSQL. O ponto de extremidade REST TodoResource cria uma nova classe de entidade Todo e a persiste. Essa classe é um modelo de domínio mapeado na tabela Todo. A tabela é criada automaticamente pelo JPA.

Ao estender PanacheEntity, você obtém vários métodos Criar, Ler, Atualizar e Excluir (CRUD) genéricos para seu tipo. Portanto, você pode fazer coisas como salvar e excluir objetos Todo em apenas uma linha de código Java.

Adicione o seguinte código à entidade 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 +
                '}';
    }
}

Para gerenciar essa classe, atualize a TodoResource para que ela possa publicar interfaces REST para armazenar e recuperar dados usando HTTP. Abra a classe TodoResource e substitua o código pelo seguinte:

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.POST;
import jakarta.ws.rs.Path;
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();
    }
}

Executar o aplicativo

Quando você executa o aplicativo no modo de desenvolvimento, o Docker precisa estar em execução. Isso ocorre porque o Quarkus detecta que você precisa de um banco de dados PostgreSQL (devido à dependência postgreSQL quarkus-jdbc-postgresql declarada no arquivo pom.xml), baixa a imagem do Docker do PostgreSQL e inicia um contêiner com o banco de dados. Em seguida, ele cria automaticamente a tabela Todo no banco de dados.

Verifique se o Docker está sendo executado localmente em seu computador e execute o aplicativo to-do usando este comando:

cd todo
./mvnw quarkus:dev    # On Mac or Linux
mvnw.cmd quarkus:dev  # On Windows

O aplicativo Quarkus deve iniciar e se conectar ao banco de dados. A seguinte saída deve ser exibida:

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>

Para testar o aplicativo, você pode usar cURL.

Em um terminal separado, crie um novo item da lista de tarefas no banco de dados com o comando a seguir. Você deve ver o log no console do 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

Esse comando deve retornar o item criado (com um identificador):

{"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"}

Crie uma segunda tarefa usando o seguinte comando cURL:

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

Em seguida, recupere os dados usando uma nova solicitação cURL:

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

Este comando retorna a lista de itens de to-do, incluindo os itens que você criou:

[ 
  {"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}
]

Testar o aplicativo

Para testar o aplicativo, você pode usar a classe TodoResourceTest existente. Precisa testar o endpoint REST. Para testar o endpoint, ele usa RESTAssured. Substitua o código na classe TodoResourceTest pelo seguinte código:

package com.example.demo;

import io.quarkus.test.junit.QuarkusTest;
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 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;

        given().body(todo)
                .header(CONTENT_TYPE, APPLICATION_JSON)
                .when().post("/api/todos")
                .then()
                .statusCode(201);
    }
}

Quando você testa o aplicativo, o Docker Desktop precisa estar em execução porque o Quarkus detecta que ele precisa do banco de dados PostgreSQL para teste. Teste o aplicativo usando este comando:

./mvnw clean test    # On Mac or Linux
mvnw.cmd clean test  # On Windows

Você deverá ver uma saída semelhante a esta:

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