This is an automated email from the ASF dual-hosted git repository. fmariani pushed a commit to branch camel-spring-boot-4.8.x in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit 527646878f836c4807c534ad3bc23e86306101e9 Author: Croway <[email protected]> AuthorDate: Thu Oct 17 18:40:13 2024 +0200 CAMEL-21461: Add features to Platform Http SB Starter Certification Tests + Minor Fixes Add failing tests Add ReaderCache Converter test fix streaming large file test header mapping is already done in DefaultHttpBinding fix streaming CSB-5889: Let's cache the body so that is can be read multiple times CSB-5889: regen Disable not implemented feature tests Co-Authored-By: johnpoth <[email protected]> Handle Consumes only for methods with body --- .../camel-platform-http-starter/pom.xml | 39 ++ .../springboot/CamelRequestHandlerMapping.java | 6 +- .../http/springboot/PlatformHttpMessage.java | 8 +- .../springboot/SpringBootPlatformHttpBinding.java | 10 +- .../PlatformHttpAsyncRequestHandlingTest.java | 11 +- .../platform/http/springboot/PlatformHttpBase.java | 3 +- .../http/springboot/PlatformHttpStreamingTest.java | 6 +- ...pringBootPlatformHttpCamelIntegrationsTest.java | 105 ++++++ .../SpringBootPlatformHttpCertificationTest.java | 391 +++++++++++++++++++++ .../SpringBootPlatformHttpCookiesTest.java | 160 +++++++++ .../SpringBootPlatformHttpEngineTest.java | 253 +++++++++++++ ...SpringBootPlatformHttpHandleWriteErrorTest.java | 4 +- ...pringBootPlatformHttpJacksonConverterTest.java} | 66 ++-- ...pringBootPlatformHttpMultipleExecutorsTest.java | 4 +- .../SpringBootPlatformHttpProxyTest.java | 109 ++++++ .../SpringBootPlatformHttpRestDSLTest.java | 4 +- .../SpringBootPlatformHttpSessionTest.java | 174 +++++++++ .../SpringBootPlatformHttpSpringSessionTest.java | 67 ++++ .../springboot/SpringBootPlatformHttpTest.java | 4 +- .../SpringBootPlatformHttpTypeConverterTest.java | 135 +++++++ .../SpringBootPlatformHttpValidationTest.java | 191 ++++++++++ pom.xml | 2 + 22 files changed, 1711 insertions(+), 41 deletions(-) diff --git a/components-starter/camel-platform-http-starter/pom.xml b/components-starter/camel-platform-http-starter/pom.xml index f3b66c9e3ca..ba8f36e6043 100644 --- a/components-starter/camel-platform-http-starter/pom.xml +++ b/components-starter/camel-platform-http-starter/pom.xml @@ -55,6 +55,24 @@ <version>${rest-assured-version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + <version>${spring-boot-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.session</groupId> + <artifactId>spring-session-hazelcast</artifactId> + <version>${spring-session-hazelcast-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.hazelcast</groupId> + <artifactId>hazelcast</artifactId> + <version>${hazelcast-version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> @@ -65,6 +83,27 @@ <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-jackson-starter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-http-starter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.wiremock.integrations</groupId> + <artifactId>wiremock-spring-boot</artifactId> + <version>${wiremock-spring-boot-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel.springboot</groupId> + <artifactId>camel-jsonpath-starter</artifactId> + <scope>test</scope> + </dependency> <!--START OF GENERATED CODE--> <dependency> <groupId>org.apache.camel.springboot</groupId> diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java index 380d69ce232..9eb332f45c4 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java @@ -133,7 +133,7 @@ public class CamelRequestHandlerMapping extends RequestMappingHandlerMapping imp @Override public void registerHttpEndpoint(HttpEndpointModel model) { - RequestMappingInfo info = asRequestMappingInfo(model); + List<RequestMappingInfo> requestMappingInfos = asRequestMappingInfo(model); Method m = ReflectionHelper.findMethod(SpringBootPlatformHttpConsumer.class, "service", HttpServletRequest.class, HttpServletResponse.class); for (RequestMappingInfo info : requestMappingInfos) { @@ -148,7 +148,9 @@ public class CamelRequestHandlerMapping extends RequestMappingHandlerMapping imp // noop } - private RequestMappingInfo asRequestMappingInfo(HttpEndpointModel model) { + private List<RequestMappingInfo> asRequestMappingInfo(HttpEndpointModel model) { + List<RequestMappingInfo> result = new ArrayList<>(); + // allowed methods from model or endpoint List<RequestMethod> methods = new ArrayList<>(); String verbs = model.getVerbs(); diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java index 4e8abf92fe3..9d8e151ea38 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java @@ -20,6 +20,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.camel.Exchange; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.StreamCache; import org.apache.camel.http.common.HttpBinding; import org.apache.camel.support.DefaultMessage; import org.apache.camel.util.ObjectHelper; @@ -60,7 +61,12 @@ public class PlatformHttpMessage extends DefaultMessage { if (flag != null && flag) { this.setHeader("CamelSkipWwwFormUrlEncoding", Boolean.TRUE); } - + // we need to favor using stream cache so the body can be re-read later when routing the message + StreamCache newBody = camelContext.getTypeConverter().tryConvertTo(StreamCache.class, + exchange, getBody()); + if (newBody != null) { + setBody(newBody); + } binding.readRequest(request, this); } diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java index eea1e9a1904..d5e493a28b4 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java @@ -16,9 +16,16 @@ */ package org.apache.camel.component.platform.http.springboot; +import jakarta.activation.DataHandler; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; -import org.apache.camel.Message; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.camel.*; +import org.apache.camel.attachment.AttachmentMessage; +import org.apache.camel.attachment.CamelFileDataSource; import org.apache.camel.component.platform.http.PlatformHttpEndpoint; +import org.apache.camel.converter.stream.CachedOutputStream; import org.apache.camel.http.base.HttpHelper; import org.apache.camel.http.common.DefaultHttpBinding; import org.apache.camel.support.ExchangeHelper; @@ -38,6 +45,7 @@ import java.util.Locale; import java.util.UUID; public class SpringBootPlatformHttpBinding extends DefaultHttpBinding { + private static final Logger LOG = LoggerFactory.getLogger(SpringBootPlatformHttpBinding.class); protected void populateRequestParameters(HttpServletRequest request, Message message) { super.populateRequestParameters(request, message); diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpAsyncRequestHandlingTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpAsyncRequestHandlingTest.java index 302149febbf..1dcc7e50dd1 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpAsyncRequestHandlingTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpAsyncRequestHandlingTest.java @@ -23,6 +23,8 @@ import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; @@ -32,14 +34,11 @@ import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java index 79d5cb321d2..1e4fa6e7373 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpBase.java @@ -16,7 +16,6 @@ */ package org.apache.camel.component.platform.http.springboot; -import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.ServiceStatus; import org.assertj.core.api.Assertions; @@ -25,6 +24,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; +import java.util.concurrent.TimeUnit; + import static org.junit.jupiter.api.Assertions.assertEquals; abstract class PlatformHttpBase { diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpStreamingTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpStreamingTest.java index 05962f12349..86f3c056a90 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpStreamingTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/PlatformHttpStreamingTest.java @@ -24,7 +24,9 @@ import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.apache.camel.util.IOHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Bean; @@ -42,7 +44,7 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; -@SpringBootApplication +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCamelIntegrationsTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCamelIntegrationsTest.java new file mode 100644 index 00000000000..29e5520165d --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCamelIntegrationsTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import static io.restassured.RestAssured.given; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpCamelIntegrationsTest.class, SpringBootPlatformHttpCamelIntegrationsTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpCamelIntegrationsTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + rest().post("message").to("direct:route"); + from("direct:route") + .choice() + .when().jsonpath("$[?(@.counter>0)]") + .setBody(constant("positive")) + .otherwise() + .setBody(constant("negative")) + .end(); + } + }; + } + } + + @Test + public void test() { + String data = "{\"counter\":1}"; + given() + .body(data) + .header("Content-Type", "application/json") + .when() + .post("/message") + .then() + .statusCode(200) + .body(Matchers.is("positive")); + + data = "{\"counter\":0}"; + given() + .body(data) + .header("Content-Type", "application/json") + .when() + .post("/message") + .then() + .statusCode(200) + .body(Matchers.is("negative")); + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java new file mode 100644 index 00000000000..39c9305bc6b --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java @@ -0,0 +1,391 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.platform.http.cookie.CookieConfiguration; +import org.apache.camel.component.platform.http.cookie.CookieHandler; +import org.apache.camel.model.rest.RestBindingMode; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.apache.camel.util.IOHelper; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Random; + +import static io.restassured.RestAssured.given; +import static io.restassured.matcher.RestAssuredMatchers.detailedCookie; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpCertificationTest.class, SpringBootPlatformHttpCertificationTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpCertificationTest extends PlatformHttpBase { + + private static final String postRouteId = "SpringBootPlatformHttpRestDSLTest_mypost"; + private static final String getRouteId = "SpringBootPlatformHttpRestDSLTest_myget"; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + getCamelContext().getStreamCachingStrategy().setSpoolEnabled(true); + rest() + .get("myget").id(getRouteId).to("direct:get") + .post("mypost").id(postRouteId).to("direct:post"); + + from("direct:post").transform().body(String.class, b -> b.toUpperCase()); + from("direct:get").setBody().constant("get") + .log("hello"); + + rest("rest").get("/test") + .consumes("application/json,application/xml") + .produces("application/json,application/xml") + .bindingMode(RestBindingMode.auto) + .to("direct:rest"); + + rest("rest").post("/test") + .consumes("application/json,application/xml") + .produces("application/json,application/xml") + .bindingMode(RestBindingMode.auto) + .to("direct:rest"); + + from("direct:rest") + .setBody(simple("Hello")); + + from("platform-http:/streaming?useStreaming=true") + .log("Done echoing back request body as response body"); + + from("platform-http:/helloStreaming?useStreaming=true") + .transform().simple("Hello ${body}"); + + from("platform-http:/streamingFileRequestResponseBody?useStreaming=true") + .log("Done processing request"); + + from("platform-http:/streamingWithFormUrlEncodedBody?useStreaming=true") + .setBody().simple("foo = ${header.foo}"); + + from("platform-http:/streamingWithSpecificEncoding?useStreaming=true") + .log("Done echoing back request body as response body"); + + from("platform-http:/streamingWithClosedInputStreamResponse?useStreaming=true") + .process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + // Simulate an error processing an input stream by closing it ahead of the response being written + // Verifies the response promise.fail is called correctly + InputStream stream = getClass().getResourceAsStream("/application.properties"); + if (stream != null) { + stream.close(); + } + exchange.getMessage().setBody(stream); + } + }); + + from("platform-http:/streamingWithUnconvertableResponseType?useStreaming=true") + .process(new Processor() { + @Override + public void process(Exchange exchange) { + // Force a type conversion exception and verify the response promise.fail is called correctly + exchange.getMessage().setBody(new TestBean()); + } + }) + .log("Conversion done"); + + from("platform-http:/session") + .setBody().constant("session"); + + from("platform-http:/streamingWithLargeRequestAndResponseBody?useStreaming=true") + .log("Done echoing back request body as response body"); + + from("platform-http:/addCookie?useCookieHandler=true") + .process(exchange -> { + exchange.getProperty(Exchange.COOKIE_HANDLER, CookieHandler.class).addCookie("foo", "bar"); + }) + .setBody().constant("add"); + } + }; + } + } + + @Override + protected String getPostRouteId() { + return postRouteId; + } + + @Override + protected String getGetRouteId() { + return getRouteId; + } + + @Test + public void testLoad() throws Exception { + waitUntilRouteIsStarted(1, getGetRouteId()); + + for (int i = 0; i < 1_000; i++) { + Assertions.assertThat(restTemplate.getForEntity("/myget", String.class).getStatusCode().value()).isEqualTo(200); + } + } + + @Test + public void nonSupportedContentType() { + RestAssured.given() + .header("Content-Type", "notSupported") + .post("rest/test") + .then() + .statusCode(415); + } + + @Test + public void oneContentType() { + RestAssured.given() + .header("Content-type", ContentType.XML) + .header("Accept", ContentType.XML) + .get("rest/test") + .then() + .statusCode(200) + .body(is("Hello")); + } + + @Test + public void noContentTypeOkForGet() { + RestAssured.given() + .get("rest/test") + .then() + .statusCode(200) + .body(is("\"Hello\"")); + } + + @Test + public void nonSupportedAccept() { + RestAssured.given() + .header("Content-type", ContentType.XML) + .header("Accept", ContentType.BINARY) + .get("rest/test") + .then() + .statusCode(406); + } + + @Test + public void streaming() { + String requestBody = "Vert.x Platform HTTP"; + given() + .body(requestBody) + .post("/helloStreaming") + .then() + .statusCode(200) + .body(is("Hello " + requestBody)); + } + + @Test + void streamingFileRequestResponseBody() throws Exception { + String content = "Hello World"; + Path testFile = Files.createTempFile("platform-http-testing", "txt"); + Files.writeString(testFile, content); + + given() + .body(testFile.toFile()) + .post("/streamingFileRequestResponseBody") + .then() + .statusCode(200) + .body(is(content)); + } + + @Test + void streamingWithFormUrlEncodedBody() throws Exception { + given() + .contentType(ContentType.URLENC) + .formParam("foo", "bar") + .post("/streamingWithFormUrlEncodedBody") + .then() + .statusCode(200) + .body(is("foo = bar")); + } + + @Test + void streamingWithSpecificEncoding() throws Exception { + Path input = Files.createTempFile("platform-http-input", "dat"); + Path output = Files.createTempFile("platform-http-output", "dat"); + + String fileContent = "Content with special character ð"; + Files.writeString(input, fileContent, StandardCharsets.ISO_8859_1); + + InputStream response = given() + .body(new FileInputStream(input.toFile())) + .post("/streamingWithSpecificEncoding") + .then() + .statusCode(200) + .extract() + .body() + .asInputStream(); + + try (FileOutputStream fos = new FileOutputStream(output.toFile())) { + IOHelper.copy(response, fos); + } + + assertEquals(fileContent, Files.readString(output, StandardCharsets.ISO_8859_1)); + } + + @Test + @Disabled("Test is failing, work in progress") + void streamingWithClosedInputStreamResponse() throws Exception { + + given() + .get("/streamingWithClosedInputStreamResponse") + .then() + .statusCode(500); + } + + @Test + void streamingWithUnconvertableResponseType() throws Exception { + given() + .get("/streamingWithUnconvertableResponseType") + .then() + .statusCode(500); + } + + static final class TestBean { + } + + @Test + public void session() { + Map<String, String> cookies = given() + .when() + .get("/session") + .then() + .statusCode(200) +// .cookie("vertx-web.session", +// detailedCookie() +// .path("/").value(notNullValue()) +// .httpOnly(false) +// .secured(false) +// .sameSite("Strict")) +// .header("cookie", nullValue()) + .body(equalTo("session")) + .extract().cookies(); + + System.out.println(cookies); + } + + @Autowired + CamelContext camelContext; + + @Test + @DisabledIfSystemProperty(named = "ci.env.name", matches = "apache.org", + disabledReason = "File too large for Apache CI") + void streamingWithLargeRequestAndResponseBody() throws Exception { + camelContext.getStreamCachingStrategy().setSpoolEnabled(true); + + Path input = createLargeFile(); + Path output = Files.createTempFile("platform-http-output", "dat"); + + InputStream response = given() + .body(new FileInputStream(input.toFile())) + .post("/streaming") + .then() + .extract() + .asInputStream(); + + try (FileOutputStream fos = new FileOutputStream(output.toFile())) { + IOHelper.copy(response, fos); + } + + assertEquals(input.toFile().length(), output.toFile().length()); + } + + private Path createLargeFile() throws IOException { + // Create a 4GB file containing random data + Path path = Files.createTempFile("platform-http-input", "dat"); + try (FileOutputStream fos = new FileOutputStream(path.toFile())) { + Random random = new Random(); + long targetFileSize = (long) (4 * Math.pow(1024, 3)); + long bytesWritten = 0L; + + byte[] data = new byte[1024]; + while (bytesWritten < targetFileSize) { + random.nextBytes(data); + fos.write(data); + bytesWritten += data.length; + } + } + return path; + } + + private static CookieHandler getCookieHandler(Exchange exchange) { + return exchange.getProperty(Exchange.COOKIE_HANDLER, CookieHandler.class); + } + + @Test + @Disabled("Test is failing, work in progress") + public void testAddCookie() throws Exception { + given() + .when() + .get("/addCookie") + .then() + .statusCode(200) + .cookie("foo", + detailedCookie() + .value("bar") + .path(CookieConfiguration.DEFAULT_PATH) + .domain((String) null) + .sameSite(CookieConfiguration.DEFAULT_SAME_SITE.getValue())) + .body(equalTo("add")); + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCookiesTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCookiesTest.java new file mode 100644 index 00000000000..830e22381c1 --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCookiesTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import jakarta.servlet.http.Cookie; +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import java.util.Arrays; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpCookiesTest.class, SpringBootPlatformHttpCookiesTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpCookiesTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("platform-http:/add") + .process(exchange -> { + PlatformHttpMessage message = (PlatformHttpMessage) exchange.getMessage(); + message.getResponse().addCookie(new Cookie("foo", "bar")); + }) + .setBody().constant("add"); + + from("platform-http:/remove") + .process(exchange -> { + PlatformHttpMessage message = (PlatformHttpMessage) exchange.getMessage(); + + Cookie cookie = Arrays.stream(message.getRequest().getCookies()).findFirst().get(); + cookie.setMaxAge(0); + cookie.setValue(null); + + message.getResponse().addCookie(cookie); + }) + .setBody().constant("remove"); + + from("platform-http:/replace") + .process(exchange -> { + PlatformHttpMessage message = (PlatformHttpMessage) exchange.getMessage(); + assertEquals(1, message.getRequest().getCookies().length); + + Cookie cookie = new Cookie("XSRF-TOKEN", "88533580000c314"); + cookie.setPath("/"); + message.getResponse().addCookie(cookie); + }) + .setBody().constant("replace"); + + from("platform-http:/echo") + .setBody().constant("echo"); + } + }; + } + } + + @Test + public void addCookie() { + given() + .header("cookie", "foo=bar") + .when() + .get("/add") + .then() + .statusCode(200) + .header("set-cookie", "foo=bar") + .body(equalTo("add")); + } + + @Test + public void removeCookie() { + given() + .header("cookie", "foo=bar") + .when() + .get("/remove") + .then() + .statusCode(200) + .header("set-cookie", startsWith("foo=; Max-Age=0; Expires=")) + .body(equalTo("remove")); + } + + @Test + public void replaceCookie() { + given() + .header("cookie", "XSRF-TOKEN=c359b44aef83415") + .when() + .get("/replace") + .then() + .statusCode(200) + .header("set-cookie", "XSRF-TOKEN=88533580000c314; Path=/") + .body(equalTo("replace")); + } + + @Test + public void echoCookie() { + given() + .header("cookie", "echo=cookie") + .when() + .get("/echo") + .then() + .statusCode(200) + .header("cookie", startsWith("echo=cookie")) + .body(equalTo("echo")); + } + +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java new file mode 100644 index 00000000000..01d3885965e --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import jakarta.activation.DataHandler; +import org.apache.camel.CamelContext; +import org.apache.camel.attachment.AttachmentMessage; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Collections; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpEngineTest.class, SpringBootPlatformHttpEngineTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpEngineTest { + private final static String attachmentId = "myTestFile"; + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + rest() + .post("upload").to("direct:upload"); + + from("direct:upload") + .process(exchange -> { + AttachmentMessage message = exchange.getMessage(AttachmentMessage.class); + DataHandler attachment = message.getAttachment(attachmentId); + message.setBody(attachment.getContent()); + }); + + from("platform-http:/form/post") + .convertBodyTo(String.class); + + from("platform-http:/greeting/{name}?matchOnUriPrefix=true") + .transform().simple("Hello ${header.name}"); + + from("platform-http:/text/post") + .log("POST:/test/post has body ${body}"); + + from("platform-http:/responseHeaders") + .setHeader("nonEmptyFromRoute", constant("nonEmptyFromRouteValue")) + .setHeader("emptyFromRoute", constant("")) + .setBody().simple("Hello World"); + + from("platform-http:/multipleHeaders") + .setHeader("nonEmptyFromRoute", constant("nonEmptyFromRouteValue")) + .setBody().simple("Hello World"); + + from("platform-http:/consumerSuspended") + .routeId("consumerSuspended") + .setBody().constant("get"); + + from("platform-http:/error/response") + // Set the response to something that can't be type converted + .setBody().constant(Collections.EMPTY_SET); + + from("platform-http:/error/query/param") + .setBody().constant("Error"); + } + }; + } + } + + @Test + public void testAttachment() throws Exception { + final String attachmentId = "myTestFile"; + final String fileContent = "Test multipart upload content"; + final File tempFile = File.createTempFile("platform-http", ".txt"); + + Files.write(tempFile.toPath(), fileContent.getBytes(StandardCharsets.UTF_8)); + + given() + .multiPart(attachmentId, tempFile) + .when() + .post("/upload") + .then() + .statusCode(200) + .body(is(fileContent)); + } + + @Test + public void formPost() { + given() + .formParam("foo", "bar") + .formParam("cheese", "wine") + .when() + .post("/form/post") + .then() + .statusCode(200) + .body(is("foo=bar&cheese=wine")); + } + + @Test + @Disabled("Test is failing, work in progress") + public void matchOnUriPrefix() { + final String greeting = "Hello Camel"; + given() + .when() + .get("/greeting") + .then() + .statusCode(404); + + given() + .when() + .get("/greeting/Camel") + .then() + .statusCode(200) + .body(equalTo(greeting)); + + given() + .when() + .get("/greeting/Camel/other/path/") + .then() + .statusCode(200) + .body(equalTo(greeting)); + } + + @Test + public void textContentPost() { + String payload = "Hello World"; + given() + .contentType(ContentType.TEXT) + .body(payload) + .when() + .post("/text/post") + .then() + .statusCode(200) + .body(is(payload)); + } + + @Test + public void responseHeaders() { + RestAssured.given() + .header("nonEmpty", "nonEmptyValue") + .header("empty", "") + .get("/responseHeaders") + .then() + .statusCode(200) + .body(equalTo("Hello World")) + .header("nonEmpty", "nonEmptyValue") + .header("empty", "") + .header("nonEmptyFromRoute", "nonEmptyFromRouteValue") + .header("emptyFromRoute", ""); + } + + @Test + public void multipleHeaders() { + RestAssured.given() + .header("nonEmpty", "nonEmptyValue") + .header("empty", "") + .get("/multipleHeaders?duplicated=1&duplicated=2") + .then() + .statusCode(200) + .body(equalTo("Hello World")) + .header("nonEmpty", "nonEmptyValue") + .header("nonEmptyFromRoute", "nonEmptyFromRouteValue"); + } + + @Test + @Disabled("Test is failing, work in progress") + public void consumerSuspended() throws Exception { + given() + .when() + .get("/consumerSuspended") + .then() + .statusCode(200) + .body(equalTo("get")); + + camelContext.getRouteController().suspendRoute("consumerSuspended"); + + given() + .when() + .get("/get") + .then() + .statusCode(503); + } + + @Test + public void responseTypeConversionErrorHandled() { + get("/error/response") + .then() + .statusCode(500); + } + + @Test + @Disabled("Test is failing, work in progress") + public void responseBadQueryParamErrorHandled() { + get("/error/query/param?::") + .then() + .statusCode(500); + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java index 37a3cb384bb..4be423aab44 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpHandleWriteErrorTest.java @@ -30,6 +30,8 @@ import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,7 +41,7 @@ import org.springframework.test.annotation.DirtiesContext.ClassMode; import java.io.IOException; -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpJacksonConverterTest.java similarity index 51% copy from components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java copy to components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpJacksonConverterTest.java index b6761b9877d..afda83e5c9d 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpJacksonConverterTest.java @@ -16,31 +16,50 @@ */ package org.apache.camel.component.platform.http.springboot; +import io.restassured.RestAssured; +import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jackson.JacksonConstants; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -@EnableAutoConfiguration -@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, - SpringBootPlatformHttpRestDSLTest.class, SpringBootPlatformHttpRestDSLTest.TestConfiguration.class, + SpringBootPlatformHttpJacksonConverterTest.class, SpringBootPlatformHttpJacksonConverterTest.TestConfiguration.class, PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) -public class SpringBootPlatformHttpRestDSLTest extends PlatformHttpBase { +public class SpringBootPlatformHttpJacksonConverterTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; - private static final String postRouteId = "SpringBootPlatformHttpRestDSLTest_mypost"; + @LocalServerPort + private Integer port; - private static final String getRouteId = "SpringBootPlatformHttpRestDSLTest_myget"; + @BeforeEach + void setUp() { + RestAssured.port = port; + } - // ************************************* - // Config - // ************************************* @Configuration public static class TestConfiguration { @@ -49,24 +68,23 @@ public class SpringBootPlatformHttpRestDSLTest extends PlatformHttpBase { return new RouteBuilder() { @Override public void configure() { - rest() - .get("myget").id(getRouteId).to("direct:get") - .post("mypost").id(postRouteId).to("direct:post"); + getContext().getGlobalOptions().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); - from("direct:post").transform().body(String.class, b -> b.toUpperCase()); - from("direct:get").setBody().constant("get"); + from("platform-http:/hello") + .setBody().constant("{\"hello\": \"world\"}") + .unmarshal().json(); } }; } } - @Override - protected String getPostRouteId() { - return postRouteId; - } - - @Override - protected String getGetRouteId() { - return getRouteId; + @Test + public void jacksonTypeConverter() { + given() + .when() + .get("/hello") + .then() + .statusCode(200) + .body(equalTo("{\"hello\":\"world\"}")); } -} +} \ No newline at end of file diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpMultipleExecutorsTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpMultipleExecutorsTest.java index 7e51f3644db..7d0816d7c52 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpMultipleExecutorsTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpMultipleExecutorsTest.java @@ -23,6 +23,8 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,7 +35,7 @@ import org.springframework.test.annotation.DirtiesContext; import java.util.List; import java.util.concurrent.Executor; -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java new file mode 100644 index 00000000000..fe75c7ad177 --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import com.github.tomakehurst.wiremock.client.WireMock; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.wiremock.spring.ConfigureWireMock; +import org.wiremock.spring.EnableWireMock; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsString; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpProxyTest.class, SpringBootPlatformHttpProxyTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +@EnableWireMock(@ConfigureWireMock(portProperties = "customPort")) +public class SpringBootPlatformHttpProxyTest { + + @Value("${wiremock.server.baseUrl}") + private String wiremockUrl; + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + WireMock.stubFor(get(urlPathEqualTo("/")) + .willReturn(aResponse() + .withBody( + "{\"message\": \"Hello World\"}"))); + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("platform-http:proxy?useStreaming=false") + .log("${headers}") + .toD("${headers." + Exchange.HTTP_URI + "}?bridgeEndpoint=true"); + } + }; + } + } + + @Test + @Disabled("Test is failing, work in progress") + public void httpProxy() { + final var proxyURI = "http://localhost:" + RestAssured.port; + + final var originURI = wiremockUrl; + + given() + .proxy(proxyURI) + .contentType(ContentType.JSON) + .when().get(originURI) + .then() + .statusCode(200) + .body(containsString("{\"message\": \"Hello World\"}")); + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java index b6761b9877d..c9e4b5b17f6 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpRestDSLTest.java @@ -20,13 +20,15 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSessionTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSessionTest.java new file mode 100644 index 00000000000..5f09e6a7d01 --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSessionTest.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import io.restassured.http.Cookie; +import io.restassured.internal.ValidatableResponseImpl; +import io.restassured.response.Response; +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.test.annotation.DirtiesContext; + +import static io.restassured.RestAssured.given; + +@EnableAutoConfiguration +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpSessionTest.class, SpringBootPlatformHttpSessionTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpSessionTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + @EnableWebSecurity + public static class TestConfiguration { + + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.builder() + .username("user") + .password("{noop}password") + .authorities("USER") + .build(); + + UserDetails admin = User.builder() + .username("admin") + .password("{noop}password") + .authorities("ADMIN") + .build(); + + return new InMemoryUserDetailsManager(user, admin); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable()) + .authorizeHttpRequests(authorizeRequests -> + authorizeRequests.anyRequest().authenticated()) + .httpBasic(Customizer.withDefaults()) + .build(); + } + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + rest("/authenticated") + .post("/first") + .to("direct:first") + .get("/second") + .to("direct:second"); + + from("direct:first") + .process(exchange -> { + PlatformHttpMessage message = (PlatformHttpMessage) exchange.getMessage(); + message.getRequest().getSession() + .setAttribute("json-attribute", exchange.getMessage().getBody(String.class)); + }); + + from("direct:second") + .process(exchange -> { + PlatformHttpMessage message = (PlatformHttpMessage) exchange.getMessage(); + + exchange.getIn().setBody( + message.getRequest().getSession().getAttribute("json-attribute")); + }); + } + }; + } + } + + protected String getSessionKey() { + return "JSESSIONID"; + } + + @Test + public void session() { + String jsonAttribute = """ + {"test":"attribute"} + """; + + var result = given() + .auth().basic("user", "password") + .body(jsonAttribute) + .when() + .post("/authenticated/first") + .then() + .statusCode(200) + .cookie(getSessionKey()); + + String session = ((ValidatableResponseImpl) result).originalResponse().cookies().get(getSessionKey()); + + result = given() + .auth().basic("user", "password") + .cookie(new Cookie.Builder(getSessionKey(), session).build()) + .when() + .get("/authenticated/second") + .then() + .statusCode(200) + .body(Matchers.is(jsonAttribute)); + + Response response = ((ValidatableResponseImpl) result).originalResponse(); + + result = given() + .auth().basic("admin", "password") + .when() + .get("/authenticated/second") + .then() + .statusCode(204) + .body(Matchers.is("")); + + response = ((ValidatableResponseImpl) result).originalResponse(); + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSpringSessionTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSpringSessionTest.java new file mode 100644 index 00000000000..531bb8c8f8e --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpSpringSessionTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import com.hazelcast.config.*; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository; +import org.springframework.session.hazelcast.PrincipalNameExtractor; +import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession; +import org.springframework.test.annotation.DirtiesContext; + +@EnableAutoConfiguration +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpSpringSessionTest.class, SpringBootPlatformHttpSpringSessionTest.TestConfiguration.class, + SpringBootPlatformHttpSessionTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpSpringSessionTest extends SpringBootPlatformHttpSessionTest { + + @Override + protected String getSessionKey() { + return "SESSION"; + } + + @Configuration + @EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 150) + public static class TestConfiguration { + + @Bean(destroyMethod = "shutdown") + public HazelcastInstance hazelcastInstance() { + Config config = new Config(); + NetworkConfig networkConfig = config.getNetworkConfig(); + networkConfig.setPort(0); + networkConfig.getJoin().getAutoDetectionConfig().setEnabled(false); + AttributeConfig attributeConfig = new AttributeConfig() + .setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE) + .setExtractorClassName(PrincipalNameExtractor.class.getName()); + config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) + .addAttributeConfig(attributeConfig) + .addIndexConfig( + new IndexConfig(IndexType.HASH, HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)); + return Hazelcast.newHazelcastInstance(config); + } + } +} diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTest.java index 77355b21277..a844a15f2a8 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTest.java @@ -20,13 +20,15 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) @CamelSpringBootTest @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTypeConverterTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTypeConverterTest.java new file mode 100644 index 00000000000..2b567e130c9 --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpTypeConverterTest.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import org.apache.camel.*; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpTypeConverterTest.class, SpringBootPlatformHttpTypeConverterTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpTypeConverterTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + getContext().getTypeConverterRegistry().addTypeConverter(InputStream.class, + Map.class, + new InputStreamTypeConverter()); + + from("platform-http:/inputstream") + .routeId("inputstream") + .setBody().constant(Collections.singletonMap("is", "my-test")); + } + }; + } + } + + @Test + public void customTypeConverter() { + given() + .when() + .get("/inputstream") + .then() + .statusCode(200) + .body(equalTo("InputStream:my-test")); + } + + static class InputStreamTypeConverter implements TypeConverter { + @Override + public boolean allowNull() { + return false; + } + + @Override + public <T> T convertTo(Class<T> type, Object value) throws TypeConversionException { + return null; + } + + @Override + public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException { + byte[] out = ("InputStream:" + ((Map) value).get("is")).getBytes(StandardCharsets.UTF_8); + return type.cast(new ByteArrayInputStream(out)); + } + + @Override + public <T> T mandatoryConvertTo(Class<T> type, Object value) throws TypeConversionException, NoTypeConversionAvailableException { + return null; + } + + @Override + public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException, NoTypeConversionAvailableException { + return null; + } + + @Override + public <T> T tryConvertTo(Class<T> type, Object value) { + return null; + } + + @Override + public <T> T tryConvertTo(Class<T> type, Exchange exchange, Object value) { + return null; + } + } +} \ No newline at end of file diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java new file mode 100644 index 00000000000..4e3d1ed7983 --- /dev/null +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.platform.http.springboot; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.response.ValidatableResponse; +import io.restassured.specification.RequestSpecification; +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.platform.http.spi.Method; +import org.apache.camel.model.rest.RestBindingMode; +import org.apache.camel.model.rest.RestParamType; +import org.apache.camel.spring.boot.CamelAutoConfiguration; +import org.apache.camel.test.spring.junit5.CamelSpringBootTest; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; + +import java.util.List; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.*; + +@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@CamelSpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class, + SpringBootPlatformHttpValidationTest.class, SpringBootPlatformHttpValidationTest.TestConfiguration.class, + PlatformHttpComponentAutoConfiguration.class, SpringBootPlatformHttpAutoConfiguration.class, }) +public class SpringBootPlatformHttpValidationTest { + + @Autowired + TestRestTemplate restTemplate; + + @Autowired + CamelContext camelContext; + + @LocalServerPort + private Integer port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Configuration + public static class TestConfiguration { + + @Bean + public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + rest("/rest") + .post("/validate/body") + .clientRequestValidation(true) + .param().name("body").type(RestParamType.body).required(true).endParam() + .to("direct:rest"); + from("direct:rest") + .setBody(simple("Hello ${body}")); + + from("platform-http:/echo") + .setBody().simple("${body}"); + + from("platform-http:/test") + .setBody().simple("Hello ${body[method]}"); + + rest("/invalidContentTypeClientRequestValidation") + .clientRequestValidation(true) + .bindingMode(RestBindingMode.json) + .post("/validate/body") + .consumes("text/plain") + .produces("application/json") + .to("direct:invalidContentTypeClientRequestValidation"); + from("direct:invalidContentTypeClientRequestValidation") + .setBody(simple("Hello ${body}")); + } + }; + } + } + + @Test + public void requestValidation() throws Exception { + given() + .when() + .post("/rest/validate/body") + .then() + .statusCode(400) + .body(is("The request body is missing.")); + + given() + .body(" ") + .when() + .post("/rest/validate/body") + .then() + .statusCode(400) + .body(is("The request body is missing.")); + + given() + .body("Camel Platform HTTP Vert.x") + .when() + .post("/rest/validate/body") + .then() + .statusCode(200) + .body(is("Hello Camel Platform HTTP Vert.x")); + } + + @Test + @Disabled("Test is failing, work in progress") + public void requestBodyAllowed() { + for (Method method : Method.values()) { + ValidatableResponse validatableResponse = given() + .contentType(ContentType.JSON) + .when() + .body("{\"method\": \"" + method + "\"}") + .request(method.name(), "/echo") + .then() + .statusCode(200); + + Matcher<String> expectedBody; + if (method.equals(Method.HEAD)) { + // HEAD response body is ignored + validatableResponse.body(emptyString()); + } else { + validatableResponse.body("method", equalTo(method.name())); + } + } + } + + @Test + @Disabled("Test is failing, work in progress") + public void requestBodyAllowedFormUrlEncoded() { + final List<Method> methodsWithBodyAllowed = List.of(Method.POST, Method.PUT, Method.PATCH, Method.DELETE); + + RequestSpecification request = given() + .when() + .contentType(ContentType.URLENC); + + for (Method method : Method.values()) { + if (methodsWithBodyAllowed.contains(method)) { + request.body("method=" + method) + .request(method.name(), "/test") + .then() + .statusCode(200) + .body(equalTo("Hello " + method)); + } else { + request.body(method) + .request(method.name(), "/test") + .then() + .statusCode(500); + } + } + } + + @Test + public void invalidContentTypeClientRequestValidation() { + given() + .when() + .body("{\"name\": \"Donald\"}") + .contentType("application/json") + .post("/invalidContentTypeClientRequestValidation/validate/body") + .then() + .statusCode(415); + } +} diff --git a/pom.xml b/pom.xml index c4e05800c76..48ee423ea2a 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,8 @@ <surefire.version>${maven-surefire-plugin-version}</surefire.version> <swagger-parser-v3-version>2.1.10</swagger-parser-v3-version> <cyclonedx-maven-plugin-version>2.8.1</cyclonedx-maven-plugin-version> + <wiremock-spring-boot-version>3.0.2</wiremock-spring-boot-version> + <spring-session-hazelcast-version>3.3.2</spring-session-hazelcast-version> </properties>
