1 | package edu.ucsb.cs156.frontiers.controllers; | |
2 | ||
3 | ||
4 | import com.fasterxml.jackson.core.JsonProcessingException; | |
5 | import com.fasterxml.jackson.databind.JsonNode; | |
6 | import edu.ucsb.cs156.frontiers.entities.Course; | |
7 | import edu.ucsb.cs156.frontiers.entities.RosterStudent; | |
8 | import edu.ucsb.cs156.frontiers.entities.User; | |
9 | import edu.ucsb.cs156.frontiers.enums.OrgStatus; | |
10 | import edu.ucsb.cs156.frontiers.repositories.CourseRepository; | |
11 | import edu.ucsb.cs156.frontiers.repositories.RosterStudentRepository; | |
12 | import edu.ucsb.cs156.frontiers.repositories.UserRepository; | |
13 | import io.swagger.v3.oas.annotations.tags.Tag; | |
14 | import lombok.extern.slf4j.Slf4j; | |
15 | import org.springframework.http.ResponseEntity; | |
16 | import org.springframework.web.bind.annotation.PostMapping; | |
17 | import org.springframework.web.bind.annotation.RequestBody; | |
18 | import org.springframework.web.bind.annotation.RequestMapping; | |
19 | import org.springframework.web.bind.annotation.RestController; | |
20 | ||
21 | import java.util.Optional; | |
22 | ||
23 | @Tag(name = "Webhooks Controller") | |
24 | @RestController | |
25 | @RequestMapping("/api/webhooks") | |
26 | @Slf4j | |
27 | public class WebhookController { | |
28 | ||
29 | ||
30 | private final CourseRepository courseRepository; | |
31 | private final RosterStudentRepository rosterStudentRepository; | |
32 | ||
33 | public WebhookController(CourseRepository courseRepository, RosterStudentRepository rosterStudentRepository) { | |
34 | this.courseRepository = courseRepository; | |
35 | this.rosterStudentRepository = rosterStudentRepository; | |
36 | } | |
37 | ||
38 | /** | |
39 | * Accepts webhooks from GitHub, currently to update the membership status of a RosterStudent. | |
40 | * @param jsonBody body of the webhook. The description of the currently used webhook is available in docs/webhooks.md | |
41 | * | |
42 | * @return either the word success so GitHub will not flag the webhook as a failure, or the updated RosterStudent | |
43 | */ | |
44 | @PostMapping("/github") | |
45 | public ResponseEntity<String> createGitHubWebhook(@RequestBody JsonNode jsonBody) throws JsonProcessingException { | |
46 | log.info("Received GitHub webhook: {}", jsonBody.toString()); | |
47 | ||
48 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if(!jsonBody.has("action")){ |
49 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
50 | } | |
51 | | |
52 | String action = jsonBody.get("action").asText(); | |
53 | log.info("Webhook action: {}", action); | |
54 | | |
55 | // Early return if not an action we care about | |
56 |
2
1. createGitHubWebhook : negated conditional → KILLED 2. createGitHubWebhook : negated conditional → KILLED |
if(!action.equals("member_added") && !action.equals("member_invited")) { |
57 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
58 | } | |
59 | | |
60 | // Extract GitHub login based on payload structure | |
61 | String githubLogin = null; | |
62 | String installationId = null; | |
63 | | |
64 | // For member_added events, the structure is different | |
65 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if (action.equals("member_added")) { |
66 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if (!jsonBody.has("membership") || |
67 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.get("membership").has("user") || |
68 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.get("membership").get("user").has("login") || |
69 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.has("installation") || |
70 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.get("installation").has("id")) { |
71 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
72 | } | |
73 | | |
74 | githubLogin = jsonBody.get("membership").get("user").get("login").asText(); | |
75 | installationId = jsonBody.get("installation").get("id").asText(); | |
76 | } | |
77 | // For member_invited events, use the original structure | |
78 | else { // must be "member_invited" based on earlier check | |
79 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if (!jsonBody.has("user") || |
80 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.get("user").has("login") || |
81 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.has("installation") || |
82 |
1
1. createGitHubWebhook : negated conditional → KILLED |
!jsonBody.get("installation").has("id")) { |
83 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
84 | } | |
85 | | |
86 | githubLogin = jsonBody.get("user").get("login").asText(); | |
87 | installationId = jsonBody.get("installation").get("id").asText(); | |
88 | } | |
89 | | |
90 | log.info("GitHub login: {}, Installation ID: {}", githubLogin, installationId); | |
91 | | |
92 | Optional<Course> course = courseRepository.findByInstallationId(installationId); | |
93 | log.info("Course found: {}", course.isPresent()); | |
94 | | |
95 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if(!course.isPresent()){ |
96 | log.warn("No course found with installation ID: {}", installationId); | |
97 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
98 | } | |
99 | | |
100 | Optional<RosterStudent> student = rosterStudentRepository.findByCourseAndGithubLogin(course.get(), githubLogin); | |
101 | log.info("Student found: {}", student.isPresent()); | |
102 | | |
103 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if(!student.isPresent()){ |
104 | log.warn("No student found with GitHub login: {} in course: {}", githubLogin, course.get().getCourseName()); | |
105 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok().body("success"); |
106 | } | |
107 | | |
108 | RosterStudent updatedStudent = student.get(); | |
109 | log.info("Current student org status: {}", updatedStudent.getOrgStatus()); | |
110 | | |
111 | // Update status based on action | |
112 |
1
1. createGitHubWebhook : negated conditional → KILLED |
if(action.equals("member_added")) { |
113 |
1
1. createGitHubWebhook : removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED |
updatedStudent.setOrgStatus(OrgStatus.MEMBER); |
114 | log.info("Setting status to MEMBER"); | |
115 | } else { // must be "member_invited" based on earlier check | |
116 |
1
1. createGitHubWebhook : removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED |
updatedStudent.setOrgStatus(OrgStatus.INVITED); |
117 | log.info("Setting status to INVITED"); | |
118 | } | |
119 | | |
120 | rosterStudentRepository.save(updatedStudent); | |
121 | log.info("Student saved with new org status: {}", updatedStudent.getOrgStatus()); | |
122 |
1
1. createGitHubWebhook : replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED |
return ResponseEntity.ok(updatedStudent.toString()); |
123 | } | |
124 | } | |
Mutations | ||
48 |
1.1 |
|
49 |
1.1 |
|
56 |
1.1 2.2 |
|
57 |
1.1 |
|
65 |
1.1 |
|
66 |
1.1 |
|
67 |
1.1 |
|
68 |
1.1 |
|
69 |
1.1 |
|
70 |
1.1 |
|
71 |
1.1 |
|
79 |
1.1 |
|
80 |
1.1 |
|
81 |
1.1 |
|
82 |
1.1 |
|
83 |
1.1 |
|
95 |
1.1 |
|
97 |
1.1 |
|
103 |
1.1 |
|
105 |
1.1 |
|
112 |
1.1 |
|
113 |
1.1 |
|
116 |
1.1 |
|
122 |
1.1 |