Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resteasy reactive in legumes #18

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<quarkus-artemis-bom.version>3.0.0</quarkus-artemis-bom.version>
<quarkus-pooled-jms.version>2.2.0</quarkus-pooled-jms.version>
<lombok.version>1.18.26</lombok.version>
<rest-assured.version>5.3.0</rest-assured.version>
<rest-assured.version>5.3.2</rest-assured.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.0.0</surefire-plugin.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,35 @@
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<!-- Hibernate Reactive dependency -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>

<!-- Reactive SQL client for PostgreSQL -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.quarkus</groupId>-->
<!-- <artifactId>quarkus-jdbc-postgresql</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
package com.brunobat.rest;

import com.brunobat.rest.data.LegumeItem;
import com.brunobat.rest.model.Legume;
import io.quarkus.hibernate.orm.panache.PanacheRepository;

import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;

import java.util.List;
import java.util.stream.Stream;

@ApplicationScoped
public class LegumeRepository implements PanacheRepository<Legume> {

@Inject
EntityManager manager;

public Stream<Legume> listLegumes(int pageIndex) {
return find("SELECT h FROM Legume h").page(pageIndex, 16).stream();
@WithSession
public Uni<List<Legume>> listLegumes(int pageIndex) {
return find("SELECT h FROM Legume h").page(pageIndex, 16).list();
}

public void remove(final LegumeItem legume) {
manager.remove(legume);
public void removeLegume(final String legumeId) {
find("SELECT h FROM Legume h WHERE h.id=?1", legumeId).firstResult().onItem().transform(legume -> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be select?
Can't you just do "delete from x where x.id = ?!"?

Copy link
Owner Author

@brunobat brunobat Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, yeah.🤦🏻‍♂️

delete(legume);
return legume;
});
}

public Legume merge(final Legume legumeToAdd) {
return manager.merge(legumeToAdd);
public Uni<Legume> createLegume(final Legume legumeToAdd) {
return persist(legumeToAdd);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import com.brunobat.rest.data.LegumeItem;
import com.brunobat.rest.data.LegumeNew;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.PathParam;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
Expand Down Expand Up @@ -51,7 +52,7 @@ public interface LegumeApi {
responseCode = "500",
description = "Internal Server Error"
)
Response provision();
Uni<Response> provision();

@POST
@Operation(
Expand Down Expand Up @@ -87,7 +88,7 @@ public interface LegumeApi {
responseCode = "500",
description = "Internal Server Error"
)
public Response add(@Valid final LegumeNew legume);
public Uni<Response> add(@Valid final LegumeNew legume);

@DELETE
@Path("{id}")
Expand All @@ -109,7 +110,7 @@ public interface LegumeApi {
responseCode = "500",
description = "Internal Server Error"
)
Response delete(
Uni<Response> delete(
@Parameter(name = "id",
description = "Id of the Legume to delete",
required = true,
Expand Down Expand Up @@ -137,7 +138,7 @@ Response delete(
description = "Internal Server Error"
)
@GET
List<LegumeItem> list(
Uni<List<LegumeItem>> list(
@Parameter(name = "pageIndex", required = false)
@QueryParam("pageIndex")
final int pageIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
import com.brunobat.rest.data.LegumeNew;
import com.brunobat.rest.message.MessageSender;
import com.brunobat.rest.model.Legume;
import lombok.extern.slf4j.Slf4j;

import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.ws.rs.core.Response;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static jakarta.ws.rs.core.Response.Status.CREATED;
import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
import static jakarta.ws.rs.core.Response.Status.NO_CONTENT;
Expand All @@ -33,68 +33,84 @@ public class LegumeResource implements LegumeApi {
@Inject
MessageSender messageSender;

@Transactional
public Response provision() {
@WithTransaction
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Panache's @tx instead of plain Jakarta @tx ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reactive code requires a different annotation. I got a warning message.

public Uni<Response> provision() {
final LegumeNew carrot = LegumeNew.builder()
.name("Carrot")
.description("Root vegetable, usually orange")
.build();
.name("Carrot")
.description("Root vegetable, usually orange")
.build();
final LegumeNew zucchini = LegumeNew.builder()
.name("Zucchini")
.description("Summer squash")
.build();
return Response.status(CREATED).entity(asList(
addLegume(carrot),
addLegume(zucchini))).build();
.name("Zucchini")
.description("Summer squash")
.build();

return Uni.combine().all().unis(
addLegume(carrot),
addLegume(zucchini))
.asTuple()
.onItem().transform(tuple -> {
List<LegumeItem> resultList = new ArrayList<>();
resultList.add(tuple.getItem1());
resultList.add(tuple.getItem2());
return Response.status(CREATED).entity(resultList).build();
});
}

@Transactional
public Response add(@Valid final LegumeNew legumeNew) {
return Response.status(CREATED).entity(addLegume(legumeNew)).build();
@WithTransaction
public Uni<Response> add(@Valid final LegumeNew legumeNew) {
return addLegume(legumeNew)
.onItem().transform(legumeItem -> Response
.status(CREATED)
.entity(legumeItem)
.build());
}

@Transactional
public Response delete(@NotEmpty final String legumeId) {
@WithTransaction
public Uni<Response> delete(@NotEmpty final String legumeId) {
return find(legumeId)
.map(legume -> {
repository.remove(legume);
return Response.status(NO_CONTENT).build();
})
.orElse(Response.status(NOT_FOUND).build());
.onItem().transform(legumeOpt -> legumeOpt
.map(legume -> {
repository.removeLegume(legume.getId());
return Response.status(NO_CONTENT).build();
})
.orElse(Response.status(NOT_FOUND).build()));
}

public List<LegumeItem> list(int pageIndex) {
public Uni<List<LegumeItem>> list(int pageIndex) {
// log.info("someone asked for a list for index: " + pageIndex);
return repository.listLegumes(pageIndex)
.map(this::getLegumeItem)
.collect(Collectors.toList());
.onItem().transform(legumes -> legumes.stream()
.map(this::getLegumeItem)
.collect(Collectors.toList()));
}

private Optional<LegumeItem> find(final String legumeId) {
return repository.find("id", legumeId).stream()
.map(this::getLegumeItem)
.findFirst();
private Uni<Optional<LegumeItem>> find(final String legumeId) {
return repository.find("id", legumeId).list()
.onItem().transform(legumes -> legumes.stream()
.map(this::getLegumeItem)
.findFirst());
}

private LegumeItem addLegume(final @Valid LegumeNew legumeNew) {
private Uni<LegumeItem> addLegume(final LegumeNew legumeNew) {
final Legume legumeToAdd = Legume.builder()
.name(legumeNew.getName())
.description((legumeNew.getDescription()))
.build();

final Legume addedLegume = repository.merge(legumeToAdd);
final LegumeItem legumeItem = getLegumeItem(addedLegume);
.name(legumeNew.getName())
.description((legumeNew.getDescription()))
.build();

messageSender.send(legumeItem.getName());
final Uni<Legume> addedLegume = repository.createLegume(legumeToAdd);

return legumeItem;
return addedLegume.onItem().transform(legume -> {
LegumeItem legumeItem = getLegumeItem(legume);
messageSender.send(legumeItem.getName());
return legumeItem;
});
}

private LegumeItem getLegumeItem(final Legume addedLegume) {
return LegumeItem.builder()
.id(addedLegume.getId())
.name(addedLegume.getName())
.description(addedLegume.getDescription())
.build();
.id(addedLegume.getId())
.name(addedLegume.getName())
.description(addedLegume.getDescription())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# DB
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/test?current_schema=public
quarkus.datasource.reactive.url=postgresql://localhost:5432/test?current_schema=public
quarkus.datasource.username: pguser
quarkus.datasource.password: changeit
quarkus.datasource.jdbc.max-size=50
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ public class LegumeResourceTest {
public void testProvisionAndList() {
given()
.header("Content-Type", "application/json; encoding=utf8; charset=utf8")
.header("Accept", "application/json; encoding=utf8; charset=utf8")
.when().post("/legumes/init")
.then()
.statusCode(201);

given()
.header("Content-Type", "application/json; encoding=utf8; charset=utf8")
.header("Accept", "application/json; encoding=utf8; charset=utf8")
.when().get("/legumes")
.then()
.log().all()
.statusCode(200)
.body("$.size()", is(2),
"name", containsInAnyOrder("Carrot", "Zucchini"),
"description", containsInAnyOrder("Root vegetable, usually orange", "Summer squash"));


}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
## configure your datasource
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:test;MODE=PostgreSQL;DB_CLOSE_DELAY=-1
quarkus.datasource.username: sa
quarkus.datasource.password:
quarkus.datasource.jdbc.max-size: 20
#quarkus.datasource.db-kind=h2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented out on purpose?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no reactive driver support for H2

#quarkus.datasource.reactive.url=h2:mem:test;MODE=PostgreSQL;DB_CLOSE_DELAY=-1
#quarkus.datasource.username: sa
#quarkus.datasource.password:
#quarkus.datasource.jdbc.max-size: 20
# drop and create the database at startup (use `update` to only update the schema)
#quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect
quarkus.hibernate-orm.database.generation=drop-and-create
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{pa
#quarkus.otel.bsp.max.export.batch.size=64
#quarkus.otel.bsp.schedule.delay=500ms
otel.metrics.exporter=none
quarkus.otel.traces.sampler=traceidratio
quarkus.otel.traces.sampler.arg=0.1

# Configures the Artemis properties.
quarkus.artemis.url=tcp://localhost:61616
Expand Down