RepositoryService.java

1
package edu.ucsb.cs156.frontiers.services;
2
3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.fasterxml.jackson.databind.ObjectMapper;
5
import edu.ucsb.cs156.frontiers.entities.Course;
6
import edu.ucsb.cs156.frontiers.entities.CourseStaff;
7
import edu.ucsb.cs156.frontiers.entities.RosterStudent;
8
import edu.ucsb.cs156.frontiers.enums.RepositoryPermissions;
9
import java.security.NoSuchAlgorithmException;
10
import java.security.spec.InvalidKeySpecException;
11
import java.util.HashMap;
12
import java.util.Map;
13
import lombok.extern.slf4j.Slf4j;
14
import org.springframework.boot.web.client.RestTemplateBuilder;
15
import org.springframework.http.*;
16
import org.springframework.stereotype.Service;
17
import org.springframework.web.client.HttpClientErrorException;
18
import org.springframework.web.client.RestTemplate;
19
20
@Service
21
@Slf4j
22
public class RepositoryService {
23
  private final JwtService jwtService;
24
  private final RestTemplate restTemplate;
25
  private final ObjectMapper mapper;
26
27
  public RepositoryService(
28
      JwtService jwtService, RestTemplateBuilder restTemplateBuilder, ObjectMapper mapper) {
29
    this.jwtService = jwtService;
30
    this.restTemplate = restTemplateBuilder.build();
31
    this.mapper = mapper;
32
  }
33
34
  /**
35
   * Creates a single student repository if it doesn't already exist, and provisions access to the
36
   * repository by that student
37
   *
38
   * @param course The Course in question
39
   * @param student RosterStudent of the student the repository should be created for
40
   * @param repoPrefix Name of the project or assignment. Used to title the repository, in the
41
   *     format repoPrefix-githubLogin
42
   * @param isPrivate Whether the repository is private or not
43
   */
44
  public void createStudentRepository(
45
      Course course,
46
      RosterStudent student,
47
      String repoPrefix,
48
      Boolean isPrivate,
49
      RepositoryPermissions permissions)
50
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
51
    String newRepoName = repoPrefix + "-" + student.getGithubLogin();
52
    String token = jwtService.getInstallationToken(course);
53
    String existenceEndpoint =
54
        "https://api.github.com/repos/" + course.getOrgName() + "/" + newRepoName;
55
    String createEndpoint = "https://api.github.com/orgs/" + course.getOrgName() + "/repos";
56
    String provisionEndpoint =
57
        "https://api.github.com/repos/"
58
            + course.getOrgName()
59
            + "/"
60
            + newRepoName
61
            + "/collaborators/"
62
            + student.getGithubLogin();
63
    HttpHeaders existenceHeaders = new HttpHeaders();
64 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("Authorization", "Bearer " + token);
65 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("Accept", "application/vnd.github+json");
66 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("X-GitHub-Api-Version", "2022-11-28");
67
68
    HttpEntity<String> existenceEntity = new HttpEntity<>(existenceHeaders);
69
70
    try {
71
      restTemplate.exchange(existenceEndpoint, HttpMethod.GET, existenceEntity, String.class);
72
    } catch (HttpClientErrorException e) {
73 1 1. createStudentRepository : negated conditional → KILLED
      if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
74
        HttpHeaders createHeaders = new HttpHeaders();
75 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("Authorization", "Bearer " + token);
76 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("Accept", "application/vnd.github+json");
77 1 1. createStudentRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("X-GitHub-Api-Version", "2022-11-28");
78
79
        Map<String, Object> body = new HashMap<>();
80
        body.put("name", newRepoName);
81
        body.put("private", isPrivate);
82
        String bodyAsJson = mapper.writeValueAsString(body);
83
84
        HttpEntity<String> createEntity = new HttpEntity<>(bodyAsJson, createHeaders);
85
86
        restTemplate.exchange(createEndpoint, HttpMethod.POST, createEntity, String.class);
87
      } else {
88
        log.warn(
89
            "Unexpected response code {} when checking for existence of repository {}",
90
            e.getStatusCode(),
91
            newRepoName);
92
        return;
93
      }
94
    }
95
    try {
96
      Map<String, Object> provisionBody = new HashMap<>();
97
      provisionBody.put("permission", permissions.getApiName());
98
      String provisionAsJson = mapper.writeValueAsString(provisionBody);
99
100
      HttpEntity<String> provisionEntity = new HttpEntity<>(provisionAsJson, existenceHeaders);
101
      restTemplate.exchange(provisionEndpoint, HttpMethod.PUT, provisionEntity, String.class);
102
    } catch (HttpClientErrorException ignored) {
103
104
    }
105
  }
106
107
  /**
108
   * Creates a single staff repository if it doesn't already exist, and provisions access to the
109
   * repository by that staff member
110
   *
111
   * @param course The Course in question
112
   * @param staff CourseStaff of the staff member the repository should be created for
113
   * @param repoPrefix Name of the project or assignment. Used to title the repository, in the
114
   *     format repoPrefix-githubLogin
115
   * @param isPrivate Whether the repository is private or not
116
   * @param permissions The permissions level to grant to the staff member
117
   */
118
  public void createStaffRepository(
119
      Course course,
120
      CourseStaff staff,
121
      String repoPrefix,
122
      Boolean isPrivate,
123
      RepositoryPermissions permissions)
124
      throws NoSuchAlgorithmException, InvalidKeySpecException, JsonProcessingException {
125
    String newRepoName = repoPrefix + "-" + staff.getGithubLogin();
126
    String token = jwtService.getInstallationToken(course);
127
    String existenceEndpoint =
128
        "https://api.github.com/repos/" + course.getOrgName() + "/" + newRepoName;
129
    String createEndpoint = "https://api.github.com/orgs/" + course.getOrgName() + "/repos";
130
    String provisionEndpoint =
131
        "https://api.github.com/repos/"
132
            + course.getOrgName()
133
            + "/"
134
            + newRepoName
135
            + "/collaborators/"
136
            + staff.getGithubLogin();
137
    HttpHeaders existenceHeaders = new HttpHeaders();
138 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("Authorization", "Bearer " + token);
139 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("Accept", "application/vnd.github+json");
140 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
    existenceHeaders.add("X-GitHub-Api-Version", "2022-11-28");
141
142
    HttpEntity<String> existenceEntity = new HttpEntity<>(existenceHeaders);
143
144
    try {
145
      restTemplate.exchange(existenceEndpoint, HttpMethod.GET, existenceEntity, String.class);
146
    } catch (HttpClientErrorException e) {
147 1 1. createStaffRepository : negated conditional → KILLED
      if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
148
        HttpHeaders createHeaders = new HttpHeaders();
149 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("Authorization", "Bearer " + token);
150 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("Accept", "application/vnd.github+json");
151 1 1. createStaffRepository : removed call to org/springframework/http/HttpHeaders::add → KILLED
        createHeaders.add("X-GitHub-Api-Version", "2022-11-28");
152
153
        Map<String, Object> body = new HashMap<>();
154
        body.put("name", newRepoName);
155
        body.put("private", isPrivate);
156
        String bodyAsJson = mapper.writeValueAsString(body);
157
158
        HttpEntity<String> createEntity = new HttpEntity<>(bodyAsJson, createHeaders);
159
160
        restTemplate.exchange(createEndpoint, HttpMethod.POST, createEntity, String.class);
161
      } else {
162
        log.warn(
163
            "Unexpected response code {} when checking for existence of repository {}",
164
            e.getStatusCode(),
165
            newRepoName);
166
        return;
167
      }
168
    }
169
    try {
170
      Map<String, Object> provisionBody = new HashMap<>();
171
      provisionBody.put("permission", permissions.getApiName());
172
      String provisionAsJson = mapper.writeValueAsString(provisionBody);
173
174
      HttpEntity<String> provisionEntity = new HttpEntity<>(provisionAsJson, existenceHeaders);
175
      restTemplate.exchange(provisionEndpoint, HttpMethod.PUT, provisionEntity, String.class);
176
    } catch (HttpClientErrorException ignored) {
177
178
    }
179
  }
180
}

Mutations

64

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:student_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

65

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:student_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

66

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:student_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

73

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:student_exits_if_not_not_found()]
negated conditional → KILLED

75

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_student_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

76

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_student_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

77

1.1
Location : createStudentRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_student_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

138

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:staff_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

139

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:staff_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

140

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:staff_exits_if_not_not_found()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

147

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:staff_exits_if_not_not_found()]
negated conditional → KILLED

149

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_staff_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

150

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_staff_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

151

1.1
Location : createStaffRepository
Killed by : edu.ucsb.cs156.frontiers.services.RepositoryServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.services.RepositoryServiceTests]/[method:successfully_creates_staff_repo_private()]
removed call to org/springframework/http/HttpHeaders::add → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0