diff --git a/observability-lib/api/dashboard.go b/observability-lib/api/dashboard.go index 1a0620fbf6..7f47ff87b9 100644 --- a/observability-lib/api/dashboard.go +++ b/observability-lib/api/dashboard.go @@ -87,6 +87,67 @@ func (c *Client) PostDashboard(dashboard PostDashboardRequest) (PostDashboardRes return grafanaResp, resp, nil } +type DashboardPanel struct { + ID int `json:"id"` + Title string `json:"title"` + Type string `json:"type"` +} + +type dashboardPanelJSON struct { + ID int `json:"id"` + Title string `json:"title"` + Type string `json:"type"` + Panels []dashboardPanelJSON `json:"panels"` +} + +type getDashboardByUIDResponse struct { + Dashboard struct { + ID uint `json:"id"` + UID string `json:"uid"` + Title string `json:"title"` + Panels []dashboardPanelJSON `json:"panels"` + } `json:"dashboard"` +} + +func collectDashboardPanels(panels []dashboardPanelJSON) []DashboardPanel { + result := make([]DashboardPanel, 0, len(panels)) + for _, panel := range panels { + result = append(result, DashboardPanel{ + ID: panel.ID, + Title: panel.Title, + Type: panel.Type, + }) + if len(panel.Panels) > 0 { + result = append(result, collectDashboardPanels(panel.Panels)...) + } + } + return result +} + +// GetDashboardPanelsByUID returns all panels (including nested row panels) with their IDs and titles. +func (c *Client) GetDashboardPanelsByUID(uid string) ([]DashboardPanel, *resty.Response, error) { + var grafanaResp getDashboardByUIDResponse + + resp, err := c.resty.R(). + SetHeader("Accept", "application/json"). + SetResult(&grafanaResp). + Get("/api/dashboards/uid/" + uid) + + if err != nil { + return nil, resp, fmt.Errorf("error making API request: %w", err) + } + + statusCode := resp.StatusCode() + if statusCode == 404 { + return nil, resp, nil + } + if statusCode != 200 { + return nil, resp, fmt.Errorf("error fetching dashboard %q, received unexpected status code %d: %s", uid, statusCode, resp.String()) + } + + return collectDashboardPanels(grafanaResp.Dashboard.Panels), resp, nil +} + func (c *Client) DeleteDashboardByUID(uid string) (*resty.Response, error) { resp, err := c.resty.R(). SetHeader("Content-Type", "application/json"). diff --git a/observability-lib/api/dashboard_test.go b/observability-lib/api/dashboard_test.go new file mode 100644 index 0000000000..ce9842a9fb --- /dev/null +++ b/observability-lib/api/dashboard_test.go @@ -0,0 +1,82 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-resty/resty/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetDashboardPanelsByUID(t *testing.T) { + dashboardUID := "dashboard-uid" + expectedResponse := getDashboardByUIDResponse{ + Dashboard: struct { + ID uint `json:"id"` + UID string `json:"uid"` + Title string `json:"title"` + Panels []dashboardPanelJSON `json:"panels"` + }{ + ID: 10, + UID: dashboardUID, + Title: "Test Dashboard", + Panels: []dashboardPanelJSON{ + {ID: 1, Title: "Uptime", Type: "stat"}, + { + ID: 2, + Title: "Resource Usage", + Type: "row", + Panels: []dashboardPanelJSON{ + {ID: 3, Title: "CPU Usage", Type: "timeseries"}, + {ID: 4, Title: "Memory Usage", Type: "stat"}, + }, + }, + }, + }, + } + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/api/dashboards/uid/"+dashboardUID, r.URL.Path) + assert.Equal(t, "application/json", r.Header.Get("Accept")) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + assert.NoError(t, json.NewEncoder(w).Encode(expectedResponse)) + })) + defer ts.Close() + + client := &Client{ + resty: resty.New().SetBaseURL(ts.URL), + } + + panels, resp, err := client.GetDashboardPanelsByUID(dashboardUID) + + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode()) + assert.Equal(t, []DashboardPanel{ + {ID: 1, Title: "Uptime", Type: "stat"}, + {ID: 2, Title: "Resource Usage", Type: "row"}, + {ID: 3, Title: "CPU Usage", Type: "timeseries"}, + {ID: 4, Title: "Memory Usage", Type: "stat"}, + }, panels) +} + +func TestGetDashboardPanelsByUID_NotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + + client := &Client{ + resty: resty.New().SetBaseURL(ts.URL), + } + + panels, resp, err := client.GetDashboardPanelsByUID("missing-dashboard") + + require.NoError(t, err) + assert.Equal(t, http.StatusNotFound, resp.StatusCode()) + assert.Nil(t, panels) +}