#581 - Add Cassandra examples for auditing.
This commit is contained in:
@@ -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
|
||||||
|
*
|
||||||
|
* http://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 example.springdata.cassandra.auditing;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
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.annotation.Transient;
|
||||||
|
import org.springframework.data.cassandra.core.mapping.Table;
|
||||||
|
import org.springframework.data.domain.Persistable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple domain object that declares properties annotated with Spring Data's annotations for auditing.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Table
|
||||||
|
public class AuditedPerson implements Persistable<Long> {
|
||||||
|
|
||||||
|
@Id Long id;
|
||||||
|
|
||||||
|
@CreatedBy String createdBy;
|
||||||
|
|
||||||
|
@LastModifiedBy String lastModifiedBy;
|
||||||
|
|
||||||
|
@CreatedDate Instant createdDate;
|
||||||
|
|
||||||
|
@LastModifiedDate Instant lastModifiedDate;
|
||||||
|
|
||||||
|
@Transient boolean isNew;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNew() {
|
||||||
|
return isNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* http://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 example.springdata.cassandra.auditing;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple repository interface for {@link AuditedPerson} instances.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public interface AuditedPersonRepository extends CrudRepository<AuditedPerson, Long> {}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* http://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 example.springdata.cassandra.auditing;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.cassandra.config.EnableCassandraAuditing;
|
||||||
|
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
|
||||||
|
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||||
|
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
|
||||||
|
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
|
||||||
|
import org.springframework.data.domain.AuditorAware;
|
||||||
|
|
||||||
|
import com.datastax.oss.driver.api.core.CqlSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic {@link Configuration} to create the necessary schema for the {@link AuditedPerson} table.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
@EntityScan(basePackageClasses = AuditedPerson.class)
|
||||||
|
@EnableCassandraAuditing
|
||||||
|
class BasicConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code @Bean} method defining a supplier for an auditor. This could be also an integration with a security
|
||||||
|
* framework such as Spring Security.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
AuditorAware<String> auditorAware() {
|
||||||
|
return () -> Optional.of("Some user");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code @Bean} method defining a {@link MappingCassandraConverter} as currently the auditing requires a bean
|
||||||
|
* definition for {@link MappingCassandraConverter}.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MappingCassandraConverter cassandraConverter(CassandraMappingContext mapping,
|
||||||
|
CassandraCustomConversions conversions, CqlSession session) {
|
||||||
|
|
||||||
|
MappingCassandraConverter converter = new MappingCassandraConverter(mapping);
|
||||||
|
|
||||||
|
converter.setCodecRegistry(session.getContext().getCodecRegistry());
|
||||||
|
converter.setCustomConversions(conversions);
|
||||||
|
converter.setUserTypeResolver(new SimpleUserTypeResolver(session));
|
||||||
|
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* http://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 example.springdata.cassandra.auditing;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import example.springdata.cassandra.util.CassandraKeyspace;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test showing the basic usage of {@link AuditedPersonRepository}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(classes = BasicConfiguration.class)
|
||||||
|
public class AuditedPersonRepositoryTests {
|
||||||
|
|
||||||
|
@ClassRule public final static CassandraKeyspace CASSANDRA_KEYSPACE = CassandraKeyspace.onLocalhost();
|
||||||
|
|
||||||
|
@Autowired AuditedPersonRepository repository;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
repository.deleteAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saving an object using the Cassandra Repository will create a persistent representation of the object in Cassandra.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void insertShouldSetCreatedDate() {
|
||||||
|
|
||||||
|
AuditedPerson person = new AuditedPerson();
|
||||||
|
person.setId(42L);
|
||||||
|
person.setNew(true); // Cassandra does not support auto-generation hence we need
|
||||||
|
// to supply whether our object is a new one.
|
||||||
|
AuditedPerson saved = repository.save(person);
|
||||||
|
|
||||||
|
assertThat(saved.getCreatedBy()).isEqualTo("Some user");
|
||||||
|
assertThat(saved.getLastModifiedBy()).isEqualTo("Some user");
|
||||||
|
assertThat(saved.getCreatedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||||
|
Instant.now().plus(Duration.ofMinutes(1)));
|
||||||
|
assertThat(saved.getLastModifiedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||||
|
Instant.now().plus(Duration.ofMinutes(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifying an existing object will update the last modified fields.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void updateShouldSetLastModifiedDate() {
|
||||||
|
|
||||||
|
AuditedPerson person = new AuditedPerson();
|
||||||
|
person.setId(42L);
|
||||||
|
person.setNew(true); // Cassandra does not support auto-generation hence we need
|
||||||
|
// to supply whether our object is a new one.
|
||||||
|
repository.save(person);
|
||||||
|
|
||||||
|
person.setNew(false);
|
||||||
|
|
||||||
|
AuditedPerson modified = repository.save(person);
|
||||||
|
|
||||||
|
assertThat(modified.getCreatedBy()).isEqualTo("Some user");
|
||||||
|
assertThat(modified.getLastModifiedBy()).isEqualTo("Some user");
|
||||||
|
assertThat(modified.getCreatedDate()).isBetween(Instant.now().minus(Duration.ofMinutes(1)),
|
||||||
|
Instant.now().plus(Duration.ofMinutes(1)));
|
||||||
|
assertThat(modified.getLastModifiedDate())
|
||||||
|
.isBetween(Instant.now().minus(Duration.ofMinutes(1)), Instant.now().plus(Duration.ofMinutes(1)))
|
||||||
|
.isNotEqualTo(modified.getCreatedDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user