Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lazy-planes-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'grafana-github-datasource': minor
---

Add support for PDC
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Watch this video to learn more about setting up the Grafana GitHub data source p

This data source uses the [`githubv4` package](https://github.com/shurcooL/githubv4), which is under active development.

## Private data source connect - Only for Grafana Cloud users.

Establishes a private, secured connection between a Grafana Cloud stack and data sources within a private network. Use the drop-down to locate the PDC URL. For setup instructions, refer to [Private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/) and [Configure PDC](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc). Click Manage private data source connect to open your PDC connection page and view your configuration details.

## Frequently Asked Questions

- **Why does it sometimes take up to 5 minutes for my new pull request / new issue / new commit to show up?**
Expand Down
38 changes: 29 additions & 9 deletions pkg/github/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
googlegithub "github.com/google/go-github/v72/github"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
"github.com/influxdata/tdigest"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -54,27 +55,33 @@ var runnerPerMinuteRate = map[string]float64{

// New instantiates a new GitHub API client.
func New(ctx context.Context, settings models.Settings) (*Client, error) {
plugin := backend.PluginConfigFromContext(ctx)
opts, err := plugin.DataSourceInstanceSettings.HTTPClientOptions(ctx)
if err != nil {
return nil, err
}

if settings.SelectedAuthType == models.AuthTypeGithubApp {
return createAppClient(settings)
return createAppClient(settings, opts)
}
if settings.SelectedAuthType == models.AuthTypePAT {
return createAccessTokenClient(ctx, settings)
return createAccessTokenClient(ctx, settings, opts)
}
return nil, backend.DownstreamError(errors.New("access token or app token are required"))
}

func createAppClient(settings models.Settings) (*Client, error) {
transport, err := httpclient.GetDefaultTransport()
func createAppClient(settings models.Settings, opts httpclient.Options) (*Client, error) {
httpClient, err := httpclient.New(opts)
if err != nil {
return nil, backend.DownstreamError(errors.New("error: http.DefaultTransport is not of type *http.Transport"))
return nil, backend.DownstreamErrorf("error creating http client: %w", err)
}
itr, err := ghinstallation.New(transport, settings.AppIdInt64, settings.InstallationIdInt64, []byte(settings.PrivateKey))

itr, err := ghinstallation.New(httpClient.Transport, settings.AppIdInt64, settings.InstallationIdInt64, []byte(settings.PrivateKey))
if err != nil {
return nil, backend.DownstreamError(errors.New("error creating token source"))
}

httpClient := &http.Client{Transport: itr}

httpClient.Transport = itr
if settings.GitHubURL == "" {
return &Client{
restClient: googlegithub.NewClient(httpClient),
Expand All @@ -87,13 +94,26 @@ func createAppClient(settings models.Settings) (*Client, error) {
return useGitHubEnterprise(httpClient, settings)
}

func createAccessTokenClient(ctx context.Context, settings models.Settings) (*Client, error) {
func createAccessTokenClient(ctx context.Context, settings models.Settings, opts httpclient.Options) (*Client, error) {
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: settings.AccessToken},
)

httpClient := oauth2.NewClient(ctx, src)

cli := proxy.New(opts.ProxyOptions)
if cli.SecureSocksProxyEnabled() {
// only override the Transport if Secure Proxy is enabled.
transport, err := httpclient.GetTransport(opts)
if err != nil {
return nil, backend.DownstreamErrorf("error getting the transport: %w", err)
}

httpClient.Transport = &oauth2.Transport{
Base: transport,
Source: oauth2.ReuseTokenSource(nil, src),
}
}
if settings.GitHubURL == "" {
return &Client{
restClient: googlegithub.NewClient(httpClient),
Expand Down
6 changes: 3 additions & 3 deletions pkg/plugin/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ func NewGitHubInstance(ctx context.Context, settings models.Settings) (instancem
}

// NewDataSourceInstance creates a new instance
func NewDataSourceInstance(_ context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
func NewDataSourceInstance(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
datasourceSettings, err := models.LoadSettings(settings)
if err != nil {
return nil, err
}

datasourceSettings.CachingEnabled = true

instance, err := NewGitHubInstance(context.Background(), datasourceSettings)
instance, err := NewGitHubInstance(ctx, datasourceSettings)
if err != nil {
return instance, fmt.Errorf("instantiating github instance: %w", err)
}
Expand Down
23 changes: 18 additions & 5 deletions src/views/ConfigEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { css } from '@emotion/css';
import { Collapse, Field, Input, Label, RadioButtonGroup, SecretInput, SecretTextArea, useStyles2 } from '@grafana/ui';
import { ConfigSection, DataSourceDescription } from '@grafana/plugin-ui';
import { Divider } from 'components/Divider';
import { components as selectors } from '../components/selectors';
import {
onUpdateDatasourceJsonDataOption,
onUpdateDatasourceSecureJsonDataOption,
type DataSourcePluginOptionsEditorProps,
type GrafanaTheme2,
type SelectableValue,
} from '@grafana/data';
import { ConfigSection, DataSourceDescription } from '@grafana/plugin-ui';
import { config } from '@grafana/runtime';
import {
Collapse,
Field,
Input,
Label,
RadioButtonGroup,
SecretInput,
SecretTextArea,
SecureSocksProxySettings,
useStyles2,
} from '@grafana/ui';
import { Divider } from 'components/Divider';
import type { GitHubAuthType, GitHubLicenseType, GitHubDataSourceOptions, GitHubSecureJsonData } from 'types/config';
import { components as selectors } from '../components/selectors';

export type ConfigEditorProps = DataSourcePluginOptionsEditorProps<GitHubDataSourceOptions, GitHubSecureJsonData>;

Expand Down Expand Up @@ -196,7 +207,9 @@ const ConfigEditor = (props: ConfigEditorProps) => {
</>
)}
</ConfigSection>

{config.secureSocksDSProxyEnabled && (
<SecureSocksProxySettings options={options} onOptionsChange={onOptionsChange} />
)}
<Divider />

<ConfigSection title="Connection" isCollapsible>
Expand Down
Loading