Use OOP-ish style for SonarQube webhook handling
Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com>
This commit is contained in:
parent
86a644f31f
commit
5082e5d3f3
14
internal/webhook_handler/main_test.go
Normal file
14
internal/webhook_handler/main_test.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package webhook_handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// SETUP: mute logs
|
||||
func TestMain(m *testing.M) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package sonarqube
|
||||
package webhook_handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -9,9 +9,16 @@ import (
|
|||
"strings"
|
||||
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
webhook "gitea-sonarqube-pr-bot/internal/webhooks/sonarqube"
|
||||
)
|
||||
|
||||
func inProjectsMapping(p []settings.Project, n string) bool {
|
||||
type fetchDetailsType func(w *webhook.Webhook)
|
||||
|
||||
type SonarQubeWebhookHandler struct {
|
||||
fetchDetails fetchDetailsType
|
||||
}
|
||||
|
||||
func (_ *SonarQubeWebhookHandler) inProjectsMapping(p []settings.Project, n string) bool {
|
||||
for _, proj := range p {
|
||||
if proj.SonarQube.Key == n {
|
||||
return true
|
||||
|
@ -21,11 +28,11 @@ func inProjectsMapping(p []settings.Project, n string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func HandleWebhook(rw http.ResponseWriter, r *http.Request) {
|
||||
func (h *SonarQubeWebhookHandler) Handle(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
project := r.Header.Get("X-SonarQube-Project")
|
||||
if !inProjectsMapping(settings.Projects, project) {
|
||||
if !h.inProjectsMapping(settings.Projects, project) {
|
||||
log.Printf("Received hook for project '%s' which is not configured. Request ignored.", project)
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
@ -44,7 +51,7 @@ func HandleWebhook(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
w, ok := NewWebhook(raw)
|
||||
w, ok := webhook.New(raw)
|
||||
if !ok {
|
||||
rw.WriteHeader(http.StatusUnprocessableEntity)
|
||||
io.WriteString(rw, `{"message": "Error parsing POST body."}`)
|
||||
|
@ -60,5 +67,16 @@ func HandleWebhook(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Printf("%s", w)
|
||||
h.fetchDetails(w)
|
||||
}
|
||||
|
||||
|
||||
func fetchDetails(w *webhook.Webhook) {
|
||||
log.Printf("Hello from the original one: %s", w)
|
||||
}
|
||||
|
||||
func NewSonarQubeWebhookHandler() *SonarQubeWebhookHandler {
|
||||
return &SonarQubeWebhookHandler{
|
||||
fetchDetails: fetchDetails,
|
||||
}
|
||||
}
|
|
@ -1,16 +1,23 @@
|
|||
package sonarqube
|
||||
package webhook_handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
webhook "gitea-sonarqube-pr-bot/internal/webhooks/sonarqube"
|
||||
)
|
||||
|
||||
func withValidRequestData(t *testing.T) (*http.Request, *httptest.ResponseRecorder, http.HandlerFunc) {
|
||||
webhookHandler := NewSonarQubeWebhookHandler()
|
||||
webhookHandler.fetchDetails = func(w *webhook.Webhook) {
|
||||
log.Printf("Overridden fetchDetails")
|
||||
}
|
||||
|
||||
jsonBody := []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`)
|
||||
req, err := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
|
@ -19,12 +26,12 @@ func withValidRequestData(t *testing.T) (*http.Request, *httptest.ResponseRecord
|
|||
req.Header.Set("X-SonarQube-Project", "pr-bot")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(HandleWebhook)
|
||||
handler := http.HandlerFunc(webhookHandler.Handle)
|
||||
|
||||
return req, rr, handler
|
||||
}
|
||||
|
||||
func TestHandleWebhookProjectMapped(t *testing.T) {
|
||||
func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
|
||||
settings.Projects = []settings.Project{
|
||||
settings.Project{
|
||||
SonarQube: struct{Key string}{
|
||||
|
@ -39,7 +46,7 @@ func TestHandleWebhookProjectMapped(t *testing.T) {
|
|||
assert.Equal(t, `{"message": "Processing data. See bot logs for details."}`, rr.Body.String())
|
||||
}
|
||||
|
||||
func TestHandleWebhookProjectNotMapped(t *testing.T) {
|
||||
func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
|
||||
settings.Projects = []settings.Project{
|
||||
settings.Project{
|
||||
SonarQube: struct{Key string}{
|
||||
|
@ -54,7 +61,7 @@ func TestHandleWebhookProjectNotMapped(t *testing.T) {
|
|||
assert.Equal(t, `{"message": "Project 'pr-bot' not in configured list. Request ignored."}`, rr.Body.String())
|
||||
}
|
||||
|
||||
func TestHandleWebhookInvalidJSONBody(t *testing.T) {
|
||||
func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) {
|
||||
settings.Projects = []settings.Project{
|
||||
settings.Project{
|
||||
SonarQube: struct{Key string}{
|
||||
|
@ -71,7 +78,7 @@ func TestHandleWebhookInvalidJSONBody(t *testing.T) {
|
|||
req.Header.Set("X-SonarQube-Project", "pr-bot")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(HandleWebhook)
|
||||
handler := http.HandlerFunc(NewSonarQubeWebhookHandler().Handle)
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
|
|
@ -10,38 +10,32 @@ import (
|
|||
type Webhook struct {
|
||||
ServerUrl string `mapstructure:"serverUrl"`
|
||||
Revision string
|
||||
Branch branch
|
||||
QualityGate qualityGate `mapstructure:"qualityGate"`
|
||||
Branch struct {
|
||||
Name string
|
||||
Type string
|
||||
Url string
|
||||
}
|
||||
QualityGate struct {
|
||||
Status string
|
||||
Conditions []struct {
|
||||
Metric string
|
||||
Status string
|
||||
}
|
||||
} `mapstructure:"qualityGate"`
|
||||
}
|
||||
|
||||
type branch struct {
|
||||
Name string
|
||||
Type string
|
||||
Url string
|
||||
}
|
||||
|
||||
type qualityGate struct {
|
||||
Status string
|
||||
Conditions []condition
|
||||
}
|
||||
|
||||
type condition struct {
|
||||
Metric string
|
||||
Status string
|
||||
}
|
||||
|
||||
func NewWebhook(raw []byte) (*Webhook, bool) {
|
||||
func New(raw []byte) (*Webhook, bool) {
|
||||
v := viper.New()
|
||||
v.SetConfigType("json")
|
||||
v.ReadConfig(bytes.NewBuffer(raw))
|
||||
|
||||
w := Webhook{}
|
||||
w := &Webhook{}
|
||||
|
||||
err := v.Unmarshal(&w)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing SonarQube webhook: %s", err.Error())
|
||||
return nil, false
|
||||
return w, false
|
||||
}
|
||||
|
||||
return &w, true
|
||||
return w, true
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
func TestNewWebhook(t *testing.T) {
|
||||
raw := []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`)
|
||||
response, ok := NewWebhook(raw)
|
||||
response, ok := New(raw)
|
||||
|
||||
assert.NotNil(t, response)
|
||||
assert.True(t, ok)
|
||||
|
@ -16,8 +16,7 @@ func TestNewWebhook(t *testing.T) {
|
|||
|
||||
func TestNewWebhookInvalidJSON(t *testing.T) {
|
||||
raw := []byte(`{ "serverUrl": ["invalid-server-url-content"] }`)
|
||||
response, ok := NewWebhook(raw)
|
||||
_, ok := New(raw)
|
||||
|
||||
assert.Nil(t, response)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue