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
b58a3c93
Unverified
Commit
b58a3c93
authored
Feb 04, 2019
by
Torkel Ödegaard
Committed by
GitHub
Feb 04, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15194 from grafana/explore/url
Explore - UI panels state persistance in url
parents
09708dfe
1a0b21b8
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
155 additions
and
46 deletions
+155
-46
public/app/core/utils/explore.test.ts
+30
-2
public/app/core/utils/explore.ts
+36
-4
public/app/features/explore/Explore.tsx
+6
-4
public/app/features/explore/state/actionTypes.ts
+2
-0
public/app/features/explore/state/actions.ts
+72
-35
public/app/features/explore/state/reducers.ts
+2
-1
public/app/types/explore.ts
+7
-0
No files found.
public/app/core/utils/explore.test.ts
View file @
b58a3c93
...
...
@@ -13,6 +13,11 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = {
datasource
:
null
,
queries
:
[],
range
:
DEFAULT_RANGE
,
ui
:
{
showingGraph
:
true
,
showingTable
:
true
,
showingLogs
:
true
,
}
};
describe
(
'state functions'
,
()
=>
{
...
...
@@ -69,9 +74,11 @@ describe('state functions', () => {
to
:
'now'
,
},
};
expect
(
serializeStateToUrlParam
(
state
)).
toBe
(
'{"datasource":"foo","queries":[{"expr":"metric{test=
\\
"a/b
\\
"}"},'
+
'{"expr":"super{foo=
\\
"x/z
\\
"}"}],"range":{"from":"now-5h","to":"now"}}'
'{"expr":"super{foo=
\\
"x/z
\\
"}"}],"range":{"from":"now-5h","to":"now"},'
+
'"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}'
);
});
...
...
@@ -93,7 +100,7 @@ describe('state functions', () => {
},
};
expect
(
serializeStateToUrlParam
(
state
,
true
)).
toBe
(
'["now-5h","now","foo",{"expr":"metric{test=
\\
"a/b
\\
"}"},{"expr":"super{foo=
\\
"x/z
\\
"}"}]'
'["now-5h","now","foo",{"expr":"metric{test=
\\
"a/b
\\
"}"},{"expr":"super{foo=
\\
"x/z
\\
"}"}
,{"ui":[true,true,true]}
]'
);
});
});
...
...
@@ -118,7 +125,28 @@ describe('state functions', () => {
};
const
serialized
=
serializeStateToUrlParam
(
state
);
const
parsed
=
parseUrlState
(
serialized
);
expect
(
state
).
toMatchObject
(
parsed
);
});
it
(
'can parse the compact serialized state into the original state'
,
()
=>
{
const
state
=
{
...
DEFAULT_EXPLORE_STATE
,
datasource
:
'foo'
,
queries
:
[
{
expr
:
'metric{test="a/b"}'
,
},
{
expr
:
'super{foo="x/z"}'
,
},
],
range
:
{
from
:
'now - 5h'
,
to
:
'now'
,
},
};
const
serialized
=
serializeStateToUrlParam
(
state
,
true
);
const
parsed
=
parseUrlState
(
serialized
);
expect
(
state
).
toMatchObject
(
parsed
);
});
});
...
...
public/app/core/utils/explore.ts
View file @
b58a3c93
...
...
@@ -27,6 +27,12 @@ export const DEFAULT_RANGE = {
to
:
'now'
,
};
export
const
DEFAULT_UI_STATE
=
{
showingTable
:
true
,
showingGraph
:
true
,
showingLogs
:
true
,
};
const
MAX_HISTORY_ITEMS
=
100
;
export
const
LAST_USED_DATASOURCE_KEY
=
'grafana.explore.datasource'
;
...
...
@@ -147,7 +153,12 @@ export function buildQueryTransaction(
export
const
clearQueryKeys
:
((
query
:
DataQuery
)
=>
object
)
=
({
key
,
refId
,
...
rest
})
=>
rest
;
const
isMetricSegment
=
(
segment
:
{
[
key
:
string
]:
string
})
=>
segment
.
hasOwnProperty
(
'expr'
);
const
isUISegment
=
(
segment
:
{
[
key
:
string
]:
string
})
=>
segment
.
hasOwnProperty
(
'ui'
);
export
function
parseUrlState
(
initial
:
string
|
undefined
):
ExploreUrlState
{
let
uiState
=
DEFAULT_UI_STATE
;
if
(
initial
)
{
try
{
const
parsed
=
JSON
.
parse
(
decodeURI
(
initial
));
...
...
@@ -160,20 +171,41 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
to
:
parsed
[
1
],
};
const
datasource
=
parsed
[
2
];
const
queries
=
parsed
.
slice
(
3
);
return
{
datasource
,
queries
,
range
};
let
queries
=
[];
parsed
.
slice
(
3
).
forEach
(
segment
=>
{
if
(
isMetricSegment
(
segment
))
{
queries
=
[...
queries
,
segment
];
}
if
(
isUISegment
(
segment
))
{
uiState
=
{
showingGraph
:
segment
.
ui
[
0
],
showingLogs
:
segment
.
ui
[
1
],
showingTable
:
segment
.
ui
[
2
],
};
}
});
return
{
datasource
,
queries
,
range
,
ui
:
uiState
};
}
return
parsed
;
}
catch
(
e
)
{
console
.
error
(
e
);
}
}
return
{
datasource
:
null
,
queries
:
[],
range
:
DEFAULT_RANGE
};
return
{
datasource
:
null
,
queries
:
[],
range
:
DEFAULT_RANGE
,
ui
:
uiState
};
}
export
function
serializeStateToUrlParam
(
urlState
:
ExploreUrlState
,
compact
?:
boolean
):
string
{
if
(
compact
)
{
return
JSON
.
stringify
([
urlState
.
range
.
from
,
urlState
.
range
.
to
,
urlState
.
datasource
,
...
urlState
.
queries
]);
return
JSON
.
stringify
([
urlState
.
range
.
from
,
urlState
.
range
.
to
,
urlState
.
datasource
,
...
urlState
.
queries
,
{
ui
:
[
!!
urlState
.
ui
.
showingGraph
,
!!
urlState
.
ui
.
showingLogs
,
!!
urlState
.
ui
.
showingTable
]
},
]);
}
return
JSON
.
stringify
(
urlState
);
}
...
...
public/app/features/explore/Explore.tsx
View file @
b58a3c93
...
...
@@ -32,7 +32,7 @@ import {
import
{
RawTimeRange
,
TimeRange
,
DataQuery
}
from
'@grafana/ui'
;
import
{
ExploreItemState
,
ExploreUrlState
,
RangeScanner
,
ExploreId
}
from
'app/types/explore'
;
import
{
StoreState
}
from
'app/types'
;
import
{
LAST_USED_DATASOURCE_KEY
,
ensureQueries
,
DEFAULT_RANGE
}
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
{
ExploreToolbar
}
from
'./ExploreToolbar'
;
...
...
@@ -61,7 +61,7 @@ interface ExploreProps {
supportsGraph
:
boolean
|
null
;
supportsLogs
:
boolean
|
null
;
supportsTable
:
boolean
|
null
;
urlState
:
ExploreUrlState
;
urlState
?
:
ExploreUrlState
;
}
/**
...
...
@@ -107,18 +107,20 @@ export class Explore extends React.PureComponent<ExploreProps> {
// Don't initialize on split, but need to initialize urlparameters when present
if
(
!
initialized
)
{
// Load URL state and parse range
const
{
datasource
,
queries
,
range
=
DEFAULT_RANGE
}
=
(
urlState
||
{})
as
ExploreUrlState
;
const
{
datasource
,
queries
,
range
=
DEFAULT_RANGE
,
ui
=
DEFAULT_UI_STATE
}
=
(
urlState
||
{})
as
ExploreUrlState
;
const
initialDatasource
=
datasource
||
store
.
get
(
LAST_USED_DATASOURCE_KEY
);
const
initialQueries
:
DataQuery
[]
=
ensureQueries
(
queries
);
const
initialRange
=
{
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
)
};
const
width
=
this
.
el
?
this
.
el
.
offsetWidth
:
0
;
this
.
props
.
initializeExplore
(
exploreId
,
initialDatasource
,
initialQueries
,
initialRange
,
width
,
this
.
exploreEvents
this
.
exploreEvents
,
ui
);
}
}
...
...
public/app/features/explore/state/actionTypes.ts
View file @
b58a3c93
...
...
@@ -8,6 +8,7 @@ import {
RangeScanner
,
ResultType
,
QueryTransaction
,
ExploreUIState
,
}
from
'app/types/explore'
;
export
enum
ActionTypes
{
...
...
@@ -106,6 +107,7 @@ export interface InitializeExploreAction {
exploreDatasources
:
DataSourceSelectItem
[];
queries
:
DataQuery
[];
range
:
RawTimeRange
;
ui
:
ExploreUIState
;
};
}
...
...
public/app/features/explore/state/actions.ts
View file @
b58a3c93
...
...
@@ -38,6 +38,7 @@ import {
ResultType
,
QueryOptions
,
QueryTransaction
,
ExploreUIState
,
}
from
'app/types/explore'
;
import
{
...
...
@@ -78,7 +79,15 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun
await
dispatch
(
importQueries
(
exploreId
,
modifiedQueries
,
currentDataSourceInstance
,
newDataSourceInstance
));
dispatch
(
updateDatasourceInstance
(
exploreId
,
newDataSourceInstance
));
dispatch
(
loadDatasource
(
exploreId
,
newDataSourceInstance
));
try
{
await
dispatch
(
loadDatasource
(
exploreId
,
newDataSourceInstance
));
}
catch
(
error
)
{
console
.
error
(
error
);
return
;
}
dispatch
(
runQueries
(
exploreId
));
};
}
...
...
@@ -154,7 +163,8 @@ export function initializeExplore(
queries
:
DataQuery
[],
range
:
RawTimeRange
,
containerWidth
:
number
,
eventBridge
:
Emitter
eventBridge
:
Emitter
,
ui
:
ExploreUIState
):
ThunkResult
<
void
>
{
return
async
dispatch
=>
{
const
exploreDatasources
:
DataSourceSelectItem
[]
=
getDatasourceSrv
()
...
...
@@ -175,6 +185,7 @@ export function initializeExplore(
exploreDatasources
,
queries
,
range
,
ui
,
},
});
...
...
@@ -194,7 +205,14 @@ export function initializeExplore(
}
dispatch
(
updateDatasourceInstance
(
exploreId
,
instance
));
dispatch
(
loadDatasource
(
exploreId
,
instance
));
try
{
await
dispatch
(
loadDatasource
(
exploreId
,
instance
));
}
catch
(
error
)
{
console
.
error
(
error
);
return
;
}
dispatch
(
runQueries
(
exploreId
,
true
));
}
else
{
dispatch
(
loadDatasourceMissing
(
exploreId
));
}
...
...
@@ -258,10 +276,7 @@ export const queriesImported = (exploreId: ExploreId, queries: DataQuery[]): Que
* run datasource-specific code. Existing queries are imported to the new datasource if an importer exists,
* e.g., Prometheus -> Loki queries.
*/
export
const
loadDatasourceSuccess
=
(
exploreId
:
ExploreId
,
instance
:
any
,
):
LoadDatasourceSuccessAction
=>
{
export
const
loadDatasourceSuccess
=
(
exploreId
:
ExploreId
,
instance
:
any
):
LoadDatasourceSuccessAction
=>
{
// Capabilities
const
supportsGraph
=
instance
.
meta
.
metrics
;
const
supportsLogs
=
instance
.
meta
.
logs
;
...
...
@@ -343,8 +358,8 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T
// Keep ID to track selection
dispatch
(
loadDatasourcePending
(
exploreId
,
datasourceName
));
let
datasourceError
=
null
;
try
{
const
testResult
=
await
instance
.
testDatasource
();
datasourceError
=
testResult
.
status
===
'success'
?
null
:
testResult
.
message
;
...
...
@@ -354,7 +369,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T
if
(
datasourceError
)
{
dispatch
(
loadDatasourceFailure
(
exploreId
,
datasourceError
));
return
;
return
Promise
.
reject
(
`
${
datasourceName
}
loading failed`
)
;
}
if
(
datasourceName
!==
getState
().
explore
[
exploreId
].
requestedDatasourceName
)
{
...
...
@@ -372,7 +387,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T
}
dispatch
(
loadDatasourceSuccess
(
exploreId
,
instance
));
dispatch
(
runQueries
(
exploreId
)
);
return
Promise
.
resolve
(
);
};
}
...
...
@@ -572,7 +587,7 @@ export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult
/**
* Main action to run queries and dispatches sub-actions based on which result viewers are active
*/
export
function
runQueries
(
exploreId
:
ExploreId
)
{
export
function
runQueries
(
exploreId
:
ExploreId
,
ignoreUIState
=
false
)
{
return
(
dispatch
,
getState
)
=>
{
const
{
datasourceInstance
,
...
...
@@ -596,7 +611,7 @@ export function runQueries(exploreId: ExploreId) {
const
interval
=
datasourceInstance
.
interval
;
// Keep table queries first since they need to return quickly
if
(
showingTable
&&
supportsTable
)
{
if
(
(
ignoreUIState
||
showingTable
)
&&
supportsTable
)
{
dispatch
(
runQueriesForType
(
exploreId
,
...
...
@@ -611,7 +626,7 @@ export function runQueries(exploreId: ExploreId) {
)
);
}
if
(
showingGraph
&&
supportsGraph
)
{
if
(
(
ignoreUIState
||
showingGraph
)
&&
supportsGraph
)
{
dispatch
(
runQueriesForType
(
exploreId
,
...
...
@@ -625,9 +640,10 @@ export function runQueries(exploreId: ExploreId) {
)
);
}
if
(
showingLogs
&&
supportsLogs
)
{
if
(
(
ignoreUIState
||
showingLogs
)
&&
supportsLogs
)
{
dispatch
(
runQueriesForType
(
exploreId
,
'Logs'
,
{
interval
,
format
:
'logs'
}));
}
dispatch
(
stateSave
());
};
}
...
...
@@ -766,6 +782,11 @@ export function stateSave() {
datasource
:
left
.
datasourceInstance
.
name
,
queries
:
left
.
modifiedQueries
.
map
(
clearQueryKeys
),
range
:
left
.
range
,
ui
:
{
showingGraph
:
left
.
showingGraph
,
showingLogs
:
left
.
showingLogs
,
showingTable
:
left
.
showingTable
,
},
};
urlStates
.
left
=
serializeStateToUrlParam
(
leftUrlState
,
true
);
if
(
split
)
{
...
...
@@ -773,48 +794,64 @@ export function stateSave() {
datasource
:
right
.
datasourceInstance
.
name
,
queries
:
right
.
modifiedQueries
.
map
(
clearQueryKeys
),
range
:
right
.
range
,
ui
:
{
showingGraph
:
right
.
showingGraph
,
showingLogs
:
right
.
showingLogs
,
showingTable
:
right
.
showingTable
,
},
};
urlStates
.
right
=
serializeStateToUrlParam
(
rightUrlState
,
true
);
}
dispatch
(
updateLocation
({
query
:
urlStates
}));
};
}
/**
* Expand/collapse the graph result viewer. When collapsed, graph queries won't be run.
* Creates action to collapse graph/logs/table panel. When panel is collapsed,
* queries won't be run
*/
export
function
toggleGraph
(
exploreId
:
ExploreId
):
ThunkResult
<
void
>
{
const
togglePanelActionCreator
=
(
type
:
ActionTypes
.
ToggleGraph
|
ActionTypes
.
ToggleTable
|
ActionTypes
.
ToggleLogs
)
=>
(
exploreId
:
ExploreId
)
=>
{
return
(
dispatch
,
getState
)
=>
{
dispatch
({
type
:
ActionTypes
.
ToggleGraph
,
payload
:
{
exploreId
}
});
if
(
getState
().
explore
[
exploreId
].
showingGraph
)
{
let
shouldRunQueries
;
dispatch
({
type
,
payload
:
{
exploreId
}
});
dispatch
(
stateSave
());
switch
(
type
)
{
case
ActionTypes
.
ToggleGraph
:
shouldRunQueries
=
getState
().
explore
[
exploreId
].
showingGraph
;
break
;
case
ActionTypes
.
ToggleLogs
:
shouldRunQueries
=
getState
().
explore
[
exploreId
].
showingLogs
;
break
;
case
ActionTypes
.
ToggleTable
:
shouldRunQueries
=
getState
().
explore
[
exploreId
].
showingTable
;
break
;
}
if
(
shouldRunQueries
)
{
dispatch
(
runQueries
(
exploreId
));
}
};
}
};
/**
* Expand/collapse the graph result viewer. When collapsed, graph queries won't be run.
*/
export
const
toggleGraph
=
togglePanelActionCreator
(
ActionTypes
.
ToggleGraph
);
/**
* Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
*/
export
function
toggleLogs
(
exploreId
:
ExploreId
):
ThunkResult
<
void
>
{
return
(
dispatch
,
getState
)
=>
{
dispatch
({
type
:
ActionTypes
.
ToggleLogs
,
payload
:
{
exploreId
}
});
if
(
getState
().
explore
[
exploreId
].
showingLogs
)
{
dispatch
(
runQueries
(
exploreId
));
}
};
}
export
const
toggleLogs
=
togglePanelActionCreator
(
ActionTypes
.
ToggleLogs
);
/**
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
*/
export
function
toggleTable
(
exploreId
:
ExploreId
):
ThunkResult
<
void
>
{
return
(
dispatch
,
getState
)
=>
{
dispatch
({
type
:
ActionTypes
.
ToggleTable
,
payload
:
{
exploreId
}
});
if
(
getState
().
explore
[
exploreId
].
showingTable
)
{
dispatch
(
runQueries
(
exploreId
));
}
};
}
export
const
toggleTable
=
togglePanelActionCreator
(
ActionTypes
.
ToggleTable
);
/**
* Resets state for explore.
...
...
public/app/features/explore/state/reducers.ts
View file @
b58a3c93
...
...
@@ -163,7 +163,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
}
case
ActionTypes
.
InitializeExplore
:
{
const
{
containerWidth
,
eventBridge
,
exploreDatasources
,
queries
,
range
}
=
action
.
payload
;
const
{
containerWidth
,
eventBridge
,
exploreDatasources
,
queries
,
range
,
ui
}
=
action
.
payload
;
return
{
...
state
,
containerWidth
,
...
...
@@ -173,6 +173,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
initialQueries
:
queries
,
initialized
:
true
,
modifiedQueries
:
queries
.
slice
(),
...
ui
,
};
}
...
...
public/app/types/explore.ts
View file @
b58a3c93
...
...
@@ -231,10 +231,17 @@ export interface ExploreItemState {
tableResult
?:
TableModel
;
}
export
interface
ExploreUIState
{
showingTable
:
boolean
;
showingGraph
:
boolean
;
showingLogs
:
boolean
;
}
export
interface
ExploreUrlState
{
datasource
:
string
;
queries
:
any
[];
// Should be a DataQuery, but we're going to strip refIds, so typing makes less sense
range
:
RawTimeRange
;
ui
:
ExploreUIState
;
}
export
interface
HistoryItem
<
TQuery
extends
DataQuery
=
DataQuery
>
{
...
...
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