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.findAll();
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 = "Get a paginated list of jobs")
60
  @PreAuthorize("hasRole('ROLE_ADMIN')")
61
  @GetMapping(value = "/paged", produces = "application/json")
62
  public Page<Job> someJobs(
63
      @Parameter(
64
              name = "page",
65
              description = "what page of the data",
66
              example = "0",
67
              required = true)
68
          @RequestParam
69
          int page,
70
      @Parameter(
71
              name = "pageSize",
72
              description = "size of each page",
73
              example = "5",
74
              required = true)
75
          @RequestParam
76
          int pageSize,
77
      @Parameter(
78
              name = "sortField",
79
              description = "sort field",
80
              example = "createdAt",
81
              required = false)
82
          @RequestParam(defaultValue = "createdAt")
83
          String sortField,
84
      @Parameter(
85
              name = "sortDirection",
86
              description = "sort direction",
87
              example = "ASC",
88
              required = false)
89
          @RequestParam(defaultValue = "ASC")
90
          String sortDirection) {
91
92
    List<String> allowedSortFields = Arrays.asList("createdAt", "updatedAt", "createdBy", "status");
93
94 1 1. someJobs : negated conditional → KILLED
    if (!allowedSortFields.contains(sortField)) {
95
      throw new IllegalArgumentException(
96
          String.format(
97
              "%s is not a valid sort field.  Valid values are %s", sortField, allowedSortFields));
98
    }
99
100
    List<String> allowedSortDirections = Arrays.asList("ASC", "DESC");
101 1 1. someJobs : negated conditional → KILLED
    if (!allowedSortDirections.contains(sortDirection)) {
102
      throw new IllegalArgumentException(
103
          String.format(
104
              "%s is not a valid sort direction.  Valid values are %s",
105
              sortDirection, allowedSortDirections));
106
    }
107
108
    Direction sortDirectionObject = Direction.ASC;
109 1 1. someJobs : negated conditional → KILLED
    if (sortDirection.equals("DESC")) {
110
      sortDirectionObject = Direction.DESC;
111
    }
112
113
    PageRequest pageRequest = PageRequest.of(page, pageSize, sortDirectionObject, sortField);
114 1 1. someJobs : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::someJobs → KILLED
    return jobsRepository.findAll(pageRequest);
115
  }
116
117
  @Operation(summary = "Delete all job records")
118
  @PreAuthorize("hasRole('ROLE_ADMIN')")
119
  @DeleteMapping("/all")
120
  public Map<String, String> deleteAllJobs() {
121 1 1. deleteAllJobs : removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteAll → KILLED
    jobsRepository.deleteAll();
122 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");
123
  }
124
125
  @Operation(summary = "Get a specific Job Log by ID if it is in the database")
126
  @PreAuthorize("hasRole('ROLE_ADMIN')")
127
  @GetMapping("")
128
  public Job getJobLogById(
129
      @Parameter(name = "id", description = "ID of the job") @RequestParam Long id)
130
      throws JsonProcessingException {
131
132
    Job job =
133 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));
134
135 1 1. getJobLogById : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogById → KILLED
    return job;
136
  }
137
138
  @Operation(summary = "Delete specific job record")
139
  @PreAuthorize("hasRole('ROLE_ADMIN')")
140
  @DeleteMapping("")
141
  public Map<String, String> deleteAllJobs(@Parameter(name = "id") @RequestParam Long id) {
142 1 1. deleteAllJobs : negated conditional → KILLED
    if (!jobsRepository.existsById(id)) {
143 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));
144
    }
145 1 1. deleteAllJobs : removed call to edu/ucsb/cs156/courses/repositories/JobsRepository::deleteById → KILLED
    jobsRepository.deleteById(id);
146 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));
147
  }
148
149
  @Operation(summary = "Launch Test Job (click fail if you want to test exception handling)")
150
  @PreAuthorize("hasRole('ROLE_ADMIN')")
151
  @PostMapping("/launch/testjob")
152
  public Job launchTestJob(
153
      @Parameter(name = "fail") @RequestParam Boolean fail,
154
      @Parameter(name = "sleepMs") @RequestParam Integer sleepMs) {
155
156
    TestJob testJob = TestJob.builder().fail(fail).sleepMs(sleepMs).build();
157 1 1. launchTestJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchTestJob → KILLED
    return jobService.runAsJob(testJob);
158
  }
159
160
  @Operation(summary = "Launch Job to Update Course Data")
161
  @PreAuthorize("hasRole('ROLE_ADMIN')")
162
  @PostMapping("/launch/updateCourses")
163
  public Job launchUpdateCourseDataJob(
164
      @Parameter(name = "quarterYYYYQ", description = "quarter (YYYYQ format)") @RequestParam
165
          String quarterYYYYQ,
166
      @Parameter(name = "subjectArea") @RequestParam String subjectArea,
167
      @Parameter(
168
              name = "ifStale",
169
              description = "true if job should only update when data is stale")
170
          @RequestParam(defaultValue = "true")
171
          Boolean ifStale) {
172
173
    log.info(
174
        "launchUpdateCourseDataJob: quarterYYYYQ={}, subjectArea={}, ifStale={}",
175
        quarterYYYYQ,
176
        subjectArea,
177
        ifStale);
178
    var job =
179
        updateCourseDataJobFactory.createForSubjectAndQuarterAndIfStale(
180
            subjectArea, quarterYYYYQ, ifStale);
181
182 1 1. launchUpdateCourseDataJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataJob → KILLED
    return jobService.runAsJob(job);
183
  }
184
185
  @Operation(summary = "Launch Job to Update Course Data using Quarter")
186
  @PreAuthorize("hasRole('ROLE_ADMIN')")
187
  @PostMapping("/launch/updateQuarterCourses")
188
  public Job launchUpdateCourseDataWithQuarterJob(
189
      @Parameter(name = "quarterYYYYQ", description = "quarter (YYYYQ format)") @RequestParam
190
          String quarterYYYYQ,
191
      @Parameter(
192
              name = "ifStale",
193
              description = "true if job should only update when data is stale")
194
          @RequestParam(defaultValue = "true")
195
          Boolean ifStale) {
196
197
    var job = updateCourseDataJobFactory.createForQuarter(quarterYYYYQ, ifStale);
198
199 1 1. launchUpdateCourseDataWithQuarterJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataWithQuarterJob → KILLED
    return jobService.runAsJob(job);
200
  }
201
202
  @Operation(summary = "Get long job logs")
203
  @PreAuthorize("hasRole('ROLE_ADMIN')")
204
  @GetMapping("/logs/{id}")
205
  public String getJobLogs(@Parameter(name = "id", description = "Job ID") @PathVariable Long id) {
206
207 1 1. getJobLogs : replaced return value with "" for edu/ucsb/cs156/courses/controllers/JobsController::getJobLogs → KILLED
    return jobService.getLongJob(id);
208
  }
209
210
  @Operation(summary = "Launch Job to Update Course Data for range of quarters")
211
  @PreAuthorize("hasRole('ROLE_ADMIN')")
212
  @PostMapping("/launch/updateCoursesRangeOfQuarters")
213
  public Job launchUpdateCourseDataRangeOfQuartersJob(
214
      @Parameter(name = "start_quarterYYYYQ", description = "start quarter (YYYYQ format)")
215
          @RequestParam
216
          String start_quarterYYYYQ,
217
      @Parameter(name = "end_quarterYYYYQ", description = "end quarter (YYYYQ format)")
218
          @RequestParam
219
          String end_quarterYYYYQ,
220
      @Parameter(
221
              name = "ifStale",
222
              description = "true if job should only update when data is stale")
223
          @RequestParam(defaultValue = "true")
224
          Boolean ifStale) {
225
226
    var job =
227
        updateCourseDataJobFactory.createForQuarterRange(
228
            start_quarterYYYYQ, end_quarterYYYYQ, ifStale);
229
230 1 1. launchUpdateCourseDataRangeOfQuartersJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersJob → KILLED
    return jobService.runAsJob(job);
231
  }
232
233
  @Operation(
234
      summary = "Launch Job to Update Course Data for a range of quarters for a single subject")
235
  @PreAuthorize("hasRole('ROLE_ADMIN')")
236
  @PostMapping("/launch/updateCoursesRangeOfQuartersSingleSubject")
237
  public Job launchUpdateCourseDataRangeOfQuartersSingleSubjectJob(
238
      @Parameter(name = "subjectArea", description = "subject area") @RequestParam
239
          String subjectArea,
240
      @Parameter(name = "start_quarterYYYYQ", description = "start quarter (YYYYQ format)")
241
          @RequestParam
242
          String start_quarterYYYYQ,
243
      @Parameter(name = "end_quarterYYYYQ", description = "end quarter (YYYYQ format)")
244
          @RequestParam
245
          String end_quarterYYYYQ,
246
      @Parameter(
247
              name = "ifStale",
248
              description = "true if job should only update when data is stale")
249
          @RequestParam(defaultValue = "true")
250
          Boolean ifStale) {
251
252
    var job =
253
        updateCourseDataJobFactory.createForSubjectAndQuarterRange(
254
            subjectArea, start_quarterYYYYQ, end_quarterYYYYQ, ifStale);
255
256 1 1. launchUpdateCourseDataRangeOfQuartersSingleSubjectJob : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUpdateCourseDataRangeOfQuartersSingleSubjectJob → KILLED
    return jobService.runAsJob(job);
257
  }
258
259
  @Operation(summary = "Launch Job to update grade history")
260
  @PreAuthorize("hasRole('ROLE_ADMIN')")
261
  @PostMapping("/launch/uploadGradeData")
262
  public Job launchUploadGradeData() {
263
    UploadGradeDataJob updateGradeDataJob = updateGradeDataJobFactory.create();
264 1 1. launchUploadGradeData : replaced return value with null for edu/ucsb/cs156/courses/controllers/JobsController::launchUploadGradeData → KILLED
    return jobService.runAsJob(updateGradeDataJob);
265
  }
266
}

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

94

1.1
Location : someJobs
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

101

1.1
Location : someJobs
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

109

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

114

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

121

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

122

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

133

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

135

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

142

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()]
negated conditional → KILLED

143

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

145

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

146

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

157

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

182

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

199

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

207

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

230

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

256

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

264

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

Active mutators

Tests examined


Report generated by PIT 1.17.0