3157 Fix problem with marking threads read when opening the same thread multiple times simultaneously

This commit is contained in:
Andreas Svanberg 2024-04-03 11:01:04 +02:00
parent a3dbed13d2
commit db84cc6c17
7 changed files with 38 additions and 41 deletions

@ -51,8 +51,11 @@ public class BasicForumServiceImpl implements BasicForumService {
@Override
@Transactional
public boolean setThreadRead(User user, ForumThread forumThread, boolean read) {
for (ForumPost post : forumThread.getPosts()) {
setRead(user, post, read);
readStateRepository.setThreadRead(user, forumThread, read);
if (read) {
for (ForumPost post : forumThread.getPosts()) {
eventBus.post(new ForumPostReadEvent(post, user));
}
}
return read;
}

@ -3,20 +3,5 @@ package se.su.dsv.scipro.forum;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.system.User;
public final class ForumPostReadEvent {
private final ForumPost post;
private final User user;
public ForumPostReadEvent(final ForumPost post, final User user) {
this.post = post;
this.user = user;
}
public ForumPost getPost() {
return post;
}
public User getUser() {
return user;
}
public record ForumPostReadEvent(ForumPost post, User user) {
}

@ -6,6 +6,7 @@ import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumPostReadState;
import se.su.dsv.scipro.forum.dataobjects.ForumPostReadStateId;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.system.User;
@Transactional
@ -13,4 +14,6 @@ public interface ForumPostReadStateRepository
extends JpaRepository<ForumPostReadState, ForumPostReadStateId>, QueryDslPredicateExecutor<ForumPostReadState> {
ForumPostReadState find(User user, ForumPost post);
void setThreadRead(User user, ForumThread forumThread, boolean read);
}

@ -1,8 +1,11 @@
package se.su.dsv.scipro.forum;
import com.google.inject.persist.Transactional;
import jakarta.persistence.LockModeType;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumPostReadState;
import se.su.dsv.scipro.forum.dataobjects.ForumPostReadStateId;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.QForumPostReadState;
import se.su.dsv.scipro.system.GenericRepo;
import se.su.dsv.scipro.system.User;
@ -23,4 +26,21 @@ public class ForumPostReadStateRepositoryImpl extends GenericRepo<ForumPostReadS
public ForumPostReadState find(User user, ForumPost post) {
return findOne(allOf(QForumPostReadState.forumPostReadState.id.user.eq(user), QForumPostReadState.forumPostReadState.id.post.eq(post)));
}
@Override
@Transactional
public void setThreadRead(User user, ForumThread forumThread, boolean read) {
EntityManager em = em();
em.lock(forumThread, LockModeType.PESSIMISTIC_WRITE);
for (ForumPost post : forumThread.getPosts()) {
ForumPostReadState state = find(user, post);
if (state == null) {
state = new ForumPostReadState();
state.setId(new ForumPostReadStateId(user, post));
}
state.setRead(read);
em.persist(state);
}
em.lock(forumThread, LockModeType.NONE);
}
}

@ -97,7 +97,7 @@ public class ForumNotifications {
@Subscribe
@Transactional
public void forumPostRead(ForumPostReadEvent forumPostReadEvent) {
forumNotificationRepository.findByForumPost(forumPostReadEvent.getPost()).ifPresent(connection ->
notificationService.setRead(forumPostReadEvent.getUser(), connection.getNotificationEvent(), true));
forumNotificationRepository.findByForumPost(forumPostReadEvent.post()).ifPresent(connection ->
notificationService.setRead(forumPostReadEvent.user(), connection.getNotificationEvent(), true));
}
}

@ -11,8 +11,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumPostReadState;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forummail.ForumMailSettingsService;
import se.su.dsv.scipro.mail.MailEventService;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.test.ForumBuilder;
import se.su.dsv.scipro.test.UserBuilder;
@ -35,10 +33,6 @@ public class BasicForumServiceImplTest {
@Mock
private ForumPostRepository postRepository;
@Mock
private MailEventService mailEventService;
@Mock
private ForumMailSettingsService mailSettingsService;
@Mock
private EventBus eventBus;
@InjectMocks
private BasicForumServiceImpl basicForumService;
@ -93,23 +87,20 @@ public class BasicForumServiceImplTest {
}
@Test
public void testMarkThreadRead() {
when(readStateRepository.find(any(User.class), any(ForumPost.class))).thenReturn(new ForumPostReadState());
when(readStateRepository.save(isA(ForumPostReadState.class))).thenAnswer(AdditionalAnswers.returnsFirstArg());
public void testMarkThreadReadPostsEvent() {
User user = new User();
ForumPost post = new ForumPost();
post.setContent("post 1");
ForumPost post2 = new ForumPost();
post2.setContent("post 2");
ForumThread forumThread = new ForumThread();
forumThread.addPost(post);
forumThread.addPost(post2);
basicForumService.setThreadRead(user, forumThread, true);
ArgumentCaptor<ForumPostReadState> captor = ArgumentCaptor.forClass(ForumPostReadState.class);
verify(readStateRepository, times(2)).save(captor.capture());
assertTrue(captor.getValue().isRead(), "Did not save correct read state");
verify(eventBus).post(new ForumPostReadEvent(post, user));
verify(eventBus).post(new ForumPostReadEvent(post2, user));
}
@Test
@ -181,8 +172,8 @@ public class BasicForumServiceImplTest {
verify(eventBus).post(captor.capture());
ForumPostReadEvent event = captor.getValue();
assertEquals(event.getPost(), post);
assertEquals(event.getUser(), user);
assertEquals(event.post(), post);
assertEquals(event.user(), user);
}
@Test

@ -8,7 +8,6 @@ import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightSupervisorMyGroups;
import se.su.dsv.scipro.forum.BasicForumService;
import se.su.dsv.scipro.forum.GroupForumService;
import se.su.dsv.scipro.forum.dataobjects.GroupThread;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
@ -22,8 +21,6 @@ public class SupervisorViewGroupThreadPage extends AbstractSupervisorGroupPage i
@Inject
private GroupForumService groupForumService;
@Inject
private BasicForumService basicForumService;
public SupervisorViewGroupThreadPage(final PageParameters parameters) {
super(parameters);
@ -43,8 +40,6 @@ public class SupervisorViewGroupThreadPage extends AbstractSupervisorGroupPage i
add(new FeedbackPanel("feedback"));
basicForumService.setThreadRead(loggedInUser(), groupThread.getForumThread(), true);
add(new ViewForumThreadPanel<>("thread", groupThreadModel, new GroupForumThread(groupForumService)) {
@Override
protected Component newBackLink(final String id) {