better names #5

Merged
rozetko merged 2 commits from better-names into master 2 years ago
  1. 6
      spec/elasticsearch.jest.ts
  2. 7
      spec/graphite.jest.ts
  3. 8
      spec/postgres.jest.ts
  4. 4
      spec/prometheus.jest.ts
  5. 15
      spec/targets.jest.ts
  6. 2
      spec/utils.jest.ts
  7. 31
      src/connectors/connector_factory.ts
  8. 22
      src/connectors/elasticsearch.ts
  9. 12
      src/connectors/graphite.ts
  10. 14
      src/connectors/index.ts
  11. 13
      src/connectors/influxdb.ts
  12. 2
      src/connectors/mysql.ts
  13. 2
      src/connectors/postgres.ts
  14. 14
      src/connectors/prometheus.ts
  15. 14
      src/connectors/sql.ts
  16. 0
      src/connectors/utils.ts
  17. 29
      src/index.ts
  18. 75
      src/metrics/metrics_factory.ts
  19. 45
      src/models/query_config.ts
  20. 4
      src/services/direct_service.ts
  21. 4
      src/services/grafana_service.ts
  22. 9
      src/tsdb-kit/index.ts
  23. 2
      src/types.ts

6
spec/elasticsearch.jest.ts

@ -1,5 +1,5 @@
import { ElasticsearchMetric } from '../src/metrics/elasticsearch_metric'; import { ElasticsearchMetric } from '../src/connectors/elasticsearch';
import { Datasource, DatasourceType } from '../src/metrics/metric'; import { Datasource, DatasourceType } from '../src/connectors';
import 'jest'; import 'jest';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -265,6 +265,6 @@ describe('simple query', function(){
] ]
}; };
expect(elasticMetric.getResults(result)).toEqual(expectedResult); expect(elasticMetric.parseResponse(result)).toEqual(expectedResult);
}); });
}); });

7
spec/graphite.jest.ts

@ -1,5 +1,4 @@
import { Datasource, Metric } from '../src/index'; import { Datasource, QueryConfig, DatasourceType } from '../src/index';
import { DatasourceType } from '../src/metrics/metric';
import 'jest'; import 'jest';
@ -17,10 +16,10 @@ describe('correct Graphite query', function() {
}; };
let target = `target=template(hosts.$hostname.cpu, hostname="worker1")`; let target = `target=template(hosts.$hostname.cpu, hostname="worker1")`;
let query = new Metric(datasource, [target]); let queryConfig = new QueryConfig(datasource, [target]);
it("test simple query with time clause", function () { it("test simple query with time clause", function () {
expect(query.metricQuery.getQuery(1534809600000, 1537488000000, 500, 0).url).toBe( expect(queryConfig.datasourceConnector.getQuery(1534809600000, 1537488000000, 500, 0).url).toBe(
`${datasource.url}?target=${target}&from=1534809600&until=1537488000&maxDataPoints=500` `${datasource.url}?target=${target}&from=1534809600&until=1537488000&maxDataPoints=500`
) )
}); });

8
spec/postgres.jest.ts

@ -1,5 +1,5 @@
import { PostgresMetric } from '../src/metrics/postgres_metric'; import { PostgresMetric } from '../src/connectors/postgres';
import { DatasourceType, MetricQuery } from '../src/metrics/metric'; import { DatasourceType, DatasourceQuery } from '../src/connectors';
import 'jest'; import 'jest';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -12,7 +12,7 @@ describe('Test query creation', function() {
let from = 1542983750857; let from = 1542983750857;
let to = 1542984313292; let to = 1542984313292;
let postgres = getMetricForSqlQuery(); let postgres = getMetricForSqlQuery();
let mQuery: MetricQuery = postgres.getQuery(from, to, limit, offset); let mQuery: DatasourceQuery = postgres.getQuery(from, to, limit, offset);
it('test that payload placed to data field', function() { it('test that payload placed to data field', function() {
expect('data' in mQuery.schema).toBeTruthy(); expect('data' in mQuery.schema).toBeTruthy();
@ -58,7 +58,7 @@ describe('Test result parsing', function() {
} }
} }
let result = postgres.getResults(response); let result = postgres.parseResponse(response);
it('check results columns order', function() { it('check results columns order', function() {
let timestampColumnNumber = result.columns.indexOf('timestamp'); let timestampColumnNumber = result.columns.indexOf('timestamp');

4
spec/prometheus.jest.ts

@ -1,6 +1,6 @@
import 'jest'; import 'jest';
import { PrometheusMetric } from '../src/metrics/prometheus_metric'; import { PrometheusMetric } from '../src/connectors/prometheus';
import { DatasourceType } from '../src/metrics/metric'; import { DatasourceType } from '../src/connectors';
describe('Test Prometheus time range processing', function() { describe('Test Prometheus time range processing', function() {

15
spec/targets.jest.ts

@ -1,5 +1,4 @@
import { Datasource, Metric } from '../src/index'; import { Datasource, DatasourceType, QueryConfig } from '../src/index';
import { DatasourceType } from '../src/metrics/metric';
import 'jest'; import 'jest';
@ -18,24 +17,24 @@ describe('Correct InfluxDB query', function() {
let target = 'mean("value")'; let target = 'mean("value")';
it("test query with two time expressions", function() { it("test query with two time expressions", function() {
let query = new Metric(datasource, [target]); const queryConfig = new QueryConfig(datasource, [target]);
expect(query.metricQuery.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe( expect(queryConfig.datasourceConnector.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe(
`SELECT mean("value") FROM "db" WHERE time >= 1534809600ms AND time <= 1537488000ms LIMIT 666 OFFSET 10` `SELECT mean("value") FROM "db" WHERE time >= 1534809600ms AND time <= 1537488000ms LIMIT 666 OFFSET 10`
) )
}); });
it('test query with one time expression', function() { it('test query with one time expression', function() {
datasource.params.q = `SELECT mean("value") FROM "cpu_value" WHERE time >= now() - 6h GROUP BY time(30s) fill(null)`; datasource.params.q = `SELECT mean("value") FROM "cpu_value" WHERE time >= now() - 6h GROUP BY time(30s) fill(null)`;
let query = new Metric(datasource, [target]); const queryConfig = new QueryConfig(datasource, [target]);
expect(query.metricQuery.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe( expect(queryConfig.datasourceConnector.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe(
`SELECT mean("value") FROM "cpu_value" WHERE time >= 1534809600ms AND time <= 1537488000ms GROUP BY time(30s) fill(null) LIMIT 666 OFFSET 10` `SELECT mean("value") FROM "cpu_value" WHERE time >= 1534809600ms AND time <= 1537488000ms GROUP BY time(30s) fill(null) LIMIT 666 OFFSET 10`
) )
}); });
it('test query with time expression', function() { it('test query with time expression', function() {
datasource.params.q = `SELECT mean("value") FROM "cpu_value" WHERE time>= now() - 6h AND time<xxx GROUP BY time(30s) fill(null)`; datasource.params.q = `SELECT mean("value") FROM "cpu_value" WHERE time>= now() - 6h AND time<xxx GROUP BY time(30s) fill(null)`;
let query = new Metric(datasource, [target]); const queryConfig = new QueryConfig(datasource, [target]);
expect(query.metricQuery.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe( expect(queryConfig.datasourceConnector.getQuery(1534809600,1537488000,666,10).schema.params.q).toBe(
`SELECT mean("value") FROM "cpu_value" WHERE time >= 1534809600ms AND time <= 1537488000ms GROUP BY time(30s) fill(null) LIMIT 666 OFFSET 10` `SELECT mean("value") FROM "cpu_value" WHERE time >= 1534809600ms AND time <= 1537488000ms GROUP BY time(30s) fill(null) LIMIT 666 OFFSET 10`
) )
}); });

2
spec/utils.jest.ts

@ -1,4 +1,4 @@
import { processSQLLimitOffset } from '../src/metrics/utils'; import { processSQLLimitOffset } from '../src/connectors/utils';
import 'jest'; import 'jest';

31
src/connectors/connector_factory.ts

@ -0,0 +1,31 @@
import { InfluxdbMetric } from './influxdb';
import { GraphiteMetric } from './graphite';
import { DatasourceConnector, DatasourceType } from '.';
import { PrometheusMetric } from './prometheus';
import { PostgresMetric } from './postgres';
import { ElasticsearchMetric } from './elasticsearch';
import { MysqlMetric } from './mysql';
import { QueryConfig } from '../models/query_config';
export function connectorFactory(
queryConfig: QueryConfig,
): DatasourceConnector {
const classMap = {
[DatasourceType.INFLUXDB]: InfluxdbMetric,
[DatasourceType.GRAPHITE]: GraphiteMetric,
[DatasourceType.PROMETHEUS]: PrometheusMetric,
[DatasourceType.POSTGRES]: PostgresMetric,
[DatasourceType.ELASTICSEARCH]: ElasticsearchMetric,
[DatasourceType.MYSQL]: MysqlMetric,
};
const datasource = queryConfig.datasource;
const targets = queryConfig.targets;
if(classMap[datasource.type] === undefined) {
console.error(`Datasources of type ${datasource.type} are not supported currently`);
throw new Error(`Datasources of type ${datasource.type} are not supported currently`);
} else {
return new classMap[datasource.type](datasource, targets);
}
}

22
src/metrics/elasticsearch_metric.ts → src/connectors/elasticsearch.ts

@ -1,4 +1,4 @@
import { AbstractMetric, Datasource, MetricId, MetricQuery, MetricResults } from './metric'; import { DatasourceConnector, Datasource, DatasourceQuery, DataTable } from '.';
import { TsdbKitError } from '../types'; import { TsdbKitError } from '../types';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -6,7 +6,7 @@ import * as _ from 'lodash';
export type RangeFilter = { range: { [key: string]: { gte: String, lte: String } } }; export type RangeFilter = { range: { [key: string]: { gte: String, lte: String } } };
export type QueryStringFilter = { query_string: { analyze_wildcard: Boolean, query: String } }; export type QueryStringFilter = { query_string: { analyze_wildcard: Boolean, query: String } };
export type QueryConfig = { export type ElasticsearchQuery = {
size: number, size: number,
query: { query: {
bool: { bool: {
@ -28,23 +28,23 @@ export type Aggregation = {
const DATE_HISTOGRAM_FIELD = 'date_histogram'; const DATE_HISTOGRAM_FIELD = 'date_histogram';
export class ElasticsearchMetric extends AbstractMetric { export class ElasticsearchMetric extends DatasourceConnector {
constructor(datasource: Datasource, targets: any[], id?: MetricId) { constructor(datasource: Datasource, targets: any[]) {
super(datasource, targets, id); super(datasource, targets);
} }
getQuery(from: number, to: number, limit: number, offset: number): MetricQuery { getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery {
let data = this.datasource.data.split('\n').map(d => d === '' ? d: JSON.parse(d)); let data = this.datasource.data.split('\n').map(d => d === '' ? d: JSON.parse(d));
if(data.length === 0) { if(data.length === 0) {
throw new TsdbKitError('Datasource data is empty'); throw new TsdbKitError('Datasource data is empty');
} }
const queryConfig: QueryConfig = data[1]; const query: ElasticsearchQuery = data[1];
queryConfig.size = 0; query.size = 0;
let timeField: string | null = null; let timeField: string | null = null;
let aggs = _.filter(queryConfig.aggs, f => _.has(f, DATE_HISTOGRAM_FIELD)); let aggs = _.filter(query.aggs, f => _.has(f, DATE_HISTOGRAM_FIELD));
_.each(aggs, (agg: Aggregation) => { _.each(aggs, (agg: Aggregation) => {
agg[DATE_HISTOGRAM_FIELD].extended_bounds = { agg[DATE_HISTOGRAM_FIELD].extended_bounds = {
min: from.toString(), min: from.toString(),
@ -63,7 +63,7 @@ export class ElasticsearchMetric extends AbstractMetric {
throw new Error('datasource time field not found'); throw new Error('datasource time field not found');
} }
let filters = queryConfig.query.bool.filter.filter(f => _.has(f, 'range')) as RangeFilter[]; let filters = query.query.bool.filter.filter(f => _.has(f, 'range')) as RangeFilter[];
if(filters.length === 0) { if(filters.length === 0) {
throw new TsdbKitError('Empty filters'); throw new TsdbKitError('Empty filters');
} }
@ -86,7 +86,7 @@ export class ElasticsearchMetric extends AbstractMetric {
} }
} }
getResults(res): MetricResults { parseResponse(res): DataTable {
let columns = ['timestamp', 'target']; let columns = ['timestamp', 'target'];
let values = []; let values = [];

12
src/metrics/graphite_metric.ts → src/connectors/graphite.ts

@ -1,14 +1,14 @@
import { AbstractMetric, Datasource, MetricId, MetricQuery, MetricResults } from './metric'; import { DatasourceConnector, Datasource, DatasourceQuery, DataTable } from '.';
import * as _ from 'lodash'; import * as _ from 'lodash';
export class GraphiteMetric extends AbstractMetric { export class GraphiteMetric extends DatasourceConnector {
constructor(datasource: Datasource, targets: any[], id?: MetricId) { constructor(datasource: Datasource, targets: any[]) {
super(datasource, targets, id); super(datasource, targets);
} }
getQuery(from: number, to: number, limit: number, offset: number): MetricQuery { getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery {
let fromDate = Math.floor(from / 1000); let fromDate = Math.floor(from / 1000);
let toDate = Math.floor(to / 1000); let toDate = Math.floor(to / 1000);
@ -42,7 +42,7 @@ export class GraphiteMetric extends AbstractMetric {
} }
} }
getResults(res): MetricResults { parseResponse(res): DataTable {
if(res.data === undefined || res.data.length < 1) { if(res.data === undefined || res.data.length < 1) {
console.log('datasource return empty response, no data'); console.log('datasource return empty response, no data');

14
src/metrics/metric.ts → src/connectors/index.ts

@ -24,31 +24,29 @@ export declare type Datasource = {
datasourceId?: string; datasourceId?: string;
}; };
export type MetricQuery = { export type DatasourceQuery = {
url: string; url: string;
method: string; method: string;
schema: any; schema: any;
headers?: any; headers?: any;
} }
export type MetricResults = { export type DataTable = {
values: (number | null)[][]; values: (number | null)[][];
columns: string[]; columns: string[];
} }
export type MetricId = string; export abstract class DatasourceConnector {
export abstract class AbstractMetric {
constructor( constructor(
public datasource: Datasource, public datasource: Datasource,
// TODO: Target type
public targets: any[], public targets: any[],
public id?: MetricId
) {}; ) {};
abstract getQuery(from: number, to: number, limit: number, offset: number): MetricQuery;
/* /*
from / to - timestamp in ms from / to - timestamp in ms
limit - max number of items in result limit - max number of items in result
offset - number of items to skip from timerange start offset - number of items to skip from timerange start
*/ */
abstract getResults(res): MetricResults; abstract getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery;
abstract parseResponse(res): DataTable;
} }

13
src/metrics/influxdb_metric.ts → src/connectors/influxdb.ts

@ -1,16 +1,15 @@
import { AbstractMetric, Datasource, MetricId, MetricQuery, MetricResults } from "./metric"; import { DatasourceConnector, Datasource, DatasourceQuery, DataTable } from '.';
import { processSQLLimitOffset } from './utils'; import { processSQLLimitOffset } from './utils';
const INFLUX_QUERY_TIME_REGEX = /time ?[><=]+ ?[^A-Z]+(AND ?time ?[><=]+ ?[^A-Z]+)?/; const INFLUX_QUERY_TIME_REGEX = /time ?[><=]+ ?[^A-Z]+(AND ?time ?[><=]+ ?[^A-Z]+)?/;
export class InfluxdbMetric extends DatasourceConnector {
export class InfluxdbMetric extends AbstractMetric {
private _queryParts: string[]; private _queryParts: string[];
constructor(datasource: Datasource, targets: any[], id?: MetricId) { constructor(datasource: Datasource, targets: any[]) {
super(datasource, targets, id); super(datasource, targets);
var queryStr = datasource.params.q; var queryStr = datasource.params.q;
this._queryParts = queryStr.split(INFLUX_QUERY_TIME_REGEX); this._queryParts = queryStr.split(INFLUX_QUERY_TIME_REGEX);
@ -24,7 +23,7 @@ export class InfluxdbMetric extends AbstractMetric {
} }
} }
getQuery(from: number, to: number, limit: number, offset: number): MetricQuery { getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery {
let timeClause = `time >= ${from}ms AND time <= ${to}ms`; let timeClause = `time >= ${from}ms AND time <= ${to}ms`;
let q = `${this._queryParts[0]} ${timeClause} ${this._queryParts[2]}`; let q = `${this._queryParts[0]} ${timeClause} ${this._queryParts[2]}`;
q = processSQLLimitOffset(q, limit, offset); q = processSQLLimitOffset(q, limit, offset);
@ -41,7 +40,7 @@ export class InfluxdbMetric extends AbstractMetric {
} }
} }
getResults(res): MetricResults { parseResponse(res): DataTable {
let emptyResult = { let emptyResult = {
columns: ['timestamp', 'target'], columns: ['timestamp', 'target'],
values: [] values: []

2
src/metrics/mysql_metric.ts → src/connectors/mysql.ts

@ -1,4 +1,4 @@
import { SqlMetric } from './sql_metric'; import { SqlMetric } from './sql';
export class MysqlMetric extends SqlMetric { export class MysqlMetric extends SqlMetric {

2
src/metrics/postgres_metric.ts → src/connectors/postgres.ts

@ -1,4 +1,4 @@
import { SqlMetric } from './sql_metric'; import { SqlMetric } from './sql';
export class PostgresMetric extends SqlMetric { export class PostgresMetric extends SqlMetric {

14
src/metrics/prometheus_metric.ts → src/connectors/prometheus.ts

@ -1,15 +1,15 @@
import { AbstractMetric, Datasource, MetricId, MetricQuery, MetricResults } from './metric'; import { DatasourceConnector, Datasource, DatasourceQuery, DataTable } from '.';
const QUERY_TIME_REGEX = /\&start=[^\&]*\&end=[^\&]*\&/; const QUERY_TIME_REGEX = /\&start=[^\&]*\&end=[^\&]*\&/;
export class PrometheusMetric extends AbstractMetric { export class PrometheusMetric extends DatasourceConnector {
constructor(datasource: Datasource, targets: any[], id?: MetricId) { constructor(datasource: Datasource, targets: any[]) {
super(datasource, targets, id); super(datasource, targets);
} }
getQuery(from: number, to: number, limit: number, offset: number): MetricQuery { getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery {
let url = this.datasource.url; let url = this.datasource.url;
from = Math.floor(from / 1000); //prometheus uses seconds for timestamp from = Math.floor(from / 1000); //prometheus uses seconds for timestamp
to = Math.floor(to / 1000); to = Math.floor(to / 1000);
@ -25,7 +25,7 @@ export class PrometheusMetric extends AbstractMetric {
} }
} }
getResults(res): MetricResults { parseResponse(res): DataTable {
if(res.data === undefined || res.data.data.result.length < 1) { if(res.data === undefined || res.data.data.result.length < 1) {
console.log('datasource return empty response, no data'); console.log('datasource return empty response, no data');
@ -36,7 +36,7 @@ export class PrometheusMetric extends AbstractMetric {
} }
let result = res.data.data.result; let result = res.data.data.result;
let result_matrix: MetricResults = { let result_matrix: DataTable = {
columns: ['timestamp'], columns: ['timestamp'],
values: [] values: []
}; };

14
src/metrics/sql_metric.ts → src/connectors/sql.ts

@ -1,15 +1,15 @@
import { AbstractMetric, Datasource, MetricId, MetricQuery, MetricResults } from './metric'; import { DatasourceConnector, Datasource, DatasourceQuery, DataTable } from '.';
import { processSQLLimitOffset } from './utils'; import { processSQLLimitOffset } from './utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
// for 26.09.2020 it works for all SQL datasources // as of 26.09.2020, it works for all SQL datasources
export class SqlMetric extends AbstractMetric { export class SqlMetric extends DatasourceConnector {
private _targetName: string; //save first target name, while multi metric not implemented private _targetName: string; //save first target name, while multi metric not implemented
private url: string = 'api/tsdb/query'; private url: string = 'api/tsdb/query';
constructor(datasource: Datasource, targets: any[], id?: MetricId) { constructor(datasource: Datasource, targets: any[]) {
super(datasource, targets, id); super(datasource, targets);
if(targets.length === 0) { if(targets.length === 0) {
throw Error('got empty targets list'); throw Error('got empty targets list');
@ -17,7 +17,7 @@ export class SqlMetric extends AbstractMetric {
this._targetName = targets[0].refId; this._targetName = targets[0].refId;
} }
getQuery(from: number, to: number, limit: number, offset: number): MetricQuery { getQuery(from: number, to: number, limit: number, offset: number): DatasourceQuery {
let queries = this.targets; let queries = this.targets;
_.forEach(queries, q => { _.forEach(queries, q => {
@ -40,7 +40,7 @@ export class SqlMetric extends AbstractMetric {
}; };
} }
getResults(res): MetricResults { parseResponse(res): DataTable {
let emptyResult = { let emptyResult = {
columns: ['timestamp', 'target'], columns: ['timestamp', 'target'],
values: [] values: []

0
src/metrics/utils.ts → src/connectors/utils.ts

29
src/index.ts

@ -1,33 +1,32 @@
import { MetricResults, QueryType } from './metrics/metric'; import { DataTable, QueryType } from './connectors';
import { Metric } from './metrics/metrics_factory'; import { QueryConfig } from './models/query_config';
import { queryDirect } from './services/direct_service'; import { queryDirect } from './services/direct_service';
import { queryGrafana } from './services/grafana_service'; import { queryGrafana } from './services/grafana_service';
import { BadRange } from './types'; import { BadRange } from './types';
export { Metric } from './metrics/metrics_factory'; export { QueryConfig } from './models/query_config';
export { Datasource } from './metrics/metric' export { Datasource, DatasourceType, DataTable } from './connectors'
export { DatasourceUnavailable } from './types'; export { DatasourceUnavailable } from './types';
export { GrafanaUnavailable } from './services/grafana_service'; export { GrafanaUnavailable } from './services/grafana_service';
const CHUNK_SIZE = 50000; const CHUNK_SIZE = 50000;
/** /**
* @param metric to query to Grafana * @param queryConfig
* @returns { values: [time, value][], columns: string[] } * @returns { values: [time, value][], columns: string[] }
*/ */
export async function queryByMetric( export async function queryByConfig(
// TODO: check how did we wanna use `url` field // TODO: check how did we wanna use `url` field
metric: Metric, url: string, from: number, to: number, queryType: QueryType, queryConfig: QueryConfig, url: string, from: number, to: number, queryType: QueryType,
// TODO: we need an abstract DatasourceConfig class which will differ in direct and grafana queries // TODO: we need an abstract DatasourceConfig class which will differ in direct and grafana queries
apiKey?: string apiKey?: string
): Promise<MetricResults> { ): Promise<DataTable> {
if(from > to) { if(from > to) {
throw new BadRange( throw new BadRange(
`Data-kit got wrong range: from ${from} > to ${to}`, `Data-kit got wrong range: from ${from} > to ${to}`,
metric.datasource.type, queryConfig.datasource.type,
url url
); );
} }
@ -36,26 +35,26 @@ export async function queryByMetric(
console.warn(`Data-kit got from === to`); console.warn(`Data-kit got from === to`);
} }
let data: MetricResults = { let data: DataTable = {
values: [], values: [],
columns: [] columns: []
}; };
while(true) { while(true) {
let query = metric.metricQuery.getQuery(from, to, CHUNK_SIZE, data.values.length); let query = queryConfig.datasourceConnector.getQuery(from, to, CHUNK_SIZE, data.values.length);
let res: any; let res: any;
// TODO: use polymorphic `query` method instead // TODO: use polymorphic `query` method instead
switch(queryType) { switch(queryType) {
case QueryType.GRAFANA: case QueryType.GRAFANA:
res = await queryGrafana(query, apiKey as string, metric.datasource); res = await queryGrafana(query, apiKey as string, queryConfig.datasource);
break; break;
case QueryType.DIRECT: case QueryType.DIRECT:
res = await queryDirect(query, metric.datasource); res = await queryDirect(query, queryConfig.datasource);
break; break;
default: default:
throw new Error(`Unknown query type: ${queryType}`); throw new Error(`Unknown query type: ${queryType}`);
} }
let chunk = metric.metricQuery.getResults(res); let chunk = queryConfig.datasourceConnector.parseResponse(res);
let values = chunk.values; let values = chunk.values;
data.values = data.values.concat(values); data.values = data.values.concat(values);
data.columns = chunk.columns; data.columns = chunk.columns;

75
src/metrics/metrics_factory.ts

@ -1,75 +0,0 @@
import { InfluxdbMetric } from './influxdb_metric';
import { GraphiteMetric } from './graphite_metric';
import { AbstractMetric, Datasource, DatasourceType, MetricId } from './metric';
import { PrometheusMetric } from './prometheus_metric';
import { PostgresMetric } from './postgres_metric';
import { ElasticsearchMetric } from './elasticsearch_metric';
import { MysqlMetric } from './mysql_metric';
export function metricFactory(
datasource: Datasource,
targets: any[],
id?: MetricId
): AbstractMetric {
const classMap = {
[DatasourceType.INFLUXDB]: InfluxdbMetric,
[DatasourceType.GRAPHITE]: GraphiteMetric,
[DatasourceType.PROMETHEUS]: PrometheusMetric,
[DatasourceType.POSTGRES]: PostgresMetric,
[DatasourceType.ELASTICSEARCH]: ElasticsearchMetric,
[DatasourceType.MYSQL]: MysqlMetric,
};
if(classMap[datasource.type] === undefined) {
console.error(`Datasources of type ${datasource.type} are not supported currently`);
throw new Error(`Datasources of type ${datasource.type} are not supported currently`);
} else {
return new classMap[datasource.type](datasource, targets, id);
}
}
export class Metric {
datasource: Datasource;
targets: any[];
id?: MetricId;
private _metricQuery?: AbstractMetric;
constructor(datasource: Datasource, targets: any[], id?: MetricId) {
if(datasource === undefined) {
throw new Error('datasource is undefined');
}
if(targets === undefined) {
throw new Error('targets is undefined');
}
this.datasource = datasource;
this.targets = targets;
this.id = id;
}
public get metricQuery() {
if(this._metricQuery === undefined) {
this._metricQuery = metricFactory(this.datasource, this.targets, this.id);
}
return this._metricQuery;
}
public toObject() {
return {
datasource: this.datasource,
targets: this.targets,
_id: this.id
};
}
static fromObject(obj: any): Metric {
if(obj === undefined) {
throw new Error('obj is undefined');
}
return new Metric(
obj.datasource,
obj.targets,
obj._id
);
}
}

45
src/models/query_config.ts

@ -0,0 +1,45 @@
import { Datasource, DatasourceConnector } from '../connectors';
import { connectorFactory } from '../connectors/connector_factory';
export class QueryConfig {
datasource: Datasource;
// TODO: Target type (depends on datasource type)
targets: any[];
private _datasourceConnector?: DatasourceConnector;
constructor(datasource: Datasource, targets: any[]) {
if(datasource === undefined) {
throw new Error('datasource is undefined');
}
if(targets === undefined) {
throw new Error('targets is undefined');
}
this.datasource = datasource;
this.targets = targets;
}
get datasourceConnector(): DatasourceConnector {
if(this._datasourceConnector === undefined) {
this._datasourceConnector = connectorFactory(this);
}
return this._datasourceConnector;
}
public toObject() {
return {
datasource: this.datasource,
targets: this.targets,
};
}
static fromObject(obj: any): QueryConfig {
if(obj === undefined) {
throw new Error('obj is undefined');
}
return new QueryConfig(
obj.datasource,
obj.targets,
);
}
}

4
src/services/direct_service.ts

@ -1,5 +1,5 @@
import { DatasourceUnavailable } from '../types'; import { DatasourceUnavailable } from '../types';
import { Datasource, MetricQuery } from '../metrics/metric'; import { Datasource, DatasourceQuery } from '../connectors';
import axios from 'axios'; import axios from 'axios';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -7,7 +7,7 @@ import * as _ from 'lodash';
// TODO: support direct queries auth // TODO: support direct queries auth
// TODO: move to class and inherit from QueryService abstract class // TODO: move to class and inherit from QueryService abstract class
export async function queryDirect(query: MetricQuery, datasource: Datasource) { export async function queryDirect(query: DatasourceQuery, datasource: Datasource) {
let axiosQuery = { let axiosQuery = {
url: query.url, url: query.url,
method: query.method, method: query.method,

4
src/services/grafana_service.ts

@ -1,4 +1,4 @@
import { Datasource, MetricQuery } from '../metrics/metric'; import { Datasource, DatasourceQuery } from '../connectors';
import { TsdbKitError, DatasourceUnavailable } from '../types'; import { TsdbKitError, DatasourceUnavailable } from '../types';
import axios from 'axios'; import axios from 'axios';
@ -8,7 +8,7 @@ import * as _ from 'lodash';
export class GrafanaUnavailable extends TsdbKitError { }; export class GrafanaUnavailable extends TsdbKitError { };
// TODO: move to class and inherit from QueryService abstract class // TODO: move to class and inherit from QueryService abstract class
export async function queryGrafana(query: MetricQuery, apiKey: string, datasource: Datasource) { export async function queryGrafana(query: DatasourceQuery, apiKey: string, datasource: Datasource) {
let headers = { Authorization: `Bearer ${apiKey}` }; let headers = { Authorization: `Bearer ${apiKey}` };
const grafanaUrl = getGrafanaUrl(query.url); const grafanaUrl = getGrafanaUrl(query.url);

9
src/tsdb-kit/index.ts

@ -1,7 +1,6 @@
import { queryByMetric, Metric } from '..'; import { queryByConfig, QueryConfig } from '..';
import { PrometheusMetric } from '../metrics/prometheus_metric'; import { DatasourceType, QueryType } from '../connectors';
import { DatasourceType, QueryType } from '../metrics/metric';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -17,8 +16,8 @@ const datasource = {
url: `${PROMETHEUS_URL}/api/v1/query_range?query=${QUERY}&start=1543411320&end=1543432950&step=30` url: `${PROMETHEUS_URL}/api/v1/query_range?query=${QUERY}&start=1543411320&end=1543432950&step=30`
} }
const targets = []; const targets = [];
const metric = new Metric(datasource, targets); const queryConfig = new QueryConfig(datasource, targets);
queryByMetric(metric as any, PROMETHEUS_URL, FROM, TO, QueryType.DIRECT) queryByConfig(queryConfig, PROMETHEUS_URL, FROM, TO, QueryType.DIRECT)
.then(res => { .then(res => {
console.log(res); console.log(res);
}) })

2
src/types.ts

@ -1,4 +1,4 @@
import { DatasourceType } from './metrics/metric'; import { DatasourceType } from './connectors';
export class TsdbKitError extends Error { export class TsdbKitError extends Error {

Loading…
Cancel
Save