Refactor: Add UID endpoint for get dashboard version (#48600)

* Refactor: Add UID endpoint for get dashboard version

* Add initial docs language

* Add new endpoint in swagger

* Change access control to dashboards

* Add parameters to swagger endpoint

* Return UID in response; Update docs to reflect this; Implement Ying suggestion

* Update docs/sources/http_api/dashboard_versions.md

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

* Update pkg/api/api.go

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

* Update pkg/models/dashboard_version.go

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

* Rename UID to DashboardUID for clarity; use dashUID in method

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
Kat Yang 2022-05-17 06:59:02 -04:00 committed by GitHub
parent ce6a5694ff
commit 9a0f2ec449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 17 deletions

View File

@ -67,6 +67,8 @@ Status Codes:
## Get dashboard version ## Get dashboard version
> **Warning:** This API is deprecated since Grafana v9.0.0 and will be removed in a future release. Refer to the [new get dashboard version API](#get-dashboard-version-by-dashboard-uid).
`GET /api/dashboards/id/:dashboardId/versions/:version` `GET /api/dashboards/id/:dashboardId/versions/:version`
Get the dashboard version with the given version, for the dashboard with the given id. Get the dashboard version with the given version, for the dashboard with the given id.
@ -177,6 +179,119 @@ Status Codes:
- **401** - Unauthorized - **401** - Unauthorized
- **404** - Dashboard version not found - **404** - Dashboard version not found
## Get dashboard version by dashboard UID
`GET /api/dashboards/uid/:uid/versions/:version`
Get the dashboard version with the given version, for the dashboard with the given UID.
**Example request for getting a dashboard version**:
```http
GET /api/dashboards/id/1/versions/1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 1300
{
"id": 1,
"dashboardId": 1,
"uid": "QA7wKklGz",
"parentVersion": 0,
"restoredFrom": 0,
"version": 1,
"created": "2017-04-26T17:18:38-04:00",
"message": "Initial save",
"data": {
"annotations": {
"list": [
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"hideControls": false,
"id": 1,
"links": [
],
"rows": [
{
"collapse": false,
"height": "250px",
"panels": [
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6"
}
],
"schemaVersion": 14,
"style": "dark",
"tags": [
],
"templating": {
"list": [
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "browser",
"title": "test",
"version": 1
},
"createdBy": "admin"
}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **404** - Dashboard version not found
## Restore dashboard ## Restore dashboard
`POST /api/dashboards/id/:dashboardId/restore` `POST /api/dashboards/id/:dashboardId/restore`

View File

@ -369,6 +369,7 @@ func (hs *HTTPServer) registerRoutes() {
dashboardRoute.Get("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsRead)), routing.Wrap(hs.GetDashboard)) dashboardRoute.Get("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsRead)), routing.Wrap(hs.GetDashboard))
dashboardRoute.Delete("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsDelete)), routing.Wrap(hs.DeleteDashboardByUID)) dashboardRoute.Delete("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsDelete)), routing.Wrap(hs.DeleteDashboardByUID))
dashboardRoute.Group("/uid/:uid", func(dashUidRoute routing.RouteRegister) { dashboardRoute.Group("/uid/:uid", func(dashUidRoute routing.RouteRegister) {
dashUidRoute.Get("/versions/:id", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.GetDashboardVersion))
dashUidRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) { dashUidRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
dashboardPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsRead)), routing.Wrap(hs.GetDashboardPermissionList)) dashboardPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsRead)), routing.Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite)), routing.Wrap(hs.UpdateDashboardPermissions)) dashboardPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite)), routing.Wrap(hs.UpdateDashboardPermissions))

View File

@ -558,10 +558,26 @@ func (hs *HTTPServer) GetDashboardVersions(c *models.ReqContext) response.Respon
// GetDashboardVersion returns the dashboard version with the given ID. // GetDashboardVersion returns the dashboard version with the given ID.
func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Response { func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Response {
dashID, err := strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64) var dashID int64
var err error
dashUID := web.Params(c.Req)[":uid"]
if dashUID == "" {
dashID, err = strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
if err != nil { if err != nil {
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err) return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
} }
} else {
q := models.GetDashboardQuery{
OrgId: c.SignedInUser.OrgId,
Uid: dashUID,
}
if err := hs.SQLStore.GetDashboard(c.Req.Context(), &q); err != nil {
return response.Error(http.StatusBadRequest, "failed to get dashboard by UID", err)
}
dashID = q.Result.Id
}
guardian := guardian.New(c.Req.Context(), dashID, c.OrgId, c.SignedInUser) guardian := guardian.New(c.Req.Context(), dashID, c.OrgId, c.SignedInUser)
if canSave, err := guardian.CanSave(); err != nil || !canSave { if canSave, err := guardian.CanSave(); err != nil || !canSave {
@ -587,6 +603,7 @@ func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Respons
dashVersionMeta := &models.DashboardVersionMeta{ dashVersionMeta := &models.DashboardVersionMeta{
Id: query.Result.Id, Id: query.Result.Id,
DashboardId: query.Result.DashboardId, DashboardId: query.Result.DashboardId,
DashboardUID: dashUID,
Data: query.Result.Data, Data: query.Result.Data,
ParentVersion: query.Result.ParentVersion, ParentVersion: query.Result.ParentVersion,
RestoredFrom: query.Result.RestoredFrom, RestoredFrom: query.Result.RestoredFrom,

View File

@ -101,7 +101,7 @@ import (
// 422: unprocessableEntityError // 422: unprocessableEntityError
// 500: internalServerError // 500: internalServerError
// swagger:parameters getDashboardByUID deleteDashboardByUID getDashboardPermissionsWithUid postDashboardPermissionsWithUid // swagger:parameters getDashboardByUID deleteDashboardByUID getDashboardPermissionsWithUid postDashboardPermissionsWithUid getDashboardVersionByUID
type UID struct { type UID struct {
// in:path // in:path
// required:true // required:true

View File

@ -17,6 +17,21 @@ import "github.com/grafana/grafana/pkg/models"
// //
// Get a specific dashboard version. // Get a specific dashboard version.
// //
// Please refer to [updated API](#/dashboard_versions/getDashboardVersionByUID) instead
//
// Deprecated: true
//
// Responses:
// 200: dashboardVersionResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
// swagger:route GET /dashboards/uid/{uid}/versions/{DashboardVersionID} dashboard_versions getDashboardVersionByUID
//
// Get a specific dashboard version using UID.
//
// Responses: // Responses:
// 200: dashboardVersionResponse // 200: dashboardVersionResponse
// 401: unauthorisedError // 401: unauthorisedError
@ -43,7 +58,7 @@ type DashboardIdParam struct {
DashboardID int64 DashboardID int64
} }
// swagger:parameters getDashboardVersion // swagger:parameters getDashboardVersion getDashboardVersionByUID
type DashboardVersionIdParam struct { type DashboardVersionIdParam struct {
// in:path // in:path
DashboardVersionID int64 DashboardVersionID int64

View File

@ -34,6 +34,7 @@ type DashboardVersion struct {
type DashboardVersionMeta struct { type DashboardVersionMeta struct {
Id int64 `json:"id"` Id int64 `json:"id"`
DashboardId int64 `json:"dashboardId"` DashboardId int64 `json:"dashboardId"`
DashboardUID string `json:"uid"`
ParentVersion int `json:"parentVersion"` ParentVersion int `json:"parentVersion"`
RestoredFrom int `json:"restoredFrom"` RestoredFrom int `json:"restoredFrom"`
Version int `json:"version"` Version int `json:"version"`

View File

@ -3715,9 +3715,11 @@
}, },
"/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": { "/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": {
"get": { "get": {
"description": "Please refer to [updated API](#/dashboard_versions/getDashboardVersionByUID) instead",
"tags": ["dashboard_versions"], "tags": ["dashboard_versions"],
"summary": "Get a specific dashboard version.", "summary": "Get a specific dashboard version.",
"operationId": "getDashboardVersion", "operationId": "getDashboardVersion",
"deprecated": true,
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
@ -3978,6 +3980,46 @@
} }
} }
}, },
"/dashboards/uid/{uid}/versions/{DashboardVersionID}": {
"get": {
"tags": ["dashboard_versions"],
"summary": "Get a specific dashboard version using UID.",
"operationId": "getDashboardVersionByUID",
"parameters": [
{
"type": "string",
"x-go-name": "UID",
"name": "uid",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"name": "DashboardVersionID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/dashboardVersionResponse"
},
"401": {
"$ref": "#/responses/unauthorisedError"
},
"403": {
"$ref": "#/responses/forbiddenError"
},
"404": {
"$ref": "#/responses/notFoundError"
},
"500": {
"$ref": "#/responses/internalServerError"
}
}
}
},
"/datasources": { "/datasources": {
"get": { "get": {
"description": "If you are running Grafana Enterprise and have Fine-grained access control enabled\nyou need to have a permission with action: `datasources:read` and scope: `datasources:*`.", "description": "If you are running Grafana Enterprise and have Fine-grained access control enabled\nyou need to have a permission with action: `datasources:read` and scope: `datasources:*`.",
@ -4692,15 +4734,15 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"x-go-name": "PermissionID", "x-go-name": "DatasourceID",
"name": "permissionId", "name": "datasource_id",
"in": "path", "in": "path",
"required": true "required": true
}, },
{ {
"type": "string", "type": "string",
"x-go-name": "DatasourceID", "x-go-name": "PermissionID",
"name": "datasource_id", "name": "permissionId",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -13670,7 +13712,7 @@
"x-go-name": "SavedItems" "x-go-name": "SavedItems"
} }
}, },
"x-go-package": "github.com/grafana/grafana/pkg/services/preference" "x-go-package": "github.com/grafana/grafana/pkg/models"
}, },
"NewApiKeyResult": { "NewApiKeyResult": {
"type": "object", "type": "object",
@ -14754,7 +14796,7 @@
"x-go-name": "HomeTab" "x-go-name": "HomeTab"
} }
}, },
"x-go-package": "github.com/grafana/grafana/pkg/services/preference" "x-go-package": "github.com/grafana/grafana/pkg/models"
}, },
"Receiver": { "Receiver": {
"type": "object", "type": "object",

View File

@ -2777,9 +2777,11 @@
}, },
"/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": { "/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": {
"get": { "get": {
"description": "Please refer to [updated API](#/dashboard_versions/getDashboardVersionByUID) instead",
"tags": ["dashboard_versions"], "tags": ["dashboard_versions"],
"summary": "Get a specific dashboard version.", "summary": "Get a specific dashboard version.",
"operationId": "getDashboardVersion", "operationId": "getDashboardVersion",
"deprecated": true,
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
@ -3040,6 +3042,46 @@
} }
} }
}, },
"/dashboards/uid/{uid}/versions/{DashboardVersionID}": {
"get": {
"tags": ["dashboard_versions"],
"summary": "Get a specific dashboard version using UID.",
"operationId": "getDashboardVersionByUID",
"parameters": [
{
"type": "string",
"x-go-name": "UID",
"name": "uid",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"name": "DashboardVersionID",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/dashboardVersionResponse"
},
"401": {
"$ref": "#/responses/unauthorisedError"
},
"403": {
"$ref": "#/responses/forbiddenError"
},
"404": {
"$ref": "#/responses/notFoundError"
},
"500": {
"$ref": "#/responses/internalServerError"
}
}
}
},
"/datasources": { "/datasources": {
"get": { "get": {
"description": "If you are running Grafana Enterprise and have Fine-grained access control enabled\nyou need to have a permission with action: `datasources:read` and scope: `datasources:*`.", "description": "If you are running Grafana Enterprise and have Fine-grained access control enabled\nyou need to have a permission with action: `datasources:read` and scope: `datasources:*`.",
@ -3754,15 +3796,15 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"x-go-name": "PermissionID", "x-go-name": "DatasourceID",
"name": "permissionId", "name": "datasource_id",
"in": "path", "in": "path",
"required": true "required": true
}, },
{ {
"type": "string", "type": "string",
"x-go-name": "DatasourceID", "x-go-name": "PermissionID",
"name": "datasource_id", "name": "permissionId",
"in": "path", "in": "path",
"required": true "required": true
} }
@ -10758,7 +10800,7 @@
"x-go-name": "SavedItems" "x-go-name": "SavedItems"
} }
}, },
"x-go-package": "github.com/grafana/grafana/pkg/services/preference" "x-go-package": "github.com/grafana/grafana/pkg/models"
}, },
"NewApiKeyResult": { "NewApiKeyResult": {
"type": "object", "type": "object",
@ -11211,7 +11253,7 @@
"x-go-name": "HomeTab" "x-go-name": "HomeTab"
} }
}, },
"x-go-package": "github.com/grafana/grafana/pkg/services/preference" "x-go-package": "github.com/grafana/grafana/pkg/models"
}, },
"RecordingRuleJSON": { "RecordingRuleJSON": {
"description": "RecordingRuleJSON is the external representation of a recording rule", "description": "RecordingRuleJSON is the external representation of a recording rule",