@ -1,3 +1,4 @@
import { snakeCase } from 'lodash' ;
import { EMPTY , interval , Observable , of } from 'rxjs' ;
import { thunkTester } from 'test/core/thunk/thunkTester' ;
import { assertIsDefined } from 'test/helpers/asserts' ;
@ -8,16 +9,18 @@ import {
DataQueryResponse ,
DataSourceApi ,
DataSourceJsonData ,
DataSourceWithLogsVolume Support ,
DataSourceWithSupplementaryQueries Support ,
LoadingState ,
MutableDataFrame ,
RawTimeRange ,
SupplementaryQueryType ,
} from '@grafana/data' ;
import { ExploreId , ExploreItemState , StoreState , SupplementaryQueryType , ThunkDispatch } from 'app/types' ;
import { ExploreId , ExploreItemState , StoreState , ThunkDispatch } from 'app/types' ;
import { reducerTester } from '../../../../test/core/redux/reducerTester' ;
import { configureStore } from '../../../store/configureStore' ;
import { setTimeSrv } from '../../dashboard/services/TimeSrv' ;
import { setTimeSrv , TimeSrv } from '../../dashboard/services/TimeSrv' ;
import { supplementaryQueryTypes } from '../utils/supplementaryQueries' ;
import { createDefaultInitialState } from './helpers' ;
import { saveCorrelationsAction } from './main' ;
@ -33,8 +36,8 @@ import {
runQueries ,
scanStartAction ,
scanStopAction ,
storeSupplementaryQueryDataProviderAction ,
setSupplementaryQueryEnabled ,
cleanSupplementaryQueryDataProviderAction ,
} from './query' ;
import { makeExplorePaneState } from './utils' ;
@ -99,10 +102,10 @@ function setupQueryResponse(state: StoreState) {
describe ( 'runQueries' , ( ) = > {
const setupTests = ( ) = > {
setTimeSrv ( { init() { } } as any ) ;
setTimeSrv ( { init() { } } as unknown as TimeSrv ) ;
return configureStore ( {
. . . ( defaultInitialState as any ) ,
} ) ;
. . . defaultInitialState ,
} as unknown as Partial < StoreState > ) ;
} ;
it ( 'should pass dataFrames to state even if there is error in response' , async ( ) = > {
@ -114,20 +117,23 @@ describe('runQueries', () => {
expect ( getState ( ) . explore [ ExploreId . left ] . graphResult ) . toBeDefined ( ) ;
} ) ;
it ( 'should modify the request-id for log-vo lume queries' , async ( ) = > {
it ( 'should modify the request-id for a ll s upple mentary queries' , ( ) = > {
const { dispatch , getState } = setupTests ( ) ;
setupQueryResponse ( getState ( ) ) ;
await dispatch ( saveCorrelationsAction ( [ ] ) ) ;
await dispatch ( runQueries ( ExploreId . left ) ) ;
dispatch ( saveCorrelationsAction ( [ ] ) ) ;
dispatch ( runQueries ( ExploreId . left ) ) ;
const state = getState ( ) . explore [ ExploreId . left ] ;
expect ( state . queryResponse . request ? . requestId ) . toBe ( 'explore_left' ) ;
const datasource = state . datasourceInstance as unknown as DataSourceWithLogsVolumeSupport < DataQuery > ;
expect ( datasource . getLogsVolumeDataProvider ) . toBeCalledWith (
expect . objectContaining ( {
requestId : 'explore_left_log_volume' ,
} )
) ;
const datasource = state . datasourceInstance as unknown as DataSourceWithSupplementaryQueriesSupport < DataQuery > ;
for ( const type of supplementaryQueryTypes ) {
expect ( datasource . getDataProvider ) . toHaveBeenCalledWith (
type ,
expect . objectContaining ( {
requestId : ` explore_left_ ${ snakeCase ( type ) } ` ,
} )
) ;
}
} ) ;
it ( 'should set state to done if query completes without emitting' , async ( ) = > {
@ -164,7 +170,10 @@ describe('running queries', () => {
querySubscription : unsubscribable ,
queries : [ 'A' ] ,
range : testRange ,
supplementaryQueries : { [ SupplementaryQueryType . LogsVolume ] : { enabled : true } } ,
supplementaryQueries : {
[ SupplementaryQueryType . LogsVolume ] : { enabled : true } ,
[ SupplementaryQueryType . LogsSample ] : { enabled : true } ,
} ,
} ,
} ,
@ -180,8 +189,10 @@ describe('running queries', () => {
expect ( dispatchedActions ) . toEqual ( [
scanStopAction ( { exploreId } ) ,
cancelQueriesAction ( { exploreId } ) ,
store SupplementaryQueryDataProviderAction( { exploreId , type : SupplementaryQueryType . LogsVolume } ) ,
clean SupplementaryQueryDataProviderAction( { exploreId , type : SupplementaryQueryType . LogsVolume } ) ,
cleanSupplementaryQueryAction ( { exploreId , type : SupplementaryQueryType . LogsVolume } ) ,
cleanSupplementaryQueryDataProviderAction ( { exploreId , type : SupplementaryQueryType . LogsSample } ) ,
cleanSupplementaryQueryAction ( { exploreId , type : SupplementaryQueryType . LogsSample } ) ,
] ) ;
} ) ;
} ) ;
@ -190,14 +201,14 @@ describe('importing queries', () => {
describe ( 'when importing queries between the same type of data source' , ( ) = > {
it ( 'remove datasource property from all of the queries' , async ( ) = > {
const { dispatch , getState } : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
datasourceInstance : datasources [ 0 ] ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
await dispatch (
importQueries (
@ -276,7 +287,7 @@ describe('reducer', () => {
describe ( 'caching' , ( ) = > {
it ( 'should add response to cache' , async ( ) = > {
const { dispatch , getState } : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
@ -287,7 +298,7 @@ describe('reducer', () => {
absoluteRange : { from : 1621348027000 , to : 1621348050000 } ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
await dispatch ( addResultsToCache ( ExploreId . left ) ) ;
@ -298,7 +309,7 @@ describe('reducer', () => {
it ( 'should not add response to cache if response is still loading' , async ( ) = > {
const { dispatch , getState } : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
@ -306,7 +317,7 @@ describe('reducer', () => {
absoluteRange : { from : 1621348027000 , to : 1621348050000 } ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
await dispatch ( addResultsToCache ( ExploreId . left ) ) ;
@ -315,7 +326,7 @@ describe('reducer', () => {
it ( 'should not add duplicate response to cache' , async ( ) = > {
const { dispatch , getState } : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
@ -332,7 +343,7 @@ describe('reducer', () => {
] ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
await dispatch ( addResultsToCache ( ExploreId . left ) ) ;
@ -344,7 +355,7 @@ describe('reducer', () => {
it ( 'should clear cache' , async ( ) = > {
const { dispatch , getState } : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
@ -356,7 +367,7 @@ describe('reducer', () => {
] ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
await dispatch ( clearCache ( ExploreId . left ) ) ;
@ -364,15 +375,15 @@ describe('reducer', () => {
} ) ;
} ) ;
describe ( 'log volume ' , ( ) = > {
describe ( 'supplementary queries ' , ( ) = > {
let dispatch : ThunkDispatch ,
getState : ( ) = > StoreState ,
unsubscribes : Function [ ] ,
mockLogsVolume DataProvider : ( ) = > Observable < DataQueryResponse > ;
mockDataProvider : ( ) = > Observable < DataQueryResponse > ;
beforeEach ( ( ) = > {
unsubscribes = [ ] ;
mockLogsVolume DataProvider = ( ) = > {
mockDataProvider = ( ) = > {
return {
subscribe : ( ) = > {
const unsubscribe = jest . fn ( ) ;
@ -385,7 +396,7 @@ describe('reducer', () => {
} ;
const store : { dispatch : ThunkDispatch ; getState : ( ) = > StoreState } = configureStore ( {
. . . ( defaultInitialState as any ) ,
. . . defaultInitialState ,
explore : {
[ ExploreId . left ] : {
. . . defaultInitialState . explore [ ExploreId . left ] ,
@ -395,13 +406,17 @@ describe('reducer', () => {
meta : {
id : 'something' ,
} ,
getLogsVolume DataProvider : ( ) = > {
return mockLogsVolume DataProvider ( ) ;
getDataProvider : ( ) = > {
return mockDataProvider ( ) ;
} ,
getSupportedSupplementaryQueryTypes : ( ) = > [
SupplementaryQueryType . LogsVolume ,
SupplementaryQueryType . LogsSample ,
] ,
} ,
} ,
} ,
} ) ;
} as unknown as Partial < StoreState > ) ;
dispatch = store . dispatch ;
getState = store . getState ;
@ -409,109 +424,111 @@ describe('reducer', () => {
setupQueryResponse ( getState ( ) ) ;
} ) ;
it ( 'should cancel any unfinished log s vol ume queries when a new query is run' , async ( ) = > {
await dispatch ( runQueries ( ExploreId . left ) ) ;
it ( 'should cancel any unfinished supple mentary queries when a new query is run' , async ( ) = > {
dispatch ( runQueries ( ExploreId . left ) ) ;
// first query is run automatically
// loading in progress - one subscription created, not cleaned up yet
expect ( unsubscribes ) . toHaveLength ( 1 ) ;
// loading in progress - subscriptions for both supplementary queries are created, not cleaned up yet
expect ( unsubscribes ) . toHaveLength ( 2 ) ;
expect ( unsubscribes [ 0 ] ) . not . toBeCalled ( ) ;
expect ( unsubscribes [ 1 ] ) . not . toBeCalled ( ) ;
setupQueryResponse ( getState ( ) ) ;
await dispatch ( runQueries ( ExploreId . left ) ) ;
// a new query is run while log volume query is not resolve yet...
dispatch ( runQueries ( ExploreId . left ) ) ;
// a new query is run while supplementary queries are not resolve yet...
expect ( unsubscribes [ 0 ] ) . toBeCalled ( ) ;
// first subscription is cleaned up, a new subscription is created automatically
expect ( unsubscribes ) . toHaveLength ( 2 ) ;
expect ( unsubscribes [ 1 ] ) . not . toBeCalled ( ) ;
expect ( unsubscribes [ 1 ] ) . toBeCalled ( ) ;
// first subscriptions are cleaned up, a new subscriptions are created automatically
expect ( unsubscribes ) . toHaveLength ( 4 ) ;
expect ( unsubscribes [ 2 ] ) . not . toBeCalled ( ) ;
expect ( unsubscribes [ 3 ] ) . not . toBeCalled ( ) ;
} ) ;
it ( 'should cancel log volume query when the main query is canceled' , async ( ) = > {
await dispatch ( runQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 1 ) ;
it ( 'should cancel all supported supplementary queries when the main query is canceled' , ( ) = > {
dispatch ( runQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 2 ) ;
expect ( unsubscribes [ 0 ] ) . not . toBeCalled ( ) ;
expect ( unsubscribes [ 1 ] ) . not . toBeCalled ( ) ;
await dispatch ( cancelQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 1 ) ;
dispatch ( cancelQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 2 ) ;
expect ( unsubscribes [ 0 ] ) . toBeCalled ( ) ;
expect ( unsubscribes [ 1 ] ) . toBeCalled ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeUndefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeUndefined ( ) ;
for ( const type of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ) . toBeUndefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . dataProvider ) . toBeUndefined ( ) ;
}
} ) ;
it ( 'should load log s vol ume after running the query' , async ( ) = > {
await dispatch ( runQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 1 ) ;
it ( 'should load supple mentary queries after running the query' , ( ) = > {
dispatch ( runQueries ( ExploreId . left ) ) ;
expect ( unsubscribes ) . toHaveLength ( 2 ) ;
} ) ;
it ( 'should clean any incomplete log volume data when main query is canceled' , async ( ) = > {
mockLogsVolume DataProvider = ( ) = > {
it ( 'should clean any incomplete supplementary queries data when main query is canceled' , ( ) = > {
mockDataProvider = ( ) = > {
return of ( { state : LoadingState.Loading , error : undefined , data : [ ] } ) ;
} ;
await dispatch ( runQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data ! . state
) . toBe ( LoadingState . Loading ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeDefined ( ) ;
await dispatch ( cancelQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeUndefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeUndefined ( ) ;
dispatch ( runQueries ( ExploreId . left ) ) ;
for ( const type of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ) . toBeDefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ! . state ) . toBe ( LoadingState . Loading ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . dataProvider ) . toBeDefined ( ) ;
}
for ( const type of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ) . toBeDefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ! . state ) . toBe ( LoadingState . Loading ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . dataProvider ) . toBeDefined ( ) ;
}
dispatch ( cancelQueries ( ExploreId . left ) ) ;
for ( const type of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ) . toBeUndefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ type ] . data ) . toBeUndefined ( ) ;
}
} ) ;
it ( 'keeps complete log volume data when main query is canceled' , async ( ) = > {
mockLogsVolume DataProvider = ( ) = > {
it ( 'keeps complete supplementary data when main query is canceled' , async ( ) = > {
mockDataProvider = ( ) = > {
return of (
{ state : LoadingState.Loading , error : undefined , data : [ ] } ,
{ state : LoadingState.Done , error : undefined , data : [ { } ] }
) ;
} ;
await dispatch ( runQueries ( ExploreId . left ) ) ;
dispatch ( runQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data ! . state
) . toBe ( LoadingState . Done ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeDefined ( ) ;
for ( const types of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . data ) . toBeDefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . data ! . state ) . toBe ( LoadingState . Done ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . dataProvider ) . toBeDefined ( ) ;
}
await dispatch ( cancelQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data ! . state
) . toBe ( LoadingState . Done ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeUndefined ( ) ;
dispatch ( cancelQueries ( ExploreId . left ) ) ;
for ( const types of supplementaryQueryTypes ) {
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . data ) . toBeDefined ( ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . data ! . state ) . toBe ( LoadingState . Done ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ types ] . dataProvider ) . toBeUndefined ( ) ;
}
} ) ;
it ( 'do not load logsVolume data when disabled' , async ( ) = > {
// turn logsvolume off
it ( 'do not load disabled supplementary query data' , ( ) = > {
mockDataProvider = ( ) = > {
return of ( { state : LoadingState.Done , error : undefined , data : [ { } ] } ) ;
} ;
// turn logs volume off (but keep log sample on)
dispatch ( setSupplementaryQueryEnabled ( ExploreId . left , false , SupplementaryQueryType . LogsVolume ) ) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . enabled ) . toBe (
false
) ;
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . enabled ) . toBe (
true
) ;
// verify that if we run a query, it will: 1) not do logs volume, 2) do logs sample 3) provider will still be set for both
dispatch ( runQueries ( ExploreId . left ) ) ;
// verify that if we run a query, it will not do logsvolume, but the Provider will still be set
await dispatch ( runQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . data
) . toBeUndefined ( ) ;
@ -521,29 +538,51 @@ describe('reducer', () => {
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . data
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . dataSubscription
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . dataProvider
) . toBeDefined ( ) ;
} ) ;
it ( 'load logsVolume data when it gets enabled' , async ( ) = > {
// first it is disabled
it ( 'load data of supplementary query tha t gets enabled' , async ( ) = > {
// first we start w ith both supplementary quer ie s disabled
dispatch ( setSupplementaryQueryEnabled ( ExploreId . left , false , SupplementaryQueryType . LogsVolume ) ) ;
dispatch ( setSupplementaryQueryEnabled ( ExploreId . left , false , SupplementaryQueryType . LogsSample ) ) ;
// runQueries sets up the logsVolume query, but does not run it
await dispatch ( runQueries ( ExploreId . left ) ) ;
// runQueries sets up providers, but does not run queries
dispatch ( runQueries ( ExploreId . left ) ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataProvider
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . dataProvider
) . toBeDefined ( ) ;
// we turn logsvolume on
await dispatch ( setSupplementaryQueryEnabled ( ExploreId . left , true , SupplementaryQueryType . LogsVolume ) ) ;
// we turn 1 supplementary query ( logs volume) on
dispatch ( setSupplementaryQueryEnabled ( ExploreId . left , true , SupplementaryQueryType . LogsVolume ) ) ;
// verify it was turned on
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . enabled ) . toBe (
true
) ;
// verify that other stay off
expect ( getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . enabled ) . toBe (
false
) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsVolume ] . dataSubscription
) . toBeDefined ( ) ;
expect (
getState ( ) . explore [ ExploreId . left ] . supplementaryQueries [ SupplementaryQueryType . LogsSample ] . dataSubscription
) . toBeUndefined ( ) ;
} ) ;
} ) ;
} ) ;