2014-12-29 20:36:08 +08:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2021-06-15 22:08:27 +08:00
|
|
|
"context"
|
2015-02-03 22:04:35 +08:00
|
|
|
"encoding/json"
|
2020-07-21 17:12:01 +08:00
|
|
|
"errors"
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
"fmt"
|
2021-11-29 17:18:01 +08:00
|
|
|
"net/http"
|
2015-02-03 22:04:35 +08:00
|
|
|
"os"
|
2019-04-30 19:32:18 +08:00
|
|
|
"path/filepath"
|
2021-11-17 17:57:37 +08:00
|
|
|
"strconv"
|
2022-05-17 00:59:02 +08:00
|
|
|
"strings"
|
2015-02-03 22:04:35 +08:00
|
|
|
|
2025-04-24 01:54:35 +08:00
|
|
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
2025-01-21 17:06:55 +08:00
|
|
|
claims "github.com/grafana/authlib/types"
|
2025-04-24 01:54:35 +08:00
|
|
|
dashboardsV1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
2022-01-28 17:28:33 +08:00
|
|
|
"github.com/grafana/grafana/pkg/api/apierrors"
|
2015-02-05 17:37:13 +08:00
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
2021-01-15 21:43:20 +08:00
|
|
|
"github.com/grafana/grafana/pkg/api/response"
|
2024-06-13 12:11:35 +08:00
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
2017-06-06 06:14:40 +08:00
|
|
|
"github.com/grafana/grafana/pkg/components/dashdiffs"
|
2016-12-08 17:25:05 +08:00
|
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
2019-02-24 06:35:26 +08:00
|
|
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
2022-04-04 22:57:43 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
2023-01-27 15:50:36 +08:00
|
|
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
2021-10-11 20:30:59 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2025-03-26 20:26:14 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
2022-05-25 16:41:51 +08:00
|
|
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
2022-05-22 08:44:12 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
2025-01-15 13:15:58 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/folder"
|
2022-08-10 17:56:48 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/org"
|
2022-04-21 21:03:17 +08:00
|
|
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
2022-11-04 03:30:12 +08:00
|
|
|
publicdashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
2022-05-19 20:32:10 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/star"
|
2022-08-02 22:58:05 +08:00
|
|
|
"github.com/grafana/grafana/pkg/services/user"
|
2015-02-05 17:37:13 +08:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2021-10-11 20:30:59 +08:00
|
|
|
"github.com/grafana/grafana/pkg/web"
|
2014-12-29 20:36:08 +08:00
|
|
|
)
|
|
|
|
|
2018-09-22 16:50:00 +08:00
|
|
|
const (
|
|
|
|
anonString = "Anonymous"
|
|
|
|
)
|
|
|
|
|
2025-09-05 03:45:01 +08:00
|
|
|
func (hs *HTTPServer) isDashboardStarredByUser(c *contextmodel.ReqContext, dashUID string) (bool, error) {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.isDashboardStarredByUser")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2015-02-02 18:32:32 +08:00
|
|
|
if !c.IsSignedIn {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
if !c.IsIdentityType(claims.TypeUser) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
userID, err := c.GetInternalID()
|
2023-08-30 22:51:18 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2025-09-23 22:13:05 +08:00
|
|
|
query := star.IsStarredByUserQuery{UserID: userID, OrgID: c.OrgID, DashboardUID: dashUID}
|
2022-05-19 20:32:10 +08:00
|
|
|
return hs.starService.IsStarredByUser(c.Req.Context(), &query)
|
2015-02-02 18:32:32 +08:00
|
|
|
}
|
|
|
|
|
2021-01-15 21:43:20 +08:00
|
|
|
func dashboardGuardianResponse(err error) response.Response {
|
2017-06-20 01:47:44 +08:00
|
|
|
if err != nil {
|
2025-09-11 18:35:58 +08:00
|
|
|
return dashboardErrResponse(err, "Error while checking dashboard permissions")
|
2017-06-20 01:47:44 +08:00
|
|
|
}
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusForbidden, "Access denied to this dashboard", nil)
|
2017-06-20 01:47:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:route GET /dashboards/uid/{uid} dashboards getDashboardByUID
|
|
|
|
//
|
|
|
|
// Get dashboard by uid.
|
|
|
|
//
|
|
|
|
// Will return the dashboard given the dashboard unique identifier (uid).
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: dashboardResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
2025-03-08 08:20:02 +08:00
|
|
|
// 406: notAcceptableError
|
2022-07-27 21:54:37 +08:00
|
|
|
// 500: internalServerError
|
2025-01-15 13:15:58 +08:00
|
|
|
//
|
|
|
|
//nolint:gocyclo
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetDashboard")
|
2024-06-27 04:03:13 +08:00
|
|
|
defer span.End()
|
2024-08-30 14:26:15 +08:00
|
|
|
c.Req = c.Req.WithContext(ctx)
|
2024-06-27 04:03:13 +08:00
|
|
|
|
2021-10-11 20:30:59 +08:00
|
|
|
uid := web.Params(c.Req)[":uid"]
|
2025-04-10 20:42:23 +08:00
|
|
|
dash, rsp := hs.getDashboardHelper(ctx, c.GetOrgID(), 0, uid)
|
2017-06-18 06:24:38 +08:00
|
|
|
if rsp != nil {
|
|
|
|
return rsp
|
2014-12-29 20:36:08 +08:00
|
|
|
}
|
2022-07-20 07:44:41 +08:00
|
|
|
|
2025-03-08 08:20:02 +08:00
|
|
|
// v2 is not supported in /api
|
2025-03-04 12:47:45 +08:00
|
|
|
if strings.HasPrefix(dash.APIVersion, "v2") {
|
2025-04-10 20:42:23 +08:00
|
|
|
url := fmt.Sprintf("/apis/dashboard.grafana.app/%s/namespaces/%s/dashboards/%s", dash.APIVersion, hs.namespacer(c.GetOrgID()), dash.UID)
|
2025-03-08 08:20:02 +08:00
|
|
|
return response.Error(http.StatusNotAcceptable, "dashboard api version not supported, use "+url+" instead", nil)
|
2025-03-04 12:47:45 +08:00
|
|
|
}
|
|
|
|
|
2022-07-20 07:44:41 +08:00
|
|
|
var (
|
2022-12-22 23:31:54 +08:00
|
|
|
publicDashboardEnabled = false
|
|
|
|
err error
|
2022-07-20 07:44:41 +08:00
|
|
|
)
|
2022-11-04 03:30:12 +08:00
|
|
|
|
2023-12-19 18:43:54 +08:00
|
|
|
// If public dashboards is enabled and we have a public dashboard, update meta values
|
2024-11-20 22:36:19 +08:00
|
|
|
if hs.Cfg.PublicDashboardsEnabled {
|
2025-04-10 20:42:23 +08:00
|
|
|
publicDashboard, err := hs.PublicDashboardsApi.PublicDashboardService.FindByDashboardUid(ctx, c.GetOrgID(), dash.UID)
|
2022-11-04 03:30:12 +08:00
|
|
|
if err != nil && !errors.Is(err, publicdashboardModels.ErrPublicDashboardNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while retrieving public dashboards", err)
|
2022-07-20 07:44:41 +08:00
|
|
|
}
|
2022-11-04 03:30:12 +08:00
|
|
|
|
2024-02-22 16:34:14 +08:00
|
|
|
if publicDashboard != nil && (hs.License.FeatureEnabled(publicdashboardModels.FeaturePublicDashboardsEmailSharing) || publicDashboard.Share != publicdashboardModels.EmailShareType) {
|
2022-11-04 03:30:12 +08:00
|
|
|
publicDashboardEnabled = publicDashboard.IsEnabled
|
|
|
|
}
|
2022-07-20 07:44:41 +08:00
|
|
|
}
|
|
|
|
|
2021-01-04 21:41:17 +08:00
|
|
|
// When dash contains only keys id, uid that means dashboard data is not valid and json decode failed.
|
|
|
|
if dash.Data != nil {
|
|
|
|
isEmptyData := true
|
|
|
|
for k := range dash.Data.MustMap() {
|
|
|
|
if k != "id" && k != "uid" {
|
|
|
|
isEmptyData = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isEmptyData {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while loading dashboard, dashboard data is invalid", nil)
|
2021-01-04 21:41:17 +08:00
|
|
|
}
|
2025-01-07 07:35:02 +08:00
|
|
|
|
|
|
|
// the dashboard id is no longer set in the spec for unified storage, set it here to keep api compatibility
|
|
|
|
if dash.Data.Get("id").MustString() == "" {
|
|
|
|
dash.Data.Set("id", dash.ID)
|
|
|
|
}
|
2021-01-04 21:41:17 +08:00
|
|
|
}
|
2022-12-15 22:34:17 +08:00
|
|
|
|
2025-03-21 01:38:09 +08:00
|
|
|
dashScope := dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dash.UID)
|
|
|
|
writeEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashScope)
|
|
|
|
canSave, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, writeEvaluator)
|
|
|
|
canEdit := canSave
|
|
|
|
//nolint:staticcheck // ViewersCanEdit is deprecated but still used for backward compatibility
|
|
|
|
if hs.Cfg.ViewersCanEdit {
|
|
|
|
canEdit = true
|
2017-06-12 21:48:55 +08:00
|
|
|
}
|
2025-03-21 01:38:09 +08:00
|
|
|
deleteEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsDelete, dashScope)
|
|
|
|
canDelete, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, deleteEvaluator)
|
2025-03-21 18:32:27 +08:00
|
|
|
adminEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsPermissionsWrite, dashScope)
|
2025-03-21 01:38:09 +08:00
|
|
|
canAdmin, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, adminEvaluator)
|
2017-06-18 06:24:38 +08:00
|
|
|
|
2025-09-05 03:45:01 +08:00
|
|
|
isStarred, err := hs.isDashboardStarredByUser(c, dash.UID)
|
2017-06-12 21:48:55 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while checking if dashboard was starred by user", err)
|
2017-06-12 21:48:55 +08:00
|
|
|
}
|
2016-01-29 01:53:19 +08:00
|
|
|
// Finding creator and last updater of the dashboard
|
2018-09-22 16:50:00 +08:00
|
|
|
updater, creator := anonString, anonString
|
2016-01-28 13:55:54 +08:00
|
|
|
if dash.UpdatedBy > 0 {
|
2025-01-10 17:06:59 +08:00
|
|
|
updater = hs.getIdentityName(ctx, dash.OrgID, dash.UpdatedBy)
|
2016-01-28 14:00:24 +08:00
|
|
|
}
|
|
|
|
if dash.CreatedBy > 0 {
|
2025-01-10 17:06:59 +08:00
|
|
|
creator = hs.getIdentityName(ctx, dash.OrgID, dash.CreatedBy)
|
2016-01-28 14:00:24 +08:00
|
|
|
}
|
2015-12-18 19:38:49 +08:00
|
|
|
|
2025-03-26 00:32:49 +08:00
|
|
|
annotationPermissions := &dashboardsV1.AnnotationPermission{}
|
2024-06-27 04:03:13 +08:00
|
|
|
if hs.Features.IsEnabled(ctx, featuremgmt.FlagAnnotationPermissionUpdate) {
|
2023-11-23 18:47:37 +08:00
|
|
|
hs.getAnnotationPermissionsByScope(c, &annotationPermissions.Dashboard, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dash.UID))
|
|
|
|
} else {
|
|
|
|
hs.getAnnotationPermissionsByScope(c, &annotationPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard)
|
|
|
|
}
|
2023-07-10 20:14:21 +08:00
|
|
|
hs.getAnnotationPermissionsByScope(c, &annotationPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization)
|
2022-04-04 22:57:43 +08:00
|
|
|
|
2017-06-17 04:57:37 +08:00
|
|
|
meta := dtos.DashboardMeta{
|
2022-12-22 23:31:54 +08:00
|
|
|
IsStarred: isStarred,
|
|
|
|
Slug: dash.Slug,
|
2023-01-18 20:52:41 +08:00
|
|
|
Type: dashboards.DashTypeDB,
|
2022-12-22 23:31:54 +08:00
|
|
|
CanStar: c.IsSignedIn,
|
|
|
|
CanSave: canSave,
|
|
|
|
CanEdit: canEdit,
|
|
|
|
CanAdmin: canAdmin,
|
|
|
|
CanDelete: canDelete,
|
|
|
|
Created: dash.Created,
|
|
|
|
Updated: dash.Updated,
|
|
|
|
UpdatedBy: updater,
|
|
|
|
CreatedBy: creator,
|
|
|
|
Version: dash.Version,
|
2025-03-04 12:47:45 +08:00
|
|
|
APIVersion: dash.APIVersion,
|
2022-12-22 23:31:54 +08:00
|
|
|
HasACL: dash.HasACL,
|
|
|
|
IsFolder: dash.IsFolder,
|
2023-11-07 00:31:44 +08:00
|
|
|
FolderId: dash.FolderID, // nolint:staticcheck
|
2023-01-16 23:33:55 +08:00
|
|
|
Url: dash.GetURL(),
|
2022-12-22 23:31:54 +08:00
|
|
|
FolderTitle: "General",
|
|
|
|
AnnotationsPermissions: annotationPermissions,
|
|
|
|
PublicDashboardEnabled: publicDashboardEnabled,
|
2017-06-17 04:57:37 +08:00
|
|
|
}
|
2024-01-24 19:39:11 +08:00
|
|
|
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetDashboard).Inc()
|
2025-01-15 13:15:58 +08:00
|
|
|
// lookup folder title & url
|
2025-07-30 05:52:57 +08:00
|
|
|
if dash.FolderUID != "" {
|
2025-01-15 13:15:58 +08:00
|
|
|
queryResult, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
2025-04-10 20:42:23 +08:00
|
|
|
OrgID: c.GetOrgID(),
|
2025-01-15 13:15:58 +08:00
|
|
|
UID: &dash.FolderUID,
|
|
|
|
SignedInUser: c.SignedInUser,
|
|
|
|
})
|
|
|
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
|
|
|
return response.Error(http.StatusNotFound, "Folder not found", err)
|
|
|
|
}
|
2025-02-08 06:19:23 +08:00
|
|
|
if apierrors.IsForbidden(err) {
|
|
|
|
// the dashboard is in a folder the user can't access, so return the dashboard without folder info
|
|
|
|
err = nil
|
|
|
|
queryResult = &folder.Folder{
|
|
|
|
UID: dash.FolderUID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
hs.log.Error("Failed to get dashboard folder", "error", err)
|
|
|
|
return response.Error(http.StatusInternalServerError, "Dashboard folder could not be read", err)
|
|
|
|
}
|
|
|
|
|
2025-01-15 13:15:58 +08:00
|
|
|
meta.FolderUid = queryResult.UID
|
|
|
|
meta.FolderTitle = queryResult.Title
|
|
|
|
meta.FolderId = queryResult.ID // nolint:staticcheck
|
|
|
|
queryResult = queryResult.WithURL()
|
|
|
|
meta.FolderUrl = queryResult.URL
|
|
|
|
} else if dash.FolderID > 0 { // nolint:staticcheck
|
|
|
|
query := dashboards.GetDashboardQuery{ID: dash.FolderID, OrgID: c.SignedInUser.GetOrgID()} // nolint:staticcheck
|
2024-01-24 19:39:11 +08:00
|
|
|
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetDashboard).Inc()
|
2024-06-27 04:03:13 +08:00
|
|
|
queryResult, err := hs.DashboardService.GetDashboard(ctx, &query)
|
2023-01-25 17:36:26 +08:00
|
|
|
if err != nil {
|
2022-06-30 21:31:54 +08:00
|
|
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusNotFound, "Folder not found", err)
|
2021-05-26 22:20:13 +08:00
|
|
|
}
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Dashboard folder could not be read", err)
|
2017-06-17 04:57:37 +08:00
|
|
|
}
|
2023-01-25 17:36:26 +08:00
|
|
|
meta.FolderUid = queryResult.UID
|
|
|
|
meta.FolderTitle = queryResult.Title
|
|
|
|
meta.FolderUrl = queryResult.GetURL()
|
2017-06-17 04:57:37 +08:00
|
|
|
}
|
|
|
|
|
2024-06-27 04:03:13 +08:00
|
|
|
provisioningData, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(ctx, dash.ID)
|
2018-04-10 15:31:35 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while checking if dashboard is provisioned", err)
|
2018-04-10 15:31:35 +08:00
|
|
|
}
|
|
|
|
|
2019-04-30 19:32:18 +08:00
|
|
|
if provisioningData != nil {
|
2020-04-15 14:12:52 +08:00
|
|
|
allowUIUpdate := hs.ProvisioningService.GetAllowUIUpdatesFromConfig(provisioningData.Name)
|
|
|
|
if !allowUIUpdate {
|
2019-10-31 21:27:31 +08:00
|
|
|
meta.Provisioned = true
|
|
|
|
}
|
|
|
|
|
2019-04-30 19:32:18 +08:00
|
|
|
meta.ProvisionedExternalId, err = filepath.Rel(
|
|
|
|
hs.ProvisioningService.GetDashboardProvisionerResolvedPath(provisioningData.Name),
|
2023-01-18 20:52:41 +08:00
|
|
|
provisioningData.ExternalID,
|
2019-04-30 19:32:18 +08:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
// Not sure when this could happen so not sure how to better handle this. Right now ProvisionedExternalId
|
|
|
|
// is for better UX, showing in Save/Delete dialogs and so it won't break anything if it is empty.
|
|
|
|
hs.log.Warn("Failed to create ProvisionedExternalId", "err", err)
|
|
|
|
}
|
2018-02-28 01:51:04 +08:00
|
|
|
}
|
|
|
|
|
2017-06-06 21:40:10 +08:00
|
|
|
// make sure db version is in sync with json model version
|
|
|
|
dash.Data.Set("version", dash.Version)
|
|
|
|
|
2015-05-04 14:36:44 +08:00
|
|
|
dto := dtos.DashboardFullWithMeta{
|
|
|
|
Dashboard: dash.Data,
|
2017-06-17 04:57:37 +08:00
|
|
|
Meta: meta,
|
2015-01-29 19:10:34 +08:00
|
|
|
}
|
2014-12-29 20:36:08 +08:00
|
|
|
|
2019-07-16 22:58:46 +08:00
|
|
|
c.TimeRequest(metrics.MApiDashboardGet)
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, dto)
|
2017-06-12 21:48:55 +08:00
|
|
|
}
|
|
|
|
|
2025-03-26 00:32:49 +08:00
|
|
|
func (hs *HTTPServer) getAnnotationPermissionsByScope(c *contextmodel.ReqContext, actions *dashboardsV1.AnnotationActions, scope string) {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.getAnnotationPermissionsByScope")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2022-04-04 22:57:43 +08:00
|
|
|
var err error
|
|
|
|
|
2022-04-05 19:20:10 +08:00
|
|
|
evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope)
|
|
|
|
actions.CanAdd, err = hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluate)
|
|
|
|
if err != nil {
|
|
|
|
hs.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsCreate, "scope", scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsDelete, scope)
|
2022-04-04 22:57:43 +08:00
|
|
|
actions.CanDelete, err = hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluate)
|
|
|
|
if err != nil {
|
|
|
|
hs.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsDelete, "scope", scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsWrite, scope)
|
|
|
|
actions.CanEdit, err = hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluate)
|
|
|
|
if err != nil {
|
|
|
|
hs.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsWrite, "scope", scope)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-10 17:06:59 +08:00
|
|
|
// getIdentityName returns name of either user or service account
|
|
|
|
func (hs *HTTPServer) getIdentityName(ctx context.Context, orgID, id int64) string {
|
|
|
|
ctx, span := tracer.Start(ctx, "api.getIdentityName")
|
2024-08-30 14:26:15 +08:00
|
|
|
defer span.End()
|
|
|
|
|
2025-01-10 17:06:59 +08:00
|
|
|
// We use GetSignedInUser here instead of GetByID so both user and service accounts are resolved.
|
|
|
|
ident, err := hs.userService.GetSignedInUser(ctx, &user.GetSignedInUserQuery{
|
|
|
|
UserID: id,
|
|
|
|
OrgID: orgID,
|
|
|
|
})
|
2016-01-28 14:00:24 +08:00
|
|
|
if err != nil {
|
2018-09-22 16:50:00 +08:00
|
|
|
return anonString
|
2016-01-28 14:00:24 +08:00
|
|
|
}
|
2025-01-10 17:06:59 +08:00
|
|
|
|
|
|
|
if ident.IsIdentityType(claims.TypeServiceAccount) {
|
|
|
|
return ident.GetName()
|
|
|
|
}
|
|
|
|
return ident.GetLogin()
|
2016-01-28 13:55:54 +08:00
|
|
|
}
|
|
|
|
|
2023-01-16 23:33:55 +08:00
|
|
|
func (hs *HTTPServer) getDashboardHelper(ctx context.Context, orgID int64, id int64, uid string) (*dashboards.Dashboard, response.Response) {
|
2024-08-06 09:17:39 +08:00
|
|
|
ctx, span := hs.tracer.Start(ctx, "api.getDashboardHelper")
|
|
|
|
defer span.End()
|
|
|
|
|
2023-01-16 23:33:55 +08:00
|
|
|
var query dashboards.GetDashboardQuery
|
2018-01-30 04:23:07 +08:00
|
|
|
|
|
|
|
if len(uid) > 0 {
|
2023-01-16 23:33:55 +08:00
|
|
|
query = dashboards.GetDashboardQuery{UID: uid, ID: id, OrgID: orgID}
|
2018-01-30 04:23:07 +08:00
|
|
|
} else {
|
2023-01-16 23:33:55 +08:00
|
|
|
query = dashboards.GetDashboardQuery{ID: id, OrgID: orgID}
|
2018-01-30 04:23:07 +08:00
|
|
|
}
|
|
|
|
|
2023-01-25 17:36:26 +08:00
|
|
|
queryResult, err := hs.DashboardService.GetDashboard(ctx, &query)
|
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return nil, response.Error(http.StatusNotFound, "Dashboard not found", err)
|
2017-06-18 06:24:38 +08:00
|
|
|
}
|
2018-01-29 20:51:01 +08:00
|
|
|
|
2023-01-25 17:36:26 +08:00
|
|
|
return queryResult, nil
|
2017-06-18 06:24:38 +08:00
|
|
|
}
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route DELETE /dashboards/uid/{uid} dashboards deleteDashboardByUID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Delete dashboard by uid.
|
|
|
|
//
|
|
|
|
// Will delete the dashboard given the specified unique identifier (uid).
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: deleteDashboardResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) DeleteDashboardByUID(c *contextmodel.ReqContext) response.Response {
|
2021-01-20 16:28:10 +08:00
|
|
|
return hs.deleteDashboard(c)
|
2019-04-10 19:29:10 +08:00
|
|
|
}
|
|
|
|
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) deleteDashboard(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.deleteDashboard")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2024-05-17 01:36:26 +08:00
|
|
|
uid := web.Params(c.Req)[":uid"]
|
|
|
|
|
2025-04-03 15:52:54 +08:00
|
|
|
var rsp response.Response
|
2025-04-10 20:42:23 +08:00
|
|
|
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.GetOrgID(), 0, uid)
|
2025-04-03 15:52:54 +08:00
|
|
|
if rsp != nil {
|
|
|
|
return rsp
|
2017-06-13 05:05:32 +08:00
|
|
|
}
|
2024-02-04 08:16:19 +08:00
|
|
|
if dash.IsFolder {
|
|
|
|
return response.Error(http.StatusBadRequest, "Use folders endpoint for deleting folders.", nil)
|
|
|
|
}
|
|
|
|
|
2021-05-12 14:48:17 +08:00
|
|
|
// disconnect all library elements for this dashboard
|
2025-03-21 01:38:09 +08:00
|
|
|
err := hs.LibraryElementService.DisconnectElementsFromDashboard(c.Req.Context(), dash.ID)
|
2021-05-12 14:48:17 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
hs.log.Error(
|
|
|
|
"Failed to disconnect library elements",
|
|
|
|
"dashboard", dash.ID,
|
2024-08-09 23:20:24 +08:00
|
|
|
"identity", c.GetID(),
|
2023-08-30 22:51:18 +08:00
|
|
|
"error", err)
|
2021-01-20 16:28:10 +08:00
|
|
|
}
|
2022-02-16 21:15:44 +08:00
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
err = hs.DashboardService.DeleteDashboard(c.Req.Context(), dash.ID, dash.UID, c.GetOrgID())
|
2020-07-21 17:12:01 +08:00
|
|
|
if err != nil {
|
2025-09-11 18:35:58 +08:00
|
|
|
return dashboardErrResponse(err, "Failed to delete dashboard")
|
2023-08-30 22:51:18 +08:00
|
|
|
}
|
|
|
|
|
2021-05-04 23:44:55 +08:00
|
|
|
if hs.Live != nil {
|
2025-04-10 20:42:23 +08:00
|
|
|
err := hs.Live.GrafanaScope.Dashboards.DashboardDeleted(c.GetOrgID(), c.SignedInUser, dash.UID)
|
2021-04-24 03:55:31 +08:00
|
|
|
if err != nil {
|
2023-01-16 23:33:55 +08:00
|
|
|
hs.log.Error("Failed to broadcast delete info", "dashboard", dash.UID, "error", err)
|
2021-04-24 03:55:31 +08:00
|
|
|
}
|
|
|
|
}
|
2024-05-07 02:17:34 +08:00
|
|
|
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, util.DynMap{
|
2018-02-21 23:38:09 +08:00
|
|
|
"title": dash.Title,
|
|
|
|
"message": fmt.Sprintf("Dashboard %s deleted", dash.Title),
|
2024-05-17 01:36:26 +08:00
|
|
|
"uid": dash.UID,
|
2018-02-21 23:38:09 +08:00
|
|
|
})
|
2014-12-29 20:36:08 +08:00
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:route POST /dashboards/db dashboards postDashboard
|
|
|
|
//
|
|
|
|
// Create / Update dashboard
|
|
|
|
//
|
|
|
|
// Creates a new dashboard or updates an existing dashboard.
|
2024-02-04 08:16:19 +08:00
|
|
|
// Note: This endpoint is not intended for creating folders, use `POST /api/folders` for that.
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: postDashboardResponse
|
|
|
|
// 400: badRequestError
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 412: preconditionFailedError
|
|
|
|
// 422: unprocessableEntityError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) PostDashboard(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.PostDashboard")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2023-01-16 23:33:55 +08:00
|
|
|
cmd := dashboards.SaveDashboardCommand{}
|
2021-11-29 17:18:01 +08:00
|
|
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
|
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
|
|
}
|
|
|
|
return hs.postDashboard(c, cmd)
|
|
|
|
}
|
|
|
|
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.SaveDashboardCommand) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.postDashboard")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2024-02-04 08:16:19 +08:00
|
|
|
if cmd.IsFolder {
|
|
|
|
return response.Error(http.StatusBadRequest, "Use folders endpoint for saving folders.", nil)
|
|
|
|
}
|
|
|
|
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx = c.Req.Context()
|
2021-04-28 19:38:33 +08:00
|
|
|
var err error
|
2023-08-30 22:51:18 +08:00
|
|
|
|
2024-08-09 23:20:24 +08:00
|
|
|
var userID int64
|
2025-04-10 20:42:23 +08:00
|
|
|
if id, err := identity.UserIdentifier(c.GetID()); err == nil {
|
2024-08-09 23:20:24 +08:00
|
|
|
userID = id
|
2023-08-30 22:51:18 +08:00
|
|
|
}
|
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
cmd.OrgID = c.GetOrgID()
|
2023-08-30 22:51:18 +08:00
|
|
|
cmd.UserID = userID
|
2021-05-26 22:20:13 +08:00
|
|
|
|
2015-07-20 22:38:25 +08:00
|
|
|
dash := cmd.GetDashboardModel()
|
2023-01-16 23:33:55 +08:00
|
|
|
newDashboard := dash.ID == 0
|
2019-03-05 23:44:41 +08:00
|
|
|
if newDashboard {
|
2022-11-15 03:08:10 +08:00
|
|
|
limitReached, err := hs.QuotaService.QuotaReached(c, dashboards.QuotaTargetSrv)
|
2015-07-20 22:38:25 +08:00
|
|
|
if err != nil {
|
2023-09-06 19:37:54 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to get quota", err)
|
2015-07-20 22:38:25 +08:00
|
|
|
}
|
|
|
|
if limitReached {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusForbidden, "Quota reached", nil)
|
2015-07-20 22:38:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 20:52:41 +08:00
|
|
|
var provisioningData *dashboards.DashboardProvisioning
|
2023-01-16 23:33:55 +08:00
|
|
|
if dash.ID != 0 {
|
|
|
|
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(c.Req.Context(), dash.ID)
|
2021-12-17 23:31:52 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while checking if dashboard is provisioned using ID", err)
|
2021-12-17 23:31:52 +08:00
|
|
|
}
|
|
|
|
provisioningData = data
|
2023-01-16 23:33:55 +08:00
|
|
|
} else if dash.UID != "" {
|
|
|
|
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(c.Req.Context(), dash.OrgID, dash.UID)
|
2022-06-30 21:31:54 +08:00
|
|
|
if err != nil && !errors.Is(err, dashboards.ErrProvisionedDashboardNotFound) && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while checking if dashboard is provisioned", err)
|
2021-12-17 23:31:52 +08:00
|
|
|
}
|
|
|
|
provisioningData = data
|
2019-10-31 21:27:31 +08:00
|
|
|
}
|
|
|
|
|
2019-11-01 14:57:03 +08:00
|
|
|
allowUiUpdate := true
|
|
|
|
if provisioningData != nil {
|
2020-04-15 14:12:52 +08:00
|
|
|
allowUiUpdate = hs.ProvisioningService.GetAllowUIUpdatesFromConfig(provisioningData.Name)
|
2019-11-01 14:57:03 +08:00
|
|
|
}
|
2019-10-31 21:27:31 +08:00
|
|
|
|
2018-01-23 19:28:56 +08:00
|
|
|
dashItem := &dashboards.SaveDashboardDTO{
|
2017-12-01 20:50:47 +08:00
|
|
|
Dashboard: dash,
|
|
|
|
Message: cmd.Message,
|
2025-04-10 20:42:23 +08:00
|
|
|
OrgID: c.GetOrgID(),
|
2018-02-19 18:12:56 +08:00
|
|
|
User: c.SignedInUser,
|
2017-12-12 20:18:00 +08:00
|
|
|
Overwrite: cmd.Overwrite,
|
2016-11-24 18:22:13 +08:00
|
|
|
}
|
|
|
|
|
2024-05-07 02:17:34 +08:00
|
|
|
dashboard, saveErr := hs.DashboardService.SaveDashboard(ctx, dashItem, allowUiUpdate)
|
2021-04-24 03:55:31 +08:00
|
|
|
|
2021-05-04 23:44:55 +08:00
|
|
|
if hs.Live != nil {
|
|
|
|
// Tell everyone listening that the dashboard changed
|
2021-04-24 03:55:31 +08:00
|
|
|
if dashboard == nil {
|
|
|
|
dashboard = dash // the original request
|
|
|
|
}
|
|
|
|
|
2021-10-15 16:33:01 +08:00
|
|
|
// This will broadcast all save requests only if a `gitops` observer exists.
|
2021-04-24 03:55:31 +08:00
|
|
|
// gitops is useful when trying to save dashboards in an environment where the user can not save
|
|
|
|
channel := hs.Live.GrafanaScope.Dashboards
|
2025-04-10 20:42:23 +08:00
|
|
|
liveerr := channel.DashboardSaved(c.GetOrgID(), c.SignedInUser, cmd.Message, dashboard, saveErr)
|
2021-04-24 03:55:31 +08:00
|
|
|
|
|
|
|
// When an error exists, but the value broadcast to a gitops listener return 202
|
2025-04-10 20:42:23 +08:00
|
|
|
if liveerr == nil && saveErr != nil && channel.HasGitOpsObserver(c.GetOrgID()) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.JSON(http.StatusAccepted, util.DynMap{
|
2021-04-24 03:55:31 +08:00
|
|
|
"status": "pending",
|
|
|
|
"message": "changes were broadcast to the gitops listener",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if liveerr != nil {
|
2023-09-06 19:37:54 +08:00
|
|
|
hs.log.Warn("Unable to broadcast save event", "uid", dashboard.UID, "error", liveerr)
|
2021-04-24 03:55:31 +08:00
|
|
|
}
|
|
|
|
}
|
2021-05-04 23:44:55 +08:00
|
|
|
|
2024-05-07 02:17:34 +08:00
|
|
|
if saveErr != nil {
|
|
|
|
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, saveErr)
|
2019-12-20 18:42:47 +08:00
|
|
|
}
|
|
|
|
|
2021-05-12 14:48:17 +08:00
|
|
|
// connect library panels for this dashboard after the dashboard is stored and has an ID
|
2021-11-17 19:04:22 +08:00
|
|
|
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(ctx, c.SignedInUser, dashboard)
|
2021-05-12 14:48:17 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Error while connecting library panels", err)
|
2021-01-20 16:28:10 +08:00
|
|
|
}
|
|
|
|
|
2019-12-20 18:42:47 +08:00
|
|
|
c.TimeRequest(metrics.MApiDashboardSave)
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, util.DynMap{
|
2023-11-01 23:01:54 +08:00
|
|
|
"status": "success",
|
|
|
|
"slug": dashboard.Slug,
|
|
|
|
"version": dashboard.Version,
|
|
|
|
"id": dashboard.ID,
|
|
|
|
"uid": dashboard.UID,
|
|
|
|
"url": dashboard.GetURL(),
|
|
|
|
"folderUid": dashboard.FolderUID,
|
2019-12-20 18:42:47 +08:00
|
|
|
})
|
|
|
|
}
|
2017-12-01 20:50:47 +08:00
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:route GET /dashboards/home dashboards getHomeDashboard
|
|
|
|
//
|
|
|
|
// Get home dashboard.
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: getHomeDashboardResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetHomeDashboard(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetHomeDashboard")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2024-08-09 23:20:24 +08:00
|
|
|
var userID int64
|
2025-04-10 20:42:23 +08:00
|
|
|
if id, err := identity.UserIdentifier(c.GetID()); err == nil {
|
2024-08-09 23:20:24 +08:00
|
|
|
userID = id
|
2023-08-30 22:51:18 +08:00
|
|
|
}
|
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
prefsQuery := pref.GetPreferenceWithDefaultsQuery{OrgID: c.GetOrgID(), UserID: userID, Teams: c.GetTeams()}
|
2021-04-13 21:27:51 +08:00
|
|
|
homePage := hs.Cfg.HomePage
|
|
|
|
|
2022-04-21 21:03:17 +08:00
|
|
|
preference, err := hs.preferenceService.GetWithDefaults(c.Req.Context(), &prefsQuery)
|
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to get preferences", err)
|
2016-03-17 17:29:34 +08:00
|
|
|
}
|
|
|
|
|
2025-06-13 20:10:44 +08:00
|
|
|
if preference.HomeDashboardUID == "" && len(homePage) > 0 {
|
2021-04-13 21:27:51 +08:00
|
|
|
homePageRedirect := dtos.DashboardRedirect{RedirectUri: homePage}
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, &homePageRedirect)
|
2021-04-13 21:27:51 +08:00
|
|
|
}
|
|
|
|
|
2025-06-13 20:10:44 +08:00
|
|
|
if preference.HomeDashboardUID != "" {
|
|
|
|
slugQueryResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &dashboards.GetDashboardQuery{UID: preference.HomeDashboardUID, OrgID: c.GetOrgID()})
|
2016-05-24 13:39:58 +08:00
|
|
|
if err == nil {
|
2025-06-13 20:10:44 +08:00
|
|
|
url := dashboards.GetDashboardURL(preference.HomeDashboardUID, slugQueryResult.Slug)
|
2018-02-05 17:24:48 +08:00
|
|
|
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, &dashRedirect)
|
2016-03-17 17:29:34 +08:00
|
|
|
}
|
2020-12-03 17:11:14 +08:00
|
|
|
hs.log.Warn("Failed to get slug from database", "err", err)
|
2016-03-17 17:29:34 +08:00
|
|
|
}
|
|
|
|
|
2020-06-23 00:00:39 +08:00
|
|
|
filePath := hs.Cfg.DefaultHomeDashboardPath
|
|
|
|
if filePath == "" {
|
2020-10-19 23:35:31 +08:00
|
|
|
filePath = filepath.Join(hs.Cfg.StaticRootPath, "dashboards/home.json")
|
2020-06-23 00:00:39 +08:00
|
|
|
}
|
|
|
|
|
2020-12-04 05:13:06 +08:00
|
|
|
// It's safe to ignore gosec warning G304 since the variable part of the file path comes from a configuration
|
|
|
|
// variable
|
|
|
|
// nolint:gosec
|
2015-02-03 22:04:35 +08:00
|
|
|
file, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to load home dashboard", err)
|
2015-02-03 22:04:35 +08:00
|
|
|
}
|
2020-12-03 17:11:14 +08:00
|
|
|
defer func() {
|
|
|
|
if err := file.Close(); err != nil {
|
|
|
|
hs.log.Warn("Failed to close dashboard file", "path", filePath, "err", err)
|
|
|
|
}
|
|
|
|
}()
|
2015-02-03 22:04:35 +08:00
|
|
|
|
2015-05-04 14:36:44 +08:00
|
|
|
dash := dtos.DashboardFullWithMeta{}
|
2025-04-10 20:42:23 +08:00
|
|
|
dash.Meta.CanEdit = c.HasRole(org.RoleEditor)
|
2018-02-02 17:33:31 +08:00
|
|
|
dash.Meta.FolderTitle = "General"
|
2022-04-12 15:30:34 +08:00
|
|
|
dash.Dashboard = simplejson.New()
|
2017-06-17 09:25:24 +08:00
|
|
|
|
2015-02-03 22:04:35 +08:00
|
|
|
jsonParser := json.NewDecoder(file)
|
2022-04-12 15:30:34 +08:00
|
|
|
if err := jsonParser.Decode(dash.Dashboard); err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to load home dashboard", err)
|
2015-02-03 22:04:35 +08:00
|
|
|
}
|
|
|
|
|
2020-11-02 23:12:22 +08:00
|
|
|
hs.addGettingStartedPanelToHomeDashboard(c, dash.Dashboard)
|
2016-12-08 17:25:05 +08:00
|
|
|
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, &dash)
|
2015-02-03 22:04:35 +08:00
|
|
|
}
|
2015-05-12 20:11:30 +08:00
|
|
|
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) addGettingStartedPanelToHomeDashboard(c *contextmodel.ReqContext, dash *simplejson.Json) {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.addGettingStartedPanelToHomeDashboard")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2020-11-02 23:12:22 +08:00
|
|
|
// We only add this getting started panel for Admins who have not dismissed it,
|
|
|
|
// and if a custom default home dashboard hasn't been configured
|
2022-08-10 17:56:48 +08:00
|
|
|
if !c.HasUserRole(org.RoleAdmin) ||
|
|
|
|
c.HasHelpFlag(user.HelpFlagGettingStartedPanelDismissed) ||
|
2020-11-02 23:12:22 +08:00
|
|
|
hs.Cfg.DefaultHomeDashboardPath != "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-08-25 20:47:57 +08:00
|
|
|
panels := dash.Get("panels").MustArray()
|
2016-12-08 17:25:05 +08:00
|
|
|
|
2023-08-30 23:46:47 +08:00
|
|
|
newpanel := simplejson.NewFromAny(map[string]any{
|
2017-10-11 01:48:06 +08:00
|
|
|
"type": "gettingstarted",
|
|
|
|
"id": 123123,
|
2023-08-30 23:46:47 +08:00
|
|
|
"gridPos": map[string]any{
|
2017-10-11 01:48:06 +08:00
|
|
|
"x": 0,
|
2019-01-28 22:01:42 +08:00
|
|
|
"y": 3,
|
2017-10-25 01:22:56 +08:00
|
|
|
"w": 24,
|
2020-05-13 14:00:40 +08:00
|
|
|
"h": 9,
|
2017-10-11 01:48:06 +08:00
|
|
|
},
|
2016-12-08 17:25:05 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
panels = append(panels, newpanel)
|
2017-08-25 20:47:57 +08:00
|
|
|
dash.Set("panels", panels)
|
2016-12-08 17:25:05 +08:00
|
|
|
}
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route GET /dashboards/id/{DashboardID}/versions dashboards versions getDashboardVersionsByID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Gets all existing versions for the dashboard.
|
|
|
|
//
|
2025-08-28 05:51:04 +08:00
|
|
|
// Please refer to [updated API](#/dashboards/getDashboardVersionsByUID) instead
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Deprecated: true
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: dashboardVersionsResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route GET /dashboards/uid/{uid}/versions dashboards versions getDashboardVersionsByUID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Gets all existing versions for the dashboard using UID.
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: dashboardVersionsResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetDashboardVersions(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetDashboardVersions")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2022-05-18 00:57:27 +08:00
|
|
|
var dashID int64
|
|
|
|
|
|
|
|
var err error
|
|
|
|
dashUID := web.Params(c.Req)[":uid"]
|
2017-06-14 06:28:34 +08:00
|
|
|
|
2022-05-18 00:57:27 +08:00
|
|
|
if dashUID == "" {
|
|
|
|
dashID, err = strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
|
|
|
|
}
|
|
|
|
}
|
2022-12-15 22:34:17 +08:00
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.GetOrgID(), dashID, dashUID)
|
2022-12-15 22:34:17 +08:00
|
|
|
if rsp != nil {
|
|
|
|
return rsp
|
|
|
|
}
|
|
|
|
|
2022-06-02 21:59:05 +08:00
|
|
|
query := dashver.ListDashboardVersionsQuery{
|
2025-04-10 20:42:23 +08:00
|
|
|
OrgID: c.GetOrgID(),
|
2025-01-28 22:17:52 +08:00
|
|
|
DashboardID: dash.ID,
|
|
|
|
DashboardUID: dash.UID,
|
|
|
|
Limit: c.QueryInt("limit"),
|
|
|
|
Start: c.QueryInt("start"),
|
|
|
|
ContinueToken: c.Query("continueToken"),
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2025-01-28 22:17:52 +08:00
|
|
|
resp, err := hs.dashboardVersionService.List(c.Req.Context(), &query)
|
2022-06-02 21:59:05 +08:00
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusNotFound, fmt.Sprintf("No versions found for dashboardId %d", dash.ID), err)
|
2017-06-02 05:57:09 +08:00
|
|
|
}
|
|
|
|
|
2025-01-28 22:17:52 +08:00
|
|
|
loginMem := make(map[int64]string, len(resp.Versions))
|
|
|
|
res := make([]dashver.DashboardVersionMeta, 0, len(resp.Versions))
|
|
|
|
for _, version := range resp.Versions {
|
2023-03-30 22:31:53 +08:00
|
|
|
msg := version.Message
|
2017-06-06 04:59:04 +08:00
|
|
|
if version.RestoredFrom == version.Version {
|
2023-03-30 22:31:53 +08:00
|
|
|
msg = "Initial save (created by migration)"
|
2017-06-06 04:59:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if version.RestoredFrom > 0 {
|
2023-03-30 22:31:53 +08:00
|
|
|
msg = fmt.Sprintf("Restored from version %d", version.RestoredFrom)
|
2017-06-06 04:59:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if version.ParentVersion == 0 {
|
2023-03-30 22:31:53 +08:00
|
|
|
msg = "Initial save"
|
2017-06-06 04:59:04 +08:00
|
|
|
}
|
2023-03-30 22:31:53 +08:00
|
|
|
|
|
|
|
creator := anonString
|
|
|
|
if version.CreatedBy > 0 {
|
|
|
|
login, found := loginMem[version.CreatedBy]
|
|
|
|
if found {
|
|
|
|
creator = login
|
|
|
|
} else {
|
2025-04-10 20:42:23 +08:00
|
|
|
creator = hs.getIdentityName(c.Req.Context(), c.GetOrgID(), version.CreatedBy)
|
2023-03-30 22:31:53 +08:00
|
|
|
if creator != anonString {
|
|
|
|
loginMem[version.CreatedBy] = creator
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, dashver.DashboardVersionMeta{
|
|
|
|
ID: version.ID,
|
|
|
|
DashboardID: version.DashboardID,
|
|
|
|
DashboardUID: dash.UID,
|
|
|
|
Data: version.Data,
|
|
|
|
ParentVersion: version.ParentVersion,
|
|
|
|
RestoredFrom: version.RestoredFrom,
|
|
|
|
Version: version.Version,
|
|
|
|
Created: version.Created,
|
|
|
|
Message: msg,
|
|
|
|
CreatedBy: creator,
|
|
|
|
})
|
2017-06-06 04:59:04 +08:00
|
|
|
}
|
|
|
|
|
2025-01-28 22:17:52 +08:00
|
|
|
return response.JSON(http.StatusOK, dashver.DashboardVersionResponseMeta{
|
|
|
|
Versions: res,
|
|
|
|
ContinueToken: resp.ContinueToken,
|
|
|
|
})
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route GET /dashboards/id/{DashboardID}/versions/{DashboardVersionID} dashboards versions getDashboardVersionByID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Get a specific dashboard version.
|
|
|
|
//
|
2025-08-28 05:51:04 +08:00
|
|
|
// Please refer to [updated API](#/dashboards/getDashboardVersionByUID) instead
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Deprecated: true
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: dashboardVersionResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route GET /dashboards/uid/{uid}/versions/{DashboardVersionID} dashboards versions getDashboardVersionByUID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Get a specific dashboard version using UID.
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: dashboardVersionResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetDashboardVersion")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2022-05-17 18:59:02 +08:00
|
|
|
var dashID int64
|
|
|
|
|
|
|
|
var err error
|
|
|
|
dashUID := web.Params(c.Req)[":uid"]
|
|
|
|
|
2023-01-16 23:33:55 +08:00
|
|
|
var dash *dashboards.Dashboard
|
2022-05-17 18:59:02 +08:00
|
|
|
if dashUID == "" {
|
|
|
|
dashID, err = strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
|
|
|
|
}
|
2022-01-15 00:55:57 +08:00
|
|
|
}
|
2017-06-14 06:28:34 +08:00
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.GetOrgID(), dashID, dashUID)
|
2022-12-15 22:34:17 +08:00
|
|
|
if rsp != nil {
|
|
|
|
return rsp
|
|
|
|
}
|
|
|
|
|
2025-01-28 22:17:52 +08:00
|
|
|
version, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return response.Err(err)
|
|
|
|
}
|
2022-05-25 16:41:51 +08:00
|
|
|
query := dashver.GetDashboardVersionQuery{
|
2025-04-10 20:42:23 +08:00
|
|
|
OrgID: c.GetOrgID(),
|
2023-02-08 01:27:38 +08:00
|
|
|
DashboardID: dash.ID,
|
|
|
|
DashboardUID: dash.UID,
|
2025-01-28 22:17:52 +08:00
|
|
|
Version: version,
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
2017-06-05 22:34:32 +08:00
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
res, err := hs.dashboardVersionService.Get(c.Req.Context(), &query)
|
|
|
|
if err != nil {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dash.ID), err)
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2018-09-22 16:50:00 +08:00
|
|
|
creator := anonString
|
2022-05-25 16:41:51 +08:00
|
|
|
if res.CreatedBy > 0 {
|
2025-01-10 17:06:59 +08:00
|
|
|
creator = hs.getIdentityName(c.Req.Context(), dash.OrgID, res.CreatedBy)
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2022-06-08 18:22:55 +08:00
|
|
|
dashVersionMeta := &dashver.DashboardVersionMeta{
|
|
|
|
ID: res.ID,
|
|
|
|
DashboardID: res.DashboardID,
|
2023-02-08 01:27:38 +08:00
|
|
|
DashboardUID: dash.UID,
|
2022-05-25 16:41:51 +08:00
|
|
|
Data: res.Data,
|
|
|
|
ParentVersion: res.ParentVersion,
|
|
|
|
RestoredFrom: res.RestoredFrom,
|
|
|
|
Version: res.Version,
|
|
|
|
Created: res.Created,
|
|
|
|
Message: res.Message,
|
2019-04-15 15:18:28 +08:00
|
|
|
CreatedBy: creator,
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.JSON(http.StatusOK, dashVersionMeta)
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:route POST /dashboards/calculate-diff dashboards calculateDashboardDiff
|
|
|
|
//
|
|
|
|
// Perform diff on two dashboards.
|
|
|
|
//
|
|
|
|
// Produces:
|
|
|
|
// - application/json
|
|
|
|
// - text/html
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: calculateDashboardDiffResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) CalculateDashboardDiff(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.CalculateDashboardDiff")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2021-11-29 17:18:01 +08:00
|
|
|
apiOptions := dtos.CalculateDiffOptions{}
|
|
|
|
if err := web.Bind(c.Req, &apiOptions); err != nil {
|
|
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
|
|
}
|
2022-12-15 22:34:17 +08:00
|
|
|
|
2025-03-21 01:38:09 +08:00
|
|
|
evaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScope(strconv.FormatInt(apiOptions.Base.DashboardId, 10)))
|
|
|
|
if canWrite, err := hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluator); err != nil || !canWrite {
|
2018-02-28 00:53:30 +08:00
|
|
|
return dashboardGuardianResponse(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if apiOptions.Base.DashboardId != apiOptions.New.DashboardId {
|
2025-03-21 01:38:09 +08:00
|
|
|
evaluator = accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScope(strconv.FormatInt(apiOptions.New.DashboardId, 10)))
|
|
|
|
if canWrite, err := hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluator); err != nil || !canWrite {
|
2018-02-28 00:53:30 +08:00
|
|
|
return dashboardGuardianResponse(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-07 17:50:09 +08:00
|
|
|
options := dashdiffs.Options{
|
2025-04-10 20:42:23 +08:00
|
|
|
OrgId: c.GetOrgID(),
|
2017-06-07 17:50:09 +08:00
|
|
|
DiffType: dashdiffs.ParseDiffType(apiOptions.DiffType),
|
|
|
|
Base: dashdiffs.DiffTarget{
|
|
|
|
DashboardId: apiOptions.Base.DashboardId,
|
|
|
|
Version: apiOptions.Base.Version,
|
|
|
|
UnsavedDashboard: apiOptions.Base.UnsavedDashboard,
|
|
|
|
},
|
|
|
|
New: dashdiffs.DiffTarget{
|
|
|
|
DashboardId: apiOptions.New.DashboardId,
|
|
|
|
Version: apiOptions.New.Version,
|
|
|
|
UnsavedDashboard: apiOptions.New.UnsavedDashboard,
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
baseVersionQuery := dashver.GetDashboardVersionQuery{
|
|
|
|
DashboardID: options.Base.DashboardId,
|
2022-02-10 16:58:52 +08:00
|
|
|
Version: options.Base.Version,
|
2022-05-25 16:41:51 +08:00
|
|
|
OrgID: options.OrgId,
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
baseVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &baseVersionQuery)
|
|
|
|
if err != nil {
|
2022-06-08 18:22:55 +08:00
|
|
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusNotFound, "Dashboard version not found", err)
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Unable to compute diff", err)
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
newVersionQuery := dashver.GetDashboardVersionQuery{
|
|
|
|
DashboardID: options.New.DashboardId,
|
2022-02-10 16:58:52 +08:00
|
|
|
Version: options.New.Version,
|
2022-05-25 16:41:51 +08:00
|
|
|
OrgID: options.OrgId,
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
newVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &newVersionQuery)
|
|
|
|
if err != nil {
|
2022-06-08 18:22:55 +08:00
|
|
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusNotFound, "Dashboard version not found", err)
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Unable to compute diff", err)
|
2022-02-10 16:58:52 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 16:41:51 +08:00
|
|
|
baseData := baseVersionRes.Data
|
|
|
|
newData := newVersionRes.Data
|
2022-02-10 16:58:52 +08:00
|
|
|
|
|
|
|
result, err := dashdiffs.CalculateDiff(c.Req.Context(), &options, baseData, newData)
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
if err != nil {
|
2022-06-08 18:22:55 +08:00
|
|
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusNotFound, "Dashboard version not found", err)
|
2017-06-07 20:21:40 +08:00
|
|
|
}
|
2023-08-30 22:51:18 +08:00
|
|
|
return response.Error(http.StatusInternalServerError, "Unable to compute diff", err)
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
2017-06-06 05:29:25 +08:00
|
|
|
|
2017-06-07 17:50:09 +08:00
|
|
|
if options.DiffType == dashdiffs.DiffDelta {
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.Respond(http.StatusOK, result.Delta).SetHeader("Content-Type", "application/json")
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
2018-02-28 00:53:30 +08:00
|
|
|
|
2022-04-15 20:01:58 +08:00
|
|
|
return response.Respond(http.StatusOK, result.Delta).SetHeader("Content-Type", "text/html")
|
History and Version Control for Dashboard Updates
A simple version control system for dashboards. Closes #1504.
Goals
1. To create a new dashboard version every time a dashboard is saved.
2. To allow users to view all versions of a given dashboard.
3. To allow users to rollback to a previous version of a dashboard.
4. To allow users to compare two versions of a dashboard.
Usage
Navigate to a dashboard, and click the settings cog. From there, click
the "Changelog" button to be brought to the Changelog view. In this
view, a table containing each version of a dashboard can be seen. Each
entry in the table represents a dashboard version. A selectable
checkbox, the version number, date created, name of the user who created
that version, and commit message is shown in the table, along with a
button that allows a user to restore to a previous version of that
dashboard. If a user wants to restore to a previous version of their
dashboard, they can do so by clicking the previously mentioned button.
If a user wants to compare two different versions of a dashboard, they
can do so by clicking the checkbox of two different dashboard versions,
then clicking the "Compare versions" button located below the dashboard.
From there, the user is brought to a view showing a summary of the
dashboard differences. Each summarized change contains a link that can
be clicked to take the user a JSON diff highlighting the changes line by
line.
Overview of Changes
Backend Changes
- A `dashboard_version` table was created to store each dashboard
version, along with a dashboard version model and structs to represent
the queries and commands necessary for the dashboard version API
methods.
- API endpoints were created to support working with dashboard
versions.
- Methods were added to create, update, read, and destroy dashboard
versions in the database.
- Logic was added to compute the diff between two versions, and
display it to the user.
- The dashboard migration logic was updated to save a "Version
1" of each existing dashboard in the database.
Frontend Changes
- New views
- Methods to pull JSON and HTML from endpoints
New API Endpoints
Each endpoint requires the authorization header to be sent in
the format,
```
Authorization: Bearer <jwt>
```
where `<jwt>` is a JSON web token obtained from the Grafana
admin panel.
`GET "/api/dashboards/db/:dashboardId/versions?orderBy=<string>&limit=<int>&start=<int>"`
Get all dashboard versions for the given dashboard ID. Accepts
three URL parameters:
- `orderBy` String to order the results by. Possible values
are `version`, `created`, `created_by`, `message`. Default
is `versions`. Ordering is always in descending order.
- `limit` Maximum number of results to return
- `start` Position in results to start from
`GET "/api/dashboards/db/:dashboardId/versions/:id"`
Get an individual dashboard version by ID, for the given
dashboard ID.
`POST "/api/dashboards/db/:dashboardId/restore"`
Restore to the given dashboard version. Post body is of
content-type `application/json`, and must contain.
```json
{
"dashboardId": <int>,
"version": <int>
}
```
`GET "/api/dashboards/db/:dashboardId/compare/:versionA...:versionB"`
Compare two dashboard versions by ID for the given
dashboard ID, returning a JSON delta formatted
representation of the diff. The URL format follows
what GitHub does. For example, visiting
[/api/dashboards/db/18/compare/22...33](http://ec2-54-80-139-44.compute-1.amazonaws.com:3000/api/dashboards/db/18/compare/22...33)
will return the diff between versions 22 and 33 for
the dashboard ID 18.
Dependencies Added
- The Go package [gojsondiff](https://github.com/yudai/gojsondiff)
was added and vendored.
2017-05-25 07:14:39 +08:00
|
|
|
}
|
|
|
|
|
2025-08-28 05:51:04 +08:00
|
|
|
// swagger:route POST /dashboards/uid/{uid}/restore dashboards versions restoreDashboardVersionByUID
|
2022-07-27 21:54:37 +08:00
|
|
|
//
|
|
|
|
// Restore a dashboard to a given dashboard version using UID.
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: postDashboardResponse
|
2025-09-11 18:35:58 +08:00
|
|
|
// 400: badRequestError
|
2022-07-27 21:54:37 +08:00
|
|
|
// 401: unauthorisedError
|
|
|
|
// 403: forbiddenError
|
|
|
|
// 404: notFoundError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) response.Response {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.RestoreDashboardVersion")
|
|
|
|
defer span.End()
|
2022-05-18 00:57:27 +08:00
|
|
|
|
2025-09-11 18:35:58 +08:00
|
|
|
c.Req = c.Req.WithContext(ctx)
|
2022-05-18 00:57:27 +08:00
|
|
|
|
2025-09-11 18:35:58 +08:00
|
|
|
var apiCmd dtos.RestoreDashboardVersionCommand
|
2021-11-29 17:18:01 +08:00
|
|
|
if err := web.Bind(c.Req, &apiCmd); err != nil {
|
2025-09-11 18:35:58 +08:00
|
|
|
hs.log.Error("error restoring dashboard version: invalid request", "error", err)
|
2021-11-29 17:18:01 +08:00
|
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
|
|
}
|
2025-09-11 18:35:58 +08:00
|
|
|
|
|
|
|
var (
|
|
|
|
dashID int64
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
dashUID := web.Params(c.Req)[":uid"]
|
2022-05-18 00:57:27 +08:00
|
|
|
if dashUID == "" {
|
|
|
|
dashID, err = strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
|
|
|
|
if err != nil {
|
2025-09-11 18:35:58 +08:00
|
|
|
hs.log.Error("error restoring dashboard version: invalid dashboardId", "error", err)
|
2022-05-18 00:57:27 +08:00
|
|
|
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
|
|
|
|
}
|
2022-01-15 00:55:57 +08:00
|
|
|
}
|
|
|
|
|
2025-09-11 18:35:58 +08:00
|
|
|
res, err := hs.dashboardVersionService.RestoreVersion(ctx, &dashver.RestoreVersionCommand{
|
|
|
|
Requester: c.SignedInUser,
|
|
|
|
DashboardUID: dashUID,
|
|
|
|
DashboardID: dashID,
|
|
|
|
Version: apiCmd.Version,
|
|
|
|
})
|
2022-05-25 16:41:51 +08:00
|
|
|
if err != nil {
|
2025-09-11 18:35:58 +08:00
|
|
|
hs.log.Error("error restoring dashboard version: service call failed", "error", err)
|
|
|
|
return dashboardErrResponse(err, "Failed to restore dashboard version")
|
2025-03-24 23:48:46 +08:00
|
|
|
}
|
|
|
|
|
2025-09-11 18:35:58 +08:00
|
|
|
return response.JSON(http.StatusOK, util.DynMap{
|
|
|
|
"status": "success",
|
|
|
|
"slug": res.Slug,
|
|
|
|
"version": res.Version,
|
|
|
|
"id": res.ID,
|
|
|
|
"uid": res.UID,
|
|
|
|
"url": res.GetURL(),
|
|
|
|
"folderUid": res.FolderUID,
|
|
|
|
})
|
2025-03-24 23:48:46 +08:00
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:route GET /dashboards/tags dashboards getDashboardTags
|
|
|
|
//
|
|
|
|
// Get all dashboards tags of an organisation.
|
|
|
|
//
|
|
|
|
// Responses:
|
|
|
|
// 200: getDashboardsTagsResponse
|
|
|
|
// 401: unauthorisedError
|
|
|
|
// 500: internalServerError
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetDashboardTags(c *contextmodel.ReqContext) {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetDashboardTags")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2025-04-10 20:42:23 +08:00
|
|
|
query := dashboards.GetDashboardTagsQuery{OrgID: c.GetOrgID()}
|
2023-01-25 17:36:26 +08:00
|
|
|
queryResult, err := hs.DashboardService.GetDashboardTags(c.Req.Context(), &query)
|
2015-05-13 16:45:53 +08:00
|
|
|
if err != nil {
|
|
|
|
c.JsonApiErr(500, "Failed to get tags from database", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-25 17:36:26 +08:00
|
|
|
c.JSON(http.StatusOK, queryResult)
|
2015-05-13 16:45:53 +08:00
|
|
|
}
|
2022-05-17 00:59:02 +08:00
|
|
|
|
|
|
|
// GetDashboardUIDs converts internal ids to UIDs
|
2023-01-27 15:50:36 +08:00
|
|
|
func (hs *HTTPServer) GetDashboardUIDs(c *contextmodel.ReqContext) {
|
2024-08-30 14:26:15 +08:00
|
|
|
ctx, span := tracer.Start(c.Req.Context(), "api.GetDashboardUIDs")
|
|
|
|
defer span.End()
|
|
|
|
c.Req = c.Req.WithContext(ctx)
|
|
|
|
|
2022-05-17 00:59:02 +08:00
|
|
|
ids := strings.Split(web.Params(c.Req)[":ids"], ",")
|
|
|
|
uids := make([]string, 0, len(ids))
|
|
|
|
|
2023-01-16 23:33:55 +08:00
|
|
|
q := &dashboards.GetDashboardRefByIDQuery{}
|
2022-05-17 00:59:02 +08:00
|
|
|
for _, idstr := range ids {
|
|
|
|
id, err := strconv.ParseInt(idstr, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2023-01-16 23:33:55 +08:00
|
|
|
q.ID = id
|
2023-01-25 17:36:26 +08:00
|
|
|
qResult, err := hs.DashboardService.GetDashboardUIDByID(c.Req.Context(), q)
|
2022-05-17 00:59:02 +08:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2023-01-25 17:36:26 +08:00
|
|
|
uids = append(uids, qResult.UID)
|
2022-05-17 00:59:02 +08:00
|
|
|
}
|
|
|
|
c.JSON(http.StatusOK, uids)
|
|
|
|
}
|
2022-07-27 21:54:37 +08:00
|
|
|
|
2025-09-11 18:35:58 +08:00
|
|
|
func dashboardErrResponse(err error, fallbackMessage string) response.Response {
|
|
|
|
var dashboardErr dashboardaccess.DashboardErr
|
|
|
|
if ok := errors.As(err, &dashboardErr); ok {
|
|
|
|
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var statusErr *k8serrors.StatusError
|
|
|
|
if errors.As(err, &statusErr) {
|
|
|
|
return response.Error(int(statusErr.ErrStatus.Code), statusErr.ErrStatus.Message, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.Error(http.StatusInternalServerError, fallbackMessage, err)
|
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:parameters restoreDashboardVersionByID
|
|
|
|
type RestoreDashboardVersionByIDParams struct {
|
|
|
|
// in:body
|
|
|
|
// required:true
|
|
|
|
Body dtos.RestoreDashboardVersionCommand
|
|
|
|
// in:path
|
|
|
|
DashboardID int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardVersionsByID
|
|
|
|
type GetDashboardVersionsByIDParams struct {
|
|
|
|
// in:path
|
|
|
|
DashboardID int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardVersionsByUID
|
|
|
|
type GetDashboardVersionsByUIDParams struct {
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters restoreDashboardVersionByUID
|
|
|
|
type RestoreDashboardVersionByUIDParams struct {
|
|
|
|
// in:body
|
|
|
|
// required:true
|
|
|
|
Body dtos.RestoreDashboardVersionCommand
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardVersionByID
|
|
|
|
type GetDashboardVersionByIDParams struct {
|
|
|
|
// in:path
|
|
|
|
DashboardID int64
|
|
|
|
// in:path
|
|
|
|
DashboardVersionID int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardVersionByUID
|
|
|
|
type GetDashboardVersionByUIDParams struct {
|
|
|
|
// in:path
|
|
|
|
DashboardVersionID int64
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardVersions getDashboardVersionsByUID
|
|
|
|
type GetDashboardVersionsParams struct {
|
|
|
|
// Maximum number of results to return
|
|
|
|
// in:query
|
|
|
|
// required:false
|
|
|
|
// default:0
|
|
|
|
Limit int `json:"limit"`
|
|
|
|
|
|
|
|
// Version to start from when returning queries
|
|
|
|
// in:query
|
|
|
|
// required:false
|
|
|
|
// default:0
|
|
|
|
Start int `json:"start"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters getDashboardByUID
|
|
|
|
type GetDashboardByUIDParams struct {
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters deleteDashboardByUID
|
|
|
|
type DeleteDashboardByUIDParams struct {
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
2024-05-17 01:36:26 +08:00
|
|
|
// swagger:parameters hardDeleteDashboardByUID
|
|
|
|
type HardDeleteDashboardByUIDParams struct {
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
}
|
|
|
|
|
2022-07-27 21:54:37 +08:00
|
|
|
// swagger:parameters postDashboard
|
|
|
|
type PostDashboardParams struct {
|
|
|
|
// in:body
|
|
|
|
// required:true
|
2023-01-18 20:52:41 +08:00
|
|
|
Body dashboards.SaveDashboardCommand
|
2022-07-27 21:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:parameters calculateDashboardDiff
|
|
|
|
type CalcDashboardDiffParams struct {
|
|
|
|
// in:body
|
|
|
|
// required:true
|
|
|
|
Body struct {
|
|
|
|
Base dtos.CalculateDiffTarget `json:"base" binding:"Required"`
|
|
|
|
New dtos.CalculateDiffTarget `json:"new" binding:"Required"`
|
|
|
|
// The type of diff to return
|
|
|
|
// Description:
|
|
|
|
// * `basic`
|
|
|
|
// * `json`
|
|
|
|
// Enum: basic,json
|
|
|
|
DiffType string `json:"diffType" binding:"Required"`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response dashboardResponse
|
|
|
|
type DashboardResponse struct {
|
|
|
|
// The response message
|
|
|
|
// in: body
|
|
|
|
Body dtos.DashboardFullWithMeta `json:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response deleteDashboardResponse
|
|
|
|
type DeleteDashboardResponse struct {
|
|
|
|
// The response message
|
|
|
|
// in: body
|
|
|
|
Body struct {
|
2024-05-17 01:36:26 +08:00
|
|
|
// UID Identifier of the deleted dashboard.
|
2022-07-27 21:54:37 +08:00
|
|
|
// required: true
|
|
|
|
// example: 65
|
2024-05-17 01:36:26 +08:00
|
|
|
UID string `json:"uid"`
|
2022-07-27 21:54:37 +08:00
|
|
|
|
|
|
|
// Title Title of the deleted dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: My Dashboard
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
|
|
|
// Message Message of the deleted dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: Dashboard My Dashboard deleted
|
|
|
|
Message string `json:"message"`
|
|
|
|
} `json:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response postDashboardResponse
|
|
|
|
type PostDashboardResponse struct {
|
|
|
|
// in: body
|
|
|
|
Body struct {
|
|
|
|
// Status status of the response.
|
|
|
|
// required: true
|
|
|
|
// example: success
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
|
|
|
// Slug The slug of the dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: my-dashboard
|
|
|
|
Slug string `json:"title"`
|
|
|
|
|
|
|
|
// Version The version of the dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: 2
|
|
|
|
Verion int64 `json:"version"`
|
|
|
|
|
|
|
|
// ID The unique identifier (id) of the created/updated dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: 1
|
2023-10-19 15:16:53 +08:00
|
|
|
ID int64 `json:"id"`
|
2022-07-27 21:54:37 +08:00
|
|
|
|
|
|
|
// UID The unique identifier (uid) of the created/updated dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: nHz3SXiiz
|
|
|
|
UID string `json:"uid"`
|
|
|
|
|
|
|
|
// URL The relative URL for accessing the created/updated dashboard.
|
|
|
|
// required: true
|
|
|
|
// example: /d/nHz3SXiiz/my-dashboard
|
|
|
|
URL string `json:"url"`
|
2023-11-01 23:01:54 +08:00
|
|
|
|
|
|
|
// FolderUID The unique identifier (uid) of the folder the dashboard belongs to.
|
|
|
|
// required: false
|
|
|
|
FolderUID string `json:"folderUid"`
|
2022-07-27 21:54:37 +08:00
|
|
|
} `json:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response calculateDashboardDiffResponse
|
|
|
|
type CalculateDashboardDiffResponse struct {
|
|
|
|
// in: body
|
|
|
|
Body []byte `json:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response getHomeDashboardResponse
|
|
|
|
type GetHomeDashboardResponse struct {
|
|
|
|
// in: body
|
|
|
|
Body GetHomeDashboardResponseBody `json:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response getDashboardsTagsResponse
|
|
|
|
type DashboardsTagsResponse struct {
|
|
|
|
// in: body
|
2023-01-18 20:52:41 +08:00
|
|
|
Body []*dashboards.DashboardTagCloudItem `json:"body"`
|
2022-07-27 21:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get home dashboard response.
|
|
|
|
// swagger:model GetHomeDashboardResponse
|
|
|
|
type GetHomeDashboardResponseBody struct {
|
|
|
|
// swagger:allOf
|
|
|
|
// required: false
|
|
|
|
dtos.DashboardFullWithMeta
|
|
|
|
|
|
|
|
// swagger:allOf
|
|
|
|
// required: false
|
|
|
|
dtos.DashboardRedirect
|
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response dashboardVersionsResponse
|
|
|
|
type DashboardVersionsResponse struct {
|
|
|
|
// in: body
|
2023-03-30 22:31:53 +08:00
|
|
|
Body []dashver.DashboardVersionMeta `json:"body"`
|
2022-07-27 21:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// swagger:response dashboardVersionResponse
|
|
|
|
type DashboardVersionResponse struct {
|
|
|
|
// in: body
|
|
|
|
Body *dashver.DashboardVersionMeta `json:"body"`
|
|
|
|
}
|
2024-05-17 01:36:26 +08:00
|
|
|
|
|
|
|
// swagger:parameters restoreDeletedDashboardByUID
|
|
|
|
type RestoreDeletedDashboardByUID struct {
|
|
|
|
// in:path
|
|
|
|
// required:true
|
|
|
|
UID string `json:"uid"`
|
|
|
|
|
|
|
|
// in:body
|
|
|
|
// required:true
|
|
|
|
Body dashboards.RestoreDeletedDashboardCommand
|
|
|
|
}
|