2014-12-29 06:36:08 -06:00
package api
import (
2021-10-07 09:33:50 -05:00
"context"
2020-03-18 06:08:52 -05:00
"encoding/json"
2020-11-19 06:34:28 -06:00
"errors"
2020-04-22 03:30:06 -05:00
"fmt"
2021-11-29 03:18:01 -06:00
"net/http"
2016-03-15 14:29:35 -05:00
"sort"
2022-01-14 10:55:57 -06:00
"strconv"
2022-09-20 12:31:08 -05:00
"strings"
2016-03-15 14:29:35 -05:00
2023-11-03 09:02:57 -05:00
"github.com/grafana/grafana-plugin-sdk-go/backend"
2023-11-14 14:50:27 -06:00
2020-05-12 06:04:18 -05:00
"github.com/grafana/grafana/pkg/api/datasource"
2015-02-05 03:37:13 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2024-06-12 23:11:35 -05:00
"github.com/grafana/grafana/pkg/apimachinery/identity"
2022-09-20 12:31:08 -05:00
"github.com/grafana/grafana/pkg/components/simplejson"
2020-06-17 04:17:11 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2022-03-24 06:21:26 -05:00
"github.com/grafana/grafana/pkg/services/datasources"
2023-10-18 10:09:22 -05:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-09-20 12:31:08 -05:00
"github.com/grafana/grafana/pkg/setting"
2015-06-01 05:15:49 -05:00
"github.com/grafana/grafana/pkg/util"
2021-10-11 07:30:59 -05:00
"github.com/grafana/grafana/pkg/web"
2014-12-29 06:36:08 -06:00
)
2020-06-17 04:17:11 -05:00
var datasourcesLogger = log . New ( "datasources" )
2022-06-27 11:23:15 -05:00
var secretsPluginError datasources . ErrDatasourceSecretsPluginUserFriendly
2020-06-17 04:17:11 -05:00
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources datasources getDataSources
//
// Get all data sources.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:read` and scope: `datasources:*`.
//
// Responses:
// 200: getDataSourcesResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetDataSources ( c * contextmodel . ReqContext ) response . Response {
2023-10-06 04:34:36 -05:00
query := datasources . GetDataSourcesQuery { OrgID : c . SignedInUser . GetOrgID ( ) , DataSourceLimit : hs . Cfg . DataSourceLimit }
2014-12-29 06:36:08 -06:00
2023-02-09 08:49:44 -06:00
dataSources , err := hs . DataSourcesService . GetDataSources ( c . Req . Context ( ) , & query )
if err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasources" , err )
2014-12-29 06:36:08 -06:00
}
2024-09-10 04:19:02 -05:00
filtered , err := hs . dsGuardian . New ( c . SignedInUser . OrgID , c . SignedInUser ) . FilterDatasourcesByReadPermissions ( dataSources )
2022-01-17 03:16:12 -06:00
if err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasources" , err )
2022-01-17 03:16:12 -06:00
}
2016-03-15 14:29:35 -05:00
result := make ( dtos . DataSourceList , 0 )
2022-01-17 03:16:12 -06:00
for _ , ds := range filtered {
2017-04-25 08:17:49 -05:00
dsItem := dtos . DataSourceListItemDTO {
2023-02-02 10:22:43 -06:00
OrgId : ds . OrgID ,
Id : ds . ID ,
UID : ds . UID ,
2014-12-29 06:36:08 -06:00
Name : ds . Name ,
2023-02-02 10:22:43 -06:00
Url : ds . URL ,
2014-12-29 06:36:08 -06:00
Type : ds . Type ,
2021-02-22 06:02:10 -06:00
TypeName : ds . Type ,
2014-12-29 06:36:08 -06:00
Access : ds . Access ,
Database : ds . Database ,
User : ds . User ,
BasicAuth : ds . BasicAuth ,
2015-01-09 09:36:23 -06:00
IsDefault : ds . IsDefault ,
2016-09-01 10:21:13 -05:00
JsonData : ds . JsonData ,
2017-10-24 08:28:39 -05:00
ReadOnly : ds . ReadOnly ,
2014-12-29 06:36:08 -06:00
}
2016-03-15 14:29:35 -05:00
2021-11-17 05:04:22 -06:00
if plugin , exists := hs . pluginStore . Plugin ( c . Req . Context ( ) , ds . Type ) ; exists {
2016-03-15 14:29:35 -05:00
dsItem . TypeLogoUrl = plugin . Info . Logos . Small
2021-02-22 06:02:10 -06:00
dsItem . TypeName = plugin . Name
2023-06-02 12:46:12 -05:00
dsItem . Type = plugin . ID // may be from an alias
2016-03-15 14:29:35 -05:00
} else {
2016-03-23 09:09:48 -05:00
dsItem . TypeLogoUrl = "public/img/icn-datasource.svg"
2016-03-15 14:29:35 -05:00
}
result = append ( result , dsItem )
2014-12-29 06:36:08 -06:00
}
2016-03-15 14:29:35 -05:00
sort . Sort ( result )
2017-02-08 07:20:07 -06:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & result )
2014-12-29 06:36:08 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/{id} datasources getDataSourceByID
//
// Get a single data source by Id.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:read` and scopes: `datasources:*`, `datasources:id:*` and `datasources:id:1` (single data source).
//
// Please refer to [updated API](#/datasources/getDataSourceByUID) instead
//
// Deprecated: true
//
// Responses:
// 200: getDataSourceResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetDataSourceById ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
2022-09-05 08:10:45 -05:00
return response . Error ( http . StatusBadRequest , "id is invalid" , nil )
2022-01-14 10:55:57 -06:00
}
2022-06-27 11:23:15 -05:00
query := datasources . GetDataSourceQuery {
2023-02-02 10:22:43 -06:00
ID : id ,
2023-10-06 04:34:36 -05:00
OrgID : c . SignedInUser . GetOrgID ( ) ,
2015-02-14 03:04:27 -06:00
}
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( c . Req . Context ( ) , & query )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2015-11-13 02:43:25 -06:00
}
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceIdentifierNotSet ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Datasource id is missing" , nil )
2021-03-29 01:56:58 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasources" , err )
2015-02-14 03:04:27 -06:00
}
2023-02-09 08:49:44 -06:00
dto := hs . convertModelToDtos ( c . Req . Context ( ) , dataSource )
2022-02-18 04:27:00 -06:00
2021-12-15 05:08:15 -06:00
// Add accesscontrol metadata
2024-10-07 05:08:16 -05:00
dto . AccessControl = getAccessControlMetadata ( c , datasources . ScopePrefix , dto . UID )
2021-12-15 05:08:15 -06:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & dto )
2015-02-14 03:04:27 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /datasources/{id} datasources deleteDataSourceByID
//
// Delete an existing data source by id.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:delete` and scopes: `datasources:*`, `datasources:id:*` and `datasources:id:1` (single data source).
//
// Please refer to [updated API](#/datasources/deleteDataSourceByUID) instead
//
// Deprecated: true
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 404: notFoundError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) DeleteDataSourceById ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2014-12-29 06:36:08 -06:00
if id <= 0 {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Missing valid datasource id" , nil )
2014-12-29 06:36:08 -06:00
}
2023-10-06 04:34:36 -05:00
ds , err := hs . getRawDataSourceById ( c . Req . Context ( ) , id , c . SignedInUser . GetOrgID ( ) )
2017-10-24 08:28:39 -05:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2021-01-13 12:16:27 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Failed to delete datasource" , nil )
2021-01-13 12:16:27 -06:00
}
if ds . ReadOnly {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusForbidden , "Cannot delete read-only data source" , nil )
2021-01-13 12:16:27 -06:00
}
2023-10-06 04:34:36 -05:00
cmd := & datasources . DeleteDataSourceCommand { ID : id , OrgID : c . SignedInUser . GetOrgID ( ) , Name : ds . Name }
2021-01-13 12:16:27 -06:00
2022-02-11 08:52:14 -06:00
err = hs . DataSourcesService . DeleteDataSource ( c . Req . Context ( ) , cmd )
2021-01-13 12:16:27 -06:00
if err != nil {
2022-06-16 11:26:57 -05:00
if errors . As ( err , & secretsPluginError ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource: " + err . Error ( ) , err )
2022-06-16 11:26:57 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource" , err )
2021-01-13 12:16:27 -06:00
}
2023-10-06 04:34:36 -05:00
hs . Live . HandleDatasourceDelete ( c . SignedInUser . GetOrgID ( ) , ds . UID )
2021-05-18 13:39:56 -05:00
2021-01-15 07:43:20 -06:00
return response . Success ( "Data source deleted" )
2021-01-13 12:16:27 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/uid/{uid} datasources getDataSourceByUID
//
// Get a single data source by UID.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:read` and scopes: `datasources:*`, `datasources:uid:*` and `datasources:uid:kLtEtcRGk` (single data source).
//
// Responses:
// 200: getDataSourceResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetDataSourceByUID ( c * contextmodel . ReqContext ) response . Response {
2023-10-06 04:34:36 -05:00
ds , err := hs . getRawDataSourceByUID ( c . Req . Context ( ) , web . Params ( c . Req ) [ ":uid" ] , c . SignedInUser . GetOrgID ( ) )
2021-01-13 12:16:27 -06:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2021-12-15 05:08:15 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2021-01-13 12:16:27 -06:00
}
2021-12-15 05:08:15 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasource" , err )
2021-01-13 12:16:27 -06:00
}
2022-06-09 06:56:24 -05:00
dto := hs . convertModelToDtos ( c . Req . Context ( ) , ds )
2021-12-15 05:08:15 -06:00
// Add accesscontrol metadata
2024-10-07 05:08:16 -05:00
dto . AccessControl = getAccessControlMetadata ( c , datasources . ScopePrefix , dto . UID )
2022-03-24 02:58:10 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & dto )
2021-01-13 12:16:27 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /datasources/uid/{uid} datasources deleteDataSourceByUID
//
// Delete an existing data source by UID.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:delete` and scopes: `datasources:*`, `datasources:uid:*` and `datasources:uid:kLtEtcRGk` (single data source).
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) DeleteDataSourceByUID ( c * contextmodel . ReqContext ) response . Response {
2021-10-11 07:30:59 -05:00
uid := web . Params ( c . Req ) [ ":uid" ]
2021-01-13 12:16:27 -06:00
if uid == "" {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Missing datasource uid" , nil )
2021-01-13 12:16:27 -06:00
}
2023-10-06 04:34:36 -05:00
ds , err := hs . getRawDataSourceByUID ( c . Req . Context ( ) , uid , c . SignedInUser . GetOrgID ( ) )
2021-01-13 12:16:27 -06:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2021-01-13 12:16:27 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Failed to delete datasource" , nil )
2017-10-24 08:28:39 -05:00
}
if ds . ReadOnly {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusForbidden , "Cannot delete read-only data source" , nil )
2017-10-24 08:28:39 -05:00
}
2023-10-06 04:34:36 -05:00
cmd := & datasources . DeleteDataSourceCommand { UID : uid , OrgID : c . SignedInUser . GetOrgID ( ) , Name : ds . Name }
2017-02-09 20:11:36 -06:00
2022-02-11 08:52:14 -06:00
err = hs . DataSourcesService . DeleteDataSource ( c . Req . Context ( ) , cmd )
2017-02-09 20:11:36 -06:00
if err != nil {
2022-06-16 11:26:57 -05:00
if errors . As ( err , & secretsPluginError ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource: " + err . Error ( ) , err )
2022-06-16 11:26:57 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource" , err )
2017-02-09 20:11:36 -06:00
}
2023-10-06 04:34:36 -05:00
hs . Live . HandleDatasourceDelete ( c . SignedInUser . GetOrgID ( ) , ds . UID )
2021-05-18 13:39:56 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2021-11-05 09:06:14 -05:00
"message" : "Data source deleted" ,
2023-02-02 10:22:43 -06:00
"id" : ds . ID ,
2021-11-05 09:06:14 -05:00
} )
2017-02-09 20:11:36 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route DELETE /datasources/name/{name} datasources deleteDataSourceByName
//
// Delete an existing data source by name.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:delete` and scopes: `datasources:*`, `datasources:name:*` and `datasources:name:test_datasource` (single data source).
//
// Responses:
// 200: deleteDataSourceByNameResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) DeleteDataSourceByName ( c * contextmodel . ReqContext ) response . Response {
2021-10-11 07:30:59 -05:00
name := web . Params ( c . Req ) [ ":name" ]
2017-02-09 20:11:36 -06:00
if name == "" {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusBadRequest , "Missing valid datasource name" , nil )
2017-02-09 20:11:36 -06:00
}
2023-10-06 04:34:36 -05:00
getCmd := & datasources . GetDataSourceQuery { Name : name , OrgID : c . SignedInUser . GetOrgID ( ) }
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( c . Req . Context ( ) , getCmd )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2018-06-22 21:15:36 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource" , err )
2017-10-24 08:28:39 -05:00
}
2023-02-09 08:49:44 -06:00
if dataSource . ReadOnly {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusForbidden , "Cannot delete read-only data source" , nil )
2017-10-24 08:28:39 -05:00
}
2014-12-29 06:36:08 -06:00
2023-10-06 04:34:36 -05:00
cmd := & datasources . DeleteDataSourceCommand { Name : name , OrgID : c . SignedInUser . GetOrgID ( ) }
2023-02-09 08:49:44 -06:00
err = hs . DataSourcesService . DeleteDataSource ( c . Req . Context ( ) , cmd )
2014-12-29 06:36:08 -06:00
if err != nil {
2022-06-16 11:26:57 -05:00
if errors . As ( err , & secretsPluginError ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource: " + err . Error ( ) , err )
2022-06-16 11:26:57 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to delete datasource" , err )
2014-12-29 06:36:08 -06:00
}
2023-10-06 04:34:36 -05:00
hs . Live . HandleDatasourceDelete ( c . SignedInUser . GetOrgID ( ) , dataSource . UID )
2021-05-18 13:39:56 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2020-07-31 01:22:09 -05:00
"message" : "Data source deleted" ,
2023-02-09 08:49:44 -06:00
"id" : dataSource . ID ,
2020-07-31 01:22:09 -05:00
} )
2014-12-29 06:36:08 -06:00
}
2022-01-31 09:39:55 -06:00
func validateURL ( cmdType string , url string ) response . Response {
if _ , err := datasource . ValidateURL ( cmdType , url ) ; err != nil {
2022-07-29 07:46:51 -05:00
datasourcesLogger . Error ( "Failed to validate URL" , "url" , url )
return response . Error ( http . StatusBadRequest , "Validation error, invalid URL" , err )
2020-04-22 03:30:06 -05:00
}
return nil
}
2022-09-20 12:31:08 -05:00
// validateJSONData prevents the user from adding a custom header with name that matches the auth proxy header name.
// This is done to prevent data source proxy from being used to circumvent auth proxy.
// For more context take a look at CVE-2022-35957
2024-10-25 04:07:53 -05:00
func validateJSONData ( jsonData * simplejson . Json , cfg * setting . Cfg ) error {
2023-11-03 09:02:57 -05:00
if jsonData == nil {
2022-09-20 12:31:08 -05:00
return nil
}
2024-03-01 04:31:06 -06:00
if cfg . AuthProxy . Enabled {
2023-11-03 09:02:57 -05:00
for key , value := range jsonData . MustMap ( ) {
if strings . HasPrefix ( key , datasources . CustomHeaderName ) {
header := fmt . Sprint ( value )
2024-03-01 04:31:06 -06:00
if http . CanonicalHeaderKey ( header ) == http . CanonicalHeaderKey ( cfg . AuthProxy . HeaderName ) {
2023-11-03 09:02:57 -05:00
datasourcesLogger . Error ( "Forbidden to add a data source header with a name equal to auth proxy header name" , "headerName" , key )
return errors . New ( "validation error, invalid header name specified" )
}
2022-09-20 12:31:08 -05:00
}
}
}
2023-10-17 05:23:54 -05:00
2022-09-20 12:31:08 -05:00
return nil
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /datasources datasources addDataSource
//
// Create a data source.
//
// By defining `password` and `basicAuthPassword` under secureJsonData property
// Grafana encrypts them securely as an encrypted blob in the database.
// The response then lists the encrypted fields under secureJsonFields.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:create`
//
// Responses:
// 200: createOrUpdateDatasourceResponse
// 401: unauthorisedError
// 403: forbiddenError
// 409: conflictError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) AddDataSource ( c * contextmodel . ReqContext ) response . Response {
2022-06-27 11:23:15 -05:00
cmd := datasources . AddDataSourceCommand { }
2021-11-29 03:18:01 -06:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-01-31 09:39:55 -06:00
2024-08-09 10:20:24 -05:00
userID , _ := identity . UserIdentifier ( c . SignedInUser . GetID ( ) )
2023-10-09 09:07:28 -05:00
2023-02-02 10:22:43 -06:00
datasourcesLogger . Debug ( "Received command to add data source" , "url" , cmd . URL )
2023-10-06 04:34:36 -05:00
cmd . OrgID = c . SignedInUser . GetOrgID ( )
2023-10-09 09:07:28 -05:00
cmd . UserID = userID
2023-02-02 10:22:43 -06:00
if cmd . URL != "" {
if resp := validateURL ( cmd . Type , cmd . URL ) ; resp != nil {
2022-01-31 09:39:55 -06:00
return resp
}
2020-04-22 03:30:06 -05:00
}
2023-11-08 08:37:32 -06:00
2024-10-25 04:07:53 -05:00
if err := validateJSONData ( cmd . JsonData , hs . Cfg ) ; err != nil {
2022-09-20 12:31:08 -05:00
return response . Error ( http . StatusBadRequest , "Failed to add datasource" , err )
}
2014-12-29 06:36:08 -06:00
2024-10-25 04:07:53 -05:00
// It's forbidden to update the rules from the datasource api.
// team HTTP headers update have to be done through `updateDatasourceLBACRules`
if hs . Features != nil && hs . Features . IsEnabled ( c . Req . Context ( ) , featuremgmt . FlagTeamHttpHeaders ) {
if cmd . JsonData != nil {
if _ , ok := cmd . JsonData . CheckGet ( "teamHttpHeaders" ) ; ok {
return response . Error ( http . StatusForbidden , "Cannot create datasource with team HTTP headers, need to use updateDatasourceLBACRules API" , nil )
}
}
}
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . AddDataSource ( c . Req . Context ( ) , & cmd )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNameExists ) || errors . Is ( err , datasources . ErrDataSourceUidExists ) {
2023-07-17 09:27:19 -05:00
return response . Error ( http . StatusConflict , err . Error ( ) , err )
2016-10-01 09:41:27 -05:00
}
2022-06-16 11:26:57 -05:00
if errors . As ( err , & secretsPluginError ) {
2023-07-17 09:27:19 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to add datasource: " + err . Error ( ) , err )
2022-06-16 11:26:57 -05:00
}
2023-07-17 09:27:19 -05:00
return response . ErrOrFallback ( http . StatusInternalServerError , "Failed to add datasource" , err )
2014-12-29 06:36:08 -06:00
}
2022-11-24 08:38:55 -06:00
// Clear permission cache for the user who's created the data source, so that new permissions are fetched for their next call
// Required for cases when caller wants to immediately interact with the newly created object
2023-08-25 08:19:58 -05:00
hs . accesscontrolService . ClearUserPermissionCache ( c . SignedInUser )
2022-11-24 08:38:55 -06:00
2023-02-09 08:49:44 -06:00
ds := hs . convertModelToDtos ( c . Req . Context ( ) , dataSource )
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2017-10-19 10:28:54 -05:00
"message" : "Datasource added" ,
2023-02-09 08:49:44 -06:00
"id" : dataSource . ID ,
"name" : dataSource . Name ,
2017-10-19 10:28:54 -05:00
"datasource" : ds ,
} )
2014-12-29 06:36:08 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /datasources/{id} datasources updateDataSourceByID
//
// Update an existing data source by its sequential ID.
//
// Similar to creating a data source, `password` and `basicAuthPassword` should be defined under
// secureJsonData in order to be stored securely as an encrypted blob in the database. Then, the
// encrypted fields are listed under secureJsonFields section in the response.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:write` and scopes: `datasources:*`, `datasources:id:*` and `datasources:id:1` (single data source).
//
// Please refer to [updated API](#/datasources/updateDataSourceByUID) instead
//
// Deprecated: true
//
// Responses:
// 200: createOrUpdateDatasourceResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateDataSourceByID ( c * contextmodel . ReqContext ) response . Response {
2022-06-27 11:23:15 -05:00
cmd := datasources . UpdateDataSourceCommand { }
2021-11-29 03:18:01 -06:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2023-02-02 10:22:43 -06:00
datasourcesLogger . Debug ( "Received command to update data source" , "url" , cmd . URL )
2023-10-06 04:34:36 -05:00
cmd . OrgID = c . SignedInUser . GetOrgID ( )
2022-01-14 10:55:57 -06:00
var err error
2023-02-02 10:22:43 -06:00
if cmd . ID , err = strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 ) ; err != nil {
2022-01-14 10:55:57 -06:00
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2023-02-02 10:22:43 -06:00
if resp := validateURL ( cmd . Type , cmd . URL ) ; resp != nil {
2020-04-22 03:30:06 -05:00
return resp
}
2024-10-25 04:07:53 -05:00
if err := validateJSONData ( cmd . JsonData , hs . Cfg ) ; err != nil {
2022-09-20 12:31:08 -05:00
return response . Error ( http . StatusBadRequest , "Failed to update datasource" , err )
}
2023-02-02 10:22:43 -06:00
ds , err := hs . getRawDataSourceById ( c . Req . Context ( ) , cmd . ID , cmd . OrgID )
2022-01-21 17:22:43 -06:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2022-01-21 17:22:43 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to update datasource" , err )
2022-01-21 17:22:43 -06:00
}
2023-11-08 08:37:32 -06:00
2022-05-23 08:13:13 -05:00
return hs . updateDataSourceByID ( c , ds , cmd )
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /datasources/uid/{uid} datasources updateDataSourceByUID
//
// Update an existing data source.
//
// Similar to creating a data source, `password` and `basicAuthPassword` should be defined under
// secureJsonData in order to be stored securely as an encrypted blob in the database. Then, the
// encrypted fields are listed under secureJsonFields section in the response.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:write` and scopes: `datasources:*`, `datasources:uid:*` and `datasources:uid:1` (single data source).
//
// Responses:
// 200: createOrUpdateDatasourceResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateDataSourceByUID ( c * contextmodel . ReqContext ) response . Response {
2022-06-27 11:23:15 -05:00
cmd := datasources . UpdateDataSourceCommand { }
2022-05-23 08:13:13 -05:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2023-02-02 10:22:43 -06:00
datasourcesLogger . Debug ( "Received command to update data source" , "url" , cmd . URL )
2023-10-06 04:34:36 -05:00
cmd . OrgID = c . SignedInUser . GetOrgID ( )
2023-02-02 10:22:43 -06:00
if resp := validateURL ( cmd . Type , cmd . URL ) ; resp != nil {
2022-05-23 08:13:13 -05:00
return resp
}
2024-10-25 04:07:53 -05:00
if err := validateJSONData ( cmd . JsonData , hs . Cfg ) ; err != nil {
2022-09-20 12:31:08 -05:00
return response . Error ( http . StatusBadRequest , "Failed to update datasource" , err )
}
2022-05-23 08:13:13 -05:00
2023-10-06 04:34:36 -05:00
ds , err := hs . getRawDataSourceByUID ( c . Req . Context ( ) , web . Params ( c . Req ) [ ":uid" ] , c . SignedInUser . GetOrgID ( ) )
2022-05-23 08:13:13 -05:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2022-05-23 08:13:13 -05:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
}
return response . Error ( http . StatusInternalServerError , "Failed to update datasource" , err )
}
2023-02-02 10:22:43 -06:00
cmd . ID = ds . ID
2023-11-08 08:37:32 -06:00
2022-05-23 08:13:13 -05:00
return hs . updateDataSourceByID ( c , ds , cmd )
}
2022-01-21 17:22:43 -06:00
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) updateDataSourceByID ( c * contextmodel . ReqContext , ds * datasources . DataSource , cmd datasources . UpdateDataSourceCommand ) response . Response {
2022-01-21 17:22:43 -06:00
if ds . ReadOnly {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusForbidden , "Cannot update read-only data source" , nil )
2022-01-21 17:22:43 -06:00
}
2023-02-09 08:49:44 -06:00
_ , err := hs . DataSourcesService . UpdateDataSource ( c . Req . Context ( ) , & cmd )
2016-11-18 09:44:59 -06:00
if err != nil {
2023-07-17 09:27:19 -05:00
if errors . Is ( err , datasources . ErrDataSourceNameExists ) {
return response . Error ( http . StatusConflict , "Failed to update datasource: " + err . Error ( ) , err )
}
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceUpdatingOldVersion ) {
2023-07-17 09:27:19 -05:00
return response . Error ( http . StatusConflict , "Datasource has already been updated by someone else. Please reload and try again" , err )
2017-10-19 10:28:54 -05:00
}
2022-06-16 11:26:57 -05:00
if errors . As ( err , & secretsPluginError ) {
2023-07-17 09:27:19 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to update datasource: " + err . Error ( ) , err )
2022-06-16 11:26:57 -05:00
}
2023-07-17 09:27:19 -05:00
return response . ErrOrFallback ( http . StatusInternalServerError , "Failed to update datasource" , err )
2014-12-29 06:36:08 -06:00
}
2018-08-07 10:56:02 -05:00
2022-06-27 11:23:15 -05:00
query := datasources . GetDataSourceQuery {
2023-02-02 10:22:43 -06:00
ID : cmd . ID ,
2023-10-06 04:34:36 -05:00
OrgID : c . SignedInUser . GetOrgID ( ) ,
2018-08-07 10:56:02 -05:00
}
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( c . Req . Context ( ) , & query )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2018-08-07 10:56:02 -05:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasource" , err )
2018-08-07 10:56:02 -05:00
}
2023-02-09 08:49:44 -06:00
datasourceDTO := hs . convertModelToDtos ( c . Req . Context ( ) , dataSource )
2021-05-18 13:39:56 -05:00
2023-10-06 04:34:36 -05:00
hs . Live . HandleDatasourceUpdate ( c . SignedInUser . GetOrgID ( ) , datasourceDTO . UID )
2018-08-07 10:56:02 -05:00
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , util . DynMap {
2017-10-19 10:28:54 -05:00
"message" : "Datasource updated" ,
2023-02-02 10:22:43 -06:00
"id" : cmd . ID ,
2017-10-19 10:28:54 -05:00
"name" : cmd . Name ,
2021-05-18 13:39:56 -05:00
"datasource" : datasourceDTO ,
2017-10-19 10:28:54 -05:00
} )
2016-11-18 09:44:59 -06:00
}
2022-06-27 11:23:15 -05:00
func ( hs * HTTPServer ) getRawDataSourceById ( ctx context . Context , id int64 , orgID int64 ) ( * datasources . DataSource , error ) {
query := datasources . GetDataSourceQuery {
2023-02-02 10:22:43 -06:00
ID : id ,
OrgID : orgID ,
2016-11-18 09:44:59 -06:00
}
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( ctx , & query )
if err != nil {
2016-11-18 09:44:59 -06:00
return nil , err
}
2023-02-09 08:49:44 -06:00
return dataSource , nil
2014-12-29 06:36:08 -06:00
}
2015-02-28 01:25:13 -06:00
2022-06-27 11:23:15 -05:00
func ( hs * HTTPServer ) getRawDataSourceByUID ( ctx context . Context , uid string , orgID int64 ) ( * datasources . DataSource , error ) {
query := datasources . GetDataSourceQuery {
2023-02-02 10:22:43 -06:00
UID : uid ,
OrgID : orgID ,
2021-01-13 12:16:27 -06:00
}
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( ctx , & query )
if err != nil {
2021-01-13 12:16:27 -06:00
return nil , err
}
2023-02-09 08:49:44 -06:00
return dataSource , nil
2021-01-13 12:16:27 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/name/{name} datasources getDataSourceByName
//
// Get a single data source by Name.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:read` and scopes: `datasources:*`, `datasources:name:*` and `datasources:name:test_datasource` (single data source).
//
// Responses:
// 200: getDataSourceResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetDataSourceByName ( c * contextmodel . ReqContext ) response . Response {
2023-10-06 04:34:36 -05:00
query := datasources . GetDataSourceQuery { Name : web . Params ( c . Req ) [ ":name" ] , OrgID : c . SignedInUser . GetOrgID ( ) }
2016-03-07 14:25:26 -06:00
2023-02-09 08:49:44 -06:00
dataSource , err := hs . DataSourcesService . GetDataSource ( c . Req . Context ( ) , & query )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2016-03-07 14:25:26 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasources" , err )
2016-03-07 14:25:26 -06:00
}
2023-02-09 08:49:44 -06:00
dto := hs . convertModelToDtos ( c . Req . Context ( ) , dataSource )
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & dto )
2016-03-08 04:51:03 -06:00
}
2016-03-07 14:25:26 -06:00
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/id/{name} datasources getDataSourceIdByName
//
// Get data source Id by Name.
//
// If you are running Grafana Enterprise and have Fine-grained access control enabled
// you need to have a permission with action: `datasources:read` and scopes: `datasources:*`, `datasources:name:*` and `datasources:name:test_datasource` (single data source).
//
// Responses:
// 200: getDataSourceIDResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetDataSourceIdByName ( c * contextmodel . ReqContext ) response . Response {
2023-10-06 04:34:36 -05:00
query := datasources . GetDataSourceQuery { Name : web . Params ( c . Req ) [ ":name" ] , OrgID : c . SignedInUser . GetOrgID ( ) }
2016-03-10 03:31:10 -06:00
2023-02-09 08:49:44 -06:00
ds , err := hs . DataSourcesService . GetDataSource ( c . Req . Context ( ) , & query )
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , "Data source not found" , nil )
2016-03-10 03:31:10 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to query datasources" , err )
2016-03-10 03:31:10 -06:00
}
dtos := dtos . AnyId {
2023-02-02 10:22:43 -06:00
Id : ds . ID ,
2016-03-10 03:31:10 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & dtos )
2016-03-10 03:31:10 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/{id}/resources/{datasource_proxy_route} datasources callDatasourceResourceByID
//
// Fetch data source resources by Id.
//
// Please refer to [updated API](#/datasources/callDatasourceResourceWithUID) instead
//
// Deprecated: true
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CallDatasourceResource ( c * contextmodel . ReqContext ) {
2022-01-14 10:55:57 -06:00
datasourceID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
2022-09-05 08:10:45 -05:00
c . JsonApiErr ( http . StatusBadRequest , "id is invalid" , nil )
2022-01-14 10:55:57 -06:00
return
}
2023-04-12 11:30:33 -05:00
ds , err := hs . DataSourceCache . GetDatasource ( c . Req . Context ( ) , datasourceID , c . SignedInUser , c . SkipDSCache )
2020-01-31 04:15:50 -06:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceAccessDenied ) {
2020-03-03 04:45:16 -06:00
c . JsonApiErr ( 403 , "Access denied to datasource" , err )
return
2020-01-31 04:15:50 -06:00
}
2020-03-03 04:45:16 -06:00
c . JsonApiErr ( 500 , "Unable to load datasource meta data" , err )
return
2020-01-31 04:15:50 -06:00
}
2021-11-17 05:04:22 -06:00
plugin , exists := hs . pluginStore . Plugin ( c . Req . Context ( ) , ds . Type )
if ! exists {
2020-03-03 04:45:16 -06:00
c . JsonApiErr ( 500 , "Unable to find datasource plugin" , err )
return
}
2022-05-06 03:58:02 -05:00
hs . callPluginResourceWithDataSource ( c , plugin . ID , ds )
2020-01-31 04:15:50 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/uid/{uid}/resources/{datasource_proxy_route} datasources callDatasourceResourceWithUID
//
// Fetch data source resources.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CallDatasourceResourceWithUID ( c * contextmodel . ReqContext ) {
2022-05-19 11:27:59 -05:00
dsUID := web . Params ( c . Req ) [ ":uid" ]
if ! util . IsValidShortUID ( dsUID ) {
c . JsonApiErr ( http . StatusBadRequest , "UID is invalid" , nil )
return
}
2023-04-12 11:30:33 -05:00
ds , err := hs . DataSourceCache . GetDatasourceByUID ( c . Req . Context ( ) , dsUID , c . SignedInUser , c . SkipDSCache )
2022-05-19 11:27:59 -05:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceAccessDenied ) {
2022-05-19 11:27:59 -05:00
c . JsonApiErr ( http . StatusForbidden , "Access denied to datasource" , err )
return
}
c . JsonApiErr ( http . StatusInternalServerError , "Unable to load datasource meta data" , err )
return
}
plugin , exists := hs . pluginStore . Plugin ( c . Req . Context ( ) , ds . Type )
if ! exists {
c . JsonApiErr ( http . StatusInternalServerError , "Unable to find datasource plugin" , err )
return
}
hs . callPluginResourceWithDataSource ( c , plugin . ID , ds )
}
2022-06-27 11:23:15 -05:00
func ( hs * HTTPServer ) convertModelToDtos ( ctx context . Context , ds * datasources . DataSource ) dtos . DataSource {
2016-11-18 09:44:59 -06:00
dto := dtos . DataSource {
2023-02-02 10:22:43 -06:00
Id : ds . ID ,
UID : ds . UID ,
OrgId : ds . OrgID ,
2022-06-03 10:38:22 -05:00
Name : ds . Name ,
2023-02-02 10:22:43 -06:00
Url : ds . URL ,
2022-06-03 10:38:22 -05:00
Type : ds . Type ,
Access : ds . Access ,
Database : ds . Database ,
User : ds . User ,
BasicAuth : ds . BasicAuth ,
BasicAuthUser : ds . BasicAuthUser ,
WithCredentials : ds . WithCredentials ,
IsDefault : ds . IsDefault ,
JsonData : ds . JsonData ,
SecureJsonFields : map [ string ] bool { } ,
Version : ds . Version ,
ReadOnly : ds . ReadOnly ,
2024-05-14 06:58:27 -05:00
APIVersion : ds . APIVersion ,
2016-11-18 09:44:59 -06:00
}
2023-06-02 12:46:12 -05:00
if hs . pluginStore != nil {
if plugin , exists := hs . pluginStore . Plugin ( ctx , ds . Type ) ; exists {
dto . TypeLogoUrl = plugin . Info . Logos . Small
dto . Type = plugin . ID // may be from an alias
} else {
dto . TypeLogoUrl = "public/img/icn-datasource.svg"
}
}
2022-04-25 11:57:45 -05:00
secrets , err := hs . DataSourcesService . DecryptedValues ( ctx , ds )
if err == nil {
for k , v := range secrets {
if len ( v ) > 0 {
dto . SecureJsonFields [ k ] = true
}
2016-11-24 09:24:47 -06:00
}
2022-04-25 13:12:44 -05:00
} else {
datasourcesLogger . Debug ( "Failed to retrieve datasource secrets to parse secure json fields" , "error" , err )
2016-11-24 09:24:47 -06:00
}
2016-11-18 09:44:59 -06:00
return dto
2016-03-07 14:25:26 -06:00
}
2020-03-13 06:31:44 -05:00
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/uid/{uid}/health datasources checkDatasourceHealthWithUID
//
// Sends a health check request to the plugin datasource identified by the UID.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CheckDatasourceHealthWithUID ( c * contextmodel . ReqContext ) response . Response {
2022-05-19 11:27:59 -05:00
dsUID := web . Params ( c . Req ) [ ":uid" ]
if ! util . IsValidShortUID ( dsUID ) {
return response . Error ( http . StatusBadRequest , "UID is invalid" , nil )
}
2023-04-12 11:30:33 -05:00
ds , err := hs . DataSourceCache . GetDatasourceByUID ( c . Req . Context ( ) , dsUID , c . SignedInUser , c . SkipDSCache )
2022-05-19 11:27:59 -05:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceAccessDenied ) {
2022-05-19 11:27:59 -05:00
return response . Error ( http . StatusForbidden , "Access denied to datasource" , err )
}
return response . Error ( http . StatusInternalServerError , "Unable to load datasource metadata" , err )
}
return hs . checkDatasourceHealth ( c , ds )
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /datasources/{id}/health datasources checkDatasourceHealthByID
//
// Sends a health check request to the plugin datasource identified by the ID.
//
// Please refer to [updated API](#/datasources/checkDatasourceHealthWithUID) instead
//
// Deprecated: true
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) CheckDatasourceHealth ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
datasourceID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
2022-09-05 08:10:45 -05:00
return response . Error ( http . StatusBadRequest , "id is invalid" , nil )
2022-01-14 10:55:57 -06:00
}
2020-03-13 06:31:44 -05:00
2023-04-12 11:30:33 -05:00
ds , err := hs . DataSourceCache . GetDatasource ( c . Req . Context ( ) , datasourceID , c . SignedInUser , c . SkipDSCache )
2020-03-13 06:31:44 -05:00
if err != nil {
2022-06-27 11:23:15 -05:00
if errors . Is ( err , datasources . ErrDataSourceAccessDenied ) {
2022-01-27 11:06:38 -06:00
return response . Error ( http . StatusForbidden , "Access denied to datasource" , err )
2020-03-13 06:31:44 -05:00
}
2022-01-27 11:06:38 -06:00
return response . Error ( http . StatusInternalServerError , "Unable to load datasource metadata" , err )
2020-03-13 06:31:44 -05:00
}
2022-05-19 11:27:59 -05:00
return hs . checkDatasourceHealth ( c , ds )
}
2020-03-13 06:31:44 -05:00
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) checkDatasourceHealth ( c * contextmodel . ReqContext , ds * datasources . DataSource ) response . Response {
2023-06-08 06:59:51 -05:00
pCtx , err := hs . pluginContextProvider . GetWithDataSource ( c . Req . Context ( ) , ds . Type , c . SignedInUser , ds )
2020-04-23 13:08:21 -05:00
if err != nil {
2023-09-25 05:10:47 -05:00
return response . ErrOrFallback ( http . StatusInternalServerError , "Unable to get plugin context" , err )
2020-04-23 13:08:21 -05:00
}
2021-11-01 04:53:33 -05:00
req := & backend . CheckHealthRequest {
2023-06-08 06:59:51 -05:00
PluginContext : pCtx ,
Headers : map [ string ] string { } ,
2020-04-23 13:08:21 -05:00
}
2022-01-27 11:06:38 -06:00
var dsURL string
if req . PluginContext . DataSourceInstanceSettings != nil {
dsURL = req . PluginContext . DataSourceInstanceSettings . URL
}
err = hs . PluginRequestValidator . Validate ( dsURL , c . Req )
if err != nil {
return response . Error ( http . StatusForbidden , "Access denied" , err )
}
2021-11-01 04:53:33 -05:00
resp , err := hs . pluginClient . CheckHealth ( c . Req . Context ( ) , req )
2020-03-13 06:31:44 -05:00
if err != nil {
2020-06-11 09:14:05 -05:00
return translatePluginRequestErrorToAPIError ( err )
2020-03-13 06:31:44 -05:00
}
2023-08-30 10:46:47 -05:00
payload := map [ string ] any {
2020-03-18 06:08:52 -05:00
"status" : resp . Status . String ( ) ,
"message" : resp . Message ,
}
// Unmarshal JSONDetails if it's not empty.
if len ( resp . JSONDetails ) > 0 {
2023-08-30 10:46:47 -05:00
var jsonDetails map [ string ] any
2020-03-18 06:08:52 -05:00
err = json . Unmarshal ( resp . JSONDetails , & jsonDetails )
if err != nil {
2022-01-27 11:06:38 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to unmarshal detailed response from backend plugin" , err )
2020-03-18 06:08:52 -05:00
}
payload [ "details" ] = jsonDetails
2020-03-13 06:31:44 -05:00
}
2020-06-11 09:14:05 -05:00
if resp . Status != backend . HealthStatusOk {
2022-01-27 11:06:38 -06:00
return response . JSON ( http . StatusBadRequest , payload )
2020-03-13 06:31:44 -05:00
}
2022-01-27 11:06:38 -06:00
return response . JSON ( http . StatusOK , payload )
2020-03-13 06:31:44 -05:00
}
2021-10-07 09:33:50 -05:00
2022-07-27 08:54:37 -05:00
// swagger:parameters checkDatasourceHealthByID
type CheckDatasourceHealthByIDParams struct {
// in:path
// required:true
DatasourceID string ` json:"id" `
}
// swagger:parameters callDatasourceResourceByID
type CallDatasourceResourceByIDParams struct {
// in:path
// required:true
DatasourceID string ` json:"id" `
}
// swagger:parameters deleteDataSourceByID
type DeleteDataSourceByIDParams struct {
// in:path
// required:true
DatasourceID string ` json:"id" `
}
// swagger:parameters getDataSourceByID
type GetDataSourceByIDParams struct {
// in:path
// required:true
DatasourceID string ` json:"id" `
}
// swagger:parameters checkDatasourceHealthWithUID
type CheckDatasourceHealthWithUIDParams struct {
// in:path
// required:true
DatasourceUID string ` json:"uid" `
}
// swagger:parameters callDatasourceResourceWithUID
type CallDatasourceResourceWithUIDParams struct {
// in:path
// required:true
DatasourceUID string ` json:"uid" `
}
// swagger:parameters deleteDataSourceByUID
type DeleteDataSourceByUIDParams struct {
// in:path
// required:true
DatasourceUID string ` json:"uid" `
}
// swagger:parameters getDataSourceByUID
type GetDataSourceByUIDParams struct {
// in:path
// required:true
DatasourceUID string ` json:"uid" `
}
// swagger:parameters getDataSourceByName
type GetDataSourceByNameParams struct {
// in:path
// required:true
DatasourceName string ` json:"name" `
}
// swagger:parameters deleteDataSourceByName
type DeleteDataSourceByNameParams struct {
// in:path
// required:true
DatasourceName string ` json:"name" `
}
// swagger:parameters getDataSourceIdByName
type GetDataSourceIdByNameParams struct {
// in:path
// required:true
DatasourceName string ` json:"name" `
}
// swagger:parameters addDataSource
type AddDataSourceParams struct {
// in:body
// required:true
Body datasources . AddDataSourceCommand
}
// swagger:parameters updateDataSourceByID
type UpdateDataSourceByIDParams struct {
// in:body
// required:true
Body datasources . UpdateDataSourceCommand
// in:path
// required:true
DatasourceID string ` json:"id" `
}
// swagger:parameters updateDataSourceByUID
type UpdateDataSourceByUIDParams struct {
// in:body
// required:true
Body datasources . UpdateDataSourceCommand
// in:path
// required:true
DatasourceUID string ` json:"uid" `
}
// swagger:response getDataSourcesResponse
type GetDataSourcesResponse struct {
// The response message
// in: body
Body dtos . DataSourceList ` json:"body" `
}
// swagger:response getDataSourceResponse
type GetDataSourceResponse struct {
// The response message
// in: body
Body dtos . DataSource ` json:"body" `
}
// swagger:response createOrUpdateDatasourceResponse
type CreateOrUpdateDatasourceResponse struct {
// The response message
// in: body
Body struct {
// ID Identifier of the new data source.
// required: true
// example: 65
ID int64 ` json:"id" `
// Name of the new data source.
// required: true
// example: My Data source
Name string ` json:"name" `
// Message Message of the deleted dashboard.
// required: true
// example: Data source added
Message string ` json:"message" `
// Datasource properties
// required: true
Datasource dtos . DataSource ` json:"datasource" `
} ` json:"body" `
}
// swagger:response getDataSourceIDResponse
type GetDataSourceIDresponse struct {
// The response message
// in: body
Body struct {
// ID Identifier of the data source.
// required: true
// example: 65
ID int64 ` json:"id" `
} ` json:"body" `
}
// swagger:response deleteDataSourceByNameResponse
type DeleteDataSourceByNameResponse struct {
// The response message
// in: body
Body struct {
// ID Identifier of the deleted data source.
// required: true
// example: 65
ID int64 ` json:"id" `
// Message Message of the deleted dashboard.
// required: true
// example: Dashboard My Dashboard deleted
Message string ` json:"message" `
} ` json:"body" `
}