diff --git a/iris-client-bff/pom.xml b/iris-client-bff/pom.xml
index 14b5a4a6b..067c2a3e2 100644
--- a/iris-client-bff/pom.xml
+++ b/iris-client-bff/pom.xml
@@ -159,6 +159,12 @@
3.0.4
test
+
+ com.github.javafaker
+ javafaker
+ 1.0.2
+ test
+
diff --git a/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataRequestRepository.java b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataRequestRepository.java
index 2677f49b8..0b2b0084d 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataRequestRepository.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataRequestRepository.java
@@ -10,6 +10,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.util.Streamable;
/**
* @author Jens Kutzsche
@@ -27,9 +28,17 @@ public interface CaseDataRequestRepository extends JpaRepository findByStatus(Status status, Pageable pageable);
- Page findByRefIdContainsOrNameContainsAllIgnoreCase(String search, String search1, Pageable pageable);
+ Page findByRefIdContainsOrNameContainsAllIgnoreCase(String search, String search1,
+ Pageable pageable);
@Query("select r from CaseDataRequest r where r.status = :status and ( upper(r.refId) like concat('%', upper(:search), '%') or upper(r.name) like concat('%', upper(:search), '%'))")
Page findByStatusAndSearchByRefIdOrName(Status status, String search, Pageable pageable);
+ /**
+ * Returns the {@link CaseDataRequest}s created before the given {@link Instant}.
+ *
+ * @param refDate must not be {@literal null}.
+ * @return
+ */
+ Streamable findByMetadataCreatedIsBefore(Instant refDate);
}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataSubmissionRepository.java b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataSubmissionRepository.java
index 722d3cd52..ac08ca08f 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataSubmissionRepository.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDataSubmissionRepository.java
@@ -1,15 +1,18 @@
package iris.client_bff.cases;
import iris.client_bff.cases.model.CaseDataSubmission;
-
import iris.client_bff.cases.model.CaseDataSubmission.DataSubmissionIdentifier;
+
import javax.transaction.Transactional;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.util.Streamable;
-public interface CaseDataSubmissionRepository extends CrudRepository {
+public interface CaseDataSubmissionRepository extends JpaRepository {
@Transactional
Streamable findAllByRequest(CaseDataRequest request);
+
+ @Transactional
+ void deleteAllByRequestIn(Iterable extends CaseDataRequest> requests);
}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDeleteJob.java b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDeleteJob.java
new file mode 100644
index 000000000..ed157fe5a
--- /dev/null
+++ b/iris-client-bff/src/main/java/iris/client_bff/cases/CaseDeleteJob.java
@@ -0,0 +1,76 @@
+package iris.client_bff.cases;
+
+import iris.client_bff.cases.CaseDataRequest.DataRequestIdentifier;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.ZoneId;
+import java.util.stream.Collectors;
+
+import javax.transaction.Transactional;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.ConstructorBinding;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class collects all old cases and deletes this.
+ *
+ * @author Jens Kutzsche
+ */
+@Component
+@Slf4j
+@RequiredArgsConstructor
+class CaseDeleteJob {
+
+ private final @NonNull CaseDataRequestRepository caseRequests;
+ private final @NonNull CaseDataSubmissionRepository caseSubmissions;
+ private final @NonNull CaseDeletionProperties properties;
+
+ @Transactional
+ @Scheduled(cron = "${iris.client.case.delete-cron:-}")
+ void deleteCaseRequests() {
+
+ var refDate = LocalDate.now().minus(properties.getDeleteAfter()).atStartOfDay().atZone(ZoneId.systemDefault())
+ .toInstant();
+
+ var oldRequests = caseRequests.findByMetadataCreatedIsBefore(refDate).toList();
+
+ if (oldRequests.isEmpty()) {
+ return;
+ }
+
+ log.debug("{} case data request(s) are deleted with period {} after their creation!",
+ oldRequests,
+ properties.getDeleteAfter(),
+ oldRequests.get(0).getCreatedAt());
+
+ caseSubmissions.deleteAllByRequestIn(oldRequests);
+ caseRequests.deleteAll(oldRequests);
+
+ log.info("{} case data request(s) (IDs: {}) were deleted with period {} after their creation at {}!",
+ oldRequests.size(),
+ oldRequests.stream().map(CaseDataRequest::getId)
+ .map(DataRequestIdentifier::toString)
+ .collect(Collectors.joining(", ")),
+ properties.getDeleteAfter(),
+ oldRequests.get(0).getCreatedAt());
+ }
+
+ @ConstructorBinding
+ @RequiredArgsConstructor
+ @ConfigurationProperties("iris.client.case")
+ @Getter
+ public static class CaseDeletionProperties {
+
+ /**
+ * Defines the {@link Period} after that a case will be deleted starting from the creation date.
+ */
+ private final Period deleteAfter;
+ }
+}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/core/IrisDateTimeProvider.java b/iris-client-bff/src/main/java/iris/client_bff/core/IrisDateTimeProvider.java
index 276e20471..13a8ca089 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/core/IrisDateTimeProvider.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/core/IrisDateTimeProvider.java
@@ -4,7 +4,7 @@
import lombok.RequiredArgsConstructor;
import lombok.Setter;
-import java.time.Instant;
+import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
@@ -42,7 +42,7 @@ public void reset() {
*/
@Override
public Optional getNow() {
- return Optional.of(Instant.now().plus(delta));
+ return Optional.of(LocalDateTime.now().plus(delta));
}
@ConstructorBinding
diff --git a/iris-client-bff/src/main/java/iris/client_bff/events/EventDataRequestRepository.java b/iris-client-bff/src/main/java/iris/client_bff/events/EventDataRequestRepository.java
index d0c88ef91..5666ec39e 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/events/EventDataRequestRepository.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/events/EventDataRequestRepository.java
@@ -10,6 +10,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.util.Streamable;
/**
* @author Jens Kutzsche
@@ -27,8 +28,17 @@ public interface EventDataRequestRepository extends JpaRepository findByStatus(Status status, Pageable pageable);
- Page findByRefIdContainsOrNameContainsAllIgnoreCase(String search, String search1, Pageable pageable);
+ Page findByRefIdContainsOrNameContainsAllIgnoreCase(String search, String search1,
+ Pageable pageable);
@Query("select r from EventDataRequest r where r.status = :status and ( upper(r.refId) like concat('%', upper(:search), '%') or upper(r.name) like concat('%', upper(:search), '%'))")
Page findByStatusAndSearchByRefIdOrName(Status status, String search, Pageable pageable);
+
+ /**
+ * Returns the {@link EventDataRequest}s created before the given {@link Instant}.
+ *
+ * @param refDate must not be {@literal null}.
+ * @return
+ */
+ Streamable findByMetadataCreatedIsBefore(Instant refDate);
}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/events/EventDataSubmissionRepository.java b/iris-client-bff/src/main/java/iris/client_bff/events/EventDataSubmissionRepository.java
index 3e92eb208..fdc4a2af1 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/events/EventDataSubmissionRepository.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/events/EventDataSubmissionRepository.java
@@ -5,14 +5,17 @@
import javax.transaction.Transactional;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.util.Streamable;
/**
* @author Jens Kutzsche
*/
-public interface EventDataSubmissionRepository extends CrudRepository {
+public interface EventDataSubmissionRepository extends JpaRepository {
@Transactional
Streamable findAllByRequest(EventDataRequest request);
+
+ @Transactional
+ void deleteAllByRequestIn(Iterable extends EventDataRequest> requests);
}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/events/EventDeleteJob.java b/iris-client-bff/src/main/java/iris/client_bff/events/EventDeleteJob.java
new file mode 100644
index 000000000..6f5df94f3
--- /dev/null
+++ b/iris-client-bff/src/main/java/iris/client_bff/events/EventDeleteJob.java
@@ -0,0 +1,75 @@
+package iris.client_bff.events;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.ZoneId;
+import java.util.stream.Collectors;
+
+import javax.transaction.Transactional;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.ConstructorBinding;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class collects all old events and deletes this.
+ *
+ * @author Jens Kutzsche
+ */
+@Component
+@Slf4j
+@RequiredArgsConstructor
+class EventDeleteJob {
+
+ private final @NonNull EventDataRequestRepository eventRequests;
+ private final @NonNull EventDataSubmissionRepository eventSubmissions;
+ private final @NonNull EventDeletionProperties properties;
+
+ @Transactional
+ @Scheduled(cron = "${iris.client.event.delete-cron:-}")
+ void deleteEventRequests() {
+
+ var refDate = LocalDate.now().minus(properties.getDeleteAfter()).atStartOfDay().atZone(ZoneId.systemDefault())
+ .toInstant();
+
+ var oldRequests = eventRequests.findByMetadataCreatedIsBefore(refDate).toList();
+
+ if (oldRequests.isEmpty()) {
+ return;
+ }
+
+ log.debug("{} event data request(s) are deleted with period {} after their creation!",
+ oldRequests,
+ properties.getDeleteAfter(),
+ oldRequests.get(0).getCreatedAt());
+
+ eventSubmissions.deleteAllByRequestIn(oldRequests);
+ eventRequests.deleteAll(oldRequests);
+
+ log.info("{} event data request(s) (IDs: {}) were deleted with period {} after their creation at {}!",
+ oldRequests.size(),
+ oldRequests.stream().map(EventDataRequest::getId)
+ .map(EventDataRequest.DataRequestIdentifier::toString)
+ .collect(Collectors.joining(", ")),
+ properties.getDeleteAfter(),
+ oldRequests.get(0).getCreatedAt());
+ }
+
+ @ConstructorBinding
+ @RequiredArgsConstructor
+ @ConfigurationProperties("iris.client.event")
+ @Getter
+ public static class EventDeletionProperties {
+
+ /**
+ * Defines the {@link Period} after that a event will be deleted starting from the creation date.
+ */
+ private final Period deleteAfter;
+ }
+}
diff --git a/iris-client-bff/src/main/java/iris/client_bff/events/model/Location.java b/iris-client-bff/src/main/java/iris/client_bff/events/model/Location.java
index 68040328c..0c5a6a634 100644
--- a/iris-client-bff/src/main/java/iris/client_bff/events/model/Location.java
+++ b/iris-client-bff/src/main/java/iris/client_bff/events/model/Location.java
@@ -1,6 +1,7 @@
package iris.client_bff.events.model;
import iris.client_bff.core.Id;
+import iris.client_bff.events.EventDataRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -13,6 +14,7 @@
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
+import javax.persistence.OneToOne;
@Entity
@Data
@@ -45,6 +47,9 @@ public class Location {
private String contactPhone;
+ @OneToOne(mappedBy = "location")
+ private EventDataRequest request;
+
@Embeddable
@EqualsAndHashCode
@Getter
diff --git a/iris-client-bff/src/main/resources/application.properties b/iris-client-bff/src/main/resources/application.properties
index 79a1bf7fa..5b3dfe507 100644
--- a/iris-client-bff/src/main/resources/application.properties
+++ b/iris-client-bff/src/main/resources/application.properties
@@ -9,6 +9,10 @@ spring.application.name=IRIS Client Backend for Frontend
iris.client.mailing.active=false
iris.client.sendAbort.active=false
+iris.client.case.delete-after=6m
+iris.client.case.delete-cron=0 30 1 * * *
+iris.client.event.delete-after=6m
+iris.client.event.delete-cron=0 30 1 * * *
# Spring Mail
spring.mail.host=127.0.0.1
diff --git a/iris-client-bff/src/test/java/iris/client_bff/FakerConfig.java b/iris-client-bff/src/test/java/iris/client_bff/FakerConfig.java
new file mode 100644
index 000000000..50ec9223a
--- /dev/null
+++ b/iris-client-bff/src/test/java/iris/client_bff/FakerConfig.java
@@ -0,0 +1,43 @@
+package iris.client_bff;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Locale;
+import java.util.Random;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.github.javafaker.Faker;
+
+/**
+ * @author Jens Kutzsche
+ */
+@Configuration
+@Slf4j
+public class FakerConfig {
+
+ @Value("${iris.test.faker.seed:#{null}}")
+ Long preConfiguredSeed;
+
+ static Faker faker;
+
+ @Bean
+ public Faker getFaker() {
+
+ if (faker == null) {
+
+ var seed = preConfiguredSeed == null ? RandomUtils.nextLong() : preConfiguredSeed;
+
+ log.info(
+ "Faker is created with SEED = {}; The seed can be set with property iris.test.faker.seed or env IRIS_TEST_FAKER_SEED.",
+ seed);
+
+ faker = new Faker(Locale.GERMANY, new Random(seed));
+ }
+
+ return faker;
+ }
+}
diff --git a/iris-client-bff/src/test/java/iris/client_bff/cases/CaseDeleteJobIntegrationTests.java b/iris-client-bff/src/test/java/iris/client_bff/cases/CaseDeleteJobIntegrationTests.java
new file mode 100644
index 000000000..3d7b87af6
--- /dev/null
+++ b/iris-client-bff/src/test/java/iris/client_bff/cases/CaseDeleteJobIntegrationTests.java
@@ -0,0 +1,79 @@
+package iris.client_bff.cases;
+
+import static org.assertj.core.api.Assertions.*;
+
+import iris.client_bff.IrisWebIntegrationTest;
+import iris.client_bff.cases.model.CaseDataSubmission;
+import iris.client_bff.core.IrisDateTimeProvider;
+import lombok.RequiredArgsConstructor;
+
+import java.time.Instant;
+import java.time.Period;
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import com.github.javafaker.Faker;
+
+/**
+ * @author Jens Kutzsche
+ */
+@IrisWebIntegrationTest
+@RequiredArgsConstructor
+class CaseDeleteJobIntegrationTests {
+
+ private final CaseDataRequestRepository caseRequests;
+ private final CaseDataSubmissionRepository caseSubmissions;
+ private final IrisDateTimeProvider dateTimeProvider;
+ private final CaseDeleteJob deleteJob;
+ private final Faker faker;
+
+ @Test // Issue #244
+ void testDeleteCaseRequests() {
+
+ var requestsSize = caseRequests.findAll().size();
+ var submissionSize = caseSubmissions.findAll().size();
+
+ // in time
+ dateTimeProvider.setDelta(Period.ofMonths(-6));
+
+ var request = createRequest(faker.name().name(), faker.idNumber().valid(),
+ faker.date().past(200, 186, TimeUnit.DAYS).toInstant());
+ createSubmission(request);
+
+ // to old
+ dateTimeProvider.setDelta(Period.ofMonths(-6).minusDays(1));
+
+ var oldName = faker.name().name();
+
+ request = createRequest(oldName, faker.idNumber().valid(), faker.date().past(200, 186, TimeUnit.DAYS).toInstant());
+ createSubmission(request);
+
+ dateTimeProvider.reset();
+
+ // extra element from data initialization
+ assertThat(caseRequests.findAll()).hasSize(requestsSize + 2).extracting(CaseDataRequest::getName).contains(oldName);
+ assertThat(caseSubmissions.findAll()).hasSize(submissionSize + 2);
+
+ deleteJob.deleteCaseRequests();
+
+ assertThat(caseRequests.findAll()).hasSize(requestsSize + 1).extracting(CaseDataRequest::getName)
+ .doesNotContain(oldName);
+ assertThat(caseSubmissions.findAll()).hasSize(submissionSize + 1);
+ }
+
+ private CaseDataRequest createRequest(String name, String refId, Instant date) {
+
+ return caseRequests.save(CaseDataRequest.builder()
+ .name(name)
+ .refId(refId)
+ .requestStart(date)
+ .requestEnd(date.plus(6, ChronoUnit.HOURS))
+ .build());
+ }
+
+ private void createSubmission(CaseDataRequest request) {
+ caseSubmissions.save(new CaseDataSubmission(request, null, null, null, null, null, null, null));
+ }
+}
diff --git a/iris-client-bff/src/test/java/iris/client_bff/core/mail/TestEmailServer.java b/iris-client-bff/src/test/java/iris/client_bff/core/mail/TestEmailServer.java
index 92f0e1091..9cff17a1b 100644
--- a/iris-client-bff/src/test/java/iris/client_bff/core/mail/TestEmailServer.java
+++ b/iris-client-bff/src/test/java/iris/client_bff/core/mail/TestEmailServer.java
@@ -23,7 +23,6 @@
* Wrapper around a {@link GreenMail} instance to simplify testing assertions.
*
* @author Oliver Drotbohm
- * @since 1.4
*/
@Slf4j
@Component
diff --git a/iris-client-bff/src/test/java/iris/client_bff/events/EventDeleteJobIntegrationTests.java b/iris-client-bff/src/test/java/iris/client_bff/events/EventDeleteJobIntegrationTests.java
new file mode 100644
index 000000000..76f26ed7f
--- /dev/null
+++ b/iris-client-bff/src/test/java/iris/client_bff/events/EventDeleteJobIntegrationTests.java
@@ -0,0 +1,89 @@
+package iris.client_bff.events;
+
+import static org.assertj.core.api.Assertions.*;
+
+import iris.client_bff.IrisWebIntegrationTest;
+import iris.client_bff.core.IrisDateTimeProvider;
+import iris.client_bff.events.model.EventDataSubmission;
+import iris.client_bff.events.model.Location;
+import iris.client_bff.events.model.Location.LocationIdentifier;
+import lombok.RequiredArgsConstructor;
+
+import java.time.Instant;
+import java.time.Period;
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+import com.github.javafaker.Faker;
+
+/**
+ * @author Jens Kutzsche
+ */
+@IrisWebIntegrationTest
+@RequiredArgsConstructor
+class EventDeleteJobIntegrationTests {
+
+ private final EventDataRequestRepository eventRequests;
+ private final EventDataSubmissionRepository eventSubmissions;
+ private final IrisDateTimeProvider dateTimeProvider;
+ private final EventDeleteJob deleteJob;
+ private final Faker faker;
+
+ @Test // Issue #244
+ void testDeleteEventRequests() {
+
+ var requestsSize = eventRequests.findAll().size();
+ var submissionSize = eventSubmissions.findAll().size();
+
+ // in time
+ dateTimeProvider.setDelta(Period.ofMonths(-6));
+
+ var request = createRequest(faker.name().name(), faker.idNumber().valid(),
+ faker.date().past(200, 186, TimeUnit.DAYS).toInstant());
+ createSubmission(request);
+
+ // to old
+ dateTimeProvider.setDelta(Period.ofMonths(-6).minusDays(1));
+
+ var oldName = faker.name().name();
+
+ request = createRequest(oldName, faker.idNumber().valid(), faker.date().past(200, 186, TimeUnit.DAYS).toInstant());
+ createSubmission(request);
+
+ dateTimeProvider.reset();
+
+ // extra element from data initialization
+ assertThat(eventRequests.findAll()).hasSize(requestsSize + 2).element(4).satisfies(it -> {
+ assertThat(it.getName()).isEqualTo(oldName);
+ });
+ assertThat(eventSubmissions.findAll()).hasSize(submissionSize + 2);
+
+ deleteJob.deleteEventRequests();
+
+ assertThat(eventRequests.findAll()).hasSize(requestsSize + 1).extracting(EventDataRequest::getName)
+ .doesNotContain(oldName);
+ assertThat(eventSubmissions.findAll()).hasSize(submissionSize + 1);
+ }
+
+ private EventDataRequest createRequest(String name, String refId, Instant date) {
+
+ var location = new Location(new LocationIdentifier(), faker.idNumber().valid(), faker.idNumber().valid(), null,
+ null, null, null,
+ null, null,
+ null, null, null, null);
+
+ return eventRequests.save(EventDataRequest.builder()
+ .name(name)
+ .refId(refId)
+ .requestStart(date)
+ .requestEnd(date.plus(6, ChronoUnit.HOURS))
+ .location(location)
+ .build());
+ }
+
+ private void createSubmission(EventDataRequest request) {
+ eventSubmissions.save(new EventDataSubmission(request, null, null, null, null, null));
+ }
+}