Retrieve actual data from SonarQube for comment

Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com>
This commit is contained in:
justusbunsi 2021-10-09 18:09:54 +02:00
parent c3566d9208
commit e608a8f969
No known key found for this signature in database
GPG key ID: 990B348ECAC9C7DB
7 changed files with 102 additions and 39 deletions

View file

@ -28,7 +28,7 @@ func (sdk *GiteaSdk) PostComment(repo settings.GiteaRepository, idx int, msg str
func New() *GiteaSdk {
client, err := gitea.NewClient(settings.Gitea.Url, gitea.SetToken(settings.Gitea.Token.Value))
if err != nil {
panic(fmt.Errorf("Cannot initialize Gitea client: %w", err))
panic(fmt.Errorf("cannot initialize Gitea client: %w", err))
}
return &GiteaSdk{client}

View file

@ -0,0 +1,53 @@
package sonarqube_sdk
import (
"fmt"
"strings"
)
type period struct {
Value string `json:"value"`
}
type MeasuresComponentMeasure struct {
Metric string `json:"metric"`
Value string `json:"value"`
Period *period `json:"period,omitempty"`
}
type MeasuresComponentMetric struct {
Key string `json:"key"`
Name string `json:"name"`
}
type MeasuresComponent struct {
PullRequest string `json:"pullRequest"`
Measures []MeasuresComponentMeasure `json:"measures"`
}
type MeasuresResponse struct {
Component MeasuresComponent `json:"component"`
Metrics []MeasuresComponentMetric `json:"metrics"`
}
func (mr *MeasuresResponse) GetRenderedMarkdownTable() string {
metricsTranslations := map[string]string{}
for _, metric := range mr.Metrics {
metricsTranslations[metric.Key] = metric.Name
}
measures := make([]string, len(mr.Component.Measures))
for i, measure := range mr.Component.Measures {
value := measure.Value
if measure.Period != nil {
value = measure.Period.Value
}
measures[i] = fmt.Sprintf("| %s | %s |", metricsTranslations[measure.Metric], value)
}
table := `
| Metric | Current |
| -------- | -------- |
%s`
return fmt.Sprintf(table, strings.Join(measures, "\n"))
}

View file

@ -2,16 +2,16 @@ package sonarqube_sdk
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"gitea-sonarqube-pr-bot/internal/settings"
)
type SonarQubeSdkInterface interface {
GetMeasures(string, string) (string, error)
GetMeasures(string, string) (*MeasuresResponse, error)
}
type SonarQubeSdk struct {
@ -20,20 +20,26 @@ type SonarQubeSdk struct {
token string
}
func (sdk *SonarQubeSdk) GetMeasures(project string, branch string) (string, error) {
func (sdk *SonarQubeSdk) GetMeasures(project string, branch string) (*MeasuresResponse, error) {
url := fmt.Sprintf("%s/api/measures/component?additionalFields=metrics&metricKeys=bugs,vulnerabilities,new_security_hotspots,violations&component=%s&pullRequest=%s", sdk.baseUrl, project, branch)
log.Println(url)
req, err := http.NewRequest("GET", url, nil)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
panic(fmt.Errorf("Cannot initialize Request: %w", err))
return nil, fmt.Errorf("cannot initialize Request: %w", err)
}
req.Header.Add("Authorization", sdk.basicAuth())
resp, _ := sdk.client.Do(req)
rawResp, _ := sdk.client.Do(req)
if rawResp.Body != nil {
defer rawResp.Body.Close()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
body, _ := io.ReadAll(rawResp.Body)
response := &MeasuresResponse{}
err = json.Unmarshal(body, &response)
if err != nil {
return nil, fmt.Errorf("cannot parse response from SonarQube: %w", err)
}
return string(body), nil
return response, nil
}
func (sdk *SonarQubeSdk) basicAuth() string {

View file

@ -12,6 +12,7 @@ import (
giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea_sdk"
sqSdk "gitea-sonarqube-pr-bot/internal/clients/sonarqube_sdk"
"github.com/gorilla/mux"
"github.com/urfave/cli/v2"
)

View file

@ -20,34 +20,23 @@ type SonarQubeWebhookHandler struct {
sqSdk sqSdk.SonarQubeSdkInterface
}
func (h *SonarQubeWebhookHandler) composeGiteaComment(w *webhook.Webhook) string {
a, _ := h.sqSdk.GetMeasures(w.Project.Key, w.Branch.Name)
log.Println(a)
status := ":white_check_mark:"
if w.QualityGate.Status != "OK" {
status = ":x:"
func (h *SonarQubeWebhookHandler) composeGiteaComment(w *webhook.Webhook) (string, error) {
m, err := h.sqSdk.GetMeasures(w.Project.Key, w.Branch.Name)
if err != nil {
return "", err
}
measures := `| Metric | Current |
| -------- | -------- |
| Bugs | 123 |
| Code Smells | 1 |
| Vulnerabilities | 1 |
`
message := make([]string, 5)
message[0] = w.GetRenderedQualityGate()
message[1] = m.GetRenderedMarkdownTable()
message[2] = fmt.Sprintf("See [SonarQube](%s) for details.", w.Branch.Url)
message[3] = "---"
message[4] = "- If you want the bot to check again, post `/sqbot review`"
msg := `**Quality Gate**: %s
**Measures**
%s
See [SonarQube](https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1) for details.`
return fmt.Sprintf(msg, status, measures)
return strings.Join(message, "\n\n"), nil
}
func (_ *SonarQubeWebhookHandler) inProjectsMapping(p []settings.Project, n string) (bool, int) {
func (*SonarQubeWebhookHandler) inProjectsMapping(p []settings.Project, n string) (bool, int) {
for idx, proj := range p {
if proj.SonarQube.Key == n {
return true, idx
@ -65,7 +54,11 @@ func (h *SonarQubeWebhookHandler) processData(w *webhook.Webhook, repo settings.
h.fetchDetails(w)
comment := h.composeGiteaComment(w)
comment, err := h.composeGiteaComment(w)
if err != nil {
log.Printf("Error composing Gitea comment: %s", err.Error())
return
}
h.giteaSdk.PostComment(repo, w.PRIndex, comment)
}

View file

@ -6,6 +6,7 @@ import (
"net/http/httptest"
"testing"
sqSDK "gitea-sonarqube-pr-bot/internal/clients/sonarqube_sdk"
"gitea-sonarqube-pr-bot/internal/settings"
webhook "gitea-sonarqube-pr-bot/internal/webhooks/sonarqube"
@ -33,8 +34,8 @@ type SQSdkMock struct {
mock.Mock
}
func (h *SQSdkMock) GetMeasures(project string, branch string) (string, error) {
return "", nil
func (h *SQSdkMock) GetMeasures(project string, branch string) (*sqSDK.MeasuresResponse, error) {
return &sqSDK.MeasuresResponse{}, nil
}
func defaultMockPreparation(h *HandlerPartialMock) {

View file

@ -33,6 +33,15 @@ type Webhook struct {
PRIndex int
}
func (w *Webhook) GetRenderedQualityGate() string {
status := ":white_check_mark:"
if w.QualityGate.Status != "OK" {
status = ":x:"
}
return fmt.Sprintf("**Quality Gate**: %s", status)
}
func New(raw []byte) (*Webhook, bool) {
v := viper.New()
v.SetConfigType("json")
@ -61,8 +70,8 @@ func parsePRIndex(w *Webhook) (int, error) {
re := regexp.MustCompile(`^PR-(\d+)$`)
res := re.FindSubmatch([]byte(w.Branch.Name))
if len(res) != 2 {
return 0, fmt.Errorf("Branch name '%s' does not match regex '%s'.", w.Branch.Name, re.String())
return 0, fmt.Errorf("branch name '%s' does not match regex '%s'", w.Branch.Name, re.String())
}
return strconv.Atoi(fmt.Sprintf("%s", res[1]))
return strconv.Atoi(string(res[1]))
}