diff --git a/client/client-samples/pom.xml b/client/client-samples/pom.xml index 15464ea0b..a0e556a50 100644 --- a/client/client-samples/pom.xml +++ b/client/client-samples/pom.xml @@ -26,6 +26,7 @@ + sample-client-elasticsearch-product sample-client-graphql-book sample-client-reactive-football sample-client-reactive-weather diff --git a/client/client-samples/sample-client-elasticsearch-product/pom.xml b/client/client-samples/sample-client-elasticsearch-product/pom.xml new file mode 100644 index 000000000..3e4fe2c96 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + io.americanexpress.synapse + client-samples + 0.4.10-SNAPSHOT + + + sample-client-elasticsearch-product + + + + io.americanexpress.synapse + synapse-client-elasticsearch + + + io.americanexpress.synapse + synapse-client-test + + + + + diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/CreateProductElasticSearchDocumentClient.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/CreateProductElasticSearchDocumentClient.java new file mode 100644 index 000000000..e394d1e9a --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/CreateProductElasticSearchDocumentClient.java @@ -0,0 +1,25 @@ +package io.americanexpress.sample.client.elasticsearch.product.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.sample.client.elasticsearch.product.model.Product; +import io.americanexpress.synapse.client.elasticsearch.client.BaseCreateElasticSearchDocumentSearchClient; +import org.springframework.stereotype.Component; +import static io.americanexpress.sample.client.elasticsearch.product.config.ProductElasticSearchConfig.INDEX_NAME; + +/** + * {@code CreateProductElasticSearchDocumentClient} creates a document in ElasticSearch. + * + * @author sshre31 + */ +@Component +public class CreateProductElasticSearchDocumentClient extends BaseCreateElasticSearchDocumentSearchClient { + + /** + * Create an instance of CreateProductElasticSearchDocumentClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + */ + public CreateProductElasticSearchDocumentClient(ElasticsearchClient elasticsearchClient) { + super(elasticsearchClient, INDEX_NAME); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/DeleteProductElasticSearchDocumentClient.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/DeleteProductElasticSearchDocumentClient.java new file mode 100644 index 000000000..d449d8bbf --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/DeleteProductElasticSearchDocumentClient.java @@ -0,0 +1,25 @@ +package io.americanexpress.sample.client.elasticsearch.product.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.sample.client.elasticsearch.product.model.Product; +import io.americanexpress.synapse.client.elasticsearch.client.BaseDeleteElasticSearchDocumentSearchClient; +import org.springframework.stereotype.Component; +import static io.americanexpress.sample.client.elasticsearch.product.config.ProductElasticSearchConfig.INDEX_NAME; + +/** + * {@code DeleteProductElasticSearchDocumentClient} deletes a document from ElasticSearch. + * + * @author sshre31 + */ +@Component +public class DeleteProductElasticSearchDocumentClient extends BaseDeleteElasticSearchDocumentSearchClient { + + /** + * Create an instance of BaseDeleteElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + */ + public DeleteProductElasticSearchDocumentClient(ElasticsearchClient elasticsearchClient) { + super(elasticsearchClient, INDEX_NAME); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/ReadProductElasticSearchDocumentClient.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/ReadProductElasticSearchDocumentClient.java new file mode 100644 index 000000000..195b6b377 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/ReadProductElasticSearchDocumentClient.java @@ -0,0 +1,25 @@ +package io.americanexpress.sample.client.elasticsearch.product.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.sample.client.elasticsearch.product.model.Product; +import io.americanexpress.synapse.client.elasticsearch.client.BaseReadElasticSearchDocumentSearchClient; +import org.springframework.stereotype.Component; +import static io.americanexpress.sample.client.elasticsearch.product.config.ProductElasticSearchConfig.INDEX_NAME; + +/** + * {@code ReadProductElasticSearchDocumentClient} reads a document from ElasticSearch. + * + * @author sshre31 + */ +@Component +public class ReadProductElasticSearchDocumentClient extends BaseReadElasticSearchDocumentSearchClient { + + /** + * Create an instance of BaseReadElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + */ + public ReadProductElasticSearchDocumentClient(ElasticsearchClient elasticsearchClient) { + super(elasticsearchClient, INDEX_NAME); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/UpdateProductElasticSearchDocumentClient.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/UpdateProductElasticSearchDocumentClient.java new file mode 100644 index 000000000..fe6cfaf0b --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/client/UpdateProductElasticSearchDocumentClient.java @@ -0,0 +1,25 @@ +package io.americanexpress.sample.client.elasticsearch.product.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.sample.client.elasticsearch.product.model.Product; +import io.americanexpress.synapse.client.elasticsearch.client.BaseUpdateElasticSearchDocumentSearchClient; +import org.springframework.stereotype.Component; +import static io.americanexpress.sample.client.elasticsearch.product.config.ProductElasticSearchConfig.INDEX_NAME; + +/** + * {@code UpdateProductElasticSearchDocumentClient} + * + * @author sshre31 + */ +@Component +public class UpdateProductElasticSearchDocumentClient extends BaseUpdateElasticSearchDocumentSearchClient { + + /** + * Create an instance of BaseUpdateElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + */ + public UpdateProductElasticSearchDocumentClient(ElasticsearchClient elasticsearchClient) { + super(elasticsearchClient, INDEX_NAME); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/config/ProductElasticSearchConfig.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/config/ProductElasticSearchConfig.java new file mode 100644 index 000000000..33f12e828 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/config/ProductElasticSearchConfig.java @@ -0,0 +1,31 @@ +package io.americanexpress.sample.client.elasticsearch.product.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.americanexpress.synapse.client.elasticsearch.config.BaseElasticSearchClientConfig; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * {@code ProductElasticSearchConfig} specifies the configuration for the ElasticSearch client. + * + * @author sshre31 + */ +@ComponentScan("io.americanexpress.sample.client.elasticsearch.product") +@Configuration +public class ProductElasticSearchConfig extends BaseElasticSearchClientConfig { + + /** + * Index name. + */ + public static final String INDEX_NAME = "products"; + + /** + * Constructor taking in objectMapper & metricInterceptor. + * + * @param defaultObjectMapper the default object mapper + */ + public ProductElasticSearchConfig(ObjectMapper defaultObjectMapper, Environment environment) { + super(defaultObjectMapper, environment); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/model/Product.java b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/model/Product.java new file mode 100644 index 000000000..facc0a77c --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/main/java/io/americanexpress/sample/client/elasticsearch/product/model/Product.java @@ -0,0 +1,57 @@ +package io.americanexpress.sample.client.elasticsearch.product.model; + +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; + +/** + * {@code Product} represents a product. + * + * @author sshre31 + */ +public class Product extends BaseElasticSearchData { + + /** + * Name of the product. + */ + private String name; + + /** + * Description of the product. + */ + private String description; + + /** + * Get the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Set the name. + * + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get the description. + * + * @return description + */ + public String getDescription() { + return description; + } + + /** + * Set the description. + * + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/CreateProductElasticSearchDocumentIT.java b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/CreateProductElasticSearchDocumentIT.java new file mode 100644 index 000000000..226bea418 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/CreateProductElasticSearchDocumentIT.java @@ -0,0 +1,44 @@ +package io.americanexpress.sample.client.elasticsearch.client; + +import io.americanexpress.sample.client.elasticsearch.config.ProductElasticSearchConfigTest; +import io.americanexpress.sample.client.elasticsearch.product.client.CreateProductElasticSearchDocumentClient; +import io.americanexpress.sample.client.elasticsearch.product.model.Product; +import io.americanexpress.synapse.framework.exception.ApplicationClientException; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * {@code CreateProductElasticSearchDocumentIT} tests the {@link CreateProductElasticSearchDocumentClient} class. + * + * @author sshre31 + */ +@ContextConfiguration(classes = ProductElasticSearchConfigTest.class) +@ExtendWith(SpringExtension.class) +class CreateProductElasticSearchDocumentIT { + + @Autowired + CreateProductElasticSearchDocumentClient createProductElasticSearchDocument; + + @Test + void save_providedValidProduct_expectedSuccess() { + var product = new Product(); + product.setId(UUID.randomUUID().toString()); + product.setName("Ice Cream"); + product.setDescription("Fudge Ice Cream."); + assertDoesNotThrow(() -> createProductElasticSearchDocument.save(product)); + } + + @Test + void read_providedValidProduct_expectedException() { + var product = new Product(); + product.setName("Ice Cream"); + product.setDescription("Fudge Ice Cream."); + assertThrows(ApplicationClientException.class, () -> createProductElasticSearchDocument.save(product)); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/ReadProductElasticSearchDocumentIT.java b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/ReadProductElasticSearchDocumentIT.java new file mode 100644 index 000000000..5e955c414 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/client/ReadProductElasticSearchDocumentIT.java @@ -0,0 +1,50 @@ +package io.americanexpress.sample.client.elasticsearch.client; + +import io.americanexpress.sample.client.elasticsearch.config.ProductElasticSearchConfigTest; +import io.americanexpress.sample.client.elasticsearch.product.client.ReadProductElasticSearchDocumentClient; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@code ReadProductElasticSearchDocumentIT} + * + * @author sshre31 + */ +@ContextConfiguration(classes = ProductElasticSearchConfigTest.class) +@ExtendWith(SpringExtension.class) +class ReadProductElasticSearchDocumentIT { + + + @Autowired + ReadProductElasticSearchDocumentClient readProductElasticSearchDocumentClient; + + @Test + void findById_providedValidId_expectedSuccess() throws IOException { + var product = readProductElasticSearchDocumentClient.findById("{id}"); + assertNotNull(product); + } + + @Test + void findAll_providedValidRequest_expectedSuccess() throws IOException { + var allProducts = readProductElasticSearchDocumentClient.findAll(0, 10); + assertNotNull(allProducts); + } + + @Test + void searchByKey_providedValidKeyword_expectedSuccess() throws IOException { + var products = readProductElasticSearchDocumentClient.searchByKey("name", "Ice Cream", 0, 10); + assertNotNull(products); + } + + @Test + void doesExists_providedValidId_expectedSuccess() throws IOException { + var products = readProductElasticSearchDocumentClient.doesExists("{id}"); + assertTrue(products); + } +} diff --git a/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/config/ProductElasticSearchConfigTest.java b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/config/ProductElasticSearchConfigTest.java new file mode 100644 index 000000000..5dacb3c05 --- /dev/null +++ b/client/client-samples/sample-client-elasticsearch-product/src/test/java/io/americanexpress/sample/client/elasticsearch/config/ProductElasticSearchConfigTest.java @@ -0,0 +1,16 @@ +package io.americanexpress.sample.client.elasticsearch.config; + +import io.americanexpress.sample.client.elasticsearch.product.config.ProductElasticSearchConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * {@code ProductElasticSearchConfigTest} + * + * @author sshre31 + */ +@Configuration +@Import(ProductElasticSearchConfig.class) +public class ProductElasticSearchConfigTest { + +} diff --git a/client/pom.xml b/client/pom.xml index dffdbe2a8..0e5c8d63b 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -27,6 +27,7 @@ client-samples + synapse-client-elasticsearch synapse-client-graphql synapse-client-rest synapse-client-soap diff --git a/client/synapse-client-elasticsearch/README.md b/client/synapse-client-elasticsearch/README.md new file mode 100644 index 000000000..e9bb9da5f --- /dev/null +++ b/client/synapse-client-elasticsearch/README.md @@ -0,0 +1,36 @@ +# synapse-client-elasticsearch + +## Description +The `synapse-client-elasticsearch` module provides a client interface for interacting with Elasticsearch within the Synapse framework. It includes functionalities for indexing, searching, and managing Elasticsearch documents. + +## Features +- Indexing documents into Elasticsearch +- Searching documents with various query types +- Managing Elasticsearch indices +- Handling Elasticsearch exceptions + +## Installation +To include the `synapse-client-elasticsearch` module in your project, add the following dependency to your `pom.xml`: + +## Usage + +Add the configuration to your `application.properties` file: + +```properties +elastic-client.url={elastic-url} +elastic-client.username={elastic-username} +elastic-client.password={elastic-password} +``` + +- To utilize this module, add the following dependency to the pom.xml file: +```xml + + io.americanexpress.synapse + synapse-client-elasticsearch + 4.0.0 + +``` +Or add the following to the build.gradle file: +``` +implementation 'io.americanexpress.synapse:synapse-client-graphql:0.3.32-SNAPSHOT' +``` diff --git a/client/synapse-client-elasticsearch/pom.xml b/client/synapse-client-elasticsearch/pom.xml new file mode 100644 index 000000000..ece66ade3 --- /dev/null +++ b/client/synapse-client-elasticsearch/pom.xml @@ -0,0 +1,84 @@ + + + + io.americanexpress.synapse + client + 0.4.10-SNAPSHOT + + + 4.0.0 + synapse-client-elasticsearch + + + + + + + io.americanexpress.synapse + synapse-framework-exception + + + io.americanexpress.synapse + synapse-framework-test + + + io.americanexpress.synapse + synapse-utilities-common + + + + + + org.slf4j + slf4j-ext + + + org.springframework.boot + spring-boot-starter-webflux + + + co.elastic.clients + elasticsearch-java + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + + + maven-checkstyle-plugin + + + maven-compiler-plugin + + + maven-dependency-plugin + + + maven-jar-plugin + + + maven-pmd-plugin + + + maven-surefire-plugin + + + + + + + + maven-checkstyle-plugin + + + maven-pmd-plugin + + + + diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseCreateElasticSearchDocumentSearchClient.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseCreateElasticSearchDocumentSearchClient.java new file mode 100644 index 000000000..a9cf0f64b --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseCreateElasticSearchDocumentSearchClient.java @@ -0,0 +1,45 @@ +package io.americanexpress.synapse.client.elasticsearch.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; +import io.americanexpress.synapse.framework.exception.ApplicationClientException; +import io.americanexpress.synapse.framework.exception.model.ErrorCode; +import io.micrometer.common.util.StringUtils; +import java.io.IOException; + +/** + * {@code BaseCreateElasticSearchDocumentSearchClient} creates a document in ElasticSearch. + * + * @author sshre31 + */ +public abstract class BaseCreateElasticSearchDocumentSearchClient extends BaseElasticSearchClient { + + /** + * Create an instance of BaseCreateElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + * @param indexName indexName + */ + protected BaseCreateElasticSearchDocumentSearchClient(ElasticsearchClient elasticsearchClient, + String indexName) { + super(elasticsearchClient, indexName); + } + + /** + * Save the document in ElasticSearch. + * + * @param document the document to save + * @throws IOException if an error occurs while saving the document + */ + public void save(T document) throws IOException { + if (StringUtils.isBlank(document.getId())) { + throw new ApplicationClientException("Document ID is required.", ErrorCode.GENERIC_4XX_ERROR); + } + + this.elasticsearchClient.index(i -> i + .index(this.indexName) + .id(document.getId()) + .document(document) + ); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseDeleteElasticSearchDocumentSearchClient.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseDeleteElasticSearchDocumentSearchClient.java new file mode 100644 index 000000000..0d4a21cbb --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseDeleteElasticSearchDocumentSearchClient.java @@ -0,0 +1,49 @@ +package io.americanexpress.synapse.client.elasticsearch.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; +import java.io.IOException; + +/** + * {@code BaseDeleteElasticSearchDocumentSearchClient} deletes a document in ElasticSearch. + * + * @author sshre31 + */ +public abstract class BaseDeleteElasticSearchDocumentSearchClient extends BaseElasticSearchClient { + + /** + * Create an instance of BaseDeleteElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + * @param indexName indexName + */ + protected BaseDeleteElasticSearchDocumentSearchClient(ElasticsearchClient elasticsearchClient, String indexName) { + super(elasticsearchClient, indexName); + } + + /** + * Delete the document in ElasticSearch. + * + * @param id the identifier + * @throws IOException if an error occurs while deleting the document + */ + public void deleteById(String id) throws IOException { + this.elasticsearchClient.delete(d -> d + .index(this.indexName) + .id(id) + ); + } + + /** + * Delete the document in ElasticSearch. + * + * @param document the document to delete + * @throws IOException if an error occurs while deleting the document + */ + public void delete(T document) throws IOException { + this.elasticsearchClient.delete(d -> d + .index(this.indexName) + .id(document.getId()) + ); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseElasticSearchClient.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseElasticSearchClient.java new file mode 100644 index 000000000..fc7a64c05 --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseElasticSearchClient.java @@ -0,0 +1,105 @@ +package io.americanexpress.synapse.client.elasticsearch.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.core.search.HitsMetadata; +import co.elastic.clients.elasticsearch.core.search.TotalHits; +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * {@code BaseElasticSearchClient} is the base class for all ElasticSearch clients. + * + * @author sshre31 + */ +public abstract class BaseElasticSearchClient { + + /** + * Elasticsearch client. + */ + protected final ElasticsearchClient elasticsearchClient; + + /** + * Index name. + */ + protected final String indexName; + + /** + * Class of the document. + */ + protected Class documentType; + + /** + * Create an instance of BaseElasticSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + * @param indexName indexName + */ + protected BaseElasticSearchClient(ElasticsearchClient elasticsearchClient, String indexName) { + this.elasticsearchClient = elasticsearchClient; + this.indexName = indexName; + initialize(); + } + + /** + * Initialize the document type. + */ + @SuppressWarnings("unchecked") + private void initialize() { + var parameterizedType = ((ParameterizedType) getClass().getGenericSuperclass()); + this.documentType = (Class) parameterizedType.getActualTypeArguments()[0]; + } + + /** + * Render the results from the search response. + * + * @param response the search response + * @return the list of results + */ + List renderResults(SearchResponse response) { + return Optional.ofNullable(response) + .map(SearchResponse::hits) + .map(HitsMetadata::hits) + .orElseGet(ArrayList::new) + .stream() + .map(Hit::source) + .toList(); + } + + /** + * Render the page results from the search response. + * + * @param response the search response + * @param page the page number + * @param size the page size + * @return the page of results + */ + Page renderPageResults(SearchResponse response, int page, int size) { + var hitsMetadata = Optional.ofNullable(response) + .map(SearchResponse::hits) + .orElse(null); + + long totalHits = Optional.ofNullable(hitsMetadata) + .map(HitsMetadata::total) + .map(TotalHits::value) + .orElse(0L); + + List results = Optional.ofNullable(hitsMetadata) + .map(HitsMetadata::hits) + .orElseGet(ArrayList::new) + .stream() + .map(Hit::source) + .toList(); + + var pageable = PageRequest.of(page, size); + return new PageImpl<>(results, pageable, totalHits); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseReadElasticSearchDocumentSearchClient.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseReadElasticSearchDocumentSearchClient.java new file mode 100644 index 000000000..453af0212 --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseReadElasticSearchDocumentSearchClient.java @@ -0,0 +1,94 @@ +package io.americanexpress.synapse.client.elasticsearch.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.GetResponse; +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; +import java.io.IOException; +import org.springframework.data.domain.Page; + +/** + * {@code BaseReadElasticSearchDocumentSearchClient} reads a document in ElasticSearch. + * + * @author sshre31 + */ +public abstract class BaseReadElasticSearchDocumentSearchClient extends BaseElasticSearchClient { + + /** + * Create an instance of BaseReadElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + * @param indexName indexName + */ + protected BaseReadElasticSearchDocumentSearchClient(ElasticsearchClient elasticsearchClient, String indexName) { + super(elasticsearchClient, indexName); + } + + /** + * Find the document by the identifier. + * + * @param id the identifier + * @return the document + * @throws IOException if an error occurs while finding the document + */ + public T findById(String id) throws IOException { + var response = elasticsearchClient.get(g -> g + .index(indexName) + .id(id), this.documentType + ); + return response.found() ? response.source() : null; + } + + /** + * Find all the documents. + * + * @return the list of documents + * @throws IOException if an error occurs while finding the documents + */ + public Page findAll(int page, int size) throws IOException { + var response = elasticsearchClient.search(s -> s + .index(indexName) + .query(q -> q.matchAll(m -> m)) + .from(page) + .size(size), + this.documentType + ); + + return renderPageResults(response, page, size); + } + + /** + * Find the document by the key. + * + * @param key the key + * @param value the value + * @throws IOException if an error occurs while finding the documents + */ + public Page searchByKey(String key, String value, int page, int size) throws IOException { + var response = elasticsearchClient.search(s -> s + .index(indexName) + .query(q -> q.match(t -> t + .field(key) + .query(value) + )) + .from(page) + .size(size), this.documentType + ); + + return renderPageResults(response, page, size); + } + + /** + * Check if the document exists. + * + * @param id the identifier + * @return true if the document exists, false otherwise + * @throws IOException if an error occurs while checking if the document exists + */ + public boolean doesExists(String id) throws IOException { + GetResponse response = elasticsearchClient.get(g -> g + .index(indexName) + .id(id), this.documentType + ); + return response.found(); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseUpdateElasticSearchDocumentSearchClient.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseUpdateElasticSearchDocumentSearchClient.java new file mode 100644 index 000000000..637a6e5c7 --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/client/BaseUpdateElasticSearchDocumentSearchClient.java @@ -0,0 +1,53 @@ +package io.americanexpress.synapse.client.elasticsearch.client; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import io.americanexpress.synapse.client.elasticsearch.model.BaseElasticSearchData; +import java.io.IOException; + +/** + * {@code BaseUpdateElasticSearchDocumentSearchClient} + * + * @author sshre31 + */ +public abstract class BaseUpdateElasticSearchDocumentSearchClient extends BaseElasticSearchClient { + + /** + * Create an instance of BaseUpdateElasticSearchDocumentSearchClient with the specified parameters. + * + * @param elasticsearchClient elasticsearchClient + * @param indexName indexName + */ + protected BaseUpdateElasticSearchDocumentSearchClient(ElasticsearchClient elasticsearchClient, String indexName) { + super(elasticsearchClient, indexName); + } + + /** + * Update the document in ElasticSearch. + * + * @param document the document to update + * @throws IOException if an error occurs while updating the document + */ + public void update(T document) throws IOException { + elasticsearchClient.update(u -> u + .index(indexName) + .id(document.getId()) + .doc(document), + this.documentType + ); + } + + /** + * Upsert the document in ElasticSearch. + * + * @param document the document to update + * @throws IOException if an error occurs while upserting the document + */ + public void upsert(T document) throws IOException { + elasticsearchClient.update(u -> u + .index(indexName) + .id(document.getId()) + .upsert(document), + this.documentType + ); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/config/BaseElasticSearchClientConfig.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/config/BaseElasticSearchClientConfig.java new file mode 100644 index 000000000..5db0e97e3 --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/config/BaseElasticSearchClientConfig.java @@ -0,0 +1,75 @@ +package io.americanexpress.synapse.client.elasticsearch.config; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.americanexpress.synapse.framework.exception.config.ExceptionConfig; +import io.americanexpress.synapse.utilities.common.config.UtilitiesCommonConfig; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; + +/** + * {@code BaseElasticSearchClientConfig} specifies the base configuration for the ElasticSearch client. + * + * @author sshre31 + */ +@Configuration +@ComponentScan(basePackages = "io.americanexpress.synapse.client.elasticsearch") +@Import({ExceptionConfig.class, UtilitiesCommonConfig.class}) +public class BaseElasticSearchClientConfig { + + /** + * Default object mapper. + */ + private final ObjectMapper defaultObjectMapper; + + /** + * The environment. + */ + private final Environment environment; + + /** + * Constructor taking in objectMapper & metricInterceptor. + * + * @param defaultObjectMapper the default object mapper + */ + public BaseElasticSearchClientConfig(ObjectMapper defaultObjectMapper, Environment environment) { + this.defaultObjectMapper = defaultObjectMapper; + this.environment = environment; + } + + /** + * Creates an instance of {@link ElasticsearchClient} to interact with the ElasticSearch cluster. + * + * @return the {@link ElasticsearchClient} instance. + */ + @Bean + public ElasticsearchClient elasticsearchClient() { + var elasticSearchUrl = environment.getRequiredProperty("elastic-client.url"); + var elasticSearchUser = environment.getRequiredProperty("elastic-client.username"); + var elasticSearchPassword = environment.getRequiredProperty("elastic-client.password"); + + var basicCredentialsProvider = new BasicCredentialsProvider(); + basicCredentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticSearchUser, elasticSearchPassword)); + + var restClient = RestClient + .builder(HttpHost.create(elasticSearchUrl)) + .setHttpClientConfigCallback(httpAsyncClientBuilder -> + httpAsyncClientBuilder.setDefaultCredentialsProvider(basicCredentialsProvider)) + .build(); + + var transport = new RestClientTransport( + restClient, new JacksonJsonpMapper(defaultObjectMapper)); + + return new ElasticsearchClient(transport); + } +} diff --git a/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/model/BaseElasticSearchData.java b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/model/BaseElasticSearchData.java new file mode 100644 index 000000000..f291264a3 --- /dev/null +++ b/client/synapse-client-elasticsearch/src/main/java/io/americanexpress/synapse/client/elasticsearch/model/BaseElasticSearchData.java @@ -0,0 +1,30 @@ +package io.americanexpress.synapse.client.elasticsearch.model; + +/** + * {@code BaseElasticSearchData} is the base class for all ElasticSearch data. + * + * @author sshre31 + */ +public abstract class BaseElasticSearchData { + + /** + * The id of the entity. + */ + private String id; + + /** + * Gets the entity's id. + * @return the id + */ + public String getId() { + return id; + } + + /** + * Sets the entity's id. + * @param id the id of this entity + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/pom.xml b/pom.xml index 7b53ae7d8..9bd8cba8c 100644 --- a/pom.xml +++ b/pom.xml @@ -271,6 +271,11 @@ + + io.americanexpress.synapse + synapse-client-elasticsearch + ${project.version} + io.americanexpress.synapse synapse-client-graphql