Skip to content

Commit

Permalink
Add deleteAllVersions
Browse files Browse the repository at this point in the history
- findAllVersions now returns an ordered (by id) list

Fixes #663
  • Loading branch information
paulcwarren committed Nov 4, 2021
1 parent afed319 commit 56a3a2a
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
import java.io.Serializable;
import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RestResource;

public interface LockingAndVersioningRepository<T, ID extends Serializable> {
Expand Down Expand Up @@ -123,6 +120,13 @@ public interface LockingAndVersioningRepository<T, ID extends Serializable> {
*/
void delete(T entity);

/**
* Deletes all versions of the given entity.
*
* @param <T> the entity
*/
void deleteAllVersions(T entity);

/**
* Returns whether the given entity is a private working copy, or not
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.apache.commons.logging.Log;
Expand Down Expand Up @@ -315,7 +316,7 @@ public <S extends T> List<S> findAllVersionsLatest(Class<S> entityClass) {
@Override
public <S extends T> List<S> findAllVersions(S entity) {

String sql = "select t from ${entityClass} t where t.${ancestorRootId} = " + getAncestralRootId(entity);
String sql = "select t from ${entityClass} t where t.${ancestorRootId} = " + getAncestralRootId(entity) + " order by t.${id} desc";

StringSubstitutor sub = new StringSubstitutor(getAttributeMap(entity.getClass()));
sql = sub.replace(sql);
Expand Down Expand Up @@ -382,6 +383,35 @@ public void delete(T entity) {
em.remove(em.contains(entity) ? entity : em.merge(entity));
}

@Override
@Transactional
public void deleteAllVersions(T entity) {

Authentication authentication = auth.getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
throw new SecurityException("no principal");
}

Object id = this.getId(entity);
if (id == null) return;

if (!isHead(entity)) {
throw new LockingAndVersioningException("not head");
}

if (lockingService.lockOwner(id) != null && !lockingService.isLockOwner(id, authentication)) {
throw new LockOwnerException("not lock owner");
}

String sql = "delete from ${entityClass} t where t.${ancestorRootId} = " + getAncestralRootId(entity);

StringSubstitutor sub = new StringSubstitutor(getAttributeMap(entity.getClass()));
sql = sub.replace(sql);

Query q = em.createQuery(sql);
q.executeUpdate();
}

protected <T> boolean isHead(T entity) {
boolean isHead = false;
if (BeanUtils.hasFieldWithAnnotation(entity, SuccessorId.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.number.OrderingComparison.greaterThan;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -403,10 +406,19 @@ public class JpaLockingAndVersioningRepositoryImplIT {
e2v2 = repo.unlock(e2v2);
});

It("should return the version series", () -> {
It("should return the ordered version series", () -> {
List<TestEntity> results = repo.findAllVersions(e1);
assertThat(results.size(), is(2));
assertThat(results, Matchers.hasItems(hasProperty("xid", is(e1.getXid())), hasProperty("xid", is(e1v11.getXid()))));

// is ordered
{
long lastId = 0;
for (int i=results.size() - 1; i >= 0; i--) {
assertThat(results.get(i).getXid(), is(greaterThan(lastId)));
lastId = results.get(i).getXid();
}
}
});
});

Expand Down Expand Up @@ -714,6 +726,125 @@ public class JpaLockingAndVersioningRepositoryImplIT {
});
});
});

Context("#deleteAllVersions", () -> {

BeforeEach(() -> {
setupSecurityContext("some-principal", true);

e1 = repo.save(e1);
e1 = repo.lock(e1);
e1v11 = repo.version(e1, new VersionInfo("1.1", "Minor"));
e1v11 = repo.unlock(e1v11);
});

Context("given no principal", () -> {

BeforeEach(() -> {
setupSecurityContext(null, false);
});

It("should throw a SecurityException", () -> {
try {
repo.deleteAllVersions(e1v11);
fail("expected security exception");
} catch (Exception e) {
assertThat(e, is(instanceOf(SecurityException.class)));
}
});
});

Context("given an unauthenticated principal", () -> {

BeforeEach(() -> {
setupSecurityContext("some-principal", false);
});

It("should throw a SecurityException", () -> {
try {
repo.deleteAllVersions(e1v11);
fail("expected security exception");
} catch (Exception e) {
assertThat(e, is(instanceOf(SecurityException.class)));
}
});
});

Context("given a principal", () -> {

BeforeEach(() -> {
setupSecurityContext("some-principal", true);
});

Context("given the entity is not in a version tree", () -> {

Context("given the principal is the lock owner", () -> {

It("should delete version series", () -> {
e1v11 = repo.lock(e1v11);

List<Long> ids = new ArrayList<>();
repo.findAllVersions(e1v11).forEach((doc) -> {
ids.add(doc.getXid());
});

repo.deleteAllVersions(e1v11);

ids.forEach((id) -> {
assertThat(repo.existsById(id), is(false));
});
});
});

Context("given the principal is not the lock owner", () -> {

BeforeEach(() -> {
e1v11 = repo.lock(e1v11);
setupSecurityContext("some-other-principal", true);
});

It("should fail to delete the entity", () -> {
try {
repo.deleteAllVersions(e1v11);
fail("expected lockownerexception");
} catch (Exception e) {
assertThat(e, is(instanceOf(LockOwnerException.class)));
assertThat(e.getMessage(), containsString("not lock owner"));
}
});
});

Context("given there is no lock", () -> {

It("should delete version series", () -> {
List<Long> ids = new ArrayList<>();
repo.findAllVersions(e1).forEach((doc) -> {
ids.add(doc.getXid());
});

repo.deleteAllVersions(e1v11);

ids.forEach((id) -> {
assertThat(repo.existsById(id), is(false));
});
});
});
});

Context("given the entity is not the head", () -> {

It("should fail", () -> {
try {
repo.deleteAllVersions(e1);
fail("expected lockingandversioningexception");
} catch (Exception e) {
assertThat(e, is(instanceOf(LockingAndVersioningException.class)));
assertThat(e.getMessage(), containsString("not head"));
}
});
});
});
});
});
});
}
Expand Down

0 comments on commit 56a3a2a

Please sign in to comment.