diff --git a/src/grafana_metric_model.ts b/src/grafana-datasource-kit/grafana_metric_model.ts similarity index 100% rename from src/grafana_metric_model.ts rename to src/grafana-datasource-kit/grafana_metric_model.ts diff --git a/src/grafana-datasource-kit/grafana_service.ts b/src/grafana-datasource-kit/grafana_service.ts new file mode 100644 index 0000000..a2108f9 --- /dev/null +++ b/src/grafana-datasource-kit/grafana_service.ts @@ -0,0 +1,63 @@ +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 +): Promise<[number, number][]> { + + let datasource = metric.datasource; + + let origin = new URL(panelUrl).origin; + let url = `${origin}/${datasource.url}`; + + let params = datasource.params + let data = []; + + let chunkParams = Object.assign({}, params); + while(true) { + chunkParams.q = metric.metricQuery.getQuery(from, to, CHUNK_SIZE, data.length); + var chunk = await queryGrafana(url, chunkParams); + data = data.concat(chunk); + if(chunk.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 headers = { Authorization: `Bearer ${getApiKey(url)}` }; + + 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].values as [number, number][]; +} diff --git a/src/grafana_api.ts b/src/grafana_api.ts deleted file mode 100644 index 76a8efd..0000000 --- a/src/grafana_api.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { getApiKey } from './config'; - -import axios from 'axios'; - - -export class GrafanaAPI { - private apiKey; - constructor(private grafanaUrl) { - getApiKey(grafanaUrl) - .then(key => { - this.apiKey = key; - console.log(this.apiKey); - }); - } - - private get _headers() { - return { - 'Authorization': `Bearer ${this.apiKey}`, - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }; - } - - private async _getDatasourceByName(name) { - return fetch(`${this.grafanaUrl}/api/datasources/name/${name}`, { - method: 'GET', - headers: this._headers - }) - .then(data => data.json()); - } - - public async queryDatasource(datasourceName, measurement, query) { - let datasource = await this._getDatasourceByName(datasourceName); - - return this._queryGrafana(`${this.grafanaUrl}/api/datasources/proxy/${datasource.id}/query`, { - q: encodeURIComponent(query), - db: datasource.database, - epoch: 'ms' - }); - } - - private async _queryGrafana(url: string, params: any) { - try { - var res = await axios.get(url, { params, headers: this._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].values as [number, number][]; - } - -} - diff --git a/src/target.ts b/src/target.ts index 50b2819..bdfb6ed 100644 --- a/src/target.ts +++ b/src/target.ts @@ -1,4 +1,5 @@ -import { GrafanaAPI } from './grafana_api'; +import { queryByMetric } from './grafana-datasource-kit/grafana_service'; +import { GrafanaDatasource } from './grafana-datasource-kit/grafana_metric_model'; import * as csv from 'fast-csv'; import * as path from 'path'; @@ -13,18 +14,26 @@ export class Target { private days: number; private day: number; private csvStream: any; - private grafana: GrafanaAPI; + private _datasource: GrafanaDatasource; constructor( - private grafanaUrl: string, + panelUrl: string, private user: string, - private datasource: string, + datasource: string, private measurement: string, private query: string, private from: number, private to: number - ) { - this.grafana = new GrafanaAPI(this.grafanaUrl); + ) { + this._datasource = { + url: panelUrl, + type: type, + params: { + db: string, + q: string, + epoch: string + } + } } public updateStatus(status) { @@ -67,7 +76,7 @@ export class Target { console.log(`${this.day} day: ${from}ms -> ${to}ms`); let currentQuery = this.query.replace('$timeFilter', `time >= ${from}ms AND time <= ${to}ms`).replace('$__interval', '1s'); - let metrics = await this.grafana.queryDatasource(this.datasource, this.measurement, currentQuery); + let metrics = await queryByMetric(this.datasource, currentQuery); console.log(metrics); if(metrics.length > 0) {