Closes #14: Soft delete on DocumentTag + audit log ordering

This commit is contained in:
jendib 2015-08-28 01:02:33 +02:00
parent 86cae53789
commit 08e4f6ddae
5 changed files with 119 additions and 60 deletions

View File

@ -55,31 +55,50 @@ public class TagDao {
/** /**
* Update tags on a document. * Update tags on a document.
* *
* @param documentId * @param documentId Document ID
* @param tagIdSet * @param tagIdSet Set of tag ID
*/ */
public void updateTagList(String documentId, Set<String> tagIdSet) { public void updateTagList(String documentId, Set<String> tagIdSet) {
// Delete old tag links
EntityManager em = ThreadLocalContext.get().getEntityManager(); EntityManager em = ThreadLocalContext.get().getEntityManager();
Query q = em.createQuery("delete DocumentTag dt where dt.documentId = :documentId");
q.setParameter("documentId", documentId);
q.executeUpdate();
// Create new tag links // Get current tag links
for (String tagId : tagIdSet) { Query q = em.createQuery("select dt from DocumentTag dt where dt.documentId = :documentId and dt.deleteDate is null");
DocumentTag documentTag = new DocumentTag(); q.setParameter("documentId", documentId);
documentTag.setId(UUID.randomUUID().toString()); @SuppressWarnings("unchecked")
documentTag.setDocumentId(documentId); List<DocumentTag> documentTagList = q.getResultList();
documentTag.setTagId(tagId);
em.persist(documentTag); // Deleting tags no longer linked
for (DocumentTag documentTag : documentTagList) {
if (!tagIdSet.contains(documentTag.getTagId())) {
documentTag.setDeleteDate(new Date());
}
} }
// Adding new tag links
for (String tagId : tagIdSet) {
boolean found = false;
for (DocumentTag documentTag : documentTagList) {
if (documentTag.getTagId().equals(tagId)) {
found = true;
break;
}
}
if (!found) {
DocumentTag documentTag = new DocumentTag();
documentTag.setId(UUID.randomUUID().toString());
documentTag.setDocumentId(documentId);
documentTag.setTagId(tagId);
em.persist(documentTag);
}
}
} }
/** /**
* Returns tag list on a document. * Returns tag list on a document.
* @param documentId *
* @return * @param documentId Document ID
* @return List of tags
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<TagDto> getByDocumentId(String documentId, String userId) { public List<TagDto> getByDocumentId(String documentId, String userId) {
@ -87,7 +106,7 @@ public class TagDao {
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C from T_DOCUMENT_TAG dt "); StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C from T_DOCUMENT_TAG dt ");
sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C "); sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C ");
sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null "); sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null ");
sb.append(" and t.TAG_IDUSER_C = :userId "); sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null ");
sb.append(" order by t.TAG_NAME_C "); sb.append(" order by t.TAG_NAME_C ");
// Perform the query // Perform the query
@ -111,15 +130,16 @@ public class TagDao {
/** /**
* Returns stats on tags. * Returns stats on tags.
* @param documentId *
* @return * @param documentId Document ID
* @return Stats by tag
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<TagStatDto> getStats(String userId) { public List<TagStatDto> getStats(String userId) {
EntityManager em = ThreadLocalContext.get().getEntityManager(); EntityManager em = ThreadLocalContext.get().getEntityManager();
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, count(d.DOC_ID_C) "); StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, count(d.DOC_ID_C) ");
sb.append(" from T_TAG t "); sb.append(" from T_TAG t ");
sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C "); sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C and dt.DOT_DELETEDATE_D is null ");
sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId "); sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId ");
sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null "); sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null ");
sb.append(" group by t.TAG_ID_C "); sb.append(" group by t.TAG_ID_C ");
@ -168,6 +188,7 @@ public class TagDao {
/** /**
* Returns a tag by name. * Returns a tag by name.
*
* @param userId User ID * @param userId User ID
* @param name Name * @param name Name
* @return Tag * @return Tag
@ -186,6 +207,7 @@ public class TagDao {
/** /**
* Returns a tag by ID. * Returns a tag by ID.
*
* @param userId User ID * @param userId User ID
* @param tagId Tag ID * @param tagId Tag ID
* @return Tag * @return Tag
@ -216,8 +238,7 @@ public class TagDao {
Tag tagDb = (Tag) q.getSingleResult(); Tag tagDb = (Tag) q.getSingleResult();
// Delete the tag // Delete the tag
Date dateNow = new Date(); tagDb.setDeleteDate(new Date());
tagDb.setDeleteDate(dateNow);
// Delete linked data // Delete linked data
q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId"); q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId");

View File

@ -6,7 +6,9 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table; import javax.persistence.Table;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date;
/** /**
* Link between a document and a tag. * Link between a document and a tag.
@ -40,6 +42,12 @@ public class DocumentTag implements Serializable {
@Column(name = "DOT_IDTAG_C", length = 36) @Column(name = "DOT_IDTAG_C", length = 36)
private String tagId; private String tagId;
/**
* Deletion date.
*/
@Column(name = "DOT_DELETEDATE_D")
private Date deleteDate;
/** /**
* Getter of id. * Getter of id.
* *
@ -94,42 +102,22 @@ public class DocumentTag implements Serializable {
this.tagId = tagId; this.tagId = tagId;
} }
@Override /**
public int hashCode() { * Getter of deleteDate.
final int prime = 31; *
int result = 1; * @return the deleteDate
result = prime * result + ((documentId == null) ? 0 : documentId.hashCode()); */
result = prime * result + ((tagId == null) ? 0 : tagId.hashCode()); public Date getDeleteDate() {
return result; return deleteDate;
} }
@Override /**
public boolean equals(Object obj) { * Setter of deleteDate.
if (this == obj) { *
return true; * @param deleteDate deleteDate
} */
if (obj == null) { public void setDeleteDate(Date deleteDate) {
return false; this.deleteDate = deleteDate;
}
if (getClass() != obj.getClass()) {
return false;
}
DocumentTag other = (DocumentTag) obj;
if (documentId == null) {
if (other.documentId != null) {
return false;
}
} else if (!documentId.equals(other.documentId)) {
return false;
}
if (tagId == null) {
if (other.tagId != null) {
return false;
}
} else if (!tagId.equals(other.tagId)) {
return false;
}
return true;
} }
@Override @Override

View File

@ -3,4 +3,5 @@ alter table T_AUTHENTICATION_TOKEN add column AUT_IP_C varchar(45);
alter table T_AUTHENTICATION_TOKEN add column AUT_UA_C varchar(1000); alter table T_AUTHENTICATION_TOKEN add column AUT_UA_C varchar(1000);
create cached table T_AUDIT_LOG ( LOG_ID_C varchar(36) not null, LOG_IDENTITY_C varchar(36) not null, LOG_CLASSENTITY_C varchar(50) not null, LOG_TYPE_C varchar(50) not null, LOG_MESSAGE_C varchar(1000), LOG_CREATEDATE_D datetime, primary key (LOG_ID_C) ); create cached table T_AUDIT_LOG ( LOG_ID_C varchar(36) not null, LOG_IDENTITY_C varchar(36) not null, LOG_CLASSENTITY_C varchar(50) not null, LOG_TYPE_C varchar(50) not null, LOG_MESSAGE_C varchar(1000), LOG_CREATEDATE_D datetime, primary key (LOG_ID_C) );
create index IDX_LOG_COMPOSITE on T_AUDIT_LOG (LOG_IDENTITY_C, LOG_CLASSENTITY_C); create index IDX_LOG_COMPOSITE on T_AUDIT_LOG (LOG_IDENTITY_C, LOG_CLASSENTITY_C);
alter table T_DOCUMENT_TAG add column DOT_DELETEDATE_D datetime;
update T_CONFIG set CFG_VALUE_C='10' where CFG_ID_C='DB_VERSION'; update T_CONFIG set CFG_VALUE_C='10' where CFG_ID_C='DB_VERSION';

View File

@ -47,7 +47,7 @@ public class AuditLogResource extends BaseResource {
// On a document or a user? // On a document or a user?
PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0); PaginatedList<AuditLogDto> paginatedList = PaginatedLists.create(20, 0);
SortCriteria sortCriteria = new SortCriteria(1, true); SortCriteria sortCriteria = new SortCriteria(1, false);
AuditLogCriteria criteria = new AuditLogCriteria(); AuditLogCriteria criteria = new AuditLogCriteria();
if (documentId == null) { if (documentId == null) {
// Search logs for a user // Search logs for a user

View File

@ -75,12 +75,61 @@ public class TestTagResource extends BaseJerseyTest {
documentResource = resource().path("/document"); documentResource = resource().path("/document");
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token)); documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
postParams = new MultivaluedMapImpl(); postParams = new MultivaluedMapImpl();
postParams.add("title", "My super document 1"); postParams.add("title", "My super document 2");
postParams.add("tags", tag4Id); postParams.add("tags", tag4Id);
postParams.add("language", "eng"); postParams.add("language", "eng");
response = documentResource.put(ClientResponse.class, postParams); response = documentResource.put(ClientResponse.class, postParams);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
json = response.getEntity(JSONObject.class); json = response.getEntity(JSONObject.class);
String document2Id = json.getString("id");
// Check tags on a document
documentResource = resource().path("/document/" + document2Id);
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
response = documentResource.get(ClientResponse.class);
json = response.getEntity(JSONObject.class);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
JSONArray tags = json.getJSONArray("tags");
Assert.assertEquals(1, tags.length());
Assert.assertEquals(tag4Id, tags.getJSONObject(0).getString("id"));
// Update tags on a document
documentResource = resource().path("/document/" + document2Id);
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
postParams = new MultivaluedMapImpl();
postParams.add("tags", tag3Id);
postParams.add("tags", tag4Id);
response = documentResource.post(ClientResponse.class, postParams);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
// Check tags on a document
documentResource = resource().path("/document/" + document2Id);
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
response = documentResource.get(ClientResponse.class);
json = response.getEntity(JSONObject.class);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
tags = json.getJSONArray("tags");
Assert.assertEquals(2, tags.length());
Assert.assertEquals(tag3Id, tags.getJSONObject(0).getString("id"));
Assert.assertEquals(tag4Id, tags.getJSONObject(1).getString("id"));
// Update tags on a document
documentResource = resource().path("/document/" + document2Id);
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
postParams = new MultivaluedMapImpl();
postParams.add("tags", tag4Id);
response = documentResource.post(ClientResponse.class, postParams);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
// Check tags on a document
documentResource = resource().path("/document/" + document2Id);
documentResource.addFilter(new CookieAuthenticationFilter(tag1Token));
response = documentResource.get(ClientResponse.class);
json = response.getEntity(JSONObject.class);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
tags = json.getJSONArray("tags");
Assert.assertEquals(1, tags.length());
Assert.assertEquals(tag4Id, tags.getJSONObject(0).getString("id"));
// Get tag stats // Get tag stats
tagResource = resource().path("/tag/stats"); tagResource = resource().path("/tag/stats");
@ -99,7 +148,7 @@ public class TestTagResource extends BaseJerseyTest {
response = tagResource.get(ClientResponse.class); response = tagResource.get(ClientResponse.class);
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
json = response.getEntity(JSONObject.class); json = response.getEntity(JSONObject.class);
JSONArray tags = json.getJSONArray("tags"); tags = json.getJSONArray("tags");
Assert.assertTrue(tags.length() > 0); Assert.assertTrue(tags.length() > 0);
Assert.assertEquals("Tag4", tags.getJSONObject(1).getString("name")); Assert.assertEquals("Tag4", tags.getJSONObject(1).getString("name"));
Assert.assertEquals("#00ff00", tags.getJSONObject(1).getString("color")); Assert.assertEquals("#00ff00", tags.getJSONObject(1).getString("color"));