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