Skip to content

Commit f0d6185

Browse files
committed
feat(all): add rule/extension to delete data from ES index
1 parent 4659237 commit f0d6185

File tree

22 files changed

+776
-218
lines changed

22 files changed

+776
-218
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# spring-esdata-loader
33
[![Build Status](https://travis-ci.org/tinesoft/spring-esdata-loader.svg?branch=master)](https://travis-ci.org/tinesoft/spring-esdata-loader)
44

5-
`spring-esdata-loader` is a Java testing library to help you write integration tests for your [spring-data elasticsearch](https://spring.io/projects/spring-data-elasticsearch)-based projects, by allowing you to easily load data into Elasticsearch, using entity mappings (i.e domain classes annotated with `@Document`, `@Field`, etc) and via a specific **Junit 4**'s Rule or **JUnit Jupiter**'s Extension.
5+
`spring-esdata-loader` is a Java testing library to help you write integration tests for your [spring-data elasticsearch](https://spring.io/projects/spring-data-elasticsearch)-based projects, by allowing you to easily load data into Elasticsearch, using entity mappings (i.e domain classes annotated with `@Document`, `@Field`, etc) and via specific **Junit 4**'s Rules or **JUnit Jupiter**'s Extensions.
66

77
The library reads all the metadata it needs from the entity classes (index name, index type, etc) , uses them to create/refresh the index on the ES server and feeds it with the data using the `ElasticsearchTemplate` present in your test application context.
88

99
## Features
1010

1111
* **Simple API** and no configuration required
12-
* Support for **JUnit 4** via `LoadEsDataRule`
13-
* Support for **JUnit Jupiter** via `@LoadEsDataConfig` or `@LoadEsDataExtension`
12+
* Support for **JUnit 4** via `LoadEsDataRule`, `DeleteEsDataRule`
13+
* Support for **JUnit Jupiter** via `@LoadEsDataConfig` / `@LoadEsDataExtension` or `@DeleteEsDataConfig` / `@DeleteEsDataExtension`
1414
* Built-in support for **gzipped data**
1515
* Written in **Java 8**
1616
* Based on **Spring (Data, Test)**
@@ -43,14 +43,14 @@ To get started,
4343
<td>JUnit 4</td>
4444
<td>
4545
<pre lang="groovy">dependencies {
46-
testImplementation 'com.github.spring-esdata-loader:spring-esdata-loader-junit4:1.0.0'
46+
testImplementation 'com.github.spring-esdata-loader:spring-esdata-loader-junit4:2.0.0'
4747
}</pre>
4848
</td>
4949
<td>
5050
<pre lang="xml">&lt;dependency&gt;
5151
&lt;groupId&gt;com.github.spring-esdata-loader&lt;/groupId&gt;
5252
&lt;artifactId>spring-esdata-loader-junit4&lt;/artifactId&gt;
53-
&lt;version>1.0.0&lt;/version&gt;
53+
&lt;version>2.0.0&lt;/version&gt;
5454
&lt;scope>test&lt;/scope&gt;
5555
&lt;/dependency&gt;</pre>
5656
</td>
@@ -59,14 +59,14 @@ To get started,
5959
<td>JUnit Jupiter</td>
6060
<td>
6161
<pre lang="groovy">dependencies {
62-
testImplementation 'com.github.spring-esdata-loader:spring-esdata-loader-junit-jupiter:1.0.0'
62+
testImplementation 'com.github.spring-esdata-loader:spring-esdata-loader-junit-jupiter:2.0.0'
6363
}</pre>
6464
</td>
6565
<td>
6666
<pre lang="xml">&lt;dependency&gt;
6767
&lt;groupId&gt;com.github.spring-esdata-loader&lt;/groupId&gt;
6868
&lt;artifactId>spring-esdata-loader-junit-jupiter&lt;/artifactId&gt;
69-
&lt;version>1.0.0&lt;/version&gt;
69+
&lt;version>2.0.0&lt;/version&gt;
7070
&lt;scope>test&lt;/scope&gt;
7171
&lt;/dependency&gt;</pre>
7272
</td>

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ buildscript {
1414
classpath "org.ajoberstar:gradle-git-publish:2.1.1"
1515
}
1616
}
17-
description = "Set of JUnit Rule & Extension to easily load data to test your spring-data elasticsearch-based projects"
17+
description = "Set of JUnit Rules & Extensions to easily load data to test your spring-data elasticsearch-based projects"
1818

1919
allprojects {
2020
apply plugin: "nebula.release"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.spring.esdata.loader.core;
2+
3+
import org.springframework.core.annotation.AliasFor;
4+
5+
import java.lang.annotation.*;
6+
7+
/**
8+
* {@code @LoadEsData} is a {@linkplain Repeatable repeatable} annotation
9+
* that is used to define which data to delete from Elasticsearch index.
10+
*
11+
* @author tinesoft
12+
*/
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Target({ElementType.METHOD, ElementType.TYPE})
15+
public @interface DeleteEsData {
16+
/**
17+
* Alias for {@link DeleteEsData#value}.
18+
*
19+
* @return mapping classes of the data to be deleted from Elasticsearch
20+
*/
21+
@AliasFor("value")
22+
Class<?>[] esEntityClasses() default {};
23+
24+
25+
/**
26+
* Alias for {@link DeleteEsData#esEntityClasses}.
27+
*
28+
* @return mapping classes of the data to be deleted from Elasticsearch
29+
*/
30+
@AliasFor("esEntityClasses")
31+
Class<?>[] value() default {};
32+
33+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.spring.esdata.loader.core;
2+
3+
public interface EsDataLoader {
4+
void delete(Class<?> esEntityClass);
5+
6+
void load(IndexData d);
7+
}

core/src/main/java/com/github/spring/esdata/loader/core/SpringEsDataLoader.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @author tinesoft
2323
*
2424
*/
25-
public class SpringEsDataLoader {
25+
public class SpringEsDataLoader implements EsDataLoader {
2626

2727
private static final Logger LOGGER = LoggerFactory.getLogger(SpringEsDataLoader.class);
2828
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@@ -38,14 +38,30 @@ public SpringEsDataLoader(final ElasticsearchTemplate esTemplate) {
3838
this.esTemplate = esTemplate;
3939
}
4040

41-
/**
42-
* Loads given data into ElasticSearch. Target indices are dropped and recreated before data are inserted in bulk.
43-
* @param d the data to load
41+
/**
42+
* Deletes data from Elasticsearch using provided class to retrieve related index.
43+
*
44+
* @param esEntityClass the data to load
45+
*/
46+
@Override
47+
public void delete(Class<?> esEntityClass) {
48+
LOGGER.debug("Dropping data in Index '{}'...", esEntityClass.getSimpleName());
49+
this.esTemplate.deleteIndex(esEntityClass);
50+
this.esTemplate.createIndex(esEntityClass);
51+
this.esTemplate.putMapping(esEntityClass);
52+
this.esTemplate.refresh(esEntityClass);
53+
54+
}
55+
56+
/**
57+
* Loads given data into Elasticsearch. Target indices are dropped and recreated before data are inserted in bulk.
58+
* @param d the data to load
4459
*/
45-
public void load(final IndexData d) {
60+
@Override
61+
public void load(final IndexData d) {
4662

4763
// first recreate the index
48-
LOGGER.info("Recreating Index for '{}'...", d.getEsEntityClass().getSimpleName());
64+
LOGGER.debug("Recreating Index for '{}'...", d.getEsEntityClass().getSimpleName());
4965
this.esTemplate.deleteIndex(d.esEntityClass);
5066
this.esTemplate.createIndex(d.esEntityClass);
5167
this.esTemplate.putMapping(d.esEntityClass);

core/src/main/java/com/github/spring/esdata/loader/core/SpringUtils.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
77

88
import java.util.Map;
9-
import java.util.function.Consumer;
109

1110
/**
1211
* Utility class to interact with Spring.
@@ -44,12 +43,13 @@ public static <T> T getBeanOfType(ApplicationContext applicationContext, final C
4443
return null;
4544
}
4645

47-
/**
48-
* Retrieve a {@link ElasticsearchTemplate} from the {@link ApplicationContext} and creates a data loader from it.
49-
* @param appContext the Spring {@link ApplicationContext}
50-
* @return a {@link Consumer} that accepts one or more {@link IndexData} objects and inserts them in the underlying ES Server.
51-
*/
52-
public static Consumer<IndexData> getDataLoader(final ApplicationContext appContext) {
46+
/**
47+
* Retrieve a {@link ElasticsearchTemplate} from the {@link ApplicationContext} and creates a {@link EsDataLoader} from it.
48+
*
49+
* @param appContext the Spring {@link ApplicationContext}
50+
* @return a {@link EsDataLoader} that can insert or remove data from the underlying ES Server.
51+
*/
52+
public static EsDataLoader getDataLoader(final ApplicationContext appContext) {
5353

5454
if (appContext == null) {
5555
LOGGER.error(
@@ -63,9 +63,9 @@ public static Consumer<IndexData> getDataLoader(final ApplicationContext appCont
6363
LOGGER.error("No Spring's bean of type 'ElasticsearchTemplate' was found!");
6464
throw new IllegalStateException(
6565
"Missing bean of type 'ElasticsearchTemplate' in your Spring configuration");
66-
}
66+
}
6767

68-
return new SpringEsDataLoader(esTemplate)::load;
68+
return new SpringEsDataLoader(esTemplate);
6969
}
7070

7171
}

demo/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ext["junit-jupiter.version"] = "5.5.0"
1010
dependencies {
1111

1212
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
13-
13+
1414
testImplementation 'org.springframework.boot:spring-boot-starter-test'
1515

1616
testImplementation 'org.testcontainers:elasticsearch:1.11.3'
@@ -34,4 +34,5 @@ test {
3434
testLogging {
3535
events 'PASSED', 'FAILED', 'SKIPPED'
3636
}
37-
}
37+
38+
}

demo/src/main/java/com/github/spring/esdata/loader/demo/model/BookEsEntity.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,31 @@ public BookEsEntity(String id, String isbn, String title, AuthorEsEntity author)
3333
}
3434

3535
public String getId() {
36-
return id;
36+
return this.id;
3737
}
3838

3939
public void setId(String id) {
4040
this.id = id;
4141
}
4242

4343
public String getIsbn() {
44-
return isbn;
44+
return this.isbn;
4545
}
4646

4747
public void setIsbn(final String isbn) {
4848
this.isbn = isbn;
4949
}
5050

5151
public String getTitle() {
52-
return title;
52+
return this.title;
5353
}
5454

5555
public void setTitle(final String title) {
5656
this.title = title;
5757
}
5858

5959
public AuthorEsEntity getAuthor() {
60-
return author;
60+
return this.author;
6161
}
6262

6363
public void setAuthor(final AuthorEsEntity author) {
@@ -67,13 +67,13 @@ public void setAuthor(final AuthorEsEntity author) {
6767
@Override
6868
public boolean equals(Object o) {
6969
if (this == o) return true;
70-
if (o == null || getClass() != o.getClass()) return false;
71-
BookEsEntity that = (BookEsEntity) o;
72-
return id.equals(that.id);
70+
if (o == null || this.getClass() != o.getClass()) return false;
71+
BookEsEntity that = (BookEsEntity) o;
72+
return this.id.equals(that.id);
7373
}
7474

7575
@Override
7676
public int hashCode() {
77-
return Objects.hash(id);
77+
return Objects.hash(this.id);
7878
}
7979
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.github.spring.esdata.loader.demo.junit.jupiter;
2+
3+
import com.github.spring.esdata.loader.core.DeleteEsData;
4+
import com.github.spring.esdata.loader.demo.DemoTestPropertyValues;
5+
import com.github.spring.esdata.loader.demo.model.AuthorEsEntity;
6+
import com.github.spring.esdata.loader.demo.model.BookEsEntity;
7+
import com.github.spring.esdata.loader.demo.model.LibraryEsEntity;
8+
import com.github.spring.esdata.loader.junit.jupiter.DeleteEsDataConfig;
9+
import org.junit.jupiter.api.MethodOrderer;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.TestMethodOrder;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.boot.test.mock.mockito.MockReset;
14+
import org.springframework.boot.test.mock.mockito.SpyBean;
15+
import org.springframework.context.ApplicationContextInitializer;
16+
import org.springframework.context.ConfigurableApplicationContext;
17+
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
18+
import org.springframework.test.context.ContextConfiguration;
19+
import org.testcontainers.elasticsearch.ElasticsearchContainer;
20+
import org.testcontainers.junit.jupiter.Container;
21+
import org.testcontainers.junit.jupiter.Testcontainers;
22+
23+
import static org.mockito.Mockito.never;
24+
import static org.mockito.Mockito.verify;
25+
26+
27+
/**
28+
* Simple integration test to illustrate how to use the library when testing with JUnit Jupiter.
29+
*
30+
* @author tinesoft
31+
*/
32+
33+
// helper to easily start a dockerized Elasticsearch server to run our tests against (not required to use this library)
34+
@Testcontainers
35+
36+
// The following annotation registers the @DeleteEsDataExtention with JUnit Jupiter
37+
// and specifies which data to remove from the underlying Elasticsearch Server.
38+
// The data will be removed only once, before all tests (i.e in the beforeAll() phase)
39+
@DeleteEsDataConfig({AuthorEsEntity.class, BookEsEntity.class})
40+
@SpringBootTest
41+
42+
//for this test setup only, not required in general
43+
@ContextConfiguration(initializers = DeleteEsDataExtensionTest.ExposedDockerizedEsConfiguration.class)
44+
45+
@TestMethodOrder(MethodOrderer.Alphanumeric.class)// because of the @SpyBean below, we need to control test order
46+
public class DeleteEsDataExtensionTest {
47+
48+
@Container
49+
// helper to easily start a dockerized Elasticsearch server to run our tests against (not required to use this library)
50+
public static final ElasticsearchContainer ES_CONTAINER = new ElasticsearchContainer(DemoTestPropertyValues.ES_DOCKER_IMAGE_VERSION);
51+
52+
@SpyBean(reset = MockReset.NONE)
53+
private ElasticsearchTemplate esMockTemplate;
54+
55+
@Test
56+
public void dataDeletedAtClassLevel() {
57+
58+
verify(this.esMockTemplate).deleteIndex(AuthorEsEntity.class);
59+
verify(this.esMockTemplate).deleteIndex(BookEsEntity.class);
60+
verify(this.esMockTemplate, never()).deleteIndex(LibraryEsEntity.class);//removed at method level, see below #dataDeleteedAtMethodLevel()
61+
}
62+
63+
@Test
64+
// the following data will be removed for this test only
65+
@DeleteEsData(esEntityClasses = {LibraryEsEntity.class})
66+
public void dataDeletedAtMethodLevel() {
67+
68+
verify(this.esMockTemplate).deleteIndex(AuthorEsEntity.class);//removed at class level, before all tests
69+
verify(this.esMockTemplate).deleteIndex(BookEsEntity.class);//removed at class level, before all tests
70+
verify(this.esMockTemplate).deleteIndex(LibraryEsEntity.class);
71+
}
72+
73+
public static class ExposedDockerizedEsConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> {
74+
75+
@Override
76+
public void initialize(ConfigurableApplicationContext cac) {
77+
DemoTestPropertyValues.using(ES_CONTAINER).applyTo(cac.getEnvironment());
78+
}
79+
}
80+
81+
}

demo/src/test/java/com/github/spring/esdata/loader/demo/junit/jupiter/LoadEsDataExtensionTest.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@
3838
@LoadEsData(esEntityClass = BookEsEntity.class, location = "/data/books.json.gz"), // built-in support for gzipped files
3939
})
4040
@SpringBootTest
41-
42-
@ContextConfiguration(initializers = LoadEsDataExtensionTest.ExposeDockerizedElasticsearchServer.class)//for this test setup only, not required in general
41+
//for this test setup only, not required in general
42+
@ContextConfiguration(initializers = LoadEsDataExtensionTest.ExposedDockerizedEsConfiguration.class)
4343
public class LoadEsDataExtensionTest {
4444

45-
@Container // helper to easily start a dockerized Elasticsearch server to run our tests against (not required to use this library)
46-
private static final ElasticsearchContainer ES_CONTAINER = new ElasticsearchContainer(DemoTestPropertyValues.ES_DOCKER_IMAGE_VERSION);
45+
@Container
46+
// helper to easily start a dockerized Elasticsearch server to run our tests against (not required to use this library)
47+
public static final ElasticsearchContainer ES_CONTAINER = new ElasticsearchContainer(DemoTestPropertyValues.ES_DOCKER_IMAGE_VERSION);
4748

48-
@Autowired
49+
@Autowired
4950
private AuthorEsRepository esAuthorRepository;
5051

5152
@Autowired
@@ -80,12 +81,13 @@ public void dataLoadedAtMethodLevel() {
8081
assertThat(libraries).hasSize(5);
8182
}
8283

83-
static class ExposeDockerizedElasticsearchServer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
84+
public static class ExposedDockerizedEsConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> {
85+
86+
@Override
87+
public void initialize(ConfigurableApplicationContext cac) {
88+
DemoTestPropertyValues.using(ES_CONTAINER).applyTo(cac.getEnvironment());
89+
}
90+
}
8491

85-
@Override
86-
public void initialize(ConfigurableApplicationContext cac) {
87-
DemoTestPropertyValues.using(ES_CONTAINER).applyTo(cac.getEnvironment());
88-
}
89-
}
9092
}
9193

0 commit comments

Comments
 (0)