DATACMNS-1259 - Fixed support for Long values in Auditables.

Using Instant as internal data type since it's a point in time without time zone which LocalDateTime isn't. Added necessary converters. Fixed one JodaTime converter that used UTC to use SystemDefault like other similar converters.

In case of a conversion failure the error message now contains the source type.

Original pull request: #273.
This commit is contained in:
Jens Schauder
2018-02-13 14:34:58 +01:00
committed by Oliver Gierke
parent 4947ca01dc
commit 5251f72e69
5 changed files with 175 additions and 8 deletions

View File

@@ -18,18 +18,27 @@ package org.springframework.data.auditing;
import static org.assertj.core.api.Assertions.*;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Optional;
import org.junit.Test;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper;
import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper;
import org.springframework.data.domain.Auditable;
/**
* Unit tests for {@link DefaultAuditableBeanWrapperFactory}.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Jens Schauder
* @since 1.5
*/
public class DefaultAuditableBeanWrapperFactoryUnitTests {
@@ -88,4 +97,70 @@ public class DefaultAuditableBeanWrapperFactoryUnitTests {
assertThat(wrapper).hasValueSatisfying(it -> it.setLastModifiedDate(zonedDateTime));
}
@Test // DATACMNS-1259
public void lastModifiedDateAsLongIsAvailableViaWrapper() {
LongBasedAuditable source = new LongBasedAuditable();
source.dateModified = 42000L;
Optional<AuditableBeanWrapper> beanWrapper = factory.getBeanWrapperFor(source);
assertThat(beanWrapper).isPresent();
assertThat(beanWrapper.flatMap(AuditableBeanWrapper::getLastModifiedDate).get()) //
.extracting(ta -> ta.getLong(ChronoField.INSTANT_SECONDS)) //
.containsExactly(42L);
}
@Test // DATACMNS-1259
public void canSetLastModifiedDateAsInstantViaWrapperOnLongField() {
LongBasedAuditable source = new LongBasedAuditable();
Optional<AuditableBeanWrapper> beanWrapper = factory.getBeanWrapperFor(source);
assertThat(beanWrapper).isPresent();
beanWrapper.get().setLastModifiedDate(Instant.ofEpochMilli(42L));
assertThat(source.dateModified).isEqualTo(42L);
}
@Test // DATACMNS-1259
public void canSetLastModifiedDateAsLocalDateTimeViaWrapperOnLongField() {
LongBasedAuditable source = new LongBasedAuditable();
Optional<AuditableBeanWrapper> beanWrapper = factory.getBeanWrapperFor(source);
assertThat(beanWrapper).isPresent();
beanWrapper.get().setLastModifiedDate(LocalDateTime.ofInstant(Instant.ofEpochMilli(42L), ZoneOffset.systemDefault()));
assertThat(source.dateModified).isEqualTo(42L);
}
@Test // DATACMNS-1259
public void lastModifiedAsLocalDateTimeDateIsAvailableViaWrapperAsLocalDateTime() {
LocalDateTime now = LocalDateTime.now();
AuditedUser source = new AuditedUser();
source.setLastModifiedDate(now);
Optional<AuditableBeanWrapper> beanWrapper = factory.getBeanWrapperFor(source);
assertThat(beanWrapper).isPresent();
assertThat(beanWrapper.flatMap(AuditableBeanWrapper::getLastModifiedDate).get()) //
.isEqualTo(now);
}
public static class LongBasedAuditable {
@CreatedDate
public Long dateCreated;
@LastModifiedDate
public Long dateModified;
}
}

View File

@@ -20,6 +20,8 @@ import static org.mockito.Mockito.*;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Collections;
@@ -27,6 +29,7 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Optional;
import org.assertj.core.api.AbstractLongAssert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.CreatedBy;
@@ -45,6 +48,7 @@ import org.springframework.data.mapping.context.SampleMappingContext;
* Unit tests for {@link MappingAuditableBeanWrapperFactory}.
*
* @author Oliver Gierke
* @author Jens Schauder
* @since 1.8
*/
public class MappingAuditableBeanWrapperFactoryUnitTests {
@@ -169,6 +173,14 @@ public class MappingAuditableBeanWrapperFactoryUnitTests {
assertThat(wrapper.flatMap(it -> it.getLastModifiedDate())).hasValue(sample.modified);
}
@Test // DATACMNS-1259
public void exposesLongAsModificationDate() {
Long reference = new Date().getTime();
assertLastModificationDate(reference, Instant.ofEpochMilli(reference));
}
private void assertLastModificationDate(Object source, TemporalAccessor expected) {
Sample sample = new Sample();
@@ -176,7 +188,27 @@ public class MappingAuditableBeanWrapperFactoryUnitTests {
Optional<AuditableBeanWrapper> wrapper = factory.getBeanWrapperFor(sample);
assertThat(wrapper.flatMap(it -> it.getLastModifiedDate())).hasValue(expected);
assertThat(wrapper.flatMap(it -> it.getLastModifiedDate())).hasValueSatisfying(ta -> {
compareTemporalAccessors(expected, ta);
});
}
private AbstractLongAssert<?> compareTemporalAccessors(TemporalAccessor expected, TemporalAccessor actual) {
long actualSeconds = getInstantSeconds(actual);
long expectedSeconds = getInstantSeconds(expected);
return assertThat(actualSeconds).describedAs("Difference is %s", actualSeconds - expectedSeconds)
.isEqualTo(expectedSeconds);
}
private long getInstantSeconds(TemporalAccessor actual) {
if (actual instanceof LocalDateTime) {
return getInstantSeconds(((LocalDateTime) actual).atZone(ZoneOffset.systemDefault()));
}
return actual.getLong(ChronoField.INSTANT_SECONDS);
}
static class Sample {