From b3b72b8a5fe6d2ee4ab35515543c530031a38a12 Mon Sep 17 00:00:00 2001 From: Eric Benzacar Date: Tue, 30 Sep 2014 14:05:48 -0400 Subject: [PATCH 1/4] Initial commit for auditing package for Ektorp --- org.ektorp.audit/.gitignore | 1 + org.ektorp.audit/pom.xml | 87 +++++++++++++ .../java/org/ektorp/audit/AuditAspect.java | 63 +++++++++ .../java/org/ektorp/audit/AuditableBean.java | 71 ++++++++++ .../org/ektorp/audit/AuditableBeanImpl.java | 67 ++++++++++ .../org/ektorp/audit/AuditingHandler.java | 123 ++++++++++++++++++ .../java/org/ektorp/audit/AuditorAware.java | 18 +++ .../ektorp/audit/annotation/Auditable.java | 20 +++ .../annotation/AuditableAnnotationAspect.java | 24 ++++ .../ektorp/audit/annotation/package-info.java | 8 ++ .../java/org/ektorp/audit/package-info.java | 8 ++ 11 files changed, 490 insertions(+) create mode 100644 org.ektorp.audit/.gitignore create mode 100644 org.ektorp.audit/pom.xml create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/AuditAspect.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBean.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBeanImpl.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/AuditingHandler.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/AuditorAware.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/Auditable.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/AuditableAnnotationAspect.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/package-info.java create mode 100644 org.ektorp.audit/src/main/java/org/ektorp/audit/package-info.java diff --git a/org.ektorp.audit/.gitignore b/org.ektorp.audit/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/org.ektorp.audit/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/org.ektorp.audit/pom.xml b/org.ektorp.audit/pom.xml new file mode 100644 index 00000000..0e61299a --- /dev/null +++ b/org.ektorp.audit/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + org.ektorp + org.ektorp.audit + jar + Ektorp Audit + 1.4.2 + Provides Automatic Auditing support to Ektorp + + org.ektorp + org.ektorp.parent + 1.4.2 + + + + + org.ektorp + org.ektorp + ${project.version} + + + commons-logging + commons-logging + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + joda-time + joda-time + + + javax.validation + validation-api + 1.1.0.Final + + + junit + junit + test + + + org.mockito + mockito-all + test + + + org.slf4j + slf4j-log4j12 + test + + + log4j + log4j + test + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.7 + + + + org.springframework + spring-webmvc + + + org.ektorp + org.ektorp + + + ignore + true + + + + + + diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditAspect.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditAspect.java new file mode 100644 index 00000000..242eedc9 --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditAspect.java @@ -0,0 +1,63 @@ +package org.ektorp.audit; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +@Aspect +public class AuditAspect { + + // SLF4J logger + private static final Logger logger = LoggerFactory.getLogger(AuditAspect.class); + + /** + * The handler to be used during the auditing + */ + private AuditingHandler handler; + + /** + * Sets the auditing handler + */ + public void setAuditingHandler( AuditingHandler handler){ + this.handler = handler; + } + + // Pointcut to the create method + @Pointcut("(execution(* org.ektorp.CouchDbConnector+.create(.., Object)) && args(o) ) || ( execution(* org.ektorp.CouchDbConnector+.create(Object)) && args(o))") + public void create(AuditableBean o) {} + + // Pointcut to the update method + @Pointcut("execution(* org.ektorp.CouchDbConnector+.update(Object)) && args(o)") + public void update(AuditableBean o){} + + /** + * Sets modification and creation date and auditor on the target object on update events. + * + * @param target + */ + @Before("create(target)") + public void beforeCreate(AuditableBean target){ + // get handler + if (handler != null) { + handler.markCreated(target); + } + } + + /** + * Sets creation date and auditor on the target object on create events. + * + * @param target + */ + @Before("update(target)") + public void beforeUpdate(AuditableBean target){ + // get handler + if (handler != null) { + handler.markModified(target); + } + } + + +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBean.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBean.java new file mode 100644 index 00000000..6aac053f --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBean.java @@ -0,0 +1,71 @@ +package org.ektorp.audit; + +import org.joda.time.DateTime; + + +public interface AuditableBean { + + /** + * Returns the unique identifier of the user who caused the creation of this + * record. This column is not a foreign key to the User table. + * + * @return The unique identifier of the user who created this record. + */ + public Object getCreatedBy(); + + /** + * Sets the unique identifier of the user who caused the creation of this + * record. + * + * @param createdBy The unique identifier of the user who created this + * record. + */ + public void setCreatedBy(final Object createdBy); + + /** + * Returns the date and time of when this record was created. + * + * @return The date and time this record was created. + */ + public DateTime getCreatedDate(); + + /** + * Sets the date and time of when this record was created. + * + * @param createdOn The date and time this record was created. + */ + public void setCreatedDate(final DateTime creationDate); + + /** + * Returns the unique identifier of the user who most recently caused the + * modification of this record. This column is not a foreign key to the User + * table. + * + * @return The unique identifier of the user who last modified this record. + */ + public Object getLastModifiedBy(); + + /** + * Sets the unique identifier of the user who most recently caused the + * modification of this record. + * + * @param lastModifiedBy The unique identifier of the user who last modified + * this record. + */ + public void setLastModifiedBy(final Object lastModifiedBy); + + /** + * Returns the date and time of when this record was last modified. + * + * @return The date and time this record was last modified. + */ + public DateTime getLastModifiedDate(); + + /** + * Sets the date and time of when this record was last modified. + * + * @param lastModifiedOn The date and time this record was last modified. + */ + public void setLastModifiedDate(final DateTime lastModifiedOn); + +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBeanImpl.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBeanImpl.java new file mode 100644 index 00000000..e9a9ab9b --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditableBeanImpl.java @@ -0,0 +1,67 @@ +package org.ektorp.audit; + +import javax.validation.constraints.NotNull; + +import org.joda.time.DateTime; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Implementation of the AuditableBean interface + */ +public class AuditableBeanImpl implements AuditableBean{ + + @NotNull + @JsonProperty("createdDate") + private DateTime createdDate; + + @NotNull + @JsonProperty("lastModifiedDate") + private DateTime lastModifiedDate; + + @JsonProperty("createdBy") + private Object createdBy; + + @JsonProperty("lastModifiedBy") + private Object lastModifiedBy; + + @Override + public DateTime getCreatedDate(){ + return this.createdDate; + } + + @Override + public DateTime getLastModifiedDate(){ + return this.lastModifiedDate; + } + + @Override + public void setCreatedDate(final DateTime createdDate){ + this.createdDate = createdDate; + } + + @Override + public void setLastModifiedDate(final DateTime lastModifiedDate){ + this.lastModifiedDate = lastModifiedDate; + } + + @Override + public Object getLastModifiedBy(){ + return this.lastModifiedBy; + } + + @Override + public Object getCreatedBy(){ + return this.createdBy; + } + + @Override + public void setCreatedBy(final Object createdBy ){ + this.createdBy = createdBy; + } + + @Override + public void setLastModifiedBy(final Object modifiedBy){ + this.lastModifiedBy = modifiedBy; + } +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditingHandler.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditingHandler.java new file mode 100644 index 00000000..1137d918 --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditingHandler.java @@ -0,0 +1,123 @@ +package org.ektorp.audit; + +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Auditing handler to mark entity objects created and modified. Heavily influenced by Spring Data. + * + * @author Eric Benzacar + */ +public class AuditingHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AuditingHandler.class); + + private AuditorAware auditorAware; + private boolean modifyOnCreation = true; + + /** + * Setter to inject a {@code AuditorAware} component to retrieve the current auditor. + * + * @param auditorAware the auditorAware to set + */ + public void setAuditorAware(final AuditorAware auditorAware) { + + if (auditorAware == null) + throw new IllegalArgumentException("argument required. It must not be null" ); + this.auditorAware = auditorAware; + } + + + /** + * Set this to false if you want to treat entity creation as modification and thus set the current date as + * modification date, too. Defaults to {@code true}. + * + * @param modifyOnCreation if modification information shall be set on creation, too + */ + public void setModifyOnCreation(boolean modifyOnCreation) { + this.modifyOnCreation = modifyOnCreation; + } + + + /** + * Marks the given object as created. + * + * @param source + */ + public void markCreated(Object source) { + touch(source, true); + } + + /** + * Marks the given object as modified. + * + * @param source + */ + public void markModified(Object source) { + touch(source, false); + } + + private void touch(Object target, boolean isNew) { + + if( !( target instanceof AuditableBean )) + return; + + AuditableBean bean = (AuditableBean)target; + + Object auditor = touchAuditor(bean, isNew); + DateTime now = touchDate(bean, isNew); + + Object defaultedNow = now == null ? "not set" : now; + Object defaultedAuditor = auditor == null ? "unknown" : auditor; + + LOGGER.debug("Touched {} - Last modification at {} by {}", new Object[] { target, defaultedNow, defaultedAuditor }); + } + + /** + * Sets modifying and creating auditioner. Creating auditioner is only set on new auditables. + * + * @param auditable + * @return + */ + private Object touchAuditor(AuditableBean bean, boolean isNew) { + + if (null == auditorAware) { + return null; + } + + Object auditor = auditorAware.getCurrentAuditor(); + + if (isNew) { + bean.setCreatedBy(auditor); + if (!modifyOnCreation) { + return auditor; + } + } + + bean.setLastModifiedBy(auditor); + return auditor; + } + + /** + * Touches the auditable regarding modification and creation date. Creation date is only set on new auditables. + * + * @param wrapper + * @return + */ + private DateTime touchDate(AuditableBean bean, boolean isNew) { + + DateTime now = new DateTime(); + + if (isNew) { + bean.setCreatedDate(now); + if (!modifyOnCreation) { + return now; + } + } + + bean.setLastModifiedDate(now); + return now; + } + +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditorAware.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditorAware.java new file mode 100644 index 00000000..ee2982aa --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/AuditorAware.java @@ -0,0 +1,18 @@ +package org.ektorp.audit; + +/** + * Interface for components that are aware of the application's current auditor. This will be some kind of user id mostly. + * + * @param String the id of the auditing instance + * @author Eric Benzacar + */ +public interface AuditorAware { + + /** + * Returns the current auditor of the application. The AuditingHandler stores this auditor + * in the document's audit fields + * + * @return the current auditor + */ + T getCurrentAuditor(); +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/Auditable.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/Auditable.java new file mode 100644 index 00000000..4dae43aa --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/Auditable.java @@ -0,0 +1,20 @@ +package org.ektorp.audit.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Enables the Auditable interface on the JavaBean in question + * + * @author Eric Benzacar + * + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Auditable { + +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/AuditableAnnotationAspect.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/AuditableAnnotationAspect.java new file mode 100644 index 00000000..f5fb4254 --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/AuditableAnnotationAspect.java @@ -0,0 +1,24 @@ +package org.ektorp.audit.annotation; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.DeclareMixin; +import org.ektorp.audit.AuditableBean; +import org.ektorp.audit.AuditableBeanImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +@Aspect +public class AuditableAnnotationAspect { + + // SLF4J logger + private static final Logger logger = LoggerFactory.getLogger(AuditableAnnotationAspect.class); + + /* + * Assign the AuditableBean interface to any bean with the Auditable annotation + */ + @DeclareMixin("@org.ektorp.audit.annotation.Auditable *") + public AuditableBean auditableBeanMixin(){ + return new AuditableBeanImpl(); + } +} diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/package-info.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/package-info.java new file mode 100644 index 00000000..f29cff30 --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/annotation/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author Eric Benzacar + * + */ +package org.ektorp.audit.annotation; \ No newline at end of file diff --git a/org.ektorp.audit/src/main/java/org/ektorp/audit/package-info.java b/org.ektorp.audit/src/main/java/org/ektorp/audit/package-info.java new file mode 100644 index 00000000..59d8f9ee --- /dev/null +++ b/org.ektorp.audit/src/main/java/org/ektorp/audit/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author Eric Benzacar + * + */ +package org.ektorp.audit; \ No newline at end of file From 0f9dc44753a66f980f460a9b2bf93e3bc8610928 Mon Sep 17 00:00:00 2001 From: Eric Benzacar Date: Tue, 30 Sep 2014 14:07:27 -0400 Subject: [PATCH 2/4] Updated developer section --- org.ektorp.audit/pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/org.ektorp.audit/pom.xml b/org.ektorp.audit/pom.xml index 0e61299a..2d74c940 100644 --- a/org.ektorp.audit/pom.xml +++ b/org.ektorp.audit/pom.xml @@ -12,6 +12,19 @@ org.ektorp.parent 1.4.2 + + + eric.benzacar + Eric Benzacar + ebenzacar@gmail.com + https://github.com/benze/Ektorp.git + + architect + developer + + -5 + + From 110ae97319b5910709852f8497102eadad2b7242 Mon Sep 17 00:00:00 2001 From: Eric Benzacar Date: Tue, 30 Sep 2014 14:11:19 -0400 Subject: [PATCH 3/4] Updated scope for joda-time. --- org.ektorp.audit/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.ektorp.audit/pom.xml b/org.ektorp.audit/pom.xml index 2d74c940..ff5e551b 100644 --- a/org.ektorp.audit/pom.xml +++ b/org.ektorp.audit/pom.xml @@ -46,6 +46,8 @@ joda-time joda-time + + compile javax.validation From 9b58f5192b6dd69875c7e9e3acc2b172912b427f Mon Sep 17 00:00:00 2001 From: Eric Benzacar Date: Wed, 1 Oct 2014 11:11:57 -0400 Subject: [PATCH 4/4] Added unit test for the auditing library --- org.ektorp.audit/pom.xml | 33 +++- .../test/java/org/ektorp/audit/AuditTest.java | 171 ++++++++++++++++++ .../java/org/ektorp/domain/TestDocument.java | 30 +++ .../org/ektorp/domain/TestRepository.java | 23 +++ .../org/ektorp/impl/HttpResponseStub.java | 66 +++++++ 5 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 org.ektorp.audit/src/test/java/org/ektorp/audit/AuditTest.java create mode 100644 org.ektorp.audit/src/test/java/org/ektorp/domain/TestDocument.java create mode 100644 org.ektorp.audit/src/test/java/org/ektorp/domain/TestRepository.java create mode 100644 org.ektorp.audit/src/test/java/org/ektorp/impl/HttpResponseStub.java diff --git a/org.ektorp.audit/pom.xml b/org.ektorp.audit/pom.xml index ff5e551b..3fc8fdb1 100644 --- a/org.ektorp.audit/pom.xml +++ b/org.ektorp.audit/pom.xml @@ -46,7 +46,11 @@ joda-time joda-time - + compile + + + joda-time + joda-time compile @@ -74,6 +78,25 @@ log4j test + + com.fasterxml.jackson.datatype + jackson-datatype-joda + test + + + org.apache.commons + commons-lang3 + 3.1 + test + + + + org.hamcrest + hamcrest-all + 1.3 + test + + @@ -81,6 +104,14 @@ org.codehaus.mojo aspectj-maven-plugin 1.7 + + + + compile + test-compile + + + diff --git a/org.ektorp.audit/src/test/java/org/ektorp/audit/AuditTest.java b/org.ektorp.audit/src/test/java/org/ektorp/audit/AuditTest.java new file mode 100644 index 00000000..94f456a3 --- /dev/null +++ b/org.ektorp.audit/src/test/java/org/ektorp/audit/AuditTest.java @@ -0,0 +1,171 @@ +package org.ektorp.audit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.Aspects; +import org.ektorp.CouchDbConnector; +import org.ektorp.CouchDbInstance; +import org.ektorp.domain.TestDocument; +import org.ektorp.domain.TestRepository; +import org.ektorp.http.HttpClient; +import org.ektorp.http.HttpResponse; +import org.ektorp.impl.HttpResponseStub; +import org.ektorp.impl.StdCouchDbInstance; +import org.ektorp.impl.StdObjectMapperFactory; +import org.ektorp.util.Assert; +import org.ektorp.util.Documents; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.joda.JodaModule; + +@RunWith(MockitoJUnitRunner.class) +public class AuditTest { + + @Mock + private HttpClient client; + + private TestRepository repo; + private ObjectMapper mapper; + + // default auditor name + private String auditorName = ""; + + protected void setAuditorName(String name ){ + this.auditorName = name; + } + /** + * Configures the audit handler in the AuditAspect + */ + private void configureAuditHandler(){ + AuditAspect aspect = Aspects.aspectOf(AuditAspect.class); + + // specify the handler + AuditingHandler handler = new AuditingHandler(); + aspect.setAuditingHandler(handler); + + // specify the auditor for the handler + AuditorAware auditor = new AuditorAware() { + public String getCurrentAuditor() { + return auditorName; + } + }; + handler.setAuditorAware(auditor); + } + + @Before + public void setup() throws MalformedURLException{ + // initialize the auditor aspect + configureAuditHandler(); + + // setup the mock client + when(client.head("/testdb/")).thenReturn(HttpResponseStub.valueOf(201, "{\"ok\": true}")); + when(client.put(anyString(), anyString())).thenAnswer(new Answer() { + @Override + public HttpResponse answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + String json = (String)args[1]; + Object o = mapper.readValue(json, TestDocument.class); + + // Get the doc id + String id = Documents.getId(o); + Assert.hasText(id, "document id cannot be empty"); + + // return a created string + return HttpResponseStub.valueOf( 201, "{\"ok\": true, \"id\": \"" + id + "\", \"rev\": \""+ id + "\"}"); + } + }); + + when(client.post(anyString(), anyString())).thenAnswer(new Answer() { + @Override + public HttpResponse answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + String json = (String)args[1]; + Object o = mapper.readValue(json, TestDocument.class); + // generate a random id and rev if object doesn't already contain one + String id = Documents.getId(o); + if( StringUtils.isEmpty(id) ) + id = RandomStringUtils.randomAlphanumeric(5); + + // return a success string + return HttpResponseStub.valueOf( 200, "{\"ok\": true, \"id\": \"" + id + "\", \"rev\": \""+ id + "\"}"); + } + }); + + // configure the object mapper to register the Joda converters automatically + StdObjectMapperFactory omf = new StdObjectMapperFactory(){ + @Override + public synchronized ObjectMapper createObjectMapper() { + ObjectMapper om = super.createObjectMapper(); + om.registerModule(new JodaModule()); + return om; + } + + @Override + public ObjectMapper createObjectMapper(CouchDbConnector connector) { + ObjectMapper om = super.createObjectMapper(); + om.registerModule(new JodaModule()); + return om; + } + }; + + + // init the repo + CouchDbInstance instance = new StdCouchDbInstance(client, omf); + CouchDbConnector connector = instance.createConnector("testdb/", true); + mapper = omf.createObjectMapper(); + + repo = new TestRepository(TestDocument.class, connector); + } + + + + + @Test + public void testAllAuditFields() { + DateTime now = new DateTime(); + String auditorName = RandomStringUtils.randomAlphanumeric(10); + setAuditorName( auditorName ); + + TestDocument doc = new TestDocument(); + doc.setValue("Some Test Data"); + repo.add(doc); + + AuditableBean auditableDoc = (AuditableBean)doc; + + DateTime createdDate = auditableDoc.getCreatedDate(); + assertThat(auditableDoc.getCreatedDate(), greaterThanOrEqualTo(now)); + assertThat(auditableDoc.getLastModifiedDate(), greaterThanOrEqualTo(now)); + assertThat((String)auditableDoc.getCreatedBy(), equalTo(auditorName)); + assertThat((String)auditableDoc.getLastModifiedBy(), equalTo(auditorName)); + + // update the document as a different user + String updateAuditor = RandomStringUtils.randomAlphanumeric(10); + setAuditorName( updateAuditor); + doc.setValue("New Data"); + repo.update(doc); + + assertThat(auditableDoc.getCreatedDate(), equalTo(createdDate)); + assertThat(auditableDoc.getLastModifiedDate(), greaterThan(createdDate)); + assertThat((String)auditableDoc.getCreatedBy(), equalTo(auditorName)); + assertThat((String)auditableDoc.getLastModifiedBy(), equalTo(updateAuditor)); + + } + +} diff --git a/org.ektorp.audit/src/test/java/org/ektorp/domain/TestDocument.java b/org.ektorp.audit/src/test/java/org/ektorp/domain/TestDocument.java new file mode 100644 index 00000000..4ba15148 --- /dev/null +++ b/org.ektorp.audit/src/test/java/org/ektorp/domain/TestDocument.java @@ -0,0 +1,30 @@ +package org.ektorp.domain; + +import org.ektorp.audit.annotation.Auditable; +import org.ektorp.support.CouchDbDocument; + +@Auditable +public class TestDocument extends CouchDbDocument { + + public TestDocument() { + // TODO Auto-generated constructor stub + } + + private String value; + + /** + * @return the value + */ + public String getValue() { + return value; + } + + /** + * @param value the value to set + */ + public void setValue(String value) { + this.value = value; + } + + +} diff --git a/org.ektorp.audit/src/test/java/org/ektorp/domain/TestRepository.java b/org.ektorp.audit/src/test/java/org/ektorp/domain/TestRepository.java new file mode 100644 index 00000000..70dd997c --- /dev/null +++ b/org.ektorp.audit/src/test/java/org/ektorp/domain/TestRepository.java @@ -0,0 +1,23 @@ +package org.ektorp.domain; + +import org.ektorp.CouchDbConnector; +import org.ektorp.support.CouchDbRepositorySupport; + +public class TestRepository extends CouchDbRepositorySupport { + + public TestRepository(Class type, CouchDbConnector db) { + super(type, db); + // TODO Auto-generated constructor stub + } + + public TestRepository(Class type, CouchDbConnector db, boolean createIfNotExists) { + super(type, db, createIfNotExists); + // TODO Auto-generated constructor stub + } + + public TestRepository(Class type, CouchDbConnector db, String designDocName) { + super(type, db, designDocName); + // TODO Auto-generated constructor stub + } + +} diff --git a/org.ektorp.audit/src/test/java/org/ektorp/impl/HttpResponseStub.java b/org.ektorp.audit/src/test/java/org/ektorp/impl/HttpResponseStub.java new file mode 100644 index 00000000..8826588a --- /dev/null +++ b/org.ektorp.audit/src/test/java/org/ektorp/impl/HttpResponseStub.java @@ -0,0 +1,66 @@ +package org.ektorp.impl; + +import java.io.*; +import java.nio.charset.Charset; + +import org.apache.commons.io.input.ReaderInputStream; +import org.ektorp.http.*; + +public class HttpResponseStub implements HttpResponse { + + public static HttpResponse valueOf(int code, String body) { + return new HttpResponseStub(code, body); + } + + private final int code; + + private final String body; + + private final Charset charset = Charset.forName("UTF-8"); + + HttpResponseStub(int code, String body) { + this.code = code; + this.body = body; + } + + public int getCode() { + return code; + } + + public InputStream getContent() { + return new ReaderInputStream(new StringReader(body), charset); + } + + public String getETag() { + // TODO Auto-generated method stub + return null; + } + + public boolean isSuccessful() { + return code < 300; + } + + public String getContentType() { + // TODO Auto-generated method stub + return null; + } + + public void releaseConnection() { + // TODO Auto-generated method stub + + } + + public long getContentLength() { + // TODO Auto-generated method stub + return 0; + } + + public void abort() { + // TODO Auto-generated method stub + } + + public String getRequestURI() { + return "static/test/path"; + } + +}