From 9909d3be1e7e5f7d21074dca80dd2a84ff8244ef Mon Sep 17 00:00:00 2001 From: James Netherton Date: Fri, 7 Jun 2024 11:24:44 +0100 Subject: [PATCH] Increase elasticsearch-rest-client test coverage Fixes #5737 Fixes #6163 Fixes #6164 --- .../deployment/pom.xml | 2 +- .../elasticsearch-rest-client/runtime/pom.xml | 2 +- .../elasticsearch-rest-client/pom.xml | 48 +++ .../rest/client/it/Document.java | 58 +++ .../it/ElasticsearchRestClientResource.java | 151 ++++++-- .../it/ElasticsearchRestClientRoutes.java | 65 ++++ .../src/main/resources/application.properties | 17 + .../it/ElasticsearchRestClientTest.java | 360 +++++++++++++++++- .../it/ElasticsearchRestTestResource.java | 136 +++++++ .../client/it/ElasticsearchTestResource.java | 83 ---- pom.xml | 2 +- 11 files changed, 799 insertions(+), 125 deletions(-) create mode 100644 integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/Document.java create mode 100644 integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientRoutes.java create mode 100644 integration-tests-jvm/elasticsearch-rest-client/src/main/resources/application.properties create mode 100644 integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestTestResource.java delete mode 100644 integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchTestResource.java diff --git a/extensions-jvm/elasticsearch-rest-client/deployment/pom.xml b/extensions-jvm/elasticsearch-rest-client/deployment/pom.xml index fa40e7f750cf..c9b1c305ee1e 100644 --- a/extensions-jvm/elasticsearch-rest-client/deployment/pom.xml +++ b/extensions-jvm/elasticsearch-rest-client/deployment/pom.xml @@ -40,7 +40,7 @@ io.quarkus - quarkus-elasticsearch-rest-client-deployment + quarkus-elasticsearch-rest-client-common-deployment diff --git a/extensions-jvm/elasticsearch-rest-client/runtime/pom.xml b/extensions-jvm/elasticsearch-rest-client/runtime/pom.xml index 600523c6eeaa..771e9880b4cb 100644 --- a/extensions-jvm/elasticsearch-rest-client/runtime/pom.xml +++ b/extensions-jvm/elasticsearch-rest-client/runtime/pom.xml @@ -45,7 +45,7 @@ io.quarkus - quarkus-elasticsearch-rest-client + quarkus-elasticsearch-rest-client-common diff --git a/integration-tests-jvm/elasticsearch-rest-client/pom.xml b/integration-tests-jvm/elasticsearch-rest-client/pom.xml index 4fd8790a4df2..366830148b12 100644 --- a/integration-tests-jvm/elasticsearch-rest-client/pom.xml +++ b/integration-tests-jvm/elasticsearch-rest-client/pom.xml @@ -31,14 +31,26 @@ Integration tests for Camel Quarkus Elasticsearch Low level Rest Client extension + + org.apache.camel.quarkus + camel-quarkus-direct + org.apache.camel.quarkus camel-quarkus-elasticsearch-rest-client + + org.apache.camel.quarkus + camel-quarkus-jackson + io.quarkus quarkus-resteasy + + io.quarkus + quarkus-resteasy-jackson + @@ -67,6 +79,16 @@ quarkus-junit4-mock test + + org.awaitility + awaitility + test + + + org.apache.camel.quarkus + camel-quarkus-integration-tests-support-certificate-generator + test + @@ -79,6 +101,19 @@ + + org.apache.camel.quarkus + camel-quarkus-direct-deployment + ${project.version} + pom + test + + + * + * + + + org.apache.camel.quarkus camel-quarkus-elasticsearch-rest-client-deployment @@ -92,6 +127,19 @@ + + org.apache.camel.quarkus + camel-quarkus-jackson-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/Document.java b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/Document.java new file mode 100644 index 000000000000..2a57725bc163 --- /dev/null +++ b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/Document.java @@ -0,0 +1,58 @@ +/* + * 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.rest.client.it; + +import java.util.Objects; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection(fields = false) +public class Document { + private String id; + private String value; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Document document = (Document) o; + return Objects.equals(id, document.id) && Objects.equals(value, document.value); + } + + @Override + public int hashCode() { + return Objects.hash(id, value); + } +} diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientResource.java b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientResource.java index d7bb6592bc5f..27c8195691ab 100644 --- a/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientResource.java +++ b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientResource.java @@ -16,60 +16,147 @@ */ package org.apache.camel.quarkus.component.elasticsearch.rest.client.it; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; 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.CamelContext; -import org.apache.camel.ProducerTemplate; -import org.jboss.logging.Logger; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.FluentProducerTemplate; +import org.apache.camel.component.elasticsearch.rest.client.ElasticSearchRestClientConstant; +import org.elasticsearch.client.ResponseException; @Path("/elasticsearch-rest-client") @ApplicationScoped public class ElasticsearchRestClientResource { - - private static final Logger LOG = Logger.getLogger(ElasticsearchRestClientResource.class); - - private static final String COMPONENT_ELASTICSEARCH_REST_CLIENT = "elasticsearch-rest-client"; - @Inject - CamelContext context; + private static final String HEADER_COMPONENT = "component"; @Inject - ProducerTemplate producerTemplate; + FluentProducerTemplate fluentProducerTemplate; - @Path("/load/component/elasticsearch-rest-client") + @Path("/get") @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getData(@QueryParam("indexName") String indexName, @QueryParam("indexId") String indexId) { + try { + Document document = fluentProducerTemplate.to("direct:get") + .withHeader(ElasticSearchRestClientConstant.INDEX_NAME, indexName) + .withHeader(ElasticSearchRestClientConstant.ID, indexId) + .request(Document.class); + return Response.ok().entity(document).build(); + } catch (CamelExecutionException e) { + if (e.getCause() instanceof ResponseException responseException) { + return Response.status(responseException.getResponse().getStatusLine().getStatusCode()).build(); + } + } + return Response.serverError().build(); + } + + @Path("/index/create") + @POST @Produces(MediaType.TEXT_PLAIN) - public Response loadComponentElasticsearchRestClient() throws Exception { - /* This is an autogenerated test */ - if (context.getComponent(COMPONENT_ELASTICSEARCH_REST_CLIENT) != null) { - return Response.ok().build(); + public Response createIndex( + @QueryParam("indexName") String indexName, + String settings) throws Exception { + + Map headers = new HashMap<>(); + headers.put(ElasticSearchRestClientConstant.INDEX_NAME, indexName); + if (settings != null) { + headers.put(ElasticSearchRestClientConstant.INDEX_SETTINGS, settings); } - LOG.warnf("Could not load [%s] from the Camel context", COMPONENT_ELASTICSEARCH_REST_CLIENT); - return Response.status(500, COMPONENT_ELASTICSEARCH_REST_CLIENT + " could not be loaded from the Camel context") + + boolean success = fluentProducerTemplate.to("direct:createIndex") + .withHeaders(headers) + .request(boolean.class); + + return Response.created(new URI("https://camel.apache.org/")) + .entity(success) .build(); } - @Path("/index/{indexName}") - @GET + @Path("/index") + @POST + @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.TEXT_PLAIN) - public Response createIndex(@PathParam("indexName") String indexName) throws ExecutionException, InterruptedException { - String endpointUri = String.format( - "elasticsearch-rest-client:my-cluster?operation=CREATE_INDEX&indexName=%s", indexName); - CompletableFuture ack = producerTemplate.asyncRequestBody(endpointUri, null, Boolean.class); - Boolean response = ack.get(); - - if (response) { - return Response.ok().build(); + public Response indexData( + @QueryParam("indexName") String indexName, + @QueryParam("indexId") String indexId, + Document document) throws Exception { + + Map headers = new HashMap<>(); + headers.put(ElasticSearchRestClientConstant.INDEX_NAME, indexName); + if (indexId != null) { + headers.put(ElasticSearchRestClientConstant.ID, indexId); } - return Response.status(500, "Could not create index") - .build(); + + String result = fluentProducerTemplate.to("direct:index") + .withBody(document) + .withHeaders(headers) + .request(String.class); + + if (indexId != null) { + return Response.ok(result).build(); + } else { + return Response.created(new URI("https://camel.apache.org/")) + .entity(result) + .build(); + } + } + + @Path("/delete") + @DELETE + @Produces(MediaType.TEXT_PLAIN) + public Response deleteData(@QueryParam("indexName") String indexName, @QueryParam("indexId") String indexId) { + fluentProducerTemplate.to("direct:delete") + .withBody(indexId) + .withHeader(ElasticSearchRestClientConstant.INDEX_NAME, indexName) + .withHeader(ElasticSearchRestClientConstant.ID, indexId) + .request(); + + return Response.noContent().build(); + } + + @Path("/delete/index") + @DELETE + @Produces(MediaType.TEXT_PLAIN) + public Response deleteIndexData(@QueryParam("indexName") String indexName) { + boolean result = fluentProducerTemplate.to("direct:deleteIndex") + .withHeader(ElasticSearchRestClientConstant.INDEX_NAME, indexName) + .request(boolean.class); + return Response.ok(result).build(); + } + + @Path("/search") + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response search(@QueryParam("indexName") String indexName, Map criteria) { + String result = fluentProducerTemplate.to("direct:search") + .withHeader(ElasticSearchRestClientConstant.INDEX_NAME, indexName) + .withBody(criteria) + .request(String.class); + return Response.ok(result).build(); + } + + @Path("/search") + @GET + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.APPLICATION_JSON) + public Response searchByJSON(@QueryParam("indexName") String indexName, String query) { + String result = fluentProducerTemplate.to("direct:search") + .withHeader(ElasticSearchRestClientConstant.INDEX_NAME, indexName) + .withHeader(ElasticSearchRestClientConstant.SEARCH_QUERY, query) + .request(String.class); + return Response.ok(result).build(); } } diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientRoutes.java b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientRoutes.java new file mode 100644 index 000000000000..dde90e15bcc5 --- /dev/null +++ b/integration-tests-jvm/elasticsearch-rest-client/src/main/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientRoutes.java @@ -0,0 +1,65 @@ +/* + * 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.rest.client.it; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.elasticsearch.rest.client.ElasticsearchRestClientOperation; + +import static org.apache.camel.component.elasticsearch.rest.client.ElasticsearchRestClientOperation.*; + +public class ElasticsearchRestClientRoutes extends RouteBuilder { + @Override + public void configure() { + from("direct:createIndex") + .to(elasticsearchRestClient(CREATE_INDEX)); + + from("direct:createIndexSettings") + .to(elasticsearchRestClient(CREATE_INDEX)); + + from("direct:delete") + .to(elasticsearchRestClient(DELETE)); + + from("direct:deleteIndex") + .to(elasticsearchRestClient(DELETE_INDEX)); + + from("direct:get") + .to(elasticsearchRestClient(GET_BY_ID)) + .convertBodyTo(String.class) + .unmarshal().json(Document.class); + + from("direct:index") + .marshal().json() + .to(elasticsearchRestClient(INDEX_OR_UPDATE)); + + from("direct:search") + .to(elasticsearchRestClient(SEARCH)); + + from("direct:searchQuery") + .to(elasticsearchRestClient(SEARCH)); + } + + private String elasticsearchRestClient(ElasticsearchRestClientOperation operation) { + return "elasticsearch-rest-client:camel-quarkus" + + "?operation=" + operation + + "&hostAddressesList={{camel.elasticsearch-rest-client.host-addresses-list}}" + + "&user={{camel.elasticsearch-rest-client.user}}" + + "&password={{camel.elasticsearch-rest-client.password}}" + + "&certificatePath={{camel.elasticsearch-rest-client.cert}}" + + "&enableSniffer=true"; + } + +} diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/main/resources/application.properties b/integration-tests-jvm/elasticsearch-rest-client/src/main/resources/application.properties new file mode 100644 index 000000000000..42c2aa61f7d1 --- /dev/null +++ b/integration-tests-jvm/elasticsearch-rest-client/src/main/resources/application.properties @@ -0,0 +1,17 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +quarkus.elasticsearch.devservices.enabled = false \ No newline at end of file diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientTest.java b/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientTest.java index e67032187bb7..250b2e16060d 100644 --- a/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientTest.java +++ b/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestClientTest.java @@ -16,27 +16,373 @@ */ package org.apache.camel.quarkus.component.elasticsearch.rest.client.it; +import java.util.Map; +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 me.escoffier.certs.Format; +import me.escoffier.certs.junit5.Certificate; +import org.apache.camel.quarkus.test.support.certificate.TestCertificates; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import static org.hamcrest.Matchers.is; + +@TestCertificates(certificates = { + @Certificate(name = ElasticsearchRestTestResource.CERTIFICATE_NAME, formats = { + Format.PKCS12 }, password = ElasticsearchRestTestResource.KEYSTORE_PASSWORD) +}, baseDir = ElasticsearchRestTestResource.CERTS_BASEDIR, docker = true) @QuarkusTest +@QuarkusTestResource(ElasticsearchRestTestResource.class) class ElasticsearchRestClientTest { + @AfterEach + public void afterEach() { + // Clean up all indexed data + RestAssured.given() + .queryParam("indexName", "_all") + .delete("/elasticsearch-rest-client/delete/index") + .then() + .statusCode(200) + .body(is("true")); + } + @Test - public void loadComponentElasticsearchRestClient() { - /* A simple autogenerated test */ - RestAssured.get("/elasticsearch-rest-client/load/component/elasticsearch-rest-client") + public void testElasticsearchBasicOperations() { + String indexName = UUID.randomUUID().toString(); + String documentId = UUID.randomUUID().toString(); + String documentValue = "Camel Quarkus ElasticSearch"; + Document document = new Document(); + document.setId(documentId); + document.setValue(documentValue); + + RestAssured.given() + .queryParam("indexName", indexName) + .post("/elasticsearch-rest-client/index/create") + .then() + .statusCode(201) + .body(is("true")); + + // Index data + String indexId = RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(document) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + // Verify index exists + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(200) + .body( + "id", is(documentId), + "value", is(documentValue)); + + // Update indexed data + String updatedDocumentValue = documentValue + " Updated"; + document.setValue(updatedDocumentValue); + RestAssured.given() + .contentType(ContentType.JSON) + .queryParam("indexId", indexId) + .queryParam("indexName", indexName) + .body(document) + .post("/elasticsearch-rest-client/index") .then() .statusCode(200); + + // Verify updated data + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(200) + .body( + "id", is(documentId), + "value", is(updatedDocumentValue)); + + // Delete indexed data + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .delete("/elasticsearch-rest-client/delete") + .then() + .statusCode(204); + + // Verify data deleted + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(404); } @Test - public void test() { - /* Create an index*/ - RestAssured.get("/elasticsearch-rest-client/index/test") + public void testElasticsearchIndexSettings() { + String indexName = UUID.randomUUID().toString(); + String documentId = UUID.randomUUID().toString(); + String documentValue = "Camel Quarkus ElasticSearch"; + Document document = new Document(); + document.setId(documentId); + document.setValue(documentValue); + + String indexSettings = "{\"settings\":{\"number_of_replicas\": 1,\"number_of_shards\": 3,\"analysis\": {},\"refresh_interval\": \"1s\"},\"mappings\":{\"dynamic\": false,\"properties\": {\"title\": {\"type\": \"text\", \"analyzer\": \"english\"}}}}"; + RestAssured.given() + .queryParam("indexName", indexName) + .body(indexSettings) + .post("/elasticsearch-rest-client/index/create") .then() - .statusCode(200); + .statusCode(201) + .body(is("true")); + + // Index data + String indexId = RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(document) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + // Verify index exists + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(200) + .body( + "id", is(documentId), + "value", is(documentValue)); + + // Delete indexed data + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .delete("/elasticsearch-rest-client/delete") + .then() + .statusCode(204); + + // Verify data deleted + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(404); } + @Test + public void testElasticsearchDeleteIndex() { + String indexName = UUID.randomUUID().toString(); + String documentId = UUID.randomUUID().toString(); + String documentValue = "Camel Quarkus ElasticSearch"; + Document document = new Document(); + document.setId(documentId); + document.setValue(documentValue); + + // Create index + RestAssured.given() + .queryParam("indexName", indexName) + .post("/elasticsearch-rest-client/index/create") + .then() + .statusCode(201) + .body(is("true")); + + // Index data + String indexId = RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(document) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + // Verify index exists + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(200) + .body( + "id", is(documentId), + "value", is(documentValue)); + + // Delete indexed data + RestAssured.given() + .queryParam("indexName", indexName) + .delete("/elasticsearch-rest-client/delete/index") + .then() + .statusCode(200) + .body(is("true")); + + // Verify data deleted + RestAssured.given() + .queryParam("indexName", indexName) + .queryParam("indexId", indexId) + .get("/elasticsearch-rest-client/get") + .then() + .statusCode(404); + } + + @Test + public void testElasticsearchSearch() { + String indexName = UUID.randomUUID().toString(); + String documentIdA = UUID.randomUUID().toString(); + String documentValueA = "Camel Quarkus ElasticSearch A"; + Document documentA = new Document(); + documentA.setId(documentIdA); + documentA.setValue(documentValueA); + + String documentIdB = UUID.randomUUID().toString(); + String documentValueB = "Camel Quarkus ElasticSearch B"; + Document documentB = new Document(); + documentB.setId(documentIdB); + documentB.setValue(documentValueB); + + // Create index + RestAssured.given() + .queryParam("indexName", indexName) + .post("/elasticsearch-rest-client/index/create") + .then() + .statusCode(201) + .body(is("true")); + + // Index data + RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(documentA) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(documentB) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + // Search all data + Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + RestAssured.given() + .contentType(ContentType.JSON) + .queryParam("indexName", indexName) + .body(Map.of()) + .get("/elasticsearch-rest-client/search") + .then() + .statusCode(200) + .body( + "id[0]", is(documentIdA), + "value[0]", is(documentValueA), + "id[1]", is(documentIdB), + "value[1]", is(documentValueB)); + }); + + // Search specific data + Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + RestAssured.given() + .contentType(ContentType.JSON) + .queryParam("indexName", indexName) + .body(Map.of("id", documentIdB)) + .get("/elasticsearch-rest-client/search") + .then() + .statusCode(200) + .body( + "id[0]", is(documentIdB), + "value[0]", is(documentValueB)); + }); + } + + @Test + public void testElasticsearchSearchJson() { + String indexName = UUID.randomUUID().toString(); + String documentIdA = UUID.randomUUID().toString(); + String documentValueA = "Camel Quarkus ElasticSearch A"; + Document documentA = new Document(); + documentA.setId(documentIdA); + documentA.setValue(documentValueA); + + String documentIdB = UUID.randomUUID().toString(); + String documentValueB = "Camel Quarkus ElasticSearch B"; + Document documentB = new Document(); + documentB.setId(documentIdB); + documentB.setValue(documentValueB); + + // Create index + RestAssured.given() + .queryParam("indexName", indexName) + .post("/elasticsearch-rest-client/index/create") + .then() + .statusCode(201) + .body(is("true")); + + // Index data + RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(documentA) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + RestAssured.given() + .queryParam("indexName", indexName) + .contentType(ContentType.JSON) + .body(documentB) + .post("/elasticsearch-rest-client/index") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + // Search data + String query = "{\"query\": {\"bool\": {\"must\": [{\"match\": {\"id\": \"" + documentIdB + + "\"}},{\"match\": { \"value\": \"" + documentValueB + "\"}}]}}}"; + Awaitility.await().pollInterval(50, TimeUnit.MILLISECONDS).atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + RestAssured.given() + .contentType(ContentType.TEXT) + .queryParam("indexName", indexName) + .body(query) + .get("/elasticsearch-rest-client/search") + .then() + .statusCode(200) + .body( + "id[0]", is(documentIdB), + "value[0]", is(documentValueB)); + }); + } } diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestTestResource.java b/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestTestResource.java new file mode 100644 index 000000000000..01d25c83b034 --- /dev/null +++ b/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchRestTestResource.java @@ -0,0 +1,136 @@ +/* + * 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.rest.client.it; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.Base64; +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.images.builder.Transferable; + +public class ElasticsearchRestTestResource implements QuarkusTestResourceLifecycleManager { + + public static final String CERTS_BASEDIR = "target/certs"; + public static final String CERTIFICATE_NAME = "elasticsearch"; + public static final String KEYSTORE_PASSWORD = "s3cr3t"; + private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchRestTestResource.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() { + exportCertificateCAForClient(); + + try { + container = new GenericContainer<>(ELASTICSEARCH_IMAGE) + .withExposedPorts(ELASTICSEARCH_PORT) + .withLogConsumer(new Slf4jLogConsumer(LOGGER)) + .withEnv("discovery.type", "single-node") + .withEnv("xpack.security.enabled", "true") + .withEnv("xpack.security.transport.ssl.enabled", "true") + .withEnv("xpack.security.transport.ssl.verification_mode", "certificate") + .withEnv("xpack.security.transport.ssl.keystore.path", "certs/elasticsearch-keystore.p12") + .withEnv("xpack.security.transport.ssl.keystore.password", KEYSTORE_PASSWORD) + .withEnv("xpack.security.transport.ssl.truststore.path", "certs/elasticsearch-truststore.p12") + .withEnv("xpack.security.transport.ssl.truststore.password", KEYSTORE_PASSWORD) + .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) + .withCopyToContainer( + Transferable.of(Files.readAllBytes(Paths.get("target/certs/elasticsearch-keystore.p12"))), + "/usr/share/elasticsearch/config/certs/elasticsearch-keystore.p12") + .withCopyToContainer( + Transferable.of(Files.readAllBytes(Paths.get("target/certs/elasticsearch-truststore.p12"))), + "/usr/share/elasticsearch/config/certs/elasticsearch-truststore.p12") + .waitingFor(Wait.forListeningPort()); + + container.start(); + String hostAddresses = String.format("%s:%s", container.getHost(), container.getMappedPort(ELASTICSEARCH_PORT)); + + // TODO: Replace this in future with component options + // https://issues.apache.org/jira/browse/CAMEL-20846 + return CollectionHelper.mapOf( + "camel.elasticsearch-rest-client.host-addresses-list", hostAddresses, + "camel.elasticsearch-rest-client.user", ELASTICSEARCH_USERNAME, + "camel.elasticsearch-rest-client.password", ELASTICSEARCH_PASSWORD, + "camel.elasticsearch-rest-client.cert", "file:target/certs/ca.crt"); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void stop() { + try { + if (container != null) { + container.stop(); + } + } catch (Exception e) { + // Ignored + } + } + + private void exportCertificateCAForClient() { + Path path = Paths.get("target/certs/elasticsearch-keystore.p12"); + File outputFile = path.getParent().resolve("ca.crt").toFile(); + try { + KeyStore keyStore = KeyStore.getInstance("pkcs12"); + try (FileInputStream fis = new FileInputStream(path.toAbsolutePath().toString())) { + keyStore.load(fis, KEYSTORE_PASSWORD.toCharArray()); + } + + Certificate cert = keyStore.getCertificate(CERTIFICATE_NAME); + if (cert == null) { + throw new IllegalStateException("Unable to find a certificate in keystore named " + CERTIFICATE_NAME); + } + + Base64.Encoder encoder = Base64.getEncoder(); + try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) { + writer.write("-----BEGIN CERTIFICATE-----"); + writer.write("\n"); + writer.write(encoder.encodeToString(cert.getEncoded())); + writer.write("\n"); + writer.write("-----END CERTIFICATE-----"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchTestResource.java b/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchTestResource.java deleted file mode 100644 index e24a467ef00b..000000000000 --- a/integration-tests-jvm/elasticsearch-rest-client/src/test/java/org/apache/camel/quarkus/component/elasticsearch/rest/client/it/ElasticsearchTestResource.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.rest.client.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("%s:%s", container.getHost(), 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-rest-client.hostAddressesList", hostAddresses, - "camel.component.elasticsearch-rest-client.user", ELASTICSEARCH_USERNAME, - "camel.component.elasticsearch-rest-client.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 ef57b9f5ba9c..9e855831aeb0 100644 --- a/pom.xml +++ b/pom.xml @@ -223,7 +223,7 @@ docker.io/couchbase/server:7.2.0 docker.io/couchdb:2.3.1 docker.io/eclipse-mosquitto:2.0.18 - docker.io/elasticsearch:8.8.1 + docker.io/elastic/elasticsearch:8.13.2 docker.io/hapiproject/hapi ${fhir.container.image.base}:v6.8.3 ${fhir.container.image.base}:v4.2.0