Dashboards: Fix creating dashboard under folder using deprecated API (#77501)

* Dashboards: Add integration tests for creating a dashboard

* Fix creating dashboard under folder using deprecated API

* Update swagger response

* Fix comments
This commit is contained in:
Sofia Papagiannaki 2023-11-01 17:01:54 +02:00 committed by GitHub
parent 8b43d1b1ab
commit dfc33a70b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 185 additions and 9 deletions

View File

@ -527,6 +527,7 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
"id": dashboard.ID,
"uid": dashboard.UID,
"url": dashboard.GetURL(),
"folderUid": dashboard.FolderUID,
})
}
@ -1301,6 +1302,10 @@ type PostDashboardResponse struct {
// required: true
// example: /d/nHz3SXiiz/my-dashboard
URL string `json:"url"`
// FolderUID The unique identifier (uid) of the folder the dashboard belongs to.
// required: false
FolderUID string `json:"folderUid"`
} `json:"body"`
}

View File

@ -116,7 +116,7 @@ func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.
var dashFolder *folder.Folder
var err error
switch {
case cmd.UID != nil:
case cmd.UID != nil && *cmd.UID != "":
dashFolder, err = s.getFolderByUID(ctx, cmd.OrgID, *cmd.UID)
if err != nil {
return nil, err

View File

@ -13,9 +13,9 @@ type Service interface {
Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error)
// GetFolder takes a GetFolderCommand and returns a folder matching the
// request. One of ID, UID, or Title must be included. If multiple values
// request. One of UID, ID or Title must be included. If multiple values
// are included in the request, Grafana will select one in order of
// specificity (ID, UID, Title).
// specificity (UID, ID, Title).
Get(ctx context.Context, cmd *GetFolderQuery) (*Folder, error)
// Update is used to update a folder's UID, Title and Description. To change

View File

@ -15,9 +15,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgimpl"
"github.com/grafana/grafana/pkg/services/plugindashboards"
@ -28,6 +30,7 @@ import (
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/tests/testinfra"
"github.com/grafana/grafana/pkg/util"
)
func TestIntegrationDashboardQuota(t *testing.T) {
@ -272,3 +275,163 @@ providers:
})
})
}
func TestIntegrationCreate(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableAnonymous: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
// Create user
createUser(t, store, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleAdmin),
Password: "admin",
Login: "admin",
})
t.Run("create dashboard should succeed", func(t *testing.T) {
dashboardDataOne, err := simplejson.NewJson([]byte(`{"title":"just testing"}`))
require.NoError(t, err)
buf1 := &bytes.Buffer{}
err = json.NewEncoder(buf1).Encode(dashboards.SaveDashboardCommand{
Dashboard: dashboardDataOne,
})
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/db", grafanaListedAddr)
// nolint:gosec
resp, err := http.Post(u, "application/json", buf1)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var m util.DynMap
err = json.Unmarshal(b, &m)
require.NoError(t, err)
assert.NotEmpty(t, m["id"])
assert.NotEmpty(t, m["uid"])
})
t.Run("create dashboard under folder should succeed", func(t *testing.T) {
folder := createFolder(t, grafanaListedAddr, "test folder")
dashboardDataOne, err := simplejson.NewJson([]byte(`{"title":"just testing"}`))
require.NoError(t, err)
buf1 := &bytes.Buffer{}
err = json.NewEncoder(buf1).Encode(dashboards.SaveDashboardCommand{
Dashboard: dashboardDataOne,
FolderUID: folder.Uid,
})
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/db", grafanaListedAddr)
// nolint:gosec
resp, err := http.Post(u, "application/json", buf1)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var m util.DynMap
err = json.Unmarshal(b, &m)
require.NoError(t, err)
assert.NotEmpty(t, m["id"])
assert.NotEmpty(t, m["uid"])
assert.Equal(t, folder.Uid, m["folderUid"])
})
t.Run("create dashboard under folder (using deprecated folder sequential ID) should succeed", func(t *testing.T) {
folder := createFolder(t, grafanaListedAddr, "test folder 2")
dashboardDataOne, err := simplejson.NewJson([]byte(`{"title":"just testing"}`))
require.NoError(t, err)
buf1 := &bytes.Buffer{}
err = json.NewEncoder(buf1).Encode(dashboards.SaveDashboardCommand{
Dashboard: dashboardDataOne,
FolderID: folder.Id,
})
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/db", grafanaListedAddr)
// nolint:gosec
resp, err := http.Post(u, "application/json", buf1)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var m util.DynMap
err = json.Unmarshal(b, &m)
require.NoError(t, err)
assert.NotEmpty(t, m["id"])
assert.NotEmpty(t, m["uid"])
assert.Equal(t, folder.Uid, m["folderUid"])
})
t.Run("create dashboard under unknow folder should fail", func(t *testing.T) {
folderUID := "unknown"
// Import dashboard
dashboardDataOne, err := simplejson.NewJson([]byte(`{"title":"just testing"}`))
require.NoError(t, err)
buf1 := &bytes.Buffer{}
err = json.NewEncoder(buf1).Encode(dashboards.SaveDashboardCommand{
Dashboard: dashboardDataOne,
FolderUID: folderUID,
})
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/dashboards/db", grafanaListedAddr)
// nolint:gosec
resp, err := http.Post(u, "application/json", buf1)
require.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var m util.DynMap
err = json.Unmarshal(b, &m)
require.NoError(t, err)
assert.Equal(t, "Folder not found", m["message"])
})
}
func createFolder(t *testing.T, grafanaListedAddr string, title string) *dtos.Folder {
t.Helper()
buf1 := &bytes.Buffer{}
err := json.NewEncoder(buf1).Encode(folder.CreateFolderCommand{
Title: title,
})
require.NoError(t, err)
u := fmt.Sprintf("http://admin:admin@%s/api/folders", grafanaListedAddr)
// nolint:gosec
resp, err := http.Post(u, "application/json", buf1)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
var f *dtos.Folder
err = json.Unmarshal(b, &f)
require.NoError(t, err)
return f
}

View File

@ -22456,6 +22456,10 @@
"url"
],
"properties": {
"folderUid": {
"description": "FolderUID The unique identifier (uid) of the folder the dashboard belongs to.",
"type": "string"
},
"id": {
"description": "ID The unique identifier (id) of the created/updated dashboard.",
"type": "integer",

View File

@ -1579,6 +1579,10 @@
"application/json": {
"schema": {
"properties": {
"folderUid": {
"description": "FolderUID The unique identifier (uid) of the folder the dashboard belongs to.",
"type": "string"
},
"id": {
"description": "ID The unique identifier (id) of the created/updated dashboard.",
"example": 1,