2014-12-22 05:25:08 -06:00
|
|
|
package sqlstore
|
|
|
|
|
|
|
|
import (
|
2018-01-30 16:31:02 -06:00
|
|
|
"strings"
|
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-24 18:14:39 -05:00
|
|
|
"time"
|
2015-02-04 04:35:59 -06:00
|
|
|
|
2015-02-05 03:37:13 -06:00
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
2015-03-22 14:14:00 -05:00
|
|
|
"github.com/grafana/grafana/pkg/metrics"
|
2015-02-05 03:37:13 -06:00
|
|
|
m "github.com/grafana/grafana/pkg/models"
|
2015-06-05 01:15:38 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/search"
|
2018-01-31 10:27:28 -06:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2014-12-22 05:25:08 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2014-12-29 06:58:06 -06:00
|
|
|
bus.AddHandler("sql", SaveDashboard)
|
|
|
|
bus.AddHandler("sql", GetDashboard)
|
2016-01-28 18:41:23 -06:00
|
|
|
bus.AddHandler("sql", GetDashboards)
|
2014-12-29 06:58:06 -06:00
|
|
|
bus.AddHandler("sql", DeleteDashboard)
|
|
|
|
bus.AddHandler("sql", SearchDashboards)
|
2015-01-06 11:39:26 -06:00
|
|
|
bus.AddHandler("sql", GetDashboardTags)
|
2016-03-17 03:01:58 -05:00
|
|
|
bus.AddHandler("sql", GetDashboardSlugById)
|
2018-02-01 06:32:00 -06:00
|
|
|
bus.AddHandler("sql", GetDashboardUIDById)
|
2016-07-08 02:35:06 -05:00
|
|
|
bus.AddHandler("sql", GetDashboardsByPluginId)
|
2018-01-19 10:41:09 -06:00
|
|
|
bus.AddHandler("sql", GetFoldersForSignedInUser)
|
2018-01-30 16:31:02 -06:00
|
|
|
bus.AddHandler("sql", GetDashboardPermissionsForUser)
|
2018-02-01 07:13:42 -06:00
|
|
|
bus.AddHandler("sql", GetDashboardsBySlug)
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
var generateNewUid func() string = util.GenerateShortUid
|
|
|
|
|
2014-12-29 06:58:06 -06:00
|
|
|
func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
2017-05-23 03:56:23 -05:00
|
|
|
return inTransaction(func(sess *DBSession) error {
|
2014-12-22 05:25:08 -06:00
|
|
|
dash := cmd.GetDashboardModel()
|
2017-06-01 16:30:31 -05:00
|
|
|
|
2015-01-05 10:04:29 -06:00
|
|
|
// try get existing dashboard
|
2018-01-30 11:57:33 -06:00
|
|
|
var existing m.Dashboard
|
2015-01-05 10:04:29 -06:00
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
if dash.Id != 0 {
|
2015-05-04 01:19:29 -05:00
|
|
|
dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !dashWithIdExists {
|
|
|
|
return m.ErrDashboardNotFound
|
2015-03-02 15:24:01 -06:00
|
|
|
}
|
2015-05-04 01:19:29 -05:00
|
|
|
|
2015-03-02 15:24:01 -06:00
|
|
|
// check for is someone else has written in between
|
|
|
|
if dash.Version != existing.Version {
|
|
|
|
if cmd.Overwrite {
|
|
|
|
dash.Version = existing.Version
|
|
|
|
} else {
|
|
|
|
return m.ErrDashboardVersionMismatch
|
|
|
|
}
|
|
|
|
}
|
2016-07-08 06:41:46 -05:00
|
|
|
|
|
|
|
// do not allow plugin dashboard updates without overwrite flag
|
|
|
|
if existing.PluginId != "" && cmd.Overwrite == false {
|
|
|
|
return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
|
|
|
}
|
2018-01-31 10:27:28 -06:00
|
|
|
} else if dash.Uid != "" {
|
|
|
|
var sameUid m.Dashboard
|
|
|
|
sameUidExists, err := sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&sameUid)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-05 10:04:29 -06:00
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
if sameUidExists {
|
|
|
|
// another dashboard with same uid
|
|
|
|
if dash.Id != sameUid.Id {
|
|
|
|
if cmd.Overwrite {
|
|
|
|
dash.Id = sameUid.Id
|
|
|
|
dash.Version = sameUid.Version
|
|
|
|
} else {
|
|
|
|
return m.ErrDashboardWithSameUIDExists
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-04 01:19:29 -05:00
|
|
|
}
|
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
if dash.Uid == "" {
|
|
|
|
uid, err := generateNewDashboardUid(sess, dash.OrgId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-05-04 01:19:29 -05:00
|
|
|
}
|
2018-01-31 10:27:28 -06:00
|
|
|
dash.Uid = uid
|
|
|
|
dash.Data.Set("uid", uid)
|
2015-05-04 01:19:29 -05:00
|
|
|
}
|
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
err := guaranteeDashboardNameIsUniqueInFolder(sess, dash)
|
2018-01-31 06:47:28 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-05-04 01:19:29 -05:00
|
|
|
}
|
|
|
|
|
2017-06-23 16:22:09 -05:00
|
|
|
err = setHasAcl(sess, dash)
|
|
|
|
if err != nil {
|
|
|
|
return 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-24 18:14:39 -05:00
|
|
|
parentVersion := dash.Version
|
|
|
|
affectedRows := int64(0)
|
2017-06-05 09:34:32 -05:00
|
|
|
|
2014-12-22 05:25:08 -06:00
|
|
|
if dash.Id == 0 {
|
2017-06-06 08:40:10 -05:00
|
|
|
dash.Version = 1
|
2017-09-06 04:23:52 -05:00
|
|
|
metrics.M_Api_Dashboard_Insert.Inc()
|
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-24 18:14:39 -05:00
|
|
|
dash.Data.Set("version", dash.Version)
|
2015-05-04 00:46:46 -05:00
|
|
|
affectedRows, err = sess.Insert(dash)
|
2014-12-22 05:25:08 -06:00
|
|
|
} else {
|
2017-06-23 16:22:09 -05:00
|
|
|
dash.Version++
|
2016-03-12 03:13:49 -06:00
|
|
|
dash.Data.Set("version", dash.Version)
|
2017-11-23 04:29:06 -06:00
|
|
|
|
|
|
|
if !cmd.UpdatedAt.IsZero() {
|
|
|
|
dash.Updated = cmd.UpdatedAt
|
|
|
|
}
|
|
|
|
|
2018-01-30 11:57:33 -06:00
|
|
|
affectedRows, err = sess.MustCols("folder_id", "has_acl").ID(dash.Id).Update(dash)
|
2015-05-04 00:46:46 -05:00
|
|
|
}
|
2017-06-05 09:34:32 -05:00
|
|
|
|
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-24 18:14:39 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-05 09:34:32 -05:00
|
|
|
|
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-24 18:14:39 -05:00
|
|
|
if affectedRows == 0 {
|
|
|
|
return m.ErrDashboardNotFound
|
|
|
|
}
|
2015-05-04 00:46:46 -05:00
|
|
|
|
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-24 18:14:39 -05:00
|
|
|
dashVersion := &m.DashboardVersion{
|
|
|
|
DashboardId: dash.Id,
|
|
|
|
ParentVersion: parentVersion,
|
2017-06-05 15:59:04 -05:00
|
|
|
RestoredFrom: cmd.RestoredFrom,
|
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-24 18:14:39 -05:00
|
|
|
Version: dash.Version,
|
|
|
|
Created: time.Now(),
|
|
|
|
CreatedBy: dash.UpdatedBy,
|
|
|
|
Message: cmd.Message,
|
|
|
|
Data: dash.Data,
|
|
|
|
}
|
2017-06-05 09:34:32 -05:00
|
|
|
|
|
|
|
// insert version entry
|
|
|
|
if affectedRows, err = sess.Insert(dashVersion); err != nil {
|
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-24 18:14:39 -05:00
|
|
|
return err
|
2017-06-05 09:34:32 -05:00
|
|
|
} else if affectedRows == 0 {
|
2015-05-04 00:46:46 -05:00
|
|
|
return m.ErrDashboardNotFound
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
|
|
|
|
2017-06-23 16:22:09 -05:00
|
|
|
// delete existing tags
|
2015-01-07 05:37:24 -06:00
|
|
|
_, err = sess.Exec("DELETE FROM dashboard_tag WHERE dashboard_id=?", dash.Id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert new tags
|
|
|
|
tags := dash.GetTags()
|
|
|
|
if len(tags) > 0 {
|
|
|
|
for _, tag := range tags {
|
2015-01-20 07:15:48 -06:00
|
|
|
if _, err := sess.Insert(&DashboardTag{DashboardId: dash.Id, Term: tag}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-07 05:37:24 -06:00
|
|
|
}
|
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
cmd.Result = dash
|
|
|
|
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
2018-02-01 06:32:00 -06:00
|
|
|
|
2018-01-31 10:27:28 -06:00
|
|
|
func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
uid := generateNewUid()
|
|
|
|
|
|
|
|
exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&m.Dashboard{})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
return uid, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", m.ErrDashboardFailedGenerateUniqueUid
|
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
|
2018-01-31 06:47:28 -06:00
|
|
|
func guaranteeDashboardNameIsUniqueInFolder(sess *DBSession, dash *m.Dashboard) error {
|
|
|
|
var sameNameInFolder m.Dashboard
|
|
|
|
sameNameInFolderExist, err := sess.Where("org_id=? AND title=? AND folder_id = ? AND uid <> ?",
|
|
|
|
dash.OrgId, dash.Title, dash.FolderId, dash.Uid).
|
|
|
|
Get(&sameNameInFolder)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if sameNameInFolderExist {
|
|
|
|
return m.ErrDashboardWithSameNameInFolderExists
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
|
2017-06-23 16:22:09 -05:00
|
|
|
func setHasAcl(sess *DBSession, dash *m.Dashboard) error {
|
|
|
|
// check if parent has acl
|
|
|
|
if dash.FolderId > 0 {
|
|
|
|
var parent m.Dashboard
|
|
|
|
if hasParent, err := sess.Where("folder_id=?", dash.FolderId).Get(&parent); err != nil {
|
|
|
|
return err
|
|
|
|
} else if hasParent && parent.HasAcl {
|
|
|
|
dash.HasAcl = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if dash has its own acl
|
|
|
|
if dash.Id > 0 {
|
|
|
|
if res, err := sess.Query("SELECT 1 from dashboard_acl WHERE dashboard_id =?", dash.Id); err != nil {
|
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
if len(res) > 0 {
|
|
|
|
dash.HasAcl = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-29 06:58:06 -06:00
|
|
|
func GetDashboard(query *m.GetDashboardQuery) error {
|
2018-01-29 14:23:07 -06:00
|
|
|
dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
|
2014-12-22 05:25:08 -06:00
|
|
|
has, err := x.Get(&dashboard)
|
2017-06-05 10:45:27 -05:00
|
|
|
|
2014-12-22 05:25:08 -06:00
|
|
|
if err != nil {
|
2014-12-29 06:58:06 -06:00
|
|
|
return err
|
2014-12-22 05:25:08 -06:00
|
|
|
} else if has == false {
|
2014-12-29 06:58:06 -06:00
|
|
|
return m.ErrDashboardNotFound
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
|
|
|
|
2016-03-12 03:13:49 -06:00
|
|
|
dashboard.Data.Set("id", dashboard.Id)
|
2018-01-29 10:20:18 -06:00
|
|
|
dashboard.Data.Set("uid", dashboard.Uid)
|
2014-12-29 06:58:06 -06:00
|
|
|
query.Result = &dashboard
|
|
|
|
return nil
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
|
|
|
|
2015-01-07 05:37:24 -06:00
|
|
|
type DashboardSearchProjection struct {
|
2017-06-16 20:00:13 -05:00
|
|
|
Id int64
|
2018-01-30 08:24:14 -06:00
|
|
|
Uid string
|
2017-06-16 20:00:13 -05:00
|
|
|
Title string
|
|
|
|
Slug string
|
|
|
|
Term string
|
|
|
|
IsFolder bool
|
2017-06-23 15:00:26 -05:00
|
|
|
FolderId int64
|
2017-06-16 20:00:13 -05:00
|
|
|
FolderSlug string
|
|
|
|
FolderTitle string
|
2015-01-07 05:37:24 -06:00
|
|
|
}
|
|
|
|
|
2017-03-27 07:36:28 -05:00
|
|
|
func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
|
2017-06-23 16:13:58 -05:00
|
|
|
limit := query.Limit
|
|
|
|
if limit == 0 {
|
|
|
|
limit = 1000
|
|
|
|
}
|
|
|
|
|
2017-11-17 08:30:21 -06:00
|
|
|
sb := NewSearchBuilder(query.SignedInUser, limit).
|
|
|
|
WithTags(query.Tags).
|
|
|
|
WithDashboardIdsIn(query.DashboardIds)
|
2015-01-06 10:15:52 -06:00
|
|
|
|
2015-02-04 04:35:59 -06:00
|
|
|
if query.IsStarred {
|
2017-11-16 17:16:33 -06:00
|
|
|
sb.IsStarred()
|
2017-06-16 19:33:53 -05:00
|
|
|
}
|
|
|
|
|
2015-02-04 04:35:59 -06:00
|
|
|
if len(query.Title) > 0 {
|
2017-11-16 17:16:33 -06:00
|
|
|
sb.WithTitle(query.Title)
|
2015-02-04 04:35:59 -06:00
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
|
2017-11-16 17:16:33 -06:00
|
|
|
if len(query.Type) > 0 {
|
|
|
|
sb.WithType(query.Type)
|
2017-05-24 11:28:13 -05:00
|
|
|
}
|
|
|
|
|
2017-11-20 05:47:03 -06:00
|
|
|
if len(query.FolderIds) > 0 {
|
|
|
|
sb.WithFolderIds(query.FolderIds)
|
2017-11-17 08:30:21 -06:00
|
|
|
}
|
|
|
|
|
2017-11-16 17:16:33 -06:00
|
|
|
var res []DashboardSearchProjection
|
|
|
|
|
|
|
|
sql, params := sb.ToSql()
|
|
|
|
err := x.Sql(sql, params...).Find(&res)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-06-01 16:30:31 -05:00
|
|
|
}
|
|
|
|
|
2017-11-16 17:16:33 -06:00
|
|
|
return res, nil
|
2017-03-27 07:36:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
|
|
|
|
res, err := findDashboards(query)
|
2015-01-07 05:37:24 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-01 16:30:31 -05:00
|
|
|
makeQueryResult(query, res)
|
2015-01-07 05:37:24 -06:00
|
|
|
|
2017-06-01 16:30:31 -05:00
|
|
|
return nil
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
|
|
|
|
2017-03-27 07:36:28 -05:00
|
|
|
func getHitType(item DashboardSearchProjection) search.HitType {
|
|
|
|
var hitType search.HitType
|
|
|
|
if item.IsFolder {
|
|
|
|
hitType = search.DashHitFolder
|
|
|
|
} else {
|
|
|
|
hitType = search.DashHitDB
|
|
|
|
}
|
|
|
|
|
|
|
|
return hitType
|
|
|
|
}
|
|
|
|
|
2017-06-01 16:30:31 -05:00
|
|
|
func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []DashboardSearchProjection) {
|
|
|
|
query.Result = make([]*search.Hit, 0)
|
|
|
|
hits := make(map[int64]*search.Hit)
|
|
|
|
|
|
|
|
for _, item := range res {
|
|
|
|
hit, exists := hits[item.Id]
|
|
|
|
if !exists {
|
|
|
|
hit = &search.Hit{
|
2017-06-23 15:00:26 -05:00
|
|
|
Id: item.Id,
|
2018-01-31 10:15:00 -06:00
|
|
|
Uid: item.Uid,
|
2017-06-23 15:00:26 -05:00
|
|
|
Title: item.Title,
|
|
|
|
Uri: "db/" + item.Slug,
|
2018-01-31 16:14:48 -06:00
|
|
|
Url: m.GetDashboardFolderUrl(item.IsFolder, item.Uid, item.Slug),
|
2017-06-23 15:00:26 -05:00
|
|
|
Type: getHitType(item),
|
|
|
|
FolderId: item.FolderId,
|
|
|
|
FolderTitle: item.FolderTitle,
|
|
|
|
FolderSlug: item.FolderSlug,
|
|
|
|
Tags: []string{},
|
2017-06-01 16:30:31 -05:00
|
|
|
}
|
2018-01-30 08:24:14 -06:00
|
|
|
|
2017-06-01 16:30:31 -05:00
|
|
|
query.Result = append(query.Result, hit)
|
|
|
|
hits[item.Id] = hit
|
|
|
|
}
|
|
|
|
if len(item.Term) > 0 {
|
|
|
|
hit.Tags = append(hit.Tags, item.Term)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-06 11:39:26 -06:00
|
|
|
func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
2015-01-20 08:23:14 -06:00
|
|
|
sql := `SELECT
|
|
|
|
COUNT(*) as count,
|
|
|
|
term
|
|
|
|
FROM dashboard
|
|
|
|
INNER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id
|
2015-02-23 13:07:49 -06:00
|
|
|
WHERE dashboard.org_id=?
|
2015-01-20 08:23:14 -06:00
|
|
|
GROUP BY term`
|
|
|
|
|
|
|
|
query.Result = make([]*m.DashboardTagCloudItem, 0)
|
2015-02-23 13:07:49 -06:00
|
|
|
sess := x.Sql(sql, query.OrgId)
|
2015-01-07 05:37:24 -06:00
|
|
|
err := sess.Find(&query.Result)
|
|
|
|
return err
|
2015-01-06 11:39:26 -06:00
|
|
|
}
|
|
|
|
|
2018-01-19 10:41:09 -06:00
|
|
|
func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
|
|
|
|
query.Result = make([]*m.DashboardFolder, 0)
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if query.SignedInUser.OrgRole == m.ROLE_ADMIN {
|
|
|
|
sql := `SELECT distinct d.id, d.title
|
|
|
|
FROM dashboard AS d WHERE d.is_folder = ?
|
|
|
|
ORDER BY d.title ASC`
|
|
|
|
|
|
|
|
err = x.Sql(sql, dialect.BooleanStr(true)).Find(&query.Result)
|
|
|
|
} else {
|
|
|
|
params := make([]interface{}, 0)
|
|
|
|
sql := `SELECT distinct d.id, d.title
|
|
|
|
FROM dashboard AS d
|
|
|
|
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
|
|
|
|
LEFT JOIN team_member AS ugm ON ugm.team_id = da.team_id
|
|
|
|
LEFT JOIN org_user ou ON ou.role = da.role AND ou.user_id = ?
|
2018-01-30 16:31:02 -06:00
|
|
|
LEFT JOIN org_user ouRole ON ouRole.role = 'Editor' AND ouRole.user_id = ? AND ouRole.org_id = ?`
|
2018-01-19 10:41:09 -06:00
|
|
|
params = append(params, query.SignedInUser.UserId)
|
|
|
|
params = append(params, query.SignedInUser.UserId)
|
2018-01-30 16:31:02 -06:00
|
|
|
params = append(params, query.OrgId)
|
2018-01-19 10:41:09 -06:00
|
|
|
|
2018-02-01 10:27:29 -06:00
|
|
|
sql += ` WHERE
|
2018-01-19 10:41:09 -06:00
|
|
|
d.org_id = ? AND
|
2018-02-01 10:27:29 -06:00
|
|
|
d.is_folder = ? AND
|
2018-01-19 10:41:09 -06:00
|
|
|
(
|
2018-02-01 10:27:29 -06:00
|
|
|
(d.has_acl = ? AND da.permission > 1 AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL))
|
|
|
|
OR (d.has_acl = ? AND ouRole.id IS NOT NULL)
|
2018-01-19 10:41:09 -06:00
|
|
|
)`
|
|
|
|
params = append(params, query.OrgId)
|
2018-02-01 10:27:29 -06:00
|
|
|
params = append(params, dialect.BooleanStr(true))
|
|
|
|
params = append(params, dialect.BooleanStr(true))
|
2018-01-19 10:41:09 -06:00
|
|
|
params = append(params, query.SignedInUser.UserId)
|
|
|
|
params = append(params, query.SignedInUser.UserId)
|
2018-02-01 10:27:29 -06:00
|
|
|
params = append(params, dialect.BooleanStr(false))
|
2018-01-19 10:41:09 -06:00
|
|
|
|
|
|
|
if len(query.Title) > 0 {
|
|
|
|
sql += " AND d.title " + dialect.LikeStr() + " ?"
|
|
|
|
params = append(params, "%"+query.Title+"%")
|
|
|
|
}
|
|
|
|
|
|
|
|
sql += ` ORDER BY d.title ASC`
|
|
|
|
err = x.Sql(sql, params...).Find(&query.Result)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-12-29 06:58:06 -06:00
|
|
|
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
2017-05-23 03:56:23 -05:00
|
|
|
return inTransaction(func(sess *DBSession) error {
|
2017-06-17 17:24:38 -05:00
|
|
|
dashboard := m.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
|
2016-04-25 04:22:13 -05:00
|
|
|
has, err := sess.Get(&dashboard)
|
2015-08-12 02:23:46 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if has == false {
|
|
|
|
return m.ErrDashboardNotFound
|
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
|
2015-08-12 02:23:46 -05:00
|
|
|
deletes := []string{
|
|
|
|
"DELETE FROM dashboard_tag WHERE dashboard_id = ? ",
|
|
|
|
"DELETE FROM star WHERE dashboard_id = ? ",
|
|
|
|
"DELETE FROM dashboard WHERE id = ?",
|
2016-11-06 03:49:43 -06:00
|
|
|
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
|
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-24 18:14:39 -05:00
|
|
|
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
|
2017-06-23 15:00:26 -05:00
|
|
|
"DELETE FROM dashboard WHERE folder_id = ?",
|
2017-10-07 03:31:39 -05:00
|
|
|
"DELETE FROM annotation WHERE dashboard_id = ?",
|
2015-08-12 02:23:46 -05:00
|
|
|
}
|
2014-12-22 05:25:08 -06:00
|
|
|
|
2015-08-12 02:23:46 -05:00
|
|
|
for _, sql := range deletes {
|
|
|
|
_, err := sess.Exec(sql, dashboard.Id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-23 03:56:23 -05:00
|
|
|
if err := DeleteAlertDefinition(dashboard.Id, sess); err != nil {
|
2016-04-26 08:48:29 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-08-12 02:23:46 -05:00
|
|
|
return nil
|
|
|
|
})
|
2014-12-22 05:25:08 -06:00
|
|
|
}
|
2016-01-28 18:41:23 -06:00
|
|
|
|
|
|
|
func GetDashboards(query *m.GetDashboardsQuery) error {
|
|
|
|
if len(query.DashboardIds) == 0 {
|
|
|
|
return m.ErrCommandValidationFailed
|
|
|
|
}
|
|
|
|
|
2016-06-23 09:30:12 -05:00
|
|
|
var dashboards = make([]*m.Dashboard, 0)
|
2016-01-28 18:41:23 -06:00
|
|
|
|
|
|
|
err := x.In("id", query.DashboardIds).Find(&dashboards)
|
2016-06-23 09:30:12 -05:00
|
|
|
query.Result = dashboards
|
2016-01-28 18:41:23 -06:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2016-03-17 03:01:58 -05:00
|
|
|
|
2018-01-30 16:31:02 -06:00
|
|
|
// GetDashboardPermissionsForUser returns the maximum permission the specified user has for a dashboard(s)
|
|
|
|
// The function takes in a list of dashboard ids and the user id and role
|
|
|
|
func GetDashboardPermissionsForUser(query *m.GetDashboardPermissionsForUserQuery) error {
|
|
|
|
if len(query.DashboardIds) == 0 {
|
|
|
|
return m.ErrCommandValidationFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
if query.OrgRole == m.ROLE_ADMIN {
|
|
|
|
var permissions = make([]*m.DashboardPermissionForUser, 0)
|
|
|
|
for _, d := range query.DashboardIds {
|
|
|
|
permissions = append(permissions, &m.DashboardPermissionForUser{
|
|
|
|
DashboardId: d,
|
|
|
|
Permission: m.PERMISSION_ADMIN,
|
|
|
|
PermissionName: m.PERMISSION_ADMIN.String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
query.Result = permissions
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
params := make([]interface{}, 0)
|
|
|
|
|
|
|
|
// check dashboards that have ACLs via user id, team id or role
|
|
|
|
sql := `SELECT d.id AS dashboard_id, MAX(COALESCE(da.permission, pt.permission)) AS permission
|
|
|
|
FROM dashboard AS d
|
|
|
|
LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
|
|
|
|
LEFT JOIN team_member as ugm on ugm.team_id = da.team_id
|
|
|
|
LEFT JOIN org_user ou ON ou.role = da.role AND ou.user_id = ?
|
|
|
|
`
|
|
|
|
params = append(params, query.UserId)
|
|
|
|
|
|
|
|
//check the user's role for dashboards that do not have hasAcl set
|
|
|
|
sql += `LEFT JOIN org_user ouRole ON ouRole.user_id = ? AND ouRole.org_id = ?`
|
|
|
|
params = append(params, query.UserId)
|
|
|
|
params = append(params, query.OrgId)
|
|
|
|
|
|
|
|
sql += `
|
2018-02-01 10:27:29 -06:00
|
|
|
LEFT JOIN (SELECT 1 AS permission, 'Viewer' AS role
|
|
|
|
UNION SELECT 2 AS permission, 'Editor' AS role
|
|
|
|
UNION SELECT 4 AS permission, 'Admin' AS role) pt ON ouRole.role = pt.role
|
2018-01-30 16:31:02 -06:00
|
|
|
WHERE
|
|
|
|
d.Id IN (?` + strings.Repeat(",?", len(query.DashboardIds)-1) + `) `
|
|
|
|
for _, id := range query.DashboardIds {
|
|
|
|
params = append(params, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
sql += ` AND
|
|
|
|
d.org_id = ? AND
|
|
|
|
(
|
|
|
|
(d.has_acl = ? AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL))
|
|
|
|
OR (d.has_acl = ? AND ouRole.id IS NOT NULL)
|
|
|
|
)
|
|
|
|
group by d.id
|
|
|
|
order by d.id asc`
|
|
|
|
params = append(params, query.OrgId)
|
2018-02-01 10:27:29 -06:00
|
|
|
params = append(params, dialect.BooleanStr(true))
|
2018-01-30 16:31:02 -06:00
|
|
|
params = append(params, query.UserId)
|
|
|
|
params = append(params, query.UserId)
|
|
|
|
params = append(params, dialect.BooleanStr(false))
|
|
|
|
|
2018-02-01 10:27:29 -06:00
|
|
|
x.ShowSQL(true)
|
2018-01-30 16:31:02 -06:00
|
|
|
err := x.Sql(sql, params...).Find(&query.Result)
|
2018-02-01 10:27:29 -06:00
|
|
|
x.ShowSQL(false)
|
2018-01-30 16:31:02 -06:00
|
|
|
|
|
|
|
for _, p := range query.Result {
|
|
|
|
p.PermissionName = p.Permission.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-07-08 02:35:06 -05:00
|
|
|
func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error {
|
|
|
|
var dashboards = make([]*m.Dashboard, 0)
|
2017-12-20 05:15:49 -06:00
|
|
|
whereExpr := "org_id=? AND plugin_id=? AND is_folder=" + dialect.BooleanStr(false)
|
2016-07-08 02:35:06 -05:00
|
|
|
|
2017-12-20 05:15:49 -06:00
|
|
|
err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards)
|
2016-07-08 02:35:06 -05:00
|
|
|
query.Result = dashboards
|
2016-01-28 18:41:23 -06:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2016-03-17 03:01:58 -05:00
|
|
|
|
2016-03-20 05:52:19 -05:00
|
|
|
type DashboardSlugDTO struct {
|
|
|
|
Slug string
|
|
|
|
}
|
|
|
|
|
2016-03-17 03:01:58 -05:00
|
|
|
func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
|
2016-03-20 05:52:19 -05:00
|
|
|
var rawSql = `SELECT slug from dashboard WHERE Id=?`
|
|
|
|
var slug = DashboardSlugDTO{}
|
|
|
|
|
2018-02-01 06:32:00 -06:00
|
|
|
exists, err := x.SQL(rawSql, query.Id).Get(&slug)
|
2016-03-17 03:01:58 -05:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-03-20 05:52:19 -05:00
|
|
|
} else if exists == false {
|
2016-03-17 03:01:58 -05:00
|
|
|
return m.ErrDashboardNotFound
|
|
|
|
}
|
|
|
|
|
2016-03-20 05:52:19 -05:00
|
|
|
query.Result = slug.Slug
|
2016-03-17 03:01:58 -05:00
|
|
|
return nil
|
|
|
|
}
|
2018-01-31 09:51:06 -06:00
|
|
|
|
|
|
|
func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error {
|
2018-02-01 07:13:42 -06:00
|
|
|
var dashboards []*m.Dashboard
|
2018-01-31 09:51:06 -06:00
|
|
|
|
|
|
|
if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
query.Result = dashboards
|
|
|
|
return nil
|
|
|
|
}
|
2018-02-01 06:32:00 -06:00
|
|
|
|
|
|
|
func GetDashboardUIDById(query *m.GetDashboardUIDByIdQuery) error {
|
|
|
|
var rawSql = `SELECT uid, slug from dashboard WHERE Id=?`
|
|
|
|
|
|
|
|
us := &m.DashboardRef{}
|
|
|
|
|
|
|
|
exists, err := x.SQL(rawSql, query.Id).Get(us)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if exists == false {
|
|
|
|
return m.ErrDashboardNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
query.Result = us
|
|
|
|
return nil
|
|
|
|
}
|