Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
a6c2a8c3
Commit
a6c2a8c3
authored
Mar 22, 2019
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into table-reducer
parents
beba9676
eedbc485
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
668 additions
and
250 deletions
+668
-250
README.md
+1
-1
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
+2
-2
packages/grafana-ui/src/components/Gauge/Gauge.tsx
+2
-3
packages/grafana-ui/src/types/data.ts
+14
-0
packages/grafana-ui/src/types/displayValue.ts
+11
-0
packages/grafana-ui/src/types/index.ts
+1
-0
packages/grafana-ui/src/utils/displayValue.test.ts
+31
-2
packages/grafana-ui/src/utils/displayValue.ts
+63
-13
public/app/core/actions/location.ts
+2
-15
public/app/core/reducers/location.ts
+9
-9
public/app/core/redux/actionCreatorFactory.ts
+4
-0
public/app/features/alerting/AlertRuleList.test.tsx
+3
-1
public/app/features/annotations/all.ts
+1
-3
public/app/features/annotations/event.ts
+0
-11
public/app/features/annotations/event_editor.ts
+1
-1
public/app/features/annotations/event_manager.ts
+8
-8
public/app/features/dashboard/containers/DashboardPage.test.tsx
+3
-2
public/app/features/datasources/state/actions.ts
+3
-4
public/app/features/explore/Explore.tsx
+45
-17
public/app/features/explore/Wrapper.tsx
+5
-36
public/app/features/explore/state/actionTypes.ts
+1
-23
public/app/features/explore/state/actions.test.ts
+147
-0
public/app/features/explore/state/actions.ts
+51
-3
public/app/features/explore/state/reducers.test.ts
+0
-0
public/app/features/explore/state/reducers.ts
+105
-19
public/app/plugins/datasource/graphite/gfunc.ts
+10
-3
public/app/plugins/datasource/graphite/graphite_query.ts
+5
-5
public/app/plugins/datasource/graphite/specs/gfunc.test.ts
+36
-10
public/app/plugins/datasource/graphite/specs/graphite_query.test.ts
+2
-1
public/app/plugins/datasource/graphite/specs/query_ctrl.test.ts
+3
-2
public/app/plugins/datasource/prometheus/datasource.ts
+10
-4
public/app/plugins/panel/singlestat/module.ts
+12
-50
public/app/types/explore.ts
+11
-0
public/test/core/thunk/thunkTester.ts
+64
-0
public/test/specs/helpers.ts
+1
-1
scripts/grunt/options/compress.js
+1
-1
No files found.
README.md
View file @
a6c2a8c3
...
@@ -163,5 +163,5 @@ plugin development.
...
@@ -163,5 +163,5 @@ plugin development.
## License
## License
Grafana is distributed under
[
Apache 2.0 License
](
https://github.com/grafana/grafana/blob/master/LICENSE
.md
)
.
Grafana is distributed under
[
Apache 2.0 License
](
https://github.com/grafana/grafana/blob/master/LICENSE
)
.
packages/grafana-ui/src/components/BarGauge/BarGauge.tsx
View file @
a6c2a8c3
...
@@ -3,10 +3,10 @@ import React, { PureComponent, CSSProperties, ReactNode } from 'react';
...
@@ -3,10 +3,10 @@ import React, { PureComponent, CSSProperties, ReactNode } from 'react';
import
tinycolor
from
'tinycolor2'
;
import
tinycolor
from
'tinycolor2'
;
// Utils
// Utils
import
{
getColorFromHexRgbOrName
,
getThresholdForValue
,
DisplayValue
}
from
'../../utils'
;
import
{
getColorFromHexRgbOrName
,
getThresholdForValue
}
from
'../../utils'
;
// Types
// Types
import
{
Themeable
,
TimeSeriesValue
,
Threshold
,
VizOrientation
}
from
'../../types'
;
import
{
DisplayValue
,
Themeable
,
TimeSeriesValue
,
Threshold
,
VizOrientation
}
from
'../../types'
;
const
BAR_SIZE_RATIO
=
0.8
;
const
BAR_SIZE_RATIO
=
0.8
;
...
...
packages/grafana-ui/src/components/Gauge/Gauge.tsx
View file @
a6c2a8c3
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
$
from
'jquery'
;
import
$
from
'jquery'
;
import
{
Threshold
,
GrafanaThemeType
}
from
'../../types'
;
import
{
getColorFromHexRgbOrName
}
from
'../../utils'
;
import
{
getColorFromHexRgbOrName
}
from
'../../utils'
;
import
{
Themeable
}
from
'../../index'
;
import
{
DisplayValue
}
from
'../../utils/displayValue
'
;
import
{
DisplayValue
,
Threshold
,
GrafanaThemeType
,
Themeable
}
from
'../../types
'
;
export
interface
Props
extends
Themeable
{
export
interface
Props
extends
Themeable
{
height
:
number
;
height
:
number
;
...
...
packages/grafana-ui/src/types/data.ts
View file @
a6c2a8c3
...
@@ -76,3 +76,17 @@ export interface TableData {
...
@@ -76,3 +76,17 @@ export interface TableData {
rows
:
any
[][];
rows
:
any
[][];
tags
?:
Tags
;
tags
?:
Tags
;
}
}
export
interface
AnnotationEvent
{
annotation
?:
any
;
dashboardId
?:
number
;
panelId
?:
number
;
userId
?:
number
;
time
?:
number
;
timeEnd
?:
number
;
isRegion
?:
boolean
;
title
?:
string
;
text
?:
string
;
type
?:
string
;
tags
?:
string
;
}
packages/grafana-ui/src/types/displayValue.ts
0 → 100644
View file @
a6c2a8c3
export
interface
DisplayValue
{
text
:
string
;
// Show in the UI
numeric
:
number
;
// Use isNaN to check if it is a real number
color
?:
string
;
// color based on configs or Threshold
title
?:
string
;
}
export
interface
DecimalInfo
{
decimals
:
number
;
scaledDecimals
:
number
;
}
packages/grafana-ui/src/types/index.ts
View file @
a6c2a8c3
...
@@ -6,3 +6,4 @@ export * from './datasource';
...
@@ -6,3 +6,4 @@ export * from './datasource';
export
*
from
'./theme'
;
export
*
from
'./theme'
;
export
*
from
'./threshold'
;
export
*
from
'./threshold'
;
export
*
from
'./input'
;
export
*
from
'./input'
;
export
*
from
'./displayValue'
;
packages/grafana-ui/src/utils/displayValue.test.ts
View file @
a6c2a8c3
import
{
getDisplayProcessor
,
getColorFromThreshold
,
DisplayProcessor
,
Display
Value
}
from
'./displayValue'
;
import
{
getDisplayProcessor
,
getColorFromThreshold
,
DisplayProcessor
,
getDecimalsFor
Value
}
from
'./displayValue'
;
import
{
MappingType
,
ValueMapping
}
from
'../types/panel
'
;
import
{
DisplayValue
,
MappingType
,
ValueMapping
}
from
'../types
'
;
function
assertSame
(
input
:
any
,
processors
:
DisplayProcessor
[],
match
:
DisplayValue
)
{
function
assertSame
(
input
:
any
,
processors
:
DisplayProcessor
[],
match
:
DisplayValue
)
{
processors
.
forEach
(
processor
=>
{
processors
.
forEach
(
processor
=>
{
...
@@ -144,6 +144,20 @@ describe('Format value', () => {
...
@@ -144,6 +144,20 @@ describe('Format value', () => {
expect
(
result
.
text
).
toEqual
(
'10.0'
);
expect
(
result
.
text
).
toEqual
(
'10.0'
);
});
});
it
(
'should set auto decimals, 1 significant'
,
()
=>
{
const
value
=
'1.23'
;
const
instance
=
getDisplayProcessor
({
decimals
:
null
});
expect
(
instance
(
value
).
text
).
toEqual
(
'1.2'
);
});
it
(
'should set auto decimals, 2 significant'
,
()
=>
{
const
value
=
'0.0245'
;
const
instance
=
getDisplayProcessor
({
decimals
:
null
});
expect
(
instance
(
value
).
text
).
toEqual
(
'0.02'
);
});
it
(
'should return mapped value if there are matching value mappings'
,
()
=>
{
it
(
'should return mapped value if there are matching value mappings'
,
()
=>
{
const
valueMappings
:
ValueMapping
[]
=
[
const
valueMappings
:
ValueMapping
[]
=
[
{
id
:
0
,
operator
:
''
,
text
:
'1-20'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'20'
},
{
id
:
0
,
operator
:
''
,
text
:
'1-20'
,
type
:
MappingType
.
RangeToText
,
from
:
'1'
,
to
:
'20'
},
...
@@ -155,3 +169,18 @@ describe('Format value', () => {
...
@@ -155,3 +169,18 @@ describe('Format value', () => {
expect
(
instance
(
value
).
text
).
toEqual
(
'1-20'
);
expect
(
instance
(
value
).
text
).
toEqual
(
'1-20'
);
});
});
});
});
describe
(
'getDecimalsForValue()'
,
()
=>
{
it
(
'should calculate reasonable decimals precision for given value'
,
()
=>
{
expect
(
getDecimalsForValue
(
1.01
)).
toEqual
({
decimals
:
1
,
scaledDecimals
:
4
});
expect
(
getDecimalsForValue
(
9.01
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
2
});
expect
(
getDecimalsForValue
(
1.1
)).
toEqual
({
decimals
:
1
,
scaledDecimals
:
4
});
expect
(
getDecimalsForValue
(
2
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
2
});
expect
(
getDecimalsForValue
(
20
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
1
});
expect
(
getDecimalsForValue
(
200
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
0
});
expect
(
getDecimalsForValue
(
2000
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
0
});
expect
(
getDecimalsForValue
(
20000
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
-
2
});
expect
(
getDecimalsForValue
(
200000
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
-
3
});
expect
(
getDecimalsForValue
(
200000000
)).
toEqual
({
decimals
:
0
,
scaledDecimals
:
-
6
});
});
});
packages/grafana-ui/src/utils/displayValue.ts
View file @
a6c2a8c3
import
{
ValueMapping
,
Threshold
}
from
'../types'
;
// Libraries
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
{
getValueFormat
,
DecimalCount
}
from
'./valueFormats/valueFormats'
;
import
moment
from
'moment'
;
// Utils
import
{
getValueFormat
}
from
'./valueFormats/valueFormats'
;
import
{
getMappedValue
}
from
'./valueMappings'
;
import
{
getMappedValue
}
from
'./valueMappings'
;
import
{
GrafanaTheme
,
GrafanaThemeType
}
from
'../types'
;
import
{
getColorFromHexRgbOrName
}
from
'./namedColorsPalette'
;
import
{
getColorFromHexRgbOrName
}
from
'./namedColorsPalette'
;
import
moment
from
'moment'
;
export
interface
DisplayValue
{
// Types
text
:
string
;
// Show in the UI
import
{
Threshold
,
ValueMapping
,
DecimalInfo
,
DisplayValue
,
GrafanaTheme
,
GrafanaThemeType
}
from
'../types'
;
numeric
:
number
;
// Use isNaN to check if it is a real number
import
{
DecimalCount
}
from
'./valueFormats/valueFormats'
;
color
?:
string
;
// color based on configs or Threshold
}
export
type
DisplayProcessor
=
(
value
:
any
)
=>
DisplayValue
;
export
interface
DisplayValueOptions
{
export
interface
DisplayValueOptions
{
unit
?:
string
;
unit
?:
string
;
decimals
?:
DecimalCount
;
decimals
?:
DecimalCount
;
scaledDecimals
?:
DecimalCount
;
dateFormat
?:
string
;
// If set try to convert numbers to date
dateFormat
?:
string
;
// If set try to convert numbers to date
color
?:
string
;
color
?:
string
;
...
@@ -32,11 +32,10 @@ export interface DisplayValueOptions {
...
@@ -32,11 +32,10 @@ export interface DisplayValueOptions {
theme
?:
GrafanaTheme
;
// Will pick 'dark' if not defined
theme
?:
GrafanaTheme
;
// Will pick 'dark' if not defined
}
}
export
type
DisplayProcessor
=
(
value
:
any
)
=>
DisplayValue
;
export
function
getDisplayProcessor
(
options
?:
DisplayValueOptions
):
DisplayProcessor
{
export
function
getDisplayProcessor
(
options
?:
DisplayValueOptions
):
DisplayProcessor
{
if
(
options
&&
!
_
.
isEmpty
(
options
))
{
if
(
options
&&
!
_
.
isEmpty
(
options
))
{
const
formatFunc
=
getValueFormat
(
options
.
unit
||
'none'
);
const
formatFunc
=
getValueFormat
(
options
.
unit
||
'none'
);
return
(
value
:
any
)
=>
{
return
(
value
:
any
)
=>
{
const
{
prefix
,
suffix
,
mappings
,
thresholds
,
theme
}
=
options
;
const
{
prefix
,
suffix
,
mappings
,
thresholds
,
theme
}
=
options
;
let
color
=
options
.
color
;
let
color
=
options
.
color
;
...
@@ -47,12 +46,15 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
...
@@ -47,12 +46,15 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
let
shouldFormat
=
true
;
let
shouldFormat
=
true
;
if
(
mappings
&&
mappings
.
length
>
0
)
{
if
(
mappings
&&
mappings
.
length
>
0
)
{
const
mappedValue
=
getMappedValue
(
mappings
,
value
);
const
mappedValue
=
getMappedValue
(
mappings
,
value
);
if
(
mappedValue
)
{
if
(
mappedValue
)
{
text
=
mappedValue
.
text
;
text
=
mappedValue
.
text
;
const
v
=
toNumber
(
text
);
const
v
=
toNumber
(
text
);
if
(
!
isNaN
(
v
))
{
if
(
!
isNaN
(
v
))
{
numeric
=
v
;
numeric
=
v
;
}
}
shouldFormat
=
false
;
shouldFormat
=
false
;
}
}
}
}
...
@@ -67,7 +69,19 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
...
@@ -67,7 +69,19 @@ export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProce
if
(
!
isNaN
(
numeric
))
{
if
(
!
isNaN
(
numeric
))
{
if
(
shouldFormat
&&
!
_
.
isBoolean
(
value
))
{
if
(
shouldFormat
&&
!
_
.
isBoolean
(
value
))
{
text
=
formatFunc
(
numeric
,
options
.
decimals
,
options
.
scaledDecimals
,
options
.
isUtc
);
let
decimals
;
let
scaledDecimals
=
0
;
if
(
!
options
.
decimals
)
{
const
decimalInfo
=
getDecimalsForValue
(
value
);
decimals
=
decimalInfo
.
decimals
;
scaledDecimals
=
decimalInfo
.
scaledDecimals
;
}
else
{
decimals
=
options
.
decimals
;
}
text
=
formatFunc
(
numeric
,
decimals
,
scaledDecimals
,
options
.
isUtc
);
}
}
if
(
thresholds
&&
thresholds
.
length
>
0
)
{
if
(
thresholds
&&
thresholds
.
length
>
0
)
{
color
=
getColorFromThreshold
(
numeric
,
thresholds
,
theme
);
color
=
getColorFromThreshold
(
numeric
,
thresholds
,
theme
);
...
@@ -143,3 +157,39 @@ export function getColorFromThreshold(value: number, thresholds: Threshold[], th
...
@@ -143,3 +157,39 @@ export function getColorFromThreshold(value: number, thresholds: Threshold[], th
// Use the first threshold as the default color
// Use the first threshold as the default color
return
getColorFromHexRgbOrName
(
thresholds
[
0
].
color
,
themeType
);
return
getColorFromHexRgbOrName
(
thresholds
[
0
].
color
,
themeType
);
}
}
export
function
getDecimalsForValue
(
value
:
number
):
DecimalInfo
{
const
delta
=
value
/
2
;
let
dec
=
-
Math
.
floor
(
Math
.
log
(
delta
)
/
Math
.
LN10
);
const
magn
=
Math
.
pow
(
10
,
-
dec
);
const
norm
=
delta
/
magn
;
// norm is between 1.0 and 10.0
let
size
;
if
(
norm
<
1.5
)
{
size
=
1
;
}
else
if
(
norm
<
3
)
{
size
=
2
;
// special case for 2.5, requires an extra decimal
if
(
norm
>
2.25
)
{
size
=
2.5
;
++
dec
;
}
}
else
if
(
norm
<
7.5
)
{
size
=
5
;
}
else
{
size
=
10
;
}
size
*=
magn
;
// reduce starting decimals if not needed
if
(
Math
.
floor
(
value
)
===
value
)
{
dec
=
0
;
}
const
decimals
=
Math
.
max
(
0
,
dec
);
const
scaledDecimals
=
decimals
-
Math
.
floor
(
Math
.
log
(
size
)
/
Math
.
LN10
)
+
2
;
return
{
decimals
,
scaledDecimals
};
}
public/app/core/actions/location.ts
View file @
a6c2a8c3
import
{
LocationUpdate
}
from
'app/types'
;
import
{
LocationUpdate
}
from
'app/types'
;
import
{
actionCreatorFactory
}
from
'app/core/redux'
;
export
enum
CoreActionTypes
{
export
const
updateLocation
=
actionCreatorFactory
<
LocationUpdate
>
(
'UPDATE_LOCATION'
).
create
();
UpdateLocation
=
'UPDATE_LOCATION'
,
}
export
type
Action
=
UpdateLocationAction
;
export
interface
UpdateLocationAction
{
type
:
CoreActionTypes
.
UpdateLocation
;
payload
:
LocationUpdate
;
}
export
const
updateLocation
=
(
location
:
LocationUpdate
):
UpdateLocationAction
=>
({
type
:
CoreActionTypes
.
UpdateLocation
,
payload
:
location
,
});
public/app/core/reducers/location.ts
View file @
a6c2a8c3
import
{
Action
,
CoreActionTypes
}
from
'app/core/actions/location'
;
import
{
LocationState
}
from
'app/types'
;
import
{
LocationState
}
from
'app/types'
;
import
{
renderUrl
}
from
'app/core/utils/url'
;
import
{
renderUrl
}
from
'app/core/utils/url'
;
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
{
reducerFactory
}
from
'app/core/redux'
;
import
{
updateLocation
}
from
'app/core/actions'
;
export
const
initialState
:
LocationState
=
{
export
const
initialState
:
LocationState
=
{
url
:
''
,
url
:
''
,
...
@@ -12,9 +13,10 @@ export const initialState: LocationState = {
...
@@ -12,9 +13,10 @@ export const initialState: LocationState = {
lastUpdated
:
0
,
lastUpdated
:
0
,
};
};
export
const
locationReducer
=
(
state
=
initialState
,
action
:
Action
):
LocationState
=>
{
export
const
locationReducer
=
reducerFactory
<
LocationState
>
(
initialState
)
switch
(
action
.
type
)
{
.
addMapper
({
case
CoreActionTypes
.
UpdateLocation
:
{
filter
:
updateLocation
,
mapper
:
(
state
,
action
):
LocationState
=>
{
const
{
path
,
routeParams
,
replace
}
=
action
.
payload
;
const
{
path
,
routeParams
,
replace
}
=
action
.
payload
;
let
query
=
action
.
payload
.
query
||
state
.
query
;
let
query
=
action
.
payload
.
query
||
state
.
query
;
...
@@ -31,8 +33,6 @@ export const locationReducer = (state = initialState, action: Action): LocationS
...
@@ -31,8 +33,6 @@ export const locationReducer = (state = initialState, action: Action): LocationS
replace
:
replace
===
true
,
replace
:
replace
===
true
,
lastUpdated
:
new
Date
().
getTime
(),
lastUpdated
:
new
Date
().
getTime
(),
};
};
}
},
}
})
.
create
();
return
state
;
};
public/app/core/redux/actionCreatorFactory.ts
View file @
a6c2a8c3
...
@@ -68,5 +68,9 @@ export const getNoPayloadActionCreatorMock = (creator: NoPayloadActionCreator):
...
@@ -68,5 +68,9 @@ export const getNoPayloadActionCreatorMock = (creator: NoPayloadActionCreator):
return
mock
;
return
mock
;
};
};
export
const
mockActionCreator
=
(
creator
:
ActionCreator
<
any
>
)
=>
{
return
Object
.
assign
(
jest
.
fn
(),
creator
);
};
// Should only be used by tests
// Should only be used by tests
export
const
resetAllActionCreatorTypes
=
()
=>
(
allActionCreators
.
length
=
0
);
export
const
resetAllActionCreatorTypes
=
()
=>
(
allActionCreators
.
length
=
0
);
public/app/features/alerting/AlertRuleList.test.tsx
View file @
a6c2a8c3
...
@@ -3,6 +3,8 @@ import { shallow } from 'enzyme';
...
@@ -3,6 +3,8 @@ import { shallow } from 'enzyme';
import
{
AlertRuleList
,
Props
}
from
'./AlertRuleList'
;
import
{
AlertRuleList
,
Props
}
from
'./AlertRuleList'
;
import
{
AlertRule
,
NavModel
}
from
'../../types'
;
import
{
AlertRule
,
NavModel
}
from
'../../types'
;
import
appEvents
from
'../../core/app_events'
;
import
appEvents
from
'../../core/app_events'
;
import
{
mockActionCreator
}
from
'app/core/redux'
;
import
{
updateLocation
}
from
'app/core/actions'
;
jest
.
mock
(
'../../core/app_events'
,
()
=>
({
jest
.
mock
(
'../../core/app_events'
,
()
=>
({
emit
:
jest
.
fn
(),
emit
:
jest
.
fn
(),
...
@@ -12,7 +14,7 @@ const setup = (propOverrides?: object) => {
...
@@ -12,7 +14,7 @@ const setup = (propOverrides?: object) => {
const
props
:
Props
=
{
const
props
:
Props
=
{
navModel
:
{}
as
NavModel
,
navModel
:
{}
as
NavModel
,
alertRules
:
[]
as
AlertRule
[],
alertRules
:
[]
as
AlertRule
[],
updateLocation
:
jest
.
fn
(
),
updateLocation
:
mockActionCreator
(
updateLocation
),
getAlertRulesAsync
:
jest
.
fn
(),
getAlertRulesAsync
:
jest
.
fn
(),
setSearchQuery
:
jest
.
fn
(),
setSearchQuery
:
jest
.
fn
(),
togglePauseAlertRule
:
jest
.
fn
(),
togglePauseAlertRule
:
jest
.
fn
(),
...
...
public/app/features/annotations/all.ts
View file @
a6c2a8c3
import
{
AnnotationsSrv
}
from
'./annotations_srv'
;
import
{
AnnotationsSrv
}
from
'./annotations_srv'
;
import
{
eventEditor
}
from
'./event_editor'
;
import
{
eventEditor
}
from
'./event_editor'
;
import
{
EventManager
}
from
'./event_manager'
;
import
{
EventManager
}
from
'./event_manager'
;
import
{
AnnotationEvent
}
from
'./event'
;
import
{
annotationTooltipDirective
}
from
'./annotation_tooltip'
;
import
{
annotationTooltipDirective
}
from
'./annotation_tooltip'
;
export
{
AnnotationsSrv
,
eventEditor
,
EventManager
,
annotationTooltipDirective
};
export
{
AnnotationsSrv
,
eventEditor
,
EventManager
,
AnnotationEvent
,
annotationTooltipDirective
};
public/app/features/annotations/event.ts
deleted
100644 → 0
View file @
beba9676
export
class
AnnotationEvent
{
dashboardId
:
number
;
panelId
:
number
;
userId
:
number
;
time
:
any
;
timeEnd
:
any
;
isRegion
:
boolean
;
text
:
string
;
type
:
string
;
tags
:
string
;
}
public/app/features/annotations/event_editor.ts
View file @
a6c2a8c3
...
@@ -2,7 +2,7 @@ import _ from 'lodash';
...
@@ -2,7 +2,7 @@ import _ from 'lodash';
import
moment
from
'moment'
;
import
moment
from
'moment'
;
import
{
coreModule
}
from
'app/core/core'
;
import
{
coreModule
}
from
'app/core/core'
;
import
{
MetricsPanelCtrl
}
from
'app/plugins/sdk'
;
import
{
MetricsPanelCtrl
}
from
'app/plugins/sdk'
;
import
{
AnnotationEvent
}
from
'
./event
'
;
import
{
AnnotationEvent
}
from
'
@grafana/ui
'
;
export
class
EventEditorCtrl
{
export
class
EventEditorCtrl
{
panelCtrl
:
MetricsPanelCtrl
;
panelCtrl
:
MetricsPanelCtrl
;
...
...
public/app/features/annotations/event_manager.ts
View file @
a6c2a8c3
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
import
tinycolor
from
'tinycolor2'
;
import
tinycolor
from
'tinycolor2'
;
import
{
import
{
OK_COLOR
,
OK_COLOR
,
...
@@ -11,7 +10,7 @@ import {
...
@@ -11,7 +10,7 @@ import {
}
from
'@grafana/ui'
;
}
from
'@grafana/ui'
;
import
{
MetricsPanelCtrl
}
from
'app/plugins/sdk'
;
import
{
MetricsPanelCtrl
}
from
'app/plugins/sdk'
;
import
{
AnnotationEvent
}
from
'
./event
'
;
import
{
AnnotationEvent
}
from
'
@grafana/ui
'
;
export
class
EventManager
{
export
class
EventManager
{
event
:
AnnotationEvent
;
event
:
AnnotationEvent
;
...
@@ -31,16 +30,17 @@ export class EventManager {
...
@@ -31,16 +30,17 @@ export class EventManager {
updateTime
(
range
)
{
updateTime
(
range
)
{
if
(
!
this
.
event
)
{
if
(
!
this
.
event
)
{
this
.
event
=
new
AnnotationEvent
()
;
this
.
event
=
{}
;
this
.
event
.
dashboardId
=
this
.
panelCtrl
.
dashboard
.
id
;
this
.
event
.
dashboardId
=
this
.
panelCtrl
.
dashboard
.
id
;
this
.
event
.
panelId
=
this
.
panelCtrl
.
panel
.
id
;
this
.
event
.
panelId
=
this
.
panelCtrl
.
panel
.
id
;
}
}
// update time
// update time
this
.
event
.
time
=
moment
(
range
.
from
)
;
this
.
event
.
time
=
range
.
from
;
this
.
event
.
isRegion
=
false
;
this
.
event
.
isRegion
=
false
;
if
(
range
.
to
)
{
if
(
range
.
to
)
{
this
.
event
.
timeEnd
=
moment
(
range
.
to
)
;
this
.
event
.
timeEnd
=
range
.
to
;
this
.
event
.
isRegion
=
true
;
this
.
event
.
isRegion
=
true
;
}
}
...
@@ -90,8 +90,8 @@ export class EventManager {
...
@@ -90,8 +90,8 @@ export class EventManager {
annotations
=
[
annotations
=
[
{
{
isRegion
:
true
,
isRegion
:
true
,
min
:
this
.
event
.
time
.
valueOf
()
,
min
:
this
.
event
.
time
,
timeEnd
:
this
.
event
.
timeEnd
.
valueOf
()
,
timeEnd
:
this
.
event
.
timeEnd
,
text
:
this
.
event
.
text
,
text
:
this
.
event
.
text
,
eventType
:
'$__editing'
,
eventType
:
'$__editing'
,
editModel
:
this
.
event
,
editModel
:
this
.
event
,
...
@@ -100,7 +100,7 @@ export class EventManager {
...
@@ -100,7 +100,7 @@ export class EventManager {
}
else
{
}
else
{
annotations
=
[
annotations
=
[
{
{
min
:
this
.
event
.
time
.
valueOf
()
,
min
:
this
.
event
.
time
,
text
:
this
.
event
.
text
,
text
:
this
.
event
.
text
,
editModel
:
this
.
event
,
editModel
:
this
.
event
,
eventType
:
'$__editing'
,
eventType
:
'$__editing'
,
...
...
public/app/features/dashboard/containers/DashboardPage.test.tsx
View file @
a6c2a8c3
...
@@ -3,8 +3,9 @@ import { shallow, ShallowWrapper } from 'enzyme';
...
@@ -3,8 +3,9 @@ import { shallow, ShallowWrapper } from 'enzyme';
import
{
DashboardPage
,
Props
,
State
,
mapStateToProps
}
from
'./DashboardPage'
;
import
{
DashboardPage
,
Props
,
State
,
mapStateToProps
}
from
'./DashboardPage'
;
import
{
DashboardModel
}
from
'../state'
;
import
{
DashboardModel
}
from
'../state'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
getNoPayloadActionCreatorMock
,
NoPayloadActionCreatorMock
}
from
'app/core/redux'
;
import
{
getNoPayloadActionCreatorMock
,
NoPayloadActionCreatorMock
,
mockActionCreator
}
from
'app/core/redux'
;
import
{
DashboardRouteInfo
,
DashboardInitPhase
}
from
'app/types'
;
import
{
DashboardRouteInfo
,
DashboardInitPhase
}
from
'app/types'
;
import
{
updateLocation
}
from
'app/core/actions'
;
jest
.
mock
(
'app/features/dashboard/components/DashboardSettings/SettingsCtrl'
,
()
=>
({}));
jest
.
mock
(
'app/features/dashboard/components/DashboardSettings/SettingsCtrl'
,
()
=>
({}));
...
@@ -62,7 +63,7 @@ function dashboardPageScenario(description, scenarioFn: (ctx: ScenarioContext) =
...
@@ -62,7 +63,7 @@ function dashboardPageScenario(description, scenarioFn: (ctx: ScenarioContext) =
initPhase
:
DashboardInitPhase
.
NotStarted
,
initPhase
:
DashboardInitPhase
.
NotStarted
,
isInitSlow
:
false
,
isInitSlow
:
false
,
initDashboard
:
jest
.
fn
(),
initDashboard
:
jest
.
fn
(),
updateLocation
:
jest
.
fn
(
),
updateLocation
:
mockActionCreator
(
updateLocation
),
notifyApp
:
jest
.
fn
(),
notifyApp
:
jest
.
fn
(),
cleanUpDashboard
:
ctx
.
cleanUpDashboardMock
,
cleanUpDashboard
:
ctx
.
cleanUpDashboardMock
,
dashboard
:
null
,
dashboard
:
null
,
...
...
public/app/features/datasources/state/actions.ts
View file @
a6c2a8c3
...
@@ -4,10 +4,9 @@ import { getBackendSrv } from 'app/core/services/backend_srv';
...
@@ -4,10 +4,9 @@ import { getBackendSrv } from 'app/core/services/backend_srv';
import
{
getDatasourceSrv
}
from
'app/features/plugins/datasource_srv'
;
import
{
getDatasourceSrv
}
from
'app/features/plugins/datasource_srv'
;
import
{
LayoutMode
}
from
'app/core/components/LayoutSelector/LayoutSelector'
;
import
{
LayoutMode
}
from
'app/core/components/LayoutSelector/LayoutSelector'
;
import
{
updateLocation
,
updateNavIndex
,
UpdateNavIndexAction
}
from
'app/core/actions'
;
import
{
updateLocation
,
updateNavIndex
,
UpdateNavIndexAction
}
from
'app/core/actions'
;
import
{
UpdateLocationAction
}
from
'app/core/actions/location'
;
import
{
buildNavModel
}
from
'./navModel'
;
import
{
buildNavModel
}
from
'./navModel'
;
import
{
DataSourceSettings
}
from
'@grafana/ui/src/types'
;
import
{
DataSourceSettings
}
from
'@grafana/ui/src/types'
;
import
{
Plugin
,
StoreState
}
from
'app/types'
;
import
{
Plugin
,
StoreState
,
LocationUpdate
}
from
'app/types'
;
import
{
actionCreatorFactory
}
from
'app/core/redux'
;
import
{
actionCreatorFactory
}
from
'app/core/redux'
;
import
{
ActionOf
,
noPayloadActionCreatorFactory
}
from
'app/core/redux/actionCreatorFactory'
;
import
{
ActionOf
,
noPayloadActionCreatorFactory
}
from
'app/core/redux/actionCreatorFactory'
;
...
@@ -32,12 +31,12 @@ export const setDataSourceName = actionCreatorFactory<string>('SET_DATA_SOURCE_N
...
@@ -32,12 +31,12 @@ export const setDataSourceName = actionCreatorFactory<string>('SET_DATA_SOURCE_N
export
const
setIsDefault
=
actionCreatorFactory
<
boolean
>
(
'SET_IS_DEFAULT'
).
create
();
export
const
setIsDefault
=
actionCreatorFactory
<
boolean
>
(
'SET_IS_DEFAULT'
).
create
();
export
type
Action
=
export
type
Action
=
|
UpdateLocationAction
|
UpdateNavIndexAction
|
UpdateNavIndexAction
|
ActionOf
<
DataSourceSettings
>
|
ActionOf
<
DataSourceSettings
>
|
ActionOf
<
DataSourceSettings
[]
>
|
ActionOf
<
DataSourceSettings
[]
>
|
ActionOf
<
Plugin
>
|
ActionOf
<
Plugin
>
|
ActionOf
<
Plugin
[]
>
;
|
ActionOf
<
Plugin
[]
>
|
ActionOf
<
LocationUpdate
>
;
type
ThunkResult
<
R
>
=
ThunkAction
<
R
,
StoreState
,
undefined
,
Action
>
;
type
ThunkResult
<
R
>
=
ThunkAction
<
R
,
StoreState
,
undefined
,
Action
>
;
...
...
public/app/features/explore/Explore.tsx
View file @
a6c2a8c3
// Libraries
// Libraries
import
React
,
{
ComponentClass
}
from
'react'
;
import
React
,
{
ComponentClass
}
from
'react'
;
import
{
hot
}
from
'react-hot-loader'
;
import
{
hot
}
from
'react-hot-loader'
;
// @ts-ignore
import
{
connect
}
from
'react-redux'
;
import
{
connect
}
from
'react-redux'
;
// @ts-ignore
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
{
AutoSizer
}
from
'react-virtualized'
;
import
{
AutoSizer
}
from
'react-virtualized'
;
...
@@ -18,11 +20,19 @@ import TableContainer from './TableContainer';
...
@@ -18,11 +20,19 @@ import TableContainer from './TableContainer';
import
TimePicker
,
{
parseTime
}
from
'./TimePicker'
;
import
TimePicker
,
{
parseTime
}
from
'./TimePicker'
;
// Actions
// Actions
import
{
changeSize
,
changeTime
,
initializeExplore
,
modifyQueries
,
scanStart
,
setQueries
}
from
'./state/actions'
;
import
{
changeSize
,
changeTime
,
initializeExplore
,
modifyQueries
,
scanStart
,
setQueries
,
refreshExplore
,
}
from
'./state/actions'
;
// Types
// Types
import
{
RawTimeRange
,
TimeRange
,
DataQuery
,
ExploreStartPageProps
,
ExploreDataSourceApi
}
from
'@grafana/ui'
;
import
{
RawTimeRange
,
TimeRange
,
DataQuery
,
ExploreStartPageProps
,
ExploreDataSourceApi
}
from
'@grafana/ui'
;
import
{
ExploreItemState
,
ExploreUrlState
,
RangeScanner
,
ExploreId
}
from
'app/types/explore'
;
import
{
ExploreItemState
,
ExploreUrlState
,
RangeScanner
,
ExploreId
,
ExploreUpdateState
}
from
'app/types/explore'
;
import
{
StoreState
}
from
'app/types'
;
import
{
StoreState
}
from
'app/types'
;
import
{
LAST_USED_DATASOURCE_KEY
,
ensureQueries
,
DEFAULT_RANGE
,
DEFAULT_UI_STATE
}
from
'app/core/utils/explore'
;
import
{
LAST_USED_DATASOURCE_KEY
,
ensureQueries
,
DEFAULT_RANGE
,
DEFAULT_UI_STATE
}
from
'app/core/utils/explore'
;
import
{
Emitter
}
from
'app/core/utils/emitter'
;
import
{
Emitter
}
from
'app/core/utils/emitter'
;
...
@@ -42,6 +52,8 @@ interface ExploreProps {
...
@@ -42,6 +52,8 @@ interface ExploreProps {
initialized
:
boolean
;
initialized
:
boolean
;
modifyQueries
:
typeof
modifyQueries
;
modifyQueries
:
typeof
modifyQueries
;
range
:
RawTimeRange
;
range
:
RawTimeRange
;
update
:
ExploreUpdateState
;
refreshExplore
:
typeof
refreshExplore
;
scanner
?:
RangeScanner
;
scanner
?:
RangeScanner
;
scanning
?:
boolean
;
scanning
?:
boolean
;
scanRange
?:
RawTimeRange
;
scanRange
?:
RawTimeRange
;
...
@@ -53,8 +65,8 @@ interface ExploreProps {
...
@@ -53,8 +65,8 @@ interface ExploreProps {
supportsGraph
:
boolean
|
null
;
supportsGraph
:
boolean
|
null
;
supportsLogs
:
boolean
|
null
;
supportsLogs
:
boolean
|
null
;
supportsTable
:
boolean
|
null
;
supportsTable
:
boolean
|
null
;
urlState
:
ExploreUrlState
;
queryKeys
:
string
[];
queryKeys
:
string
[];
urlState
:
ExploreUrlState
;
}
}
/**
/**
...
@@ -89,23 +101,22 @@ export class Explore extends React.PureComponent<ExploreProps> {
...
@@ -89,23 +101,22 @@ export class Explore extends React.PureComponent<ExploreProps> {
*/
*/
timepickerRef
:
React
.
RefObject
<
TimePicker
>
;
timepickerRef
:
React
.
RefObject
<
TimePicker
>
;
constructor
(
props
)
{
constructor
(
props
:
ExploreProps
)
{
super
(
props
);
super
(
props
);
this
.
exploreEvents
=
new
Emitter
();
this
.
exploreEvents
=
new
Emitter
();
this
.
timepickerRef
=
React
.
createRef
();
this
.
timepickerRef
=
React
.
createRef
();
}
}
async
componentDidMount
()
{
componentDidMount
()
{
const
{
exploreId
,
initialized
,
urlState
}
=
this
.
props
;
const
{
exploreId
,
urlState
,
initialized
}
=
this
.
props
;
// Don't initialize on split, but need to initialize urlparameters when present
const
{
datasource
,
queries
,
range
=
DEFAULT_RANGE
,
ui
=
DEFAULT_UI_STATE
}
=
(
urlState
||
{})
as
ExploreUrlState
;
if
(
!
initialized
)
{
const
initialDatasource
=
datasource
||
store
.
get
(
LAST_USED_DATASOURCE_KEY
);
// Load URL state and parse range
const
initialQueries
:
DataQuery
[]
=
ensureQueries
(
queries
);
const
{
datasource
,
queries
,
range
=
DEFAULT_RANGE
,
ui
=
DEFAULT_UI_STATE
}
=
(
urlState
||
{})
as
ExploreUrlState
;
const
initialRange
=
{
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
)
};
const
initialDatasource
=
datasource
||
store
.
get
(
LAST_USED_DATASOURCE_KEY
);
const
width
=
this
.
el
?
this
.
el
.
offsetWidth
:
0
;
const
initialQueries
:
DataQuery
[]
=
ensureQueries
(
queries
);
const
initialRange
=
{
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
)
};
const
width
=
this
.
el
?
this
.
el
.
offsetWidth
:
0
;
// initialize the whole explore first time we mount and if browser history contains a change in datasource
if
(
!
initialized
)
{
this
.
props
.
initializeExplore
(
this
.
props
.
initializeExplore
(
exploreId
,
exploreId
,
initialDatasource
,
initialDatasource
,
...
@@ -122,7 +133,11 @@ export class Explore extends React.PureComponent<ExploreProps> {
...
@@ -122,7 +133,11 @@ export class Explore extends React.PureComponent<ExploreProps> {
this
.
exploreEvents
.
removeAllListeners
();
this
.
exploreEvents
.
removeAllListeners
();
}
}
getRef
=
el
=>
{
componentDidUpdate
(
prevProps
:
ExploreProps
)
{
this
.
refreshExplore
();
}
getRef
=
(
el
:
any
)
=>
{
this
.
el
=
el
;
this
.
el
=
el
;
};
};
...
@@ -142,7 +157,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
...
@@ -142,7 +157,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
this
.
onModifyQueries
({
type
:
'ADD_FILTER'
,
key
,
value
});
this
.
onModifyQueries
({
type
:
'ADD_FILTER'
,
key
,
value
});
};
};
onModifyQueries
=
(
action
,
index
?:
number
)
=>
{
onModifyQueries
=
(
action
:
any
,
index
?:
number
)
=>
{
const
{
datasourceInstance
}
=
this
.
props
;
const
{
datasourceInstance
}
=
this
.
props
;
if
(
datasourceInstance
&&
datasourceInstance
.
modifyQuery
)
{
if
(
datasourceInstance
&&
datasourceInstance
.
modifyQuery
)
{
const
modifier
=
(
queries
:
DataQuery
,
modification
:
any
)
=>
datasourceInstance
.
modifyQuery
(
queries
,
modification
);
const
modifier
=
(
queries
:
DataQuery
,
modification
:
any
)
=>
datasourceInstance
.
modifyQuery
(
queries
,
modification
);
...
@@ -169,6 +184,14 @@ export class Explore extends React.PureComponent<ExploreProps> {
...
@@ -169,6 +184,14 @@ export class Explore extends React.PureComponent<ExploreProps> {
this
.
props
.
scanStopAction
({
exploreId
:
this
.
props
.
exploreId
});
this
.
props
.
scanStopAction
({
exploreId
:
this
.
props
.
exploreId
});
};
};
refreshExplore
=
()
=>
{
const
{
exploreId
,
update
}
=
this
.
props
;
if
(
update
.
queries
||
update
.
ui
||
update
.
range
||
update
.
datasource
)
{
this
.
props
.
refreshExplore
(
exploreId
);
}
};
render
()
{
render
()
{
const
{
const
{
StartPage
,
StartPage
,
...
@@ -241,7 +264,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
...
@@ -241,7 +264,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
}
}
}
}
function
mapStateToProps
(
state
:
StoreState
,
{
exploreId
})
{
function
mapStateToProps
(
state
:
StoreState
,
{
exploreId
}
:
ExploreProps
)
{
const
explore
=
state
.
explore
;
const
explore
=
state
.
explore
;
const
{
split
}
=
explore
;
const
{
split
}
=
explore
;
const
item
:
ExploreItemState
=
explore
[
exploreId
];
const
item
:
ExploreItemState
=
explore
[
exploreId
];
...
@@ -258,6 +281,8 @@ function mapStateToProps(state: StoreState, { exploreId }) {
...
@@ -258,6 +281,8 @@ function mapStateToProps(state: StoreState, { exploreId }) {
supportsLogs
,
supportsLogs
,
supportsTable
,
supportsTable
,
queryKeys
,
queryKeys
,
urlState
,
update
,
}
=
item
;
}
=
item
;
return
{
return
{
StartPage
,
StartPage
,
...
@@ -273,6 +298,8 @@ function mapStateToProps(state: StoreState, { exploreId }) {
...
@@ -273,6 +298,8 @@ function mapStateToProps(state: StoreState, { exploreId }) {
supportsLogs
,
supportsLogs
,
supportsTable
,
supportsTable
,
queryKeys
,
queryKeys
,
urlState
,
update
,
};
};
}
}
...
@@ -281,6 +308,7 @@ const mapDispatchToProps = {
...
@@ -281,6 +308,7 @@ const mapDispatchToProps = {
changeTime
,
changeTime
,
initializeExplore
,
initializeExplore
,
modifyQueries
,
modifyQueries
,
refreshExplore
,
scanStart
,
scanStart
,
scanStopAction
,
scanStopAction
,
setQueries
,
setQueries
,
...
...
public/app/features/explore/Wrapper.tsx
View file @
a6c2a8c3
...
@@ -2,65 +2,37 @@ import React, { Component } from 'react';
...
@@ -2,65 +2,37 @@ import React, { Component } from 'react';
import
{
hot
}
from
'react-hot-loader'
;
import
{
hot
}
from
'react-hot-loader'
;
import
{
connect
}
from
'react-redux'
;
import
{
connect
}
from
'react-redux'
;
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
StoreState
}
from
'app/types'
;
import
{
StoreState
}
from
'app/types'
;
import
{
ExploreId
,
ExploreUrlState
}
from
'app/types/explore'
;
import
{
ExploreId
}
from
'app/types/explore'
;
import
{
parseUrlState
}
from
'app/core/utils/explore'
;
import
ErrorBoundary
from
'./ErrorBoundary'
;
import
ErrorBoundary
from
'./ErrorBoundary'
;
import
Explore
from
'./Explore'
;
import
Explore
from
'./Explore'
;
import
{
CustomScrollbar
}
from
'@grafana/ui'
;
import
{
CustomScrollbar
}
from
'@grafana/ui'
;
import
{
initializeExploreSplitAction
,
resetExploreAction
}
from
'./state/actionTypes'
;
import
{
resetExploreAction
}
from
'./state/actionTypes'
;
interface
WrapperProps
{
interface
WrapperProps
{
initializeExploreSplitAction
:
typeof
initializeExploreSplitAction
;
split
:
boolean
;
split
:
boolean
;
updateLocation
:
typeof
updateLocation
;
resetExploreAction
:
typeof
resetExploreAction
;
resetExploreAction
:
typeof
resetExploreAction
;
urlStates
:
{
[
key
:
string
]:
string
};
}
}
export
class
Wrapper
extends
Component
<
WrapperProps
>
{
export
class
Wrapper
extends
Component
<
WrapperProps
>
{
initialSplit
:
boolean
;
urlStates
:
{
[
key
:
string
]:
ExploreUrlState
};
constructor
(
props
:
WrapperProps
)
{
super
(
props
);
this
.
urlStates
=
{};
const
{
left
,
right
}
=
props
.
urlStates
;
if
(
props
.
urlStates
.
left
)
{
this
.
urlStates
.
leftState
=
parseUrlState
(
left
);
}
if
(
props
.
urlStates
.
right
)
{
this
.
urlStates
.
rightState
=
parseUrlState
(
right
);
this
.
initialSplit
=
true
;
}
}
componentDidMount
()
{
if
(
this
.
initialSplit
)
{
this
.
props
.
initializeExploreSplitAction
();
}
}
componentWillUnmount
()
{
componentWillUnmount
()
{
this
.
props
.
resetExploreAction
();
this
.
props
.
resetExploreAction
();
}
}
render
()
{
render
()
{
const
{
split
}
=
this
.
props
;
const
{
split
}
=
this
.
props
;
const
{
leftState
,
rightState
}
=
this
.
urlStates
;
return
(
return
(
<
div
className=
"page-scrollbar-wrapper"
>
<
div
className=
"page-scrollbar-wrapper"
>
<
CustomScrollbar
autoHeightMin=
{
'100%'
}
className=
"custom-scrollbar--page"
>
<
CustomScrollbar
autoHeightMin=
{
'100%'
}
className=
"custom-scrollbar--page"
>
<
div
className=
"explore-wrapper"
>
<
div
className=
"explore-wrapper"
>
<
ErrorBoundary
>
<
ErrorBoundary
>
<
Explore
exploreId=
{
ExploreId
.
left
}
urlState=
{
leftState
}
/>
<
Explore
exploreId=
{
ExploreId
.
left
}
/>
</
ErrorBoundary
>
</
ErrorBoundary
>
{
split
&&
(
{
split
&&
(
<
ErrorBoundary
>
<
ErrorBoundary
>
<
Explore
exploreId=
{
ExploreId
.
right
}
urlState=
{
rightState
}
/>
<
Explore
exploreId=
{
ExploreId
.
right
}
/>
</
ErrorBoundary
>
</
ErrorBoundary
>
)
}
)
}
</
div
>
</
div
>
...
@@ -71,14 +43,11 @@ export class Wrapper extends Component<WrapperProps> {
...
@@ -71,14 +43,11 @@ export class Wrapper extends Component<WrapperProps> {
}
}
const
mapStateToProps
=
(
state
:
StoreState
)
=>
{
const
mapStateToProps
=
(
state
:
StoreState
)
=>
{
const
urlStates
=
state
.
location
.
query
;
const
{
split
}
=
state
.
explore
;
const
{
split
}
=
state
.
explore
;
return
{
split
,
urlStates
};
return
{
split
};
};
};
const
mapDispatchToProps
=
{
const
mapDispatchToProps
=
{
initializeExploreSplitAction
,
updateLocation
,
resetExploreAction
,
resetExploreAction
,
};
};
...
...
public/app/features/explore/state/actionTypes.ts
View file @
a6c2a8c3
...
@@ -24,17 +24,11 @@ import { LogLevel } from 'app/core/logs_model';
...
@@ -24,17 +24,11 @@ import { LogLevel } from 'app/core/logs_model';
*
*
*/
*/
export
enum
ActionTypes
{
export
enum
ActionTypes
{
InitializeExploreSplit
=
'explore/INITIALIZE_EXPLORE_SPLIT'
,
SplitClose
=
'explore/SPLIT_CLOSE'
,
SplitClose
=
'explore/SPLIT_CLOSE'
,
SplitOpen
=
'explore/SPLIT_OPEN'
,
SplitOpen
=
'explore/SPLIT_OPEN'
,
ResetExplore
=
'explore/RESET_EXPLORE'
,
ResetExplore
=
'explore/RESET_EXPLORE'
,
}
}
export
interface
InitializeExploreSplitAction
{
type
:
ActionTypes
.
InitializeExploreSplit
;
payload
:
{};
}
export
interface
SplitCloseAction
{
export
interface
SplitCloseAction
{
type
:
ActionTypes
.
SplitClose
;
type
:
ActionTypes
.
SplitClose
;
payload
:
{};
payload
:
{};
...
@@ -154,10 +148,6 @@ export interface RemoveQueryRowPayload {
...
@@ -154,10 +148,6 @@ export interface RemoveQueryRowPayload {
index
:
number
;
index
:
number
;
}
}
export
interface
RunQueriesEmptyPayload
{
exploreId
:
ExploreId
;
}
export
interface
ScanStartPayload
{
export
interface
ScanStartPayload
{
exploreId
:
ExploreId
;
exploreId
:
ExploreId
;
scanner
:
RangeScanner
;
scanner
:
RangeScanner
;
...
@@ -260,11 +250,6 @@ export const initializeExploreAction = actionCreatorFactory<InitializeExplorePay
...
@@ -260,11 +250,6 @@ export const initializeExploreAction = actionCreatorFactory<InitializeExplorePay
).
create
();
).
create
();
/**
/**
* Initialize the wrapper split state
*/
export
const
initializeExploreSplitAction
=
noPayloadActionCreatorFactory
(
'explore/INITIALIZE_EXPLORE_SPLIT'
).
create
();
/**
* Display an error that happened during the selection of a datasource
* Display an error that happened during the selection of a datasource
*/
*/
export
const
loadDatasourceFailureAction
=
actionCreatorFactory
<
LoadDatasourceFailurePayload
>
(
export
const
loadDatasourceFailureAction
=
actionCreatorFactory
<
LoadDatasourceFailurePayload
>
(
...
@@ -342,7 +327,6 @@ export const queryTransactionSuccessAction = actionCreatorFactory<QueryTransacti
...
@@ -342,7 +327,6 @@ export const queryTransactionSuccessAction = actionCreatorFactory<QueryTransacti
*/
*/
export
const
removeQueryRowAction
=
actionCreatorFactory
<
RemoveQueryRowPayload
>
(
'explore/REMOVE_QUERY_ROW'
).
create
();
export
const
removeQueryRowAction
=
actionCreatorFactory
<
RemoveQueryRowPayload
>
(
'explore/REMOVE_QUERY_ROW'
).
create
();
export
const
runQueriesAction
=
noPayloadActionCreatorFactory
(
'explore/RUN_QUERIES'
).
create
();
export
const
runQueriesAction
=
noPayloadActionCreatorFactory
(
'explore/RUN_QUERIES'
).
create
();
export
const
runQueriesEmptyAction
=
actionCreatorFactory
<
RunQueriesEmptyPayload
>
(
'explore/RUN_QUERIES_EMPTY'
).
create
();
/**
/**
* Start a scan for more results using the given scanner.
* Start a scan for more results using the given scanner.
...
@@ -411,12 +395,7 @@ export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>(
...
@@ -411,12 +395,7 @@ export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>(
export
const
resetExploreAction
=
noPayloadActionCreatorFactory
(
'explore/RESET_EXPLORE'
).
create
();
export
const
resetExploreAction
=
noPayloadActionCreatorFactory
(
'explore/RESET_EXPLORE'
).
create
();
export
const
queriesImportedAction
=
actionCreatorFactory
<
QueriesImportedPayload
>
(
'explore/QueriesImported'
).
create
();
export
const
queriesImportedAction
=
actionCreatorFactory
<
QueriesImportedPayload
>
(
'explore/QueriesImported'
).
create
();
export
type
HigherOrderAction
=
export
type
HigherOrderAction
=
SplitCloseAction
|
SplitOpenAction
|
ResetExploreAction
|
ActionOf
<
any
>
;
|
InitializeExploreSplitAction
|
SplitCloseAction
|
SplitOpenAction
|
ResetExploreAction
|
ActionOf
<
any
>
;
export
type
Action
=
export
type
Action
=
|
ActionOf
<
AddQueryRowPayload
>
|
ActionOf
<
AddQueryRowPayload
>
...
@@ -435,7 +414,6 @@ export type Action =
...
@@ -435,7 +414,6 @@ export type Action =
|
ActionOf
<
QueryTransactionStartPayload
>
|
ActionOf
<
QueryTransactionStartPayload
>
|
ActionOf
<
QueryTransactionSuccessPayload
>
|
ActionOf
<
QueryTransactionSuccessPayload
>
|
ActionOf
<
RemoveQueryRowPayload
>
|
ActionOf
<
RemoveQueryRowPayload
>
|
ActionOf
<
RunQueriesEmptyPayload
>
|
ActionOf
<
ScanStartPayload
>
|
ActionOf
<
ScanStartPayload
>
|
ActionOf
<
ScanRangePayload
>
|
ActionOf
<
ScanRangePayload
>
|
ActionOf
<
SetQueriesPayload
>
|
ActionOf
<
SetQueriesPayload
>
...
...
public/app/features/explore/state/actions.test.ts
0 → 100644
View file @
a6c2a8c3
import
{
refreshExplore
}
from
'./actions'
;
import
{
ExploreId
,
ExploreUrlState
,
ExploreUpdateState
}
from
'app/types'
;
import
{
thunkTester
}
from
'test/core/thunk/thunkTester'
;
import
{
LogsDedupStrategy
}
from
'app/core/logs_model'
;
import
{
initializeExploreAction
,
InitializeExplorePayload
,
changeTimeAction
,
updateUIStateAction
,
setQueriesAction
,
}
from
'./actionTypes'
;
import
{
Emitter
}
from
'app/core/core'
;
import
{
ActionOf
}
from
'app/core/redux/actionCreatorFactory'
;
import
{
makeInitialUpdateState
}
from
'./reducers'
;
jest
.
mock
(
'app/features/plugins/datasource_srv'
,
()
=>
({
getDatasourceSrv
:
()
=>
({
getExternal
:
jest
.
fn
().
mockReturnValue
([]),
get
:
jest
.
fn
().
mockReturnValue
({
testDatasource
:
jest
.
fn
(),
init
:
jest
.
fn
(),
}),
}),
}));
const
setup
=
(
updateOverides
?:
Partial
<
ExploreUpdateState
>
)
=>
{
const
exploreId
=
ExploreId
.
left
;
const
containerWidth
=
1920
;
const
eventBridge
=
{}
as
Emitter
;
const
ui
=
{
dedupStrategy
:
LogsDedupStrategy
.
none
,
showingGraph
:
false
,
showingLogs
:
false
,
showingTable
:
false
};
const
range
=
{
from
:
'now'
,
to
:
'now'
};
const
urlState
:
ExploreUrlState
=
{
datasource
:
'some-datasource'
,
queries
:
[],
range
,
ui
};
const
updateDefaults
=
makeInitialUpdateState
();
const
update
=
{
...
updateDefaults
,
...
updateOverides
};
const
initialState
=
{
explore
:
{
[
exploreId
]:
{
initialized
:
true
,
urlState
,
containerWidth
,
eventBridge
,
update
,
datasourceInstance
:
{
name
:
'some-datasource'
},
queries
:
[],
range
,
ui
,
},
},
};
return
{
initialState
,
exploreId
,
range
,
ui
,
containerWidth
,
eventBridge
,
};
};
describe
(
'refreshExplore'
,
()
=>
{
describe
(
'when explore is initialized'
,
()
=>
{
describe
(
'and update datasource is set'
,
()
=>
{
it
(
'then it should dispatch initializeExplore'
,
()
=>
{
const
{
exploreId
,
ui
,
range
,
initialState
,
containerWidth
,
eventBridge
}
=
setup
({
datasource
:
true
});
thunkTester
(
initialState
)
.
givenThunk
(
refreshExplore
)
.
whenThunkIsDispatched
(
exploreId
)
.
thenDispatchedActionsAreEqual
(
dispatchedActions
=>
{
const
initializeExplore
=
dispatchedActions
[
0
]
as
ActionOf
<
InitializeExplorePayload
>
;
const
{
type
,
payload
}
=
initializeExplore
;
expect
(
type
).
toEqual
(
initializeExploreAction
.
type
);
expect
(
payload
.
containerWidth
).
toEqual
(
containerWidth
);
expect
(
payload
.
eventBridge
).
toEqual
(
eventBridge
);
expect
(
payload
.
exploreDatasources
).
toEqual
([]);
expect
(
payload
.
queries
.
length
).
toBe
(
1
);
// Queries have generated keys hard to expect on
expect
(
payload
.
range
).
toEqual
(
range
);
expect
(
payload
.
ui
).
toEqual
(
ui
);
return
true
;
});
});
});
describe
(
'and update range is set'
,
()
=>
{
it
(
'then it should dispatch changeTimeAction'
,
()
=>
{
const
{
exploreId
,
range
,
initialState
}
=
setup
({
range
:
true
});
thunkTester
(
initialState
)
.
givenThunk
(
refreshExplore
)
.
whenThunkIsDispatched
(
exploreId
)
.
thenDispatchedActionsAreEqual
(
dispatchedActions
=>
{
expect
(
dispatchedActions
[
0
].
type
).
toEqual
(
changeTimeAction
.
type
);
expect
(
dispatchedActions
[
0
].
payload
).
toEqual
({
exploreId
,
range
});
return
true
;
});
});
});
describe
(
'and update ui is set'
,
()
=>
{
it
(
'then it should dispatch updateUIStateAction'
,
()
=>
{
const
{
exploreId
,
initialState
,
ui
}
=
setup
({
ui
:
true
});
thunkTester
(
initialState
)
.
givenThunk
(
refreshExplore
)
.
whenThunkIsDispatched
(
exploreId
)
.
thenDispatchedActionsAreEqual
(
dispatchedActions
=>
{
expect
(
dispatchedActions
[
0
].
type
).
toEqual
(
updateUIStateAction
.
type
);
expect
(
dispatchedActions
[
0
].
payload
).
toEqual
({
...
ui
,
exploreId
});
return
true
;
});
});
});
describe
(
'and update queries is set'
,
()
=>
{
it
(
'then it should dispatch setQueriesAction'
,
()
=>
{
const
{
exploreId
,
initialState
}
=
setup
({
queries
:
true
});
thunkTester
(
initialState
)
.
givenThunk
(
refreshExplore
)
.
whenThunkIsDispatched
(
exploreId
)
.
thenDispatchedActionsAreEqual
(
dispatchedActions
=>
{
expect
(
dispatchedActions
[
0
].
type
).
toEqual
(
setQueriesAction
.
type
);
expect
(
dispatchedActions
[
0
].
payload
).
toEqual
({
exploreId
,
queries
:
[]
});
return
true
;
});
});
});
});
describe
(
'when update is not initialized'
,
()
=>
{
it
(
'then it should not dispatch any actions'
,
()
=>
{
const
exploreId
=
ExploreId
.
left
;
const
initialState
=
{
explore
:
{
[
exploreId
]:
{
initialized
:
false
}
}
};
thunkTester
(
initialState
)
.
givenThunk
(
refreshExplore
)
.
whenThunkIsDispatched
(
exploreId
)
.
thenThereAreNoDispatchedActions
();
});
});
});
public/app/features/explore/state/actions.ts
View file @
a6c2a8c3
...
@@ -16,6 +16,7 @@ import {
...
@@ -16,6 +16,7 @@ import {
updateHistory
,
updateHistory
,
buildQueryTransaction
,
buildQueryTransaction
,
serializeStateToUrlParam
,
serializeStateToUrlParam
,
parseUrlState
,
}
from
'app/core/utils/explore'
;
}
from
'app/core/utils/explore'
;
// Actions
// Actions
...
@@ -54,7 +55,6 @@ import {
...
@@ -54,7 +55,6 @@ import {
queryTransactionStartAction
,
queryTransactionStartAction
,
queryTransactionSuccessAction
,
queryTransactionSuccessAction
,
scanRangeAction
,
scanRangeAction
,
runQueriesEmptyAction
,
scanStartAction
,
scanStartAction
,
setQueriesAction
,
setQueriesAction
,
splitCloseAction
,
splitCloseAction
,
...
@@ -67,9 +67,11 @@ import {
...
@@ -67,9 +67,11 @@ import {
ToggleLogsPayload
,
ToggleLogsPayload
,
ToggleTablePayload
,
ToggleTablePayload
,
updateUIStateAction
,
updateUIStateAction
,
runQueriesAction
,
}
from
'./actionTypes'
;
}
from
'./actionTypes'
;
import
{
ActionOf
,
ActionCreator
}
from
'app/core/redux/actionCreatorFactory'
;
import
{
ActionOf
,
ActionCreator
}
from
'app/core/redux/actionCreatorFactory'
;
import
{
LogsDedupStrategy
}
from
'app/core/logs_model'
;
import
{
LogsDedupStrategy
}
from
'app/core/logs_model'
;
import
{
parseTime
}
from
'../TimePicker'
;
type
ThunkResult
<
R
>
=
ThunkAction
<
R
,
StoreState
,
undefined
,
Action
>
;
type
ThunkResult
<
R
>
=
ThunkAction
<
R
,
StoreState
,
undefined
,
Action
>
;
...
@@ -518,7 +520,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
...
@@ -518,7 +520,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
}
=
getState
().
explore
[
exploreId
];
}
=
getState
().
explore
[
exploreId
];
if
(
!
hasNonEmptyQuery
(
queries
))
{
if
(
!
hasNonEmptyQuery
(
queries
))
{
dispatch
(
runQueriesEmpty
Action
({
exploreId
}));
dispatch
(
clearQueries
Action
({
exploreId
}));
dispatch
(
stateSave
());
// Remember to saves to state and update location
dispatch
(
stateSave
());
// Remember to saves to state and update location
return
;
return
;
}
}
...
@@ -527,6 +529,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
...
@@ -527,6 +529,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false) {
// but we're using the datasource interval limit for now
// but we're using the datasource interval limit for now
const
interval
=
datasourceInstance
.
interval
;
const
interval
=
datasourceInstance
.
interval
;
dispatch
(
runQueriesAction
());
// Keep table queries first since they need to return quickly
// Keep table queries first since they need to return quickly
if
((
ignoreUIState
||
showingTable
)
&&
supportsTable
)
{
if
((
ignoreUIState
||
showingTable
)
&&
supportsTable
)
{
dispatch
(
dispatch
(
...
@@ -657,11 +660,15 @@ export function splitClose(): ThunkResult<void> {
...
@@ -657,11 +660,15 @@ export function splitClose(): ThunkResult<void> {
export
function
splitOpen
():
ThunkResult
<
void
>
{
export
function
splitOpen
():
ThunkResult
<
void
>
{
return
(
dispatch
,
getState
)
=>
{
return
(
dispatch
,
getState
)
=>
{
// Clone left state to become the right state
// Clone left state to become the right state
const
leftState
=
getState
().
explore
.
left
;
const
leftState
=
getState
().
explore
[
ExploreId
.
left
];
const
queryState
=
getState
().
location
.
query
[
ExploreId
.
left
]
as
string
;
const
urlState
=
parseUrlState
(
queryState
);
const
itemState
=
{
const
itemState
=
{
...
leftState
,
...
leftState
,
queryTransactions
:
[],
queryTransactions
:
[],
queries
:
leftState
.
queries
.
slice
(),
queries
:
leftState
.
queries
.
slice
(),
exploreId
:
ExploreId
.
right
,
urlState
,
};
};
dispatch
(
splitOpenAction
({
itemState
}));
dispatch
(
splitOpenAction
({
itemState
}));
dispatch
(
stateSave
());
dispatch
(
stateSave
());
...
@@ -766,3 +773,44 @@ export const changeDedupStrategy = (exploreId, dedupStrategy: LogsDedupStrategy)
...
@@ -766,3 +773,44 @@ export const changeDedupStrategy = (exploreId, dedupStrategy: LogsDedupStrategy)
dispatch
(
updateExploreUIState
(
exploreId
,
{
dedupStrategy
}));
dispatch
(
updateExploreUIState
(
exploreId
,
{
dedupStrategy
}));
};
};
};
};
export
function
refreshExplore
(
exploreId
:
ExploreId
):
ThunkResult
<
void
>
{
return
(
dispatch
,
getState
)
=>
{
const
itemState
=
getState
().
explore
[
exploreId
];
if
(
!
itemState
.
initialized
)
{
return
;
}
const
{
urlState
,
update
,
containerWidth
,
eventBridge
}
=
itemState
;
const
{
datasource
,
queries
,
range
,
ui
}
=
urlState
;
const
refreshQueries
=
queries
.
map
(
q
=>
({
...
q
,
...
generateEmptyQuery
(
itemState
.
queries
)
}));
const
refreshRange
=
{
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
)
};
// need to refresh datasource
if
(
update
.
datasource
)
{
const
initialQueries
=
ensureQueries
(
queries
);
const
initialRange
=
{
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
)
};
dispatch
(
initializeExplore
(
exploreId
,
datasource
,
initialQueries
,
initialRange
,
containerWidth
,
eventBridge
,
ui
));
return
;
}
if
(
update
.
range
)
{
dispatch
(
changeTimeAction
({
exploreId
,
range
:
refreshRange
as
TimeRange
}));
}
// need to refresh ui state
if
(
update
.
ui
)
{
dispatch
(
updateUIStateAction
({
...
ui
,
exploreId
}));
}
// need to refresh queries
if
(
update
.
queries
)
{
dispatch
(
setQueriesAction
({
exploreId
,
queries
:
refreshQueries
}));
}
// always run queries when refresh is needed
if
(
update
.
queries
||
update
.
ui
||
update
.
range
)
{
dispatch
(
runQueries
(
exploreId
));
}
};
}
public/app/features/explore/state/reducers.test.ts
View file @
a6c2a8c3
This diff is collapsed.
Click to expand it.
public/app/features/explore/state/reducers.ts
View file @
a6c2a8c3
// @ts-ignore
import
_
from
'lodash'
;
import
{
import
{
calculateResultsFromQueryTransactions
,
calculateResultsFromQueryTransactions
,
generateEmptyQuery
,
generateEmptyQuery
,
getIntervals
,
getIntervals
,
ensureQueries
,
ensureQueries
,
getQueryKeys
,
getQueryKeys
,
parseUrlState
,
DEFAULT_UI_STATE
,
}
from
'app/core/utils/explore'
;
}
from
'app/core/utils/explore'
;
import
{
ExploreItemState
,
ExploreState
,
QueryTransaction
}
from
'app/types/explore'
;
import
{
ExploreItemState
,
ExploreState
,
QueryTransaction
,
ExploreId
,
ExploreUpdateState
}
from
'app/types/explore'
;
import
{
DataQuery
}
from
'@grafana/ui/src/types'
;
import
{
DataQuery
}
from
'@grafana/ui/src/types'
;
import
{
HigherOrderAction
,
ActionTypes
}
from
'./actionTypes'
;
import
{
HigherOrderAction
,
ActionTypes
}
from
'./actionTypes'
;
...
@@ -28,7 +32,6 @@ import {
...
@@ -28,7 +32,6 @@ import {
queryTransactionStartAction
,
queryTransactionStartAction
,
queryTransactionSuccessAction
,
queryTransactionSuccessAction
,
removeQueryRowAction
,
removeQueryRowAction
,
runQueriesEmptyAction
,
scanRangeAction
,
scanRangeAction
,
scanStartAction
,
scanStartAction
,
scanStopAction
,
scanStopAction
,
...
@@ -40,6 +43,8 @@ import {
...
@@ -40,6 +43,8 @@ import {
updateUIStateAction
,
updateUIStateAction
,
toggleLogLevelAction
,
toggleLogLevelAction
,
}
from
'./actionTypes'
;
}
from
'./actionTypes'
;
import
{
updateLocation
}
from
'app/core/actions/location'
;
import
{
LocationUpdate
}
from
'app/types'
;
export
const
DEFAULT_RANGE
=
{
export
const
DEFAULT_RANGE
=
{
from
:
'now-6h'
,
from
:
'now-6h'
,
...
@@ -49,6 +54,12 @@ export const DEFAULT_RANGE = {
...
@@ -49,6 +54,12 @@ export const DEFAULT_RANGE = {
// Millies step for helper bar charts
// Millies step for helper bar charts
const
DEFAULT_GRAPH_INTERVAL
=
15
*
1000
;
const
DEFAULT_GRAPH_INTERVAL
=
15
*
1000
;
export
const
makeInitialUpdateState
=
():
ExploreUpdateState
=>
({
datasource
:
false
,
queries
:
false
,
range
:
false
,
ui
:
false
,
});
/**
/**
* Returns a fresh Explore area state
* Returns a fresh Explore area state
*/
*/
...
@@ -76,6 +87,8 @@ export const makeExploreItemState = (): ExploreItemState => ({
...
@@ -76,6 +87,8 @@ export const makeExploreItemState = (): ExploreItemState => ({
supportsLogs
:
null
,
supportsLogs
:
null
,
supportsTable
:
null
,
supportsTable
:
null
,
queryKeys
:
[],
queryKeys
:
[],
urlState
:
null
,
update
:
makeInitialUpdateState
(),
});
});
/**
/**
...
@@ -195,6 +208,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -195,6 +208,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
initialized
:
true
,
initialized
:
true
,
queryKeys
:
getQueryKeys
(
queries
,
state
.
datasourceInstance
),
queryKeys
:
getQueryKeys
(
queries
,
state
.
datasourceInstance
),
...
ui
,
...
ui
,
update
:
makeInitialUpdateState
(),
};
};
},
},
})
})
...
@@ -208,13 +222,23 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -208,13 +222,23 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
.
addMapper
({
.
addMapper
({
filter
:
loadDatasourceFailureAction
,
filter
:
loadDatasourceFailureAction
,
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
return
{
...
state
,
datasourceError
:
action
.
payload
.
error
,
datasourceLoading
:
false
};
return
{
...
state
,
datasourceError
:
action
.
payload
.
error
,
datasourceLoading
:
false
,
update
:
makeInitialUpdateState
(),
};
},
},
})
})
.
addMapper
({
.
addMapper
({
filter
:
loadDatasourceMissingAction
,
filter
:
loadDatasourceMissingAction
,
mapper
:
(
state
):
ExploreItemState
=>
{
mapper
:
(
state
):
ExploreItemState
=>
{
return
{
...
state
,
datasourceMissing
:
true
,
datasourceLoading
:
false
};
return
{
...
state
,
datasourceMissing
:
true
,
datasourceLoading
:
false
,
update
:
makeInitialUpdateState
(),
};
},
},
})
})
.
addMapper
({
.
addMapper
({
...
@@ -253,6 +277,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -253,6 +277,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
datasourceError
:
null
,
datasourceError
:
null
,
logsHighlighterExpressions
:
undefined
,
logsHighlighterExpressions
:
undefined
,
queryTransactions
:
[],
queryTransactions
:
[],
update
:
makeInitialUpdateState
(),
};
};
},
},
})
})
...
@@ -262,7 +287,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -262,7 +287,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
const
{
queries
,
queryTransactions
}
=
state
;
const
{
queries
,
queryTransactions
}
=
state
;
const
{
modification
,
index
,
modifier
}
=
action
.
payload
;
const
{
modification
,
index
,
modifier
}
=
action
.
payload
;
let
nextQueries
:
DataQuery
[];
let
nextQueries
:
DataQuery
[];
let
nextQueryTransactions
;
let
nextQueryTransactions
:
QueryTransaction
[]
;
if
(
index
===
undefined
)
{
if
(
index
===
undefined
)
{
// Modify all queries
// Modify all queries
nextQueries
=
queries
.
map
((
query
,
i
)
=>
({
nextQueries
=
queries
.
map
((
query
,
i
)
=>
({
...
@@ -303,7 +328,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -303,7 +328,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
filter
:
queryTransactionFailureAction
,
filter
:
queryTransactionFailureAction
,
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
const
{
queryTransactions
}
=
action
.
payload
;
const
{
queryTransactions
}
=
action
.
payload
;
return
{
...
state
,
queryTransactions
,
showingStartPage
:
false
};
return
{
...
state
,
queryTransactions
,
showingStartPage
:
false
,
update
:
makeInitialUpdateState
(),
};
},
},
})
})
.
addMapper
({
.
addMapper
({
...
@@ -319,7 +349,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -319,7 +349,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
// Append new transaction
// Append new transaction
const
nextQueryTransactions
:
QueryTransaction
[]
=
[...
remainingTransactions
,
transaction
];
const
nextQueryTransactions
:
QueryTransaction
[]
=
[...
remainingTransactions
,
transaction
];
return
{
...
state
,
queryTransactions
:
nextQueryTransactions
,
showingStartPage
:
false
};
return
{
...
state
,
queryTransactions
:
nextQueryTransactions
,
showingStartPage
:
false
,
update
:
makeInitialUpdateState
(),
};
},
},
})
})
.
addMapper
({
.
addMapper
({
...
@@ -333,7 +368,14 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -333,7 +368,14 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
queryIntervals
.
intervalMs
queryIntervals
.
intervalMs
);
);
return
{
...
state
,
...
results
,
history
,
queryTransactions
,
showingStartPage
:
false
};
return
{
...
state
,
...
results
,
history
,
queryTransactions
,
showingStartPage
:
false
,
update
:
makeInitialUpdateState
(),
};
},
},
})
})
.
addMapper
({
.
addMapper
({
...
@@ -368,12 +410,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -368,12 +410,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
},
},
})
})
.
addMapper
({
.
addMapper
({
filter
:
runQueriesEmptyAction
,
mapper
:
(
state
):
ExploreItemState
=>
{
return
{
...
state
,
queryTransactions
:
[]
};
},
})
.
addMapper
({
filter
:
scanRangeAction
,
filter
:
scanRangeAction
,
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
mapper
:
(
state
,
action
):
ExploreItemState
=>
{
return
{
...
state
,
scanRange
:
action
.
payload
.
range
};
return
{
...
state
,
scanRange
:
action
.
payload
.
range
};
...
@@ -396,6 +432,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -396,6 +432,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
scanning
:
false
,
scanning
:
false
,
scanRange
:
undefined
,
scanRange
:
undefined
,
scanner
:
undefined
,
scanner
:
undefined
,
update
:
makeInitialUpdateState
(),
};
};
},
},
})
})
...
@@ -482,6 +519,41 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
...
@@ -482,6 +519,41 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
})
})
.
create
();
.
create
();
export
const
updateChildRefreshState
=
(
state
:
Readonly
<
ExploreItemState
>
,
payload
:
LocationUpdate
,
exploreId
:
ExploreId
):
ExploreItemState
=>
{
const
path
=
payload
.
path
||
''
;
const
queryState
=
payload
.
query
[
exploreId
]
as
string
;
if
(
!
queryState
)
{
return
state
;
}
const
urlState
=
parseUrlState
(
queryState
);
if
(
!
state
.
urlState
||
path
!==
'/explore'
)
{
// we only want to refresh when browser back/forward
return
{
...
state
,
urlState
,
update
:
{
datasource
:
false
,
queries
:
false
,
range
:
false
,
ui
:
false
}
};
}
const
datasource
=
_
.
isEqual
(
urlState
?
urlState
.
datasource
:
''
,
state
.
urlState
.
datasource
)
===
false
;
const
queries
=
_
.
isEqual
(
urlState
?
urlState
.
queries
:
[],
state
.
urlState
.
queries
)
===
false
;
const
range
=
_
.
isEqual
(
urlState
?
urlState
.
range
:
DEFAULT_RANGE
,
state
.
urlState
.
range
)
===
false
;
const
ui
=
_
.
isEqual
(
urlState
?
urlState
.
ui
:
DEFAULT_UI_STATE
,
state
.
urlState
.
ui
)
===
false
;
return
{
...
state
,
urlState
,
update
:
{
...
state
.
update
,
datasource
,
queries
,
range
,
ui
,
},
};
};
/**
/**
* Global Explore reducer that handles multiple Explore areas (left and right).
* Global Explore reducer that handles multiple Explore areas (left and right).
* Actions that have an `exploreId` get routed to the ExploreItemReducer.
* Actions that have an `exploreId` get routed to the ExploreItemReducer.
...
@@ -493,16 +565,30 @@ export const exploreReducer = (state = initialExploreState, action: HigherOrderA
...
@@ -493,16 +565,30 @@ export const exploreReducer = (state = initialExploreState, action: HigherOrderA
}
}
case
ActionTypes
.
SplitOpen
:
{
case
ActionTypes
.
SplitOpen
:
{
return
{
...
state
,
split
:
true
,
right
:
action
.
payload
.
itemState
};
return
{
...
state
,
split
:
true
,
right
:
{
...
action
.
payload
.
itemState
}
};
}
case
ActionTypes
.
InitializeExploreSplit
:
{
return
{
...
state
,
split
:
true
};
}
}
case
ActionTypes
.
ResetExplore
:
{
case
ActionTypes
.
ResetExplore
:
{
return
initialExploreState
;
return
initialExploreState
;
}
}
case
updateLocation
.
type
:
{
const
{
query
}
=
action
.
payload
;
if
(
!
query
||
!
query
[
ExploreId
.
left
])
{
return
state
;
}
const
split
=
query
[
ExploreId
.
right
]
?
true
:
false
;
const
leftState
=
state
[
ExploreId
.
left
];
const
rightState
=
state
[
ExploreId
.
right
];
return
{
...
state
,
split
,
[
ExploreId
.
left
]:
updateChildRefreshState
(
leftState
,
action
.
payload
,
ExploreId
.
left
),
[
ExploreId
.
right
]:
updateChildRefreshState
(
rightState
,
action
.
payload
,
ExploreId
.
right
),
};
}
}
}
if
(
action
.
payload
)
{
if
(
action
.
payload
)
{
...
...
public/app/plugins/datasource/graphite/gfunc.ts
View file @
a6c2a8c3
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
{
isVersionGtOrEq
}
from
'app/core/utils/version'
;
import
{
isVersionGtOrEq
}
from
'app/core/utils/version'
;
import
{
InterpolateFunction
}
from
'@grafana/ui'
;
const
index
=
{};
const
index
=
{};
...
@@ -961,24 +962,30 @@ export class FuncInstance {
...
@@ -961,24 +962,30 @@ export class FuncInstance {
this
.
updateText
();
this
.
updateText
();
}
}
render
(
metricExp
)
{
render
(
metricExp
:
string
,
replaceVariables
:
InterpolateFunction
):
string
{
const
str
=
this
.
def
.
name
+
'('
;
const
str
=
this
.
def
.
name
+
'('
;
const
parameters
=
_
.
map
(
this
.
params
,
(
value
,
index
)
=>
{
const
parameters
=
_
.
map
(
this
.
params
,
(
value
,
index
)
=>
{
const
valueInterpolated
=
replaceVariables
(
value
);
let
paramType
;
let
paramType
;
if
(
index
<
this
.
def
.
params
.
length
)
{
if
(
index
<
this
.
def
.
params
.
length
)
{
paramType
=
this
.
def
.
params
[
index
].
type
;
paramType
=
this
.
def
.
params
[
index
].
type
;
}
else
if
(
_
.
get
(
_
.
last
(
this
.
def
.
params
),
'multiple'
))
{
}
else
if
(
_
.
get
(
_
.
last
(
this
.
def
.
params
),
'multiple'
))
{
paramType
=
_
.
get
(
_
.
last
(
this
.
def
.
params
),
'type'
);
paramType
=
_
.
get
(
_
.
last
(
this
.
def
.
params
),
'type'
);
}
}
// param types that should never be quoted
// param types that should never be quoted
if
(
_
.
includes
([
'value_or_series'
,
'boolean'
,
'int'
,
'float'
,
'node'
],
paramType
))
{
if
(
_
.
includes
([
'value_or_series'
,
'boolean'
,
'int'
,
'float'
,
'node'
],
paramType
))
{
return
value
;
return
value
;
}
}
// param types that might be quoted
// param types that might be quoted
if
(
_
.
includes
([
'int_or_interval'
,
'node_or_tag'
],
paramType
)
&&
_
.
isFinite
(
+
value
))
{
// To quote variables correctly we need to interpolate it to check if it contains a numeric or string value
return
_
.
toString
(
+
value
);
if
(
_
.
includes
([
'int_or_interval'
,
'node_or_tag'
],
paramType
)
&&
_
.
isFinite
(
+
valueInterpolated
))
{
return
_
.
toString
(
value
);
}
}
return
"'"
+
value
+
"'"
;
return
"'"
+
value
+
"'"
;
});
});
...
...
public/app/plugins/datasource/graphite/graphite_query.ts
View file @
a6c2a8c3
...
@@ -18,6 +18,7 @@ export default class GraphiteQuery {
...
@@ -18,6 +18,7 @@ export default class GraphiteQuery {
constructor
(
datasource
,
target
,
templateSrv
?,
scopedVars
?)
{
constructor
(
datasource
,
target
,
templateSrv
?,
scopedVars
?)
{
this
.
datasource
=
datasource
;
this
.
datasource
=
datasource
;
this
.
target
=
target
;
this
.
target
=
target
;
this
.
templateSrv
=
templateSrv
;
this
.
parseTarget
();
this
.
parseTarget
();
this
.
removeTagValue
=
'-- remove tag --'
;
this
.
removeTagValue
=
'-- remove tag --'
;
...
@@ -160,7 +161,10 @@ export default class GraphiteQuery {
...
@@ -160,7 +161,10 @@ export default class GraphiteQuery {
}
}
updateModelTarget
(
targets
)
{
updateModelTarget
(
targets
)
{
// render query
const
wrapFunction
=
(
target
:
string
,
func
:
any
)
=>
{
return
func
.
render
(
target
,
this
.
templateSrv
.
replace
);
};
if
(
!
this
.
target
.
textEditor
)
{
if
(
!
this
.
target
.
textEditor
)
{
const
metricPath
=
this
.
getSegmentPathUpTo
(
this
.
segments
.
length
).
replace
(
/
\.
select metric$/
,
''
);
const
metricPath
=
this
.
getSegmentPathUpTo
(
this
.
segments
.
length
).
replace
(
/
\.
select metric$/
,
''
);
this
.
target
.
target
=
_
.
reduce
(
this
.
functions
,
wrapFunction
,
metricPath
);
this
.
target
.
target
=
_
.
reduce
(
this
.
functions
,
wrapFunction
,
metricPath
);
...
@@ -302,10 +306,6 @@ export default class GraphiteQuery {
...
@@ -302,10 +306,6 @@ export default class GraphiteQuery {
}
}
}
}
function
wrapFunction
(
target
,
func
)
{
return
func
.
render
(
target
);
}
function
renderTagString
(
tag
)
{
function
renderTagString
(
tag
)
{
return
tag
.
key
+
tag
.
operator
+
tag
.
value
;
return
tag
.
key
+
tag
.
operator
+
tag
.
value
;
}
}
public/app/plugins/datasource/graphite/specs/gfunc.test.ts
View file @
a6c2a8c3
...
@@ -30,66 +30,92 @@ describe('when creating func instance from func names', () => {
...
@@ -30,66 +30,92 @@ describe('when creating func instance from func names', () => {
});
});
});
});
function
replaceVariablesDummy
(
str
:
string
)
{
return
str
;
}
describe
(
'when rendering func instance'
,
()
=>
{
describe
(
'when rendering func instance'
,
()
=>
{
it
(
'should handle single metric param'
,
()
=>
{
it
(
'should handle single metric param'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'sumSeries'
);
const
func
=
gfunc
.
createFuncInstance
(
'sumSeries'
);
expect
(
func
.
render
(
'hello.metric'
)).
toEqual
(
'sumSeries(hello.metric)'
);
expect
(
func
.
render
(
'hello.metric'
,
replaceVariablesDummy
)).
toEqual
(
'sumSeries(hello.metric)'
);
});
});
it
(
'should include default params if options enable it'
,
()
=>
{
it
(
'should include default params if options enable it'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'scaleToSeconds'
,
{
const
func
=
gfunc
.
createFuncInstance
(
'scaleToSeconds'
,
{
withDefaultParams
:
true
,
withDefaultParams
:
true
,
});
});
expect
(
func
.
render
(
'hello'
)).
toEqual
(
'scaleToSeconds(hello, 1)'
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
'scaleToSeconds(hello, 1)'
);
});
});
it
(
'should handle int or interval params with number'
,
()
=>
{
it
(
'should handle int or interval params with number'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'movingMedian'
);
const
func
=
gfunc
.
createFuncInstance
(
'movingMedian'
);
func
.
params
[
0
]
=
'5'
;
func
.
params
[
0
]
=
'5'
;
expect
(
func
.
render
(
'hello'
)).
toEqual
(
'movingMedian(hello, 5)'
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
'movingMedian(hello, 5)'
);
});
});
it
(
'should handle int or interval params with interval string'
,
()
=>
{
it
(
'should handle int or interval params with interval string'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'movingMedian'
);
const
func
=
gfunc
.
createFuncInstance
(
'movingMedian'
);
func
.
params
[
0
]
=
'5min'
;
func
.
params
[
0
]
=
'5min'
;
expect
(
func
.
render
(
'hello'
)).
toEqual
(
"movingMedian(hello, '5min')"
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
"movingMedian(hello, '5min')"
);
});
});
it
(
'should never quote boolean paramater'
,
()
=>
{
it
(
'should never quote boolean paramater'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'sortByName'
);
const
func
=
gfunc
.
createFuncInstance
(
'sortByName'
);
func
.
params
[
0
]
=
'$natural'
;
func
.
params
[
0
]
=
'$natural'
;
expect
(
func
.
render
(
'hello'
)).
toEqual
(
'sortByName(hello, $natural)'
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
'sortByName(hello, $natural)'
);
});
});
it
(
'should never quote int paramater'
,
()
=>
{
it
(
'should never quote int paramater'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'maximumAbove'
);
const
func
=
gfunc
.
createFuncInstance
(
'maximumAbove'
);
func
.
params
[
0
]
=
'$value'
;
func
.
params
[
0
]
=
'$value'
;
expect
(
func
.
render
(
'hello'
)).
toEqual
(
'maximumAbove(hello, $value)'
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
'maximumAbove(hello, $value)'
);
});
});
it
(
'should never quote node paramater'
,
()
=>
{
it
(
'should never quote node paramater'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'aliasByNode'
);
const
func
=
gfunc
.
createFuncInstance
(
'aliasByNode'
);
func
.
params
[
0
]
=
'$node'
;
func
.
params
[
0
]
=
'$node'
;
expect
(
func
.
render
(
'hello'
)).
toEqual
(
'aliasByNode(hello, $node)'
);
expect
(
func
.
render
(
'hello'
,
replaceVariablesDummy
)).
toEqual
(
'aliasByNode(hello, $node)'
);
});
});
it
(
'should handle metric param and int param and string param'
,
()
=>
{
it
(
'should handle metric param and int param and string param'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'groupByNode'
);
const
func
=
gfunc
.
createFuncInstance
(
'groupByNode'
);
func
.
params
[
0
]
=
5
;
func
.
params
[
0
]
=
5
;
func
.
params
[
1
]
=
'avg'
;
func
.
params
[
1
]
=
'avg'
;
expect
(
func
.
render
(
'hello.metric'
)).
toEqual
(
"groupByNode(hello.metric, 5, 'avg')"
);
expect
(
func
.
render
(
'hello.metric'
,
replaceVariablesDummy
)).
toEqual
(
"groupByNode(hello.metric, 5, 'avg')"
);
});
});
it
(
'should handle function with no metric param'
,
()
=>
{
it
(
'should handle function with no metric param'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'randomWalk'
);
const
func
=
gfunc
.
createFuncInstance
(
'randomWalk'
);
func
.
params
[
0
]
=
'test'
;
func
.
params
[
0
]
=
'test'
;
expect
(
func
.
render
(
undefined
)).
toEqual
(
"randomWalk('test')"
);
expect
(
func
.
render
(
undefined
,
replaceVariablesDummy
)).
toEqual
(
"randomWalk('test')"
);
});
});
it
(
'should handle function multiple series params'
,
()
=>
{
it
(
'should handle function multiple series params'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'asPercent'
);
const
func
=
gfunc
.
createFuncInstance
(
'asPercent'
);
func
.
params
[
0
]
=
'#B'
;
func
.
params
[
0
]
=
'#B'
;
expect
(
func
.
render
(
'#A'
)).
toEqual
(
'asPercent(#A, #B)'
);
expect
(
func
.
render
(
'#A'
,
replaceVariablesDummy
)).
toEqual
(
'asPercent(#A, #B)'
);
});
it
(
'should not quote variables that have numeric value'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'movingAverage'
);
func
.
params
[
0
]
=
'$variable'
;
const
replaceVariables
=
(
str
:
string
)
=>
{
return
str
.
replace
(
'$variable'
,
'60'
);
};
expect
(
func
.
render
(
'metric'
,
replaceVariables
)).
toBe
(
'movingAverage(metric, $variable)'
);
});
it
(
'should quote variables that have string value'
,
()
=>
{
const
func
=
gfunc
.
createFuncInstance
(
'movingAverage'
);
func
.
params
[
0
]
=
'$variable'
;
const
replaceVariables
=
(
str
:
string
)
=>
{
return
str
.
replace
(
'$variable'
,
'10min'
);
};
expect
(
func
.
render
(
'metric'
,
replaceVariables
)).
toBe
(
"movingAverage(metric, '$variable')"
);
});
});
});
});
...
...
public/app/plugins/datasource/graphite/specs/graphite_query.test.ts
View file @
a6c2a8c3
import
gfunc
from
'../gfunc'
;
import
gfunc
from
'../gfunc'
;
import
GraphiteQuery
from
'../graphite_query'
;
import
GraphiteQuery
from
'../graphite_query'
;
import
{
TemplateSrvStub
}
from
'test/specs/helpers'
;
describe
(
'Graphite query model'
,
()
=>
{
describe
(
'Graphite query model'
,
()
=>
{
const
ctx
:
any
=
{
const
ctx
:
any
=
{
...
@@ -9,7 +10,7 @@ describe('Graphite query model', () => {
...
@@ -9,7 +10,7 @@ describe('Graphite query model', () => {
waitForFuncDefsLoaded
:
jest
.
fn
().
mockReturnValue
(
Promise
.
resolve
(
null
)),
waitForFuncDefsLoaded
:
jest
.
fn
().
mockReturnValue
(
Promise
.
resolve
(
null
)),
createFuncInstance
:
gfunc
.
createFuncInstance
,
createFuncInstance
:
gfunc
.
createFuncInstance
,
},
},
templateSrv
:
{}
,
templateSrv
:
new
TemplateSrvStub
()
,
targets
:
[],
targets
:
[],
};
};
...
...
public/app/plugins/datasource/graphite/specs/query_ctrl.test.ts
View file @
a6c2a8c3
import
{
uiSegmentSrv
}
from
'app/core/services/segment_srv'
;
import
{
uiSegmentSrv
}
from
'app/core/services/segment_srv'
;
import
gfunc
from
'../gfunc'
;
import
gfunc
from
'../gfunc'
;
import
{
GraphiteQueryCtrl
}
from
'../query_ctrl'
;
import
{
GraphiteQueryCtrl
}
from
'../query_ctrl'
;
import
{
TemplateSrvStub
}
from
'test/specs/helpers'
;
describe
(
'GraphiteQueryCtrl'
,
()
=>
{
describe
(
'GraphiteQueryCtrl'
,
()
=>
{
const
ctx
=
{
const
ctx
=
{
...
@@ -30,7 +31,7 @@ describe('GraphiteQueryCtrl', () => {
...
@@ -30,7 +31,7 @@ describe('GraphiteQueryCtrl', () => {
{},
{},
{},
{},
new
uiSegmentSrv
({
trustAsHtml
:
html
=>
html
},
{
highlightVariablesAsHtml
:
()
=>
{}
}),
new
uiSegmentSrv
({
trustAsHtml
:
html
=>
html
},
{
highlightVariablesAsHtml
:
()
=>
{}
}),
{}
,
new
TemplateSrvStub
()
,
{}
{}
);
);
});
});
...
@@ -291,7 +292,7 @@ describe('GraphiteQueryCtrl', () => {
...
@@ -291,7 +292,7 @@ describe('GraphiteQueryCtrl', () => {
ctx
.
ctrl
.
target
.
target
=
"seriesByTag('tag1=value1', 'tag2!=~value2')"
;
ctx
.
ctrl
.
target
.
target
=
"seriesByTag('tag1=value1', 'tag2!=~value2')"
;
ctx
.
ctrl
.
datasource
.
metricFindQuery
=
()
=>
Promise
.
resolve
([{
expandable
:
false
}]);
ctx
.
ctrl
.
datasource
.
metricFindQuery
=
()
=>
Promise
.
resolve
([{
expandable
:
false
}]);
ctx
.
ctrl
.
parseTarget
();
ctx
.
ctrl
.
parseTarget
();
ctx
.
ctrl
.
removeTag
(
0
);
ctx
.
ctrl
.
tagChanged
({
key
:
ctx
.
ctrl
.
removeTagValue
}
);
});
});
it
(
'should update tags'
,
()
=>
{
it
(
'should update tags'
,
()
=>
{
...
...
public/app/plugins/datasource/prometheus/datasource.ts
View file @
a6c2a8c3
...
@@ -15,7 +15,7 @@ import { expandRecordingRules } from './language_utils';
...
@@ -15,7 +15,7 @@ import { expandRecordingRules } from './language_utils';
// Types
// Types
import
{
PromQuery
}
from
'./types'
;
import
{
PromQuery
}
from
'./types'
;
import
{
DataQueryOptions
,
DataSourceApi
}
from
'@grafana/ui/src/types'
;
import
{
DataQueryOptions
,
DataSourceApi
,
AnnotationEvent
}
from
'@grafana/ui/src/types'
;
import
{
ExploreUrlState
}
from
'app/types/explore'
;
import
{
ExploreUrlState
}
from
'app/types/explore'
;
export
class
PrometheusDatasource
implements
DataSourceApi
<
PromQuery
>
{
export
class
PrometheusDatasource
implements
DataSourceApi
<
PromQuery
>
{
...
@@ -355,10 +355,11 @@ export class PrometheusDatasource implements DataSourceApi<PromQuery> {
...
@@ -355,10 +355,11 @@ export class PrometheusDatasource implements DataSourceApi<PromQuery> {
})
})
.
value
();
.
value
();
const
dupCheck
=
{};
for
(
const
value
of
series
.
values
)
{
for
(
const
value
of
series
.
values
)
{
const
valueIsTrue
=
value
[
1
]
===
'1'
;
// e.g. ALERTS
const
valueIsTrue
=
value
[
1
]
===
'1'
;
// e.g. ALERTS
if
(
valueIsTrue
||
annotation
.
useValueForTime
)
{
if
(
valueIsTrue
||
annotation
.
useValueForTime
)
{
const
event
=
{
const
event
:
AnnotationEvent
=
{
annotation
:
annotation
,
annotation
:
annotation
,
title
:
self
.
resultTransformer
.
renderTemplate
(
titleFormat
,
series
.
metric
),
title
:
self
.
resultTransformer
.
renderTemplate
(
titleFormat
,
series
.
metric
),
tags
:
tags
,
tags
:
tags
,
...
@@ -366,9 +367,14 @@ export class PrometheusDatasource implements DataSourceApi<PromQuery> {
...
@@ -366,9 +367,14 @@ export class PrometheusDatasource implements DataSourceApi<PromQuery> {
};
};
if
(
annotation
.
useValueForTime
)
{
if
(
annotation
.
useValueForTime
)
{
event
[
'time'
]
=
Math
.
floor
(
parseFloat
(
value
[
1
]));
const
timestampValue
=
Math
.
floor
(
parseFloat
(
value
[
1
]));
if
(
dupCheck
[
timestampValue
])
{
continue
;
}
dupCheck
[
timestampValue
]
=
true
;
event
.
time
=
timestampValue
;
}
else
{
}
else
{
event
[
'time'
]
=
Math
.
floor
(
parseFloat
(
value
[
0
]))
*
1000
;
event
.
time
=
Math
.
floor
(
parseFloat
(
value
[
0
]))
*
1000
;
}
}
eventList
.
push
(
event
);
eventList
.
push
(
event
);
...
...
public/app/plugins/panel/singlestat/module.ts
View file @
a6c2a8c3
...
@@ -3,6 +3,7 @@ import $ from 'jquery';
...
@@ -3,6 +3,7 @@ import $ from 'jquery';
import
'vendor/flot/jquery.flot'
;
import
'vendor/flot/jquery.flot'
;
import
'vendor/flot/jquery.flot.gauge'
;
import
'vendor/flot/jquery.flot.gauge'
;
import
'app/features/panel/panellinks/link_srv'
;
import
'app/features/panel/panellinks/link_srv'
;
import
{
getDecimalsForValue
}
from
'@grafana/ui'
;
import
kbn
from
'app/core/utils/kbn'
;
import
kbn
from
'app/core/utils/kbn'
;
import
config
from
'app/core/config'
;
import
config
from
'app/core/config'
;
...
@@ -190,7 +191,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
...
@@ -190,7 +191,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
data
.
value
=
0
;
data
.
value
=
0
;
data
.
valueRounded
=
0
;
data
.
valueRounded
=
0
;
}
else
{
}
else
{
const
decimalInfo
=
this
.
getDecimalsForValue
(
data
.
value
);
const
decimalInfo
=
getDecimalsForValue
(
data
.
value
);
const
formatFunc
=
getValueFormat
(
this
.
panel
.
format
);
const
formatFunc
=
getValueFormat
(
this
.
panel
.
format
);
data
.
valueFormatted
=
formatFunc
(
data
.
valueFormatted
=
formatFunc
(
...
@@ -243,47 +244,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
...
@@ -243,47 +244,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
this
.
render
();
this
.
render
();
}
}
getDecimalsForValue
(
value
)
{
if
(
_
.
isNumber
(
this
.
panel
.
decimals
))
{
return
{
decimals
:
this
.
panel
.
decimals
,
scaledDecimals
:
null
};
}
const
delta
=
value
/
2
;
let
dec
=
-
Math
.
floor
(
Math
.
log
(
delta
)
/
Math
.
LN10
);
const
magn
=
Math
.
pow
(
10
,
-
dec
);
const
norm
=
delta
/
magn
;
// norm is between 1.0 and 10.0
let
size
;
if
(
norm
<
1.5
)
{
size
=
1
;
}
else
if
(
norm
<
3
)
{
size
=
2
;
// special case for 2.5, requires an extra decimal
if
(
norm
>
2.25
)
{
size
=
2.5
;
++
dec
;
}
}
else
if
(
norm
<
7.5
)
{
size
=
5
;
}
else
{
size
=
10
;
}
size
*=
magn
;
// reduce starting decimals if not needed
if
(
Math
.
floor
(
value
)
===
value
)
{
dec
=
0
;
}
const
result
:
any
=
{};
result
.
decimals
=
Math
.
max
(
0
,
dec
);
result
.
scaledDecimals
=
result
.
decimals
-
Math
.
floor
(
Math
.
log
(
size
)
/
Math
.
LN10
)
+
2
;
return
result
;
}
setValues
(
data
)
{
setValues
(
data
)
{
data
.
flotpairs
=
[];
data
.
flotpairs
=
[];
...
@@ -319,15 +279,17 @@ class SingleStatCtrl extends MetricsPanelCtrl {
...
@@ -319,15 +279,17 @@ class SingleStatCtrl extends MetricsPanelCtrl {
data
.
value
=
this
.
series
[
0
].
stats
[
this
.
panel
.
valueName
];
data
.
value
=
this
.
series
[
0
].
stats
[
this
.
panel
.
valueName
];
data
.
flotpairs
=
this
.
series
[
0
].
flotpairs
;
data
.
flotpairs
=
this
.
series
[
0
].
flotpairs
;
const
decimalInfo
=
this
.
getDecimalsForValue
(
data
.
value
);
let
decimals
=
this
.
panel
.
decimals
;
let
scaledDecimals
=
0
;
if
(
!
this
.
panel
.
decimals
)
{
const
decimalInfo
=
getDecimalsForValue
(
data
.
value
);
decimals
=
decimalInfo
.
decimals
;
scaledDecimals
=
decimalInfo
.
scaledDecimals
;
}
data
.
valueFormatted
=
formatFunc
(
data
.
valueFormatted
=
formatFunc
(
data
.
value
,
decimals
,
scaledDecimals
,
this
.
dashboard
.
isTimezoneUtc
());
data
.
value
,
data
.
valueRounded
=
kbn
.
roundValue
(
data
.
value
,
decimals
);
decimalInfo
.
decimals
,
decimalInfo
.
scaledDecimals
,
this
.
dashboard
.
isTimezoneUtc
()
);
data
.
valueRounded
=
kbn
.
roundValue
(
data
.
value
,
decimalInfo
.
decimals
);
}
}
// Add $__name variable for using in prefix or postfix
// Add $__name variable for using in prefix or postfix
...
...
public/app/types/explore.ts
View file @
a6c2a8c3
...
@@ -248,6 +248,17 @@ export interface ExploreItemState {
...
@@ -248,6 +248,17 @@ export interface ExploreItemState {
* Currently hidden log series
* Currently hidden log series
*/
*/
hiddenLogLevels
?:
LogLevel
[];
hiddenLogLevels
?:
LogLevel
[];
urlState
:
ExploreUrlState
;
update
:
ExploreUpdateState
;
}
export
interface
ExploreUpdateState
{
datasource
:
boolean
;
queries
:
boolean
;
range
:
boolean
;
ui
:
boolean
;
}
}
export
interface
ExploreUIState
{
export
interface
ExploreUIState
{
...
...
public/test/core/thunk/thunkTester.ts
0 → 100644
View file @
a6c2a8c3
import
configureMockStore
from
'redux-mock-store'
;
import
thunk
from
'redux-thunk'
;
import
{
ActionOf
}
from
'app/core/redux/actionCreatorFactory'
;
const
mockStore
=
configureMockStore
([
thunk
]);
export
interface
ThunkGiven
{
givenThunk
:
(
thunkFunction
:
any
)
=>
ThunkWhen
;
}
export
interface
ThunkWhen
{
whenThunkIsDispatched
:
(...
args
:
any
)
=>
ThunkThen
;
}
export
interface
ThunkThen
{
thenDispatchedActionsEqual
:
(
actions
:
Array
<
ActionOf
<
any
>>
)
=>
ThunkWhen
;
thenDispatchedActionsAreEqual
:
(
callback
:
(
actions
:
Array
<
ActionOf
<
any
>>
)
=>
boolean
)
=>
ThunkWhen
;
thenThereAreNoDispatchedActions
:
()
=>
ThunkWhen
;
}
export
const
thunkTester
=
(
initialState
:
any
):
ThunkGiven
=>
{
const
store
=
mockStore
(
initialState
);
let
thunkUnderTest
=
null
;
const
givenThunk
=
(
thunkFunction
:
any
):
ThunkWhen
=>
{
thunkUnderTest
=
thunkFunction
;
return
instance
;
};
function
whenThunkIsDispatched
(...
args
:
any
):
ThunkThen
{
store
.
dispatch
(
thunkUnderTest
(...
arguments
));
return
instance
;
}
const
thenDispatchedActionsEqual
=
(
actions
:
Array
<
ActionOf
<
any
>>
):
ThunkWhen
=>
{
const
resultingActions
=
store
.
getActions
();
expect
(
resultingActions
).
toEqual
(
actions
);
return
instance
;
};
const
thenDispatchedActionsAreEqual
=
(
callback
:
(
dispathedActions
:
Array
<
ActionOf
<
any
>>
)
=>
boolean
):
ThunkWhen
=>
{
const
resultingActions
=
store
.
getActions
();
expect
(
callback
(
resultingActions
)).
toBe
(
true
);
return
instance
;
};
const
thenThereAreNoDispatchedActions
=
()
=>
{
return
thenDispatchedActionsEqual
([]);
};
const
instance
=
{
givenThunk
,
whenThunkIsDispatched
,
thenDispatchedActionsEqual
,
thenDispatchedActionsAreEqual
,
thenThereAreNoDispatchedActions
,
};
return
instance
;
};
public/test/specs/helpers.ts
View file @
a6c2a8c3
...
@@ -172,7 +172,7 @@ export function TemplateSrvStub(this: any) {
...
@@ -172,7 +172,7 @@ export function TemplateSrvStub(this: any) {
this
.
variables
=
[];
this
.
variables
=
[];
this
.
templateSettings
=
{
interpolate
:
/
\[\[([\s\S]
+
?)\]\]
/g
};
this
.
templateSettings
=
{
interpolate
:
/
\[\[([\s\S]
+
?)\]\]
/g
};
this
.
data
=
{};
this
.
data
=
{};
this
.
replace
=
function
(
text
:
string
)
{
this
.
replace
=
(
text
:
string
)
=>
{
return
_
.
template
(
text
,
this
.
templateSettings
)(
this
.
data
);
return
_
.
template
(
text
,
this
.
templateSettings
)(
this
.
data
);
};
};
this
.
init
=
()
=>
{};
this
.
init
=
()
=>
{};
...
...
scripts/grunt/options/compress.js
View file @
a6c2a8c3
...
@@ -15,7 +15,7 @@ module.exports = function(config) {
...
@@ -15,7 +15,7 @@ module.exports = function(config) {
},
},
{
{
expand
:
true
,
expand
:
true
,
src
:
[
'LICENSE
.md
'
,
'README.md'
,
'NOTICE.md'
],
src
:
[
'LICENSE'
,
'README.md'
,
'NOTICE.md'
],
dest
:
'<%= pkg.name %>-<%= pkg.version %>/'
,
dest
:
'<%= pkg.name %>-<%= pkg.version %>/'
,
}
}
]
]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment