2022-06-02 21:27:23 -05:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2022-06-13 18:23:56 -05:00
|
|
|
"context"
|
2022-06-02 21:27:23 -05:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2022-06-13 18:23:56 -05:00
|
|
|
"io/ioutil"
|
2022-06-02 21:27:23 -05:00
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
"github.com/gofrs/uuid"
|
2022-06-27 11:23:15 -05:00
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
2022-06-02 21:27:23 -05:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2022-06-06 17:19:34 -05:00
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
2022-06-22 16:58:52 -05:00
|
|
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
2022-06-02 21:27:23 -05:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2022-06-27 11:23:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
|
|
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
2022-06-22 16:58:52 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/datasources/service"
|
2022-06-02 21:27:23 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
2022-06-13 18:23:56 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/query"
|
2022-06-22 16:58:52 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2022-06-13 18:23:56 -05:00
|
|
|
"github.com/grafana/grafana/pkg/web/webtest"
|
2022-06-02 21:27:23 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestAPIGetPublicDashboard(t *testing.T) {
|
|
|
|
t.Run("It should 404 if featureflag is not enabled", func(t *testing.T) {
|
|
|
|
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures())
|
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
|
|
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
|
|
|
Return(&models.Dashboard{}, nil).Maybe()
|
|
|
|
sc.hs.dashboardService = dashSvc
|
|
|
|
|
|
|
|
setInitCtxSignedInViewer(sc.initCtx)
|
|
|
|
response := callAPI(
|
|
|
|
sc.server,
|
|
|
|
http.MethodGet,
|
|
|
|
"/api/public/dashboards",
|
|
|
|
nil,
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
assert.Equal(t, http.StatusNotFound, response.Code)
|
|
|
|
response = callAPI(
|
|
|
|
sc.server,
|
|
|
|
http.MethodGet,
|
|
|
|
"/api/public/dashboards/asdf",
|
|
|
|
nil,
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
assert.Equal(t, http.StatusNotFound, response.Code)
|
|
|
|
})
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
DashboardUid := "dashboard-abcd1234"
|
|
|
|
token, err := uuid.NewV4()
|
|
|
|
require.NoError(t, err)
|
|
|
|
accessToken := fmt.Sprintf("%x", token)
|
2022-06-06 17:19:34 -05:00
|
|
|
|
2022-06-02 21:27:23 -05:00
|
|
|
testCases := []struct {
|
2022-06-22 16:58:52 -05:00
|
|
|
Name string
|
|
|
|
AccessToken string
|
|
|
|
ExpectedHttpResponse int
|
2022-06-02 21:27:23 -05:00
|
|
|
publicDashboardResult *models.Dashboard
|
|
|
|
publicDashboardErr error
|
|
|
|
}{
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "It gets a public dashboard",
|
|
|
|
AccessToken: accessToken,
|
|
|
|
ExpectedHttpResponse: http.StatusOK,
|
2022-06-02 21:27:23 -05:00
|
|
|
publicDashboardResult: &models.Dashboard{
|
2022-06-06 17:19:34 -05:00
|
|
|
Data: simplejson.NewFromAny(map[string]interface{}{
|
2022-06-22 16:58:52 -05:00
|
|
|
"Uid": DashboardUid,
|
2022-06-06 17:19:34 -05:00
|
|
|
}),
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
publicDashboardErr: nil,
|
|
|
|
},
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "It should return 404 if isPublicDashboard is false",
|
|
|
|
AccessToken: accessToken,
|
|
|
|
ExpectedHttpResponse: http.StatusNotFound,
|
2022-06-02 21:27:23 -05:00
|
|
|
publicDashboardResult: nil,
|
2022-06-30 08:31:54 -05:00
|
|
|
publicDashboardErr: dashboards.ErrPublicDashboardNotFound,
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range testCases {
|
2022-06-22 16:58:52 -05:00
|
|
|
t.Run(test.Name, func(t *testing.T) {
|
2022-06-02 21:27:23 -05:00
|
|
|
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
|
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
|
|
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
|
|
|
Return(test.publicDashboardResult, test.publicDashboardErr)
|
|
|
|
sc.hs.dashboardService = dashSvc
|
|
|
|
|
|
|
|
setInitCtxSignedInViewer(sc.initCtx)
|
|
|
|
response := callAPI(
|
|
|
|
sc.server,
|
|
|
|
http.MethodGet,
|
2022-06-22 16:58:52 -05:00
|
|
|
fmt.Sprintf("/api/public/dashboards/%s", test.AccessToken),
|
2022-06-02 21:27:23 -05:00
|
|
|
nil,
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
2022-06-02 21:27:23 -05:00
|
|
|
|
|
|
|
if test.publicDashboardErr == nil {
|
2022-06-06 17:19:34 -05:00
|
|
|
var dashResp dtos.DashboardFullWithMeta
|
2022-06-02 21:27:23 -05:00
|
|
|
err := json.Unmarshal(response.Body.Bytes(), &dashResp)
|
|
|
|
require.NoError(t, err)
|
2022-06-06 17:19:34 -05:00
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
assert.Equal(t, DashboardUid, dashResp.Dashboard.Get("Uid").MustString())
|
2022-06-06 17:19:34 -05:00
|
|
|
assert.Equal(t, false, dashResp.Meta.CanEdit)
|
|
|
|
assert.Equal(t, false, dashResp.Meta.CanDelete)
|
|
|
|
assert.Equal(t, false, dashResp.Meta.CanSave)
|
2022-06-02 21:27:23 -05:00
|
|
|
} else {
|
|
|
|
var errResp struct {
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
err := json.Unmarshal(response.Body.Bytes(), &errResp)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, test.publicDashboardErr.Error(), errResp.Error)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAPIGetPublicDashboardConfig(t *testing.T) {
|
2022-06-22 16:58:52 -05:00
|
|
|
pubdash := &models.PublicDashboard{IsEnabled: true}
|
2022-06-02 21:27:23 -05:00
|
|
|
|
|
|
|
testCases := []struct {
|
2022-06-22 16:58:52 -05:00
|
|
|
Name string
|
|
|
|
DashboardUid string
|
|
|
|
ExpectedHttpResponse int
|
|
|
|
PublicDashboardResult *models.PublicDashboard
|
|
|
|
PublicDashboardError error
|
2022-06-02 21:27:23 -05:00
|
|
|
}{
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "retrieves public dashboard config when dashboard is found",
|
|
|
|
DashboardUid: "1",
|
|
|
|
ExpectedHttpResponse: http.StatusOK,
|
|
|
|
PublicDashboardResult: pubdash,
|
|
|
|
PublicDashboardError: nil,
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "returns 404 when dashboard not found",
|
|
|
|
DashboardUid: "77777",
|
|
|
|
ExpectedHttpResponse: http.StatusNotFound,
|
|
|
|
PublicDashboardResult: nil,
|
2022-06-30 08:31:54 -05:00
|
|
|
PublicDashboardError: dashboards.ErrDashboardNotFound,
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "returns 500 when internal server error",
|
|
|
|
DashboardUid: "1",
|
|
|
|
ExpectedHttpResponse: http.StatusInternalServerError,
|
|
|
|
PublicDashboardResult: nil,
|
|
|
|
PublicDashboardError: errors.New("database broken"),
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range testCases {
|
2022-06-22 16:58:52 -05:00
|
|
|
t.Run(test.Name, func(t *testing.T) {
|
2022-06-02 21:27:23 -05:00
|
|
|
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
|
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
|
|
dashSvc.On("GetPublicDashboardConfig", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
|
2022-06-22 16:58:52 -05:00
|
|
|
Return(test.PublicDashboardResult, test.PublicDashboardError)
|
2022-06-02 21:27:23 -05:00
|
|
|
sc.hs.dashboardService = dashSvc
|
|
|
|
|
|
|
|
setInitCtxSignedInViewer(sc.initCtx)
|
|
|
|
response := callAPI(
|
|
|
|
sc.server,
|
|
|
|
http.MethodGet,
|
|
|
|
"/api/dashboards/uid/1/public-config",
|
|
|
|
nil,
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
2022-06-02 21:27:23 -05:00
|
|
|
|
|
|
|
if response.Code == http.StatusOK {
|
2022-06-22 16:58:52 -05:00
|
|
|
var pdcResp models.PublicDashboard
|
2022-06-02 21:27:23 -05:00
|
|
|
err := json.Unmarshal(response.Body.Bytes(), &pdcResp)
|
|
|
|
require.NoError(t, err)
|
2022-06-22 16:58:52 -05:00
|
|
|
assert.Equal(t, test.PublicDashboardResult, &pdcResp)
|
2022-06-02 21:27:23 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestApiSavePublicDashboardConfig(t *testing.T) {
|
|
|
|
testCases := []struct {
|
2022-06-22 16:58:52 -05:00
|
|
|
Name string
|
|
|
|
DashboardUid string
|
|
|
|
publicDashboardConfig *models.PublicDashboard
|
|
|
|
ExpectedHttpResponse int
|
2022-06-02 21:27:23 -05:00
|
|
|
saveDashboardError error
|
|
|
|
}{
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "returns 200 when update persists",
|
|
|
|
DashboardUid: "1",
|
|
|
|
publicDashboardConfig: &models.PublicDashboard{IsEnabled: true},
|
|
|
|
ExpectedHttpResponse: http.StatusOK,
|
2022-06-02 21:27:23 -05:00
|
|
|
saveDashboardError: nil,
|
|
|
|
},
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "returns 500 when not persisted",
|
|
|
|
ExpectedHttpResponse: http.StatusInternalServerError,
|
|
|
|
publicDashboardConfig: &models.PublicDashboard{},
|
2022-06-02 21:27:23 -05:00
|
|
|
saveDashboardError: errors.New("backend failed to save"),
|
|
|
|
},
|
|
|
|
{
|
2022-06-22 16:58:52 -05:00
|
|
|
Name: "returns 404 when dashboard not found",
|
|
|
|
ExpectedHttpResponse: http.StatusNotFound,
|
|
|
|
publicDashboardConfig: &models.PublicDashboard{},
|
2022-06-30 08:31:54 -05:00
|
|
|
saveDashboardError: dashboards.ErrDashboardNotFound,
|
2022-06-02 21:27:23 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range testCases {
|
2022-06-22 16:58:52 -05:00
|
|
|
t.Run(test.Name, func(t *testing.T) {
|
2022-06-02 21:27:23 -05:00
|
|
|
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
|
|
|
|
|
|
|
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
|
|
dashSvc.On("SavePublicDashboardConfig", mock.Anything, mock.AnythingOfType("*dashboards.SavePublicDashboardConfigDTO")).
|
2022-06-22 16:58:52 -05:00
|
|
|
Return(&models.PublicDashboard{IsEnabled: true}, test.saveDashboardError)
|
2022-06-02 21:27:23 -05:00
|
|
|
sc.hs.dashboardService = dashSvc
|
|
|
|
|
|
|
|
setInitCtxSignedInViewer(sc.initCtx)
|
|
|
|
response := callAPI(
|
|
|
|
sc.server,
|
|
|
|
http.MethodPost,
|
|
|
|
"/api/dashboards/uid/1/public-config",
|
|
|
|
strings.NewReader(`{ "isPublic": true }`),
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
2022-06-02 21:27:23 -05:00
|
|
|
|
|
|
|
// check the result if it's a 200
|
|
|
|
if response.Code == http.StatusOK {
|
|
|
|
val, err := json.Marshal(test.publicDashboardConfig)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, string(val), response.Body.String())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2022-06-13 18:23:56 -05:00
|
|
|
|
|
|
|
// `/public/dashboards/:uid/query`` endpoint test
|
|
|
|
func TestAPIQueryPublicDashboard(t *testing.T) {
|
|
|
|
queryReturnsError := false
|
|
|
|
|
|
|
|
qds := query.ProvideService(
|
|
|
|
nil,
|
|
|
|
&fakeDatasources.FakeCacheService{
|
2022-06-27 11:23:15 -05:00
|
|
|
DataSources: []*datasources.DataSource{
|
2022-06-13 18:23:56 -05:00
|
|
|
{Uid: "mysqlds"},
|
|
|
|
{Uid: "promds"},
|
|
|
|
{Uid: "promds2"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
&fakePluginRequestValidator{},
|
|
|
|
&fakeDatasources.FakeDataSourceService{},
|
|
|
|
&fakePluginClient{
|
|
|
|
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
|
|
if queryReturnsError {
|
|
|
|
return nil, errors.New("error")
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := backend.Responses{}
|
|
|
|
|
|
|
|
for _, query := range req.Queries {
|
|
|
|
resp[query.RefID] = backend.DataResponse{
|
|
|
|
Frames: []*data.Frame{
|
|
|
|
{
|
|
|
|
RefID: query.RefID,
|
|
|
|
Name: "query-" + query.RefID,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &backend.QueryDataResponse{Responses: resp}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&fakeOAuthTokenService{},
|
|
|
|
)
|
|
|
|
|
|
|
|
setup := func(enabled bool) (*webtest.Server, *dashboards.FakeDashboardService) {
|
|
|
|
fakeDashboardService := &dashboards.FakeDashboardService{}
|
|
|
|
|
|
|
|
return SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
|
|
hs.queryDataService = qds
|
|
|
|
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards, enabled)
|
|
|
|
hs.dashboardService = fakeDashboardService
|
|
|
|
}), fakeDashboardService
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("Status code is 404 when feature toggle is disabled", func(t *testing.T) {
|
|
|
|
server, _ := setup(false)
|
|
|
|
|
|
|
|
req := server.NewPostRequest(
|
|
|
|
"/api/public/dashboards/abc123/panels/2/query",
|
|
|
|
strings.NewReader("{}"),
|
|
|
|
)
|
|
|
|
resp, err := server.SendJSON(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, resp.Body.Close())
|
|
|
|
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Status code is 400 when the panel ID is invalid", func(t *testing.T) {
|
|
|
|
server, _ := setup(true)
|
|
|
|
|
|
|
|
req := server.NewPostRequest(
|
|
|
|
"/api/public/dashboards/abc123/panels/notanumber/query",
|
|
|
|
strings.NewReader("{}"),
|
|
|
|
)
|
|
|
|
resp, err := server.SendJSON(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, resp.Body.Close())
|
|
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Returns query data when feature toggle is enabled", func(t *testing.T) {
|
|
|
|
server, fakeDashboardService := setup(true)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
fakeDashboardService.On("GetPublicDashboard", mock.Anything, mock.Anything).Return(&models.Dashboard{}, nil)
|
|
|
|
fakeDashboardService.On("GetPublicDashboardConfig", mock.Anything, mock.Anything, mock.Anything).Return(&models.PublicDashboard{}, nil)
|
|
|
|
|
2022-06-13 18:23:56 -05:00
|
|
|
fakeDashboardService.On(
|
|
|
|
"BuildPublicDashboardMetricRequest",
|
|
|
|
mock.Anything,
|
2022-06-22 16:58:52 -05:00
|
|
|
mock.Anything,
|
|
|
|
mock.Anything,
|
2022-06-13 18:23:56 -05:00
|
|
|
int64(2),
|
|
|
|
).Return(dtos.MetricRequest{
|
|
|
|
Queries: []*simplejson.Json{
|
|
|
|
simplejson.MustJson([]byte(`
|
|
|
|
{
|
|
|
|
"datasource": {
|
|
|
|
"type": "prometheus",
|
|
|
|
"uid": "promds"
|
|
|
|
},
|
|
|
|
"exemplar": true,
|
|
|
|
"expr": "query_2_A",
|
|
|
|
"interval": "",
|
|
|
|
"legendFormat": "",
|
|
|
|
"refId": "A"
|
|
|
|
}
|
|
|
|
`)),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
req := server.NewPostRequest(
|
|
|
|
"/api/public/dashboards/abc123/panels/2/query",
|
|
|
|
strings.NewReader("{}"),
|
|
|
|
)
|
|
|
|
resp, err := server.SendJSON(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.JSONEq(
|
|
|
|
t,
|
|
|
|
`{
|
|
|
|
"results": {
|
|
|
|
"A": {
|
|
|
|
"frames": [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"values": []
|
|
|
|
},
|
|
|
|
"schema": {
|
|
|
|
"fields": [],
|
|
|
|
"refId": "A",
|
|
|
|
"name": "query-A"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`,
|
|
|
|
string(bodyBytes),
|
|
|
|
)
|
|
|
|
require.NoError(t, resp.Body.Close())
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Status code is 500 when the query fails", func(t *testing.T) {
|
|
|
|
server, fakeDashboardService := setup(true)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
fakeDashboardService.On("GetPublicDashboard", mock.Anything, mock.Anything).Return(&models.Dashboard{}, nil)
|
|
|
|
fakeDashboardService.On("GetPublicDashboardConfig", mock.Anything, mock.Anything, mock.Anything).Return(&models.PublicDashboard{}, nil)
|
2022-06-13 18:23:56 -05:00
|
|
|
fakeDashboardService.On(
|
|
|
|
"BuildPublicDashboardMetricRequest",
|
|
|
|
mock.Anything,
|
2022-06-22 16:58:52 -05:00
|
|
|
mock.Anything,
|
|
|
|
mock.Anything,
|
2022-06-13 18:23:56 -05:00
|
|
|
int64(2),
|
|
|
|
).Return(dtos.MetricRequest{
|
|
|
|
Queries: []*simplejson.Json{
|
|
|
|
simplejson.MustJson([]byte(`
|
|
|
|
{
|
|
|
|
"datasource": {
|
|
|
|
"type": "prometheus",
|
|
|
|
"uid": "promds"
|
|
|
|
},
|
|
|
|
"exemplar": true,
|
|
|
|
"expr": "query_2_A",
|
|
|
|
"interval": "",
|
|
|
|
"legendFormat": "",
|
|
|
|
"refId": "A"
|
|
|
|
}
|
|
|
|
`)),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
req := server.NewPostRequest(
|
|
|
|
"/api/public/dashboards/abc123/panels/2/query",
|
|
|
|
strings.NewReader("{}"),
|
|
|
|
)
|
|
|
|
queryReturnsError = true
|
|
|
|
resp, err := server.SendJSON(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, resp.Body.Close())
|
|
|
|
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
|
|
|
queryReturnsError = false
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Status code is 200 when a panel has queries from multiple datasources", func(t *testing.T) {
|
|
|
|
server, fakeDashboardService := setup(true)
|
|
|
|
|
2022-06-22 16:58:52 -05:00
|
|
|
fakeDashboardService.On("GetPublicDashboard", mock.Anything, mock.Anything).Return(&models.Dashboard{}, nil)
|
|
|
|
fakeDashboardService.On("GetPublicDashboardConfig", mock.Anything, mock.Anything, mock.Anything).Return(&models.PublicDashboard{}, nil)
|
2022-06-13 18:23:56 -05:00
|
|
|
fakeDashboardService.On(
|
|
|
|
"BuildPublicDashboardMetricRequest",
|
|
|
|
mock.Anything,
|
2022-06-22 16:58:52 -05:00
|
|
|
mock.Anything,
|
|
|
|
mock.Anything,
|
2022-06-13 18:23:56 -05:00
|
|
|
int64(2),
|
|
|
|
).Return(dtos.MetricRequest{
|
|
|
|
Queries: []*simplejson.Json{
|
|
|
|
simplejson.MustJson([]byte(`
|
|
|
|
{
|
|
|
|
"datasource": {
|
|
|
|
"type": "prometheus",
|
|
|
|
"uid": "promds"
|
|
|
|
},
|
|
|
|
"exemplar": true,
|
|
|
|
"expr": "query_2_A",
|
|
|
|
"interval": "",
|
|
|
|
"legendFormat": "",
|
|
|
|
"refId": "A"
|
|
|
|
}
|
|
|
|
`)),
|
|
|
|
simplejson.MustJson([]byte(`
|
|
|
|
{
|
|
|
|
"datasource": {
|
|
|
|
"type": "prometheus",
|
|
|
|
"uid": "promds2"
|
|
|
|
},
|
|
|
|
"exemplar": true,
|
|
|
|
"expr": "query_2_B",
|
|
|
|
"interval": "",
|
|
|
|
"legendFormat": "",
|
|
|
|
"refId": "B"
|
|
|
|
}
|
|
|
|
`)),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
req := server.NewPostRequest(
|
|
|
|
"/api/public/dashboards/abc123/panels/2/query",
|
|
|
|
strings.NewReader("{}"),
|
|
|
|
)
|
|
|
|
resp, err := server.SendJSON(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.JSONEq(
|
|
|
|
t,
|
|
|
|
`{
|
|
|
|
"results": {
|
|
|
|
"A": {
|
|
|
|
"frames": [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"values": []
|
|
|
|
},
|
|
|
|
"schema": {
|
|
|
|
"fields": [],
|
|
|
|
"refId": "A",
|
|
|
|
"name": "query-A"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"B": {
|
|
|
|
"frames": [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"values": []
|
|
|
|
},
|
|
|
|
"schema": {
|
|
|
|
"fields": [],
|
|
|
|
"refId": "B",
|
|
|
|
"name": "query-B"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`,
|
|
|
|
string(bodyBytes),
|
|
|
|
)
|
|
|
|
require.NoError(t, resp.Body.Close())
|
|
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
})
|
|
|
|
}
|
2022-06-22 16:58:52 -05:00
|
|
|
|
|
|
|
func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T) {
|
|
|
|
config := setting.NewCfg()
|
|
|
|
db := sqlstore.InitTestDB(t)
|
|
|
|
scenario := setupHTTPServerWithCfgDb(t, false, false, config, db, db, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
|
|
|
|
scenario.initCtx.SkipCache = true
|
|
|
|
cacheService := service.ProvideCacheService(localcache.ProvideService(), db)
|
|
|
|
qds := query.ProvideService(
|
|
|
|
nil,
|
|
|
|
cacheService,
|
|
|
|
nil,
|
|
|
|
&fakePluginRequestValidator{},
|
|
|
|
&fakeDatasources.FakeDataSourceService{},
|
|
|
|
&fakePluginClient{
|
|
|
|
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
|
|
resp := backend.Responses{
|
|
|
|
"A": backend.DataResponse{
|
|
|
|
Frames: []*data.Frame{{}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return &backend.QueryDataResponse{Responses: resp}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&fakeOAuthTokenService{},
|
|
|
|
)
|
|
|
|
scenario.hs.queryDataService = qds
|
|
|
|
|
2022-06-27 11:23:15 -05:00
|
|
|
_ = db.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{
|
2022-06-22 16:58:52 -05:00
|
|
|
Uid: "ds1",
|
|
|
|
OrgId: 1,
|
|
|
|
Name: "laban",
|
2022-06-27 11:23:15 -05:00
|
|
|
Type: datasources.DS_MYSQL,
|
|
|
|
Access: datasources.DS_ACCESS_DIRECT,
|
2022-06-22 16:58:52 -05:00
|
|
|
Url: "http://test",
|
|
|
|
Database: "site",
|
|
|
|
ReadOnly: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create Dashboard
|
|
|
|
saveDashboardCmd := models.SaveDashboardCommand{
|
|
|
|
OrgId: 1,
|
|
|
|
FolderId: 1,
|
|
|
|
IsFolder: false,
|
|
|
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
|
|
|
"id": nil,
|
|
|
|
"title": "test",
|
|
|
|
"panels": []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"id": 1,
|
|
|
|
"targets": []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"datasource": map[string]string{
|
|
|
|
"type": "mysql",
|
|
|
|
"uid": "ds1",
|
|
|
|
},
|
|
|
|
"refId": "A",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
dashboard, _ := scenario.dashboardsStore.SaveDashboard(saveDashboardCmd)
|
|
|
|
|
|
|
|
// Create public dashboard
|
|
|
|
savePubDashboardCmd := &dashboards.SavePublicDashboardConfigDTO{
|
|
|
|
DashboardUid: dashboard.Uid,
|
|
|
|
OrgId: dashboard.OrgId,
|
|
|
|
PublicDashboard: &models.PublicDashboard{
|
|
|
|
IsEnabled: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
pubdash, err := scenario.hs.dashboardService.SavePublicDashboardConfig(context.Background(), savePubDashboardCmd)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
response := callAPI(
|
|
|
|
scenario.server,
|
|
|
|
http.MethodPost,
|
|
|
|
fmt.Sprintf("/api/public/dashboards/%s/panels/1/query", pubdash.AccessToken),
|
|
|
|
strings.NewReader(`{}`),
|
|
|
|
t,
|
|
|
|
)
|
|
|
|
|
|
|
|
require.Equal(t, http.StatusOK, response.Code)
|
|
|
|
bodyBytes, err := ioutil.ReadAll(response.Body)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.JSONEq(
|
|
|
|
t,
|
|
|
|
`{
|
|
|
|
"results": {
|
|
|
|
"A": {
|
|
|
|
"frames": [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"values": []
|
|
|
|
},
|
|
|
|
"schema": {
|
|
|
|
"fields": []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`,
|
|
|
|
string(bodyBytes),
|
|
|
|
)
|
|
|
|
}
|