DATAES-68 - Add support for auditing annotations.

Original PR: #400
This commit is contained in:
Peter-Josef Meisch 2020-03-11 18:39:11 +01:00 committed by GitHub
parent 0b0c8027a3
commit 300eb313dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1863 additions and 45 deletions

View File

@ -24,6 +24,8 @@ include::reference/elasticsearch-clients.adoc[]
include::reference/elasticsearch-object-mapping.adoc[]
include::reference/elasticsearch-operations.adoc[]
include::reference/elasticsearch-repositories.adoc[]
include::{spring-data-commons-docs}/auditing.adoc[]
include::reference/elasticsearch-auditing.adoc[]
include::reference/elasticsearch-misc.adoc[]
:leveloffset: -1

View File

@ -0,0 +1,68 @@
[[elasticsearch.auditing]]
== Elasticsearch Auditing
=== Preparing entities
In order for the auditing code to be able to decide wether an entity instance is new, the entity must implement the `Persistable<ID>` interface which is defined as follows:
[source,java]
----
package org.springframework.data.domain;
import org.springframework.lang.Nullable;
public interface Persistable<ID> {
@Nullable
ID getId();
boolean isNew();
}
----
As the existence of an Id is not a sufficient criterion to determine if an enitity is new in Elasticsearch, additional information is necessary. One way is to use the creation-relevant auditing fields for this decision:
A `Person` entity might look as follows - omitting getter and setter methods for brevity:
[source,java]
----
@Document(indexName = "person")
public class Person implements Persistable<Long> {
@Id private Long id;
private String lastName;
private String firstName;
@Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Instant createdDate;
private String createdBy
@Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Instant lastModifiedDate;
private String lastModifiedBy;
public Long getId() { <1>
return id;
}
@Override
public boolean isNew() {
return id == null || (createdDate == null && createdBy == null); <2>
}
}
----
<1> the getter also is the required implementation from the interface
<2> an object is new if it either has no `id` or none of fields containing creation attributes are set.
=== Activating auditing
After the entities have been set up and providing the `AuditorAware` the Auditing must be activated by setting the `@EnableElasticsearchAuditing` on a configuration class:
[source,java]
----
@Configuration
@EnableElasticsearchRepositories
@EnableElasticsearchAuditing
class MyConfiguration {
// configuration code
}
----
If your code contains more than one `AuditorAware` bean for different types, you must provide the name of the bean to use as an argument to the `auditorAwareRef` parameter of the
`@EnableElasticsearchAuditing` annotation.

View File

@ -0,0 +1,109 @@
/*
* Copyright 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.config;
import static org.springframework.data.config.ParsingUtils.*;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDefinitionParser;
import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} to register a {@link AuditingEntityCallback} to transparently set auditing information
* on an entity.
*
* @author Peter-Josef Meisch
*/
public class ElasticsearchAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static String MAPPING_CONTEXT_BEAN_NAME = "simpleElasticsearchMappingContext";
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return AuditingEntityCallback.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateId()
*/
@Override
protected boolean shouldGenerateId() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String mappingContextRef = element.getAttribute("mapping-context-ref");
if (!StringUtils.hasText(mappingContextRef)) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME,
new RootBeanDefinition(SimpleElasticsearchMappingContext.class));
}
mappingContextRef = MAPPING_CONTEXT_BEAN_NAME;
}
IsNewAwareAuditingHandlerBeanDefinitionParser parser = new IsNewAwareAuditingHandlerBeanDefinitionParser(
mappingContextRef);
parser.parse(element, parserContext);
AbstractBeanDefinition isNewAwareAuditingHandler = getObjectFactoryBeanDefinition(parser.getResolvedBeanName(),
parserContext.extractSource(element));
builder.addConstructorArgValue(isNewAwareAuditingHandler);
if (ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)) {
registerReactiveAuditingEntityCallback(parserContext.getRegistry(), isNewAwareAuditingHandler,
parserContext.extractSource(element));
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry,
AbstractBeanDefinition isNewAwareAuditingHandler, @Nullable Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(isNewAwareAuditingHandler);
builder.getRawBeanDefinition().setSource(source);
registry.registerBeanDefinition(ReactiveAuditingEntityCallback.class.getName(), builder.getBeanDefinition());
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 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.config;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.util.Assert;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableElasticsearchAuditing} annotation.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
class ElasticsearchAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableElasticsearchAuditing.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
*/
@Override
protected String getAuditingHandlerBeanName() {
return "elasticsearchAuditingHandler";
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
super.registerBeanDefinitions(annotationMetadata, registry);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(ElasticsearchMappingContextLookup.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEntityCallback.class);
listenerBeanDefinitionBuilder
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);
if (ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)) {
registerReactiveAuditingEntityCallback(registry, auditingHandlerDefinition.getSource());
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry, Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(source);
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
}
/**
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingElasticsearchConverter} bean
* available in the application context.
*
* @author Oliver Gierke
*/
static class ElasticsearchMappingContextLookup implements
FactoryBean<MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty>> {
private final MappingElasticsearchConverter converter;
/**
* Creates a new {@link ElasticsearchMappingContextLookup} for the given {@link MappingElasticsearchConverter}.
*
* @param converter must not be {@literal null}.
*/
public ElasticsearchMappingContextLookup(MappingElasticsearchConverter converter) {
this.converter = converter;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> getObject()
throws Exception {
return converter.getMappingContext();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return MappingContext.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 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.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.domain.AuditorAware;
/**
* Annotation to enable auditing in Elasticsearch via annotation configuration.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ElasticsearchAuditingRegistrar.class)
public @interface EnableElasticsearchAuditing {
/**
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
*
* @return
*/
String auditorAwareRef() default "";
/**
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
*
* @return
*/
boolean setDates() default true;
/**
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
*
* @return
*/
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
* used for setting creation and modification dates.
*
* @return
*/
String dateTimeProviderRef() default "";
}

View File

@ -6,7 +6,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -23,6 +22,7 @@ import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.event.BeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@ -33,6 +33,7 @@ import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
@ -49,6 +50,8 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
protected @Nullable ElasticsearchConverter elasticsearchConverter;
protected @Nullable RequestFactory requestFactory;
private @Nullable EntityCallbacks entityCallbacks;
// region Initialization
protected void initialize(ElasticsearchConverter elasticsearchConverter) {
@ -66,12 +69,33 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (entityCallbacks == null) {
setEntityCallbacks(EntityCallbacks.create(applicationContext));
}
if (elasticsearchConverter instanceof ApplicationContextAware) {
((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context);
((ApplicationContextAware) elasticsearchConverter).setApplicationContext(applicationContext);
}
}
/**
* Set the {@link EntityCallbacks} instance to use when invoking {@link EntityCallbacks callbacks} like the
* {@link org.springframework.data.elasticsearch.core.event.BeforeConvertCallback}.
* <p />
* Overrides potentially existing {@link EntityCallbacks}.
*
* @param entityCallbacks must not be {@literal null}.
* @throws IllegalArgumentException if the given instance is {@literal null}.
* @since 4.0
*/
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
Assert.notNull(entityCallbacks, "entityCallbacks must not be null");
this.entityCallbacks = entityCallbacks;
}
// endregion
// region DocumentOperations
@ -388,6 +412,37 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
.withObject(entity) //
.build();
}
// endregion
// region callbacks
protected <T> T maybeCallbackBeforeConvert(T entity) {
if (entityCallbacks != null) {
return entityCallbacks.callback(BeforeConvertCallback.class, entity);
}
return entity;
}
protected void maybeCallbackBeforeConvertWithQuery(Object query) {
if (query instanceof IndexQuery) {
IndexQuery indexQuery = (IndexQuery) query;
Object queryObject = indexQuery.getObject();
if (queryObject != null) {
queryObject = maybeCallbackBeforeConvert(queryObject);
indexQuery.setObject(queryObject);
}
}
}
// this can be called with either a List<IndexQuery> or a List<UpdateQuery>; these query classes
// don't have a common bas class, therefore the List<?> argument
protected void maybeCallbackBeforeConvertWithQueries(List<?> queries) {
queries.forEach(this::maybeCallbackBeforeConvertWithQuery);
}
// endregion
}

View File

@ -37,6 +37,7 @@ import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@ -134,12 +135,16 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQuery(query);
IndexRequest request = requestFactory.indexRequest(query, index);
String documentId = execute(client -> client.index(request, RequestOptions.DEFAULT).getId());
// We should call this because we are not going through a mapper.
if (query.getObject() != null) {
setPersistentEntityId(query.getObject(), documentId);
Object queryObject = query.getObject();
if (queryObject != null) {
setPersistentEntityId(queryObject, documentId);
}
return documentId;
}
@ -166,6 +171,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override
protected boolean doExists(String id, IndexCoordinates index) {
GetRequest request = requestFactory.getRequest(id, index);
request.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);
return execute(client -> client.get(request, RequestOptions.DEFAULT).isExists());
}
@ -219,6 +225,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
private List<String> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQueries(queries);
BulkRequest bulkRequest = requestFactory.bulkRequest(queries, bulkOptions, index);
return checkForBulkOperationFailure(execute(client -> client.bulk(bulkRequest, RequestOptions.DEFAULT)));
}

View File

@ -136,12 +136,18 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQuery(query);
IndexRequestBuilder indexRequestBuilder = requestFactory.indexRequestBuilder(client, query, index);
String documentId = indexRequestBuilder.execute().actionGet().getId();
// We should call this because we are not going through a mapper.
if (query.getObject() != null) {
setPersistentEntityId(query.getObject(), documentId);
Object queryObject = query.getObject();
if (queryObject != null) {
setPersistentEntityId(queryObject, documentId);
}
return documentId;
}
@ -167,6 +173,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
protected boolean doExists(String id, IndexCoordinates index) {
GetRequestBuilder getRequestBuilder = requestFactory.getRequestBuilder(client, id, index);
getRequestBuilder.setFetchSource(false);
return getRequestBuilder.execute().actionGet().isExists();
}
@ -223,6 +230,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
private List<String> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQueries(queries);
BulkRequestBuilder bulkRequest = requestFactory.bulkRequestBuilder(client, queries, bulkOptions, index);
return checkForBulkOperationFailure(bulkRequest.execute().actionGet());
}

View File

@ -56,6 +56,9 @@ import org.elasticsearch.search.sort.SortOrder;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
@ -67,6 +70,7 @@ import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchC
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@ -78,6 +82,7 @@ import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
@ -93,7 +98,7 @@ import org.springframework.util.Assert;
* @author Aleksei Arsenev
* @since 3.2
*/
public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOperations {
public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOperations, ApplicationContextAware {
private static final Logger QUERY_LOGGER = LoggerFactory
.getLogger("org.springframework.data.elasticsearch.core.QUERY");
@ -108,6 +113,8 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
private @Nullable RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE;
private @Nullable IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled();
private @Nullable ReactiveEntityCallbacks entityCallbacks;
// region Initialization
public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client) {
this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()));
@ -125,6 +132,31 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
this.operations = new EntityOperations(this.mappingContext);
this.requestFactory = new RequestFactory(converter);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (entityCallbacks == null) {
setEntityCallbacks(ReactiveEntityCallbacks.create(applicationContext));
}
}
/**
* Set the {@link ReactiveEntityCallbacks} instance to use when invoking {@link ReactiveEntityCallbacks callbacks}
* like the {@link ReactiveBeforeConvertCallback}.
* <p />
* Overrides potentially existing {@link ReactiveEntityCallbacks}.
*
* @param entityCallbacks must not be {@literal null}.
* @throws IllegalArgumentException if the given instance is {@literal null}.
* @since 4.0
*/
public void setEntityCallbacks(ReactiveEntityCallbacks entityCallbacks) {
Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
this.entityCallbacks = entityCallbacks;
}
// endregion
// region DocumentOperations
@ -289,7 +321,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
private Mono<IndexResponse> doIndex(Object value, AdaptibleEntity<?> entity, IndexCoordinates index) {
return Mono.defer(() -> {
return maybeCallBeforeConvert(value).flatMap(it -> {
IndexRequest request = getIndexRequest(value, entity, index);
request = prepareIndexRequest(value, request);
return doIndex(request);
@ -821,4 +853,14 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return potentiallyTranslatedException != null ? potentiallyTranslatedException : runtimeException;
}
// region callbacks
protected <T> Mono<T> maybeCallBeforeConvert(T entity) {
if (null != entityCallbacks) {
return entityCallbacks.callback(ReactiveBeforeConvertCallback.class, entity);
}
return Mono.just(entity);
}
// endregion
}

View File

@ -25,7 +25,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.support.ReactiveSupport;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.lang.Nullable;
/**
@ -78,7 +78,7 @@ public final class SearchHitSupport {
return unwrapSearchHits(searchHits.getSearchHits());
}
if (ReactiveSupport.isReactorAvailable()) {
if (ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)) {
if (result instanceof Flux) {
Flux<?> flux = (Flux<?>) result;

View File

@ -15,17 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.elasticsearch.search.aggregations.Aggregations;
@ -38,8 +29,6 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.convert.EntityInstantiators;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
@ -57,6 +46,8 @@ import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.util.ClassTypeInformation;
@ -108,6 +99,7 @@ public class MappingElasticsearchConverter
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (mappingContext instanceof ApplicationContextAware) {
((ApplicationContextAware) mappingContext).setApplicationContext(applicationContext);
}
@ -653,7 +645,7 @@ public class MappingElasticsearchConverter
collectionSource.map(it -> {
if (it == null) {
//noinspection ReturnOfNull
// noinspection ReturnOfNull
return null;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 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.core.event;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.core.Ordered;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.util.Assert;
/**
* {@link EntityCallback} to populate auditing related fields on an entity about to be saved.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public class AuditingEntityCallback implements BeforeConvertCallback<Object>, Ordered {
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
/**
* Creates a new {@link AuditingEntityCallback} using the given {@link IsNewAwareAuditingHandler} provided by the
* given {@link ObjectFactory}.
*
* @param auditingHandlerFactory must not be {@literal null}.
*/
public AuditingEntityCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
this.auditingHandlerFactory = auditingHandlerFactory;
}
@Override
public Object onBeforeConvert(Object entity) {
return auditingHandlerFactory.getObject().markAudited(entity);
}
@Override
public int getOrder() {
return 100;
}
}

View File

@ -13,27 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.support;
package org.springframework.data.elasticsearch.core.event;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.data.repository.util.ClassUtils;
import org.springframework.data.mapping.callback.EntityCallback;
/**
* Callback being invoked before a domain object is converted to be persisted.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public final class ReactiveSupport {
private ReactiveSupport() {}
@FunctionalInterface
public interface BeforeConvertCallback<T> extends EntityCallback<T> {
/**
* @return true if project reactor is on the classpath
* Callback method that will be invoked before an entity is persisted. Can return the same or a different instance of
* the domain entity class.
*
* @param entity the entity being converted
* @return the entity to be converted
*/
public static boolean isReactorAvailable() {
AtomicBoolean available = new AtomicBoolean(false);
ClassUtils.ifPresent("reactor.core.publisher.Flux", null, aClass -> {
available.set(true);
});
return available.get();
}
T onBeforeConvert(T entity);
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 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.core.event;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.core.Ordered;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.util.Assert;
/**
* {@link EntityCallback} to populate auditing related fields on an entity about to be saved.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public class ReactiveAuditingEntityCallback implements ReactiveBeforeConvertCallback<Object>, Ordered {
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
/**
* Creates a new {@link ReactiveAuditingEntityCallback} using the given {@link IsNewAwareAuditingHandler} provided by
* the given {@link ObjectFactory}.
*
* @param auditingHandlerFactory must not be {@literal null}.
*/
public ReactiveAuditingEntityCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
this.auditingHandlerFactory = auditingHandlerFactory;
}
@Override
public Mono<Object> onBeforeConvert(Object entity) {
return Mono.just(auditingHandlerFactory.getObject().markAudited(entity));
}
@Override
public int getOrder() {
return 100;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 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.core.event;
import org.reactivestreams.Publisher;
import org.springframework.data.mapping.callback.EntityCallback;
/**
* Callback being invoked before a domain object is converted to be persisted.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
@FunctionalInterface
public interface ReactiveBeforeConvertCallback<T> extends EntityCallback<T> {
/**
* Callback method that will be invoked before an entity is persisted. Can return the same or a different instance of
* the domain entity class.
*
* @param entity the entity being converted
* @return the entity to be converted
*/
Publisher<T> onBeforeConvert(T entity);
}

View File

@ -0,0 +1,6 @@
/**
* classes and interfaces related to Spring Data Elasticsearch events and callbacks.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.core.event;

View File

@ -0,0 +1,47 @@
/*
* Copyright 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 static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* class demonstrating the setup of a JUnit 5 test in Spring Data Elasticsearch that uses the reactive rest client. The
* ContextConfiguration must include the {@link ElasticsearchRestTemplateConfiguration} class.
*
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ReactiveElasticsearchRestTemplateConfiguration.class })
@DisplayName("a sample JUnit 5 test with reactive rest client")
public class JUnit5SampleReactiveRestClientBasedTests {
@Autowired private ReactiveElasticsearchOperations elasticsearchOperations;
@Test
@DisplayName("should have a ReactiveElasticsearchOperations")
void shouldHaveARestTemplate() {
assertThat(elasticsearchOperations).isNotNull().isInstanceOf(ReactiveElasticsearchOperations.class);
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright 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.config;
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDateTime;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Persistable;
import org.springframework.data.elasticsearch.core.event.BeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
*/
public abstract class AuditingIntegrationTest {
public static AuditorAware<String> auditorProvider() {
return new AuditorAware<String>() {
int count = 0;
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Auditor " + (++count));
}
};
}
@Autowired ApplicationContext applicationContext;
@Test // DATAES-68
void shouldEnableAuditingAndSetAuditingDates() throws InterruptedException {
SimpleElasticsearchMappingContext mappingContext = applicationContext
.getBean(SimpleElasticsearchMappingContext.class);
mappingContext.getPersistentEntity(Entity.class);
EntityCallbacks callbacks = EntityCallbacks.create(applicationContext);
Entity entity = new Entity();
entity.setId("1");
entity = callbacks.callback(BeforeConvertCallback.class, entity);
assertThat(entity.getCreated()).isNotNull();
assertThat(entity.getModified()).isEqualTo(entity.created);
assertThat(entity.getCreatedBy()).isEqualTo("Auditor 1");
assertThat(entity.getModifiedBy()).isEqualTo("Auditor 1");
Thread.sleep(10);
entity = callbacks.callback(BeforeConvertCallback.class, entity);
assertThat(entity.getCreated()).isNotNull();
assertThat(entity.getModified()).isNotEqualTo(entity.created);
assertThat(entity.getCreatedBy()).isEqualTo("Auditor 1");
assertThat(entity.getModifiedBy()).isEqualTo("Auditor 2");
}
static class Entity implements Persistable<String> {
private @Nullable @Id String id;
private @Nullable @CreatedDate LocalDateTime created;
private @Nullable LocalDateTime modified;
private @Nullable @CreatedBy String createdBy;
private @Nullable @LastModifiedBy String modifiedBy;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public LocalDateTime getCreated() {
return created;
}
public void setCreated(@Nullable LocalDateTime created) {
this.created = created;
}
public void setModified(@Nullable LocalDateTime modified) {
this.modified = modified;
}
@Nullable
@LastModifiedDate
public LocalDateTime getModified() {
return modified;
}
@Nullable
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(@Nullable String createdBy) {
this.createdBy = createdBy;
}
@Nullable
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(@Nullable String modifiedBy) {
this.modifiedBy = modifiedBy;
}
@Override
public boolean isNew() {
return id == null || (created == null && createdBy == null);
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 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.config;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
/**
* Unit tests for {@link ElasticsearchAuditingRegistrar}.
*
* @author Oliver Gierke
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
public class ElasticsearchAuditingRegistrarUnitTests {
ElasticsearchAuditingRegistrar registrar = new ElasticsearchAuditingRegistrar();
@Mock AnnotationMetadata metadata;
@Mock BeanDefinitionRegistry registry;
@Test // DATAES-68
public void rejectsNullAnnotationMetadata() {
assertThatIllegalArgumentException().isThrownBy(() -> registrar.registerBeanDefinitions(null, registry));
}
@Test // DATAES-68
public void rejectsNullBeanDefinitionRegistry() {
assertThatIllegalArgumentException().isThrownBy(() -> registrar.registerBeanDefinitions(metadata, null));
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 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.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchRestAuditingIntegrationTest.Config.class })
public class ElasticsearchRestAuditingIntegrationTest extends AuditingIntegrationTest {
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchAuditing(auditorAwareRef = "auditorAware")
static class Config {
@Bean
public AuditorAware<String> auditorAware() {
return auditorProvider();
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 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.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchTransportAuditingIntegrationTest.Config.class })
public class ElasticsearchTransportAuditingIntegrationTest extends AuditingIntegrationTest {
@Import({ ElasticsearchTemplateConfiguration.class })
@EnableElasticsearchAuditing(auditorAwareRef = "auditorAware")
static class Config {
@Bean
public AuditorAware<String> auditorAware() {
return auditorProvider();
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright 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.config;
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDateTime;
import java.util.Optional;
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.Import;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Persistable;
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ReactiveAuditingIntegrationTest.Config.class })
public class ReactiveAuditingIntegrationTest {
public static AuditorAware<String> auditorProvider() {
return new AuditorAware<String>() {
int count = 0;
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Auditor " + (++count));
}
};
}
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchAuditing(auditorAwareRef = "auditorAware")
static class Config {
@Bean
public AuditorAware<String> auditorAware() {
return auditorProvider();
}
}
@Autowired ApplicationContext applicationContext;
@Test // DATAES-68
void shouldEnableAuditingAndSetAuditingDates() throws InterruptedException {
SimpleElasticsearchMappingContext mappingContext = applicationContext
.getBean(SimpleElasticsearchMappingContext.class);
mappingContext.getPersistentEntity(Entity.class);
ReactiveEntityCallbacks callbacks = ReactiveEntityCallbacks.create(applicationContext);
Entity entity = new Entity();
entity.setId("1");
entity = callbacks.callback(ReactiveBeforeConvertCallback.class, entity).block();
assertThat(entity.getCreated()).isNotNull();
assertThat(entity.getModified()).isEqualTo(entity.created);
assertThat(entity.getCreatedBy()).isEqualTo("Auditor 1");
assertThat(entity.getModifiedBy()).isEqualTo("Auditor 1");
Thread.sleep(10);
entity = callbacks.callback(ReactiveBeforeConvertCallback.class, entity).block();
assertThat(entity.getCreated()).isNotNull();
assertThat(entity.getModified()).isNotEqualTo(entity.created);
assertThat(entity.getCreatedBy()).isEqualTo("Auditor 1");
assertThat(entity.getModifiedBy()).isEqualTo("Auditor 2");
}
static class Entity implements Persistable<String> {
private @Nullable @Id String id;
private @Nullable @CreatedDate LocalDateTime created;
private @Nullable LocalDateTime modified;
private @Nullable @CreatedBy String createdBy;
private @Nullable @LastModifiedBy String modifiedBy;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public LocalDateTime getCreated() {
return created;
}
public void setCreated(@Nullable LocalDateTime created) {
this.created = created;
}
public void setModified(@Nullable LocalDateTime modified) {
this.modified = modified;
}
@Nullable
@LastModifiedDate
public LocalDateTime getModified() {
return modified;
}
@Nullable
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(@Nullable String createdBy) {
this.createdBy = createdBy;
}
@Nullable
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(@Nullable String modifiedBy) {
this.modifiedBy = modifiedBy;
}
@Override
public boolean isNew() {
return id == null || (created == null && createdBy == null);
}
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright 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.core.event;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.time.LocalDateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.Ordered;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
class AuditingEntityCallbackTests {
IsNewAwareAuditingHandler handler;
AuditingEntityCallback callback;
@BeforeEach
void setUp() {
SimpleElasticsearchMappingContext context = new SimpleElasticsearchMappingContext();
context.getPersistentEntity(Sample.class);
handler = spy(new IsNewAwareAuditingHandler(PersistentEntities.of(context)));
callback = new AuditingEntityCallback(() -> handler);
}
@Test // DATAES-68
void shouldThrowExceptionOnNullFactory() {
assertThatIllegalArgumentException().isThrownBy(() -> new AuditingEntityCallback(null));
}
@Test // DATAES-68
void shouldHaveOrder100() {
assertThat(callback).isInstanceOf(Ordered.class);
assertThat(callback.getOrder()).isEqualTo(100);
}
@Test // DATAES-68
void shouldCallHandler() {
Sample entity = new Sample();
entity.setId("42");
callback.onBeforeConvert(entity);
verify(handler).markAudited(eq(entity));
}
@Test // DATAES-68
void shouldReturnObjectFromHandler() {
Sample sample1 = new Sample();
sample1.setId("1");
Sample sample2 = new Sample();
sample2.setId("2");
doReturn(sample2).when(handler).markAudited(any());
Sample result = (Sample) callback.onBeforeConvert(sample1);
assertThat(result).isSameAs(sample2);
}
static class Sample {
@Nullable @Id String id;
@Nullable @CreatedDate LocalDateTime createdDate;
@Nullable @CreatedBy String createdBy;
@Nullable @LastModifiedDate LocalDateTime modified;
@Nullable
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Nullable
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
@Nullable
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(@Nullable String createdBy) {
this.createdBy = createdBy;
}
@Nullable
public LocalDateTime getModified() {
return modified;
}
public void setModified(LocalDateTime modified) {
this.modified = modified;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Sample sample = (Sample) o;
if (id != null ? !id.equals(sample.id) : sample.id != null)
return false;
if (createdDate != null ? !createdDate.equals(sample.createdDate) : sample.createdDate != null)
return false;
if (createdBy != null ? !createdBy.equals(sample.createdBy) : sample.createdBy != null)
return false;
return modified != null ? modified.equals(sample.modified) : sample.modified == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0);
result = 31 * result + (modified != null ? modified.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Sample{" + "id='" + id + '\'' + ", createdDate=" + createdDate + ", createdBy='" + createdBy + '\''
+ ", modified=" + modified + '}';
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 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.core.event;
import static org.assertj.core.api.Assertions.*;
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.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.stereotype.Component;
/**
* @author Peter-Josef Meisch
*/
abstract class ElasticsearchOperationsCallbackTest {
@Autowired private ElasticsearchOperations operations;
@Configuration
static class Config {
@Component
static class SampleEntityBeforeConvertCallback implements BeforeConvertCallback<SampleEntity> {
@Override
public SampleEntity onBeforeConvert(SampleEntity entity) {
entity.setText("converted");
return entity;
}
}
}
@BeforeEach
void setUp() {
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
indexOps.create();
indexOps.putMapping(indexOps.createMapping(SampleEntity.class));
}
@AfterEach
void tearDown() {
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
}
@Test
void shouldCallBeforeConvertCallback() {
SampleEntity entity = new SampleEntity("1", "test");
SampleEntity saved = operations.save(entity);
assertThat(saved.getText()).isEqualTo("converted");
}
@Document(indexName = "test-operations-callback")
static class SampleEntity {
@Id private String id;
private String text;
public SampleEntity(String id, String text) {
this.id = id;
this.text = text;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 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.core.event;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class, ElasticsearchOperationsCallbackTest.Config.class })
class ElasticsearchRestOperationsCallbackTest extends ElasticsearchOperationsCallbackTest {}

View File

@ -0,0 +1,27 @@
/*
* Copyright 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.core.event;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class, ElasticsearchOperationsCallbackTest.Config.class })
class ElasticsearchTransportOperationsCallbackTest extends ElasticsearchOperationsCallbackTest {}

View File

@ -0,0 +1,167 @@
/*
* Copyright 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.core.event;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import reactor.test.StepVerifier;
import java.time.LocalDateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.Ordered;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
class ReactiveAuditingEntityCallbackTests {
IsNewAwareAuditingHandler handler;
ReactiveAuditingEntityCallback callback;
@BeforeEach
void setUp() {
SimpleElasticsearchMappingContext context = new SimpleElasticsearchMappingContext();
context.getPersistentEntity(Sample.class);
handler = spy(new IsNewAwareAuditingHandler(PersistentEntities.of(context)));
callback = new ReactiveAuditingEntityCallback(() -> handler);
}
@Test // DATAES-68
void shouldThrowExceptionOnNullFactory() {
assertThatIllegalArgumentException().isThrownBy(() -> new AuditingEntityCallback(null));
}
@Test // DATAES-68
void shouldHaveOrder100() {
assertThat(callback).isInstanceOf(Ordered.class);
assertThat(callback.getOrder()).isEqualTo(100);
}
@Test // DATAES-68
void shouldCallHandler() {
Sample entity = new Sample();
entity.setId("42");
callback.onBeforeConvert(entity);
verify(handler).markAudited(eq(entity));
}
@Test // DATAES-68
void shouldReturnObjectFromHandler() {
Sample sample1 = new Sample();
sample1.setId("1");
Sample sample2 = new Sample();
sample2.setId("2");
doReturn(sample2).when(handler).markAudited(any());
callback.onBeforeConvert(sample1) //
.as(StepVerifier::create) //
.consumeNextWith(it -> { //
assertThat(it).isSameAs(sample2); //
}).verifyComplete();
}
static class Sample {
@Nullable @Id String id;
@Nullable @CreatedDate LocalDateTime createdDate;
@Nullable @CreatedBy String createdBy;
@Nullable @LastModifiedDate LocalDateTime modified;
@Nullable
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Nullable
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
@Nullable
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(@Nullable String createdBy) {
this.createdBy = createdBy;
}
@Nullable
public LocalDateTime getModified() {
return modified;
}
public void setModified(LocalDateTime modified) {
this.modified = modified;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Sample sample = (Sample) o;
if (id != null ? !id.equals(sample.id) : sample.id != null)
return false;
if (createdDate != null ? !createdDate.equals(sample.createdDate) : sample.createdDate != null)
return false;
if (createdBy != null ? !createdBy.equals(sample.createdBy) : sample.createdBy != null)
return false;
return modified != null ? modified.equals(sample.modified) : sample.modified == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0);
result = 31 * result + (modified != null ? modified.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Sample{" + "id='" + id + '\'' + ", createdDate=" + createdDate + ", createdBy='" + createdBy + '\''
+ ", modified=" + modified + '}';
}
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 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.core.event;
import static org.assertj.core.api.Assertions.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
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.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ReactiveElasticsearchOperationsCallbackTest.Config.class })
public class ReactiveElasticsearchOperationsCallbackTest {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class, ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Component
static class SampleEntityBeforeConvertCallback implements ReactiveBeforeConvertCallback<SampleEntity> {
@Override
public Mono<SampleEntity> onBeforeConvert(SampleEntity entity) {
entity.setText("reactive-converted");
return Mono.just(entity);
}
}
}
@Autowired private ReactiveElasticsearchOperations operations;
@Autowired private ElasticsearchOperations nonreactiveOperations;
@BeforeEach
void setUp() {
IndexOperations indexOps = nonreactiveOperations.indexOps(SampleEntity.class);
indexOps.create();
indexOps.putMapping(indexOps.createMapping(SampleEntity.class));
}
@AfterEach
void tearDown() {
IndexOperations indexOps = nonreactiveOperations.indexOps(SampleEntity.class);
indexOps.delete();
}
@Test // DATES-68
void shouldCallCallbackOnSave() {
SampleEntity sample = new SampleEntity("42", "initial");
operations.save(sample) //
.as(StepVerifier::create) //
.consumeNextWith(it -> { //
assertThat(it.text).isEqualTo("reactive-converted"); //
}) //
.verifyComplete();
}
@Document(indexName = "test-operations-reactive-callback")
static class SampleEntity {
@Id private String id;
private String text;
public SampleEntity(String id, String text) {
this.id = id;
this.text = text;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}

View File

@ -104,7 +104,7 @@ public class SimpleElasticsearchPersistentEntityTests {
}
private class EntityWithWrongVersionType {
private static class EntityWithWrongVersionType {
@Nullable @Version private String version;
@ -118,7 +118,7 @@ public class SimpleElasticsearchPersistentEntityTests {
}
}
private class EntityWithMultipleVersionField {
private static class EntityWithMultipleVersionField {
@Nullable @Version private Long version1;
@Nullable @Version private Long version2;
@ -143,7 +143,6 @@ public class SimpleElasticsearchPersistentEntityTests {
}
// DATAES-462
static class TwoScoreProperties {
@Score float first;

View File

@ -27,16 +27,14 @@ import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfig
/**
* Configuration for Spring Data Elasticsearch using
* {@link org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate}. The required
* {@link ClusterConnectionInfo} bean must be provided by the testclass.
* {@link org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate}.
*
* @author Peter-Josef Meisch
*/
@Configuration
public class ElasticsearchRestTemplateConfiguration extends AbstractElasticsearchConfiguration {
@Autowired
private ClusterConnectionInfo clusterConnectionInfo;
@Autowired private ClusterConnectionInfo clusterConnectionInfo;
@Override
@Bean

View File

@ -0,0 +1,51 @@
/*
* Copyright 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.jupiter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.data.elasticsearch.config.AbstractReactiveElasticsearchConfiguration;
/**
* Configuration for Spring Data Elasticsearch Integration Tests using
* {@link org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations}
*
* @author Peter-Josef Meisch
*/
@Configuration
public class ReactiveElasticsearchRestTemplateConfiguration extends AbstractReactiveElasticsearchConfiguration {
@Autowired private ClusterConnectionInfo clusterConnectionInfo;
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
String elasticsearchHostPort = clusterConnectionInfo.getHost() + ':' + clusterConnectionInfo.getHttpPort();
ClientConfiguration.TerminalClientConfigurationBuilder configurationBuilder = ClientConfiguration.builder() //
.connectedTo(elasticsearchHostPort);
if (clusterConnectionInfo.isUseSsl()) {
configurationBuilder = ((ClientConfiguration.MaybeSecureClientConfigurationBuilder) configurationBuilder)
.usingSsl();
}
return ReactiveRestClients.create(configurationBuilder.build());
}
}