JobsController.java

1
package edu.ucsb.cs156.courses.controllers;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.fasterxml.jackson.databind.ObjectMapper;
5
import edu.ucsb.cs156.courses.collections.ConvertedSectionCollection;
6
import edu.ucsb.cs156.courses.entities.Job;
7
import edu.ucsb.cs156.courses.errors.EntityNotFoundException;
8
import edu.ucsb.cs156.courses.jobs.TestJob;
9
import edu.ucsb.cs156.courses.jobs.UpdateCourseDataJobFactory;
10
import edu.ucsb.cs156.courses.jobs.UploadGradeDataJob;
11
import edu.ucsb.cs156.courses.jobs.UploadGradeDataJobFactory;
12
import edu.ucsb.cs156.courses.repositories.JobsRepository;
13
import edu.ucsb.cs156.courses.services.jobs.JobService;
14
import io.swagger.v3.oas.annotations.Operation;
15
import io.swagger.v3.oas.annotations.Parameter;
16
import io.swagger.v3.oas.annotations.tags.Tag;
17
import java.util.Arrays;
18
import java.util.List;
19
import java.util.Map;
20
import lombok.extern.slf4j.Slf4j;
21
import org.springframework.beans.factory.annotation.Autowired;
22
import org.springframework.data.domain.Page;
23
import org.springframework.data.domain.PageRequest;
24
import org.springframework.data.domain.Sort.Direction;
25
import org.springframework.security.access.prepost.PreAuthorize;
26
import org.springframework.web.bind.annotation.DeleteMapping;
27
import org.springframework.web.bind.annotation.GetMapping;
28
import org.springframework.web.bind.annotation.PathVariable;
29
import org.springframework.web.bind.annotation.PostMapping;
30
import org.springframework.web.bind.annotation.RequestMapping;
31
import org.springframework.web.bind.annotation.RequestParam;
32
import org.springframework.web.bind.annotation.RestController;
33
34
@Tag(name = "Jobs")
35
@RequestMapping("/api/jobs")
36
@RestController
37
@Slf4j
38
public class JobsController extends ApiController {
39
  @Autowired private JobsRepository jobsRepository;
40
41
  @Autowired private ConvertedSectionCollection convertedSectionCollection;
42
43
  @Autowired private JobService jobService;
44
45
  @Autowired ObjectMapper mapper;
46
47
  @Autowired UpdateCourseDataJobFactory updateCourseDataJobFactory;
48
49
  @Autowired UploadGradeDataJobFactory updateGradeDataJobFactory;
50
51
  @Operation(summary = "List all jobs")
52
  @PreAuthorize("hasRole('ROLE_ADMIN')")
53
  @GetMapping("/all")
54
  public Iterable<Job> allJobs() {
55
    Iterable<Job> jobs = jobsRepository.findAllByOrderByIdDesc();
56 1 1. allJobs : replaced return value with Collections.emptyList for edu/ucsb/cs156/courses/controllers/JobsController::allJobs → KILLED
    return jobs;
57
  }
58
59
  @Operation(summary = "Delete all job records")
60
  @PreAuthorize("hasRole('ROLE_ADMIN')")
61
  @DeleteMapping("/all")
62
  public Map<String, String> deleteAllJobs() {
63 1 1. deleteAllJobs : removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteAll → KILLED
    jobsRepository.deleteAll();
64 1 1. deleteAllJobs : replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED
    return Map.of("message", "All jobs deleted");
65
  }
66
67
  @Operation(summary = "Get a specific Job Log by ID if it is in the database")
68
  @PreAuthorize("hasRole('ROLE_ADMIN')")
69
  @GetMapping("")
70
  public Job getJobLogById(
71
      @Parameter(name = "id", description = "ID of the job") @RequestParam Long id)
72
      throws JsonProcessingException {
73
74
    Job job =
75 1 1. lambda$getJobLogById$0 : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::lambda$getJobLogById$0 → KILLED
        jobsRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(Job.class, id));
76
77 1 1. getJobLogById : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogById → KILLED
    return job;
78
  }
79
80
  @Operation(summary = "Delete specific job record")
81
  @PreAuthorize("hasRole('ROLE_ADMIN')")
82
  @DeleteMapping("")
83
  public Map<String, String> deleteAllJobs(@Parameter(name = "id") @RequestParam Long id) {
84 1 1. deleteAllJobs : negated conditional → KILLED
    if (!jobsRepository.existsById(id)) {
85 1 1. deleteAllJobs : replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED
      return Map.of("message", String.format("Job with id %d not found", id));
86
    }
87 1 1. deleteAllJobs : removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteById → KILLED
    jobsRepository.deleteById(id);
88 1 1. deleteAllJobs : replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED
    return Map.of("message", String.format("Job with id %d deleted", id));
89
  }
90
91
  @Operation(summary = "Launch Test Job (click fail if you want to test exception handling)")
92
  @PreAuthorize("hasRole('ROLE_ADMIN')")
93
  @PostMapping("/launch/testjob")
94
  public Job launchTestJob(
95
      @Parameter(name = "fail") @RequestParam Boolean fail,
96
      @Parameter(name = "sleepMs") @RequestParam Integer sleepMs) {
97
98
    TestJob testJob = TestJob.builder().fail(fail).sleepMs(sleepMs).build();
99 1 1. launchTestJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchTestJob → KILLED
    return jobService.runAsJob(testJob);
100
  }
101
102
  @Operation(summary = "Launch Job to Update Course Data")
103
  @PreAuthorize("hasRole('ROLE_ADMIN')")
104
  @PostMapping("/launch/updateCourses")
105
  public Job launchUpdateCourseDataJob(
106
      @Parameter(name = "quarterYYYYQ", description = "quarter (YYYYQ format)") @RequestParam
107
          String quarterYYYYQ,
108
      @Parameter(name = "subjectArea") @RequestParam String subjectArea,
109
      @Parameter(
110
              name = "ifStale",
111
              description = "true if job should only update when data is stale")
112
          @RequestParam(defaultValue = "true")
113
          Boolean ifStale) {
114
115
    log.info(
116
        "launchUpdateCourseDataJob: quarterYYYYQ={}, subjectArea={}, ifStale={}",
117
        quarterYYYYQ,
118
        subjectArea,
119
        ifStale);
120
    var job =
121
        updateCourseDataJobFactory.createForSubjectAndQuarterAndIfStale(
122
            subjectArea, quarterYYYYQ, ifStale);
123
124 1 1. launchUpdateCourseDataJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataJob → KILLED
    return jobService.runAsJob(job);
125
  }
126
127
  @Operation(summary = "Launch Job to Update Course Data using Quarter")
128
  @PreAuthorize("hasRole('ROLE_ADMIN')")
129
  @PostMapping("/launch/updateQuarterCourses")
130
  public Job launchUpdateCourseDataWithQuarterJob(
131
      @Parameter(name = "quarterYYYYQ", description = "quarter (YYYYQ format)") @RequestParam
132
          String quarterYYYYQ,
133
      @Parameter(
134
              name = "ifStale",
135
              description = "true if job should only update when data is stale")
136
          @RequestParam(defaultValue = "true")
137
          Boolean ifStale) {
138
139
    var job = updateCourseDataJobFactory.createForQuarter(quarterYYYYQ, ifStale);
140
141 1 1. launchUpdateCourseDataWithQuarterJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataWithQuarterJob → KILLED
    return jobService.runAsJob(job);
142
  }
143
144
  @Operation(summary = "Get long job logs")
145
  @PreAuthorize("hasRole('ROLE_ADMIN')")
146
  @GetMapping("/logs/{id}")
147
  public String getJobLogs(@Parameter(name = "id", description = "Job ID") @PathVariable Long id) {
148
149 1 1. getJobLogs : replaced return value with "" for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogs → KILLED
    return jobService.getLongJob(id);
150
  }
151
152
  @Operation(summary = "Launch Job to Update Course Data for range of quarters")
153
  @PreAuthorize("hasRole('ROLE_ADMIN')")
154
  @PostMapping("/launch/updateCoursesRangeOfQuarters")
155
  public Job launchUpdateCourseDataRangeOfQuartersJob(
156
      @Parameter(name = "start_quarterYYYYQ", description = "start quarter (YYYYQ format)")
157
          @RequestParam
158
          String start_quarterYYYYQ,
159
      @Parameter(name = "end_quarterYYYYQ", description = "end quarter (YYYYQ format)")
160
          @RequestParam
161
          String end_quarterYYYYQ,
162
      @Parameter(
163
              name = "ifStale",
164
              description = "true if job should only update when data is stale")
165
          @RequestParam(defaultValue = "true")
166
          Boolean ifStale) {
167
168
    var job =
169
        updateCourseDataJobFactory.createForQuarterRange(
170
            start_quarterYYYYQ, end_quarterYYYYQ, ifStale);
171
172 1 1. launchUpdateCourseDataRangeOfQuartersJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersJob → KILLED
    return jobService.runAsJob(job);
173
  }
174
175
  @Operation(
176
      summary = "Launch Job to Update Course Data for a range of quarters for a single subject")
177
  @PreAuthorize("hasRole('ROLE_ADMIN')")
178
  @PostMapping("/launch/updateCoursesRangeOfQuartersSingleSubject")
179
  public Job launchUpdateCourseDataRangeOfQuartersSingleSubjectJob(
180
      @Parameter(name = "subjectArea", description = "subject area") @RequestParam
181
          String subjectArea,
182
      @Parameter(name = "start_quarterYYYYQ", description = "start quarter (YYYYQ format)")
183
          @RequestParam
184
          String start_quarterYYYYQ,
185
      @Parameter(name = "end_quarterYYYYQ", description = "end quarter (YYYYQ format)")
186
          @RequestParam
187
          String end_quarterYYYYQ,
188
      @Parameter(
189
              name = "ifStale",
190
              description = "true if job should only update when data is stale")
191
          @RequestParam(defaultValue = "true")
192
          Boolean ifStale) {
193
194
    var job =
195
        updateCourseDataJobFactory.createForSubjectAndQuarterRange(
196
            subjectArea, start_quarterYYYYQ, end_quarterYYYYQ, ifStale);
197
198 1 1. launchUpdateCourseDataRangeOfQuartersSingleSubjectJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersSingleSubjectJob → KILLED
    return jobService.runAsJob(job);
199
  }
200
201
  @Operation(summary = "Launch Job to update grade history")
202
  @PreAuthorize("hasRole('ROLE_ADMIN')")
203
  @PostMapping("/launch/uploadGradeData")
204
  public Job launchUploadGradeData() {
205
    UploadGradeDataJob updateGradeDataJob = updateGradeDataJobFactory.create();
206 1 1. launchUploadGradeData : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUploadGradeData → KILLED
    return jobService.runAsJob(updateGradeDataJob);
207
  }
208
209
  @Operation(summary = "Get a paginated jobs")
210
  @PreAuthorize("hasRole('ROLE_ADMIN')")
211
  @GetMapping(value = "/paginated", produces = "application/json")
212
  public Page<Job> getJobs(
213
      @Parameter(
214
              name = "page",
215
              description = "what page of the data",
216
              example = "0",
217
              required = true)
218
          @RequestParam
219
          int page,
220
      @Parameter(
221
              name = "pageSize",
222
              description = "size of each page",
223
              example = "10",
224
              required = true)
225
          @RequestParam
226
          int pageSize,
227
      @Parameter(
228
              name = "sortField",
229
              description = "sort field",
230
              example = "createdAt",
231
              required = false)
232
          @RequestParam(defaultValue = "status")
233
          String sortField,
234
      @Parameter(
235
              name = "sortDirection",
236
              description = "sort direction",
237
              example = "ASC",
238
              required = false)
239
          @RequestParam(defaultValue = "DESC")
240
          String sortDirection) {
241
242
    List<String> allowedSortFields =
243
        Arrays.asList("createdBy", "status", "createdAt", "completedAt");
244
245 1 1. getJobs : negated conditional → KILLED
    if (!allowedSortFields.contains(sortField)) {
246
      throw new IllegalArgumentException(
247
          String.format(
248
              "%s is not a valid sort field. Valid values are %s", sortField, allowedSortFields));
249
    }
250
251
    List<String> allowedSortDirections = Arrays.asList("ASC", "DESC");
252 1 1. getJobs : negated conditional → KILLED
    if (!allowedSortDirections.contains(sortDirection)) {
253
      throw new IllegalArgumentException(
254
          String.format(
255
              "%s is not a valid sort direction. Valid values are %s",
256
              sortDirection, allowedSortDirections));
257
    }
258
259
    Direction sortDirectionObject = Direction.DESC;
260 1 1. getJobs : negated conditional → KILLED
    if (sortDirection.equals("ASC")) {
261
      sortDirectionObject = Direction.ASC;
262
    }
263
264
    PageRequest pageRequest = PageRequest.of(page, pageSize, sortDirectionObject, sortField);
265 1 1. getJobs : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::getJobs → KILLED
    return jobsRepository.findAll(pageRequest);
266
  }
267
}

Mutations

56

1.1
Location : allJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_get_all_jobs()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/courses/controllers/JobsController::allJobs → KILLED

63

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_delete_all_jobs()]
removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteAll → KILLED

64

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_delete_all_jobs()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

75

1.1
Location : lambda$getJobLogById$0
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:api_getJobLogById__admin_logged_in__returns_not_found_for_missing_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::lambda$getJobLogById$0 → KILLED

77

1.1
Location : getJobLogById
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:api_getJobLogById__admin_logged_in__returns_job_by_id()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogById → KILLED

84

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_gets_reasonable_error_when_deleting_non_existing_job()]
negated conditional → KILLED

85

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_gets_reasonable_error_when_deleting_non_existing_job()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

87

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_delete_specific_job()]
removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteById → KILLED

88

1.1
Location : deleteAllJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_delete_specific_job()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

99

1.1
Location : launchTestJob
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_test_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchTestJob → KILLED

124

1.1
Location : launchUpdateCourseDataJob
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_update_courses_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataJob → KILLED

141

1.1
Location : launchUpdateCourseDataWithQuarterJob
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_update_courses_job_with_quarter()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataWithQuarterJob → KILLED

149

1.1
Location : getJobLogs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:test_getJobLogs_admin_can_get_job_log()]
replaced return value with "" for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogs → KILLED

172

1.1
Location : launchUpdateCourseDataRangeOfQuartersJob
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_update_courses_range_of_quarters_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersJob → KILLED

198

1.1
Location : launchUpdateCourseDataRangeOfQuartersSingleSubjectJob
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_update_courses_range_of_quarters_single_subject_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersSingleSubjectJob → KILLED

206

1.1
Location : launchUploadGradeData
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:admin_can_launch_upload_course_grade_data_job()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUploadGradeData → KILLED

245

1.1
Location : getJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:when_sortDirection_is_invalid_throws_exception()]
negated conditional → KILLED

252

1.1
Location : getJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:when_sortDirection_is_invalid_throws_exception()]
negated conditional → KILLED

260

1.1
Location : getJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:test_paginatedJobs_empty_DESC_status()]
negated conditional → KILLED

265

1.1
Location : getJobs
Killed by : edu.ucsb.cs156.courses.controllers.JobsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.courses.controllers.JobsControllerTests]/[method:test_paginatedJobs_empty_DESC_status()]
replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::getJobs → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0