WebhookController.java

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
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:no_action()]
negated conditional → KILLED

49

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:no_action()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

56

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noCourse()]
negated conditional → KILLED

2.2
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook_memberInvited()]
negated conditional → KILLED

57

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:testMemberRemoved_withValidFields()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

65

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noCourse()]
negated conditional → KILLED

66

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberAdded_missingMembershipField()]
negated conditional → KILLED

67

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberAdded_missingUserField()]
negated conditional → KILLED

68

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberAdded_missingLoginField()]
negated conditional → KILLED

69

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noCourse()]
negated conditional → KILLED

70

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberAdded_missingInstallationIdField()]
negated conditional → KILLED

71

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberAdded_missingUserField()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

79

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberInvited_missingUserField()]
negated conditional → KILLED

80

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberInvited_missingLoginField()]
negated conditional → KILLED

81

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberInvited_missingInstallationField()]
negated conditional → KILLED

82

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberInvited_missingInstallationIdField()]
negated conditional → KILLED

83

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:memberInvited_missingLoginField()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

95

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noCourse()]
negated conditional → KILLED

97

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noCourse()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

103

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook_memberInvited()]
negated conditional → KILLED

105

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:noStudent()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

112

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook_memberInvited()]
negated conditional → KILLED

113

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook()]
removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED

116

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook_memberInvited()]
removed call to edu/ucsb/cs156/frontiers/entities/RosterStudent::setOrgStatus → KILLED

122

1.1
Location : createGitHubWebhook
Killed by : edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.frontiers.controllers.WebhookControllerTests]/[method:successfulWebhook_memberInvited()]
replaced return value with null for edu/ucsb/cs156/frontiers/controllers/WebhookController::createGitHubWebhook → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0