DATAES-914 - Use TestContainers.

This commit is contained in:
Peter-Josef Meisch 2020-08-25 16:35:42 +02:00
parent 79fdc449b8
commit 6361a1eefe
No known key found for this signature in database
GPG Key ID: DE108246970C7708
55 changed files with 790 additions and 1660 deletions

12
Jenkinsfile vendored
View File

@ -24,12 +24,16 @@ pipeline {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
options { timeout(time: 30, unit: 'MINUTES') }
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
sh 'chown -R 1001:1001 .'
}
}
@ -47,12 +51,16 @@ pipeline {
image 'adoptopenjdk/openjdk11:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
options { timeout(time: 30, unit: 'MINUTES') }
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -U -B'
sh 'chown -R 1001:1001 .'
}
}
@ -62,12 +70,16 @@ pipeline {
image 'adoptopenjdk/openjdk12:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
}
options { timeout(time: 30, unit: 'MINUTES') }
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -U -B'
sh 'chown -R 1001:1001 .'
}
}
}

14
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.1.0-DATAES-914-SNAPSHOT</version>
<parent>
<groupId>org.springframework.data.build</groupId>
@ -20,9 +20,10 @@
<properties>
<commonslang>2.6</commonslang>
<elasticsearch>7.8.1</elasticsearch>
<log4j>2.13.2</log4j>
<springdata.commons>2.4.0-SNAPSHOT</springdata.commons>
<log4j>2.13.3</log4j>
<netty>4.1.50.Final</netty>
<springdata.commons>2.4.0-SNAPSHOT</springdata.commons>
<testcontainers>1.14.3</testcontainers>
<java-module-name>spring.data.elasticsearch</java-module-name>
</properties>
@ -289,6 +290,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>elasticsearch</artifactId>
<version>${testcontainers}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -44,7 +44,9 @@ import org.springframework.util.StringUtils;
* @author Mohsin Husen
* @author Ilkang Na
* @author Peter-Josef Meisch
* @deprecated since 4.1, we're not supporting embedded Node clients anymore, use the REST client
*/
@Deprecated
public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(NodeClientFactoryBean.class);

View File

@ -173,7 +173,12 @@ class RequestFactory {
}
public GetAliasesRequest getAliasesRequest(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
return new GetAliasesRequest(aliasNames).indices(indexNames);
GetAliasesRequest getAliasesRequest = new GetAliasesRequest(aliasNames);
if (indexNames != null) {
getAliasesRequest.indices(indexNames);
}
return getAliasesRequest;
}
public IndicesAliasesRequest indicesAddAliasesRequest(AliasQuery query, IndexCoordinates index) {

View File

@ -36,7 +36,10 @@ public final class VersionInfo {
private static final Logger LOG = LoggerFactory.getLogger(VersionInfo.class);
private static final AtomicBoolean initialized = new AtomicBoolean(false);
private static String VERSION_PROPERTIES = "versions.properties";
private static final String VERSION_PROPERTIES = "versions.properties";
public static final String VERSION_SPRING_DATA_ELASTICSEARCH = "version.spring-data-elasticsearch";
public static final String VERSION_ELASTICSEARCH_CLIENT = "version.elasticsearch-client";
/**
* logs the relevant version info the first time it is called. Does nothing after the first call
@ -51,8 +54,8 @@ public final class VersionInfo {
Properties properties = new Properties();
properties.load(resource);
String versionSpringDataElasticsearch = properties.getProperty("version.spring-data-elasticsearch");
Version versionESBuilt = Version.fromString(properties.getProperty("version.elasticsearch-client"));
String versionSpringDataElasticsearch = properties.getProperty(VERSION_SPRING_DATA_ELASTICSEARCH);
Version versionESBuilt = Version.fromString(properties.getProperty(VERSION_ELASTICSEARCH_CLIENT));
Version versionESUsed = Version.CURRENT;
Version versionESCluster = clusterVersion != null ? Version.fromString(clusterVersion) : null;
@ -83,6 +86,22 @@ public final class VersionInfo {
}
}
public static Properties versionProperties() throws Exception {
try {
InputStream resource = VersionInfo.class.getClassLoader().getResourceAsStream(VERSION_PROPERTIES);
if (resource != null) {
Properties properties = new Properties();
properties.load(resource);
return properties;
} else {
throw new IllegalStateException("Resource not found");
}
} catch (Exception e) {
LOG.error("Could not load {}", VERSION_PROPERTIES, e);
throw e;
}
}
private static boolean differInMajorOrMinor(Version version1, Version version2) {
return version1.major != version2.major || version1.minor != version2.minor;
}

View File

@ -1,49 +0,0 @@
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch;
import org.elasticsearch.client.Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource;
/**
* configuration class for the classic ElasticsearchTemplate. Needs a {@link TestNodeResource} bean that should be set
* up in the test as ClassRule and exported as bean.
*
* @author Peter-Josef Meisch
*/
@Configuration
public class ElasticsearchTestConfiguration extends ElasticsearchConfigurationSupport {
@Autowired private TestNodeResource testNodeResource;
@Bean
public Client elasticsearchClient() {
return testNodeResource.client();
}
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
public ElasticsearchTemplate elasticsearchTemplate(Client elasticsearchClient,
ElasticsearchConverter elasticsearchConverter) {
return new ElasticsearchTemplate(elasticsearchClient, elasticsearchConverter);
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
/**
* @author Peter-Josef Meisch
*/
@Configuration
public class RestElasticsearchTestConfiguration extends AbstractElasticsearchConfiguration {
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
return TestUtils.restHighLevelClient();
}
}

View File

@ -1,137 +0,0 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch;
import lombok.SneakyThrows;
import java.time.Duration;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.data.elasticsearch.support.SearchHitsUtil;
import org.springframework.data.util.Version;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @currentRead Fool's Fate - Robin Hobb
*/
public final class TestUtils {
private TestUtils() {}
private static final ClientConfiguration CONFIG = ClientConfiguration.builder().connectedToLocalhost()
.withConnectTimeout(Duration.ofSeconds(5)).withSocketTimeout(Duration.ofSeconds(3)).build();
public static RestHighLevelClient restHighLevelClient() {
return RestClients.create(CONFIG).rest();
}
public static ReactiveElasticsearchClient reactiveClient() {
return ReactiveRestClients.create(CONFIG);
}
public static Version serverVersion() {
try (RestHighLevelClient client = restHighLevelClient()) {
org.elasticsearch.Version version = org.elasticsearch.Version
.fromString(client.info(RequestOptions.DEFAULT).getVersion().getNumber());
return new Version(version.major, version.minor, version.revision);
} catch (Exception e) {
return new Version(0, 0, 0);
}
}
@SneakyThrows
public static void deleteIndex(String... indexes) {
if (ObjectUtils.isEmpty(indexes)) {
return;
}
try (RestHighLevelClient client = restHighLevelClient()) {
for (String index : indexes) {
try {
client.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT);
} catch (ElasticsearchStatusException ex) {
// just ignore it
}
}
}
}
@SneakyThrows
public static boolean isEmptyIndex(String indexName) {
try (RestHighLevelClient client = restHighLevelClient()) {
return 0L == SearchHitsUtil.getTotalCount(client
.search(new SearchRequest(indexName)
.source(SearchSourceBuilder.searchSource().query(QueryBuilders.matchAllQuery())), RequestOptions.DEFAULT)
.getHits());
}
}
public static OfType documentWithId(String id) {
return new DocumentLookup(id);
}
public interface ExistsIn {
boolean existsIn(String index);
}
public interface OfType extends ExistsIn {
ExistsIn ofType(String type);
}
private static class DocumentLookup implements OfType {
private String id;
public DocumentLookup(String id) {
this.id = id;
}
@Override
@SneakyThrows
public boolean existsIn(String index) {
GetRequest request = new GetRequest(index).id(id);
try (RestHighLevelClient client = restHighLevelClient()) {
return client.get(request, RequestOptions.DEFAULT).isExists();
}
}
@Override
public ExistsIn ofType(String type) {
return this;
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch;
import java.util.Arrays;
import java.util.UUID;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.join.ParentJoinPlugin;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.transport.Netty4Plugin;
import org.springframework.data.elasticsearch.client.NodeClientFactoryBean;
/**
* @author Mohsin Husen
* @author Artur Konczak
* @author Ilkang Na
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Subhobrata Dey
*/
public class Utils {
public static Node getNode() {
String pathHome = "src/test/resources/test-home-dir";
String pathData = "target/elasticsearchTestData";
String clusterName = UUID.randomUUID().toString();
return new NodeClientFactoryBean.TestNode( //
Settings.builder() //
.put("transport.type", "netty4") //
.put("http.type", "netty4") //
.put("path.home", pathHome) //
.put("path.data", pathData) //
.put("cluster.name", clusterName) //
.put("node.max_local_storage_nodes", 100)//
// the following 3 settings are needed to avoid problems on big, but
// almost full filesystems, see DATAES-741
.put("cluster.routing.allocation.disk.watermark.low", "1gb")//
.put("cluster.routing.allocation.disk.watermark.high", "1gb")//
.put("cluster.routing.allocation.disk.watermark.flood_stage", "1gb")//
.build(), //
Arrays.asList(Netty4Plugin.class, ParentJoinPlugin.class));
}
public static Client getNodeClient() throws NodeValidationException {
return getNode().start().client();
}
}

View File

@ -18,9 +18,9 @@ package org.springframework.data.elasticsearch.client.reactive;
import static org.assertj.core.api.Assertions.*;
import lombok.SneakyThrows;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
@ -39,11 +39,6 @@ import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilders;
@ -58,13 +53,17 @@ import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.TestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersion;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
/**
@ -76,21 +75,27 @@ import org.springframework.test.context.ContextConfiguration;
* @author Thomas Geese
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class })
@ContextConfiguration(classes = { ReactiveElasticsearchClientTests.Config.class })
public class ReactiveElasticsearchClientTests {
@Configuration
static class Config extends ReactiveElasticsearchRestTemplateConfiguration {
@Override
@Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
return super.reactiveElasticsearchClient();
}
}
static final String INDEX_I = "idx-1-reactive-client-tests";
static final String INDEX_II = "idx-2-reactive-client-tests";
static final String TYPE_I = "doc-type-1";
static final String TYPE_II = "doc-type-2";
// must be <String, Object> and not <String, String>, otherwise UpdateRequest.doc() will use the overload with
// (Object...)
static final Map<String, Object> DOC_SOURCE;
RestHighLevelClient syncClient;
ReactiveElasticsearchClient client;
@Autowired ReactiveElasticsearchClient client;
@Autowired ReactiveElasticsearchOperations operations;
static {
@ -103,19 +108,14 @@ public class ReactiveElasticsearchClientTests {
@BeforeEach
public void setUp() {
syncClient = TestUtils.restHighLevelClient();
client = TestUtils.reactiveClient();
TestUtils.deleteIndex(INDEX_I, INDEX_II);
operations.indexOps(IndexCoordinates.of(INDEX_I)).delete().block();
operations.indexOps(IndexCoordinates.of(INDEX_II)).delete().block();
}
@AfterEach
public void after() throws IOException {
TestUtils.deleteIndex(INDEX_I, INDEX_II);
syncClient.close();
public void after() {
operations.indexOps(IndexCoordinates.of(INDEX_I)).delete().block();
operations.indexOps(IndexCoordinates.of(INDEX_II)).delete().block();
}
@Test // DATAES-488
@ -157,7 +157,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void getShouldFetchDocumentById() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
client.get(new GetRequest(INDEX_I, id)) //
.as(StepVerifier::create) //
@ -172,7 +172,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void getShouldCompleteForNonExistingDocuments() {
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
String id = "this-one-does-not-exist";
client.get(new GetRequest(INDEX_I, id)) //
@ -183,8 +183,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void multiGetShouldReturnAllDocumentsFromSameCollection() {
String id1 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id2 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id1 = addSourceDocument().to(INDEX_I);
String id2 = addSourceDocument().to(INDEX_I);
MultiGetRequest request = new MultiGetRequest() //
.add(INDEX_I, id1) //
@ -200,8 +200,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void multiGetShouldReturnAllExistingDocumentsFromSameCollection() {
String id1 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id1 = addSourceDocument().to(INDEX_I);
addSourceDocument().to(INDEX_I);
MultiGetRequest request = new MultiGetRequest() //
.add(INDEX_I, id1) //
@ -216,8 +216,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void multiGetShouldSkipNonExistingDocuments() {
String id1 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id2 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id1 = addSourceDocument().to(INDEX_I);
String id2 = addSourceDocument().to(INDEX_I);
MultiGetRequest request = new MultiGetRequest() //
.add(INDEX_I, id1) //
@ -234,8 +234,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void multiGetShouldCompleteIfNothingFound() {
String id1 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id2 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id1 = addSourceDocument().to(INDEX_I);
String id2 = addSourceDocument().to(INDEX_I);
client.multiGet(new MultiGetRequest() //
.add(INDEX_II, id1).add(INDEX_II, id2)) //
@ -246,8 +246,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void multiGetShouldReturnAllExistingDocumentsFromDifferentCollection() {
String id1 = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id2 = addSourceDocument().ofType(TYPE_II).to(INDEX_II);
String id1 = addSourceDocument().to(INDEX_I);
String id2 = addSourceDocument().to(INDEX_II);
MultiGetRequest request = new MultiGetRequest() //
.add(INDEX_I, id1) //
@ -263,7 +263,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void existsReturnsTrueForExistingDocuments() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
client.exists(new GetRequest(INDEX_I, id)) //
.as(StepVerifier::create) //
@ -274,7 +274,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void existsReturnsFalseForNonExistingDocuments() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
client.exists(new GetRequest(INDEX_II, id)) //
.as(StepVerifier::create) //
@ -285,7 +285,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void indexShouldAddDocument() {
IndexRequest request = indexRequest(DOC_SOURCE, INDEX_I, TYPE_I);
IndexRequest request = indexRequest();
client.index(request) //
.as(StepVerifier::create) //
@ -300,9 +300,9 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void indexShouldErrorForExistingDocuments() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
IndexRequest request = indexRequest(DOC_SOURCE, INDEX_I, TYPE_I)//
IndexRequest request = indexRequest()//
.id(id);
client.index(request) //
@ -331,7 +331,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void updateShouldUpdateExistingDocument() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
UpdateRequest request = new UpdateRequest(INDEX_I, id) //
.doc(Collections.singletonMap("dutiful", "farseer"));
@ -362,7 +362,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void deleteShouldRemoveExistingDocument() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
DeleteRequest request = new DeleteRequest(INDEX_I, id);
@ -375,7 +375,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void deleteShouldReturnNotFoundForNonExistingDocument() {
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
DeleteRequest request = new DeleteRequest(INDEX_I, "this-one-does-not-exist");
@ -388,8 +388,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-488
public void searchShouldFindExistingDocuments() {
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
addSourceDocument().to(INDEX_I);
SearchRequest request = new SearchRequest(INDEX_I) //
.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
@ -401,9 +401,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-488
public void searchShouldCompleteIfNothingFound() throws IOException {
public void searchShouldCompleteIfNothingFound() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
SearchRequest request = new SearchRequest(INDEX_I) //
.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
@ -414,10 +414,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-488
@ElasticsearchVersion(asOf = "6.5.0")
public void deleteByShouldRemoveExistingDocument() {
String id = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String id = addSourceDocument().to(INDEX_I);
DeleteByQueryRequest request = new DeleteByQueryRequest(INDEX_I) //
.setQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("_id", id)));
@ -430,10 +429,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-488
@ElasticsearchVersion(asOf = "6.5.0")
public void deleteByEmitResultWhenNothingRemoved() {
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
DeleteByQueryRequest request = new DeleteByQueryRequest(INDEX_I) //
.setQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("_id", "it-was-not-me")));
@ -448,7 +446,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-510
public void scrollShouldReadWhileEndNotReached() {
IntStream.range(0, 100).forEach(it -> add(Collections.singletonMap(it + "-foo", "bar")).ofType(TYPE_I).to(INDEX_I));
IntStream.range(0, 100).forEach(it -> add(Collections.singletonMap(it + "-foo", "bar")).to(INDEX_I));
SearchRequest request = new SearchRequest(INDEX_I) //
.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
@ -464,7 +462,7 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-510
public void scrollShouldReadWhileTakeNotReached() {
IntStream.range(0, 100).forEach(it -> add(Collections.singletonMap(it + "-foo", "bar")).ofType(TYPE_I).to(INDEX_I));
IntStream.range(0, 100).forEach(it -> add(Collections.singletonMap(it + "-foo", "bar")).to(INDEX_I));
SearchRequest request = new SearchRequest(INDEX_I) //
.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
@ -479,9 +477,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void indexExistsShouldReturnTrueIfSo() throws IOException {
public void indexExistsShouldReturnTrueIfSo() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().existsIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
@ -490,9 +488,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void indexExistsShouldReturnFalseIfNot() throws IOException {
public void indexExistsShouldReturnFalseIfNot() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().existsIndex(request -> request.indices(INDEX_II)) //
.as(StepVerifier::create) //
@ -501,20 +499,23 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569, DATAES-678
public void createIndex() throws IOException {
public void createIndex() {
client.indices().createIndex(request -> request.index(INDEX_I)) //
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
assertThat(syncClient.indices().exists(new GetIndexRequest(INDEX_I), RequestOptions.DEFAULT)).isTrue();
operations.indexOps(IndexCoordinates.of(INDEX_I)).exists() //
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
@Test // DATAES-569
public void createExistingIndexErrors() throws IOException {
public void createExistingIndexErrors() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().createIndex(request -> request.index(INDEX_I)) //
.as(StepVerifier::create) //
@ -522,16 +523,20 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569, DATAES-678
public void deleteExistingIndex() throws IOException {
public void deleteExistingIndex() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().deleteIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
assertThat(syncClient.indices().exists(new GetIndexRequest(INDEX_I), RequestOptions.DEFAULT)).isFalse();
operations.indexOps(IndexCoordinates.of(INDEX_I)) //
.exists() //
.as(StepVerifier::create) //
.expectNext(false) //
.verifyComplete();
}
@Test // DATAES-569, DATAES-767
@ -543,9 +548,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void openExistingIndex() throws IOException {
public void openExistingIndex() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().openIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
@ -561,9 +566,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void closeExistingIndex() throws IOException {
public void closeExistingIndex() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().openIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
@ -579,9 +584,9 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void refreshIndex() throws IOException {
public void refreshIndex() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().refreshIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
@ -597,14 +602,14 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-569
public void updateMapping() throws IOException {
public void updateMapping() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
Map<String, Object> jsonMap = Collections.singletonMap("properties",
Collections.singletonMap("message", Collections.singletonMap("type", "text")));
client.indices().updateMapping(request -> request.indices(INDEX_I).type(TYPE_I).source(jsonMap)) //
client.indices().updateMapping(request -> request.indices(INDEX_I).source(jsonMap)) //
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
@ -616,15 +621,15 @@ public class ReactiveElasticsearchClientTests {
Map<String, Object> jsonMap = Collections.singletonMap("properties",
Collections.singletonMap("message", Collections.singletonMap("type", "text")));
client.indices().updateMapping(request -> request.indices(INDEX_I).type(TYPE_I).source(jsonMap)) //
client.indices().updateMapping(request -> request.indices(INDEX_I).source(jsonMap)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
}
@Test // DATAES-569
public void flushIndex() throws IOException {
public void flushIndex() {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();
client.indices().flushIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
@ -641,8 +646,8 @@ public class ReactiveElasticsearchClientTests {
@Test // DATAES-684
public void bulkShouldUpdateExistingDocument() {
String idFirstDoc = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String idSecondDoc = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
String idFirstDoc = addSourceDocument().to(INDEX_I);
String idSecondDoc = addSourceDocument().to(INDEX_I);
UpdateRequest requestFirstDoc = new UpdateRequest(INDEX_I, idFirstDoc) //
.doc(Collections.singletonMap("dutiful", "farseer"));
@ -666,13 +671,15 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-567
public void aggregateReturnsAggregationResults() throws IOException {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
public void aggregateReturnsAggregationResults() {
ReactiveIndexOperations indexOps = operations.indexOps(IndexCoordinates.of(INDEX_I));
indexOps.create().block();
Map<String, Object> jsonMap = Collections.singletonMap("properties",
Collections.singletonMap("firstname", Collections.singletonMap("type", "keyword")));
syncClient.indices().putMapping(new PutMappingRequest(INDEX_I).source(jsonMap), RequestOptions.DEFAULT);
indexOps.putMapping(Mono.just(Document.from(jsonMap))).block();
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
searchSourceBuilder.aggregation(AggregationBuilders.terms("terms").field("firstname"));
@ -685,13 +692,14 @@ public class ReactiveElasticsearchClientTests {
}
@Test // DATAES-866
public void suggestReturnsSuggestionResults() throws IOException {
syncClient.indices().create(new CreateIndexRequest(INDEX_I), RequestOptions.DEFAULT);
public void suggestReturnsSuggestionResults() {
ReactiveIndexOperations indexOps = operations.indexOps(IndexCoordinates.of(INDEX_I));
indexOps.create().block();
Map<String, Object> jsonMap = Collections.singletonMap("properties",
Collections.singletonMap("firstname", Collections.singletonMap("type", "completion")));
syncClient.indices().putMapping(new PutMappingRequest(INDEX_I).source(jsonMap), RequestOptions.DEFAULT);
indexOps.putMapping(Mono.just(Document.from(jsonMap))).block();
addSourceDocument().ofType(TYPE_I).to(INDEX_I);
addSourceDocument().to(INDEX_I);
SuggestBuilder suggestBuilder = new SuggestBuilder().addSuggestion("firstname",
new CompletionSuggestionBuilder("firstname").prefix("ch"));
@ -708,55 +716,43 @@ public class ReactiveElasticsearchClientTests {
.verifyComplete();
}
private AddToIndexOfType addSourceDocument() {
private AddToIndex addSourceDocument() {
return add(DOC_SOURCE);
}
private AddToIndexOfType add(Map<String, ?> source) {
private AddToIndex add(Map<String, ?> source) {
return new AddDocument(source);
}
private IndexRequest indexRequest(Map source, String index, String type) {
private IndexRequest indexRequest() {
return new IndexRequest(index) //
return new IndexRequest(ReactiveElasticsearchClientTests.INDEX_I) //
.id(UUID.randomUUID().toString()) //
.source(source) //
.source(ReactiveElasticsearchClientTests.DOC_SOURCE) //
.setRefreshPolicy(RefreshPolicy.IMMEDIATE) //
.create(true);
}
@SneakyThrows
private String doIndex(Map<?, ?> source, String index, String type) {
return syncClient.index(indexRequest(source, index, type), RequestOptions.DEFAULT).getId();
}
interface AddToIndexOfType extends AddToIndex {
AddToIndex ofType(String type);
private String doIndex(Map<String, ?> source, String index) {
return operations.save(source, IndexCoordinates.of(index)).block().get("id").toString();
}
interface AddToIndex {
String to(String index);
}
class AddDocument implements AddToIndexOfType {
class AddDocument implements AddToIndex {
Map<String, ?> source;
@Nullable String type;
AddDocument(Map<String, ?> source) {
this.source = source;
}
@Override
public AddToIndex ofType(String type) {
this.type = type;
return this;
}
@Override
public String to(String index) {
return doIndex(new LinkedHashMap<>(source), index, type);
return doIndex(new LinkedHashMap<>(source), index);
}
}

View File

@ -28,6 +28,7 @@ import lombok.NoArgsConstructor;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.lang.Boolean;
import java.lang.Long;
import java.lang.Object;
import java.net.ConnectException;
@ -49,6 +50,9 @@ import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
@ -56,7 +60,6 @@ import org.springframework.data.annotation.Version;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.TestUtils;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
@ -64,7 +67,7 @@ import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersion;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.util.StringUtils;
@ -83,25 +86,26 @@ import org.springframework.util.StringUtils;
@SpringIntegrationTest
public class ReactiveElasticsearchTemplateTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
static class Config {}
static final String DEFAULT_INDEX = "reactive-template-test-index";
static final String ALTERNATE_INDEX = "reactive-template-tests-alternate-index";
private ElasticsearchRestTemplate restTemplate;
private ReactiveElasticsearchTemplate template;
private IndexOperations indexOperations;
@Autowired private ReactiveElasticsearchTemplate template;
private ReactiveIndexOperations indexOperations;
@BeforeEach
public void setUp() {
restTemplate = new ElasticsearchRestTemplate(TestUtils.restHighLevelClient());
indexOperations = restTemplate.indexOps(SampleEntity.class);
indexOperations = template.indexOps(SampleEntity.class);
deleteIndices();
indexOperations.create();
indexOperations.putMapping(SampleEntity.class);
indexOperations.refresh();
template = new ReactiveElasticsearchTemplate(TestUtils.reactiveClient(), restTemplate.getElasticsearchConverter());
indexOperations.create() //
.then(indexOperations.putMapping(SampleEntity.class)) //
.then(indexOperations.refresh()) //
.block(); //
}
@AfterEach
@ -110,9 +114,13 @@ public class ReactiveElasticsearchTemplateTests {
}
private void deleteIndices() {
TestUtils.deleteIndex(DEFAULT_INDEX, ALTERNATE_INDEX, "rx-template-test-index-this", "rx-template-test-index-that",
"test-index-reactive-optimistic-entity-template",
"test-index-reactive-optimistic-and-versioned-entity-template");
template.indexOps(IndexCoordinates.of(DEFAULT_INDEX)).delete().block();
template.indexOps(IndexCoordinates.of(ALTERNATE_INDEX)).delete().block();
template.indexOps(IndexCoordinates.of("rx-template-test-index-this")).delete().block();
template.indexOps(IndexCoordinates.of("rx-template-test-index-that")).delete().block();
template.indexOps(IndexCoordinates.of("test-index-reactive-optimistic-entity-template")).delete().block();
template.indexOps(IndexCoordinates.of("test-index-reactive-optimistic-and-versioned-entity-template")).delete()
.block();
}
@Test // DATAES-504
@ -147,10 +155,12 @@ public class ReactiveElasticsearchTemplateTests {
indexOperations.refresh();
SearchHits<SampleEntity> result = restTemplate.search(
new CriteriaQuery(Criteria.where("message").is(sampleEntity.getMessage())), SampleEntity.class,
IndexCoordinates.of(DEFAULT_INDEX));
assertThat(result).hasSize(1);
template
.search(new CriteriaQuery(Criteria.where("message").is(sampleEntity.getMessage())), SampleEntity.class,
IndexCoordinates.of(DEFAULT_INDEX)) //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
}
@Test // DATAES-504
@ -159,17 +169,17 @@ public class ReactiveElasticsearchTemplateTests {
SampleEntity sampleEntity = SampleEntity.builder().message("wohoo").build();
template.save(sampleEntity) //
.as(StepVerifier::create) //
.consumeNextWith(it -> {
assertThat(it.getId()).isNotNull();
indexOperations.refresh();
assertThat(TestUtils.documentWithId(it.getId()).existsIn(DEFAULT_INDEX)).isTrue();
}) //
.map(SampleEntity::getId) //
.flatMap(id -> indexOperations.refresh().thenReturn(id)) //
.flatMap(id -> documentWithIdExistsInIndex(id, DEFAULT_INDEX)).as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
private Mono<Boolean> documentWithIdExistsInIndex(String id, String index) {
return template.exists(id, IndexCoordinates.of(index));
}
@Test // DATAES-504
public void insertWithExplicitIndexNameShouldOverwriteMetadata() {
@ -181,11 +191,11 @@ public class ReactiveElasticsearchTemplateTests {
.expectNextCount(1)//
.verifyComplete();
restTemplate.refresh(IndexCoordinates.of(DEFAULT_INDEX));
restTemplate.refresh(alternateIndex);
template.indexOps(IndexCoordinates.of(DEFAULT_INDEX)).refresh().block();
template.indexOps(alternateIndex).refresh().block();
assertThat(TestUtils.documentWithId(sampleEntity.getId()).existsIn(DEFAULT_INDEX)).isFalse();
assertThat(TestUtils.documentWithId(sampleEntity.getId()).existsIn(ALTERNATE_INDEX)).isTrue();
assertThat(documentWithIdExistsInIndex(sampleEntity.getId(), DEFAULT_INDEX).block()).isFalse();
assertThat(documentWithIdExistsInIndex(sampleEntity.getId(), ALTERNATE_INDEX).block()).isTrue();
}
@Test // DATAES-504
@ -266,16 +276,14 @@ public class ReactiveElasticsearchTemplateTests {
SampleEntity sampleEntity = randomEntity("some message");
IndexQuery indexQuery = getIndexQuery(sampleEntity);
IndexCoordinates defaultIndex = IndexCoordinates.of(DEFAULT_INDEX);
IndexCoordinates alternateIndex = IndexCoordinates.of(ALTERNATE_INDEX);
restTemplate.index(indexQuery, alternateIndex);
indexOperations.refresh();
restTemplate.indexOps(defaultIndex).refresh();
restTemplate.indexOps(alternateIndex).refresh();
template.save(sampleEntity, alternateIndex) //
.then(indexOperations.refresh()) //
.then(template.indexOps(defaultIndex).refresh()) //
.then(template.indexOps(alternateIndex).refresh()) //
.block();
template.get(sampleEntity.getId(), SampleEntity.class, defaultIndex) //
.as(StepVerifier::create) //
@ -612,7 +620,6 @@ public class ReactiveElasticsearchTemplateTests {
}
@Test // DATAES-519
@ElasticsearchVersion(asOf = "6.5.0")
public void deleteByQueryShouldReturnZeroWhenIndexDoesNotExist() {
CriteriaQuery query = new CriteriaQuery(new Criteria("message").contains("test"));
@ -624,7 +631,6 @@ public class ReactiveElasticsearchTemplateTests {
}
@Test // DATAES-547
@ElasticsearchVersion(asOf = "6.5.0")
public void shouldDeleteAcrossIndex() {
String indexPrefix = "rx-template-test-index";
@ -637,8 +643,7 @@ public class ReactiveElasticsearchTemplateTests {
.as(StepVerifier::create)//
.verifyComplete();
restTemplate.refresh(thisIndex);
restTemplate.refresh(thatIndex);
template.indexOps(thisIndex).refresh().then(template.indexOps(thatIndex).refresh()).block();
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() //
.withQuery(termQuery("message", "test")) //
@ -649,11 +654,10 @@ public class ReactiveElasticsearchTemplateTests {
.expectNext(2L) //
.verifyComplete();
TestUtils.deleteIndex(thisIndex.getIndexName(), thatIndex.getIndexName());
template.indexOps(thisIndex).delete().then(template.indexOps(thatIndex).delete()).block();
}
@Test // DATAES-547
@ElasticsearchVersion(asOf = "6.5.0")
public void shouldDeleteAcrossIndexWhenNoMatchingDataPresent() {
String indexPrefix = "rx-template-test-index";
@ -666,8 +670,7 @@ public class ReactiveElasticsearchTemplateTests {
.as(StepVerifier::create)//
.verifyComplete();
restTemplate.refresh(thisIndex);
restTemplate.refresh(thatIndex);
template.indexOps(thisIndex).refresh().then(template.indexOps(thatIndex).refresh()).block();
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() //
.withQuery(termQuery("message", "negative")) //
@ -678,11 +681,10 @@ public class ReactiveElasticsearchTemplateTests {
.expectNext(0L) //
.verifyComplete();
TestUtils.deleteIndex(thisIndex.getIndexName(), thatIndex.getIndexName());
template.indexOps(thisIndex).delete().then(template.indexOps(thatIndex).delete()).block();
}
@Test // DATAES-504
@ElasticsearchVersion(asOf = "6.5.0")
public void deleteByQueryShouldReturnNumberOfDeletedDocuments() {
index(randomEntity("test message"), randomEntity("test test"), randomEntity("some message"));
@ -696,7 +698,6 @@ public class ReactiveElasticsearchTemplateTests {
}
@Test // DATAES-504
@ElasticsearchVersion(asOf = "6.5.0")
public void deleteByQueryShouldReturnZeroIfNothingDeleted() {
index(randomEntity("test message"));
@ -896,7 +897,8 @@ public class ReactiveElasticsearchTemplateTests {
OptimisticEntity original = new OptimisticEntity();
original.setMessage("It's fine");
OptimisticEntity saved = template.save(original).block();
restTemplate.refresh(OptimisticEntity.class);
template.indexOps(OptimisticEntity.class).refresh().block();
template
.search(searchQueryForOne(saved.getId()), OptimisticEntity.class,
@ -1050,12 +1052,10 @@ public class ReactiveElasticsearchTemplateTests {
IndexCoordinates indexCoordinates = IndexCoordinates.of(DEFAULT_INDEX);
if (entities.length == 1) {
restTemplate.index(getIndexQuery(entities[0]), indexCoordinates);
template.save(entities[0], indexCoordinates).then(indexOperations.refresh()).block();
} else {
restTemplate.bulkIndex(getIndexQueries(entities), indexCoordinates);
template.saveAll(Mono.just(Arrays.asList(entities)), indexCoordinates).then(indexOperations.refresh()).block();
}
indexOperations.refresh();
}
@Data

View File

@ -1,45 +0,0 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch.junit.junit4;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Christoph Strobl
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ElasticsearchVersion {
/**
* Inclusive lower bound of Elasticsearch server range.
*
* @return {@code 0.0.0} by default.
*/
String asOf() default "0.0.0";
/**
* Exclusive upper bound of Elasticsearch server range.
*
* @return {@code 9999.9999.9999} by default.
*/
String until() default "9999.9999.9999";
}

View File

@ -1,132 +0,0 @@
/*
* Copyright 2018-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch.junit.junit4;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.TestUtils;
import org.springframework.data.util.Version;
/**
* @author Christoph Strobl
*/
public class ElasticsearchVersionRule implements TestRule {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchVersionRule.class);
private static final Version ANY = new Version(9999, 9999, 9999);
private static final Version DEFAULT_HIGH = ANY;
private static final Version DEFAULT_LOW = new Version(0, 0, 0);
private final static AtomicReference<Version> currentVersion = new AtomicReference<>(null);
private final Version minVersion;
private final Version maxVersion;
public ElasticsearchVersionRule(Version min, Version max) {
this.minVersion = min;
this.maxVersion = max;
}
public static ElasticsearchVersionRule any() {
return new ElasticsearchVersionRule(ANY, ANY);
}
public static ElasticsearchVersionRule atLeast(Version minVersion) {
return new ElasticsearchVersionRule(minVersion, DEFAULT_HIGH);
}
public static ElasticsearchVersionRule atMost(Version maxVersion) {
return new ElasticsearchVersionRule(DEFAULT_LOW, maxVersion);
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (!getCurrentVersion().equals(ANY)) {
Version minVersion = ElasticsearchVersionRule.this.minVersion.equals(ANY) ? DEFAULT_LOW
: ElasticsearchVersionRule.this.minVersion;
Version maxVersion = ElasticsearchVersionRule.this.maxVersion.equals(ANY) ? DEFAULT_HIGH
: ElasticsearchVersionRule.this.maxVersion;
if (description.getAnnotation(ElasticsearchVersion.class) != null) {
ElasticsearchVersion version = description.getAnnotation(ElasticsearchVersion.class);
if (version != null) {
Version expectedMinVersion = Version.parse(version.asOf());
if (!expectedMinVersion.equals(ANY) && !expectedMinVersion.equals(DEFAULT_LOW)) {
minVersion = expectedMinVersion;
}
Version expectedMaxVersion = Version.parse(version.until());
if (!expectedMaxVersion.equals(ANY) && !expectedMaxVersion.equals(DEFAULT_HIGH)) {
maxVersion = expectedMaxVersion;
}
}
}
validateVersion(minVersion, maxVersion);
}
base.evaluate();
}
};
}
private void validateVersion(Version min, Version max) {
if (getCurrentVersion().isLessThan(min) || getCurrentVersion().isGreaterThanOrEqualTo(max)) {
throw new AssumptionViolatedException(String
.format("Expected Elasticsearch server to be in range (%s, %s] but found %s", min, max, currentVersion));
}
}
private Version getCurrentVersion() {
if (currentVersion.get() == null) {
Version current = fetchCurrentVersion();
if (currentVersion.compareAndSet(null, current)) {
logger.info("Running Elasticsearch " + current);
}
}
return currentVersion.get();
}
private Version fetchCurrentVersion() {
return TestUtils.serverVersion();
}
@Override
public String toString() {
return getCurrentVersion().toString();
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch.junit.junit4;
import java.io.IOException;
import org.elasticsearch.client.Client;
import org.elasticsearch.node.Node;
import org.junit.rules.ExternalResource;
import org.springframework.data.elasticsearch.Utils;
import org.springframework.util.Assert;
/**
* JUnit4 Rule that sets up and tears down a local Elasticsearch node.
*
* @author Peter-Josef Meisch
*/
public class TestNodeResource extends ExternalResource {
private Node node;
@Override
protected void before() throws Throwable {
node = Utils.getNode();
node.start();
}
@Override
protected void after() {
if (node != null) {
try {
node.close();
} catch (IOException ignored) {}
}
}
public Client client() {
Assert.notNull(node, "node is not initialized");
return node.client();
}
}

View File

@ -1,5 +0,0 @@
/**
* interfaces, annotations and classes related to JUnit 4 test handling.
*/
@org.springframework.lang.NonNullApi
package org.springframework.data.elasticsearch.junit.junit4;

View File

@ -15,42 +15,44 @@
*/
package org.springframework.data.elasticsearch.junit.jupiter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.Utils;
import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
/**
* This class manages the connection to an Elasticsearch Cluster, starting a local one if necessary. The information
* about the ClusterConnection is stored both as a variable in the instance for direct access from JUnit 5 and in a
* static ThreadLocal<ClusterConnectionInfo> accessible with the {@link ClusterConnection#clusterConnectionInfo()}
* method to be integrated in the Spring setup
* This class manages the connection to an Elasticsearch Cluster, starting a containerized one if necessary. The
* information about the ClusterConnection is stored both as a variable in the instance for direct access from JUnit 5
* and in a static ThreadLocal<ClusterConnectionInfo> accessible with the
* {@link ClusterConnection#clusterConnectionInfo()} method to be integrated in the Spring setup
*
* @author Peter-Josef Meisch
*/
class ClusterConnection implements ExtensionContext.Store.CloseableResource {
public class ClusterConnection implements ExtensionContext.Store.CloseableResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterConnection.class);
private static final int ELASTICSEARCH_DEFAULT_PORT = 9200;
private static final int ELASTICSEARCH_DEFAULT_TRANSPORT_PORT = 9300;
private static final String ELASTICSEARCH_DEFAULT_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch";
private static final ThreadLocal<ClusterConnectionInfo> clusterConnectionInfoThreadLocal = new ThreadLocal<>();
private Node node;
private final ClusterConnectionInfo clusterConnectionInfo;
@Nullable private final ClusterConnectionInfo clusterConnectionInfo;
/**
* creates the ClusterConnection, starting a local node if necessary.
* creates the ClusterConnection, starting a container if necessary.
*
* @param clusterUrl if null or empty a local cluster is tarted
*/
public ClusterConnection(@Nullable String clusterUrl) {
clusterConnectionInfo = StringUtils.isEmpty(clusterUrl) ? startLocalNode() : parseUrl(clusterUrl);
clusterConnectionInfo = StringUtils.isEmpty(clusterUrl) ? startElasticsearchContainer() : parseUrl(clusterUrl);
if (clusterConnectionInfo != null) {
LOGGER.debug(clusterConnectionInfo.toString());
@ -68,6 +70,7 @@ class ClusterConnection implements ExtensionContext.Store.CloseableResource {
return clusterConnectionInfoThreadLocal.get();
}
@Nullable
public ClusterConnectionInfo getClusterConnectionInfo() {
return clusterConnectionInfo;
}
@ -94,18 +97,27 @@ class ClusterConnection implements ExtensionContext.Store.CloseableResource {
}
private @Nullable ClusterConnectionInfo startLocalNode() {
LOGGER.debug("starting local node");
@Nullable
private ClusterConnectionInfo startElasticsearchContainer() {
LOGGER.debug("Starting Elasticsearch Container");
try {
node = Utils.getNode();
node.start();
String elasticsearchVersion = VersionInfo.versionProperties()
.getProperty(VersionInfo.VERSION_ELASTICSEARCH_CLIENT);
String dockerImageName = ELASTICSEARCH_DEFAULT_IMAGE + ':' + elasticsearchVersion;
LOGGER.debug("Docker image: {}", dockerImageName);
ElasticsearchContainer elasticsearchContainer = new ElasticsearchContainer(dockerImageName);
elasticsearchContainer.start();
return ClusterConnectionInfo.builder() //
.withHostAndPort("localhost", 9200) //
.withClient(node.client()) //
.withHostAndPort(elasticsearchContainer.getHost(),
elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_PORT)) //
.withTransportPort(elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_TRANSPORT_PORT)) //
.withElasticsearchContainer(elasticsearchContainer) //
.build();
} catch (NodeValidationException e) {
LOGGER.error("could not start local node", e);
} catch (Exception e) {
LOGGER.error("Could not start Elasticsearch container", e);
}
return null;
@ -114,12 +126,11 @@ class ClusterConnection implements ExtensionContext.Store.CloseableResource {
@Override
public void close() {
if (node != null) {
LOGGER.debug("closing node");
try {
node.close();
} catch (IOException ignored) {}
if (clusterConnectionInfo != null && clusterConnectionInfo.getElasticsearchContainer() != null) {
LOGGER.debug("Stopping container");
clusterConnectionInfo.getElasticsearchContainer().stop();
}
LOGGER.debug("closed");
}
}

View File

@ -15,13 +15,12 @@
*/
package org.springframework.data.elasticsearch.junit.jupiter;
import org.elasticsearch.client.Client;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
/**
* The information about the ClusterConnection. the {@link #client} field is only set if a local node is started,
* otherwise it is null. <br/>
* The information about the ClusterConnection.<br/>
* The {@link #host}, {@link #httpPort} and {@link #useSsl} values specify the values needed to connect to the cluster
* with a rest client for both a local started cluster and for one defined by the cluster URL when creating the
* {@link ClusterConnection}.<br/>
@ -33,23 +32,32 @@ public final class ClusterConnectionInfo {
private final boolean useSsl;
private final String host;
private final int httpPort;
private final Client client;
private final int transportPort;
private final String clusterName;
@Nullable private final ElasticsearchContainer elasticsearchContainer;
public static Builder builder() {
return new Builder();
}
private ClusterConnectionInfo(String host, int httpPort, boolean useSsl, Client client) {
private ClusterConnectionInfo(String host, int httpPort, boolean useSsl, int transportPort,
@Nullable ElasticsearchContainer elasticsearchContainer) {
this.host = host;
this.httpPort = httpPort;
this.useSsl = useSsl;
this.client = client;
this.transportPort = transportPort;
this.elasticsearchContainer = elasticsearchContainer;
this.clusterName = "docker-cluster";
}
@Override
public String toString() {
return "ClusterConnectionInfo{" + "useSsl=" + useSsl + ", host='" + host + '\'' + ", httpPort=" + httpPort
+ ", client=" + client + '}';
return "ClusterConnectionInfo{" + //
"useSsl=" + useSsl + //
", host='" + host + '\'' + //
", httpPort=" + httpPort + //
", transportPort=" + transportPort + //
'}'; //
}
public String getHost() {
@ -60,20 +68,29 @@ public final class ClusterConnectionInfo {
return httpPort;
}
public int getTransportPort() {
return transportPort;
}
public String getClusterName() {
return clusterName;
}
public boolean isUseSsl() {
return useSsl;
}
@Nullable
public Client getClient() {
return client;
public ElasticsearchContainer getElasticsearchContainer() {
return elasticsearchContainer;
}
public static class Builder {
boolean useSsl = false;
private String host;
private int httpPort;
private Client client = null;
private int transportPort;
@Nullable private ElasticsearchContainer elasticsearchContainer;
public Builder withHostAndPort(String host, int httpPort) {
Assert.hasLength(host, "host must not be empty");
@ -87,13 +104,18 @@ public final class ClusterConnectionInfo {
return this;
}
public Builder withClient(Client client) {
this.client = client;
public Builder withTransportPort(int transportPort) {
this.transportPort = transportPort;
return this;
}
public Builder withElasticsearchContainer(ElasticsearchContainer elasticsearchContainer) {
this.elasticsearchContainer = elasticsearchContainer;
return this;
}
public ClusterConnectionInfo build() {
return new ClusterConnectionInfo(host, httpPort, useSsl, client);
return new ClusterConnectionInfo(host, httpPort, useSsl, transportPort, elasticsearchContainer);
}
}
}

View File

@ -15,7 +15,14 @@
*/
package org.springframework.data.elasticsearch.junit.jupiter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
@ -32,8 +39,14 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverte
public class ElasticsearchTemplateConfiguration extends ElasticsearchConfigurationSupport {
@Bean
public Client elasticsearchClient(ClusterConnectionInfo clusterConnectionInfo) {
return clusterConnectionInfo.getClient();
public Client elasticsearchClient(ClusterConnectionInfo clusterConnectionInfo) throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", clusterConnectionInfo.getClusterName()).build();
TransportClient client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new TransportAddress(InetAddress.getByName(clusterConnectionInfo.getHost()),
clusterConnectionInfo.getTransportPort()));
return client;
}
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })

View File

@ -35,12 +35,14 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.junit.jupiter.SpringDataElasticsearchExtension;
import org.springframework.lang.Nullable;
/**
@ -49,6 +51,7 @@ import org.springframework.lang.Nullable;
* @author Christoph Strobl
* @author Peter-Josef Meisch
*/
@ExtendWith(SpringDataElasticsearchExtension.class)
public class CdiRepositoryTests {
@Nullable private static CdiTestContainer cdiContainer;

View File

@ -19,33 +19,31 @@ import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import org.elasticsearch.client.Client;
import org.elasticsearch.node.NodeValidationException;
import org.springframework.data.elasticsearch.Utils;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.junit.jupiter.ClusterConnection;
import org.springframework.data.elasticsearch.junit.jupiter.ClusterConnectionInfo;
/**
* @author Mohsin Husen
* @author Peter-Josef Meisch
*/
@ApplicationScoped
class ElasticsearchTemplateProducer {
class ElasticsearchOperationsProducer {
@Produces
public Client createNodeClient() throws NodeValidationException {
return Utils.getNodeClient();
}
@Produces
public ElasticsearchOperations createElasticsearchTemplate(Client client) {
return new ElasticsearchTemplate(client);
public ElasticsearchOperations createElasticsearchTemplate(RestHighLevelClient restHighLevelClient) {
return new ElasticsearchRestTemplate(restHighLevelClient);
}
@Produces
@OtherQualifier
@PersonDB
public ElasticsearchOperations createQualifiedElasticsearchTemplate(Client client) {
return new ElasticsearchTemplate(client);
public ElasticsearchOperations createQualifiedElasticsearchTemplate(RestHighLevelClient restHighLevelClient) {
return new ElasticsearchRestTemplate(restHighLevelClient);
}
@PreDestroy
@ -53,4 +51,15 @@ class ElasticsearchTemplateProducer {
// remove everything to avoid conflicts with other tests in case server not shut down properly
}
@Produces
public RestHighLevelClient elasticsearchClient() {
// we rely on the tests being run with the SpringDataElasticsearchExtension class that sets up a containerized ES.
ClusterConnectionInfo connectionInfo = ClusterConnection.clusterConnectionInfo();
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
.connectedTo(connectionInfo.getHost() + ':' + connectionInfo.getHttpPort()) //
.build();
return RestClients.create(clientConfiguration).rest();
}
}

View File

@ -26,13 +26,12 @@ import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.TestUtils;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.test.context.ContextConfiguration;
@ -46,14 +45,9 @@ import org.springframework.test.context.ContextConfiguration;
public class ReactiveElasticsearchRepositoriesRegistrarTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
public ReactiveElasticsearchTemplate reactiveElasticsearchTemplate() {
return new ReactiveElasticsearchTemplate(TestUtils.reactiveClient());
}
}
static class Config {}
@Autowired ReactiveSampleEntityRepository repository;
@Autowired ApplicationContext context;
@ -72,8 +66,7 @@ public class ReactiveElasticsearchRepositoriesRegistrarTests {
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(indexName = "test-index-sample-reactive-repositories-registrar",
replicas = 0, refreshInterval = "-1")
@Document(indexName = "test-index-sample-reactive-repositories-registrar", replicas = 0, refreshInterval = "-1")
static class SampleEntity {
@Id private String id;

View File

@ -27,23 +27,13 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import java.lang.Boolean;
import java.lang.Long;
import java.lang.Object;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.IntStream;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -57,22 +47,19 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.elasticsearch.TestUtils;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.config.AbstractReactiveElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.util.StringUtils;
/**
* @author Christoph Strobl
@ -83,38 +70,37 @@ import org.springframework.util.StringUtils;
public class SimpleReactiveElasticsearchRepositoryTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config extends AbstractReactiveElasticsearchConfiguration {
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
return TestUtils.reactiveClient();
}
}
static class Config {}
static final String INDEX = "test-index-sample-simple-reactive";
static final String TYPE = "test-type";
@Autowired ReactiveSampleEntityRepository repository;
@Autowired ReactiveElasticsearchOperations operations;
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired ReactiveSampleEntityRepository repository;
@BeforeEach
public void setUp() {
TestUtils.deleteIndex(INDEX);
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
}
@AfterEach
void after() {
TestUtils.deleteIndex(INDEX);
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
}
@Test // DATAES-519
public void saveShouldSaveSingleEntity() {
repository.save(SampleEntity.builder().build()) //
.map(SampleEntity::getId) //
.flatMap(this::documentWithIdExistsInIndex) //
.as(StepVerifier::create) //
.consumeNextWith(it -> assertThat(TestUtils.documentWithId(it.getId()).existsIn(INDEX)).isTrue()) //
.verifyComplete();
.expectNext(true).verifyComplete();
}
private Mono<Boolean> documentWithIdExistsInIndex(String id) {
return operations.exists(id, IndexCoordinates.of(INDEX));
}
@Test // DATAES-519
@ -123,10 +109,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
repository
.saveAll(Arrays.asList(SampleEntity.builder().build(), SampleEntity.builder().build(),
SampleEntity.builder().build())) //
.map(SampleEntity::getId) //
.flatMap(this::documentWithIdExistsInIndex) //
.as(StepVerifier::create) //
.consumeNextWith(it -> assertThat(TestUtils.documentWithId(it.getId()).existsIn(INDEX)).isTrue()) //
.consumeNextWith(it -> assertThat(TestUtils.documentWithId(it.getId()).existsIn(INDEX)).isTrue()) //
.consumeNextWith(it -> assertThat(TestUtils.documentWithId(it.getId()).existsIn(INDEX)).isTrue()) //
.expectNext(true) //
.expectNext(true) //
.expectNext(true) //
.verifyComplete();
}
@ -138,11 +126,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void findShouldRetrieveSingleEntityById() throws IOException {
public void findShouldRetrieveSingleEntityById() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build(), //
SampleEntity.builder().id("id-three").build());
SampleEntity.builder().id("id-three").build()) //
.block();
repository.findById("id-two").as(StepVerifier::create)//
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-two")) //
@ -150,23 +139,25 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void findByIdShouldCompleteIfNothingFound() throws IOException {
public void findByIdShouldCompleteIfNothingFound() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build(), //
SampleEntity.builder().id("id-three").build());
SampleEntity.builder().id("id-three").build()) //
.block();
repository.findById("does-not-exist").as(StepVerifier::create) //
.verifyComplete();
}
@Test // DATAES-720
public void findAllShouldReturnAllElements() throws IOException {
public void findAllShouldReturnAllElements() {
// make sure to be above the default page size of the Query interface
int count = DEFAULT_PAGE_SIZE * 2;
bulkIndex(IntStream.range(1, count + 1) //
.mapToObj(it -> SampleEntity.builder().id(String.valueOf(it)).build()) //
.toArray(SampleEntity[]::new));
.toArray(SampleEntity[]::new)) //
.block();
repository.findAll() //
.as(StepVerifier::create) //
@ -180,11 +171,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void findAllByIdShouldRetrieveMatchingDocuments() throws IOException {
public void findAllByIdShouldRetrieveMatchingDocuments() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build(), //
SampleEntity.builder().id("id-three").build());
SampleEntity.builder().id("id-three").build()) //
.block();
repository.findAllById(Arrays.asList("id-one", "id-two")) //
.as(StepVerifier::create)//
@ -194,11 +186,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void findAllByIdShouldCompleteWhenNothingFound() throws IOException {
public void findAllByIdShouldCompleteWhenNothingFound() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build(), //
SampleEntity.builder().id("id-three").build());
SampleEntity.builder().id("id-three").build()) //
.block();
repository.findAllById(Arrays.asList("can't", "touch", "this")) //
.as(StepVerifier::create)//
@ -206,11 +199,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-717
void shouldReturnFluxOfSearchHit() throws IOException {
void shouldReturnFluxOfSearchHit() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("message").build(), //
SampleEntity.builder().id("id-three").message("message").build());
SampleEntity.builder().id("id-three").message("message").build()) //
.block();
repository.queryAllByMessage("message") //
.as(StepVerifier::create) //
@ -220,11 +214,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-717
void shouldReturnFluxOfSearchHitForStringQuery() throws IOException {
void shouldReturnFluxOfSearchHitForStringQuery() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("message").build(), //
SampleEntity.builder().id("id-three").message("message").build());
SampleEntity.builder().id("id-three").message("message").build()) //
.block();
repository.queryByMessageWithString("message") //
.as(StepVerifier::create) //
@ -234,11 +229,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-372
void shouldReturnHighlightsOnAnnotatedMethod() throws IOException {
void shouldReturnHighlightsOnAnnotatedMethod() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("message").build(), //
SampleEntity.builder().id("id-three").message("message").build());
SampleEntity.builder().id("id-three").message("message").build()) //
.block();
repository.queryAllByMessage("message") //
.as(StepVerifier::create) //
@ -251,11 +247,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-372
void shouldReturnHighlightsOnAnnotatedStringQueryMethod() throws IOException {
void shouldReturnHighlightsOnAnnotatedStringQueryMethod() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("message").build(), //
SampleEntity.builder().id("id-three").message("message").build());
SampleEntity.builder().id("id-three").message("message").build()) //
.block();
repository.queryByMessageWithString("message") //
.as(StepVerifier::create) //
@ -275,20 +272,22 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void countShouldCountDocuments() throws IOException {
public void countShouldCountDocuments() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build());
SampleEntity.builder().id("id-two").build()) //
.block();
repository.count().as(StepVerifier::create).expectNext(2L).verifyComplete();
}
@Test // DATAES-519
public void existsByIdShouldReturnTrueIfExists() throws IOException {
public void existsByIdShouldReturnTrueIfExists() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.existsById("id-two") //
.as(StepVerifier::create) //
@ -297,11 +296,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void existsByIdShouldReturnFalseIfNotExists() throws IOException {
public void existsByIdShouldReturnFalseIfNotExists() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.existsById("wrecking ball") //
.as(StepVerifier::create) //
@ -310,11 +310,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void countShouldCountMatchingDocuments() throws IOException {
public void countShouldCountMatchingDocuments() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.countAllByMessage("test") //
.as(StepVerifier::create) //
@ -323,11 +324,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void existsShouldReturnTrueIfExists() throws IOException {
public void existsShouldReturnTrueIfExists() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.existsAllByMessage("message") //
.as(StepVerifier::create) //
@ -336,11 +338,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void existsShouldReturnFalseIfNotExists() throws IOException {
public void existsShouldReturnFalseIfNotExists() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.existsAllByMessage("these days") //
.as(StepVerifier::create) //
@ -349,10 +352,11 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void deleteByIdShouldCompleteIfNothingDeleted() throws IOException {
public void deleteByIdShouldCompleteIfNothingDeleted() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build());
SampleEntity.builder().id("id-two").build()) //
.block();
repository.deleteById("does-not-exist").as(StepVerifier::create).verifyComplete();
}
@ -365,61 +369,69 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void deleteByIdShouldDeleteEntry() throws IOException {
public void deleteByIdShouldDeleteEntry() {
SampleEntity toBeDeleted = SampleEntity.builder().id("id-two").build();
bulkIndex(SampleEntity.builder().id("id-one").build(), toBeDeleted);
bulkIndex(SampleEntity.builder().id("id-one").build(), toBeDeleted) //
.block();
repository.deleteById(toBeDeleted.getId()).as(StepVerifier::create).verifyComplete();
assertThat(TestUtils.documentWithId(toBeDeleted.getId()).ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
}
@Test // DATAES-519
public void deleteShouldDeleteEntry() throws IOException {
public void deleteShouldDeleteEntry() {
SampleEntity toBeDeleted = SampleEntity.builder().id("id-two").build();
bulkIndex(SampleEntity.builder().id("id-one").build(), toBeDeleted);
bulkIndex(SampleEntity.builder().id("id-one").build(), toBeDeleted) //
.block();
repository.delete(toBeDeleted).as(StepVerifier::create).verifyComplete();
assertThat(TestUtils.documentWithId(toBeDeleted.getId()).ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
}
@Test // DATAES-519
public void deleteAllShouldDeleteGivenEntries() throws IOException {
public void deleteAllShouldDeleteGivenEntries() {
SampleEntity toBeDeleted = SampleEntity.builder().id("id-one").build();
SampleEntity hangInThere = SampleEntity.builder().id("id-two").build();
SampleEntity toBeDeleted2 = SampleEntity.builder().id("id-three").build();
bulkIndex(toBeDeleted, hangInThere, toBeDeleted2);
bulkIndex(toBeDeleted, hangInThere, toBeDeleted2) //
.block();
repository.deleteAll(Arrays.asList(toBeDeleted, toBeDeleted2)).as(StepVerifier::create).verifyComplete();
assertThat(TestUtils.documentWithId(toBeDeleted.getId()).ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(TestUtils.documentWithId(toBeDeleted2.getId()).ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(TestUtils.documentWithId(hangInThere.getId()).ofType(TYPE).existsIn(INDEX)).isTrue();
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
assertThat(documentWithIdExistsInIndex(toBeDeleted2.getId()).block()).isFalse();
assertThat(documentWithIdExistsInIndex(hangInThere.getId()).block()).isTrue();
}
@Test // DATAES-519
public void deleteAllShouldDeleteAllEntries() throws IOException {
public void deleteAllShouldDeleteAllEntries() {
bulkIndex(SampleEntity.builder().id("id-one").build(), //
SampleEntity.builder().id("id-two").build(), //
SampleEntity.builder().id("id-three").build());
SampleEntity.builder().id("id-three").build()) //
.block();
repository.deleteAll().as(StepVerifier::create).verifyComplete();
assertThat(TestUtils.isEmptyIndex(INDEX)).isTrue();
repository.count() //
.as(StepVerifier::create) //
.expectNext(0L) //
.verifyComplete();
}
@Test // DATAES-519
public void derivedFinderMethodShouldBeExecutedCorrectly() throws IOException {
public void derivedFinderMethodShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.findAllByMessageLike("test") //
.as(StepVerifier::create) //
@ -428,11 +440,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedFinderMethodShouldBeExecutedCorrectlyWhenGivenPublisher() throws IOException {
public void derivedFinderMethodShouldBeExecutedCorrectlyWhenGivenPublisher() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.findAllByMessage(Mono.just("test")) //
.as(StepVerifier::create) //
@ -441,11 +454,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedFinderWithDerivedSortMethodShouldBeExecutedCorrectly() throws IOException {
public void derivedFinderWithDerivedSortMethodShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("test").rate(3).build(), //
SampleEntity.builder().id("id-two").message("test test").rate(1).build(), //
SampleEntity.builder().id("id-three").message("test test").rate(2).build());
SampleEntity.builder().id("id-three").message("test test").rate(2).build()) //
.block();
repository.findAllByMessageLikeOrderByRate("test") //
.as(StepVerifier::create) //
@ -456,11 +470,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedFinderMethodWithSortParameterShouldBeExecutedCorrectly() throws IOException {
public void derivedFinderMethodWithSortParameterShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("test").rate(3).build(), //
SampleEntity.builder().id("id-two").message("test test").rate(1).build(), //
SampleEntity.builder().id("id-three").message("test test").rate(2).build());
SampleEntity.builder().id("id-three").message("test test").rate(2).build()) //
.block();
repository.findAllByMessage("test", Sort.by(Order.asc("rate"))) //
.as(StepVerifier::create) //
@ -471,11 +486,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedFinderMethodWithPageableParameterShouldBeExecutedCorrectly() throws IOException {
public void derivedFinderMethodWithPageableParameterShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("test").rate(3).build(), //
SampleEntity.builder().id("id-two").message("test test").rate(1).build(), //
SampleEntity.builder().id("id-three").message("test test").rate(2).build());
SampleEntity.builder().id("id-three").message("test test").rate(2).build()) //
.block();
repository.findAllByMessage("test", PageRequest.of(0, 2, Sort.by(Order.asc("rate")))) //
.as(StepVerifier::create) //
@ -485,11 +501,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedFinderMethodReturningMonoShouldBeExecutedCorrectly() throws IOException {
public void derivedFinderMethodReturningMonoShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.findFirstByMessageLike("test") //
.as(StepVerifier::create) //
@ -498,11 +515,12 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void annotatedFinderMethodShouldBeExecutedCorrectly() throws IOException {
public void annotatedFinderMethodShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.findAllViaAnnotatedQueryByMessageLike("test") //
.as(StepVerifier::create) //
@ -511,60 +529,25 @@ public class SimpleReactiveElasticsearchRepositoryTests {
}
@Test // DATAES-519
public void derivedDeleteMethodShouldBeExecutedCorrectly() throws IOException {
public void derivedDeleteMethodShouldBeExecutedCorrectly() {
bulkIndex(SampleEntity.builder().id("id-one").message("message").build(), //
SampleEntity.builder().id("id-two").message("test message").build(), //
SampleEntity.builder().id("id-three").message("test test").build());
SampleEntity.builder().id("id-three").message("test test").build()) //
.block();
repository.deleteAllByMessage("message") //
.as(StepVerifier::create) //
.expectNext(2L) //
.verifyComplete();
assertThat(TestUtils.documentWithId("id-one").ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(TestUtils.documentWithId("id-two").ofType(TYPE).existsIn(INDEX)).isFalse();
assertThat(TestUtils.documentWithId("id-three").ofType(TYPE).existsIn(INDEX)).isTrue();
assertThat(documentWithIdExistsInIndex("id-one").block()).isFalse();
assertThat(documentWithIdExistsInIndex("id-two").block()).isFalse();
assertThat(documentWithIdExistsInIndex("id-three").block()).isTrue();
}
private IndexRequest indexRequest(Map<String, ?> source, String index) {
return new IndexRequest(index) //
.id(source.containsKey("id") ? source.get("id").toString() : UUID.randomUUID().toString()) //
.source(source) //
.create(true);
}
private IndexRequest indexRequestFrom(SampleEntity entity) {
Map<String, Object> target = new LinkedHashMap<>();
if (StringUtils.hasText(entity.getId())) {
target.put("id", entity.getId());
}
if (StringUtils.hasText(entity.getType())) {
target.put("type", entity.getType());
}
if (StringUtils.hasText(entity.getMessage())) {
target.put("message", entity.getMessage());
}
target.put("rate", entity.getRate());
target.put("available", entity.isAvailable());
return indexRequest(target, INDEX);
}
void bulkIndex(SampleEntity... entities) throws IOException {
BulkRequest request = new BulkRequest();
Arrays.stream(entities).forEach(it -> request.add(indexRequestFrom(it)));
try (RestHighLevelClient client = TestUtils.restHighLevelClient()) {
client.bulk(request.setRefreshPolicy(RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT);
}
Mono<Void> bulkIndex(SampleEntity... entities) {
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(INDEX)).then();
}
interface ReactiveSampleEntityRepository extends ReactiveCrudRepository<SampleEntity, String> {
@ -617,7 +600,6 @@ public class SimpleReactiveElasticsearchRepositoryTests {
private int rate;
private boolean available;
@Version private Long version;
@Score private float score;
}
}

View File

@ -16,4 +16,7 @@
<appender-ref ref="console"/>
</root>
<logger name="org.testcontainers" level="INFO"/>
<logger name="com.github.dockerjava" level="WARN"/>
</configuration>

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Adds "built in" analyzers to Elasticsearch.
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=analysis-common
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.analysis.common.CommonAnalysisPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=lang-painless
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Module for ingest processors that do not require additional security permissions or have large dependencies and resources
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=ingest-common
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.ingest.common.IngestCommonPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=lang-painless
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Lucene expressions integration for Elasticsearch
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=lang-expression
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.script.expression.ExpressionPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,32 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
grant {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
// expression runtime
permission org.elasticsearch.script.ClassPermission "java.lang.String";
permission org.elasticsearch.script.ClassPermission "org.apache.lucene.expressions.Expression";
permission org.elasticsearch.script.ClassPermission "org.apache.lucene.search.DoubleValues";
// available functions
permission org.elasticsearch.script.ClassPermission "java.lang.Math";
permission org.elasticsearch.script.ClassPermission "org.apache.lucene.util.MathUtil";
permission org.elasticsearch.script.ClassPermission "org.apache.lucene.util.SloppyMath";
};

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=An easy, safe and fast scripting language for Elasticsearch
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=lang-painless
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.painless.PainlessPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,26 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
grant {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
// needed to find the classloader to load whitelisted classes from
permission java.lang.RuntimePermission "getClassLoader";
};

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Adds advanced field mappers
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=mapper-extras
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.index.mapper.MapperExtrasPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=The Reindex module adds APIs to reindex from one index to another or update documents in place.
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=reindex
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.index.reindex.ReindexPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,33 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
grant {
// reindex opens socket connections using the rest client
permission java.net.SocketPermission "*", "connect";
};
grant codeBase "${codebase.elasticsearch-rest-client}" {
// rest client uses system properties which gets the default proxy
permission java.net.NetPermission "getProxySelector";
};
grant codeBase "${codebase.httpasyncclient}" {
// rest client uses system properties which gets the default proxy
permission java.net.NetPermission "getProxySelector";
};

View File

@ -1,45 +0,0 @@
# Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' inside a plugin.
#
### example plugin for "foo"
#
# foo.zip <-- zip file for the plugin, with this structure:
# |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# |____ <arbitrary nameN>.jar <-- any number of jars
# |____ plugin-descriptor.properties <-- example contents below:
#
# classname=foo.bar.BazPlugin
# description=My cool plugin
# version=6.0
# elasticsearch.version=6.0
# java.version=1.8
#
### mandatory elements for all plugins:
#
# 'description': simple summary of the plugin
description=Module for URL repository
#
# 'version': plugin's version
version=7.8.1
#
# 'name': the plugin name
name=repository-url
#
# 'classname': the name of the class to load, fully-qualified.
classname=org.elasticsearch.plugin.repository.url.URLRepositoryPlugin
#
# 'java.version': version of java the code is built against
# use the system property java.specification.version
# version string must be a sequence of nonnegative decimal integers
# separated by "."'s and may have leading zeros
java.version=1.8
#
# 'elasticsearch.version': version of elasticsearch compiled against
elasticsearch.version=7.8.1
### optional elements for plugins:
#
# 'extended.plugins': other plugins this plugin extends through SPI
extended.plugins=
#
# 'has.native.controller': whether or not the plugin has a native controller
has.native.controller=false

View File

@ -1,22 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
grant {
permission java.net.SocketPermission "*", "connect";
};