From 08e4f6ddae477faea9338acd886a7989b9fee013 Mon Sep 17 00:00:00 2001 From: jendib Date: Fri, 28 Aug 2015 01:02:33 +0200 Subject: [PATCH] Closes #14: Soft delete on DocumentTag + audit log ordering --- .../com/sismics/docs/core/dao/jpa/TagDao.java | 65 ++++++++++++------- .../docs/core/model/jpa/DocumentTag.java | 58 +++++++---------- .../resources/db/update/dbupdate-010-0.sql | 1 + .../docs/rest/resource/AuditLogResource.java | 2 +- .../sismics/docs/rest/TestTagResource.java | 53 ++++++++++++++- 5 files changed, 119 insertions(+), 60 deletions(-) diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java index 8345de17..da108dd9 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java @@ -55,31 +55,50 @@ public class TagDao { /** * Update tags on a document. * - * @param documentId - * @param tagIdSet + * @param documentId Document ID + * @param tagIdSet Set of tag ID */ public void updateTagList(String documentId, Set tagIdSet) { - // Delete old tag links 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 - for (String tagId : tagIdSet) { - DocumentTag documentTag = new DocumentTag(); - documentTag.setId(UUID.randomUUID().toString()); - documentTag.setDocumentId(documentId); - documentTag.setTagId(tagId); - em.persist(documentTag); + // Get current tag links + Query q = em.createQuery("select dt from DocumentTag dt where dt.documentId = :documentId and dt.deleteDate is null"); + q.setParameter("documentId", documentId); + @SuppressWarnings("unchecked") + List documentTagList = q.getResultList(); + + // 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. - * @param documentId - * @return + * + * @param documentId Document ID + * @return List of tags */ @SuppressWarnings("unchecked") public List 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 "); 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(" 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 "); // Perform the query @@ -111,15 +130,16 @@ public class TagDao { /** * Returns stats on tags. - * @param documentId - * @return + * + * @param documentId Document ID + * @return Stats by tag */ @SuppressWarnings("unchecked") public List getStats(String userId) { 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) "); 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(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null "); sb.append(" group by t.TAG_ID_C "); @@ -168,6 +188,7 @@ public class TagDao { /** * Returns a tag by name. + * * @param userId User ID * @param name Name * @return Tag @@ -186,6 +207,7 @@ public class TagDao { /** * Returns a tag by ID. + * * @param userId User ID * @param tagId Tag ID * @return Tag @@ -216,8 +238,7 @@ public class TagDao { Tag tagDb = (Tag) q.getSingleResult(); // Delete the tag - Date dateNow = new Date(); - tagDb.setDeleteDate(dateNow); + tagDb.setDeleteDate(new Date()); // Delete linked data q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId"); diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java index 53d0b770..e38963bb 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java @@ -6,7 +6,9 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; + import java.io.Serializable; +import java.util.Date; /** * Link between a document and a tag. @@ -40,6 +42,12 @@ public class DocumentTag implements Serializable { @Column(name = "DOT_IDTAG_C", length = 36) private String tagId; + /** + * Deletion date. + */ + @Column(name = "DOT_DELETEDATE_D") + private Date deleteDate; + /** * Getter of id. * @@ -94,44 +102,24 @@ public class DocumentTag implements Serializable { this.tagId = tagId; } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((documentId == null) ? 0 : documentId.hashCode()); - result = prime * result + ((tagId == null) ? 0 : tagId.hashCode()); - return result; + /** + * Getter of deleteDate. + * + * @return the deleteDate + */ + public Date getDeleteDate() { + return deleteDate; } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - 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; + /** + * Setter of deleteDate. + * + * @param deleteDate deleteDate + */ + public void setDeleteDate(Date deleteDate) { + this.deleteDate = deleteDate; } - + @Override public String toString() { return Objects.toStringHelper(this) diff --git a/docs-core/src/main/resources/db/update/dbupdate-010-0.sql b/docs-core/src/main/resources/db/update/dbupdate-010-0.sql index a3ecc9c4..3f0127d2 100644 --- a/docs-core/src/main/resources/db/update/dbupdate-010-0.sql +++ b/docs-core/src/main/resources/db/update/dbupdate-010-0.sql @@ -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); 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); +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'; \ No newline at end of file diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java index b87c3b7e..2081eb2a 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java @@ -47,7 +47,7 @@ public class AuditLogResource extends BaseResource { // On a document or a user? PaginatedList paginatedList = PaginatedLists.create(20, 0); - SortCriteria sortCriteria = new SortCriteria(1, true); + SortCriteria sortCriteria = new SortCriteria(1, false); AuditLogCriteria criteria = new AuditLogCriteria(); if (documentId == null) { // Search logs for a user diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java index cd8d1ecf..bacedd6e 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java @@ -75,12 +75,61 @@ public class TestTagResource extends BaseJerseyTest { documentResource = resource().path("/document"); documentResource.addFilter(new CookieAuthenticationFilter(tag1Token)); postParams = new MultivaluedMapImpl(); - postParams.add("title", "My super document 1"); + postParams.add("title", "My super document 2"); postParams.add("tags", tag4Id); postParams.add("language", "eng"); response = documentResource.put(ClientResponse.class, postParams); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); 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 tagResource = resource().path("/tag/stats"); @@ -99,7 +148,7 @@ public class TestTagResource extends BaseJerseyTest { response = tagResource.get(ClientResponse.class); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); json = response.getEntity(JSONObject.class); - JSONArray tags = json.getJSONArray("tags"); + tags = json.getJSONArray("tags"); Assert.assertTrue(tags.length() > 0); Assert.assertEquals("Tag4", tags.getJSONObject(1).getString("name")); Assert.assertEquals("#00ff00", tags.getJSONObject(1).getString("color"));