diff --git a/package.json b/package.json index c8169df..62ca9de 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "axios": "^0.18.0", "express": "^4.16.3", "fast-csv": "^2.4.1", + "grafana-datasource-kit": "^0.0.4", "moment": "^2.22.1", "nodemon": "^1.17.3", "ts-loader": "^3.5.0", diff --git a/src/grafana-datasource-kit/grafana_metric_model.ts b/src/grafana-datasource-kit/grafana_metric_model.ts deleted file mode 100644 index 9991233..0000000 --- a/src/grafana-datasource-kit/grafana_metric_model.ts +++ /dev/null @@ -1,88 +0,0 @@ -export type GrafanaDatasource = { - url: string, - type: string, - params: { - db: string, - q: string, - epoch: string - } -} - -export type GrafanaMetricId = string; - -export class GrafanaMetric { - - private _metricQuery: MetricQuery = undefined; - - constructor( - public datasource: GrafanaDatasource, - public targets: any[], - public id?: GrafanaMetricId - ) { - if(datasource === undefined) { - throw new Error('datasource is undefined'); - } - if(targets === undefined) { - throw new Error('targets is undefined'); - } - if(targets.length === 0) { - throw new Error('targets is empty'); - } - } - - public get metricQuery() { - if(this._metricQuery === undefined) { - this._metricQuery = new MetricQuery(this); - } - return this._metricQuery; - } - - public toObject() { - return { - datasource: this.datasource, - targets: this.targets, - _id: this.id - }; - } - - static fromObject(obj: any): GrafanaMetric { - if(obj === undefined) { - throw new Error('obj is undefined'); - } - return new GrafanaMetric( - obj.datasource, - obj.targets, - obj._id - ); - } -} - -export class MetricQuery { - - private static INFLUX_QUERY_TIME_REGEX = /time >[^A-Z]+/; - - private _queryParts: string[]; - private _type: string; - - constructor(metric: GrafanaMetric) { - this._type = metric.datasource.type; - if (this._type !== 'influxdb') { - throw new Error(`Queries of type "${metric.datasource.type}" are not supported yet.`); - } - var queryStr = metric.datasource.params.q; - this._queryParts = queryStr.split(MetricQuery.INFLUX_QUERY_TIME_REGEX); - if(this._queryParts.length == 1) { - throw new Error( - `Query "${queryStr}" is not replaced with LIMIT/OFFSET oeprators. Missing time clause.` - ); - } - if(this._queryParts.length > 2) { - throw new Error(`Query "${queryStr}" has multiple time clauses. Can't parse.`); - } - } - - getQuery(from: number, to: number, limit: number, offset: number): string { - let timeClause = `time >= ${from}ms AND time <= ${to}ms`; - return `${this._queryParts[0]} ${timeClause} ${this._queryParts[1]} LIMIT ${limit} OFFSET ${offset}`; - } -} diff --git a/src/grafana-datasource-kit/grafana_service.ts b/src/grafana-datasource-kit/grafana_service.ts deleted file mode 100644 index e105066..0000000 --- a/src/grafana-datasource-kit/grafana_service.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { GrafanaMetric } from './grafana_metric_model'; -import { getApiKey } from '../config'; -import { URL } from 'url'; -import axios from 'axios'; - - -const CHUNK_SIZE = 50000; - - -/** - * @param metric to query to Grafana - * @returns [time, value][] array - */ -export async function queryByMetric( - metric: GrafanaMetric, panelUrl: string, from: number, to: number -) { - - let datasource = metric.datasource; - - let origin = new URL(panelUrl).origin; - let url = `${origin}/${datasource.url}`; - - let params = datasource.params - let data = { - values: [], - columns: [] - }; - - let chunkParams = Object.assign({}, params); - while(true) { - chunkParams.q = metric.metricQuery.getQuery(from, to, CHUNK_SIZE, data.values.length); - var chunk = await queryGrafana(url, chunkParams); - let values = chunk.values; - data.values = data.values.concat(values); - data.columns = chunk.columns; - - if(values.length < CHUNK_SIZE) { - // because if we get less that we could, then there is nothing more - break; - } - } - - return data; -} - -async function queryGrafana(url: string, params: any) { - let origin = new URL(url).origin; - let headers = { Authorization: `Bearer ${getApiKey(origin)}` }; - - try { - var res = await axios.get(url, { params, headers }); - } catch (e) { - if(e.response.status === 401) { - throw new Error('Unauthorized. Check the $HASTIC_API_KEY.'); - } - throw new Error(e.message); - } - - if (res.data.results === undefined) { - throw new Error('results field is undefined in response.'); - } - - // TODO: support more than 1 metric (each res.data.results item is a metric) - let results = res.data.results[0]; - if (results.series === undefined) { - return []; - } - - return results.series[0]; -} diff --git a/src/target.ts b/src/target.ts index 5eb8852..46d4c58 100644 --- a/src/target.ts +++ b/src/target.ts @@ -1,10 +1,11 @@ -import { queryByMetric } from './grafana-datasource-kit/grafana_service'; -import { GrafanaDatasource, GrafanaMetric } from './grafana-datasource-kit/grafana_metric_model'; +import { queryByMetric, Datasource, Metric } from 'grafana-datasource-kit'; +import { getApiKey } from './config'; import * as csv from 'fast-csv'; import * as path from 'path'; import * as fs from 'fs'; import * as moment from 'moment'; +import { URL } from 'url'; const MS_IN_DAY = 24 * 60 * 60 * 1000; @@ -14,17 +15,17 @@ export class Target { private days: number; private day: number; private csvStream: any; - private metric: GrafanaMetric; + private metric: Metric; constructor( private panelUrl: string, private user: string, - datasource: GrafanaDatasource, + datasource: Datasource, targets: Array, private from: number, private to: number ) { - this.metric = new GrafanaMetric(datasource, targets); + this.metric = new Metric(datasource, targets); } public updateStatus(status) { @@ -64,7 +65,8 @@ export class Target { console.log(`${this.day} day: ${from}ms -> ${to}ms`); - let metrics = await queryByMetric(this.metric, this.panelUrl, from, to); + let apiKey = getApiKey(new URL(this.panelUrl).origin); + let metrics = await queryByMetric(this.metric, this.panelUrl, from, to, apiKey); if(metrics.values.length > 0) { if(metrics !== undefined) {