diff --git a/integration-tests-jvm/elasticsearch/pom.xml b/integration-tests-jvm/elasticsearch/pom.xml
index 5acd50c435fb..15bd3c7550ee 100644
--- a/integration-tests-jvm/elasticsearch/pom.xml
+++ b/integration-tests-jvm/elasticsearch/pom.xml
@@ -33,6 +33,10 @@
Integration tests for Camel Quarkus Elasticsearch extension
+
+ org.apache.camel.quarkus
+ camel-quarkus-direct
+
org.apache.camel.quarkus
camel-quarkus-elasticsearch
@@ -53,6 +57,27 @@
rest-assured
test
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+ junit
+ junit
+
+
+
+
+ io.quarkus
+ quarkus-junit4-mock
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
@@ -65,6 +90,19 @@
+
+ org.apache.camel.quarkus
+ camel-quarkus-direct-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
org.apache.camel.quarkus
camel-quarkus-elasticsearch-deployment
@@ -80,5 +118,16 @@
+
+ skip-testcontainers-tests
+
+
+ skip-testcontainers-tests
+
+
+
+ true
+
+
diff --git a/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchResource.java b/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchResource.java
new file mode 100644
index 000000000000..047266cd9125
--- /dev/null
+++ b/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchResource.java
@@ -0,0 +1,298 @@
+/*
+ * 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.quarkus.component.elasticsearch.it;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchPhraseQuery;
+import co.elastic.clients.elasticsearch.core.BulkRequest;
+import co.elastic.clients.elasticsearch.core.GetResponse;
+import co.elastic.clients.elasticsearch.core.MsearchRequest;
+import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
+import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
+import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
+import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
+import co.elastic.clients.elasticsearch.core.msearch.MultisearchBody;
+import co.elastic.clients.elasticsearch.core.msearch.MultisearchHeader;
+import co.elastic.clients.elasticsearch.core.msearch.RequestItem;
+import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
+import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PATCH;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.component.es.ElasticsearchConstants;
+
+@Path("/elasticsearch")
+@ApplicationScoped
+public class ElasticsearchResource {
+
+ private static final String HEADER_COMPONENT = "component";
+
+ @Inject
+ FluentProducerTemplate fluentProducerTemplate;
+
+ @Path("/get")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response getData(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName,
+ @QueryParam("indexId") String indexId,
+ @QueryParam("indexKey") String indexKey) {
+
+ GetResponse> response = fluentProducerTemplate.to("direct:get")
+ .withBody(indexId)
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_NAME, indexName)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(GetResponse.class);
+
+ if (response.source() == null || !(response.source() instanceof ObjectNode)) {
+ return Response.status(404).build();
+ }
+
+ return Response.ok().entity(((ObjectNode) response.source()).get(indexKey).asText()).build();
+ }
+
+ @Path("/index")
+ @POST
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response indexData(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName,
+ @QueryParam("indexKey") String indexKey,
+ String indexValue) throws Exception {
+
+ String indexId = fluentProducerTemplate.to("direct:index")
+ .withBody(createIndexedData(indexKey, indexValue))
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_NAME, indexName)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(String.class);
+
+ return Response
+ .created(new URI("https://camel.apache.org/"))
+ .entity(indexId)
+ .build();
+ }
+
+ @Path("/update")
+ @PATCH
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response updateData(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName,
+ @QueryParam("indexId") String indexId,
+ @QueryParam("indexKey") String indexKey,
+ String indexValue) {
+
+ fluentProducerTemplate.to("direct:update")
+ .withBody(Map.of("doc", createIndexedData(indexKey, indexValue)))
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_NAME, indexName)
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_ID, indexId)
+ .withHeader(HEADER_COMPONENT, component)
+ .request();
+
+ return Response.ok().build();
+ }
+
+ @Path("/delete")
+ @DELETE
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response deleteData(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName,
+ @QueryParam("indexId") String indexId) {
+
+ fluentProducerTemplate.to("direct:delete")
+ .withBody(indexId)
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_NAME, indexName)
+ .withHeader(HEADER_COMPONENT, component)
+ .request();
+
+ return Response.noContent().build();
+ }
+
+ @Path("/delete/index")
+ @DELETE
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response deleteIndexData(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName) {
+
+ DeleteIndexRequest.Builder request = new DeleteIndexRequest.Builder().index(indexName);
+
+ Boolean result = fluentProducerTemplate.to("direct:deleteIndex")
+ .withBody(request)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(Boolean.class);
+
+ return Response.ok(result).build();
+ }
+
+ @Path("/ping")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response ping(@QueryParam("component") String component) {
+ Boolean result = fluentProducerTemplate.to("direct:ping")
+ .withHeader(HEADER_COMPONENT, component)
+ .request(Boolean.class);
+
+ return Response.ok(result).build();
+ }
+
+ @Path("/exists")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response exists(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName) {
+
+ Boolean result = fluentProducerTemplate.to("direct:exists")
+ .withHeader(ElasticsearchConstants.PARAM_INDEX_NAME, indexName)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(Boolean.class);
+
+ return Response.ok(result).build();
+ }
+
+ @Path("/bulk")
+ @POST
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response bulk(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName) {
+
+ BulkRequest.Builder request = new BulkRequest.Builder();
+ request.operations(new BulkOperation.Builder()
+ .index(new IndexOperation.Builder<>().index(indexName).document(Map.of("camel", "quarkus")).build()).build());
+
+ BulkResponseItem[] result = fluentProducerTemplate.to("direct:bulk")
+ .withBody(request)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(BulkResponseItem[].class);
+
+ return Response.ok(result[0].id()).build();
+ }
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response searchByMap(
+ @QueryParam("component") String component,
+ @QueryParam("indexKey") String indexKey,
+ String searchString) {
+
+ Map actualQuery = new HashMap<>();
+ actualQuery.put(indexKey, searchString);
+
+ Map match = new HashMap<>();
+ match.put("match", actualQuery);
+
+ Map query = new HashMap<>();
+ query.put("query", match);
+
+ HitsMetadata> result = fluentProducerTemplate.to("direct:search")
+ .withBody(query)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(HitsMetadata.class);
+
+ if (result.hits().size() > 0) {
+ Object source = result.hits().get(0).source();
+ if (source instanceof ObjectNode) {
+ return Response.ok(((ObjectNode) source).get(indexKey).asText()).build();
+ }
+ }
+ // return OK as it is called in loop
+ return Response.ok().build();
+ }
+
+ @Path("/search/json")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response searchByJSON(
+ @QueryParam("component") String component,
+ @QueryParam("indexKey") String indexKey,
+ String searchString) {
+
+ String query = "{\"query\":{\"match\":{\"%s\":\"%s\"}}}";
+
+ HitsMetadata> result = fluentProducerTemplate.to("direct:search")
+ .withBody(String.format(query, indexKey, searchString))
+ .withHeader(HEADER_COMPONENT, component)
+ .request(HitsMetadata.class);
+
+ if (result.hits().size() > 0) {
+ Object source = result.hits().get(0).source();
+ if (source instanceof ObjectNode) {
+ return Response.ok(((ObjectNode) source).get(indexKey).asText()).build();
+ }
+ }
+ // return OK as it is called in loop
+ return Response.ok().build();
+ }
+
+ @Path("/search/multi")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response searchMulti(
+ @QueryParam("component") String component,
+ @QueryParam("indexName") String indexName,
+ @QueryParam("indexKey") String indexKey,
+ String searchStrings) {
+
+ final String[] searchTerms = searchStrings.split(",");
+ MsearchRequest.Builder builder = new MsearchRequest.Builder().index(indexName);
+ for (String searchTerm : searchTerms) {
+ builder.searches(new RequestItem.Builder().header(new MultisearchHeader.Builder().build())
+ .body(new MultisearchBody.Builder().query(q -> q
+ .matchPhrase(new MatchPhraseQuery.Builder().field(indexKey).query(searchTerm).build()))
+ .build())
+ .build());
+ }
+
+ MultiSearchResponseItem[] result = fluentProducerTemplate.to("direct:multiSearch")
+ .withBody(builder)
+ .withHeader(HEADER_COMPONENT, component)
+ .request(MultiSearchResponseItem[].class);
+
+ if (result.length > 0) {
+ int totalHits = 0;
+ for (MultiSearchResponseItem item : result) {
+ totalHits += item.result().hits().total().value();
+ }
+ return Response.ok(totalHits).build();
+ }
+ return Response.ok().build();
+ }
+
+ private Map createIndexedData(String indexKey, String indexValue) {
+ Map map = new HashMap<>();
+ map.put(indexKey, indexValue);
+ return map;
+ }
+}
diff --git a/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchRoutes.java b/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchRoutes.java
new file mode 100644
index 000000000000..e9177e341c3b
--- /dev/null
+++ b/integration-tests-jvm/elasticsearch/src/main/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchRoutes.java
@@ -0,0 +1,59 @@
+/*
+ * 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.quarkus.component.elasticsearch.it;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class ElasticsearchRoutes extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:bulk")
+ .toD("${header.component}://elasticsearch?operation=Bulk");
+
+ from("direct:delete")
+ .toD("${header.component}://elasticsearch?operation=Delete");
+
+ from("direct:deleteIndex")
+ .toD("${header.component}://elasticsearch?operation=DeleteIndex");
+
+ from("direct:exists")
+ .toD("${header.component}://elasticsearch?operation=Exists");
+
+ from("direct:get")
+ .toD("${header.component}://elasticsearch?operation=GetById");
+
+ from("direct:index")
+ .toD("${header.component}://elasticsearch?operation=Index");
+
+ from("direct:multiGet")
+ .toD("${header.component}://elasticsearch?operation=MultiGet");
+
+ from("direct:multiSearch")
+ .toD("${header.component}://elasticsearch?operation=MultiSearch");
+
+ from("direct:ping")
+ .toD("${header.component}://elasticsearch?operation=Ping");
+
+ from("direct:search")
+ .toD("${header.component}://elasticsearch?operation=Search");
+
+ from("direct:update")
+ .toD("${header.component}://elasticsearch?operation=Update");
+ }
+
+}
diff --git a/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTest.java b/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTest.java
new file mode 100644
index 000000000000..59559b8e8867
--- /dev/null
+++ b/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTest.java
@@ -0,0 +1,365 @@
+/*
+ * 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.quarkus.component.elasticsearch.it;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.Matchers.is;
+
+@QuarkusTest
+@QuarkusTestResource(ElasticsearchTestResource.class)
+class ElasticsearchTest {
+
+ @AfterEach
+ public void afterEach() {
+ // Clean up all indexed data
+ RestAssured.given()
+ .queryParam("component", "elasticsearch")
+ .queryParam("indexName", "_all")
+ .delete("/elasticsearch/delete/index")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchBasicOperations(String component) {
+ String indexName = UUID.randomUUID().toString();
+ String indexKey = "test-key";
+ String indexValue = "Hello Camel Quarkus ElasticSearch";
+
+ // Verify the ElasticSearch server is available
+ RestAssured.given()
+ .queryParam("component", component)
+ .get("/elasticsearch/ping")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Index data
+ String indexId = RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .contentType(ContentType.TEXT)
+ .body(indexValue)
+ .post("/elasticsearch/index")
+ .then()
+ .statusCode(201)
+ .extract()
+ .body()
+ .asString();
+
+ // Verify index exists
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Retrieve indexed data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexId", indexId)
+ .queryParam("indexKey", indexKey)
+ .get("/elasticsearch/get")
+ .then()
+ .statusCode(200)
+ .body(is(indexValue));
+
+ // Update indexed data
+ String updatedIndexValue = indexValue + " Updated";
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .queryParam("component", component)
+ .queryParam("indexId", indexId)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .body(updatedIndexValue)
+ .patch("/elasticsearch/update")
+ .then()
+ .statusCode(200);
+
+ // Verify updated data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexId", indexId)
+ .queryParam("indexKey", indexKey)
+ .get("/elasticsearch/get")
+ .then()
+ .statusCode(200)
+ .body(is(updatedIndexValue));
+
+ // Delete indexed data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexId", indexId)
+ .delete("/elasticsearch/delete")
+ .then()
+ .statusCode(204);
+
+ // Verify data deleted
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexId", indexId)
+ .queryParam("indexKey", indexKey)
+ .get("/elasticsearch/get")
+ .then()
+ .statusCode(404);
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchBulk(String component) {
+ String indexName = UUID.randomUUID().toString();
+
+ String indexId = RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .post("/elasticsearch/bulk")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexId", indexId)
+ .queryParam("indexKey", "camel")
+ .get("/elasticsearch/get")
+ .then()
+ .statusCode(200)
+ .body(is("quarkus"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchDeleteIndex(String component) {
+ String indexName = UUID.randomUUID().toString();
+ String indexKey = "test-key";
+ String indexValue = "Hello Camel Quarkus ElasticSearch";
+
+ // Index data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .contentType(ContentType.TEXT)
+ .body(indexValue)
+ .post("/elasticsearch/index")
+ .then()
+ .statusCode(201)
+ .extract()
+ .body()
+ .asString();
+
+ // Verify index exists
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Delete indexed data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .delete("/elasticsearch/delete/index")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchSearch(String component) {
+ String indexName = UUID.randomUUID().toString();
+ String indexKey = "camel-quarkus";
+ String indexValue = "Sub Atomic, Super Fast Camel Quarkus";
+
+ // Index data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .contentType(ContentType.TEXT)
+ .body(indexValue)
+ .post("/elasticsearch/index")
+ .then()
+ .statusCode(201)
+ .extract()
+ .body()
+ .asString();
+
+ // Verify index exists
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Search data
+ Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(10, TimeUnit.SECONDS).until(() -> {
+ String searchResult = RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexKey", indexKey)
+ .body("Super Fast")
+ .get("/elasticsearch/search")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+ return searchResult.equals(indexValue);
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchSearchJSON(String component) {
+ String indexName = UUID.randomUUID().toString();
+ String indexKey = "camel-quarkus";
+ String indexValue = "Sub Atomic, Super Fast Camel Quarkus";
+
+ // Index data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .contentType(ContentType.TEXT)
+ .body(indexValue)
+ .post("/elasticsearch/index")
+ .then()
+ .statusCode(201)
+ .extract()
+ .body()
+ .asString();
+
+ // Verify index exists
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Search data
+ Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(10, TimeUnit.SECONDS).until(() -> {
+ String searchResult = RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexKey", indexKey)
+ .body("Super Fast")
+ .get("/elasticsearch/search/json")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+ return searchResult.equals(indexValue);
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("componentNames")
+ public void testElasticsearchMultiSearch(String component) {
+ String indexName = UUID.randomUUID().toString();
+ String indexKey = "camel-quarkus";
+ String indexValue = "Sub Atomic, Super Fast Camel Quarkus";
+
+ // Index data
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .contentType(ContentType.TEXT)
+ .body(indexValue)
+ .post("/elasticsearch/index")
+ .then()
+ .statusCode(201)
+ .extract()
+ .body()
+ .asString();
+
+ // Verify index exists
+ RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .get("/elasticsearch/exists")
+ .then()
+ .statusCode(200)
+ .body(is("true"));
+
+ // Search data
+ Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(100, TimeUnit.SECONDS).until(() -> {
+ String hits = RestAssured.given()
+ .queryParam("component", component)
+ .queryParam("indexName", indexName)
+ .queryParam("indexKey", indexKey)
+ .body("Sub Atomic,Super Fast,Nonsense")
+ .get("/elasticsearch/search/multi")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+ return hits.equals("2");
+ });
+ }
+
+ /**
+ * This method returns array of component names used in test routes.
+ * It can be handy e.g. for testing quarkus managed elasticsearch client.
+ *
+ * @return Component name used in route.
+ */
+ @SuppressWarnings("unused")
+ private static String[] componentNames() {
+ return new String[] { "elasticsearch" };
+ }
+}
diff --git a/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTestResource.java b/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTestResource.java
new file mode 100644
index 000000000000..b449938b1a78
--- /dev/null
+++ b/integration-tests-jvm/elasticsearch/src/test/java/org/apache/camel/quarkus/component/elasticsearch/it/ElasticsearchTestResource.java
@@ -0,0 +1,83 @@
+/*
+ * 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.quarkus.component.elasticsearch.it;
+
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.apache.camel.util.CollectionHelper;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.TestcontainersConfiguration;
+
+public class ElasticsearchTestResource implements QuarkusTestResourceLifecycleManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchTestResource.class);
+ private static final String ELASTICSEARCH_IMAGE = ConfigProvider.getConfig().getValue("elasticsearch.container.image",
+ String.class);
+ private static final String ELASTICSEARCH_USERNAME = "elastic";
+ private static final String ELASTICSEARCH_PASSWORD = "changeme";
+ private static final int ELASTICSEARCH_PORT = 9200;
+
+ private GenericContainer> container;
+
+ @Override
+ public Map start() {
+ LOGGER.info(TestcontainersConfiguration.getInstance().toString());
+
+ try {
+ container = new GenericContainer<>(ELASTICSEARCH_IMAGE)
+ .withExposedPorts(ELASTICSEARCH_PORT)
+ .withLogConsumer(new Slf4jLogConsumer(LOGGER))
+ .withEnv("discovery.type", "single-node")
+ .withEnv("xpack.security.enabled", "true")
+ .withEnv("action.destructive_requires_name", "false") // needed for deleting all indexes after each test (allowing _all wildcard)
+ .withEnv("ELASTIC_USERNAME", ELASTICSEARCH_USERNAME)
+ .withEnv("ELASTIC_PASSWORD", ELASTICSEARCH_PASSWORD)
+ .waitingFor(Wait.forListeningPort());
+
+ container.start();
+
+ String hostAddresses = String.format("localhost:%s", container.getMappedPort(ELASTICSEARCH_PORT));
+
+ // Component configuration where the ElasticSearch client is managed by Camel (E.g autowiring disabled)
+ return CollectionHelper.mapOf(
+ // camel
+ "camel.component.elasticsearch.host-addresses", hostAddresses,
+ "camel.component.elasticsearch.user", ELASTICSEARCH_USERNAME,
+ "camel.component.elasticsearch.password", ELASTICSEARCH_PASSWORD);
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ if (container != null) {
+ container.stop();
+ }
+ } catch (Exception e) {
+ // Ignored
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 9433316c346b..818931a834c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -217,6 +217,7 @@
docker.io/couchdb:2.3.1
docker.io/az82/docker-derby:10.16
docker.io/eclipse-mosquitto:1.6.15
+ docker.io/elasticsearch:8.8.1
docker.io/hapiproject/hapi
${fhir.container.image.base}:v6.6.0
${fhir.container.image.base}:v4.2.0