Skip to content

Commit

Permalink
feat: add helper documentation and some bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mullerpeter committed Nov 12, 2022
1 parent 4e02aaa commit 40a347c
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 127 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Changelog

## 0.0.3 (Unreleased)
## 0.0.5 (Unreleased)

Initial release.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/hashicorp/go-hclog v0.14.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.13.1 // indirect
Expand All @@ -40,6 +41,7 @@ require (
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
Expand Down Expand Up @@ -199,6 +200,8 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
Expand All @@ -225,6 +228,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
Expand All @@ -241,6 +245,9 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "climeworks-databricks",
"version": "0.0.4",
"version": "0.0.5",
"description": "Databricks SQL Connector",
"scripts": {
"build": "grafana-toolkit plugin:build",
Expand Down
156 changes: 101 additions & 55 deletions pkg/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana-plugin-sdk-go/live"
"math"
"regexp"
"strings"
"time"
)

Expand Down Expand Up @@ -93,12 +95,107 @@ type queryModel struct {
RawSqlSelected bool `json:"rawSqlSelected"`
}

func getIntervalString(duration time.Duration) string {
hours := int(math.Floor(duration.Hours()))
minutes := int(math.Floor(duration.Minutes()))
seconds := int(math.Floor(duration.Seconds()))

returnString := ""

deliminator := ""
if hours > 0 {
returnString = fmt.Sprintf("%s%s%d HOURS", returnString, deliminator, hours)
deliminator = " "
}

remainingMinutes := minutes - (hours * 60)
if remainingMinutes > 0 {
returnString = fmt.Sprintf("%s%s%d MINUTES", returnString, deliminator, remainingMinutes)
deliminator = " "
}

remainingSeconds := seconds - (minutes * 60)
if remainingSeconds > 0 {
returnString = fmt.Sprintf("%s%s%d SECONDS", returnString, deliminator, remainingSeconds)
deliminator = " "
}

return returnString
}

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

queryString := sqlQuery
log.DefaultLogger.Info("Raw SQL Query selected", "query", queryString)

interval_string := getIntervalString(query.Interval)

var rgx = regexp.MustCompile(`\$__timeWindow\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__timeWindow placeholder found")
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("window(%s, '%s')", timeColumnName, interval_string))

rgx = regexp.MustCompile(`\$__time\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__time placeholder found")
queryString = rgx.ReplaceAllString(queryString, "window.start")
}

rgx = regexp.MustCompile(`\$__value\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__value placeholder found")
rs = rgx.FindStringSubmatch(queryString)
valueColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("avg(%s) AS value", valueColumnName))
}
} else {
rgx = regexp.MustCompile(`\$__time\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__time placeholder found")
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("%s AS time", timeColumnName))
}

rgx = regexp.MustCompile(`\$__value\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__value placeholder found")
rs := rgx.FindStringSubmatch(queryString)
valueColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("%s AS value", valueColumnName))
}
}

rgx = regexp.MustCompile(`\$__timeFilter\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
timeRangeFilter := fmt.Sprintf("%s BETWEEN '%s' AND '%s'",
timeColumnName,
query.TimeRange.From.UTC().Format("2006-01-02 15:04:05"),
query.TimeRange.To.UTC().Format("2006-01-02 15:04:05"),
)
queryString = rgx.ReplaceAllString(queryString, timeRangeFilter)
}

queryString = strings.ReplaceAll(queryString, "$__timeFrom", query.TimeRange.From.UTC().Format("2006-01-02 15:04:05"))

queryString = strings.ReplaceAll(queryString, "$__timeTo", query.TimeRange.To.UTC().Format("2006-01-02 15:04:05"))

queryString = strings.ReplaceAll(queryString, "$__interval", interval_string)

return queryString
}

func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext, query backend.DataQuery) backend.DataResponse {
response := backend.DataResponse{}

// Unmarshal the JSON into our queryModel.
var qm queryModel

log.DefaultLogger.Info("Query Ful", "query", query)
err := json.Unmarshal(query.JSON, &qm)
if err != nil {
response.Error = err
Expand All @@ -111,75 +208,24 @@ func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext,
if seconds_interval <= 0 {
seconds_interval = 1
}
interval_string := getIntervalString(query.Interval)
queryString := ""
if qm.RawSqlSelected {
queryString = qm.RawSqlQuery
log.DefaultLogger.Info("Raw SQL Query selected", "query", queryString)

var rgx = regexp.MustCompile(`\$__timeWindow\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__timeWindow placeholder found")
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("window(%s, '%d SECONDS')", timeColumnName, seconds_interval))

rgx = regexp.MustCompile(`\$__time\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__time placeholder found")
queryString = rgx.ReplaceAllString(queryString, "window.start")
}

rgx = regexp.MustCompile(`\$__value\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__value placeholder found")
rs = rgx.FindStringSubmatch(queryString)
valueColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("avg(%s) AS value", valueColumnName))
}
} else {
rgx = regexp.MustCompile(`\$__time\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__time placeholder found")
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("%s AS time", timeColumnName))
}

rgx = regexp.MustCompile(`\$__value\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
log.DefaultLogger.Info("__value placeholder found")
rs := rgx.FindStringSubmatch(queryString)
valueColumnName := rs[1]
queryString = rgx.ReplaceAllString(queryString, fmt.Sprintf("%s AS value", valueColumnName))
}
}

rgx = regexp.MustCompile(`\$__timeFilter\(([a-zA-Z0-9_-]+)\)`)
if rgx.MatchString(queryString) {
rs := rgx.FindStringSubmatch(queryString)
timeColumnName := rs[1]
timeRangeFilter := fmt.Sprintf("%s BETWEEN '%s' AND '%s'",
timeColumnName,
query.TimeRange.From.UTC().Format("2006-01-02 15:04:05"),
query.TimeRange.To.UTC().Format("2006-01-02 15:04:05"),
)
queryString = rgx.ReplaceAllString(queryString, timeRangeFilter)
}

queryString = replaceMacros(qm.RawSqlQuery, query)
} else {
whereQuery := ""
if qm.WhereQuery != "" {
whereQuery = fmt.Sprintf(" %s AND", qm.WhereQuery)
}
queryString = fmt.Sprintf("SELECT window.start, avg(%s) AS value FROM %s WHERE%s %s BETWEEN '%s' AND '%s' GROUP BY window(%s, '%d SECONDS')",
queryString = 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,
seconds_interval)
interval_string)
}
log.DefaultLogger.Info("Query", "query", queryString)

Expand Down
42 changes: 21 additions & 21 deletions src/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import { defaults } from 'lodash';

import React, {FormEvent, useState} from 'react';
import { AutoSizeInput, InlineFieldRow, InlineField, useTheme, InlineSwitch } from '@grafana/ui';
import React, {FormEvent } from 'react';
import { AutoSizeInput, InlineFieldRow, InlineField, InlineSwitch, CodeEditor } from '@grafana/ui';
import { QueryEditorProps } from '@grafana/data';
import { DataSource } from './datasource';
import Editor from "@monaco-editor/react";
import { defaultQuery, MyDataSourceOptions, MyQuery } from './types';
import * as monaco from "monaco-editor";

type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;

export function QueryEditor(props: Props) {

const [, setQueryText] = useState("")
const [sqlEditorSelected, setSqlEditorSelected] = useState(false)
const onQuerryChange = (value: string | undefined, ev: monaco.editor.IModelContentChangedEvent) => {
setQueryText(value || "")
const query = defaults(props.query, defaultQuery);
const { timeColumnName, valueColumnName, whereQuery, tableName, rawSqlQuery, rawSqlSelected } = query;

const onSQLQueryChange = (value: string) => {
const { onChange, query } = props;
onChange({ ...query, rawSqlQuery: value });
};

const onSQLSwitchChange = (event: any) => {
const { onChange, query } = props;
onChange({ ...query, rawSqlSelected: !sqlEditorSelected });
setSqlEditorSelected(!sqlEditorSelected);
onChange({ ...query, rawSqlSelected: !rawSqlSelected });
};
const ontableNameChange = (event: FormEvent<HTMLInputElement>) => {
console.log(event);
Expand Down Expand Up @@ -52,19 +49,22 @@ export function QueryEditor(props: Props) {
onChange({ ...query, whereQuery: event.currentTarget.value });
};

const query = defaults(props.query, defaultQuery);
const { timeColumnName, valueColumnName, whereQuery, tableName } = query;
const theme = useTheme()

return (
<div className="gf-form" style={{ flexDirection: "column", rowGap: "8px"}}>
{sqlEditorSelected ? (
<Editor
{rawSqlSelected ? (
<div className="code-wrapper" style={{ width: "100%" }}>
<CodeEditor
value={rawSqlQuery || ""}
language="sql"
height="200px"
theme={theme.isDark ? "vs-dark" : "vs-light"}
defaultLanguage="sql"
defaultValue="SELECT $__time(time_column), $__value(value_column) FROM catalog.default.table_name WHERE $__timeFilter(time_column) GROUP BY $__timeWindow(time_column)"
onChange={onQuerryChange}
/>
width="100%"
onBlur={onSQLQueryChange}
onSave={onSQLQueryChange}
showMiniMap={false}
showLineNumbers={false}
/>
</div>
) : (
<>
<InlineFieldRow>
Expand Down Expand Up @@ -115,7 +115,7 @@ export function QueryEditor(props: Props) {
<InlineFieldRow>
<InlineField label="SQL Editor" labelWidth={16}>
<InlineSwitch
value={sqlEditorSelected}
value={rawSqlSelected}
onChange={onSQLSwitchChange}
/>
</InlineField>
Expand Down
48 changes: 48 additions & 0 deletions src/QueryEditorHelp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { QueryEditorHelpProps} from '@grafana/data';
import { MyQuery } from './types';

const examples = [
{
title: 'Time Range & Windowing',
expression: "SELECT $__time(time_column), avg(value_column) FROM catalog.default.table_name WHERE $__timeFilter(time_column) GROUP BY $__timeWindow(time_column)",
label: 'Inserts Time range filters and window aggregation into the query. The resulting query with macros replaced would look like this:',
resultingQuery: "SELECT window.start, avg(value_column) FROM catalog.default.table_name WHERE time_column BETWEEN '2021-12-31 23:00:00' AND '2022-01-01 22:59:59' GROUP BY window(time_column, '2 HOURS')",
rawSqlQuery: "SELECT $__time(time_column), avg(value_column) FROM catalog.default.table_name WHERE $__timeFilter(time_column) GROUP BY $__timeWindow(time_column)",
rawSqlSelected: true,
},
{
title: 'Time From/To',
expression: 'SELECT time_column, value_column FROM catalog.default.table_name WHERE time_column BETWEEN $__timeFrom AND $__timeTo',
label: 'Insert Time range filters from and to time values.',
rawSqlQuery: "SELECT time_column, value_column FROM catalog.default.table_name WHERE time_column BETWEEN $__timeFrom AND $__timeTo",
resultingQuery: "SELECT time_column, value_column FROM catalog.default.table_name WHERE time_column BETWEEN '2021-12-31 23:00:00' AND '2022-01-01 22:59:59'",
rawSqlSelected: true,
},
];


type Props = QueryEditorHelpProps<MyQuery>;
export function QueryEditorHelp(props: Props) {
return (
<div>
<h2>SQL Query Documentation</h2>
<h3>Macros</h3>
{examples.map((item, index) => (
<div className="cheat-sheet-item" key={index}>
<div className="cheat-sheet-item__title">{item.title}</div>
{item.expression ? (
<div
className="cheat-sheet-item__example"
onClick={(e) => props.onClickExample({ rawSqlQuery: item.rawSqlQuery, rawSqlSelected: item.rawSqlSelected } as MyQuery)}
>
<code>{item.expression}</code>
</div>
) : null}
<div className="cheat-sheet-item__label" style={{margin: "16px 0 8px 0"}}>{item.label}</div>
{item.resultingQuery && (<code>{item.resultingQuery}</code>)}
</div>
))}
</div>
);
};
Loading

0 comments on commit 40a347c

Please sign in to comment.