Skip to content
Snippets Groups Projects
Commit 5af8d859 authored by Daniel Gerhardt's avatar Daniel Gerhardt
Browse files

Use event system to handle cascading deletes

Child entities are now deleted by an event handler of their service
class.
parent e998a218
Branches
No related merge requests found
Showing
with 128 additions and 210 deletions
......@@ -97,7 +97,7 @@ public class RoomController extends PaginationController {
@RequestMapping(value = "/{shortId}", method = RequestMethod.DELETE)
public void deleteRoom(@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId) {
de.thm.arsnova.model.Room room = roomService.getByShortId(shortId);
roomService.deleteCascading(room);
roomService.delete(room);
}
@ApiOperation(value = "count active users",
......
......@@ -29,10 +29,9 @@ public interface AnswerRepository extends CrudRepository<Answer, String> {
int countByContentId(String contentId);
<T extends Answer> List<T> findByContentId(String contentId, Class<T> type, int start, int limit);
List<Answer> findByUserIdRoomId(String userId, String roomId);
Iterable<Answer> findStubsByContentId(String contentId);
Iterable<Answer> findStubsByContentIds(List<String> contentId);
int countByRoomId(String roomId);
int deleteByContentId(String contentId);
int countByRoomIdOnlyLectureVariant(String roomId);
int countByRoomIdOnlyPreparationVariant(String roomId);
int deleteAllAnswersForQuestions(List<String> contentIds);
int deleteByContentIds(List<String> contentIds);
}
......@@ -11,7 +11,7 @@ public interface CommentRepository extends CrudRepository<Comment, String> {
CommentReadingCount countReadingByRoomIdAndUserId(String roomId, String userId);
List<Comment> findByRoomId(String roomId, int start, int limit);
List<Comment> findByRoomIdAndUserId(String roomId, String userId, int start, int limit);
Iterable<Comment> findStubsByRoomId(String roomId);
Iterable<Comment> findStubsByRoomIdAndUserId(String roomId, String userId);
Comment findOne(String commentId);
int deleteByRoomId(String roomId);
int deleteByRoomIdAndUserId(String roomId, String userId);
}
......@@ -10,8 +10,8 @@ public interface ContentRepository extends CrudRepository<Content, String> {
List<Content> findByRoomIdForSpeaker(String roomId);
int countByRoomId(String roomId);
List<String> findIdsByRoomId(String roomId);
List<String> findIdsByRoomIdAndVariant(String roomId, String variant);
int deleteByRoomId(String roomId);
Iterable<Content> findStubsByRoomId(final String roomId);
Iterable<Content> findStubsByRoomIdAndVariant(String roomId, String variant);
List<String> findUnansweredIdsByRoomIdAndUser(String roomId, String userId);
List<Content> findByRoomIdOnlyLectureVariantAndActive(String roomId);
List<Content> findByRoomIdOnlyLectureVariant(String roomId);
......
package de.thm.arsnova.persistence.couchdb;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import de.thm.arsnova.model.Answer;
import de.thm.arsnova.model.AnswerStatistics;
import de.thm.arsnova.persistence.AnswerRepository;
import de.thm.arsnova.persistence.LogEntryRepository;
import org.ektorp.BulkDeleteDocument;
import org.ektorp.ComplexKey;
import org.ektorp.CouchDbConnector;
import org.ektorp.DbAccessException;
import org.ektorp.DocumentOperationResult;
import org.ektorp.ViewResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -26,7 +22,6 @@ import java.util.List;
import java.util.Map;
public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer> implements AnswerRepository, ApplicationEventPublisherAware {
private static final int BULK_PARTITION_SIZE = 500;
private static final Logger logger = LoggerFactory.getLogger(CouchDbAnswerRepository.class);
@Autowired
......@@ -43,34 +38,18 @@ public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer> imple
this.publisher = publisher;
}
@Override
public int deleteByContentId(final String contentId) {
try {
final ViewResult result = db.queryView(createQuery("by_contentid")
.key(contentId));
final List<List<ViewResult.Row>> partitions = Lists.partition(result.getRows(), BULK_PARTITION_SIZE);
int count = 0;
for (final List<ViewResult.Row> partition: partitions) {
final List<BulkDeleteDocument> answersToDelete = new ArrayList<>();
for (final ViewResult.Row a : partition) {
final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
answersToDelete.add(d);
}
final List<DocumentOperationResult> errors = db.executeBulk(answersToDelete);
count += partition.size() - errors.size();
if (errors.size() > 0) {
logger.error("Could not bulk delete {} of {} answers.", errors.size(), partition.size());
}
}
dbLogger.log("delete", "type", "answer", "answerCount", count);
protected Iterable<Answer> createEntityStubs(final ViewResult viewResult) {
return super.createEntityStubs(viewResult, Answer::setContentId);
}
return count;
} catch (final DbAccessException e) {
logger.error("Could not delete answers for content {}.", contentId, e);
}
@Override
public Iterable<Answer> findStubsByContentId(final String contentId) {
return createEntityStubs(db.queryView(createQuery("by_contentid").key(contentId)));
}
return 0;
@Override
public Iterable<Answer> findStubsByContentIds(final List<String> contentIds) {
return createEntityStubs(db.queryView(createQuery("by_contentid").keys(contentIds)));
}
@Override
......@@ -192,45 +171,4 @@ public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer> imple
return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
}
@Override
public int deleteAllAnswersForQuestions(final List<String> contentIds) {
final ViewResult result = db.queryView(createQuery("by_contentid")
.keys(contentIds));
final List<BulkDeleteDocument> allAnswers = new ArrayList<>();
for (final ViewResult.Row a : result.getRows()) {
final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
allAnswers.add(d);
}
try {
final List<DocumentOperationResult> errors = db.executeBulk(allAnswers);
return allAnswers.size() - errors.size();
} catch (final DbAccessException e) {
logger.error("Could not bulk delete answers.", e);
}
return 0;
}
@Override
public int deleteByContentIds(final List<String> contentIds) {
final ViewResult result = db.queryView(createQuery("by_contentid")
.keys(contentIds));
final List<BulkDeleteDocument> deleteDocs = new ArrayList<>();
for (final ViewResult.Row a : result.getRows()) {
final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
deleteDocs.add(d);
}
try {
final List<DocumentOperationResult> errors = db.executeBulk(deleteDocs);
return deleteDocs.size() - errors.size();
} catch (final DbAccessException e) {
logger.error("Could not bulk delete answers.", e);
}
return 0;
}
}
......@@ -7,7 +7,6 @@ import de.thm.arsnova.persistence.CommentRepository;
import de.thm.arsnova.persistence.LogEntryRepository;
import org.ektorp.ComplexKey;
import org.ektorp.CouchDbConnector;
import org.ektorp.UpdateConflictException;
import org.ektorp.ViewResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -142,37 +141,18 @@ public class CouchDbCommentRepository extends CouchDbCrudRepository<Comment> imp
}
@Override
public int deleteByRoomId(final String roomId) {
final ViewResult result = db.queryView(createQuery("by_roomid").key(roomId));
return delete(result);
public Iterable<Comment> findStubsByRoomId(final String roomId) {
return createEntityStubs(db.queryView(createQuery("by_roomid").key(roomId).reduce(false)));
}
@Override
public int deleteByRoomIdAndUserId(final String roomId, final String userId) {
final ViewResult result = db.queryView(createQuery("by_roomid_creatorid_read")
public Iterable<Comment> findStubsByRoomIdAndUserId(final String roomId, final String userId) {
return createEntityStubs(db.queryView(createQuery("by_roomid_creatorid_read")
.startKey(ComplexKey.of(roomId, userId))
.endKey(ComplexKey.of(roomId, userId, ComplexKey.emptyObject())));
return delete(result);
.endKey(ComplexKey.of(roomId, userId, ComplexKey.emptyObject()))));
}
private int delete(final ViewResult comments) {
if (comments.isEmpty()) {
return 0;
}
/* TODO: use bulk delete */
for (final ViewResult.Row row : comments.getRows()) {
try {
db.delete(row.getId(), row.getValueAsNode().get("rev").asText());
} catch (final UpdateConflictException e) {
logger.error("Could not delete comments.", e);
}
}
/* This does account for failed deletions */
dbLogger.log("delete", "type", "comment", "commentCount", comments.getSize());
return comments.getSize();
protected Iterable<Comment> createEntityStubs(final ViewResult viewResult) {
return super.createEntityStubs(viewResult, Comment::setRoomId);
}
}
......@@ -3,10 +3,8 @@ package de.thm.arsnova.persistence.couchdb;
import de.thm.arsnova.model.Content;
import de.thm.arsnova.persistence.ContentRepository;
import de.thm.arsnova.persistence.LogEntryRepository;
import org.ektorp.BulkDeleteDocument;
import org.ektorp.ComplexKey;
import org.ektorp.CouchDbConnector;
import org.ektorp.DocumentOperationResult;
import org.ektorp.ViewResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -67,28 +65,23 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
}
@Override
public List<String> findIdsByRoomIdAndVariant(final String roomId, final String variant) {
return collectQuestionIds(db.queryView(createQuery("by_roomid_group_locked")
.startKey(ComplexKey.of(roomId, variant))
.endKey(ComplexKey.of(roomId, variant, ComplexKey.emptyObject()))
public Iterable<Content> findStubsByRoomId(final String roomId) {
return createEntityStubs(db.queryView(createQuery("by_roomid_group_locked")
.startKey(ComplexKey.of(roomId))
.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
.reduce(false)));
}
@Override
public int deleteByRoomId(final String roomId) {
final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
.startKey(ComplexKey.of(roomId))
.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
.reduce(false));
final List<BulkDeleteDocument> deleteDocs = new ArrayList<>();
for (final ViewResult.Row a : result.getRows()) {
final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
deleteDocs.add(d);
}
List<DocumentOperationResult> errors = db.executeBulk(deleteDocs);
public Iterable<Content> findStubsByRoomIdAndVariant(final String roomId, final String variant) {
return createEntityStubs(db.queryView(createQuery("by_roomid_group_locked")
.startKey(ComplexKey.of(roomId, variant))
.endKey(ComplexKey.of(roomId, variant, ComplexKey.emptyObject()))
.reduce(false)));
}
return deleteDocs.size() - errors.size();
protected Iterable<Content> createEntityStubs(final ViewResult viewResult) {
return super.createEntityStubs(viewResult, Content::setRoomId);
}
@Override
......
......@@ -4,6 +4,7 @@ import de.thm.arsnova.model.Entity;
import de.thm.arsnova.persistence.CrudRepository;
import org.ektorp.BulkDeleteDocument;
import org.ektorp.CouchDbConnector;
import org.ektorp.ViewResult;
import org.ektorp.support.CouchDbRepositorySupport;
import org.springframework.data.repository.NoRepositoryBean;
......@@ -11,6 +12,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@NoRepositoryBean
......@@ -129,4 +131,30 @@ abstract class CouchDbCrudRepository<T extends Entity> extends CouchDbRepository
public void deleteAll() {
throw new UnsupportedOperationException("Deletion of all entities is not supported for security reasons.");
}
/**
* Creates stub entities from a ViewResult. Stub entities only have meta data (id, revision, reference id) set.
*
* @param viewResult A CouchDB ViewResult. The first part of its keys is expected to be the id of another entity.
* @param keyPropertySetter A setter method of the Entity class which is called to store the first element of the
* key.
* @return Entity stubs
*/
protected Iterable<T> createEntityStubs(final ViewResult viewResult, final BiConsumer<T, String> keyPropertySetter) {
return viewResult.getRows().stream().map(row -> {
final T stub;
try {
stub = type.newInstance();
stub.setId(row.getId());
stub.setRevision(row.getValueAsNode().get("_rev").asText());
final String key = row.getKeyAsNode().isContainerNode()
? row.getKeyAsNode().get(0).asText() : row.getKey();
keyPropertySetter.accept(stub, key);
return stub;
} catch (InstantiationException | IllegalAccessException e) {
return null;
}
}).collect(Collectors.toList());
}
}
......@@ -19,6 +19,7 @@ package de.thm.arsnova.service;
import de.thm.arsnova.event.AfterCreationEvent;
import de.thm.arsnova.event.BeforeCreationEvent;
import de.thm.arsnova.event.BeforeDeletionEvent;
import de.thm.arsnova.model.Answer;
import de.thm.arsnova.model.AnswerStatistics;
import de.thm.arsnova.model.ChoiceQuestionContent;
......@@ -33,10 +34,13 @@ import de.thm.arsnova.web.exceptions.UnauthorizedException;
import org.ektorp.DbAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
......@@ -64,17 +68,19 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
public AnswerServiceImpl(
AnswerRepository repository,
RoomService roomService,
ContentService contentService,
UserService userService,
@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
super(Answer.class, repository, jackson2HttpMessageConverter.getObjectMapper());
this.answerRepository = repository;
this.roomService = roomService;
this.contentService = contentService;
this.contentService = contentService;
this.userService = userService;
}
@Autowired
public void setContentService(final ContentService contentService) {
this.contentService = contentService;
}
@Scheduled(fixedDelay = 5000)
public void flushAnswerQueue() {
if (answerQueue.isEmpty()) {
......@@ -110,7 +116,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
content.resetState();
/* FIXME: cancel timer */
contentService.update(content);
answerRepository.deleteByContentId(content.getId());
delete(answerRepository.findStubsByContentId(content.getId()));
}
@Override
......@@ -421,4 +427,11 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
public int countPreparationQuestionAnswersInternal(final String roomId) {
return answerRepository.countByRoomIdOnlyPreparationVariant(roomService.get(roomId).getId());
}
@EventListener
@Secured({"ROLE_USER", "RUN_AS_SYSTEM"})
public void handleContentDeletion(final BeforeDeletionEvent<Content> event) {
final Iterable<Answer> answers = answerRepository.findStubsByContentId(event.getEntity().getId());
delete(answers);
}
}
package de.thm.arsnova.service;
import de.thm.arsnova.event.BeforeDeletionEvent;
import de.thm.arsnova.model.Comment;
import de.thm.arsnova.model.Room;
import de.thm.arsnova.model.migration.v2.CommentReadingCount;
......@@ -9,7 +10,9 @@ import de.thm.arsnova.web.exceptions.ForbiddenException;
import de.thm.arsnova.web.exceptions.NotFoundException;
import de.thm.arsnova.web.exceptions.UnauthorizedException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
......@@ -63,9 +66,9 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
}
final User user = getCurrentUser();
if (room.getOwnerId().equals(user.getId())) {
commentRepository.deleteByRoomId(room.getId());
delete(commentRepository.findStubsByRoomId(room.getId()));
} else {
commentRepository.deleteByRoomIdAndUserId(room.getId(), user.getId());
delete(commentRepository.findStubsByRoomIdAndUserId(room.getId(), user.getId()));
}
}
......@@ -123,4 +126,11 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
}
return user;
}
@EventListener
@Secured({"ROLE_USER", "RUN_AS_SYSTEM"})
public void handleRoomDeletion(final BeforeDeletionEvent<Room> event) {
final Iterable<Comment> comments = commentRepository.findStubsByRoomId(event.getEntity().getId());
delete(comments);
}
}
......@@ -17,6 +17,7 @@
*/
package de.thm.arsnova.service;
import de.thm.arsnova.event.BeforeDeletionEvent;
import de.thm.arsnova.model.Content;
import de.thm.arsnova.model.Room;
import de.thm.arsnova.model.Room.ContentGroup;
......@@ -26,14 +27,15 @@ import de.thm.arsnova.persistence.LogEntryRepository;
import de.thm.arsnova.security.User;
import de.thm.arsnova.web.exceptions.NotFoundException;
import de.thm.arsnova.web.exceptions.UnauthorizedException;
import org.ektorp.DocumentNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
......@@ -60,6 +62,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
private ContentRepository contentRepository;
private AnswerService answerService;
private AnswerRepository answerRepository;
private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
......@@ -79,6 +83,11 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
this.userService = userService;
}
@Autowired
public void setAnswerService(final AnswerService answerService) {
this.answerService = answerService;
}
@Override
protected void modifyRetrieved(final Content content) {
if (content.getFormat() != Content.Format.TEXT && 0 == content.getState().getRound()) {
......@@ -246,16 +255,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
roomService.update(room);
}
/* TODO: Only evict cache entry for the content's session. This requires some refactoring. */
@Override
@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
@Caching(evict = {
@CacheEvict("answerlists"),
@CacheEvict(value = "contents", key = "#contentId"),
@CacheEvict(value = "contentlists", allEntries = true),
@CacheEvict(value = "lecturecontentlists", allEntries = true /*, condition = "#content.getGroups().contains('lecture')"*/),
@CacheEvict(value = "preparationcontentlists", allEntries = true /*, condition = "#content.getGroups().contains('preparation')"*/),
@CacheEvict(value = "flashcardcontentlists", allEntries = true /*, condition = "#content.getGroups().contains('flashcard')"*/) })
@PreAuthorize("isAuthenticated()")
public void delete(final String contentId) {
final Content content = get(contentId);
if (content == null) {
......@@ -263,34 +264,22 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
}
try {
final int count = answerRepository.deleteByContentId(contentId);
delete(content);
dbLogger.log("delete", "type", "content", "answerCount", count);
} catch (final IllegalArgumentException e) {
logger.error("Could not delete content {}.", contentId, e);
}
}
@PreAuthorize("hasPermission(#session, 'owner')")
@Caching(evict = {
@CacheEvict(value = "contents", allEntries = true),
@CacheEvict(value = "contentlists", key = "#room.getId()"),
@CacheEvict(value = "lecturecontentlists", key = "#room.getId()", condition = "'lecture'.equals(#variant)"),
@CacheEvict(value = "preparationcontentlists", key = "#room.getId()", condition = "'preparation'.equals(#variant)"),
@CacheEvict(value = "flashcardcontentlists", key = "#room.getId()", condition = "'flashcard'.equals(#variant)") })
@PreAuthorize("isAuthenticated()")
private void deleteBySessionAndVariant(final Room room, final String variant) {
final List<String> contentIds;
final Iterable<Content> contents;
if ("all".equals(variant)) {
contentIds = contentRepository.findIdsByRoomId(room.getId());
contents = contentRepository.findStubsByRoomId(room.getId());
} else {
contentIds = contentRepository.findIdsByRoomIdAndVariant(room.getId(), variant);
contents = contentRepository.findStubsByRoomIdAndVariant(room.getId(), variant);
}
/* TODO: use EntityService! */
final int answerCount = answerRepository.deleteByContentIds(contentIds);
final int contentCount = contentRepository.deleteByRoomId(room.getId());
dbLogger.log("delete", "type", "question", "questionCount", contentCount);
dbLogger.log("delete", "type", "answer", "answerCount", answerCount);
delete(contents);
}
@Override
......@@ -445,10 +434,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
patch(contents, Collections.singletonMap("visible", publish), Content::getState);
}
/* TODO: Split and move answer part to AnswerService */
@Override
@PreAuthorize("isAuthenticated()")
@CacheEvict(value = "answerlists", allEntries = true)
public void deleteAllContentsAnswers(final String roomId) {
final User user = getCurrentUser();
final Room room = roomService.get(roomId);
......@@ -459,38 +446,29 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId());
resetContentsRoundState(room.getId(), contents);
final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
/* TODO: use EntityService! */
answerRepository.deleteAllAnswersForQuestions(contentIds);
answerService.delete(answerRepository.findStubsByContentIds(contentIds));
}
/* TODO: Split and move answer part to AnswerService */
/* TODO: Only evict cache entry for the answer's content. This requires some refactoring. */
@Override
@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
@CacheEvict(value = "answerlists", allEntries = true)
@PreAuthorize("isAuthenticated()")
public void deleteAllPreparationAnswers(String roomId) {
final Room room = roomService.get(roomId);
final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "preparation");
resetContentsRoundState(room.getId(), contents);
final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
/* TODO: use EntityService! */
answerRepository.deleteAllAnswersForQuestions(contentIds);
answerService.delete(answerRepository.findStubsByContentIds(contentIds));
}
/* TODO: Split and move answer part to AnswerService */
/* TODO: Only evict cache entry for the answer's content. This requires some refactoring. */
@Override
@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
@CacheEvict(value = "answerlists", allEntries = true)
@PreAuthorize("isAuthenticated()")
public void deleteAllLectureAnswers(String roomId) {
final Room room = roomService.get(roomId);
final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "lecture");
resetContentsRoundState(room.getId(), contents);
final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
/* TODO: use EntityService! */
answerRepository.deleteAllAnswersForQuestions(contentIds);
answerService.delete(answerRepository.findStubsByContentIds(contentIds));
}
@Caching(evict = {
......@@ -507,4 +485,11 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
}
contentRepository.saveAll(contents);
}
@EventListener
@Secured({"ROLE_USER", "RUN_AS_SYSTEM"})
public void handleRoomDeletion(final BeforeDeletionEvent<Room> event) {
final Iterable<Content> contents = contentRepository.findStubsByRoomId(event.getEntity().getId());
delete(contents);
}
}
......@@ -62,8 +62,6 @@ public interface RoomService extends EntityService<Room> {
Room updateCreator(String id, String newCreator);
int[] deleteCascading(Room room);
ScoreStatistics getLearningProgress(String id, String type, String questionVariant);
ScoreStatistics getMyLearningProgress(String id, String type, String questionVariant);
......
......@@ -155,20 +155,10 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
long lastActivityBefore = unixTime - guestRoomInactivityThresholdDays * 24 * 60 * 60 * 1000L;
int totalCount[] = new int[] {0, 0, 0};
List<Room> inactiveRooms = roomRepository.findInactiveGuestRoomsMetadata(lastActivityBefore);
for (Room room : inactiveRooms) {
int[] count = deleteCascading(room);
totalCount[0] += count[0];
totalCount[1] += count[1];
totalCount[2] += count[2];
}
delete(inactiveRooms);
if (!inactiveRooms.isEmpty()) {
logger.info("Deleted {} inactive guest rooms.", inactiveRooms.size());
dbLogger.log("cleanup", "type", "session",
"sessionCount", inactiveRooms.size(),
"questionCount", totalCount[0],
"answerCount", totalCount[1],
"commentCount", totalCount[2]);
}
}
}
......@@ -432,25 +422,6 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
throw new UnsupportedOperationException("No longer implemented.");
}
@Override
@PreAuthorize("hasPermission(#room, 'owner')")
@Caching(evict = {
@CacheEvict("rooms"),
@CacheEvict(value = "room.id-by-shortid", key = "#room.shortId")
})
public int[] deleteCascading(final Room room) {
int[] count = new int[] {0, 0, 0};
List<String> contentIds = contentRepository.findIdsByRoomId(room.getId());
count[2] = commentRepository.deleteByRoomId(room.getId());
count[1] = answerRepository.deleteByContentIds(contentIds);
count[0] = contentRepository.deleteByRoomId(room.getId());
delete(room);
logger.debug("Deleted room document {} and related data.", room.getId());
dbLogger.log("delete", "type", "session", "id", room.getId());
return count;
}
@Override
@PreAuthorize("hasPermission(#id, 'room', 'read')")
public ScoreStatistics getLearningProgress(final String id, final String type, final String questionVariant) {
......
......@@ -19,13 +19,16 @@ public class TimerServiceImpl implements TimerService {
private UserService userService;
private RoomService roomService;
private ContentService contentService;
private AnswerService answerService;
private AnswerRepository answerRepository;
public TimerServiceImpl(final UserService userService, final RoomService roomService,
final ContentService contentService, final AnswerRepository answerRepository) {
final ContentService contentService, final AnswerService answerService,
final AnswerRepository answerRepository) {
this.userService = userService;
this.roomService = roomService;
this.contentService = contentService;
this.answerService = answerService;
this.answerRepository = answerRepository;
}
......@@ -109,7 +112,7 @@ public class TimerServiceImpl implements TimerService {
}
resetRoundManagementState(content);
answerRepository.deleteByContentId(content.getId());
answerRepository.findStubsByContentId(content.getId());
contentService.update(content);
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment