From 9a0f2ec449b590c4d826037f96057c90f9dcba45 Mon Sep 17 00:00:00 2001 From: Kat Yang <69819079+yangkb09@users.noreply.github.com> Date: Tue, 17 May 2022 06:59:02 -0400 Subject: [PATCH] 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> --- docs/sources/http_api/dashboard_versions.md | 115 ++++++++++++++++++ pkg/api/api.go | 1 + pkg/api/dashboard.go | 23 +++- pkg/api/docs/definitions/dashboard.go | 2 +- .../docs/definitions/dashboard_versions.go | 17 ++- pkg/models/dashboard_version.go | 1 + public/api-merged.json | 54 +++++++- public/api-spec.json | 54 +++++++- 8 files changed, 250 insertions(+), 17 deletions(-) diff --git a/docs/sources/http_api/dashboard_versions.md b/docs/sources/http_api/dashboard_versions.md index 23494e40e0e..5f8127e2683 100644 --- a/docs/sources/http_api/dashboard_versions.md +++ b/docs/sources/http_api/dashboard_versions.md @@ -67,6 +67,8 @@ Status Codes: ## 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 the dashboard version with the given version, for the dashboard with the given id. @@ -177,6 +179,119 @@ Status Codes: - **401** - Unauthorized - **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 `POST /api/dashboards/id/:dashboardId/restore` diff --git a/pkg/api/api.go b/pkg/api/api.go index 359ac17c107..08bbb4ce42c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -369,6 +369,7 @@ func (hs *HTTPServer) registerRoutes() { 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.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) { dashboardPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsRead)), routing.Wrap(hs.GetDashboardPermissionList)) dashboardPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite)), routing.Wrap(hs.UpdateDashboardPermissions)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 8e78aaa5992..60a99d289d8 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -558,9 +558,25 @@ func (hs *HTTPServer) GetDashboardVersions(c *models.ReqContext) response.Respon // GetDashboardVersion returns the dashboard version with the given ID. func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Response { - dashID, err := strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "dashboardId is invalid", err) + 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 { + 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) @@ -587,6 +603,7 @@ func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Respons dashVersionMeta := &models.DashboardVersionMeta{ Id: query.Result.Id, DashboardId: query.Result.DashboardId, + DashboardUID: dashUID, Data: query.Result.Data, ParentVersion: query.Result.ParentVersion, RestoredFrom: query.Result.RestoredFrom, diff --git a/pkg/api/docs/definitions/dashboard.go b/pkg/api/docs/definitions/dashboard.go index a115763cf25..5884a8ba818 100644 --- a/pkg/api/docs/definitions/dashboard.go +++ b/pkg/api/docs/definitions/dashboard.go @@ -101,7 +101,7 @@ import ( // 422: unprocessableEntityError // 500: internalServerError -// swagger:parameters getDashboardByUID deleteDashboardByUID getDashboardPermissionsWithUid postDashboardPermissionsWithUid +// swagger:parameters getDashboardByUID deleteDashboardByUID getDashboardPermissionsWithUid postDashboardPermissionsWithUid getDashboardVersionByUID type UID struct { // in:path // required:true diff --git a/pkg/api/docs/definitions/dashboard_versions.go b/pkg/api/docs/definitions/dashboard_versions.go index ea1a30f94f9..498028903c9 100644 --- a/pkg/api/docs/definitions/dashboard_versions.go +++ b/pkg/api/docs/definitions/dashboard_versions.go @@ -17,6 +17,21 @@ import "github.com/grafana/grafana/pkg/models" // // 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: // 200: dashboardVersionResponse // 401: unauthorisedError @@ -43,7 +58,7 @@ type DashboardIdParam struct { DashboardID int64 } -// swagger:parameters getDashboardVersion +// swagger:parameters getDashboardVersion getDashboardVersionByUID type DashboardVersionIdParam struct { // in:path DashboardVersionID int64 diff --git a/pkg/models/dashboard_version.go b/pkg/models/dashboard_version.go index 23c80c38a62..98ed35d66a5 100644 --- a/pkg/models/dashboard_version.go +++ b/pkg/models/dashboard_version.go @@ -34,6 +34,7 @@ type DashboardVersion struct { type DashboardVersionMeta struct { Id int64 `json:"id"` DashboardId int64 `json:"dashboardId"` + DashboardUID string `json:"uid"` ParentVersion int `json:"parentVersion"` RestoredFrom int `json:"restoredFrom"` Version int `json:"version"` diff --git a/public/api-merged.json b/public/api-merged.json index eb54cbca70c..58a76056e98 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -3715,9 +3715,11 @@ }, "/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": { "get": { + "description": "Please refer to [updated API](#/dashboard_versions/getDashboardVersionByUID) instead", "tags": ["dashboard_versions"], "summary": "Get a specific dashboard version.", "operationId": "getDashboardVersion", + "deprecated": true, "parameters": [ { "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": { "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:*`.", @@ -4692,15 +4734,15 @@ "parameters": [ { "type": "string", - "x-go-name": "PermissionID", - "name": "permissionId", + "x-go-name": "DatasourceID", + "name": "datasource_id", "in": "path", "required": true }, { "type": "string", - "x-go-name": "DatasourceID", - "name": "datasource_id", + "x-go-name": "PermissionID", + "name": "permissionId", "in": "path", "required": true } @@ -13670,7 +13712,7 @@ "x-go-name": "SavedItems" } }, - "x-go-package": "github.com/grafana/grafana/pkg/services/preference" + "x-go-package": "github.com/grafana/grafana/pkg/models" }, "NewApiKeyResult": { "type": "object", @@ -14754,7 +14796,7 @@ "x-go-name": "HomeTab" } }, - "x-go-package": "github.com/grafana/grafana/pkg/services/preference" + "x-go-package": "github.com/grafana/grafana/pkg/models" }, "Receiver": { "type": "object", diff --git a/public/api-spec.json b/public/api-spec.json index f4bb5c87778..2366bec4863 100644 --- a/public/api-spec.json +++ b/public/api-spec.json @@ -2777,9 +2777,11 @@ }, "/dashboards/id/{DashboardID}/versions/{DashboardVersionID}": { "get": { + "description": "Please refer to [updated API](#/dashboard_versions/getDashboardVersionByUID) instead", "tags": ["dashboard_versions"], "summary": "Get a specific dashboard version.", "operationId": "getDashboardVersion", + "deprecated": true, "parameters": [ { "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": { "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:*`.", @@ -3754,15 +3796,15 @@ "parameters": [ { "type": "string", - "x-go-name": "PermissionID", - "name": "permissionId", + "x-go-name": "DatasourceID", + "name": "datasource_id", "in": "path", "required": true }, { "type": "string", - "x-go-name": "DatasourceID", - "name": "datasource_id", + "x-go-name": "PermissionID", + "name": "permissionId", "in": "path", "required": true } @@ -10758,7 +10800,7 @@ "x-go-name": "SavedItems" } }, - "x-go-package": "github.com/grafana/grafana/pkg/services/preference" + "x-go-package": "github.com/grafana/grafana/pkg/models" }, "NewApiKeyResult": { "type": "object", @@ -11211,7 +11253,7 @@ "x-go-name": "HomeTab" } }, - "x-go-package": "github.com/grafana/grafana/pkg/services/preference" + "x-go-package": "github.com/grafana/grafana/pkg/models" }, "RecordingRuleJSON": { "description": "RecordingRuleJSON is the external representation of a recording rule",