Skip to content

Commit

Permalink
Add query variable support (#17)
Browse files Browse the repository at this point in the history
* Add query variable support

* add git hash info

* Revert "add git hash info"

This reverts commit 630f29c.

* update version

* preview values in text

* test update plugin.json script
  • Loading branch information
haohanyang authored Oct 22, 2024
1 parent e1a159e commit 15c756d
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 24 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ jobs:
- 27018:27017
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Update plugin.json
run: python3 scripts/update_plugin_metadata.py
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_REF_NAME: ${{ github.ref_name }}
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mongodb-datasource",
"version": "0.1.0",
"version": "0.1.1",
"scripts": {
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
Expand Down Expand Up @@ -72,4 +72,4 @@
"tslib": "2.5.3"
},
"packageManager": "npm@9.6.7"
}
}
59 changes: 59 additions & 0 deletions scripts/update_plugin_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import json
import os
import subprocess
import pprint
from datetime import datetime

timestamp = int(
subprocess.check_output(["git", "show", "-s", "--format=%ct"]).decode().strip()
)

update_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")


def get_required_env_var(key_name: str, default=None):
val = os.getenv(key_name, default)
if val is None:
raise ValueError(f"Environment variable {key_name} is not set")
return val


# ${{ github.repository }}
repo = get_required_env_var("GITHUB_REPOSITORY")
# ${{ github.sha }}
sha = get_required_env_var("GITHUB_SHA")
# ${{ github.run_id }}
run_id = get_required_env_var("GITHUB_RUN_ID")
# ${{ github.ref_name }}
branch = get_required_env_var("GITHUB_REF_NAME")

with open(os.path.join("src", "plugin.json")) as f:
metadata = json.load(f)

with open("package.json") as f:
version = json.load(f)["version"]


links = [
{"name": "Source", "url": f"https://github.com/{repo}"},
{
"name": "Commit",
"url": f"https://github.com/{repo}/commit/{sha}",
},
{
"name": "Build",
"url": f"https://github.com/{repo}/actions/runs/{run_id}",
},
]

metadata["info"]["links"] = links
if branch == "master":
metadata["info"]["version"] = version
else:
metadata["info"]["version"] = version + "-" + sha[:7]

metadata["info"]["updated"] = update_time

pprint.pprint(metadata)
with open(os.path.join("src", "plugin.json"), "w") as f:
json.dump(metadata, f)
48 changes: 48 additions & 0 deletions src/components/VariableQueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from "react";
import { DEFAULT_QUERY, VariableQuery } from "../types";
import { CodeEditor, Field, InlineField, Input } from "@grafana/ui";

interface VariableQueryProps {
query: VariableQuery;
onChange: (query: VariableQuery, definition: string) => void;
}

export const VariableQueryEditor = ({ onChange, query }: VariableQueryProps) => {
const [state, setState] = useState(query);

const saveQuery = () => {
onChange(state, `${state.collection} (${state.queryText})`);
};

const handleCollectionChange = (event: React.FormEvent<HTMLInputElement>) =>
setState({
...state,
collection: event.currentTarget.value,
});

const handleQueryTextChange = (text: string) =>
setState({
...state,
queryText: text,
});

return (
<>
<InlineField label="Collection" tooltip="Enter the MongoDB collection"
error="Please enter the collection" invalid={!query.collection}>
<Input
name="collection"
onBlur={saveQuery}
onChange={handleCollectionChange}
value={state.collection}>
</Input>
</InlineField>
<Field label="Query Text" description="MongoDB aggregate (JSON)">
<CodeEditor width="100%" height={300} language="json" onBlur={saveQuery}
value={query.queryText || DEFAULT_QUERY.queryText!} showMiniMap={false} showLineNumbers={true}
onChange={handleQueryTextChange}
/>
</Field>
</>
);
};
42 changes: 38 additions & 4 deletions src/datasource.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { DataSourceInstanceSettings, CoreApp, ScopedVars, DataQueryRequest, DataQueryResponse } from "@grafana/data";
import { DataSourceInstanceSettings, CoreApp, ScopedVars, DataQueryRequest, DataQueryResponse, LegacyMetricFindQueryOptions, MetricFindValue, dateTime } from "@grafana/data";
import { DataSourceWithBackend, getTemplateSrv } from "@grafana/runtime";
import {parseJsQuery, datetimeToJson, getBucketCount, parseJsQueryLegacy} from "./utils";
import { MongoQuery, MongoDataSourceOptions, DEFAULT_QUERY, QueryLanguage } from "./types";
import { Observable } from "rxjs";
import { parseJsQuery, datetimeToJson, getBucketCount, parseJsQueryLegacy, randomId, getMetricValues } from "./utils";
import { MongoQuery, MongoDataSourceOptions, DEFAULT_QUERY, QueryLanguage, VariableQuery } from "./types";
import { Observable, firstValueFrom } from "rxjs";


export class DataSource extends DataSourceWithBackend<MongoQuery, MongoDataSourceOptions> {
Expand All @@ -21,6 +21,40 @@ export class DataSource extends DataSourceWithBackend<MongoQuery, MongoDataSourc
};
}

async metricFindQuery(query: VariableQuery, options?: LegacyMetricFindQueryOptions): Promise<MetricFindValue[]> {
const request: DataQueryRequest<MongoQuery> = {
requestId: "variable-query-" + randomId(3),
targets: [{
refId: "A",
queryLanguage: QueryLanguage.JSON,
collection: query.collection,
queryText: getTemplateSrv().replace(query.queryText),
queryType: "table"
}],
scopedVars: options?.scopedVars || {},
interval: "5s",
timezone: "browser",
intervalMs: 5000,
range: options?.range || {
from: dateTime(),
to: dateTime(),
raw: {
from: "now",
to: "now"
}
},
app: "variable-query",
startTime: (options?.range?.from || dateTime()).toDate().getUTCMilliseconds()
};

const resp = await firstValueFrom(this.query(request));
if (resp.errors?.length && resp.errors.length > 0) {
throw new Error(resp.errors[0].message || "Unknown error");
}

return getMetricValues(resp);
}

filterQuery(query: MongoQuery): boolean {
return !!query.queryText && !!query.collection;
}
Expand Down
4 changes: 3 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { DataSource } from "./datasource";
import { ConfigEditor } from "./components/ConfigEditor";
import { QueryEditor } from "./components/QueryEditor";
import { QueryHelper } from "./components/QueryHelper";
import { VariableQueryEditor } from "./components/VariableQueryEditor";
import { MongoQuery, MongoDataSourceOptions } from "./types";

export const plugin = new DataSourcePlugin<DataSource, MongoQuery, MongoDataSourceOptions>(DataSource)
.setConfigEditor(ConfigEditor)
.setQueryEditor(QueryEditor)
.setQueryEditorHelp(QueryHelper);
.setQueryEditorHelp(QueryHelper)
.setVariableQueryEditor(VariableQueryEditor);

Check warning on line 13 in src/module.ts

View workflow job for this annotation

GitHub Actions / Build, lint and unit tests

'setVariableQueryEditor' is deprecated. -- prefer using {@link StandardVariableSupport} or {@link CustomVariableSupport} or {@link DataSourceVariableSupport} in data source instead
7 changes: 1 addition & 6 deletions src/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@
"small": "img/logo.svg",
"large": "img/logo.svg"
},
"links": [
{
"name": "Source Code",
"url": "https://github.com/haohanyang/mongodb-datasource"
}
],
"links": [],
"screenshots": [],
"version": "%VERSION%",
"updated": "%TODAY%"
Expand Down
18 changes: 10 additions & 8 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,22 @@ export const QueryLanguage = {


export const DEFAULT_QUERY: Partial<MongoQuery> = {
queryText: "[]",
queryText: `[
{
"$limit": 10
}
]`,
queryType: QueryType.TIMESERIES,
queryLanguage: QueryLanguage.JSON
};

export interface DataPoint {
Time: number;
Value: number;
}

export interface JsQueryResult {
jsonQuery?: string;
collection?: string;
error: string | null;
}

export interface DataSourceResponse {
datapoints: DataPoint[];
}

export const MongoDBAuthMethod = {
NONE: "auth-none",
Expand All @@ -51,6 +48,10 @@ export const ConnectionStringScheme = {
DNS_SEED_LIST: "dns_seed_list"
};

export interface VariableQuery {
collection?: string;
queryText?: string;
}

/**
* These are options configured for each DataSource instance
Expand All @@ -70,3 +71,4 @@ export interface MongoDataSourceOptions extends DataSourceJsonData {
export interface MySecureJsonData {
password?: string;
}

47 changes: 44 additions & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DateTime } from "@grafana/data";
import { DataFrameSchema, DataQueryResponse, DateTime, FieldType, MetricFindValue } from "@grafana/data";
import { JsQueryResult } from "types";
import shadow from "shadowrealm-api";
import {getTemplateSrv} from "@grafana/runtime";
import { getTemplateSrv } from "@grafana/runtime";

export function validateJsonQueryText(queryText?: string): string | null {
if (!queryText) {
Expand Down Expand Up @@ -57,7 +57,7 @@ export function parseJsQuery(queryText: string): JsQueryResult {
jsonQuery: result,
error: null
};
}catch (e: Error | any) {
} catch (e: Error | any) {
// if there is an error, return the error message
return {
error: e?.message
Expand Down Expand Up @@ -86,3 +86,44 @@ export function getBucketCount(from: DateTime, to: DateTime, intervalMs: number)

return count;
}

export function randomId(length: number) {
let result = "";
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}


export function getMetricValues(response: DataQueryResponse): MetricFindValue[] {
const dataframe = response.data[0] as DataFrameSchema;
const fields = dataframe.
fields.filter(f => f.type === FieldType.string || f.type === FieldType.number)
// @ts-ignore
.filter(f => f.values && f.values.length > 0);

// @ts-ignore
return fields.map(function (field) {
// @ts-ignore
const values: Array<string | number> = field.values;
let text: string;

if (values.length === 1) {
text = `${field.name}:${values[0]}`;
} else {
text = `${field.name}:[${values[0]}, ...]`;
}

return {
text: text,
// @ts-ignore
value: values.length === 1 ? values[0] : values,
expandable: true
};
});
}

0 comments on commit 15c756d

Please sign in to comment.