DATACOUCH-519 - Reinstate template auditing

This reinstates the auditing that was present before the major
refactoring. Additional testing for createdBy, createdDate,
lastModifiedBy, lastModifiedDate has been added as well.
This commit is contained in:
mikereiche
2020-04-13 13:53:18 -07:00
committed by Michael Nitschinger
parent ebb8d39afd
commit e8e13ffebc
24 changed files with 905 additions and 84 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.data.couchbase.core;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import java.io.IOException;
import java.time.Duration;
@@ -26,17 +27,30 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
/**
* KV tests
*
* Theses tests rely on a cb server running.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationTests {
private static CouchbaseClientFactory couchbaseClientFactory;
@@ -55,8 +69,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
@BeforeEach
void beforeEach() {
CouchbaseConverter couchbaseConverter = new MappingCouchbaseConverter();
couchbaseTemplate = new CouchbaseTemplate(couchbaseClientFactory, couchbaseConverter);
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
couchbaseTemplate = (CouchbaseTemplate)ac.getBean(COUCHBASE_TEMPLATE);
}
@Test
@@ -67,6 +81,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
User found = couchbaseTemplate.findById(User.class).one(user.getId());
assertEquals(user, found);
couchbaseTemplate.removeById().one(user.getId());
}
@Test
@@ -86,6 +102,9 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
User loaded = couchbaseTemplate.findById(User.class).one(toReplace.getId());
assertEquals("some other", loaded.getFirstname());
couchbaseTemplate.removeById().one(toReplace.getId());
}
@Test
@@ -99,7 +118,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
assertTrue(removeResult.getCas() != 0);
assertTrue(removeResult.getMutationToken().isPresent());
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user.getId()));
assertThrows(DataRetrievalFailureException.class,
() -> couchbaseTemplate.findById(User.class).one(user.getId()));
}
@Test
@@ -109,10 +129,11 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
assertEquals(user, inserted);
assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user));
couchbaseTemplate.removeById().one(user.getId());
}
@Test
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
void existsById() {
String id = UUID.randomUUID().toString();
assertFalse(couchbaseTemplate.existsById().one(id));
@@ -122,6 +143,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT
assertEquals(user, inserted);
assertTrue(couchbaseTemplate.existsById().one(id));
couchbaseTemplate.removeById().one(user.getId());
}
}

View File

@@ -17,30 +17,46 @@
package org.springframework.data.couchbase.core;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.couchbase.domain.Config;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.test.context.TestContext;
import com.couchbase.client.core.error.IndexExistsException;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Query tests
*
* Theses tests rely on a cb server running
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED)
class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTests {
@@ -65,26 +81,44 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest
@BeforeEach
void beforeEach() {
CouchbaseConverter couchbaseConverter = new MappingCouchbaseConverter();
couchbaseTemplate = new CouchbaseTemplate(couchbaseClientFactory, couchbaseConverter);
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
}
@Test
void findByQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
try {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).all(Arrays.asList(user1, user2));
couchbaseTemplate.upsertById(User.class).all(Arrays.asList(user1, user2));
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
.consistentWith(QueryScanConsistency.REQUEST_PLUS).all();
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class).consistentWith(
QueryScanConsistency.REQUEST_PLUS).all();
assertEquals(2, foundUsers.size());
for (User u : foundUsers) {
assertTrue(u.equals(user1) || u.equals(user2));
for (User u : foundUsers) {
System.out.println(u);
if (!(u.equals(user1) || u.equals(user2))) {
// somebody didn't clean up after themselves.
couchbaseTemplate.removeById().one(u.getId());
}
}
assertEquals(2, foundUsers.size());
TemporalAccessor auditTime = new AuditingDateTimeProvider().getNow().get();
long auditMillis = Instant.from(auditTime).toEpochMilli();
String auditUser = new NaiveAuditorAware().getCurrentAuditor().get();
for (User u : foundUsers) {
assertTrue(u.equals(user1) || u.equals(user2));
assertEquals(auditUser, u.getCreator());
assertEquals(auditMillis, u.getCreatedDate());
assertEquals(auditUser, u.getLastModifiedBy());
assertEquals(auditMillis, u.getLastModifiedDate());
}
} finally {
couchbaseTemplate.removeByQuery(User.class).all();
}
}
@Test
void removeByQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
@@ -97,8 +131,10 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest
couchbaseTemplate.removeByQuery(User.class).consistentWith(QueryScanConsistency.REQUEST_PLUS).all();
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user1.getId()));
assertThrows(DataRetrievalFailureException.class, () -> couchbaseTemplate.findById(User.class).one(user2.getId()));
assertThrows(DataRetrievalFailureException.class,
() -> couchbaseTemplate.findById(User.class).one(user1.getId()));
assertThrows(DataRetrievalFailureException.class,
() -> couchbaseTemplate.findById(User.class).one(user2.getId()));
}
}

View File

@@ -30,11 +30,14 @@ import org.springframework.data.couchbase.core.convert.DefaultCouchbaseTypeMappe
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.java.kv.GetResult;
@SpringJUnitConfig(CustomTypeKeyIntegrationTests.Config.class)
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
public class CustomTypeKeyIntegrationTests extends ClusterAwareIntegrationTests {
private static final String CUSTOM_TYPE_KEY = "javaClass";
@@ -48,6 +51,9 @@ public class CustomTypeKeyIntegrationTests extends ClusterAwareIntegrationTests
clientFactory.getBucket().waitUntilReady(Duration.ofSeconds(5));
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
// When using 'mocked', this call runs fine when the test class is ran by itself,
// but it times-out when ran together with all the tests under
// org.springframework.data.couchbase
User modified = operations.upsertById(User.class).one(user);
assertEquals(user, modified);

View File

@@ -0,0 +1,64 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy;
import java.util.UUID;
/**
* @author Oliver Gierke
*/
@Document
public class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationStrategy.UNIQUE)
private UUID id;
public AbstractEntity(){ }
/**
* set the id
*/
public void setId(UUID id) {
this.id=id;
}
/**
* @return the id
*/
public UUID getId() {
return id;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {
return false;
}
AbstractEntity that = (AbstractEntity) obj;
return this.id.equals(that.getId());
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}

View File

@@ -0,0 +1,49 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
@Configuration
@EnableCouchbaseRepositories
@EnableCouchbaseAuditing //this activates auditing
public class Config extends AbstractCouchbaseConfiguration {
String bucketname = "travel-sample";
String username = "Administrator";
String password = "password";
String connectionString = "127.0.0.1";
@Override
public String getConnectionString() {
return connectionString;
}
@Override
public String getUserName() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getBucketName() {
return bucketname;
}
@Bean(name = "auditorAwareRef")
public NaiveAuditorAware testAuditorAware() {
return new NaiveAuditorAware();
}
@Bean(name = "dateTimeProviderRef")
public DateTimeProvider testDateTimeProvider() {
return new AuditingDateTimeProvider();
}
}

View File

@@ -0,0 +1,30 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.domain.AuditorAware;
import java.util.Optional;
// These are the classes that would be used for a real getCurrentAuditor() implementation
//import org.springframework.security.core.Authentication;
//import org.springframework.security.core.context.SecurityContextHolder;
//import org.springframework.security.core.userdetails.User;
/**
* This class returns a string that represents the current user
*
* @author Michael Reiche
* @since 3.0
*/
public class NaiveAuditorAware implements AuditorAware<String> {
private Optional<String> auditor = Optional.of("auditor");
@Override
public Optional<String> getCurrentAuditor() {
return auditor;
}
public void setAuditor(String auditor) {
this.auditor = Optional.of(auditor);
}
}

View File

@@ -0,0 +1,90 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.annotation.*;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.core.mapping.event.AuditingEventListener;
import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
@Document
public class Person extends AbstractEntity {
Optional<String> firstname;
Optional<String> lastname;
@CreatedBy
private String creator;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private long lastModification;
@CreatedDate
private long creationDate; // =System.currentTimeMillis();
@Version
private long version;
public Person(){
}
public Person(String firstname, String lastname){
this();
setFirstname(firstname);
setLastname(lastname);
}
public Person(int id,String firstname, String lastname){
this(firstname,lastname);
setId(new UUID(id, id));
}
public Optional<String> getFirstname(){
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname == null ? null : ( Optional.ofNullable(firstname.equals("")?null:firstname) );
}
public void setFirstname(Optional<String> firstname) {
this.firstname = firstname;
}
public Optional<String> getLastname(){
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname == null ? null : ( Optional.ofNullable(lastname.equals("")?null:lastname) );
}
public void setLastname(Optional lastname) {
this.lastname = lastname;
}
public String toString(){
StringBuilder sb=new StringBuilder();
sb.append("Person : {\n");
sb.append(" id : "+getId());
sb.append(optional(", firstname",firstname));
sb.append(optional( ", lastname", lastname));
sb.append(", version : "+version);
if(creator != null) sb.append(", creator : "+creator);
if(creationDate != 0) sb.append(", creationDate : "+creationDate);
if(lastModifiedBy != null) sb.append(", lastModifiedBy : "+lastModifiedBy);
if(lastModification != 0) sb.append(", lastModification : "+lastModification);
sb.append("}");
return sb.toString();
}
static String optional(String name, Optional<String> obj){
if(obj != null)
if(obj.isPresent())
return(" "+name+ ": '"+obj.get()+"'\n");
else
return" "+name+": null\n" ;
return "";
}
}

View File

@@ -18,17 +18,33 @@ package org.springframework.data.couchbase.domain;
import java.util.Objects;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.*;
import org.springframework.data.couchbase.core.mapping.Document;
/**
* User entity for tests
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@Document
public class User {
@Id private String id;
@Id
private String id;
private String firstname;
private String lastname;
@CreatedBy
private String createdBy;
@CreatedDate
private long createdDate;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private long lastModifiedDate;
@Version
long version;
@PersistenceConstructor
public User(final String id, final String firstname, final String lastname) {
@@ -49,6 +65,26 @@ public class User {
return lastname;
}
public long getCreatedDate() {
return createdDate;
}
public String getCreator() {
return createdBy;
}
public long getLastModifiedDate() {
return lastModifiedDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public long getVersion() {
return version;
}
@Override
public boolean equals(Object o) {
if (this == o)
@@ -56,8 +92,8 @@ public class User {
if (o == null || getClass() != o.getClass())
return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(firstname, user.firstname)
&& Objects.equals(lastname, user.lastname);
return Objects.equals(id, user.id) && Objects.equals(firstname, user.firstname) && Objects.equals(lastname,
user.lastname);
}
@Override
@@ -67,6 +103,8 @@ public class User {
@Override
public String toString() {
return "User{" + "id='" + id + '\'' + ", firstname='" + firstname + '\'' + ", lastname='" + lastname + '\'' + '}';
return "User{" + "id='" + id + '\'' + ", firstname='" + firstname + '\'' + ", lastname='" + lastname + '\''
+ ", createdBy='" + createdBy + '\'' + ", createdDate='" + createdDate + '\'' + ", lastModifiedBy='"
+ lastModifiedBy + '\'' + ", lastModifiedDate='" + lastModifiedDate + '\'' + '}';
}
}

View File

@@ -20,7 +20,13 @@ import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
/**
* User Repository for tests
* @author Michael Nitschinger
* @author Michael Reiche
*/
@Repository
public interface UserRepository extends PagingAndSortingRepository<User, String> {

View File

@@ -0,0 +1,24 @@
package org.springframework.data.couchbase.domain.time;
import org.springframework.data.auditing.DateTimeProvider;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
public class AuditingDateTimeProvider implements DateTimeProvider {
private DateTimeService dateTimeService = new FixedDateTimeService();
public AuditingDateTimeProvider() {
}
public AuditingDateTimeProvider(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
@Override
public Optional<TemporalAccessor> getNow() {
return Optional.of(Instant.ofEpochSecond(dateTimeService.getCurrentDateAndTime().toEpochSecond()));
}
}

View File

@@ -0,0 +1,10 @@
package org.springframework.data.couchbase.domain.time;
import java.time.ZonedDateTime;
public class CurrentDateTimeService implements DateTimeService {
@Override
public ZonedDateTime getCurrentDateAndTime() {
return ZonedDateTime.now();
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.data.couchbase.domain.time;
import java.time.ZonedDateTime;
public interface DateTimeService {
ZonedDateTime getCurrentDateAndTime();
}

View File

@@ -0,0 +1,15 @@
package org.springframework.data.couchbase.domain.time;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class FixedDateTimeService implements DateTimeService {
@Override
public ZonedDateTime getCurrentDateAndTime() {
return ZonedDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneId.of("GMT-8"));
}
public static void main(String[] args) {
System.out.println((new FixedDateTimeService()).getCurrentDateAndTime());
}
}

View File

@@ -28,21 +28,30 @@ import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* Repository KV tests
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@SpringJUnitConfig(CouchbaseRepositoryKeyValueIntegrationTests.Config.class)
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
public class CouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareIntegrationTests {
@Autowired UserRepository userRepository;
@Autowired
UserRepository userRepository;
@Test
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
void saveAndFindById() {
User user = new User(UUID.randomUUID().toString(), "f", "l");
// this currently fails when using mocked in integration.properties with status "UNKNOWN"
assertFalse(userRepository.existsById(user.getId()));
userRepository.save(user);
@@ -52,6 +61,7 @@ public class CouchbaseRepositoryKeyValueIntegrationTests extends ClusterAwareInt
found.ifPresent(u -> assertEquals(user, u));
assertTrue(userRepository.existsById(user.getId()));
userRepository.delete(user);
}
@Configuration