From 73307096c36f4a012ff0325891a5e1700b6b3a74 Mon Sep 17 00:00:00 2001
From: Andreas Svanberg <andreass@dsv.su.se>
Date: Wed, 20 Nov 2024 12:56:21 +0100
Subject: [PATCH] Fix some remaining issues from the Spring migration (#20)

A few missing `@Bean` definitions were discovered.

Spring also has a much stricter requirement about requiring `@Transactional` for every database change.

## How to test `@Bean GroupFacadeImpl`
1. Log in as a supervisor
2. Go to "My groups" and create a group
3. Go back to "My groups" and try to open the group

## How to test `@Bean IdeaFacadae`
1. Create an application period that is open ("Admin" / "Match" / "Application periods")
2. Log in as an author
3. Go to "Ideas" / "My ideas"
4. Click "Select from available ideas" in the period created in step 1

## How to test missing `@Transactional`
1. Log in as a user with notifications (or generate some by for example writing in the forum)
2. Go to "Notifications" in the top right
3. Click on any notification subject to navigate to it

## How to test crash while trying to schedule final seminar
1. Log in as a supervisor
2. Open a project and attempt to schedule a final seminar

## How to test crash while trying to upload final thesis as supervisor
1. Log in as supervisor
2. Note down the supervisors username
3. Open a project that has had a final seminar
4. Go to the "Finishing up" tab
5. Submit the supervisors username as the custom principal
6. Try to upload a final thesis

## How to test removal of approved final thesis
1. Log in as supervisor
2. Note down the supervisors username
3. Open a project that has had a final seminar
4. Go to the "Finishing up" tab
5. Submit the supervisors username as the custom principal
6. Upload a final thesis
7. Approve the final thesis
8. Remove the approval

## How to test crash while trying to schedule first meeting
1. Log in as supervisor
2. Open a project
3. Go to the "First meeting tab"
4. Try to submit

## How to test crash while trying to unselect an idea as an author
1. Create an application period that is open ("Admin" / "Match" / "Application periods")
2. Log in as an author
3. Go to "Ideas" / "My ideas"
4. Click "Select from available ideas" in the period created in step 1
5. Select any available supervisor idea
6. Go back to "Ideas" / "My ideas"
7. Open the selected idea
8. Hit unselect at the bottom

## How to test crash while trying to toggle milestone
1. Log in as supervisor
2. Open any project
3. Go to "Milestones" tab
4. Attempt to toggle both individual and project milestones

## How to test crash while trying to get user's note
1. Find a user without a row in the `note` table
2. Log in as that user
3. Click "My notes" in the top right

Reviewed-on: https://gitea.dsv.su.se/DMC/scipro/pulls/20
Reviewed-by: Nico Athanassiadis <nico@dsv.su.se>
Co-authored-by: Andreas Svanberg <andreass@dsv.su.se>
Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
---
 core/src/main/java/se/su/dsv/scipro/CoreConfig.java  | 12 ++++++++++++
 .../FinalSeminarRespondentServiceImpl.java           |  2 ++
 .../scipro/finalthesis/FinalThesisServiceImpl.java   |  2 ++
 .../scipro/firstmeeting/FirstMeetingServiceImpl.java |  2 ++
 .../java/se/su/dsv/scipro/match/IdeaServiceImpl.java |  3 +--
 .../service/impl/MilestoneServiceImpl.java           |  3 +++
 .../java/se/su/dsv/scipro/notes/NoteServiceImpl.java |  2 ++
 .../notifications/NotificationServiceImpl.java       |  2 ++
 .../dsv/scipro/report/GradingReportServiceImpl.java  |  1 +
 .../se/su/dsv/scipro/report/ReportServiceImpl.java   |  2 ++
 .../reviewing/ReviewerInteractionServiceImpl.java    |  1 +
 .../dsv/scipro/reviewing/ReviewingServiceImpl.java   |  3 +++
 .../serviceimpls/UserProfileServiceImpl.java         |  1 +
 .../dsv/scipro/system/ResearchAreaServiceImpl.java   |  3 +++
 14 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java
index d9245d2155..a6ec1b8ffe 100644
--- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java
+++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java
@@ -65,6 +65,7 @@ import se.su.dsv.scipro.grading.NationalSubjectCategoryServiceImpl;
 import se.su.dsv.scipro.grading.PublicationMetadataRepository;
 import se.su.dsv.scipro.grading.PublicationMetadataServiceImpl;
 import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
+import se.su.dsv.scipro.group.GroupFacadeImpl;
 import se.su.dsv.scipro.group.GroupService;
 import se.su.dsv.scipro.group.GroupServiceImpl;
 import se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarRepository;
@@ -79,6 +80,7 @@ import se.su.dsv.scipro.match.ApplicationPeriodProjectTypeServiceImpl;
 import se.su.dsv.scipro.match.ApplicationPeriodService;
 import se.su.dsv.scipro.match.ApplicationPeriodServiceImpl;
 import se.su.dsv.scipro.match.FirstMeetingRepository;
+import se.su.dsv.scipro.match.IdeaFacade;
 import se.su.dsv.scipro.match.IdeaRepository;
 import se.su.dsv.scipro.match.IdeaService;
 import se.su.dsv.scipro.match.IdeaServiceImpl;
@@ -1009,4 +1011,14 @@ public class CoreConfig {
     public NotificationEventServiceImpl notificationEventService(NotificationEventRepository notificationEventRepository) {
         return new NotificationEventServiceImpl(notificationEventRepository);
     }
+
+    @Bean
+    public IdeaFacade ideaFacade() {
+        return new IdeaFacade();
+    }
+
+    @Bean
+    public GroupFacadeImpl groupFacade() {
+        return new GroupFacadeImpl();
+    }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRespondentServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRespondentServiceImpl.java
index ee7478c33c..f532f0289d 100644
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRespondentServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRespondentServiceImpl.java
@@ -1,5 +1,6 @@
 package se.su.dsv.scipro.finalseminar;
 
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.User;
 
@@ -33,6 +34,7 @@ public class FinalSeminarRespondentServiceImpl extends AbstractServiceImpl<Final
     }
 
     @Override
+    @Transactional
     public List<FinalSeminarRespondent> findOrCreate(FinalSeminar finalSeminar) {
         if(finalSeminar.getId() == null) {
             return new ArrayList<>();
diff --git a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
index 41251d1700..f505a3d189 100644
--- a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
@@ -56,6 +56,7 @@ public class FinalThesisServiceImpl extends AbstractServiceImpl<FinalThesis, Lon
     }
 
     @Override
+    @Transactional
     public FinalThesis upload(ProjectFileUpload fileUpload, String englishTitle, String swedishTitle) {
         ProjectFile fileDescription = storeFinalThesisFile(fileUpload);
         final FileReference reference = fileService.createReference(fileDescription.getFileDescription());
@@ -133,6 +134,7 @@ public class FinalThesisServiceImpl extends AbstractServiceImpl<FinalThesis, Lon
     }
 
     @Override
+    @Transactional
     public void removeApproval(Project project) {
         setStatus(project, FinalThesis.Status.NO_DECISION);
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingServiceImpl.java
index 23d1832201..1ab1e575a8 100644
--- a/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingServiceImpl.java
@@ -1,5 +1,6 @@
 package se.su.dsv.scipro.firstmeeting;
 
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.activityplan.Activity;
 import se.su.dsv.scipro.activityplan.ActivityPlanFacade;
 import se.su.dsv.scipro.project.Project;
@@ -26,6 +27,7 @@ public class FirstMeetingServiceImpl extends AbstractServiceImpl<ProjectFirstMee
     }
 
     @Override
+    @Transactional
     public ProjectFirstMeeting schedule(final Project project, final Date date, final String room, final String description) {
         final Optional<ProjectFirstMeeting> optFirstMeeting = findByProject(project);
         final ProjectFirstMeeting firstMeeting = optFirstMeeting
diff --git a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
index d3c410eb05..e6be18e1c3 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
@@ -358,7 +358,6 @@ public class IdeaServiceImpl extends AbstractServiceImpl<Idea, Long> implements
         }
     }
 
-    @Transactional
     private Idea acceptIdea0(Idea idea, User creator, Program accepteeProgram, User coAuthor, final ApplicationPeriod applicationPeriod) {
 
         Idea localIdea = idea;
@@ -442,6 +441,7 @@ public class IdeaServiceImpl extends AbstractServiceImpl<Idea, Long> implements
     }
 
     @Override
+    @Transactional
     public void studentUnselect(final Idea idea, final User student) {
         unmatch(idea, student, new NotificationSource());
     }
@@ -490,7 +490,6 @@ public class IdeaServiceImpl extends AbstractServiceImpl<Idea, Long> implements
         return returnValue;
     }
 
-    @Transactional
     private Idea addMatch(User creator, User supervisor, Idea idea, NotificationSource notificationSource) {
         Match match = new Match();
         match.setIdea(idea);
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
index 70c69ddef2..f7b5b8c447 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestoneServiceImpl.java
@@ -4,6 +4,7 @@ import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.jpa.impl.JPAQuery;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.milestones.dataobjects.Milestone;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
@@ -54,6 +55,7 @@ public class MilestoneServiceImpl extends AbstractServiceImpl<Milestone, Long> i
     }
 
     @Override
+    @Transactional
     public void setConfirmed(Project project, MilestoneActivityTemplate activity, boolean confirmed) {
         if (confirmed) {
             for (MilestoneActivityTemplate earlierActivity : getEarlierIncompleteProjectMilestoneActivities(project, activity)) {
@@ -84,6 +86,7 @@ public class MilestoneServiceImpl extends AbstractServiceImpl<Milestone, Long> i
     }
 
     @Override
+    @Transactional
     public void setConfirmed(Project project, User student, MilestoneActivityTemplate activity, boolean confirmed) {
 
         if (!project.hasModule(ProjectModule.MILESTONES)) {
diff --git a/core/src/main/java/se/su/dsv/scipro/notes/NoteServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notes/NoteServiceImpl.java
index 126470035e..58cb33dbc1 100755
--- a/core/src/main/java/se/su/dsv/scipro/notes/NoteServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notes/NoteServiceImpl.java
@@ -1,6 +1,7 @@
 package se.su.dsv.scipro.notes;
 
 import com.querydsl.core.types.dsl.BooleanExpression;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 import se.su.dsv.scipro.system.User;
 
@@ -20,6 +21,7 @@ public class NoteServiceImpl extends AbstractServiceImpl<Note, Long> implements
     }
 
     @Override
+    @Transactional
     public Note getNote(User user) {
         Note note = findOne(hasUser(user));
         if (note == null){
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
index 6c460a0741..5bbe68093e 100755
--- a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
@@ -50,6 +50,7 @@ public class NotificationServiceImpl extends AbstractServiceImpl<Notification,Lo
     }
 
     @Override
+    @Transactional
     public void setRead(User user, NotificationEvent notificationEvent, boolean read) {
         Iterable<Notification> notifications = findAll(
                 QNotification.notification.notificationEvent.eq(notificationEvent)
@@ -62,6 +63,7 @@ public class NotificationServiceImpl extends AbstractServiceImpl<Notification,Lo
     }
 
     @Override
+    @Transactional
     @SuppressWarnings("unchecked")
     // This looks so wrong but a List<Notification> is what we actually get
     // during runtime, see SCIPRO-167935. Reflection so awesome...
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
index 1dd53e1665..876d3a4010 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
@@ -48,6 +48,7 @@ public class GradingReportServiceImpl implements GradingReportTemplateService, G
     }
 
     @Override
+    @Transactional
     public boolean updateOppositionCriteria(SupervisorGradingReport report, FinalSeminarOpposition opposition) {
         for (GradingCriterion gradingCriterion : report.getIndividualCriteria()) {
             boolean isOppositionCriterion = gradingCriterion.getFlag() == GradingCriterion.Flag.OPPOSITION;
diff --git a/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
index 44d938d819..a0f324029f 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
@@ -22,12 +22,14 @@ public class ReportServiceImpl extends AbstractServiceImpl<Report, Long> impleme
     }
 
     @Override
+    @Transactional
     public AttachmentReport submit(AttachmentReport report) {
         report.submit();
         return save(report);
     }
 
     @Override
+    @Transactional
     public void save(AttachmentReport report, Optional<FileUpload> fileUpload) {
         storeReportFile(report, fileUpload);
         save(report);
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
index a08f5ffcbc..f0e62aacc6 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
@@ -49,6 +49,7 @@ public class ReviewerInteractionServiceImpl implements ReviewerInteractionServic
     }
 
     @Override
+    @Transactional
     public ForumPost reply(final Project project, final User user, final String content, final Set<Attachment> attachments) {
         ReviewerThread reviewerThread = getReviewerThread(project);
         ForumPost reply = forumService.createReply(reviewerThread.getForumThread(), user, content, attachments);
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingServiceImpl.java
index 9087510e37..b56e733c68 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingServiceImpl.java
@@ -7,6 +7,7 @@ import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.core.types.dsl.LiteralExpression;
 import com.querydsl.jpa.impl.JPAQuery;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.file.FileUpload;
@@ -92,6 +93,7 @@ public class ReviewingServiceImpl extends AbstractServiceImpl<ReviewerApproval,
     }
 
     @Override
+    @Transactional
     public void reject(final ReviewerApproval reviewerApproval, final String reason, final Optional<FileUpload> feedback) {
         Optional<FileReference> feedbackFile = store(feedback);
         reviewerApproval.reject(reason, feedbackFile);
@@ -106,6 +108,7 @@ public class ReviewingServiceImpl extends AbstractServiceImpl<ReviewerApproval,
     }
 
     @Override
+    @Transactional
     public void approve(final ReviewerApproval process, final String reason, final Optional<FileUpload> feedback) {
         Optional<FileReference> feedbackFile = store(feedback);
         process.approve(reason, feedbackFile);
diff --git a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
index 788ae44b07..992aa54888 100644
--- a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
@@ -38,6 +38,7 @@ public class UserProfileServiceImpl extends AbstractServiceImpl<UserProfile, Lon
     }
 
     @Override
+    @Transactional
     public Roles findSelectedRole(User user) {
         return findByUser(user).getSelectedRole();
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/system/ResearchAreaServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/ResearchAreaServiceImpl.java
index 76dc29c854..3c75c66915 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/ResearchAreaServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/ResearchAreaServiceImpl.java
@@ -3,6 +3,8 @@ package se.su.dsv.scipro.system;
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+
 import java.util.Comparator;
 import java.util.List;
 
@@ -13,6 +15,7 @@ public class ResearchAreaServiceImpl extends AbstractServiceImpl<ResearchArea,Lo
     }
 
     @Override
+    @Transactional
     public ResearchArea updateExternalResearchArea(Long identifier, String name, final boolean active) {
         ResearchArea ra = new ResearchArea();
         if (identifier != null) {