diff --git a/deployment/src/main/java/com/github/mcollovati/quarkus/hilla/deployment/QuarkusHillaExtensionProcessor.java b/deployment/src/main/java/com/github/mcollovati/quarkus/hilla/deployment/QuarkusHillaExtensionProcessor.java index 775dc237..d519f880 100644 --- a/deployment/src/main/java/com/github/mcollovati/quarkus/hilla/deployment/QuarkusHillaExtensionProcessor.java +++ b/deployment/src/main/java/com/github/mcollovati/quarkus/hilla/deployment/QuarkusHillaExtensionProcessor.java @@ -25,6 +25,7 @@ import com.vaadin.flow.router.Route; import com.vaadin.flow.server.auth.AnonymousAllowed; +import dev.hilla.BrowserCallable; import dev.hilla.Endpoint; import dev.hilla.EndpointInvoker; import dev.hilla.EndpointRegistry; @@ -164,6 +165,8 @@ void registerEndpoints( BuildProducer additionalBeanDefiningAnnotationRegistry) { additionalBeanDefiningAnnotationRegistry.produce(new BeanDefiningAnnotationBuildItem( DotName.createSimple(Endpoint.class.getName()), BuiltinScope.SINGLETON.getName())); + additionalBeanDefiningAnnotationRegistry.produce(new BeanDefiningAnnotationBuildItem( + DotName.createSimple(BrowserCallable.class.getName()), BuiltinScope.SINGLETON.getName())); } @BuildStep diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/AbstractEndpointControllerTest.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/AbstractEndpointControllerTest.java new file mode 100644 index 00000000..a05a56e8 --- /dev/null +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/AbstractEndpointControllerTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2023 Marco Collovati, Dario Götze + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.mcollovati.quarkus.hilla.deployment; + +import dev.hilla.exception.EndpointValidationException; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.Test; + +import com.github.mcollovati.quarkus.hilla.deployment.endpoints.Pojo; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; + +import static com.github.mcollovati.quarkus.hilla.deployment.TestUtils.givenEndpointRequest; + +abstract class AbstractEndpointControllerTest { + protected abstract String getEndpointName(); + + @Test + void invokeEndpoint_singleSimpleParameter() { + String msg = "A text message"; + givenEndpointRequest(getEndpointName(), "echo", TestUtils.Parameters.param("message", msg)) + .then() + .assertThat() + .statusCode(200) + .and() + .body(equalTo("\"" + msg + "\"")); + } + + @Test + void invokeEndpoint_singleComplexParameter() { + String msg = "A text message"; + Pojo pojo = new Pojo(10, msg); + givenEndpointRequest(getEndpointName(), "pojo", TestUtils.Parameters.param("pojo", pojo)) + .then() + .assertThat() + .statusCode(200) + .and() + .body("number", equalTo(100)) + .and() + .body("text", equalTo(msg + msg)); + } + + @Test + void invokeEndpoint_multipleParameters() { + givenEndpointRequest( + getEndpointName(), + "calculate", + TestUtils.Parameters.param("operator", "+").add("a", 10).add("b", 20)) + .then() + .assertThat() + .statusCode(200) + .and() + .body(equalTo("30")); + } + + @Test + void invokeEndpoint_wrongParametersOrder_badRequest() { + givenEndpointRequest( + getEndpointName(), + "calculate", + TestUtils.Parameters.param("a", 10).add("operator", "+").add("b", 20)) + .then() + .assertThat() + .statusCode(400) + .and() + .body("type", equalTo(EndpointValidationException.class.getName())) + .and() + .body( + "message", + CoreMatchers.allOf( + containsString("Validation error"), + containsString("'" + getEndpointName() + "'"), + containsString("'calculate'"))) + .body("validationErrorData[0].parameterName", equalTo("operator")); + } + + @Test + void invokeEndpoint_wrongNumberOfParameters_badRequest() { + givenEndpointRequest(getEndpointName(), "calculate", TestUtils.Parameters.param("operator", "+")) + .then() + .assertThat() + .statusCode(400) + .and() + .body( + "message", + CoreMatchers.allOf( + containsString("Incorrect number of parameters"), + containsString("'" + getEndpointName() + "'"), + containsString("'calculate'"), + containsString("expected: 3, got: 1"))); + } + + @Test + void invokeEndpoint_wrongEndpointName_notFound() { + givenEndpointRequest("NotExistingTestEndpoint", "calculate", TestUtils.Parameters.param("operator", "+")) + .then() + .assertThat() + .statusCode(404); + } + + @Test + void invokeEndpoint_wrongMethodName_notFound() { + givenEndpointRequest(getEndpointName(), "notExistingMethod", TestUtils.Parameters.param("operator", "+")) + .then() + .assertThat() + .statusCode(404); + } + + @Test + void invokeEndpoint_emptyMethodName_notFound() { + givenEndpointRequest(getEndpointName(), "", TestUtils.Parameters.param("operator", "+")) + .then() + .assertThat() + .statusCode(404); + } + + @Test + void invokeEndpoint_missingMethodName_notFound() { + RestAssured.given() + .contentType(ContentType.JSON) + .cookie("csrfToken", "CSRF_TOKEN") + .header("X-CSRF-Token", "CSRF_TOKEN") + .basePath("/connect") + .when() + .post(getEndpointName()) + .then() + .assertThat() + .statusCode(404); + } +} diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/BrowserCallableControllerTest.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/BrowserCallableControllerTest.java new file mode 100644 index 00000000..e0206f51 --- /dev/null +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/BrowserCallableControllerTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Marco Collovati, Dario Götze + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.mcollovati.quarkus.hilla.deployment; + +import io.quarkus.test.QuarkusUnitTest; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.github.mcollovati.quarkus.hilla.deployment.endpoints.Pojo; +import com.github.mcollovati.quarkus.hilla.deployment.endpoints.TestBrowserCallable; + +class BrowserCallableControllerTest extends AbstractEndpointControllerTest { + + private static final String ENDPOINT_NAME = TestBrowserCallable.class.getSimpleName(); + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(TestUtils.class, Pojo.class, TestBrowserCallable.class)); + + @Override + protected String getEndpointName() { + return ENDPOINT_NAME; + } +} diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/EndpointControllerTest.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/EndpointControllerTest.java index 732d6503..270c5ab4 100644 --- a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/EndpointControllerTest.java +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/EndpointControllerTest.java @@ -15,143 +15,25 @@ */ package com.github.mcollovati.quarkus.hilla.deployment; -import dev.hilla.exception.EndpointValidationException; import io.quarkus.test.QuarkusUnitTest; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import org.hamcrest.CoreMatchers; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import com.github.mcollovati.quarkus.hilla.deployment.TestUtils.Parameters; +import com.github.mcollovati.quarkus.hilla.deployment.endpoints.Pojo; import com.github.mcollovati.quarkus.hilla.deployment.endpoints.TestEndpoint; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; - -import static com.github.mcollovati.quarkus.hilla.deployment.TestUtils.givenEndpointRequest; - -class EndpointControllerTest { +class EndpointControllerTest extends AbstractEndpointControllerTest { private static final String ENDPOINT_NAME = TestEndpoint.class.getSimpleName(); @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() - .setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class).addClasses(TestUtils.class, TestEndpoint.class)); - - @Test - void invokeEndpoint_singleSimpleParameter() { - String msg = "A text message"; - givenEndpointRequest(ENDPOINT_NAME, "echo", Parameters.param("message", msg)) - .then() - .assertThat() - .statusCode(200) - .and() - .body(equalTo("\"" + msg + "\"")); - } - - @Test - void invokeEndpoint_singleComplexParameter() { - String msg = "A text message"; - TestEndpoint.Pojo pojo = new TestEndpoint.Pojo(10, msg); - givenEndpointRequest(ENDPOINT_NAME, "pojo", Parameters.param("pojo", pojo)) - .then() - .assertThat() - .statusCode(200) - .and() - .body("number", equalTo(100)) - .and() - .body("text", equalTo(msg + msg)); - } - - @Test - void invokeEndpoint_multipleParameters() { - givenEndpointRequest( - ENDPOINT_NAME, - "calculate", - Parameters.param("operator", "+").add("a", 10).add("b", 20)) - .then() - .assertThat() - .statusCode(200) - .and() - .body(equalTo("30")); - } - - @Test - void invokeEndpoint_wrongParametersOrder_badRequest() { - givenEndpointRequest( - ENDPOINT_NAME, - "calculate", - Parameters.param("a", 10).add("operator", "+").add("b", 20)) - .then() - .assertThat() - .statusCode(400) - .and() - .body("type", equalTo(EndpointValidationException.class.getName())) - .and() - .body( - "message", - CoreMatchers.allOf( - containsString("Validation error"), - containsString("'TestEndpoint'"), - containsString("'calculate'"))) - .body("validationErrorData[0].parameterName", equalTo("operator")); - } - - @Test - void invokeEndpoint_wrongNumberOfParameters_badRequest() { - givenEndpointRequest(ENDPOINT_NAME, "calculate", Parameters.param("operator", "+")) - .then() - .assertThat() - .statusCode(400) - .and() - .body( - "message", - CoreMatchers.allOf( - containsString("Incorrect number of parameters"), - containsString("'TestEndpoint'"), - containsString("'calculate'"), - containsString("expected: 3, got: 1"))); - } - - @Test - void invokeEndpoint_wrongEndpointName_notFound() { - givenEndpointRequest("NotExistingTestEndpoint", "calculate", Parameters.param("operator", "+")) - .then() - .assertThat() - .statusCode(404); - } - - @Test - void invokeEndpoint_wrongMethodName_notFound() { - givenEndpointRequest(ENDPOINT_NAME, "notExistingMethod", Parameters.param("operator", "+")) - .then() - .assertThat() - .statusCode(404); - } - - @Test - void invokeEndpoint_emptyMethodName_notFound() { - givenEndpointRequest(ENDPOINT_NAME, "", Parameters.param("operator", "+")) - .then() - .assertThat() - .statusCode(404); - } + .setArchiveProducer(() -> + ShrinkWrap.create(JavaArchive.class).addClasses(TestUtils.class, Pojo.class, TestEndpoint.class)); - @Test - void invokeEndpoint_missingMethodName_notFound() { - RestAssured.given() - .contentType(ContentType.JSON) - .cookie("csrfToken", "CSRF_TOKEN") - .header("X-CSRF-Token", "CSRF_TOKEN") - .basePath("/connect") - .when() - .post(ENDPOINT_NAME) - .then() - .assertThat() - .statusCode(404); + @Override + protected String getEndpointName() { + return ENDPOINT_NAME; } } diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/Pojo.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/Pojo.java new file mode 100644 index 00000000..fd77a1c4 --- /dev/null +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/Pojo.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Marco Collovati, Dario Götze + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.mcollovati.quarkus.hilla.deployment.endpoints; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Pojo { + @JsonProperty + public final int number; + + @JsonProperty + public final String text; + + @JsonCreator + public Pojo(@JsonProperty int number, @JsonProperty String text) { + this.number = number; + this.text = text; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pojo pojo = (Pojo) o; + return number == pojo.number && Objects.equals(text, pojo.text); + } + + @Override + public int hashCode() { + return Objects.hash(number, text); + } +} diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestBrowserCallable.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestBrowserCallable.java new file mode 100644 index 00000000..780a66a7 --- /dev/null +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestBrowserCallable.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Marco Collovati, Dario Götze + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.mcollovati.quarkus.hilla.deployment.endpoints; + +import com.vaadin.flow.server.auth.AnonymousAllowed; +import dev.hilla.BrowserCallable; + +@BrowserCallable +@AnonymousAllowed +public class TestBrowserCallable { + + public String echo(String message) { + return message; + } + + public int calculate(String operator, int a, int b) { + int result; + switch (operator) { + case "+": + result = a + b; + break; + case "*": + result = a * b; + break; + default: + throw new IllegalArgumentException("Invalid operation"); + } + return result; + } + + public Pojo pojo(Pojo pojo) { + return new Pojo(pojo.number * 10, pojo.text + pojo.text); + } +} diff --git a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestEndpoint.java b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestEndpoint.java index 43d86932..8e89fda7 100644 --- a/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestEndpoint.java +++ b/deployment/src/test/java/com/github/mcollovati/quarkus/hilla/deployment/endpoints/TestEndpoint.java @@ -15,10 +15,6 @@ */ package com.github.mcollovati.quarkus.hilla.deployment.endpoints; -import java.util.Objects; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import com.vaadin.flow.server.auth.AnonymousAllowed; import dev.hilla.Endpoint; @@ -48,31 +44,4 @@ public int calculate(String operator, int a, int b) { public Pojo pojo(Pojo pojo) { return new Pojo(pojo.number * 10, pojo.text + pojo.text); } - - public static class Pojo { - @JsonProperty - private final int number; - - @JsonProperty - private final String text; - - @JsonCreator - public Pojo(@JsonProperty int number, @JsonProperty String text) { - this.number = number; - this.text = text; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Pojo pojo = (Pojo) o; - return number == pojo.number && Objects.equals(text, pojo.text); - } - - @Override - public int hashCode() { - return Objects.hash(number, text); - } - } } diff --git a/integration-tests/hybrid-smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java b/integration-tests/hybrid-smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java index 0d276cc8..5f31e384 100644 --- a/integration-tests/hybrid-smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java +++ b/integration-tests/hybrid-smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java @@ -22,12 +22,12 @@ import com.example.application.entities.UserPOJO; import com.vaadin.flow.server.VaadinRequest; import com.vaadin.flow.server.auth.AnonymousAllowed; -import dev.hilla.Endpoint; +import dev.hilla.BrowserCallable; import dev.hilla.EndpointSubscription; import dev.hilla.Nonnull; import reactor.core.publisher.Flux; -@Endpoint +@BrowserCallable @AnonymousAllowed public class HelloWorldEndpoint { diff --git a/integration-tests/smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java b/integration-tests/smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java index 0d276cc8..5f31e384 100644 --- a/integration-tests/smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java +++ b/integration-tests/smoke-tests/src/main/java/com/example/application/endpoints/helloworld/HelloWorldEndpoint.java @@ -22,12 +22,12 @@ import com.example.application.entities.UserPOJO; import com.vaadin.flow.server.VaadinRequest; import com.vaadin.flow.server.auth.AnonymousAllowed; -import dev.hilla.Endpoint; +import dev.hilla.BrowserCallable; import dev.hilla.EndpointSubscription; import dev.hilla.Nonnull; import reactor.core.publisher.Flux; -@Endpoint +@BrowserCallable @AnonymousAllowed public class HelloWorldEndpoint {