Fix some remaining issues from the Spring migration ()

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: 
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>
This commit is contained in:
Andreas Svanberg 2024-11-20 12:56:21 +01:00 committed by Nico Athanassiadis
parent 1f0f15fc18
commit 73307096c3
14 changed files with 37 additions and 2 deletions

@ -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();
}
}

@ -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<>();

@ -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);
}

@ -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

@ -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);

@ -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)) {

@ -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){

@ -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...

@ -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;

@ -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);

@ -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);

@ -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);

@ -38,6 +38,7 @@ public class UserProfileServiceImpl extends AbstractServiceImpl<UserProfile, Lon
}
@Override
@Transactional
public Roles findSelectedRole(User user) {
return findByUser(user).getSelectedRole();
}

@ -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) {