CoursesController.java

1
package edu.ucsb.cs156.organic.controllers;
2
3
import edu.ucsb.cs156.organic.entities.Course;
4
import edu.ucsb.cs156.organic.entities.Staff;
5
import edu.ucsb.cs156.organic.entities.User;
6
import edu.ucsb.cs156.organic.repositories.CourseRepository;
7
import edu.ucsb.cs156.organic.repositories.StaffRepository;
8
import edu.ucsb.cs156.organic.repositories.UserRepository;
9
import io.swagger.v3.oas.annotations.Operation;
10
import io.swagger.v3.oas.annotations.Parameter;
11
import io.swagger.v3.oas.annotations.tags.Tag;
12
import lombok.extern.slf4j.Slf4j;
13
14
import com.fasterxml.jackson.core.JsonProcessingException;
15
16
17
import org.springframework.beans.factory.annotation.Autowired;
18
import org.springframework.format.annotation.DateTimeFormat;
19
import org.springframework.security.access.prepost.PreAuthorize;
20
import org.springframework.web.bind.annotation.DeleteMapping;
21
import org.springframework.web.bind.annotation.GetMapping;
22
import org.springframework.web.bind.annotation.PostMapping;
23
import org.springframework.web.bind.annotation.DeleteMapping;
24
import org.springframework.web.bind.annotation.PutMapping;
25
import org.springframework.web.bind.annotation.RequestBody;
26
import org.springframework.web.bind.annotation.RequestMapping;
27
import org.springframework.web.bind.annotation.RequestParam;
28
import org.springframework.web.bind.annotation.RestController;
29
30
import edu.ucsb.cs156.organic.errors.EntityNotFoundException;
31
32
import org.springframework.security.access.AccessDeniedException;
33
import java.time.LocalDateTime;
34
import java.util.Optional;
35
36
import javax.validation.Valid;
37
38
import javax.validation.Valid;
39
40
import java.util.Optional;
41
42
43
@Tag(name = "Courses")
44
@RequestMapping("/api/courses")
45
@RestController
46
@Slf4j
47
public class CoursesController extends ApiController {
48
49
    @Autowired
50
    CourseRepository courseRepository;
51
52
    @Autowired
53
    StaffRepository courseStaffRepository;
54
55
    @Autowired
56
    UserRepository userRepository;
57
58
    @Operation(summary = "List all courses")
59
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
60
    @GetMapping("/all")
61
    public Iterable<Course> allCourses() {
62
        User u = getCurrentUser().getUser();
63
        log.info("u={}", u);
64 1 1. allCourses : negated conditional → KILLED
        if (u.isAdmin()) {
65 1 1. allCourses : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::allCourses → KILLED
            return courseRepository.findAll();
66
        } else {
67 1 1. allCourses : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::allCourses → KILLED
            return courseRepository.findCoursesStaffedByUser(u.getGithubId());
68
        }
69
    }
70
71
    @Operation(summary= "Get a single course")
72
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
73
    @GetMapping("")
74
    public Course getById(
75
            @Parameter(name="id") @RequestParam Long id) {
76
        User u = getCurrentUser().getUser();
77
78
        Course course = courseRepository.findById(id)
79 1 1. lambda$getById$0 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getById$0 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, id));
80
        
81 1 1. getById : negated conditional → KILLED
        if(!u.isAdmin()){
82
                courseStaffRepository.findByCourseIdAndGithubId(id, u.getGithubId())
83 1 1. lambda$getById$1 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getById$1 → KILLED
                        .orElseThrow(() -> new AccessDeniedException(
84
                        String.format("User %s is not authorized to get course %d", u.getGithubLogin(), id)));
85
        }
86 1 1. getById : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::getById → KILLED
        return course;
87
    }
88
89
    @Operation(summary = "Create a new course")
90
    @PreAuthorize("hasAnyRole('ROLE_INSTRUCTOR', 'ROLE_ADMIN')")
91
    @PostMapping("/post")
92
    public Course postCourse(
93
            @Parameter(name = "name", description ="course name, e.g. CMPSC 156" ) @RequestParam String name,
94
            @Parameter(name = "school", description ="school abbreviation e.g. UCSB" ) @RequestParam String school,
95
            @Parameter(name = "term", description = "quarter or semester, e.g. F23") @RequestParam String term,
96
            @Parameter(name = "startDate", description = "in iso format, i.e. YYYY-mm-ddTHH:MM:SS; e.g. 2023-10-01T00:00:00 see https://en.wikipedia.org/wiki/ISO_8601") @RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate,
97
            @Parameter(name = "endDate", description = "in iso format, i.e. YYYY-mm-ddTHH:MM:SS; e.g. 2023-12-31T11:59:59 see https://en.wikipedia.org/wiki/ISO_8601") @RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate,
98
            @Parameter(name = "githubOrg", description = "for example ucsb-cs156-f23" ) @RequestParam String githubOrg)
99
            throws JsonProcessingException {
100
101
        Course course = Course.builder()
102
                .name(name)
103
                .school(school)
104
                .term(term)
105
                .startDate(startDate)
106
                .endDate(endDate)
107
                .githubOrg(githubOrg)
108
                .build();
109
110
        Course savedCourse = courseRepository.save(course);
111
        User u = getCurrentUser().getUser();
112
113
        Staff courseStaff = Staff.builder()
114
                .courseId(savedCourse.getId())
115
                .githubId(u.getGithubId())
116
                .build();
117
118
        log.info("courseStaff={}", courseStaff);
119
        courseStaffRepository.save(courseStaff);
120
121 1 1. postCourse : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::postCourse → KILLED
        return savedCourse;
122
    }
123
    
124
    @Operation(summary = "Update a course")
125
    @PreAuthorize("hasAnyRole('ROLE_INSTRUCTOR', 'ROLE_ADMIN')")
126
    @PutMapping("/update")
127
    public Course updateCourse(
128
            @Parameter(name = "id", description ="id") @RequestParam Long id,
129
            @RequestBody @Valid Course incoming) {
130
        User user = getCurrentUser().getUser();
131
        Course course = courseRepository.findById(id)
132 1 1. lambda$updateCourse$2 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$updateCourse$2 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, id));
133 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setName → KILLED
        course.setName(incoming.getName());
134 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setSchool → KILLED
        course.setSchool(incoming.getSchool());
135 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setTerm → KILLED
        course.setTerm(incoming.getTerm());
136 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setStartDate → KILLED
        course.setStartDate(incoming.getStartDate());
137 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setEndDate → KILLED
        course.setEndDate(incoming.getEndDate());
138 1 1. updateCourse : removed call to edu/ucsb/cs156/organic/entities/Course::setGithubOrg → KILLED
        course.setGithubOrg(incoming.getGithubOrg());
139 1 1. updateCourse : negated conditional → KILLED
        if(user.isAdmin()) {
140
                courseRepository.save(course);
141
        } else {
142
                Optional<Staff> staff = courseStaffRepository.findByCourseIdAndGithubId(course.getId(), user.getGithubId());
143 1 1. updateCourse : negated conditional → KILLED
                if(staff.isEmpty()) {
144
                        throw new EntityNotFoundException(Staff.class, id);
145
                } else {
146
                        courseRepository.save(course);
147
                }
148
        }
149 1 1. updateCourse : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::updateCourse → KILLED
        return course;
150
    }
151
152
    @Operation(summary = "Delete a course")
153
    @PreAuthorize("hasAnyRole('ROLE_INSTRUCTOR', 'ROLE_ADMIN')")
154
    @DeleteMapping("/delete")
155
    public Object deleteCourse(
156
            @Parameter(name = "id", description ="id") @RequestParam Long id) {
157
        User user = getCurrentUser().getUser();
158
        Course course = courseRepository.findById(id)
159 1 1. lambda$deleteCourse$3 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$deleteCourse$3 → KILLED
                        .orElseThrow(() -> new EntityNotFoundException(Course.class, id));
160
161 1 1. deleteCourse : negated conditional → KILLED
        if(user.isAdmin()) {
162 1 1. deleteCourse : removed call to edu/ucsb/cs156/organic/repositories/CourseRepository::delete → KILLED
                courseRepository.delete(course);
163
164 1 1. deleteCourse : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::deleteCourse → KILLED
                return genericMessage("Course with id %s deleted.".formatted(id));
165
        } else {
166
                Optional<Staff> staff = courseStaffRepository.findByCourseIdAndGithubId(course.getId(), user.getGithubId());
167 1 1. deleteCourse : negated conditional → KILLED
                if(staff.isEmpty()) {
168
                        throw new EntityNotFoundException(Staff.class, id);
169
                } else {
170 1 1. deleteCourse : removed call to edu/ucsb/cs156/organic/repositories/CourseRepository::delete → KILLED
                        courseRepository.delete(course);
171
                }
172
        }
173 1 1. deleteCourse : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::deleteCourse → KILLED
        return genericMessage("Course with id %s deleted.".formatted(id));
174
    }
175
176
    @Operation(summary = "Add a staff member to a course")
177
    @PreAuthorize("hasRole('ROLE_ADMIN')")
178
    @PostMapping("/addStaff")
179
    public Staff addStaff(
180
            @Parameter(name = "courseId") @RequestParam Long courseId,
181
            @Parameter(name = "githubLogin") @RequestParam String githubLogin)
182
            throws JsonProcessingException {
183
184
        Course course = courseRepository.findById(courseId)
185 1 1. lambda$addStaff$4 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$addStaff$4 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
186
187
        User user = userRepository.findByGithubLogin(githubLogin)
188 1 1. lambda$addStaff$5 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$addStaff$5 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(User.class, githubLogin.toString()));
189
190
        Staff courseStaff = Staff.builder()
191
                .courseId(course.getId())
192
                .githubId(user.getGithubId())
193
                .user(user)
194
                .build();
195
196
        courseStaff = courseStaffRepository.save(courseStaff);
197
        log.info("courseStaff={}", courseStaff);
198
199 1 1. addStaff : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::addStaff → KILLED
        return courseStaff;
200
    }
201
202
    @Operation(summary = "Get Staff for course")
203
    @PreAuthorize("hasRole('ROLE_ADMIN')")
204
    @GetMapping("/getStaff")
205
    public Iterable<Staff> getStaff(
206
            @Parameter(name = "courseId") @RequestParam Long courseId
207
    )
208
            throws JsonProcessingException {
209
210
        Course course = courseRepository.findById(courseId)
211 1 1. lambda$getStaff$6 : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getStaff$6 → KILLED
                .orElseThrow(() -> new EntityNotFoundException(Course.class, courseId.toString()));
212
213
        Iterable<Staff> courseStaff = courseStaffRepository.findByCourseId(course.getId());
214 1 1. getStaff : replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::getStaff → KILLED
        return courseStaff;
215
    }
216
  
217
}

Mutations

64

1.1
Location : allCourses
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:user_can_get_only_courses_for_which_they_are_staff()]
negated conditional → KILLED

65

1.1
Location : allCourses
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:admin_can_get_all_courses()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::allCourses → KILLED

67

1.1
Location : allCourses
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:user_can_get_only_courses_for_which_they_are_staff()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::allCourses → KILLED

79

1.1
Location : lambda$getById$0
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_get_course_for_a_non_existing_id()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getById$0 → KILLED

81

1.1
Location : getById
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:a_user_cannot_get_course_if_not_on_course_staff()]
negated conditional → KILLED

83

1.1
Location : lambda$getById$1
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:a_user_cannot_get_course_if_not_on_course_staff()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getById$1 → KILLED

86

1.1
Location : getById
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_get_course_by_id_for_existing_id()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::getById → KILLED

121

1.1
Location : postCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_instructor_user_can_post_a_new_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::postCourse → KILLED

132

1.1
Location : lambda$updateCourse$2
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_update_nonexistent_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$updateCourse$2 → KILLED

133

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setName → KILLED

134

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setSchool → KILLED

135

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setTerm → KILLED

136

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setStartDate → KILLED

137

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setEndDate → KILLED

138

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
removed call to edu/ucsb/cs156/organic/entities/Course::setGithubOrg → KILLED

139

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
negated conditional → KILLED

143

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_instructor_user_can_update_a_course()]
negated conditional → KILLED

149

1.1
Location : updateCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_update_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::updateCourse → KILLED

159

1.1
Location : lambda$deleteCourse$3
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_delete_nonexistent_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$deleteCourse$3 → KILLED

161

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:a_non_admin_or_instructor_user_cannot_delete_a_course()]
negated conditional → KILLED

162

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_delete_a_course()]
removed call to edu/ucsb/cs156/organic/repositories/CourseRepository::delete → KILLED

164

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_delete_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::deleteCourse → KILLED

167

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_instructor_user_can_delete_a_course()]
negated conditional → KILLED

170

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_instructor_user_can_delete_a_course()]
removed call to edu/ucsb/cs156/organic/repositories/CourseRepository::delete → KILLED

173

1.1
Location : deleteCourse
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_instructor_user_can_delete_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::deleteCourse → KILLED

185

1.1
Location : lambda$addStaff$4
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_add_staff_to_a_non_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$addStaff$4 → KILLED

188

1.1
Location : lambda$addStaff$5
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_add_non_existing_user_to_staff_of_an_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$addStaff$5 → KILLED

199

1.1
Location : addStaff
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_add_a_staff_member_to_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::addStaff → KILLED

211

1.1
Location : lambda$getStaff$6
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_cannot_get_staff_for_a_non_existing_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::lambda$getStaff$6 → KILLED

214

1.1
Location : getStaff
Killed by : edu.ucsb.cs156.organic.controllers.CoursesControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.organic.controllers.CoursesControllerTests]/[method:an_admin_user_can_get_staff_for_a_course()]
replaced return value with null for edu/ucsb/cs156/organic/controllers/CoursesController::getStaff → KILLED

Active mutators

Tests examined


Report generated by PIT 1.7.3