Exercício – Criar um aplicativo Quarkus

Concluído

Nesta unidade, você cria 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 poder executar e testar o aplicativo de lista de pendências 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 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 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 depois implanta uma versão conteinerizada dele nos Aplicativos de Contêiner do Azure.

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

mvn -U io.quarkus:quarkus-maven-plugin:3.7.3:create \
    -DplatformVersion=3.7.3 \
    -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 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 io.quarkus.platform:quarkus-bomquarkus BOM (cobrança de materiais).

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 seguinte código Java. Ele usa a API de Persistência de Java (pacote jakarta.persistence.*) para armazenar e recuperar dados do servidor PostgreSQL. Ele também usa Hibernar ORM com 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 Java Todo diretamente para a tabela do PostgreSQL Todo. O ponto de extremidade REST TodoResource cria uma nova classe de entidade Todo e a mantém. Essa classe é um modelo de domínio mapeado na tabela Todo. A tabela é criada automaticamente pela JPA.

Ao estender PanacheEntity, você obtém vários métodos CRUD (Create, Read, Update e Delete) genéricos para o 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 na 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 o TodoResource para que ele 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 de lista de pendências usando este comando:

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

O aplicativo Quarkus deverá ser iniciado e conectado ao seu banco de dados. A seguinte saída deve ser exibida:

[io.qua.dat.dep.dev.DevServicesDatasourceProcessor] Dev Services for the default datasource (postgresql) started.
[io.qua.hib.orm.dep.HibernateOrmProcessor] Setting quarkus.hibernate-orm.database.generation=drop-and-create to initialize Dev Services managed database
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
[org.hib.eng.jdb.spi.SqlExceptionHelper] (JPA Startup Thread) SQL Warning Code: 0, SQLState: 00000

[org.hib.eng.jdb.spi.SqlExceptionHelper] (JPA Startup Thread) table "todo" does not exist, skipping
[org.hib.eng.jdb.spi.SqlExceptionHelper] (JPA Startup Thread) SQL Warning Code: 0, SQLState: 00000
[org.hib.eng.jdb.spi.SqlExceptionHelper] (JPA Startup Thread) sequence "hibernate_sequence" does not exist, skipping
[io.quarkus] (Quarkus Main Thread) todo 1.0.0-SNAPSHOT on JVM (powered by Quarkus) started in 4.381s. Listening on: http://localhost:8080
[io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
[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 [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 de lista de pendências no banco de dados com o comando a seguir. Você deverá 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":"2022-12-30T15:17:20.280203Z"}

Crie uma segunda lista de pendências usando 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

Esse comando retorna a lista de itens da lista de pendências, 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. Ele precisa testar o ponto de extremidade REST. Para testar o ponto de extremidade, ele usa RESTAssured. Substitua o código na classe TodoResourceTest pelo código a seguir:

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] ------------------------------------------------------------------------