Skip to content

Commit

Permalink
adding grafana headers via custom http headers in settings
Browse files Browse the repository at this point in the history
  • Loading branch information
yesoreyeram committed Nov 28, 2024
1 parent b458af9 commit 58a8f7a
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 200 deletions.
4 changes: 0 additions & 4 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ services:
- ./provisioning/dashboards-actual/:/dashboards/
- ./provisioning:/etc/grafana/provisioning
- ./dist:/var/lib/grafana/plugins/yesoreyeram-infinity-datasource
- grafana-png:/var/lib/grafana/png
environment:
- TERM=linux
- GF_DEFAULT_APP_MODE=development
Expand All @@ -23,6 +22,3 @@ services:
- GF_SECURITY_ANGULAR_SUPPORT_ENABLED=false
- GF_SECURITY_CSRF_ALWAYS_CHECK=true
- GF_ENTERPRISE_LICENSE_TEXT=$GF_ENTERPRISE_LICENSE_TEXT

volumes:
grafana-png:
10 changes: 5 additions & 5 deletions pkg/infinity/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ func replaceSect(input string, settings models.InfinitySettings, includeSect boo
return input
}

func (client *Client) req(ctx context.Context, url string, body io.Reader, settings models.InfinitySettings, query models.Query, requestHeaders map[string]string, pCtx *backend.PluginContext) (obj any, statusCode int, duration time.Duration, err error) {
func (client *Client) req(ctx context.Context, pCtx *backend.PluginContext, url string, body io.Reader, settings models.InfinitySettings, query models.Query, requestHeaders map[string]string) (obj any, statusCode int, duration time.Duration, err error) {
ctx, span := tracing.DefaultTracer().Start(ctx, "client.req")
logger := backend.Logger.FromContext(ctx)
defer span.End()
req, err := GetRequest(ctx, settings, body, query, requestHeaders, true, pCtx)
req, err := GetRequest(ctx, pCtx, settings, body, query, requestHeaders, true)
if err != nil {
return nil, http.StatusInternalServerError, 0, errorsource.DownstreamError(fmt.Errorf("error preparing request. %w", err), false)
}
Expand Down Expand Up @@ -263,7 +263,7 @@ func removeBOMContent(input []byte) []byte {
return bytes.TrimPrefix(input, []byte("\xef\xbb\xbf"))
}

func (client *Client) GetResults(ctx context.Context, query models.Query, requestHeaders map[string]string, pCtx *backend.PluginContext) (o any, statusCode int, duration time.Duration, err error) {
func (client *Client) GetResults(ctx context.Context, pCtx *backend.PluginContext, query models.Query, requestHeaders map[string]string) (o any, statusCode int, duration time.Duration, err error) {
logger := backend.Logger.FromContext(ctx)
if query.Source == "azure-blob" {
if strings.TrimSpace(query.AzBlobContainerName) == "" || strings.TrimSpace(query.AzBlobName) == "" {
Expand Down Expand Up @@ -296,9 +296,9 @@ func (client *Client) GetResults(ctx context.Context, query models.Query, reques
switch strings.ToUpper(query.URLOptions.Method) {
case http.MethodPost:
body := GetQueryBody(ctx, query)
return client.req(ctx, query.URL, body, client.Settings, query, requestHeaders, pCtx)
return client.req(ctx, pCtx, query.URL, body, client.Settings, query, requestHeaders)
default:
return client.req(ctx, query.URL, nil, client.Settings, query, requestHeaders, pCtx)
return client.req(ctx, pCtx, query.URL, nil, client.Settings, query, requestHeaders)
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/infinity/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestInfinityClient_GetResults(t *testing.T) {
HttpClient: &http.Client{},
}
pluginContext := &backend.PluginContext{}
gotO, statusCode, duration, err := client.GetResults(context.Background(), tt.query, tt.requestHeaders, pluginContext)
gotO, statusCode, duration, err := client.GetResults(context.Background(), pluginContext, tt.query, tt.requestHeaders)
if (err != nil) != tt.wantErr {
t.Errorf("GetResults() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
30 changes: 22 additions & 8 deletions pkg/infinity/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/grafana/grafana-infinity-datasource/pkg/models"
"github.com/grafana/grafana-plugin-sdk-go/backend"
)

const dummyHeader = "xxxxxxxx"
Expand Down Expand Up @@ -65,18 +66,31 @@ func ApplyContentTypeHeader(query models.Query, settings models.InfinitySettings
return req
}

func ApplyHeadersFromSettings(settings models.InfinitySettings, req *http.Request, includeSect bool) *http.Request {
func ApplyHeadersFromSettings(pCtx *backend.PluginContext, requestHeaders map[string]string, settings models.InfinitySettings, req *http.Request, includeSect bool) *http.Request {
for key, value := range settings.CustomHeaders {
val := dummyHeader
headerValue := dummyHeader
if includeSect {
val = value
headerValue = value
}
if key != "" {
req.Header.Add(key, val)
if strings.EqualFold(key, headerKeyAccept) || strings.EqualFold(key, headerKeyContentType) {
req.Header.Set(key, val)
if pCtx != nil {
headerValue = strings.ReplaceAll(headerValue, "${__org.id}", fmt.Sprintf("%d", pCtx.OrgID))
headerValue = strings.ReplaceAll(headerValue, "${__plugin.id}", pCtx.PluginID)
headerValue = strings.ReplaceAll(headerValue, "${__plugin.version}", pCtx.PluginVersion)
if pCtx.DataSourceInstanceSettings != nil {
headerValue = strings.ReplaceAll(headerValue, "${__ds.uid}", pCtx.DataSourceInstanceSettings.UID)
headerValue = strings.ReplaceAll(headerValue, "${__ds.name}", pCtx.DataSourceInstanceSettings.Name)
headerValue = strings.ReplaceAll(headerValue, "${__ds.id}", fmt.Sprintf("%d", pCtx.DataSourceInstanceSettings.ID))
}
if pCtx.User != nil {
headerValue = strings.ReplaceAll(headerValue, "${__user.login}", pCtx.User.Login)
headerValue = strings.ReplaceAll(headerValue, "${__user.email}", pCtx.User.Email)
headerValue = strings.ReplaceAll(headerValue, "${__user.name}", pCtx.User.Name)
// headerValue = strings.ReplaceAll(headerValue, "${__user.role}", pCtx.User.Role)
}
}
if key != "" {
req.Header.Set(key, headerValue)
}
}
return req
}
Expand Down Expand Up @@ -156,4 +170,4 @@ func getQueryReqHeader(requestHeaders map[string]string, headerName string) stri
}

return ""
}
}
77 changes: 76 additions & 1 deletion pkg/infinity/headers_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package infinity

import (
"net/http"
"strings"
"testing"

"github.com/grafana/grafana-infinity-datasource/pkg/models"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/require"
)

func TestGetQueryReqHeader(t *testing.T) {
Expand Down Expand Up @@ -62,4 +67,74 @@ func TestGetQueryReqHeader(t *testing.T) {
}
})
}
}
}

func TestApplyGrafanaHeaders(t *testing.T) {
tests := []struct {
name string
settings models.InfinitySettings
pCtx *backend.PluginContext
want map[string]string
}{
{
name: "should interpolate grafana headers correctly when set in settings",
settings: models.InfinitySettings{
CustomHeaders: map[string]string{
"X-Grafana-User": "${__user.login}",
"X-Grafana-UserID": "${__user.login}",
"X-Grafana-UserEmail": "${__user.email}",
"X-Grafana-UserName": "${__user.name}",
"X-Grafana-UserRole": "${__user.role}",
"X-Grafana-OrgID": "${__org.id}",
"X-Grafana-PluginID": "${__plugin.id}",
"X-Grafana-PluginVersion": "${__plugin.version}",
"X-Grafana-DatasourceUID": "${__ds.uid}",
"X-Grafana-DatasourceName": "${__ds.name}",
"X-Grafana-DatasourceID": "${__ds.id}",
"X-Misc-Value": "${__user.login}@${__plugin.id}",
},
},
pCtx: &backend.PluginContext{
OrgID: 12345,
PluginID: "my-plugin-id",
PluginVersion: "0.0.0-preview.1",
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{
ID: 123,
UID: "my-ds-uid",
Name: "my-ds-name",
},
User: &backend.User{
Email: "testuser@localhost",
Login: "testuser",
Name: "Test User",
Role: "Admin",
},
},
want: map[string]string{
"X-Grafana-User": "testuser",
"X-Grafana-UserID": "testuser",
"X-Grafana-UserEmail": "testuser@localhost",
"X-Grafana-UserName": "Test User",
"X-Grafana-UserRole": "${__user.role}",
"X-Grafana-OrgID": "12345",
"X-Grafana-PluginID": "my-plugin-id",
"X-Grafana-PluginVersion": "0.0.0-preview.1",
"X-Grafana-DatasourceUID": "my-ds-uid",
"X-Grafana-DatasourceName": "my-ds-name",
"X-Grafana-DatasourceID": "123",
"X-Misc-Value": "testuser@my-plugin-id",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
got := ApplyHeadersFromSettings(tt.pCtx, map[string]string{}, tt.settings, req, true)
require.Equal(t, len(tt.want), len(got.Header))
for k, v := range tt.want {
require.Equal(t, v, got.Header.Get(k))
}
})
}
}
18 changes: 9 additions & 9 deletions pkg/infinity/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ import (
"github.com/grafana/infinity-libs/lib/go/transformations"
)

func GetFrameForURLSources(ctx context.Context, query models.Query, infClient Client, requestHeaders map[string]string, pCtx *backend.PluginContext) (*data.Frame, error) {
func GetFrameForURLSources(ctx context.Context, pCtx *backend.PluginContext, query models.Query, infClient Client, requestHeaders map[string]string) (*data.Frame, error) {
ctx, span := tracing.DefaultTracer().Start(ctx, "GetFrameForURLSources")
defer span.End()
if query.Type == models.QueryTypeJSON && query.Parser == models.InfinityParserBackend && query.PageMode != models.PaginationModeNone && query.PageMode != "" {
return GetPaginatedResults(ctx, query, infClient, requestHeaders, pCtx)
return GetPaginatedResults(ctx, pCtx, query, infClient, requestHeaders)
}
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, query, infClient, requestHeaders, true, pCtx)
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, pCtx, query, infClient, requestHeaders, true)
return frame, err
}

func GetPaginatedResults(ctx context.Context, query models.Query, infClient Client, requestHeaders map[string]string, pCtx *backend.PluginContext) (*data.Frame, error) {
func GetPaginatedResults(ctx context.Context, pCtx *backend.PluginContext, query models.Query, infClient Client, requestHeaders map[string]string) (*data.Frame, error) {
ctx, span := tracing.DefaultTracer().Start(ctx, "GetPaginatedResults")
defer span.End()
frames := []*data.Frame{}
Expand Down Expand Up @@ -65,12 +65,12 @@ func GetPaginatedResults(ctx context.Context, query models.Query, infClient Clie
case models.PaginationModeCursor:
queries = append(queries, query)
default:
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, query, infClient, requestHeaders, true, pCtx)
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, pCtx, query, infClient, requestHeaders, true)
return frame, err
}
if query.PageMode != models.PaginationModeCursor {
for _, currentQuery := range queries {
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, currentQuery, infClient, requestHeaders, false, pCtx)
frame, _, err := GetFrameForURLSourcesWithPostProcessing(ctx, pCtx, currentQuery, infClient, requestHeaders, false)
frames = append(frames, frame)
errs = errors.Join(errs, err)
}
Expand All @@ -87,7 +87,7 @@ func GetPaginatedResults(ctx context.Context, query models.Query, infClient Clie
break
}
i++
frame, cursor, err := GetFrameForURLSourcesWithPostProcessing(ctx, currentQuery, infClient, requestHeaders, false, pCtx)
frame, cursor, err := GetFrameForURLSourcesWithPostProcessing(ctx, pCtx, currentQuery, infClient, requestHeaders, false)
oCursor = cursor
frames = append(frames, frame)
errs = errors.Join(errs, err)
Expand Down Expand Up @@ -135,13 +135,13 @@ func ApplyPaginationItemToQuery(currentQuery models.Query, fieldType models.Pagi
return currentQuery
}

func GetFrameForURLSourcesWithPostProcessing(ctx context.Context, query models.Query, infClient Client, requestHeaders map[string]string, postProcessingRequired bool, pCtx *backend.PluginContext) (*data.Frame, string, error) {
func GetFrameForURLSourcesWithPostProcessing(ctx context.Context, pCtx *backend.PluginContext, query models.Query, infClient Client, requestHeaders map[string]string, postProcessingRequired bool) (*data.Frame, string, error) {
ctx, span := tracing.DefaultTracer().Start(ctx, "GetFrameForURLSourcesWithPostProcessing")
logger := backend.Logger.FromContext(ctx)
defer span.End()
frame := GetDummyFrame(query)
cursor := ""
urlResponseObject, statusCode, duration, err := infClient.GetResults(ctx, query, requestHeaders, pCtx)
urlResponseObject, statusCode, duration, err := infClient.GetResults(ctx, pCtx, query, requestHeaders)
frame.Meta.ExecutedQueryString = infClient.GetExecutedURL(ctx, query)
if infClient.IsMock {
duration = 123
Expand Down
21 changes: 3 additions & 18 deletions pkg/infinity/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"moul.io/http2curl"
)

func GetRequest(ctx context.Context, settings models.InfinitySettings, body io.Reader, query models.Query, requestHeaders map[string]string, includeSect bool, pCtx *backend.PluginContext) (req *http.Request, err error) {
func GetRequest(ctx context.Context, pCtx *backend.PluginContext, settings models.InfinitySettings, body io.Reader, query models.Query, requestHeaders map[string]string, includeSect bool) (req *http.Request, err error) {
ctx, span := tracing.DefaultTracer().Start(ctx, "GetRequest")
defer span.End()
url, err := GetQueryURL(ctx, settings, query, includeSect)
Expand All @@ -29,13 +29,12 @@ func GetRequest(ctx context.Context, settings models.InfinitySettings, body io.R
}
req = ApplyAcceptHeader(query, settings, req, includeSect)
req = ApplyContentTypeHeader(query, settings, req, includeSect)
req = ApplyHeadersFromSettings(settings, req, includeSect)
req = ApplyHeadersFromQuery(query, settings, req, includeSect)
req = ApplyHeadersFromSettings(pCtx, requestHeaders, settings, req, includeSect)
req = ApplyBasicAuth(settings, req, includeSect)
req = ApplyBearerToken(settings, req, includeSect)
req = ApplyApiKeyAuth(settings, req, includeSect)
req = ApplyForwardedOAuthIdentity(requestHeaders, settings, req, includeSect)
req = ApplyGrafanaHeaders(settings, req, pCtx)
return req, err
}

Expand Down Expand Up @@ -97,7 +96,7 @@ func NormalizeURL(u string) string {
func (client *Client) GetExecutedURL(ctx context.Context, query models.Query) string {
out := []string{}
if query.Source != "inline" && query.Source != "azure-blob" {
req, err := GetRequest(ctx, client.Settings, GetQueryBody(ctx, query), query, map[string]string{}, false, nil)
req, err := GetRequest(ctx, nil, client.Settings, GetQueryBody(ctx, query), query, map[string]string{}, false)
if err != nil {
return fmt.Sprintf("error retrieving full url. %s", query.URL)
}
Expand Down Expand Up @@ -125,17 +124,3 @@ func (client *Client) GetExecutedURL(ctx context.Context, query models.Query) st
}
return strings.Join(out, "\n")
}

// ApplyGrafanaHeaders adds Grafana-specific headers if enabled in settings
func ApplyGrafanaHeaders(settings models.InfinitySettings, req *http.Request, pCtx *backend.PluginContext) *http.Request {
if pCtx == nil {
return req
}
if settings.SendUserHeader && pCtx.User != nil {
req.Header.Set("X-Grafana-User", pCtx.User.Login)
}
if settings.SendDatasourceIDHeader && pCtx.DataSourceInstanceSettings != nil {
req.Header.Set("X-Grafana-Datasource-UID", pCtx.DataSourceInstanceSettings.UID)
}
return req
}
Loading

0 comments on commit 58a8f7a

Please sign in to comment.