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 ambiente de desenvolvimento integrado (IDE) de sua escolha para editar o código. Utilize um terminal à sua escolha 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 Quarkus. Você pode usar a interface web do Quarkus, um plug-in IDE ou o plug-in do Quarkus Maven. Vamos usar o plugin Maven para gerar a estrutura do projeto.

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

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

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

Em um prompt de comando, gere o aplicativo de tarefas:

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"

Este comando cria um novo projeto Quarkus. Ele gera uma estrutura de diretórios 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 (Hibernate, 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>

Nota

Todas as dependências no arquivo pom.xml são definidas na lista técnica do Quarkus (lista de materiais). io.quarkus.platform:quarkus-bom

Codificar a aplicação

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 Java Persistence API (jakarta.persistence.* pacote) para armazenar e recuperar dados do seu servidor PostgreSQL. Ele também usa Hibernate ORM com Panache (herdando de ) para simplificar a camada de io.quarkus.hibernate.orm.panache.PanacheEntitypersistência.

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

A extensão PanacheEntity fornece uma série de métodos genéricos de criação, leitura, atualização e exclusão (CRUD) para seu tipo. Assim, você pode fazer coisas como salvar e excluir Todo objetos em apenas uma linha de código Java.

Adicione o seguinte código à Todo entidade:

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 TodoResource classe 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 a aplicação

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

Verifique se o Docker está sendo executado localmente em sua máquina e execute o aplicativo de tarefas 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 seu banco de dados. Deverá ver o seguinte resultado:

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

Pode utilizar o cURL para testar a aplicação.

Em um terminal separado, crie um novo item de tarefa no banco de dados com o seguinte comando. 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

Este 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 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 pendentes, 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 a aplicação

Para testar o aplicativo, você pode usar a classe existente TodoResourceTest . Ele precisa testar o ponto de extremidade REST. Para testar o ponto de extremidade, ele usa RESTAssured. Substitua o código na TodoResourceTest classe com o 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 deteta 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

Deverá ver uma saída semelhante à seguinte:

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