Skip to content

Commit f27faef

Browse files
committed
Refactor
1 parent 72f5726 commit f27faef

File tree

5 files changed

+297
-287
lines changed

5 files changed

+297
-287
lines changed

pkg/plugin/annotations.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package plugin
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/url"
9+
"time"
10+
11+
"github.com/grafana/grafana-plugin-sdk-go/backend"
12+
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
13+
)
14+
15+
type annotationRequest struct {
16+
Range struct {
17+
From time.Time `json:"from"`
18+
To time.Time `json:"to"`
19+
} `json:"range"`
20+
Annotation struct {
21+
Query string `json:"query"`
22+
} `json:"annotation"`
23+
}
24+
25+
func setQueryParams(u *url.URL, areq *annotationRequest) *url.URL {
26+
q := u.Query()
27+
q.Set("order", `timestamp+`)
28+
q.Set("filter", fmt.Sprintf("timestamp.>=.%d,timestamp.<=.%d", areq.Range.From.UnixNano(), areq.Range.To.UnixNano()))
29+
u.RawQuery = q.Encode()
30+
return u
31+
}
32+
33+
func (d *Datasource) handleAnnotations(_ context.Context, creq *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
34+
35+
// Decode the incoming request from Grafana
36+
37+
log.DefaultLogger.Info("handleAnnotations", "req.Body", string(creq.Body))
38+
var areq annotationRequest
39+
if err := json.Unmarshal(creq.Body, &areq); err != nil {
40+
log.DefaultLogger.Error("handleAnnotations Error", "req.Body", string(creq.Body), "err", err.Error())
41+
return sender.Send(&backend.CallResourceResponse{
42+
Status: http.StatusBadRequest,
43+
Body: []byte("failed to unmarshal annotation request: " + err.Error()),
44+
})
45+
}
46+
47+
// Request detections from the Prequel API
48+
u := setQueryParams(d.url, &areq)
49+
50+
url := renderURL(u, "detections")
51+
log.DefaultLogger.With("url", url).Debug("Fetching detections for annotations")
52+
53+
resp, err := d.httpClient.Get(url)
54+
if err != nil {
55+
return sendCallResourceError(sender, "Failed request detections API", err)
56+
}
57+
defer resp.Body.Close()
58+
59+
if resp.StatusCode != http.StatusOK {
60+
return sendCallResourceError(sender, "Failed to fetch detections from API", fmt.Errorf("status code %d", resp.StatusCode))
61+
}
62+
63+
var dresp detectionResponse
64+
err = json.NewDecoder(resp.Body).Decode(&dresp)
65+
if err != nil {
66+
return sendCallResourceError(sender, "Failed to decode detections response", err)
67+
}
68+
69+
annotations := []map[string]any{}
70+
for _, item := range dresp.Items {
71+
annotations = append(annotations, map[string]interface{}{
72+
"time": item.Timestamp / int64(time.Millisecond), // Convert to milliseconds
73+
"title": item.RuleTitle,
74+
"text": fmt.Sprintf(`<a href="https://app-dev.prequel.dev/detections/%s">See full incident report</a>`, item.DetectionID),
75+
"tags": []string{item.Category},
76+
})
77+
}
78+
79+
// 3. Marshal the response and send it back to Grafana
80+
responseJSON, err := json.Marshal(annotations)
81+
if err != nil {
82+
return sendCallResourceError(sender, "Failed to marshal annotation response", err)
83+
}
84+
85+
return sender.Send(&backend.CallResourceResponse{
86+
Status: http.StatusOK,
87+
Body: responseJSON,
88+
Headers: map[string][]string{
89+
"Content-Type": {"application/json"},
90+
},
91+
})
92+
}
93+
94+
type detectionResponseItem struct {
95+
Timestamp int64 `json:"timestamp"`
96+
RuleTitle string `json:"rule_title"`
97+
DetectionID string `json:"detection_id"`
98+
Category string `json:"category"`
99+
Namespace string `json:"namespace"`
100+
ContainerName string `json:"container_name"`
101+
K8sObject string `json:"k8s_object"`
102+
}
103+
104+
type detectionResponse struct {
105+
Items []detectionResponseItem `json:"rows"`
106+
}
107+
108+
func sendCallResourceError(sender backend.CallResourceResponseSender, msg string, errs ...error) error {
109+
// Log the error details
110+
if len(errs) > 0 && errs[0] != nil {
111+
log.DefaultLogger.Error(msg, "error", errs[0].Error())
112+
}
113+
114+
return sender.Send(&backend.CallResourceResponse{
115+
Status: http.StatusBadRequest,
116+
Body: []byte(msg),
117+
})
118+
}

0 commit comments

Comments
 (0)