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

Mutations

55

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

62

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

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()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

74

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

76

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

83

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

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()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

86

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

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()]
replaced return value with Collections.emptyMap for edu/ucsb/cs156/courses/controllers/JobsController::deleteAllJobs → KILLED

112

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

129

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

137

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

160

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

186

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

194

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

232

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

239

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

247

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

252

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