diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 7cf2c464062dc..f6a01ef63fe40 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -113,7 +113,7 @@ 2.5.0.Final 2.1.4.SP1 3.6.1.Final - 4.5.8 + 4.5.9 4.5.14 4.4.16 4.1.5 @@ -143,7 +143,7 @@ 3.7.1 1.8.0 1.1.10.5 - 0.106.0 + 0.107.0 2.13.14 1.2.3 @@ -160,7 +160,7 @@ 4.2.1 3.0.6.Final 10.15.2 - 3.0.3 + 3.0.4 4.27.0 4.24.0 @@ -5743,6 +5743,11 @@ + + io.smallrye.reactive + mutiny-zero + ${mutiny-zero.version} + io.smallrye.reactive mutiny-zero-flow-adapters diff --git a/bom/dev-ui/pom.xml b/bom/dev-ui/pom.xml index 4129bc25552c7..343d595627c0b 100644 --- a/bom/dev-ui/pom.xml +++ b/bom/dev-ui/pom.xml @@ -13,7 +13,7 @@ Dependency management for dev-ui. Importable by third party extension developers. - 24.4.2 + 24.4.5 3.1.4 4.0.6 3.1.4 diff --git a/bom/test/pom.xml b/bom/test/pom.xml index c44c362e21ddb..a5ca7acc8bfa8 100644 --- a/bom/test/pom.xml +++ b/bom/test/pom.xml @@ -20,7 +20,7 @@ 2.3.1 1.3.8 - 0.106.0 + 0.107.0 1.0.0-alpha diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java index 948feb9f3db81..94598c02ce826 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java @@ -177,7 +177,7 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew args.add("-DquarkusUpdateRecipes=" + rewrite.quarkusUpdateRecipes); } if (rewrite.additionalUpdateRecipes != null) { - args.add("-DadditionalUpdateRecipes" + rewrite.additionalUpdateRecipes); + args.add("-DadditionalUpdateRecipes=" + rewrite.additionalUpdateRecipes); } if (rewrite.dryRun) { args.add("-DrewriteDryRun"); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/ImageOptions.java b/devtools/cli/src/main/java/io/quarkus/cli/image/ImageOptions.java index c5597640091ce..a2a7c8a372627 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/ImageOptions.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/ImageOptions.java @@ -9,7 +9,7 @@ public class ImageOptions { @CommandLine.Option(order = 3, names = { "--group" }, description = "The group part of the container image. Defaults to the ${user.name}.") - public Optional group = Optional.of(System.getProperty("user.name")); + public Optional group = Optional.empty(); @CommandLine.Option(order = 4, names = { "--name" }, description = "The name part of the container image. Defaults to the ${project.artifactId}.") diff --git a/devtools/gradle/gradle/libs.versions.toml b/devtools/gradle/gradle/libs.versions.toml index 4ac165ca0f938..f69fccbea1780 100644 --- a/devtools/gradle/gradle/libs.versions.toml +++ b/devtools/gradle/gradle/libs.versions.toml @@ -3,7 +3,7 @@ plugin-publish = "1.2.1" # updating Kotlin here makes QuarkusPluginTest > shouldNotFailOnProjectDependenciesWithoutMain(Path) fail kotlin = "2.0.0" -smallrye-config = "3.8.3" +smallrye-config = "3.9.1" junit5 = "5.10.3" assertj = "3.26.3" diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index f93422a0e1303..9eb8dd6cc228a 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -10,6 +10,7 @@ include::_attributes.adoc[] :numbered: :sectnums: :sectnumlevels: 4 +:toclevels: 3 :topics: templating,qute :extensions: io.quarkus:quarkus-qute,io.quarkus:quarkus-resteasy-qute,io.quarkus:quarkus-rest-qute diff --git a/docs/src/main/asciidoc/scripting.adoc b/docs/src/main/asciidoc/scripting.adoc index c2ea4bddbee9c..6f0fe640c3c14 100644 --- a/docs/src/main/asciidoc/scripting.adoc +++ b/docs/src/main/asciidoc/scripting.adoc @@ -112,7 +112,7 @@ The next lines [source,java] ---- -// //DEPS +//DEPS ---- illustrate how you add dependencies to this script. This is a feature of JBang. diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java index fbf7511f0bfae..9086c453f4e27 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java @@ -189,14 +189,13 @@ public DevServicesResultBuildItem startKeycloakContainer( StartupLogCompressor compressor = new StartupLogCompressor( (launchMode.isTest() ? "(test) " : "") + "Keycloak Dev Services Starting:", consoleInstalledBuildItem, loggingSetupBuildItem); - if (vertxInstance == null) { - vertxInstance = Vertx.vertx(); - } try { List errors = new ArrayList<>(); + boolean useSharedNetwork = DevServicesSharedNetworkBuildItem.isSharedNetworkRequired(devServicesConfig, + devServicesSharedNetworkBuildItem); RunningDevService newDevService = startContainer(dockerStatusBuildItem, keycloakBuildItemBuildProducer, - !devServicesSharedNetworkBuildItem.isEmpty(), + useSharedNetwork, devServicesConfig.timeout, errors); if (newDevService == null) { @@ -279,6 +278,12 @@ private Map prepareConfiguration( List realmNames = new LinkedList<>(); + // this needs to be only if we actually start the dev-service as it adds a shutdown hook + // whose TCCL is the Augmentation CL, which if not removed, causes a massive memory leaks + if (vertxInstance == null) { + vertxInstance = Vertx.vertx(); + } + WebClient client = OidcDevServicesUtils.createWebClient(vertxInstance); try { String adminToken = getAdminToken(client, clientAuthServerBaseUrl); diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/DataAccessImplementor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/DataAccessImplementor.java index b944fb334f02a..67235ee93dac4 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/DataAccessImplementor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/DataAccessImplementor.java @@ -91,10 +91,12 @@ ResultHandle findAll(BytecodeCreator creator, ResultHandle page, ResultHandle so * Available number of pages given a page instance. * * @param creator Bytecode creator that should be used for implementation. - * @param page Page instance. + * @param page Page instance that should be used in a query. + * @param query HQL query to list entities. + * @param queryParams Map of parameters to use by the HQL query. * @return int page count. */ - ResultHandle pageCount(BytecodeCreator creator, ResultHandle page); + ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams); /** * return the total number of entities. diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/EntityDataAccessImplementor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/EntityDataAccessImplementor.java index 3e17564a90f25..9edb9ba4ada8a 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/EntityDataAccessImplementor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/EntityDataAccessImplementor.java @@ -112,14 +112,15 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { } /** - * Implements Entity.findAll().page(page).pageCount() + * Implements Entity.find(query, params).page(page).pageCount() */ @Override - public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeStaticMethod(ofMethod(entityClassName, "findAll", PanacheQuery.class)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator.invokeStaticMethod(ofMethod(entityClassName, "find", PanacheQuery.class, + String.class, Map.class), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); } /** diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/RepositoryDataAccessImplementor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/RepositoryDataAccessImplementor.java index 2f560610164cb..bbe9c1944b2d3 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/RepositoryDataAccessImplementor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/RepositoryDataAccessImplementor.java @@ -119,14 +119,15 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { } /** - * Implements repository.findAll().page(page).pageCount() + * Implements repository.find(query, params).page(page).pageCount() */ @Override - public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "findAll", PanacheQuery.class), - getRepositoryInstance(creator)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator + .invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "find", PanacheQuery.class, + String.class, Map.class), getRepositoryInstance(creator), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); } /** diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/ResourceImplementor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/ResourceImplementor.java index 25b964f53591e..54173496437c6 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/ResourceImplementor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/ResourceImplementor.java @@ -119,9 +119,11 @@ private void implementListWithQuery(ClassCreator classCreator, DataAccessImpleme */ private void implementListPageCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) { MethodCreator methodCreator = classCreator.getMethodCreator(Constants.PAGE_COUNT_METHOD_PREFIX + "list", int.class, - Page.class); + Page.class, String.class, Map.class); ResultHandle page = methodCreator.getMethodParam(0); - methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page)); + ResultHandle query = methodCreator.getMethodParam(1); + ResultHandle queryParams = methodCreator.getMethodParam(2); + methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page, query, queryParams)); methodCreator.close(); } diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/AbstractGetMethodTest.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/AbstractGetMethodTest.java index e50a90dd79848..68b929dbf282a 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/AbstractGetMethodTest.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/AbstractGetMethodTest.java @@ -14,6 +14,8 @@ import jakarta.ws.rs.core.Link; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import io.restassured.http.Header; import io.restassured.response.Response; @@ -416,4 +418,69 @@ void shouldListEmptyTables() { .when().get("/empty-list-items") .then().statusCode(200); } + + @ParameterizedTest + @CsvSource({ + "page,0", + "size,1", + "name,first", + "collection.id,full" + }) + void shouldShowSpecificParameterInLinkHeaders(String queryParamName, String queryParamValue) { + Response response = given().accept("application/json") + .when() + .queryParam(queryParamName, queryParamValue) + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> link.getUri().getQuery() + .contains(String.format("%s=%s", queryParamName, queryParamValue))); + } + + @Test + void shouldShowAllPaginationAndCustomQueryParametersInLinkHeaders() { + Response response = given().accept("application/json") + .when() + .queryParam("page", "0") + .queryParam("size", "3") + .queryParam("name", "first") + .queryParam("namedQuery", "Item.containsInName") + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> link.getUri().getQuery() + .contains("page=0&size=3&namedQuery=Item.containsInName&name=first")); + } + + @Test + void shouldShowAllPaginationAndFilteringParametersInLinkHeaders() { + Response response = given().accept("application/json") + .when() + .queryParam("page", "0") + .queryParam("size", "1") + .queryParam("name", "first") + .queryParam("collection.id", "full") + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> { + var query = link.getUri().getQuery(); + return query.contains("page=0&size=1") && query.contains("name=first") && query.contains("collection.id=full"); + }); + } } diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/DataAccessImplementor.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/DataAccessImplementor.java index 9d3fec3a0b40a..eed594a96c95d 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/DataAccessImplementor.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/DataAccessImplementor.java @@ -91,10 +91,12 @@ ResultHandle findAll(BytecodeCreator creator, ResultHandle page, ResultHandle so * Available number of pages given a page instance. * * @param creator Bytecode creator that should be used for implementation. - * @param page Page instance. + * @param page Page instance that should be used in a query. + * @param query HQL query to list entities. + * @param queryParams Map of parameters to use by the HQL query. * @return int page count. */ - ResultHandle pageCount(BytecodeCreator creator, ResultHandle page); + ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams); /** * return the total number of entities. diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/EntityDataAccessImplementor.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/EntityDataAccessImplementor.java index ca0d84fbe5cff..74345c3664983 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/EntityDataAccessImplementor.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/EntityDataAccessImplementor.java @@ -113,14 +113,15 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { } /** - * Implements Entity.findAll().page(page).pageCount() + * Implements Entity.find(query, params).page(page).pageCount() */ @Override - public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeStaticMethod(ofMethod(entityClassName, "findAll", PanacheQuery.class)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator.invokeStaticMethod(ofMethod(entityClassName, "find", PanacheQuery.class, + String.class, Map.class), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), query); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), panacheQuery); } /** diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/RepositoryDataAccessImplementor.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/RepositoryDataAccessImplementor.java index 603ce94caec84..f1e9078f66948 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/RepositoryDataAccessImplementor.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/RepositoryDataAccessImplementor.java @@ -123,14 +123,15 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { } /** - * Implements repository.findAll().page(page).pageCount() + * Implements repository.find(query, params).page(page).pageCount() */ @Override - public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "findAll", PanacheQuery.class), - getRepositoryInstance(creator)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), query); + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator + .invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "find", PanacheQuery.class, + String.class, Map.class), getRepositoryInstance(creator), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), panacheQuery); } /** diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java index 17d2aab4f6759..6192eb12bcf73 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java @@ -130,9 +130,11 @@ private void implementCount(ClassCreator classCreator, DataAccessImplementor dat */ private void implementListPageCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) { MethodCreator methodCreator = classCreator.getMethodCreator(Constants.PAGE_COUNT_METHOD_PREFIX + "list", Uni.class, - Page.class); + Page.class, String.class, Map.class); ResultHandle page = methodCreator.getMethodParam(0); - methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page)); + ResultHandle query = methodCreator.getMethodParam(1); + ResultHandle queryParams = methodCreator.getMethodParam(2); + methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page, query, queryParams)); methodCreator.close(); } diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractGetMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractGetMethodTest.java index 77671de648648..80ae2cb4f61a2 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractGetMethodTest.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractGetMethodTest.java @@ -14,6 +14,8 @@ import jakarta.ws.rs.core.Link; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import io.restassured.http.Header; import io.restassured.response.Response; @@ -396,4 +398,69 @@ void shouldListEmptyTables() { .when().get("/empty-list-items") .then().statusCode(200); } + + @ParameterizedTest + @CsvSource({ + "page,0", + "size,1", + "name,first", + "collection.id,full" + }) + void shouldShowSpecificParameterInLinkHeaders(String queryParamName, String queryParamValue) { + Response response = given().accept("application/json") + .when() + .queryParam(queryParamName, queryParamValue) + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> link.getUri().getQuery() + .contains(String.format("%s=%s", queryParamName, queryParamValue))); + } + + @Test + void shouldShowAllPaginationAndCustomQueryParametersInLinkHeaders() { + Response response = given().accept("application/json") + .when() + .queryParam("page", "0") + .queryParam("size", "3") + .queryParam("name", "first") + .queryParam("namedQuery", "Item.containsInName") + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> link.getUri().getQuery() + .contains("page=0&size=3&namedQuery=Item.containsInName&name=first")); + } + + @Test + void shouldShowAllPaginationAndFilteringParametersInLinkHeaders() { + Response response = given().accept("application/json") + .when() + .queryParam("page", "0") + .queryParam("size", "1") + .queryParam("name", "first") + .queryParam("collection.id", "full") + .get("/items") + .thenReturn(); + + assertThat(response.getStatusCode()).isEqualTo(200); + List links = response.getHeaders().getList("Link") + .stream() + .map(header -> Link.valueOf(header.getValue())) + .toList(); + assertThat(links).allMatch(link -> { + var query = link.getUri().getQuery(); + return query.contains("page=0&size=1") && query.contains("name=first") && query.contains("collection.id=full"); + }); + } } diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/DataAccessImplementor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/DataAccessImplementor.java index a383691e2536a..53964149243b2 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/DataAccessImplementor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/DataAccessImplementor.java @@ -91,7 +91,18 @@ ResultHandle findAll(BytecodeCreator creator, ResultHandle page, ResultHandle so * Available number of pages given a page instance. * * @param creator Bytecode creator that should be used for implementation. - * @param page Page instance. + * @param page Page instance that should be used in a query. Might be null if pagination is disabled. + * @param query HQL query to list entities. + * @param queryParams Map of parameters to use by the HQL query. + * @return int page count. + */ + ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams); + + /** + * Available number of pages given a page instance. + * + * @param creator Bytecode creator that should be used for implementation. + * @param page Page instance that should be used in a query. Might be null if pagination is disabled. * @return int page count. */ ResultHandle pageCount(BytecodeCreator creator, ResultHandle page); diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/EntityDataAccessImplementor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/EntityDataAccessImplementor.java index 6e730e2eebe4a..2cd0b9dc6b483 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/EntityDataAccessImplementor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/EntityDataAccessImplementor.java @@ -88,11 +88,20 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { return creator.invokeStaticMethod(ofMethod(entityClassName, "deleteById", boolean.class, Object.class), id); } + @Override + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator.invokeStaticMethod(ofMethod(entityClassName, "find", PanacheQuery.class, + String.class, Map.class), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, + page); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); + } + @Override public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeStaticMethod(ofMethod(entityClassName, "findAll", PanacheQuery.class)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, + ResultHandle panacheQuery = creator.invokeStaticMethod(ofMethod(entityClassName, "findAll", PanacheQuery.class)); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); } } diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/RepositoryDataAccessImplementor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/RepositoryDataAccessImplementor.java index be986926f2690..c0bfc3cccee45 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/RepositoryDataAccessImplementor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/RepositoryDataAccessImplementor.java @@ -98,14 +98,22 @@ public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { getRepositoryInstance(creator), id); } + @Override + public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page, ResultHandle query, ResultHandle queryParams) { + ResultHandle panacheQuery = creator + .invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "find", PanacheQuery.class, + String.class, Map.class), getRepositoryInstance(creator), query, queryParams); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); + } + @Override public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { - ResultHandle query = creator.invokeInterfaceMethod( - ofMethod(PanacheMongoRepositoryBase.class, "findAll", PanacheQuery.class), - getRepositoryInstance(creator)); - creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, - page); - return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); + ResultHandle panacheQuery = creator + .invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "findAll", PanacheQuery.class), + getRepositoryInstance(creator)); + creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), panacheQuery, page); + return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), panacheQuery); } private ResultHandle getRepositoryInstance(BytecodeCreator creator) { diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/ResourceImplementor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/ResourceImplementor.java index 8f7dd2f527fe6..f327bfb74aaca 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/ResourceImplementor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/ResourceImplementor.java @@ -101,9 +101,15 @@ private void implementListWithQuery(BytecodeCreator body, ResultHandle page, Res private void implementListPageCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) { MethodCreator methodCreator = classCreator.getMethodCreator(Constants.PAGE_COUNT_METHOD_PREFIX + "list", int.class, - Page.class); + Page.class, String.class, Map.class); ResultHandle page = methodCreator.getMethodParam(0); - methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page)); + ResultHandle query = methodCreator.getMethodParam(1); + ResultHandle queryParams = methodCreator.getMethodParam(2); + ResultHandle hasQuery = methodCreator.invokeVirtualMethod(ofMethod(String.class, "isEmpty", boolean.class), query); + BranchResult hasQueryBranch = methodCreator.ifTrue(hasQuery); + hasQueryBranch.trueBranch().returnValue(dataAccessImplementor.pageCount(hasQueryBranch.trueBranch(), page)); + hasQueryBranch.falseBranch().returnValue(dataAccessImplementor.pageCount(hasQueryBranch.falseBranch(), page, query, + queryParams)); methodCreator.close(); } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java index 99abbdabdc13e..09905f1b89426 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java @@ -9,7 +9,6 @@ import static io.quarkus.arc.processor.DotNames.LONG; import static io.quarkus.arc.processor.DotNames.SHORT; import static io.quarkus.arc.processor.DotNames.STRING; -import static io.quarkus.gizmo.MethodDescriptor.ofConstructor; import static io.quarkus.gizmo.MethodDescriptor.ofMethod; import static io.quarkus.gizmo.Type.classType; import static io.quarkus.gizmo.Type.intType; @@ -36,7 +35,6 @@ import io.quarkus.deployment.Capabilities; import io.quarkus.gizmo.AnnotatedElement; import io.quarkus.gizmo.AssignableResultHandle; -import io.quarkus.gizmo.BranchResult; import io.quarkus.gizmo.BytecodeCreator; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.FieldDescriptor; @@ -50,6 +48,7 @@ import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties; import io.quarkus.rest.data.panache.deployment.utils.PaginationImplementor; +import io.quarkus.rest.data.panache.deployment.utils.QueryImplementor; import io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator; import io.quarkus.rest.data.panache.deployment.utils.SortImplementor; import io.quarkus.rest.data.panache.deployment.utils.UniImplementor; @@ -67,6 +66,7 @@ public class ListMethodImplementor extends StandardMethodImplementor { private final PaginationImplementor paginationImplementor = new PaginationImplementor(); private final SortImplementor sortImplementor = new SortImplementor(); + private final QueryImplementor queryImplementor = new QueryImplementor(); public ListMethodImplementor(Capabilities capabilities) { super(capabilities); @@ -229,27 +229,22 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource if (isNotReactivePanache()) { TryBlock tryBlock = implementTryBlock(methodCreator, EXCEPTION_MESSAGE); - ResultHandle pageCount = tryBlock.invokeVirtualMethod( - ofMethod(resourceMetadata.getResourceClass(), Constants.PAGE_COUNT_METHOD_PREFIX + RESOURCE_METHOD_NAME, - int.class, Page.class), - resource, page); - - ResultHandle links = paginationImplementor.getLinks(tryBlock, uriInfo, page, pageCount); + ResultHandle pageCount = pageCount(tryBlock, resourceMetadata, resource, page, namedQuery, fieldValues, int.class); + ResultHandle links = paginationImplementor.getLinks(tryBlock, uriInfo, page, pageCount, fieldValues, namedQuery); ResultHandle entities = list(tryBlock, resourceMetadata, resource, page, sort, namedQuery, fieldValues); // Return response returnValueWithLinks(tryBlock, resourceMetadata, resourceProperties, entities, links); tryBlock.close(); } else { - ResultHandle uniPageCount = methodCreator.invokeVirtualMethod( - ofMethod(resourceMetadata.getResourceClass(), Constants.PAGE_COUNT_METHOD_PREFIX + RESOURCE_METHOD_NAME, - Uni.class, Page.class), - resource, page); + ResultHandle uniPageCount = pageCount(methodCreator, resourceMetadata, resource, page, namedQuery, fieldValues, + Uni.class); methodCreator.returnValue(UniImplementor.flatMap(methodCreator, uniPageCount, EXCEPTION_MESSAGE, (body, pageCount) -> { ResultHandle pageCountAsInt = body.checkCast(pageCount, Integer.class); - ResultHandle links = paginationImplementor.getLinks(body, uriInfo, page, pageCountAsInt); + ResultHandle links = paginationImplementor.getLinks(body, uriInfo, page, pageCountAsInt, fieldValues, + namedQuery); ResultHandle uniEntities = list(body, resourceMetadata, resource, page, sort, namedQuery, fieldValues); body.returnValue(UniImplementor.map(body, uniEntities, EXCEPTION_MESSAGE, (listBody, list) -> returnValueWithLinks(listBody, resourceMetadata, resourceProperties, list, @@ -322,41 +317,21 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou methodCreator.close(); } - public ResultHandle list(BytecodeCreator creator, ResourceMetadata resourceMetadata, ResultHandle resource, - ResultHandle page, ResultHandle sort, ResultHandle namedQuery, Map fieldValues) { + private ResultHandle pageCount(BytecodeCreator creator, ResourceMetadata resourceMetadata, ResultHandle resource, + ResultHandle page, ResultHandle namedQuery, Map fieldValues, Object returnType) { + AssignableResultHandle query = queryImplementor.getQuery(creator, namedQuery, fieldValues); + ResultHandle dataParams = queryImplementor.getDataParams(creator, fieldValues); - ResultHandle dataParams = creator.newInstance(ofConstructor(HashMap.class)); - ResultHandle queryList = creator.newInstance(ofConstructor(ArrayList.class)); - for (Map.Entry field : fieldValues.entrySet()) { - String fieldName = field.getKey(); - String paramName = fieldName.replace(".", "__"); - ResultHandle fieldValueFromQuery = field.getValue(); - BytecodeCreator fieldValueFromQueryIsSet = creator.ifNotNull(fieldValueFromQuery).trueBranch(); - fieldValueFromQueryIsSet.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), - queryList, fieldValueFromQueryIsSet.load(fieldName + "=:" + paramName)); - fieldValueFromQueryIsSet.invokeInterfaceMethod( - ofMethod(Map.class, "put", Object.class, Object.class, Object.class), - dataParams, fieldValueFromQueryIsSet.load(paramName), fieldValueFromQuery); - } + return creator.invokeVirtualMethod( + ofMethod(resourceMetadata.getResourceClass(), Constants.PAGE_COUNT_METHOD_PREFIX + RESOURCE_METHOD_NAME, + returnType, Page.class, String.class, Map.class), + resource, page, query, dataParams); + } - /** - * String query; - * if (namedQuery != null) { - * query = "#" + namedQuery; - * } else { - * query = String.join(" AND ", queryList); - * } - */ - AssignableResultHandle query = creator.createVariable(String.class); - BranchResult checkIfNamedQueryIsNull = creator.ifNull(namedQuery); - BytecodeCreator whenNamedQueryIsNull = checkIfNamedQueryIsNull.trueBranch(); - BytecodeCreator whenNamedQueryIsNotNull = checkIfNamedQueryIsNull.falseBranch(); - whenNamedQueryIsNotNull.assign(query, whenNamedQueryIsNotNull.invokeVirtualMethod( - ofMethod(String.class, "concat", String.class, String.class), - whenNamedQueryIsNotNull.load("#"), namedQuery)); - whenNamedQueryIsNull.assign(query, whenNamedQueryIsNull.invokeStaticMethod( - ofMethod(String.class, "join", String.class, CharSequence.class, Iterable.class), - creator.load(" AND "), queryList)); + public ResultHandle list(BytecodeCreator creator, ResourceMetadata resourceMetadata, ResultHandle resource, + ResultHandle page, ResultHandle sort, ResultHandle namedQuery, Map fieldValues) { + AssignableResultHandle query = queryImplementor.getQuery(creator, namedQuery, fieldValues); + ResultHandle dataParams = queryImplementor.getDataParams(creator, fieldValues); return creator.invokeVirtualMethod( ofMethod(resourceMetadata.getResourceClass(), "list", isNotReactivePanache() ? List.class : Uni.class, diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/PaginationImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/PaginationImplementor.java index 08723f99ebe59..e45a14e46f35b 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/PaginationImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/PaginationImplementor.java @@ -6,6 +6,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.Map; import jakarta.ws.rs.core.Link; import jakarta.ws.rs.core.UriBuilder; @@ -48,26 +49,27 @@ private ResultHandle getValidOrDefault(BytecodeCreator creator, ResultHandle val * Return an array with the links applicable for the provided page and page count. */ public ResultHandle getLinks(BytecodeCreator creator, ResultHandle uriInfo, ResultHandle page, - ResultHandle pageCount) { + ResultHandle pageCount, Map fieldValues, ResultHandle namedQuery) { ResultHandle links = creator.newInstance(ofConstructor(ArrayList.class, int.class), creator.load(4)); ResultHandle firstPage = creator.invokeVirtualMethod(ofMethod(Page.class, "first", Page.class), page); - ResultHandle firstPageLink = getLink(creator, uriInfo, firstPage, "first"); + ResultHandle firstPageLink = getLink(creator, uriInfo, firstPage, "first", fieldValues, namedQuery); creator.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), links, firstPageLink); ResultHandle lastPage = getLastPage(creator, page, pageCount); - ResultHandle lastPageLink = getLink(creator, uriInfo, lastPage, "last"); + ResultHandle lastPageLink = getLink(creator, uriInfo, lastPage, "last", fieldValues, namedQuery); creator.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), links, lastPageLink); BytecodeCreator previousPageCreator = isTheSamePage(creator, page, firstPage).falseBranch(); ResultHandle previousPage = previousPageCreator.invokeVirtualMethod(ofMethod(Page.class, "previous", Page.class), page); - ResultHandle previousPageLink = getLink(previousPageCreator, uriInfo, previousPage, "previous"); + ResultHandle previousPageLink = getLink(previousPageCreator, uriInfo, previousPage, "previous", fieldValues, + namedQuery); previousPageCreator.invokeInterfaceMethod( ofMethod(List.class, "add", boolean.class, Object.class), links, previousPageLink); BytecodeCreator nextPageCreator = isTheSamePage(creator, page, lastPage).falseBranch(); ResultHandle nextPage = nextPageCreator.invokeVirtualMethod(ofMethod(Page.class, "next", Page.class), page); - ResultHandle nextPageLink = getLink(nextPageCreator, uriInfo, nextPage, "next"); + ResultHandle nextPageLink = getLink(nextPageCreator, uriInfo, nextPage, "next", fieldValues, namedQuery); nextPageCreator.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), links, nextPageLink); ResultHandle linksCount = creator.invokeInterfaceMethod(ofMethod(List.class, "size", int.class), links); @@ -76,9 +78,11 @@ public ResultHandle getLinks(BytecodeCreator creator, ResultHandle uriInfo, Resu ofMethod(List.class, "toArray", Object[].class, Object[].class), links, linksArray); } - private ResultHandle getLink(BytecodeCreator creator, ResultHandle uriInfo, ResultHandle page, String rel) { + private ResultHandle getLink(BytecodeCreator creator, ResultHandle uriInfo, ResultHandle page, String rel, + Map fieldValues, ResultHandle namedQuery) { ResultHandle builder = creator.invokeStaticMethod( - ofMethod(Link.class, "fromUri", Link.Builder.class, URI.class), getPageUri(creator, uriInfo, page)); + ofMethod(Link.class, "fromUri", Link.Builder.class, URI.class), + getPageUri(creator, uriInfo, page, fieldValues, namedQuery)); creator.invokeInterfaceMethod(ofMethod(Link.Builder.class, "rel", Link.Builder.class, String.class), builder, creator.load(rel)); return creator.invokeInterfaceMethod(ofMethod(Link.Builder.class, "build", Link.class, Object[].class), @@ -92,9 +96,11 @@ private ResultHandle getLink(BytecodeCreator creator, ResultHandle uriInfo, Resu * @param creator a bytecode creator to be used for code generation * @param uriInfo a {@link UriInfo} to be used for the absolute path extraction * @param page a {@link Page} to be used for getting page number and size + * @param namedQuery a custom query * @return a page {@link URI} */ - private ResultHandle getPageUri(BytecodeCreator creator, ResultHandle uriInfo, ResultHandle page) { + private ResultHandle getPageUri(BytecodeCreator creator, ResultHandle uriInfo, ResultHandle page, + Map fieldValues, ResultHandle namedQuery) { ResultHandle uriBuilder = creator.invokeInterfaceMethod( ofMethod(UriInfo.class, "getAbsolutePathBuilder", UriBuilder.class), uriInfo); @@ -110,6 +116,19 @@ private ResultHandle getPageUri(BytecodeCreator creator, ResultHandle uriInfo, R ofMethod(UriBuilder.class, "queryParam", UriBuilder.class, String.class, Object[].class), uriBuilder, creator.load("size"), creator.marshalAsArray(Object.class, size)); + BytecodeCreator existNamedQuery = creator.ifNotNull(namedQuery).trueBranch(); + existNamedQuery.invokeVirtualMethod( + ofMethod(UriBuilder.class, "queryParam", UriBuilder.class, String.class, Object[].class), + uriBuilder, existNamedQuery.load("namedQuery"), existNamedQuery.marshalAsArray(Object.class, namedQuery)); + + for (Map.Entry field : fieldValues.entrySet()) { + BytecodeCreator existFieldValue = creator.ifNotNull(field.getValue()).trueBranch(); + existFieldValue.invokeVirtualMethod( + ofMethod(UriBuilder.class, "queryParam", UriBuilder.class, String.class, Object[].class), + uriBuilder, existFieldValue.load(field.getKey()), + existFieldValue.marshalAsArray(Object.class, field.getValue())); + } + return creator.invokeVirtualMethod( ofMethod(UriBuilder.class, "build", URI.class, Object[].class), uriBuilder, creator.newArray(Object.class, 0)); } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/QueryImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/QueryImplementor.java new file mode 100644 index 0000000000000..28ad2fb05fa0a --- /dev/null +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/QueryImplementor.java @@ -0,0 +1,78 @@ +package io.quarkus.rest.data.panache.deployment.utils; + +import static io.quarkus.gizmo.MethodDescriptor.ofConstructor; +import static io.quarkus.gizmo.MethodDescriptor.ofMethod; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.gizmo.AssignableResultHandle; +import io.quarkus.gizmo.BranchResult; +import io.quarkus.gizmo.BytecodeCreator; +import io.quarkus.gizmo.ResultHandle; + +public final class QueryImplementor { + /** + * Returns the name of the query or if it is not defined, then it will return a query built by the search parameters. + * + *
+     * {@code
+     * String query;
+     * if (namedQuery != null) {
+     *     query = "#" + namedQuery;
+     * } else {
+     *     query = String.join(" AND ", queryList);
+     * }
+     * }
+     * 
+ * + * @param creator a bytecode creator to be used for code generation. + * @param namedQuery HQL query to list entities. + * @param fieldValues fields query params. + * @return query. + */ + public AssignableResultHandle getQuery(BytecodeCreator creator, ResultHandle namedQuery, + Map fieldValues) { + ResultHandle queryList = creator.newInstance(ofConstructor(ArrayList.class)); + + for (Map.Entry field : fieldValues.entrySet()) { + String fieldName = field.getKey(); + String paramName = fieldName.replace(".", "__"); + ResultHandle fieldValueFromQuery = field.getValue(); + BytecodeCreator fieldValueFromQueryIsSet = creator.ifNotNull(fieldValueFromQuery).trueBranch(); + fieldValueFromQueryIsSet.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), + queryList, fieldValueFromQueryIsSet.load(fieldName + "=:" + paramName)); + } + + AssignableResultHandle query = creator.createVariable(String.class); + BranchResult checkIfNamedQueryIsNull = creator.ifNull(namedQuery); + BytecodeCreator whenNamedQueryIsNull = checkIfNamedQueryIsNull.trueBranch(); + BytecodeCreator whenNamedQueryIsNotNull = checkIfNamedQueryIsNull.falseBranch(); + whenNamedQueryIsNotNull.assign(query, whenNamedQueryIsNotNull.invokeVirtualMethod( + ofMethod(String.class, "concat", String.class, String.class), + whenNamedQueryIsNotNull.load("#"), namedQuery)); + whenNamedQueryIsNull.assign(query, whenNamedQueryIsNull.invokeStaticMethod( + ofMethod(String.class, "join", String.class, CharSequence.class, Iterable.class), + creator.load(" AND "), queryList)); + + return query; + } + + public ResultHandle getDataParams(BytecodeCreator creator, Map fieldValues) { + ResultHandle dataParams = creator.newInstance(ofConstructor(HashMap.class)); + + for (Map.Entry field : fieldValues.entrySet()) { + String fieldName = field.getKey(); + String paramName = fieldName.replace(".", "__"); + ResultHandle fieldValueFromQuery = field.getValue(); + BytecodeCreator fieldValueFromQueryIsSet = creator.ifNotNull(fieldValueFromQuery).trueBranch(); + fieldValueFromQueryIsSet.invokeInterfaceMethod( + ofMethod(Map.class, "put", Object.class, Object.class, Object.class), + dataParams, fieldValueFromQueryIsSet.load(paramName), fieldValueFromQuery); + } + + return dataParams; + } +} diff --git a/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/ReactiveDatasourceHealthCheck.java b/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/ReactiveDatasourceHealthCheck.java index b0d804266c96b..840dec97bd7e7 100644 --- a/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/ReactiveDatasourceHealthCheck.java +++ b/extensions/reactive-datasource/runtime/src/main/java/io/quarkus/reactive/datasource/runtime/ReactiveDatasourceHealthCheck.java @@ -76,7 +76,6 @@ public HealthCheckResponse call() { //20 seconds is rather high, but using just 10 is often not enough on slow CI //systems, especially if the connections have to be established for the first time. databaseConnectionAttempt.get(20, TimeUnit.SECONDS); - builder.withData(dataSourceName, "UP"); } catch (RuntimeException | ExecutionException exception) { operationsError(dataSourceName, exception); builder.down(); @@ -105,6 +104,8 @@ private void checkFailure(AsyncResult> ar, HealthCheckResponseBuilde operationsError(dataSourceName, ar.cause()); builder.down(); builder.withData(dataSourceName, "down - connection failed: " + ar.cause().getMessage()); + } else { + builder.withData(dataSourceName, "UP"); } } diff --git a/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/DataSourceHealthCheckPayloadTest.java b/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/DataSourceHealthCheckPayloadTest.java new file mode 100644 index 0000000000000..11f13aafbb78b --- /dev/null +++ b/extensions/reactive-mssql-client/deployment/src/test/java/io/quarkus/reactive/mssql/client/DataSourceHealthCheckPayloadTest.java @@ -0,0 +1,28 @@ +package io.quarkus.reactive.mssql.client; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class DataSourceHealthCheckPayloadTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.datasource.health.enabled", "true") + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Test + public void testDataSourceHealthCheckPayload() { + RestAssured.when().get("/q/health/ready") + .then() + .body("status", CoreMatchers.equalTo("DOWN")) + .body("checks.data", CoreMatchers + .hasItem(Matchers.hasValue(CoreMatchers.containsString("down - connection failed")))); + } + +} diff --git a/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/DataSourceHealthCheckPayloadTest.java b/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/DataSourceHealthCheckPayloadTest.java new file mode 100644 index 0000000000000..c219c54b0afbd --- /dev/null +++ b/extensions/reactive-mysql-client/deployment/src/test/java/io/quarkus/reactive/mysql/client/DataSourceHealthCheckPayloadTest.java @@ -0,0 +1,28 @@ +package io.quarkus.reactive.mysql.client; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class DataSourceHealthCheckPayloadTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.datasource.health.enabled", "true") + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Test + public void testDataSourceHealthCheckPayload() { + RestAssured.when().get("/q/health/ready") + .then() + .body("status", CoreMatchers.equalTo("DOWN")) + .body("checks.data", CoreMatchers + .hasItem(Matchers.hasValue(CoreMatchers.containsString("down - connection failed")))); + } + +} diff --git a/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/DataSourceHealthCheckPayloadTest.java b/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/DataSourceHealthCheckPayloadTest.java new file mode 100644 index 0000000000000..22daf2732285b --- /dev/null +++ b/extensions/reactive-oracle-client/deployment/src/test/java/io/quarkus/reactive/oracle/client/DataSourceHealthCheckPayloadTest.java @@ -0,0 +1,28 @@ +package io.quarkus.reactive.oracle.client; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class DataSourceHealthCheckPayloadTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.datasource.health.enabled", "true") + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Test + public void testDataSourceHealthCheckPayload() { + RestAssured.when().get("/q/health/ready") + .then() + .body("status", CoreMatchers.equalTo("DOWN")) + .body("checks.data", CoreMatchers + .hasItem(Matchers.hasValue(CoreMatchers.containsString("down - connection failed")))); + } + +} diff --git a/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/DataSourceHealthCheckPayloadTest.java b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/DataSourceHealthCheckPayloadTest.java new file mode 100644 index 0000000000000..77e65102d0634 --- /dev/null +++ b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/DataSourceHealthCheckPayloadTest.java @@ -0,0 +1,28 @@ +package io.quarkus.reactive.pg.client; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class DataSourceHealthCheckPayloadTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.datasource.health.enabled", "true") + .overrideConfigKey("quarkus.devservices.enabled", "false"); + + @Test + public void testDataSourceHealthCheckPayload() { + RestAssured.when().get("/q/health/ready") + .then() + .body("status", CoreMatchers.equalTo("DOWN")) + .body("checks.data", CoreMatchers + .hasItem(Matchers.hasValue(CoreMatchers.containsString("down - connection failed")))); + } + +} diff --git a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/RepositoryMethodsImplementor.java b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/RepositoryMethodsImplementor.java index 22b936f891448..33a1f25fe1218 100644 --- a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/RepositoryMethodsImplementor.java +++ b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/RepositoryMethodsImplementor.java @@ -131,7 +131,7 @@ public void implementPagedList(ClassCreator classCreator, String repositoryInter //PagingAndSortingRepository Page findAll(Pageable pageable); public void implementListPageCount(ClassCreator classCreator, String repositoryInterfaceName) { MethodCreator methodCreator = classCreator.getMethodCreator(Constants.PAGE_COUNT_METHOD_PREFIX + "list", - int.class, Page.class); + int.class, Page.class, String.class, Map.class); if (entityClassHelper.isPagingAndSortingRepository(repositoryInterfaceName)) { ResultHandle page = methodCreator.getMethodParam(0); ResultHandle pageable = toPageable(methodCreator, page); diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireSocketHttpBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireSocketHttpBuildItem.java new file mode 100644 index 0000000000000..18f4debbea48d --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireSocketHttpBuildItem.java @@ -0,0 +1,13 @@ +package io.quarkus.vertx.http.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Marker class that can be used to force the socket to open even when using virtual HTTP. + * + * There are some use cases that may want to handle both real and virtual HTTP requests, such as mapping incoming + * gRPC requests onto JAX-RS handlers. + */ +public final class RequireSocketHttpBuildItem extends SimpleBuildItem { + public static final RequireSocketHttpBuildItem MARKER = new RequireSocketHttpBuildItem(); +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 495e331d6257a..d66390edf7334 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -420,6 +420,7 @@ void openSocket(ApplicationStartBuildItem start, BuildProducer reflectiveClass, HttpBuildTimeConfig httpBuildTimeConfig, Optional requireVirtual, + Optional requireSocket, EventLoopCountBuildItem eventLoopCount, List websocketSubProtocols, Capabilities capabilities, @@ -430,8 +431,9 @@ void openSocket(ApplicationStartBuildItem start, .produce(ReflectiveClassBuildItem.builder(VirtualServerChannel.class) .build()); } - boolean startSocket = (!startVirtual || launchMode.getLaunchMode() != LaunchMode.NORMAL) - && (requireVirtual.isEmpty() || !requireVirtual.get().isAlwaysVirtual()); + boolean startSocket = requireSocket.isPresent() || + ((!startVirtual || launchMode.getLaunchMode() != LaunchMode.NORMAL) + && (requireVirtual.isEmpty() || !requireVirtual.get().isAlwaysVirtual())); recorder.startServer(vertx.getVertx(), shutdown, launchMode.getLaunchMode(), startVirtual, startSocket, eventLoopCount.getEventLoopCount(), diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-header.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-header.js index b46e5f26a74d6..20805111c7ea1 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-header.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-header.js @@ -1,22 +1,18 @@ import { LitElement, html, css } from 'lit'; import { RouterController } from 'router-controller'; -import { StorageController } from 'storage-controller'; import { notifier } from 'notifier'; import { observeState } from 'lit-element-state'; import { themeState } from 'theme-state'; import { devuiState } from 'devui-state'; -import '@vaadin/menu-bar'; import '@vaadin/tabs'; -import '@vaadin/button'; import 'qwc/qwc-extension-link.js'; - +import './qwc-theme-switch.js'; /** * This component represent the Dev UI Header */ export class QwcHeader extends observeState(LitElement) { routerController = new RouterController(this); - storageControl = new StorageController(this); static styles = css` @@ -31,7 +27,7 @@ export class QwcHeader extends observeState(LitElement) { display: flex; justify-content: space-around; align-items: center; - padding-right: 60px; + padding-right: 10px; } .logo-title { @@ -88,13 +84,6 @@ export class QwcHeader extends observeState(LitElement) { align-items: center; } - .themeDropdown { - position: absolute; - right: 0px; - top: 10px; - z-index: 3; - } - .hidden { display:none; } @@ -103,10 +92,7 @@ export class QwcHeader extends observeState(LitElement) { static properties = { _title: {state: true}, _subTitle: {state: true}, - _rightSideNav: {state: true}, - _selectedTheme: {state: true}, - _themeOptions: {state: true}, - _desktopTheme: {state: true} + _rightSideNav: {state: true} }; constructor() { @@ -115,10 +101,6 @@ export class QwcHeader extends observeState(LitElement) { this._subTitle = null; this._rightSideNav = ""; - this._createThemeItems(); - this._restoreThemePreference(); - this._createThemeOptions(); - window.addEventListener('vaadin-router-location-changed', (event) => { this._updateHeader(event); }); @@ -126,34 +108,6 @@ export class QwcHeader extends observeState(LitElement) { connectedCallback() { super.connectedCallback(); - // Get desktop theme setting - this._desktopTheme = "dark"; - if(window.matchMedia){ - if(window.matchMedia('(prefers-color-scheme: light)').matches){ - this._desktopTheme = "light"; - } - - // Change theme setting when OS theme change - window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', e => { - if(e.matches){ - this._desktopTheme = "light"; - }else{ - this._desktopTheme = "dark"; - } - this._changeToSelectedTheme(); - }); - } - - this._changeToSelectedTheme(); - } - - _restoreThemePreference() { - const storedValue = this.storageControl.get("theme-preference"); - if(storedValue){ - this._selectedTheme = storedValue; - }else { - this._selectedTheme = "desktop"; - } } render() { @@ -197,71 +151,7 @@ export class QwcHeader extends observeState(LitElement) { } _renderThemeOptions(){ - return html` - `; - } - - _changeThemeOption(e){ - this._selectedTheme = e.detail.value.name; - this._createThemeOptions(); - this._changeToSelectedTheme(); - this.storageControl.set('theme-preference', this._selectedTheme); - } - - _changeToSelectedTheme(){ - if(this._selectedTheme === "desktop"){ - themeState.changeTo(this._desktopTheme); - }else { - themeState.changeTo(this._selectedTheme); - } - } - - _createThemeOptions(){ - - let selectedComponent = this._desktopThemeItem; - if(this._selectedTheme === "dark"){ - selectedComponent = this._darkThemeItem; - }else if(this._selectedTheme === "light"){ - selectedComponent = this._lightThemeItem; - } - - this._themeOptions = [ - { - component: selectedComponent, - children: [ - { - component: this._darkThemeItem, - name: "dark" - }, - { - component: this._lightThemeItem, - name: "light" - }, - { - component: this._desktopThemeItem, - name: "desktop" - } - ] - } - - ]; - } - - _createThemeItems() { - this._darkThemeItem = this._createThemeItem("moon", "dark"); - this._lightThemeItem = this._createThemeItem("sun", "light"); - this._desktopThemeItem = this._createThemeItem("desktop", "desktop"); - } - - _createThemeItem(iconName, ariaLabel) { - const item = document.createElement('vaadin-context-menu-item'); - const icon = document.createElement('vaadin-icon'); - item.setAttribute('aria-label', ariaLabel); - icon.setAttribute('icon', `font-awesome-solid:${iconName}`); - item.appendChild(icon); - return item; + return html``; } _updateHeader(event){ diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-theme-switch.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-theme-switch.js new file mode 100644 index 0000000000000..8acb1fde66334 --- /dev/null +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-theme-switch.js @@ -0,0 +1,98 @@ +import { LitElement, html, css } from 'lit'; +import { themeState } from 'theme-state'; +import { StorageController } from 'storage-controller'; +import '@vaadin/button'; + +/** + * Basic theme switch + */ +export class QwcThemeSwitch extends LitElement { + storageControl = new StorageController(this); + + themes = [ + { id: 0, name: 'Desktop', icon: 'font-awesome-solid:desktop' }, + { id: 1, name: 'Light', icon: 'font-awesome-solid:sun' }, + { id: 2, name: 'Dark', icon: 'font-awesome-solid:moon' } + ]; + + static styles = css` + .themeButton { + padding-left: 10px; + } + .button { + --vaadin-button-background: var(--lumo-base-color); + } + `; + + static properties = { + _selectedThemeIndex: {state: true}, + }; + + constructor() { + super(); + this._restoreThemePreference(); + } + + connectedCallback() { + super.connectedCallback(); + this._desktopTheme = "dark"; // default + if(window.matchMedia){ + if(window.matchMedia('(prefers-color-scheme: light)').matches){ + this._desktopTheme = "light"; + } + + // Change theme setting when OS theme change + window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', e => { + if(e.matches){ + this._desktopTheme = "light"; + }else{ + this._desktopTheme = "dark"; + } + if(this._selectedThemeIndex===0){ + this._changeToSelectedThemeIndex(); + } + }); + } + + this._changeToSelectedThemeIndex(); + } + + render() { + let theme = this.themes[this._selectedThemeIndex]; + + return html`
+ + + +
`; + } + + _nextTheme(e){ + this._selectedThemeIndex = (this._selectedThemeIndex + 1) % this.themes.length; + this._changeToSelectedThemeIndex(); + } + + _changeToSelectedThemeIndex(){ + let theme = this.themes[this._selectedThemeIndex]; + this.storageControl.set('theme-preference', theme.id); + + if(theme.id === 0){ // Desktop + themeState.changeTo(this._desktopTheme); + }else { + themeState.changeTo(theme.name.toLowerCase()); + } + + } + + _restoreThemePreference() { + const storedValue = this.storageControl.get("theme-preference"); + if(storedValue){ + this._selectedThemeIndex = storedValue; + } else { + this._selectedThemeIndex = 0; + } + } + + +} +customElements.define('qwc-theme-switch', QwcThemeSwitch); \ No newline at end of file diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/CloseReason.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/CloseReason.java index 108c2d150b55b..009155eb67e41 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/CloseReason.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/CloseReason.java @@ -50,4 +50,9 @@ public String getMessage() { return message; } + @Override + public String toString() { + return "CloseReason [code=" + code + ", " + (message != null ? "message=" + message : "") + "]"; + } + } diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionBase.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionBase.java index 3b5694e9ac8c6..4febc7792d813 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionBase.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionBase.java @@ -131,7 +131,12 @@ public BroadcastSender broadcast() { public CloseReason closeReason() { WebSocketBase ws = webSocket(); if (ws.isClosed()) { - return new CloseReason(ws.closeStatusCode(), ws.closeReason()); + Short code = ws.closeStatusCode(); + if (code == null) { + // This could happen if the connection is terminated abruptly + return CloseReason.INTERNAL_SERVER_ERROR; + } + return new CloseReason(code, ws.closeReason()); } return null; } diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 6beec95db70b0..c305e3fd84091 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -64,7 +64,7 @@ 2.17.2 2.6.0 3.0.2 - 3.0.3 + 3.0.4 3.0.1 4.2.1 3.13.2