public dashboards: insert default public dashboard config into database on save (#49131)

This PR adds endpoints for saving and retrieving a public dashboard configuration and and api endpoint to retrieve the public dashboard.

All of this is highly experimental and APIs will change. Notably, we will be removing isPublic from the dashboard model and moving it over to the public dashboard table in the next release.

Further context can be found here: https://github.com/grafana/grafana/pull/49131#issuecomment-1145456952
This commit is contained in:
Jeff Levin
2022-06-02 18:27:23 -08:00
committed by GitHub
parent efca93a3f3
commit 52ed651958
22 changed files with 1103 additions and 318 deletions

View File

@ -0,0 +1,227 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
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)
})
testCases := []struct {
name string
uid string
expectedHttpResponse int
publicDashboardResult *models.Dashboard
publicDashboardErr error
}{
{
name: "It gets a public dashboard",
uid: "pubdash-abcd1234",
expectedHttpResponse: http.StatusOK,
publicDashboardResult: &models.Dashboard{
Uid: "dashboard-abcd1234",
},
publicDashboardErr: nil,
},
{
name: "It should return 404 if isPublicDashboard is false",
uid: "pubdash-abcd1234",
expectedHttpResponse: http.StatusNotFound,
publicDashboardResult: nil,
publicDashboardErr: models.ErrPublicDashboardNotFound,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
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,
fmt.Sprintf("/api/public/dashboards/%v", test.uid),
nil,
t,
)
assert.Equal(t, test.expectedHttpResponse, response.Code)
if test.publicDashboardErr == nil {
var dashResp models.Dashboard
err := json.Unmarshal(response.Body.Bytes(), &dashResp)
require.NoError(t, err)
assert.Equal(t, test.publicDashboardResult.Uid, dashResp.Uid)
} 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) {
pdc := &models.PublicDashboardConfig{IsPublic: true}
testCases := []struct {
name string
dashboardUid string
expectedHttpResponse int
publicDashboardConfigResult *models.PublicDashboardConfig
publicDashboardConfigError error
}{
{
name: "retrieves public dashboard config when dashboard is found",
dashboardUid: "1",
expectedHttpResponse: http.StatusOK,
publicDashboardConfigResult: pdc,
publicDashboardConfigError: nil,
},
{
name: "returns 404 when dashboard not found",
dashboardUid: "77777",
expectedHttpResponse: http.StatusNotFound,
publicDashboardConfigResult: nil,
publicDashboardConfigError: models.ErrDashboardNotFound,
},
{
name: "returns 500 when internal server error",
dashboardUid: "1",
expectedHttpResponse: http.StatusInternalServerError,
publicDashboardConfigResult: nil,
publicDashboardConfigError: errors.New("database broken"),
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
dashSvc := dashboards.NewFakeDashboardService(t)
dashSvc.On("GetPublicDashboardConfig", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
Return(test.publicDashboardConfigResult, test.publicDashboardConfigError)
sc.hs.dashboardService = dashSvc
setInitCtxSignedInViewer(sc.initCtx)
response := callAPI(
sc.server,
http.MethodGet,
"/api/dashboards/uid/1/public-config",
nil,
t,
)
assert.Equal(t, test.expectedHttpResponse, response.Code)
if response.Code == http.StatusOK {
var pdcResp models.PublicDashboardConfig
err := json.Unmarshal(response.Body.Bytes(), &pdcResp)
require.NoError(t, err)
assert.Equal(t, test.publicDashboardConfigResult, &pdcResp)
}
})
}
}
func TestApiSavePublicDashboardConfig(t *testing.T) {
testCases := []struct {
name string
dashboardUid string
publicDashboardConfig *models.PublicDashboardConfig
expectedHttpResponse int
saveDashboardError error
}{
{
name: "returns 200 when update persists",
dashboardUid: "1",
publicDashboardConfig: &models.PublicDashboardConfig{IsPublic: true},
expectedHttpResponse: http.StatusOK,
saveDashboardError: nil,
},
{
name: "returns 500 when not persisted",
expectedHttpResponse: http.StatusInternalServerError,
publicDashboardConfig: &models.PublicDashboardConfig{},
saveDashboardError: errors.New("backend failed to save"),
},
{
name: "returns 404 when dashboard not found",
expectedHttpResponse: http.StatusNotFound,
publicDashboardConfig: &models.PublicDashboardConfig{},
saveDashboardError: models.ErrDashboardNotFound,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
sc := setupHTTPServerWithMockDb(t, false, false, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards))
dashSvc := dashboards.NewFakeDashboardService(t)
dashSvc.On("SavePublicDashboardConfig", mock.Anything, mock.AnythingOfType("*dashboards.SavePublicDashboardConfigDTO")).
Return(&models.PublicDashboardConfig{IsPublic: true}, test.saveDashboardError)
sc.hs.dashboardService = dashSvc
setInitCtxSignedInViewer(sc.initCtx)
response := callAPI(
sc.server,
http.MethodPost,
"/api/dashboards/uid/1/public-config",
strings.NewReader(`{ "isPublic": true }`),
t,
)
assert.Equal(t, test.expectedHttpResponse, response.Code)
// 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())
}
})
}
}