@ -1,23 +1,26 @@
import _ from 'lodash' ;
import { Observable , of , throwError } from 'rxjs' ;
import {
ArrayVector ,
CoreApp ,
DataQueryRequest ,
DataSourceInstanceSettings ,
DataSourcePluginMeta ,
dateMath ,
DateTime ,
dateTime ,
Field ,
MetricFindValue ,
MutableDataFrame ,
TimeRange ,
toUtc ,
} from '@grafana/data' ;
import _ from 'lodash' ;
import { BackendSrvRequest , FetchResponse } from '@grafana/runtime' ;
import { ElasticDatasource , enhanceDataFrame } from './datasource' ;
import { backendSrv } from 'app/core/services/backend_srv' ; // will use the version in __mocks__
import { TemplateSrv } from 'app/features/templating/template_srv' ;
import { ElasticsearchOptions , ElasticsearchQuery } from './types' ;
import { Filters } from './components/QueryEditor/BucketAggregationsEditor/aggregations' ;
import { createFetchResponse } from '../../../../test/helpers/createFetchResponse' ;
const ELASTICSEARCH_MOCK_URL = 'http://elasticsearch.local' ;
@ -42,12 +45,27 @@ const createTimeRange = (from: DateTime, to: DateTime): TimeRange => ({
} ,
} ) ;
describe ( 'ElasticDatasource' , function ( this : any ) {
const datasourceRequestMock = jest . spyOn ( backendSrv , 'datasourceRequest' ) ;
beforeEach ( ( ) = > {
interface Args {
data? : any ;
from ? : string ;
jsonData? : any ;
database? : string ;
mockImplementation ? : ( options : BackendSrvRequest ) = > Observable < FetchResponse > ;
}
function getTestContext ( {
data = { } ,
from = 'now-5m' ,
jsonData = { } ,
database = '[asd-]YYYY.MM.DD' ,
mockImplementation = undefined ,
} : Args = { } ) {
jest . clearAllMocks ( ) ;
} ) ;
const defaultMock = ( options : BackendSrvRequest ) = > of ( createFetchResponse ( data ) ) ;
const fetchMock = jest . spyOn ( backendSrv , 'fetch' ) ;
fetchMock . mockImplementation ( mockImplementation ? ? defaultMock ) ;
const templateSrv : any = {
replace : jest.fn ( text = > {
@ -60,72 +78,63 @@ describe('ElasticDatasource', function(this: any) {
getAdhocFilters : jest.fn ( ( ) = > [ ] ) ,
} ;
interface TestContext {
ds : ElasticDatasource ;
}
const ctx = { } as TestContext ;
function createTimeSrv ( from : string ) {
const srv : any = {
time : { from : from , to : 'now' } ,
const timeSrv : any = {
time : { from , to : 'now' } ,
} ;
s rv . timeRange = jest . fn ( ( ) = > {
timeSrv . timeRange = jest . fn ( ( ) = > {
return {
from : dateMath . parse ( s rv. time . from , false ) ,
to : dateMath.parse ( s rv. time . to , true ) ,
from : dateMath . parse ( timeSrv . time . from , false ) ,
to : dateMath.parse ( timeSrv . time . to , true ) ,
} ;
} ) ;
s rv . setTime = jest . fn ( time = > {
s rv . time = time ;
timeS rv. setTime = jest . fn ( time = > {
timeS rv. time = time ;
} ) ;
return srv ;
}
const instanceSettings : DataSourceInstanceSettings < ElasticsearchOptions > = {
id : 1 ,
meta : { } as DataSourcePluginMeta ,
name : 'test-elastic' ,
type : 'type' ,
uid : 'uid' ,
url : ELASTICSEARCH_MOCK_URL ,
database ,
jsonData ,
} ;
function createDatasource ( instanceSettings : DataSourceInstanceSettings < ElasticsearchOptions > ) {
instanceSettings . jsonData = instanceSettings . jsonData || ( { } as ElasticsearchOptions ) ;
ctx . ds = new ElasticDatasource ( instanceSettings , templateSrv as TemplateSrv ) ;
}
const ds = new ElasticDatasource ( instanceSettings , templateSrv ) ;
describe ( 'When testing datasource with index pattern' , ( ) = > {
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : '[asd-]YYYY.MM.DD' ,
jsonData : { interval : 'Daily' , esVersion : 2 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
} ) ;
return { timeSrv , ds , fetchMock } ;
}
describe ( 'ElasticDatasource' , function ( this : any ) {
describe ( 'When testing datasource with index pattern' , ( ) = > {
it ( 'should translate index pattern to current day' , ( ) = > {
let requestOptions : any ;
datasourceRequestMock . mockImplementation ( options = > {
requestOptions = options ;
return Promise . resolve ( { data : { } } ) ;
} ) ;
const { ds , fetchMock } = getTestContext ( { jsonData : { interval : 'Daily' , esVersion : 2 } } ) ;
ctx . ds . testDatasource ( ) ;
ds . testDatasource ( ) ;
const today = toUtc ( ) . format ( 'YYYY.MM.DD' ) ;
expect ( requestOptions . url ) . toBe ( ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ today } /_mapping ` ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( fetchMock . mock . calls [ 0 ] [ 0 ] . url ) . toBe ( ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ today } /_mapping ` ) ;
} ) ;
} ) ;
describe ( 'When issuing metric query with interval pattern' , ( ) = > {
let requestOptions : any , parts : any , header : any , query : any , result : any ;
beforeEach ( async ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : '[asd-]YYYY.MM.DD' ,
jsonData : { interval : 'Daily' , esVersion : 2 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
requestOptions = options ;
return Promise . resolve ( {
data : {
async function runScenario() {
const range = { from : toUtc ( [ 2015 , 4 , 30 , 10 ] ) , to : toUtc ( [ 2015 , 5 , 1 , 10 ] ) } ;
const targets = [
{
alias : '$varAlias' ,
bucketAggs : [ { type : 'date_histogram' , field : '@timestamp' , id : '1' } ] ,
metrics : [ { type : 'count' , id : '1' } ] ,
query : 'escape\\:test' ,
} ,
] ;
const query : any = { range , targets } ;
const data = {
responses : [
{
aggregations : {
@ -140,64 +149,68 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
] ,
} ,
} ) ;
} ) ;
} ;
const { ds , fetchMock } = getTestContext ( { jsonData : { interval : 'Daily' , esVersion : 2 } , data } ) ;
query = {
range : {
from : toUtc ( [ 2015 , 4 , 30 , 10 ] ) ,
to : toUtc ( [ 2015 , 5 , 1 , 10 ] ) ,
} ,
targets : [
let result : any = { } ;
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toEqual ( {
data : [
{
alias : '$varAlias' ,
bucketAggs : [ { type : 'date_histogram' , field : '@timestamp' , id : '1' } ] ,
metrics : [ { type : 'count' , id : '1' } ] ,
query : 'escape\\:test' ,
datapoints : [ [ 10 , 1000 ] ] ,
metric : 'count' ,
props : { } ,
refId : undefined ,
target : 'resolvedVariable' ,
} ,
] ,
} ;
} ) ;
result = received [ 0 ] ;
} ) ;
result = await ctx . ds . query ( query ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
const requestOptions = fetchMock . mock . calls [ 0 ] [ 0 ] ;
const parts = requestOptions . data . split ( '\n' ) ;
const header = JSON . parse ( parts [ 0 ] ) ;
const body = JSON . parse ( parts [ 1 ] ) ;
parts = requestOptions . data . split ( '\n' ) ;
header = JSON . parse ( parts [ 0 ] ) ;
} ) ;
return { result , body , header , query } ;
}
it ( 'should translate index pattern to current day' , ( ) = > {
it ( 'should translate index pattern to current day' , async ( ) = > {
const { header } = await runScenario ( ) ;
expect ( header . index ) . toEqual ( [ 'asd-2015.05.30' , 'asd-2015.05.31' , 'asd-2015.06.01' ] ) ;
} ) ;
it ( 'should not resolve the variable in the original alias field in the query' , ( ) = > {
it ( 'should not resolve the variable in the original alias field in the query' , async ( ) = > {
const { query } = await runScenario ( ) ;
expect ( query . targets [ 0 ] . alias ) . toEqual ( '$varAlias' ) ;
} ) ;
it ( 'should resolve the alias variable for the alias/target in the result' , ( ) = > {
it ( 'should resolve the alias variable for the alias/target in the result' , async ( ) = > {
const { result } = await runScenario ( ) ;
expect ( result . data [ 0 ] . target ) . toEqual ( 'resolvedVariable' ) ;
} ) ;
it ( 'should json escape lucene query' , ( ) = > {
const body = JSON . parse ( parts [ 1 ] ) ;
it ( 'should json escape lucene query' , async ( ) = > {
const { body } = await runScenario ( ) ;
expect ( body . query . bool . filter [ 1 ] . query_string . query ) . toBe ( 'escape\\:test' ) ;
} ) ;
} ) ;
describe ( 'When issuing logs query with interval pattern' , ( ) = > {
async function setupDataSource ( jsonData? : Partial < ElasticsearchOptions > ) {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'mock-index' ,
jsonData : {
jsonData = {
interval : 'Daily' ,
esVersion : 2 ,
timeField : '@timestamp' ,
. . . ( jsonData || { } ) ,
} as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
return Promise . resolve ( logsResponse ) ;
} ;
const { ds } = getTestContext ( {
jsonData ,
data : logsResponse.data ,
database : 'mock-index' ,
} ) ;
const query : DataQueryRequest < ElasticsearchQuery > = {
@ -206,7 +219,13 @@ describe('ElasticDatasource', function(this: any) {
{
alias : '$varAlias' ,
refId : 'A' ,
bucketAggs : [ { type : 'date_histogram' , settings : { interval : 'auto' } , id : '2' } ] ,
bucketAggs : [
{
type : 'date_histogram' ,
settings : { interval : 'auto' } ,
id : '2' ,
} ,
] ,
metrics : [ { type : 'count' , id : '1' } ] ,
query : 'escape\\:test' ,
isLogsQuery : true ,
@ -215,8 +234,14 @@ describe('ElasticDatasource', function(this: any) {
] ,
} as DataQueryRequest < ElasticsearchQuery > ;
const queryBuilderSpy = jest . spyOn ( ctx . ds . queryBuilder , 'getLogsQuery' ) ;
const response = await ctx . ds . query ( query ) ;
const queryBuilderSpy = jest . spyOn ( ds . queryBuilder , 'getLogsQuery' ) ;
let response : any = { } ;
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
response = received [ 0 ] ;
} ) ;
return { queryBuilderSpy , response } ;
}
@ -243,43 +268,35 @@ describe('ElasticDatasource', function(this: any) {
} ) ;
describe ( 'When issuing document query' , ( ) = > {
let requestOptions : any , parts : any , header : any ;
async function runScenario() {
const range = createTimeRange ( dateTime ( [ 2015 , 4 , 30 , 10 ] ) , dateTime ( [ 2015 , 5 , 1 , 10 ] ) ) ;
const targets = [ { refId : 'A' , metrics : [ { type : 'raw_document' , id : '1' } ] , query : 'test' } ] ;
const query : any = { range , targets } ;
const data = { responses : [ ] } ;
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'test' ,
jsonData : { esVersion : 2 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
const { ds , fetchMock } = getTestContext ( { jsonData : { esVersion : 2 } , data , database : 'test' } ) ;
datasourceRequestMock . mockImplementation ( options = > {
requestOptions = options ;
return Promise . resolve ( { data : { responses : [ ] } } ) ;
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toEqual ( { data : [ ] } ) ;
} ) ;
const query : DataQueryRequest < ElasticsearchQuery > = {
range : createTimeRange ( dateTime ( [ 2015 , 4 , 30 , 10 ] ) , dateTime ( [ 2015 , 5 , 1 , 10 ] ) ) ,
targets : [
{
refId : 'A' ,
metrics : [ { type : 'raw_document' , id : '1' } ] ,
query : 'test' ,
} ,
] ,
} as DataQueryRequest < ElasticsearchQuery > ;
ctx . ds . query ( query ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
const requestOptions = fetchMock . mock . calls [ 0 ] [ 0 ] ;
const parts = requestOptions . data . split ( '\n' ) ;
const header = JSON . parse ( parts [ 0 ] ) ;
const body = JSON . parse ( parts [ 1 ] ) ;
parts = requestOptions . data . split ( '\n' ) ;
header = JSON . parse ( parts [ 0 ] ) ;
} ) ;
return { body , header } ;
}
it ( 'should set search type to query_then_fetch' , ( ) = > {
it ( 'should set search type to query_then_fetch' , async ( ) = > {
const { header } = await runScenario ( ) ;
expect ( header . search_type ) . toEqual ( 'query_then_fetch' ) ;
} ) ;
it ( 'should set size' , ( ) = > {
const body = JSON . parse ( parts [ 1 ] ) ;
it ( 'should set size' , async ( ) = > {
const { body } = await runScenario ( ) ;
expect ( body . size ) . toBe ( 500 ) ;
} ) ;
} ) ;
@ -298,15 +315,9 @@ describe('ElasticDatasource', function(this: any) {
] ,
} as DataQueryRequest < ElasticsearchQuery > ;
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : '[asd-]YYYY.MM.DD' ,
jsonData : { interval : 'Daily' , esVersion : 7 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
it ( 'should process it properly' , async ( ) = > {
datasourceRequestMock . mockImplementation ( ( ) = > {
return Promise . resolve ( {
const { ds } = getTestContext ( {
jsonData : { interval : 'Daily' , esVersion : 7 } ,
data : {
took : 1 ,
responses : [
@ -319,23 +330,24 @@ describe('ElasticDatasource', function(this: any) {
] ,
} ,
} ) ;
} ) ;
const errObject = {
data : '{\n "reason": "all shards failed"\n}' ,
message : 'all shards failed' ,
config : {
url : 'http://localhost:3000/api/tsdb/query' ,
} ,
} ;
try {
await ctx . ds . query ( query ) ;
} catch ( err ) {
expect ( err ) . toEqual ( errObject ) ;
}
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toEqual ( errObject ) ;
} ) ;
} ) ;
it ( 'should properly throw an unknown error' , async ( ) = > {
datasourceRequestMock . mockImplementation ( ( ) = > {
return Promise . resolve ( {
const { ds } = getTestContext ( {
jsonData : { interval : 'Daily' , esVersion : 7 } ,
data : {
took : 1 ,
responses : [
@ -346,32 +358,24 @@ describe('ElasticDatasource', function(this: any) {
] ,
} ,
} ) ;
} ) ;
const errObject = {
data : '{}' ,
message : 'Unknown elastic error response' ,
config : {
url : 'http://localhost:3000/api/tsdb/query' ,
} ,
} ;
try {
await ctx . ds . query ( query ) ;
} catch ( err ) {
expect ( err ) . toEqual ( errObject ) ;
}
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toEqual ( errObject ) ;
} ) ;
} ) ;
} ) ;
describe ( 'When getting fields' , ( ) = > {
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'metricbeat' ,
jsonData : { esVersion : 50 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
return Promise . resolve ( {
data : {
const data = {
metricbeat : {
mappings : {
metricsets : {
@ -416,14 +420,14 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
} ,
} ,
} ) ;
} ) ;
} ) ;
} ;
it ( 'should return nested fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( ) ;
const { ds } = getTestContext ( { data , jsonData : { esVersion : 50 } , database : 'metricbeat' } ) ;
await expect ( ds . getFields ( ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [
@ -439,29 +443,35 @@ describe('ElasticDatasource', function(this: any) {
'system.process.name' ,
] ) ;
} ) ;
} ) ;
it ( 'should return number fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( 'number' ) ;
const { ds } = getTestContext ( { data , jsonData : { esVersion : 50 } , database : 'metricbeat' } ) ;
await expect ( ds . getFields ( 'number' ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [ 'system.cpu.system' , 'system.cpu.user' , 'system.process.cpu.total' ] ) ;
} ) ;
} ) ;
it ( 'should return date fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( 'date' ) ;
const { ds } = getTestContext ( { data , jsonData : { esVersion : 50 } , database : 'metricbeat' } ) ;
await expect ( ds . getFields ( 'date' ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [ '@timestamp' , '__timestamp' , '@timestampnano' ] ) ;
} ) ;
} ) ;
} ) ;
describe ( 'When getting field mappings on indices with gaps' , ( ) = > {
const twoWeekTimeSrv : any = createTimeSrv ( 'now-2w' ) ;
const basicResponse = {
data : {
metricbeat : {
mappings : {
metricsets : {
@ -477,11 +487,9 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
} ,
} ,
} ;
const alternateResponse = {
data : {
metricbeat : {
mappings : {
metricsets : {
@ -492,91 +500,86 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
} ,
} ,
} ;
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : '[asd-]YYYY.MM.DD' ,
jsonData : { interval : 'Daily' , esVersion : 50 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
} ) ;
it ( 'should return fields of the newest available index' , async ( ) = > {
const twoDaysBefore = toUtc ( )
. subtract ( 2 , 'day' )
. format ( 'YYYY.MM.DD' ) ;
const threeDaysBefore = toUtc ( )
. subtract ( 3 , 'day' )
. format ( 'YYYY.MM.DD' ) ;
datasourceRequestMock . mockImplementation ( options = > {
if ( options . url === ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ twoDaysBefore } /_mapping ` ) {
return Promise . resolve ( basicResponse ) ;
} else if ( options . url === ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ threeDaysBefore } /_mapping ` ) {
return Promise . resolve ( alternateResponse ) ;
const baseUrl = ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ twoDaysBefore } /_mapping ` ;
const alternateUrl = ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ threeDaysBefore } /_mapping ` ;
const { ds , timeSrv } = getTestContext ( {
from : 'now-2w' ,
jsonData : { interval : 'Daily' , esVersion : 50 } ,
mockImplementation : options = > {
if ( options . url === baseUrl ) {
return of ( createFetchResponse ( basicResponse ) ) ;
} else if ( options . url === alternateUrl ) {
return of ( createFetchResponse ( alternateResponse ) ) ;
}
return Promise . reject ( { status : 404 } ) ;
return throwError ( { status : 404 } ) ;
} ,
} ) ;
const range = twoWeekTimeSrv . timeRange ( ) ;
const fieldObjects = await ctx . ds . getFields ( undefined , range ) ;
const range = timeSrv . timeRange ( ) ;
await expect ( ds . getFields ( undefined , range ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [ '@timestamp' , 'beat.hostname' ] ) ;
} ) ;
} ) ;
it ( 'should not retry when ES is down' , async ( ) = > {
const twoDaysBefore = toUtc ( )
. subtract ( 2 , 'day' )
. format ( 'YYYY.MM.DD' ) ;
const range = twoWeekTimeSrv . timeRange ( ) ;
datasourceRequestMock . mockImplementation ( options = > {
const { ds , timeSrv , fetchMock } = getTestContext ( {
from : 'now-2w' ,
jsonData : { interval : 'Daily' , esVersion : 50 } ,
mockImplementation : options = > {
if ( options . url === ` ${ ELASTICSEARCH_MOCK_URL } /asd- ${ twoDaysBefore } /_mapping ` ) {
return Promise . resolve ( basicResponse ) ;
return of ( createFetchRespons e ( basicResponse ) ) ;
}
return Promise . reject ( { status : 500 } ) ;
return throwError ( { status : 500 } ) ;
} ,
} ) ;
expect . assertions ( 2 ) ;
try {
await ctx . ds . getFields ( undefined , range ) ;
} catch ( e ) {
expect ( e ) . toStrictEqual ( { status : 500 } ) ;
expect ( datasourceRequest Mock) . toBeCalledTimes ( 1 ) ;
}
const range = timeSrv . timeRange ( ) ;
await expect ( ds . getFields ( undefined , range ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( r eceived [ 0 ] ) . toStrictEqual ( { status : 500 } ) ;
expect ( fetch Mock) . toBeCalledTimes ( 1 ) ;
} ) ;
} ) ;
it ( 'should not retry more than 7 indices' , async ( ) = > {
const range = twoWeekTimeSrv . timeRange ( ) ;
datasourceRequestMock . mockImplementation ( ( ) = > {
return Promise . reject ( { status : 404 } ) ;
const { ds , timeSrv , fetchMock } = getTestContext ( {
from : 'now-2w' ,
jsonData : { interval : 'Daily' , esVersion : 50 } ,
mockImplementation : options = > {
return throwError ( { status : 404 } ) ;
} ,
} ) ;
const range = timeSrv . timeRange ( ) ;
expect . assertions ( 2 ) ;
try {
await ctx . ds . getFields ( undefined , range ) ;
} catch ( e ) {
expect ( e ) . toStrictEqual ( { status : 404 } ) ;
expect ( datasourceRequestMock ) . toBeCalledTimes ( 7 ) ;
}
await expect ( ds . getFields ( undefined , range ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toStrictEqual ( 'Could not find an available index for this time range.' ) ;
expect ( fetchMock ) . toBeCalledTimes ( 7 ) ;
} ) ;
} ) ;
} ) ;
describe ( 'When getting fields from ES 7.0' , ( ) = > {
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'genuine.es7._mapping.response' ,
jsonData : { esVersion : 70 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
return Promise . resolve ( {
data : {
const data = {
'genuine.es7._mapping.response' : {
mappings : {
properties : {
@ -657,16 +660,16 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
} ,
} ,
} ) ;
} ) ;
} ) ;
} ;
it ( 'should return nested fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( ) ;
const { ds } = getTestContext ( { data , database : 'genuine.es7._mapping.response' , jsonData : { esVersion : 70 } } ) ;
const fields = _ . map ( fieldObjects , 'text' ) ;
await expect ( ds . getFields ( ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [
'@timestamp_millis' ,
'classification_terms' ,
@ -684,12 +687,16 @@ describe('ElasticDatasource', function(this: any) {
'ua_terms_short' ,
] ) ;
} ) ;
} ) ;
it ( 'should return number fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( 'number' ) ;
const { ds } = getTestContext ( { data , database : 'genuine.es7._mapping.response' , jsonData : { esVersion : 70 } } ) ;
const fields = _ . map ( fieldObjects , 'text' ) ;
await expect ( ds . getFields ( 'number' ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [
'justification_blob.overall_vote_score' ,
'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness' ,
@ -699,74 +706,65 @@ describe('ElasticDatasource', function(this: any) {
'overall_vote_score' ,
] ) ;
} ) ;
} ) ;
it ( 'should return date fields' , async ( ) = > {
const fieldObjects = await ctx . ds . getFields ( 'date' ) ;
const { ds } = getTestContext ( { data , database : 'genuine.es7._mapping.response' , jsonData : { esVersion : 70 } } ) ;
const fields = _ . map ( fieldObjects , 'text' ) ;
await expect ( ds . getFields ( 'date' ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
const fieldObjects = received [ 0 ] ;
const fields = _ . map ( fieldObjects , 'text' ) ;
expect ( fields ) . toEqual ( [ '@timestamp_millis' ] ) ;
} ) ;
} ) ;
describe ( 'When issuing aggregation query on es5.x' , ( ) = > {
let requestOptions : any , parts : any , header : any ;
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'test' ,
jsonData : { esVersion : 5 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
requestOptions = options ;
return Promise . resolve ( { data : { responses : [ ] } } ) ;
} ) ;
const query : DataQueryRequest < ElasticsearchQuery > = {
range : createTimeRange ( dateTime ( [ 2015 , 4 , 30 , 10 ] ) , dateTime ( [ 2015 , 5 , 1 , 10 ] ) ) ,
targets : [
describe ( 'When issuing aggregation query on es5.x' , ( ) = > {
async function runScenario() {
const range = createTimeRange ( dateTime ( [ 2015 , 4 , 30 , 10 ] ) , dateTime ( [ 2015 , 5 , 1 , 10 ] ) ) ;
const targets = [
{
refId : 'A' ,
bucketAggs : [ { type : 'date_histogram' , field : '@timestamp' , id : '2' } ] ,
metrics : [ { type : 'count' , id : '1' } ] ,
query : 'test' ,
} ,
] ,
} as DataQueryRequest < ElasticsearchQuery > ;
] ;
const query : any = { range , targets } ;
const data = { responses : [ ] } ;
ctx . ds . query ( query ) ;
const { ds , fetchMock } = getTestContext ( { jsonData : { esVersion : 5 } , data , database : 'test' } ) ;
parts = requestOptions . data . split ( '\n' ) ;
header = JSON . parse ( parts [ 0 ] ) ;
await expect ( ds . query ( query ) ) . toEmitValuesWith ( received = > {
expect ( received . length ) . toBe ( 1 ) ;
expect ( received [ 0 ] ) . toEqual ( { data : [ ] } ) ;
} ) ;
it ( 'should not set search type to count' , ( ) = > {
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
const requestOptions = fetchMock . mock . calls [ 0 ] [ 0 ] ;
const parts = requestOptions . data . split ( '\n' ) ;
const header = JSON . parse ( parts [ 0 ] ) ;
const body = JSON . parse ( parts [ 1 ] ) ;
return { body , header } ;
}
it ( 'should not set search type to count' , async ( ) = > {
const { header } = await runScenario ( ) ;
expect ( header . search_type ) . not . toEqual ( 'count' ) ;
} ) ;
it ( 'should set size to 0' , ( ) = > {
const body = JSON . parse ( parts [ 1 ] ) ;
it ( 'should set size to 0' , async ( ) = > {
const { body } = await runScenario ( ) ;
expect ( body . size ) . toBe ( 0 ) ;
} ) ;
} ) ;
describe ( 'When issuing metricFind query on es5.x' , ( ) = > {
let requestOptions : any , parts , header : any , body : any ;
let results : MetricFindValue [ ] ;
beforeEach ( ( ) = > {
createDatasource ( {
url : ELASTICSEARCH_MOCK_URL ,
database : 'test' ,
jsonData : { esVersion : 5 } as ElasticsearchOptions ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ) ;
datasourceRequestMock . mockImplementation ( options = > {
requestOptions = options ;
return Promise . resolve ( {
data : {
async function runScenario() {
const data = {
responses : [
{
aggregations : {
@ -783,64 +781,65 @@ describe('ElasticDatasource', function(this: any) {
} ,
} ,
] ,
} ,
} ) ;
} ) ;
} ;
ctx . ds . metricFindQuery ( '{"find": "terms", "field": "test"}' ) . then ( res = > {
results = res ;
} ) ;
const { ds , fetchMock } = getTestContext ( { jsonData : { esVersion : 5 } , data , database : 'test' } ) ;
parts = requestOptions . data . split ( '\n' ) ;
header = JSON . parse ( parts [ 0 ] ) ;
body = JSON . parse ( parts [ 1 ] ) ;
} ) ;
const results = await ds . metricFindQuery ( '{"find": "terms", "field": "test"}' ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
const requestOptions = fetchMock . mock . calls [ 0 ] [ 0 ] ;
const parts = requestOptions . data . split ( '\n' ) ;
const header = JSON . parse ( parts [ 0 ] ) ;
const body = JSON . parse ( parts [ 1 ] ) ;
return { results , body , header } ;
}
it ( 'should get results' , ( ) = > {
it ( 'should get results' , async ( ) = > {
const { results } = await runScenario ( ) ;
expect ( results . length ) . toEqual ( 2 ) ;
} ) ;
it ( 'should use key or key_as_string' , ( ) = > {
it ( 'should use key or key_as_string' , async ( ) = > {
const { results } = await runScenario ( ) ;
expect ( results [ 0 ] . text ) . toEqual ( 'test' ) ;
expect ( results [ 1 ] . text ) . toEqual ( 'test2_as_string' ) ;
} ) ;
it ( 'should not set search type to count' , ( ) = > {
it ( 'should not set search type to count' , async ( ) = > {
const { header } = await runScenario ( ) ;
expect ( header . search_type ) . not . toEqual ( 'count' ) ;
} ) ;
it ( 'should set size to 0' , ( ) = > {
it ( 'should set size to 0' , async ( ) = > {
const { body } = await runScenario ( ) ;
expect ( body . size ) . toBe ( 0 ) ;
} ) ;
it ( 'should not set terms aggregation size to 0' , ( ) = > {
it ( 'should not set terms aggregation size to 0' , async ( ) = > {
const { body } = await runScenario ( ) ;
expect ( body [ 'aggs' ] [ '1' ] [ 'terms' ] . size ) . not . toBe ( 0 ) ;
} ) ;
} ) ;
describe ( 'query' , ( ) = > {
it ( 'should replace range as integer not string' , ( ) = > {
const dataSource = new ElasticDatasource (
{
url : ELASTICSEARCH_MOCK_URL ,
database : '[asd-]YYYY.MM.DD' ,
jsonData : {
interval : 'Daily' ,
esVersion : 2 ,
timeField : '@time' ,
} ,
} as DataSourceInstanceSettings < ElasticsearchOptions > ,
templateSrv as TemplateSrv
) ;
( dataSource as any ) . post = jest . fn ( ( ) = > Promise . resolve ( { responses : [ ] } ) ) ;
dataSource . query ( createElasticQuery ( ) ) ;
it ( 'should replace range as integer not string' , async ( ) = > {
const { ds } = getTestContext ( { jsonData : { interval : 'Daily' , esVersion : 2 , timeField : '@time' } } ) ;
const postMock = jest . fn ( ( url : string , data : any ) = > of ( createFetchResponse ( { responses : [ ] } ) ) ) ;
ds [ 'post' ] = postMock ;
const query = ( ( dataSource as any ) . post as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
await expect ( ds . query ( createElasticQuery ( ) ) ) . toEmitValuesWith ( received = > {
expect ( postMock ) . toHaveBeenCalledTimes ( 1 ) ;
const query = postMock . mock . calls [ 0 ] [ 1 ] ;
expect ( typeof JSON . parse ( query . split ( '\n' ) [ 1 ] ) . query . bool . filter [ 0 ] . range [ '@time' ] . gte ) . toBe ( 'number' ) ;
} ) ;
} ) ;
} ) ;
it ( 'should correctly interpolate variables in query' , ( ) = > {
const { ds } = getTestContext ( ) ;
const query : ElasticsearchQuery = {
refId : 'A' ,
bucketAggs : [ { type : 'filters' , settings : { filters : [ { query : '$var' , label : '' } ] } , id : '1' } ] ,
@ -848,13 +847,14 @@ describe('ElasticDatasource', function(this: any) {
query : '$var' ,
} ;
const interpolatedQuery = ctx . ds . interpolateVariablesInQueries ( [ query ] , { } ) [ 0 ] ;
const interpolatedQuery = ds . interpolateVariablesInQueries ( [ query ] , { } ) [ 0 ] ;
expect ( interpolatedQuery . query ) . toBe ( 'resolvedVariable' ) ;
expect ( ( interpolatedQuery . bucketAggs ! [ 0 ] as Filters ) . settings ! . filters ! [ 0 ] . query ) . toBe ( 'resolvedVariable' ) ;
} ) ;
it ( 'should correctly handle empty query strings' , ( ) = > {
const { ds } = getTestContext ( ) ;
const query : ElasticsearchQuery = {
refId : 'A' ,
bucketAggs : [ { type : 'filters' , settings : { filters : [ { query : '' , label : '' } ] } , id : '1' } ] ,
@ -862,7 +862,7 @@ describe('ElasticDatasource', function(this: any) {
query : '' ,
} ;
const interpolatedQuery = ctx . ds . interpolateVariablesInQueries ( [ query ] , { } ) [ 0 ] ;
const interpolatedQuery = ds . interpolateVariablesInQueries ( [ query ] , { } ) [ 0 ] ;
expect ( interpolatedQuery . query ) . toBe ( '*' ) ;
expect ( ( interpolatedQuery . bucketAggs ! [ 0 ] as Filters ) . settings ! . filters ! [ 0 ] . query ) . toBe ( '*' ) ;