Skip to content

Commit

Permalink
Merge pull request #9 from mullerpeter/feature/v1.0.0
Browse files Browse the repository at this point in the history
✨ feat: v1.0.0 release
  • Loading branch information
mullerpeter authored Dec 4, 2022
2 parents 5c55fec + 10108d0 commit c0b0553
Show file tree
Hide file tree
Showing 14 changed files with 1,730 additions and 1,575 deletions.
19 changes: 16 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# Changelog
## 1.0.0

## 0.0.9
### Breaking Changes:

- Datasource configuration (`hostname` & `sql path`) needs to be entered again in the Datasource Settings after upgrading.
- Queries made via Visual Editor no longer work.

---

- Removed visual Query Editor
- Added default Long to Wide Dataframe transformation to support multiline time-series data
- Made some config vars non secret
- Added alerting capability

---

### 0.0.9

- Init new DB connection on Invalid SessionHandle error

Expand All @@ -17,8 +32,6 @@
- Adds support for multiple metrics queries (SQL Editor only)
- Fixes bug where saved query was not displayed in query builder

---

### 0.0.5

- Adds full text SQL query editor with macros
24 changes: 3 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
# Databricks - Grafana Data Source Backend Plugin

Grafana Databricks integration allowing direct connection to Databricks to query and visualize Databricks data in Grafana.

### ⚠️ WIP ⚠️
![Release workflow](https://github.com/mullerpeter/databricks-grafana/actions/workflows/release.yml/badge.svg)

The plugin is still work in progress and currently only offers limited functionality.
Grafana Databricks integration allowing direct connection to Databricks to query and visualize Databricks data in Grafana.

#### TODO:
- [x] Full-text query editor
- [x] Allow multiple metrics query
- [ ] Add GROUP BY to query form
- [ ] Autocomplete
- [ ] ...
![img.png](img/full_text_sql_editor.png)

### Signing

Expand All @@ -24,17 +17,6 @@ defaults.ini
allow_loading_unsigned_plugins = mullerpeter-databricks-datasource
```

## Simple Query Editor

![img.png](img/querry_editor.png)

At the moment only simple queries for one value over time are implemented. The Time-range and TimeBucket parameters from Grafana are automatically inserted into the query. If you want to use more advanced queries use the raw SQL editor.

## Raw SQL Editor
![img.png](img/full_text_sql_editor.png)

When using the raw SQL editor, template variables are replaced by the Time-Range and Bucket (see query help in editor for examples).

## Plugin Configuration

![img_1.png](img/config_editor.png)
Expand Down
Binary file modified img/full_text_sql_editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed img/querry_editor.png
Binary file not shown.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mullerpeter-databricks-datasource",
"private": true,
"version": "0.0.9",
"version": "1.0.0",
"description": "Databricks SQL Connector",
"scripts": {
"build": "grafana-toolkit plugin:build",
Expand All @@ -14,11 +14,11 @@
"author": "Peter Müller",
"license": "Apache-2.0",
"devDependencies": {
"@grafana/data": "latest",
"@grafana/runtime": "latest",
"@grafana/toolkit": "latest",
"@grafana/ui": "latest",
"@types/lodash": "latest"
"@grafana/data": "^9.3.1",
"@grafana/runtime": "^9.3.1",
"@grafana/toolkit": "^9.3.1",
"@grafana/ui": "^9.3.1",
"@types/lodash": "^4.14.191"
},
"resolutions": {
"rxjs": "7.3.0"
Expand Down
45 changes: 31 additions & 14 deletions pkg/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,19 @@ var (
databricksDB *sqlx.DB
)

type DatasourceSettings struct {
Path string `json:"path"`
Hostname string `json:"hostname"`
}

// NewSampleDatasource creates a new datasource instance.
func NewSampleDatasource(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
databricksConnectionsString = fmt.Sprintf("databricks://:%s@%s/%s", settings.DecryptedSecureJSONData["token"], settings.DecryptedSecureJSONData["hostname"], settings.DecryptedSecureJSONData["path"])
datasourceSettings := new(DatasourceSettings)
err := json.Unmarshal(settings.JSONData, datasourceSettings)
if err != nil {
log.DefaultLogger.Info("Setting Parse Error", "err", err)
}
databricksConnectionsString = fmt.Sprintf("databricks://:%s@%s/%s", settings.DecryptedSecureJSONData["token"], datasourceSettings.Hostname, datasourceSettings.Path)
if databricksConnectionsString != "" {
log.DefaultLogger.Info("Init Databricks SQL DB")
db, err := sql.Open("databricks", databricksConnectionsString)
Expand Down Expand Up @@ -121,14 +131,15 @@ func (d *SampleDatasource) QueryData(ctx context.Context, req *backend.QueryData
return response, nil
}

type querySettings struct {
ConvertLongToWide bool `json:"convertLongToWide"`
FillMode data.FillMode `json:"fillMode"`
FillValue float64 `json:"fillValue"`
}

type queryModel struct {
WithStreaming bool `json:"withStreaming"`
TimeColumnName string `json:"timeColumnName"`
ValueColumnName string `json:"valueColumnName"`
WhereQuery string `json:"whereQuery"`
TableName string `json:"tableName"`
RawSqlQuery string `json:"rawSqlQuery"`
RawSqlSelected bool `json:"rawSqlSelected"`
RawSqlQuery string `json:"rawSqlQuery"`
QuerySettings querySettings `json:"querySettings"`
}

func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext, query backend.DataQuery) backend.DataResponse {
Expand All @@ -145,12 +156,8 @@ func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext,
return response
}

queryString := ""
if qm.RawSqlSelected {
queryString = replaceMacros(qm.RawSqlQuery, query)
} else {
queryString = createQueryBuilderQuery(query, qm)
}
queryString := replaceMacros(qm.RawSqlQuery, query)

log.DefaultLogger.Info("Query", "query", queryString)

frame := data.NewFrame("response")
Expand Down Expand Up @@ -192,6 +199,16 @@ func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext,
frame.AppendRow(res...)
}

if qm.QuerySettings.ConvertLongToWide {
wideFrame, err := data.LongToWide(frame, &data.FillMissing{Value: qm.QuerySettings.FillValue, Mode: qm.QuerySettings.FillMode})
if err != nil {
log.DefaultLogger.Info("LongToWide conversion error", "err", err)
} else {
frame = wideFrame
}

}

// add the frames to the response.
response.Frames = append(response.Frames, frame)

Expand Down
30 changes: 0 additions & 30 deletions pkg/plugin/plugin_test.go

This file was deleted.

22 changes: 0 additions & 22 deletions pkg/plugin/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,6 @@ func getIntervalString(duration time.Duration) string {
return returnString
}

func createQueryBuilderQuery(query backend.DataQuery, qm queryModel) string {
sqlQuery := ""
whereQuery := ""
if qm.WhereQuery != "" {
whereQuery = fmt.Sprintf(" %s AND", qm.WhereQuery)
}

intervalString := getIntervalString(query.Interval)

sqlQuery = fmt.Sprintf("SELECT window.start, avg(%s) AS value FROM %s WHERE%s %s BETWEEN '%s' AND '%s' GROUP BY window(%s, '%s')",
qm.ValueColumnName,
qm.TableName,
whereQuery,
qm.TimeColumnName,
query.TimeRange.From.UTC().Format("2006-01-02 15:04:05"),
query.TimeRange.To.UTC().Format("2006-01-02 15:04:05"),
qm.TimeColumnName,
intervalString)

return sqlQuery
}

func replaceMacros(sqlQuery string, query backend.DataQuery) string {

queryString := sqlQuery
Expand Down
27 changes: 10 additions & 17 deletions src/ConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { LegacyForms } from '@grafana/ui';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { MyDataSourceOptions, MySecureJsonData } from './types';

const { SecretFormField } = LegacyForms;
const { SecretFormField, FormField } = LegacyForms;

interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions> {}

Expand All @@ -27,8 +27,8 @@ export class ConfigEditor extends PureComponent<Props, State> {
const { onOptionsChange, options } = this.props;
onOptionsChange({
...options,
secureJsonData: {
...options.secureJsonData,
jsonData: {
...options.jsonData,
hostname: event.target.value,
},
});
Expand All @@ -38,8 +38,8 @@ export class ConfigEditor extends PureComponent<Props, State> {
const { onOptionsChange, options } = this.props;
onOptionsChange({
...options,
secureJsonData: {
...options.secureJsonData,
jsonData: {
...options.jsonData,
path: event.target.value,
},
});
Expand All @@ -51,14 +51,10 @@ export class ConfigEditor extends PureComponent<Props, State> {
...options,
secureJsonFields: {
...options.secureJsonFields,
hostname: false,
path: false,
token: false
},
secureJsonData: {
...options.secureJsonData,
hostname: '',
path: '',
token: '',
},
});
Expand All @@ -68,33 +64,30 @@ export class ConfigEditor extends PureComponent<Props, State> {
const { options } = this.props;
const { secureJsonFields } = options;
const secureJsonData = (options.secureJsonData || {}) as MySecureJsonData;
const jsonData = (options.jsonData || {}) as MyDataSourceOptions;

return (
<div className="gf-form-group">
<div className="gf-form--alt">
<div className="gf-form">
<SecretFormField
isConfigured={(secureJsonFields && secureJsonFields.hostname) as boolean}
value={secureJsonData.hostname || ''}
<FormField
value={jsonData.hostname || ''}
label="Server Hostname"
placeholder="XXX.cloud.databricks.com"
tooltip="Databricks Server Hostname (without http)"
labelWidth={10}
inputWidth={500}
onReset={this.onResetDBConfig}
onChange={this.onHostnameChange}
/>
</div>
<div className="gf-form">
<SecretFormField
isConfigured={(secureJsonFields && secureJsonFields.path) as boolean}
value={secureJsonData.path || ''}
<FormField
value={jsonData.path || ''}
label="HTTP Path"
placeholder="sql/1.0/endpoints/XXX"
tooltip="HTTP Path value for the existing cluster or SQL warehouse."
labelWidth={10}
inputWidth={500}
onReset={this.onResetDBConfig}
onChange={this.onPathChange}
/>
</div>
Expand Down
Loading

0 comments on commit c0b0553

Please sign in to comment.