From 35f79b01bdfa8d68ede82d8d24471b716834771c Mon Sep 17 00:00:00 2001 From: Henrik Nilsson Date: Fri, 10 Jan 2025 20:41:49 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Enable=20grafana=20for=20visual=20e?= =?UTF-8?q?xecution=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/on_commit.yaml | 56 +++++- .../src/foreverbull_cli/env.py | 58 ++++-- grafana/Dockerfile | 23 +-- .../pkg/datasource.go | 35 +++- .../provisioning/dashboards/default.yaml | 8 + .../provisioning/dashboards/executions.json | 178 ++++++++++++++++++ .../src/components/QueryEditor.tsx | 92 +++------ .../src/components/VariableQueryEditor.tsx | 37 ++++ .../src/datasource.ts | 42 ++++- .../src/types.ts | 11 +- 10 files changed, 430 insertions(+), 110 deletions(-) create mode 100644 grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/default.yaml create mode 100644 grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/executions.json create mode 100644 grafana/lhjnilsson-foreverbull-datasource/src/components/VariableQueryEditor.tsx diff --git a/.github/workflows/on_commit.yaml b/.github/workflows/on_commit.yaml index 37cd6660..6a895036 100644 --- a/.github/workflows/on_commit.yaml +++ b/.github/workflows/on_commit.yaml @@ -122,6 +122,30 @@ jobs: path: /tmp/foreverbull.tar retention-days: 1 + build-docker-grafana: + runs-on: ubuntu-latest + timeout-minutes: 30 + defaults: + run: + working-directory: ./grafana + steps: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Grafana + uses: docker/build-push-action@v6 + with: + context: grafana + file: ./grafana/Dockerfile + tags: lhjnilsson/fb-grafana:on_commit + outputs: type=docker,dest=/tmp/fb_grafana.tar + - name: Upload Grafana + uses: actions/upload-artifact@v4 + with: + name: fb-grafana + path: /tmp/fb_grafana.tar + retention-days: 1 + go-module-tests: needs: [build-docker-foreverbull, build-docker-zipline] runs-on: ubuntu-latest @@ -233,7 +257,12 @@ jobs: end-to-end-tests: needs: - [build-docker-foreverbull, build-docker-zipline, build-python-packages] + [ + build-docker-foreverbull, + build-docker-zipline, + build-python-packages, + build-docker-grafana, + ] runs-on: ubuntu-latest defaults: run: @@ -257,10 +286,17 @@ jobs: with: name: zipline path: /tmp + - name: Download Grafana + uses: actions/download-artifact@v4 + with: + name: fb-grafana + path: /tmp - name: Load zipline run: docker load -i /tmp/zipline.tar - name: load foreverbull run: docker load -i /tmp/foreverbull.tar + - name: load fb-grafana + run: docker load -i /tmp/fb_grafana.tar - name: Download foreverbull pypi package uses: actions/download-artifact@v4 with: @@ -301,7 +337,7 @@ jobs: pip install ta-lib - name: Create environment run: | - fbull env start --broker-image lhjnilsson/foreverbull:on_commit --backtest-image lhjnilsson/zipline:on_commit + fbull env start --version on_commit - name: create backtest run: fbull backtest create /tmp/example_algorithms/nasdaq.json - name: ingest @@ -337,7 +373,12 @@ jobs: run-manual-backtest: needs: - [build-docker-foreverbull, build-docker-zipline, build-python-packages] + [ + build-docker-foreverbull, + build-docker-zipline, + build-python-packages, + build-docker-grafana, + ] runs-on: ubuntu-latest defaults: run: @@ -361,10 +402,17 @@ jobs: with: name: zipline path: /tmp + - name: Download Grafana + uses: actions/download-artifact@v4 + with: + name: fb-grafana + path: /tmp - name: Load zipline run: docker load -i /tmp/zipline.tar - name: load foreverbull run: docker load -i /tmp/foreverbull.tar + - name: load grafana + run: docker load -i /tmp/fb_grafana.tar - name: Download foreverbull pypi package uses: actions/download-artifact@v4 with: @@ -396,7 +444,7 @@ jobs: pip install /tmp/foreverbull_testing-0.0.1-py3-none-any.whl - name: Create environment run: | - fbull env start --broker-image lhjnilsson/foreverbull:on_commit --backtest-image lhjnilsson/zipline:on_commit + fbull env start --version on_commit - name: create backtest run: fbull backtest create /tmp/example_algorithms/nasdaq.json - name: ingest diff --git a/client/foreverbull_cli/src/foreverbull_cli/env.py b/client/foreverbull_cli/src/foreverbull_cli/env.py index 69da0939..61382a89 100644 --- a/client/foreverbull_cli/src/foreverbull_cli/env.py +++ b/client/foreverbull_cli/src/foreverbull_cli/env.py @@ -17,8 +17,6 @@ from foreverbull_cli.output import console -version = "0.1.0" - env = typer.Typer() @@ -88,8 +86,9 @@ def setup_path( POSTGRES_IMAGE = "postgres:13.3-alpine" NATS_IMAGE = "nats:2.10-alpine" MINIO_IMAGE = "minio/minio:latest" -BROKER_IMAGE = f"lhjnilsson/foreverbull:{version}" -BACKTEST_IMAGE = f"lhjnilsson/zipline:{version}" +BROKER_IMAGE = "lhjnilsson/foreverbull:{}" +BACKTEST_IMAGE = "lhjnilsson/zipline:{}" +GRAFANA_IMAGE = "lhjnilsson/fb-grafana:{}" @env.command() @@ -116,6 +115,10 @@ def status(): foreverbull_container = d.containers.get("foreverbull_foreverbull") except docker.errors.NotFound: foreverbull_container = None + try: + grafana_container = d.containers.get("foreverbull_grafana") + except docker.errors.NotFound: + grafana_container = None try: postgres_image = d.images.get(POSTGRES_IMAGE) @@ -129,6 +132,11 @@ def status(): minio_image = d.images.get(MINIO_IMAGE) except docker.errors.ImageNotFound: minio_image = None + try: + grafana_image = d.images.get(GRAFANA_IMAGE) + except docker.errors.ImageNotFound: + grafana_image = None + try: foreverbull_image = d.images.get(BROKER_IMAGE) # type: ignore except docker.errors.ImageNotFound: @@ -154,6 +162,11 @@ def status(): "Minio", minio_image.short_id if minio_image else "Not found", ) + table.add_row( + grafana_container.status if grafana_container else "Not Found", + "Grafana", + grafana_image.short_id if grafana_image else "Not found", + ) table.add_row( foreverbull_container.status if foreverbull_container else "Not Found", "Foreverbull", @@ -164,8 +177,7 @@ def status(): @env.command() def start( - broker_image: Annotated[str, typer.Option(help="Docker image name of broker")] = BROKER_IMAGE, - backtest_image: Annotated[str, typer.Option(help="Docker image name of backtest service")] = BACKTEST_IMAGE, + version: Annotated[str, typer.Option(help="Version of images to use")] = "latest", log_level: Annotated[str, typer.Option(help="Log level")] = "INFO", ): try: @@ -192,6 +204,7 @@ def get_or_pull_image(image_name): postgres_task_id = progress.add_task("Postgres", total=2) nats_task_id = progress.add_task("NATS", total=2) minio_task_id = progress.add_task("Minio", total=2) + grafana_task_id = progress.add_task("Grafana", total=2) health_task_id = progress.add_task("Waiting for services to start", total=2) foreverbull_task_id = progress.add_task("Foreverbull", total=2) @@ -202,8 +215,9 @@ def get_or_pull_image(image_name): POSTGRES_IMAGE, NATS_IMAGE, MINIO_IMAGE, - broker_image, - backtest_image, + GRAFANA_IMAGE.format(version), + BROKER_IMAGE.format(version), + BACKTEST_IMAGE.format(version), ]: futures.append(executor.submit(get_or_pull_image, image)) wait(futures) @@ -369,7 +383,7 @@ def get_or_pull_image(image_name): except docker.errors.NotFound: try: d.containers.run( - broker_image, + BROKER_IMAGE.format(version), name="foreverbull_foreverbull", detach=True, network=NETWORK_NAME, @@ -398,7 +412,7 @@ def get_or_pull_image(image_name): "NATS_URL": "nats://nats:4222", "MINIO_URL": "minio:9000", "DOCKER_NETWORK": NETWORK_NAME, - "BACKTEST_IMAGE": backtest_image, + "BACKTEST_IMAGE": BACKTEST_IMAGE.format(version), "LOG_LEVEL": log_level, }, # type: ignore volumes={ @@ -415,9 +429,31 @@ def get_or_pull_image(image_name): completed=100, ) exit(1) - time.sleep(2) progress.update(foreverbull_task_id, completed=2) + progress.update(grafana_task_id, completed=1) + try: + d.containers.get("foreverbull_grafana") + except docker.errors.NotFound: + try: + d.containers.run( + GRAFANA_IMAGE.format(version), + name="foreverbull_grafana", + detach=True, + network=NETWORK_NAME, + hostname="grafana", + ports={"3000/tcp": 3000}, + environment={"BROKER_URL": "foreverbull:50055"}, + ) + except Exception as e: + progress.update( + grafana_task_id, + description=f"[red]Failed to start grafana: {e}", + completed=2, + ) + exit(1) + progress.update(grafana_task_id, completed=2) + @env.command() def stop(): diff --git a/grafana/Dockerfile b/grafana/Dockerfile index b3acaa1c..1a18a29c 100644 --- a/grafana/Dockerfile +++ b/grafana/Dockerfile @@ -8,40 +8,37 @@ RUN apk add --no-cache git make WORKDIR /app # Copy Go module files -COPY . . +COPY lhjnilsson-foreverbull-datasource/ . RUN go mod download # Build the binary -RUN CGO_ENABLED=0 GOOS=linux go build -o foreverbull_linux_arm64 . +RUN go install github.com/magefile/mage@latest +RUN mage # Build frontend FROM node:18 AS js-builder WORKDIR /app -COPY package.json . -COPY tsconfig.json . -COPY src/ src/ -COPY README.md . +COPY lhjnilsson-foreverbull-datasource/ . RUN npm install RUN npm run build # Second stage: Setup Grafana with the plugin -FROM grafana/grafana-oss:latest +FROM grafana/grafana-oss:11.3.2 # Create plugin directory -RUN mkdir -p /var/lib/grafana/plugins/foreverbull +RUN mkdir -p /var/lib/grafana/plugins/lhjnilsson-foreverbull-datasource/ # Copy binary from builder -COPY --from=builder /app/foreverbull_linux_arm64 /var/lib/grafana/plugins/foreverbull/ -COPY --from=js-builder /app/dist /var/lib/grafana/plugins/foreverbull/ -COPY src/plugin.json /var/lib/grafana/plugins/foreverbull/ - +COPY --from=builder /app/dist/* /var/lib/grafana/plugins/lhjnilsson-foreverbull-datasource/ +COPY --from=js-builder /app/dist/* /var/lib/grafana/plugins/lhjnilsson-foreverbull-datasource/ +COPY lhjnilsson-foreverbull-datasource/provisioning/ /etc/grafana/provisioning # Set permissions for Grafana user (uid 472) #RUN chown -R 472:472 /var/lib/grafana/plugins/foreverbull \ # && chmod +x /var/lib/grafana/plugins/foreverbull/foreverbull # Enable unsigned plugins -ENV GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=foreverbull +ENV GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=lhjnilsson-foreverbull-datasource ENV GF_AUTH_ANONYMOUS_ENABLED=true ENV GF_AUTH_DISABLE_LOGIN_FORM=true ENV GF_AUTH_ANONYMOUS_ORG_ROLE=Admin diff --git a/grafana/lhjnilsson-foreverbull-datasource/pkg/datasource.go b/grafana/lhjnilsson-foreverbull-datasource/pkg/datasource.go index df8089c2..14b8c2da 100644 --- a/grafana/lhjnilsson-foreverbull-datasource/pkg/datasource.go +++ b/grafana/lhjnilsson-foreverbull-datasource/pkg/datasource.go @@ -110,7 +110,7 @@ func (d *Datasource) HandleGetExecutionMetric(ctx context.Context, req *grafana. } type queryModel struct { - ExecutionId string `json:"execution_id"` + ExecutionId string `json:"executionId"` } func (d *Datasource) handleGetExecutionMetric(ctx context.Context, req grafana.QueryDataRequest, q grafana.DataQuery) grafana.DataResponse { @@ -126,16 +126,33 @@ func (d *Datasource) handleGetExecutionMetric(ctx context.Context, req grafana.Q return grafana.ErrDataResponse(grafana.StatusBadRequest, fmt.Sprintf("json unmarshal: %v", err.Error())) } - execution, err := d.backend.GetExecution(ctx, &pb.GetExecutionRequest{ExecutionId: "0a7378c6-950b-4e8e-af40-4378c9927e9f"}) + execution, err := d.backend.GetExecution(ctx, &pb.GetExecutionRequest{ExecutionId: qm.ExecutionId}) if err != nil { return grafana.ErrDataResponse(grafana.StatusBadRequest, fmt.Sprintf("json unmarshal: %v", err.Error())) } times := make([]time.Time, len(execution.Periods)) - values := make([]float64, len(execution.Periods)) + + pnl := make([]float64, len(execution.Periods)) + returns := make([]float64, len(execution.Periods)) + portfolio_value := make([]float64, len(execution.Periods)) + longs_count := make([]int32, len(execution.Periods)) + shorts_count := make([]int32, len(execution.Periods)) + long_value := make([]float64, len(execution.Periods)) + short_value := make([]float64, len(execution.Periods)) + sharpe := make([]*float64, len(execution.Periods)) + sortio := make([]*float64, len(execution.Periods)) for i, period := range execution.Periods { times[i] = time.Date(int(period.Date.Year), time.Month(int(period.Date.Month)), int(period.Date.Day), 0, 0, 0, 0, time.UTC) - values[i] = period.PortfolioValue + pnl[i] = period.PNL + returns[i] = period.Returns + portfolio_value[i] = period.PortfolioValue + longs_count[i] = period.LongsCount + shorts_count[i] = period.ShortsCount + long_value[i] = period.LongValue + short_value[i] = period.ShortValue + sharpe[i] = period.Sharpe + sortio[i] = period.Sortino } // create data frame response. @@ -146,7 +163,15 @@ func (d *Datasource) handleGetExecutionMetric(ctx context.Context, req grafana.Q // add fields. frame.Fields = append(frame.Fields, data.NewField("time", nil, times), - data.NewField("values", nil, values), + data.NewField("profit & loss", nil, pnl), + data.NewField("returns", nil, portfolio_value), + data.NewField("portfolio value", nil, portfolio_value), + data.NewField("longs count", nil, longs_count), + data.NewField("shorts count", nil, shorts_count), + data.NewField("long value", nil, long_value), + data.NewField("short value", nil, short_value), + data.NewField("sharpe", nil, sharpe), + data.NewField("sortio", nil, sortio), ) // add the frames to the response. diff --git a/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/default.yaml b/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/default.yaml new file mode 100644 index 00000000..cc72cdcb --- /dev/null +++ b/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/default.yaml @@ -0,0 +1,8 @@ +apiVersion: 1 + +providers: + - name: Default # A uniquely identifiable name for the provider + folder: Foreverbull # The folder where to place the dashboards + type: file + options: + path: /etc/grafana/provisioning/dashboards/ diff --git a/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/executions.json b/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/executions.json new file mode 100644 index 00000000..59c41cc1 --- /dev/null +++ b/grafana/lhjnilsson-foreverbull-datasource/provisioning/dashboards/executions.json @@ -0,0 +1,178 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "lhjnilsson-foreverbull-datasource", + "uid": "P7866CEC33224B4CB" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": ["portfolio value"], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 16, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.2", + "targets": [ + { + "datasource": { + "type": "lhjnilsson-foreverbull-datasource", + "uid": "P7866CEC33224B4CB" + }, + "executionId": "$execution", + "queryType": "GetExecutionMetric", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [ + { + "current": { + "text": "a5f5dde9-e45c-462b-9c83-68cbe29b9bd7", + "value": "a5f5dde9-e45c-462b-9c83-68cbe29b9bd7" + }, + "datasource": { + "type": "lhjnilsson-foreverbull-datasource", + "uid": "P7866CEC33224B4CB" + }, + "definition": "", + "name": "execution", + "options": [], + "query": { + "executionId": "84753648-1bba-4a76-8c5f-c0631204633e" + }, + "refresh": 1, + "regex": "", + "type": "query" + } + ] + }, + "time": { + "from": "2023-10-30T05:33:35.444Z", + "to": "2023-12-25T19:45:02.482Z" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Foreverbull", + "uid": "ee9ep4jxtwkcgf", + "version": 6, + "weekStart": "" +} diff --git a/grafana/lhjnilsson-foreverbull-datasource/src/components/QueryEditor.tsx b/grafana/lhjnilsson-foreverbull-datasource/src/components/QueryEditor.tsx index b59c1bd7..c57861ff 100644 --- a/grafana/lhjnilsson-foreverbull-datasource/src/components/QueryEditor.tsx +++ b/grafana/lhjnilsson-foreverbull-datasource/src/components/QueryEditor.tsx @@ -1,92 +1,56 @@ import React, { ChangeEvent, useState } from 'react'; -import { InlineField, Input, Stack, Select, AsyncMultiSelect } from '@grafana/ui'; +import { InlineField, Input, Stack, Select, AsyncMultiSelect, AsyncSelect, SelectValue, QueryField } from '@grafana/ui'; import { QueryEditorProps, SelectableValue } from '@grafana/data'; import { DataSource } from '../datasource'; -import { MyDataSourceOptions, MyQuery, DEFAULT_QUERY, QueryType } from '../types'; +import { MyDataSourceOptions, MyQuery, ResourceDefinition } from '../types'; import defaults from 'lodash/defaults'; -export interface QueryTypeInfo extends SelectableValue { - value: QueryType; +export interface QueryTypeInfo extends SelectableValue { + value: ResourceDefinition; } -export const queryTypeInfos: QueryTypeInfo[] = [ - { - label: 'Get Execution Metric', - value: QueryType.GetExecutionMetric, - description: ``, - }, -]; - type Props = QueryEditorProps; export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props) { - const [queryType, setQueryType] = useState(query.queryType); - const [q, setQuery] = useState(defaults(query, DEFAULT_QUERY)); - - const updateAndRunQuery = (q: MyQuery) => { - onChange(q); - setQuery(q); - onRunQuery(); - }; + const [selectedExecution, setSelectedExecution] = useState | null>(null); const loadExecutions = (): Promise>> => { return datasource.getExecutions(); }; - const loadMetrics = (): Promise>> => { - return datasource.getMetrics(); + const onExecutionChange = async (queryType: SelectableValue) => { + setSelectedExecution(queryType); + onChange({ ...query, executionId: queryType.value }); + onRunQuery(); }; - const selectedExecutions = q.executionIds?.map((x) => ({ label: x.label, value: x.value })); - const selectedMetrics = q.metrics?.map((x) => ({ label: x.label, value: x.value })); - - const onExecutionChange = (evt: Array>) => { - const m = evt.map((x) => ({ value: x.value, label: x.label })); - updateAndRunQuery({ ...q, executionIds: m }); - }; - const onMetricChange = (evt: Array>) => { - const m = evt.map((x) => ({ value: x.value, label: x.label })); - updateAndRunQuery({ ...q, metrics: m }); + const onQueryTextChange = (event: ChangeEvent) => { + onChange({ ...query, executionId: event.target.value }); + onRunQuery(); }; - const executionKey = q.executionIds?.map((x) => x.key).join(); - // END - - const currentQueryType = queryTypeInfos.find((v) => v.value === query.queryType); - const onQueryTypeChange = async (queryType: QueryType) => { - setQueryType(queryType); - updateAndRunQuery({ ...query, queryType: queryType }); - }; + const { executionId } = query; return ( - + ); } diff --git a/grafana/lhjnilsson-foreverbull-datasource/src/components/VariableQueryEditor.tsx b/grafana/lhjnilsson-foreverbull-datasource/src/components/VariableQueryEditor.tsx new file mode 100644 index 00000000..3e0ca779 --- /dev/null +++ b/grafana/lhjnilsson-foreverbull-datasource/src/components/VariableQueryEditor.tsx @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import { MyQuery } from '../types'; +import { DataSource } from '../datasource'; +import { SelectableValue } from '@grafana/data'; +import { AsyncSelect, InlineField, Input, Select, InlineFieldRow } from '@grafana/ui'; + +const VariableQueryEditor = (props: { + query: MyQuery; + onChange: (query: MyQuery, definition: string) => void; + datasource: DataSource; +}) => { + const dims: any[] = []; + const { datasource, onChange, query } = props; + const [selectedExecution, setSelectedExecution] = useState | null>(null); + + const loadExecutions = (): Promise>> => { + return datasource.getExecutions(); + }; + + const onExecutionChange = async (event: SelectableValue) => { + setSelectedExecution(event); + onChange({ ...query, executionId: event.label }, event.label); + }; + + return ( + onExecutionChange(evt)} + width={32} + required={false} + /> + ); +}; + +export default VariableQueryEditor; diff --git a/grafana/lhjnilsson-foreverbull-datasource/src/datasource.ts b/grafana/lhjnilsson-foreverbull-datasource/src/datasource.ts index d2e4a6fd..5ef6243d 100644 --- a/grafana/lhjnilsson-foreverbull-datasource/src/datasource.ts +++ b/grafana/lhjnilsson-foreverbull-datasource/src/datasource.ts @@ -1,4 +1,12 @@ -import { DataSourceInstanceSettings, CoreApp, ScopedVars, SelectableValue } from '@grafana/data'; +import { + DataSourceInstanceSettings, + CoreApp, + ScopedVars, + SelectableValue, + CustomVariableSupport, + DataQueryRequest, + DataQueryResponse, +} from '@grafana/data'; import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; import { MyQuery, MyDataSourceOptions, DEFAULT_QUERY } from './types'; @@ -9,9 +17,34 @@ export interface ResourceDefinition { description?: string; } +import VariableQueryEditor from './components/VariableQueryEditor'; + +import { Observable, from } from 'rxjs'; +import { map } from 'rxjs/operators'; + +export class DatasourceVariableSupport extends CustomVariableSupport { + editor = VariableQueryEditor; + + constructor(private datasource: DataSource) { + super(); + this.query = this.query.bind(this); + } + + async execute(query: MyQuery) { + return this.datasource.getExecutions(); + } + + query(request: DataQueryRequest): Observable { + const result = this.execute(request.targets[0]); + + return from(result).pipe(map((data) => ({ data }))); + } +} + export class DataSource extends DataSourceWithBackend { constructor(instanceSettings: DataSourceInstanceSettings) { super(instanceSettings); + this.variables = new DatasourceVariableSupport(this); } getDefaultQuery(_: CoreApp): Partial { @@ -21,7 +54,7 @@ export class DataSource extends DataSourceWithBackend = { queryType: QueryType.GetExecutionMetric, - executionIds: [], - metrics: [], }; export interface MyDataSourceOptions extends DataSourceJsonData {}