Populate and substitute variables on dashboard load
parent
3796b328eb
commit
7642ae5563
|
@ -1234,6 +1234,12 @@
|
|||
"integrity": "sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/memoize-one": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/memoize-one/-/memoize-one-4.1.0.tgz",
|
||||
"integrity": "sha512-cmSgi6JMX/yBwgpVm4GooNWIH+vEeJoa8FAa6ExOhpJbC0Juq32/uYKiKb3VPSqrEA0aOnjvwZanla3O1WZMbw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
|
@ -2538,7 +2544,7 @@
|
|||
},
|
||||
"bindings": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "http://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
|
||||
"integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -2956,7 +2962,7 @@
|
|||
},
|
||||
"callsites": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
|
||||
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -3750,7 +3756,7 @@
|
|||
},
|
||||
"css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -4632,7 +4638,7 @@
|
|||
},
|
||||
"dotenv": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "http://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
|
||||
"integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -6100,8 +6106,7 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -6125,15 +6130,13 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -6150,22 +6153,19 @@
|
|||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -6296,8 +6296,7 @@
|
|||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -6311,7 +6310,6 @@
|
|||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -6328,7 +6326,6 @@
|
|||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -6337,15 +6334,13 @@
|
|||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz",
|
||||
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -6366,7 +6361,6 @@
|
|||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -6455,8 +6449,7 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -6470,7 +6463,6 @@
|
|||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -6566,8 +6558,7 @@
|
|||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -6609,7 +6600,6 @@
|
|||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -6631,7 +6621,6 @@
|
|||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -6680,15 +6669,13 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
||||
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8290,7 +8277,7 @@
|
|||
},
|
||||
"is-obj": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
|
||||
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -10089,7 +10076,7 @@
|
|||
},
|
||||
"magic-string": {
|
||||
"version": "0.22.5",
|
||||
"resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
|
||||
"integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -11471,7 +11458,7 @@
|
|||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
"@types/jest": "^23.3.2",
|
||||
"@types/levelup": "^3.1.0",
|
||||
"@types/lodash": "^4.14.116",
|
||||
"@types/memoize-one": "^4.0.2",
|
||||
"@types/node": "^9.4.6",
|
||||
"@types/papaparse": "^4.1.34",
|
||||
"@types/prop-types": "^15.5.2",
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
deleteCell as deleteCellAJAX,
|
||||
addDashboardLabels as addDashboardLabelsAJAX,
|
||||
removeDashboardLabels as removeDashboardLabelsAJAX,
|
||||
getView as getViewAJAX,
|
||||
updateView as updateViewAJAX,
|
||||
} from 'src/dashboards/apis/v2'
|
||||
import {client} from 'src/utils/api'
|
||||
|
@ -25,13 +26,16 @@ import {
|
|||
updateTimeRangeFromQueryParams,
|
||||
DeleteTimeRangeAction,
|
||||
} from 'src/dashboards/actions/v2/ranges'
|
||||
import {setView, SetViewAction} from 'src/dashboards/actions/v2/views'
|
||||
import {
|
||||
importDashboardSucceeded,
|
||||
importDashboardFailed,
|
||||
} from 'src/shared/copy/notifications'
|
||||
import {setView, SetViewAction, setViews} from 'src/dashboards/actions/v2/views'
|
||||
import {getVariables, refreshVariableValues} from 'src/variables/actions'
|
||||
|
||||
// Utils
|
||||
import {filterUnusedVars} from 'src/shared/utils/filterUnusedVars'
|
||||
import {getVariablesForOrg} from 'src/variables/selectors'
|
||||
import {
|
||||
getNewDashboardCell,
|
||||
getClonedDashboardCell,
|
||||
|
@ -44,7 +48,7 @@ import * as copy from 'src/shared/copy/notifications'
|
|||
import {RemoteDataState} from 'src/types'
|
||||
import {PublishNotificationAction} from 'src/types/actions/notifications'
|
||||
import {CreateCell, IDashboardTemplate} from '@influxdata/influx'
|
||||
import {Dashboard, NewView, Cell} from 'src/types/v2'
|
||||
import {Dashboard, NewView, Cell, GetState} from 'src/types/v2'
|
||||
import {ILabel} from '@influxdata/influx'
|
||||
|
||||
export enum ActionTypes {
|
||||
|
@ -253,14 +257,37 @@ export const deleteDashboardAsync = (dashboard: Dashboard) => async (
|
|||
}
|
||||
|
||||
export const getDashboardAsync = (dashboardID: string) => async (
|
||||
dispatch
|
||||
dispatch,
|
||||
getState: GetState
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const dashboard = await getDashboardAJAX(dashboardID)
|
||||
// Fetch the dashboard and all variables a user has access to
|
||||
const [dashboard] = await Promise.all([
|
||||
getDashboardAJAX(dashboardID),
|
||||
dispatch(getVariables()),
|
||||
])
|
||||
|
||||
// Fetch all the views in use on the dashboard
|
||||
const views = await Promise.all(
|
||||
dashboard.cells.map(cell => getViewAJAX(dashboard.id, cell.id))
|
||||
)
|
||||
|
||||
dispatch(setViews(RemoteDataState.Done, views))
|
||||
|
||||
// Find variables in use on dashboard
|
||||
const variables = getVariablesForOrg(getState(), dashboard.orgID)
|
||||
const variablesInUse = filterUnusedVars(variables, views)
|
||||
|
||||
// Ensure the values for those variables are loaded
|
||||
await dispatch(
|
||||
refreshVariableValues(dashboard.id, dashboard.orgID, variablesInUse)
|
||||
)
|
||||
|
||||
// Now that all the necessary state has been loaded, set the dashboard
|
||||
dispatch(loadDashboard(dashboard))
|
||||
} catch {
|
||||
dispatch(replace(`/dashboards`))
|
||||
dispatch(notify(copy.dashboardNotFound(dashboardID)))
|
||||
dispatch(notify(copy.dashboardGetFailed(dashboardID)))
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -9,7 +9,23 @@ import {RemoteDataState} from 'src/types'
|
|||
import {Dispatch} from 'redux'
|
||||
import {View} from 'src/types/v2'
|
||||
|
||||
export type Action = SetViewAction
|
||||
export type Action = SetViewAction | SetViewsAction
|
||||
|
||||
export interface SetViewsAction {
|
||||
type: 'SET_VIEWS'
|
||||
payload: {
|
||||
views?: View[]
|
||||
status: RemoteDataState
|
||||
}
|
||||
}
|
||||
|
||||
export const setViews = (
|
||||
status: RemoteDataState,
|
||||
views: View[]
|
||||
): SetViewsAction => ({
|
||||
type: 'SET_VIEWS',
|
||||
payload: {views, status},
|
||||
})
|
||||
|
||||
export interface SetViewAction {
|
||||
type: 'SET_VIEW'
|
||||
|
|
|
@ -340,7 +340,7 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
|
|||
},
|
||||
ranges,
|
||||
dashboards,
|
||||
views,
|
||||
views: {views},
|
||||
} = state
|
||||
|
||||
const timeRange =
|
||||
|
|
|
@ -4,20 +4,61 @@ import {RemoteDataState} from 'src/types'
|
|||
import {View} from 'src/types/v2'
|
||||
|
||||
export interface ViewsState {
|
||||
[viewID: string]: {
|
||||
status: RemoteDataState
|
||||
view: View
|
||||
status: RemoteDataState
|
||||
views: {
|
||||
[viewID: string]: {
|
||||
status: RemoteDataState
|
||||
view: View
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const viewsReducer = (state: ViewsState = {}, action: Action) => {
|
||||
const initialState = () => ({
|
||||
status: RemoteDataState.NotStarted,
|
||||
views: {},
|
||||
})
|
||||
|
||||
const viewsReducer = (
|
||||
state: ViewsState = initialState(),
|
||||
action: Action
|
||||
): ViewsState => {
|
||||
switch (action.type) {
|
||||
case 'SET_VIEWS': {
|
||||
const {status} = action.payload
|
||||
|
||||
if (!action.payload.views) {
|
||||
return {
|
||||
...state,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
const views = action.payload.views.reduce<ViewsState['views']>(
|
||||
(acc, view) => ({
|
||||
...acc,
|
||||
[view.id]: {
|
||||
view,
|
||||
status: RemoteDataState.Done,
|
||||
},
|
||||
}),
|
||||
{}
|
||||
)
|
||||
|
||||
return {
|
||||
status,
|
||||
views,
|
||||
}
|
||||
}
|
||||
|
||||
case 'SET_VIEW': {
|
||||
const {id, view, status} = action.payload
|
||||
|
||||
return {
|
||||
...state,
|
||||
[id]: {view, status},
|
||||
views: {
|
||||
...state.views,
|
||||
[id]: {view, status},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import {get} from 'lodash'
|
||||
|
||||
import {AppState, View} from 'src/types/v2'
|
||||
|
||||
export const getView = (state: AppState, id: string): View =>
|
||||
get(state, `views.views.${id}.view`)
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
|
@ -10,25 +11,35 @@ import QueryViewSwitcher from 'src/shared/components/QueryViewSwitcher'
|
|||
// Utils
|
||||
import {GlobalAutoRefresher} from 'src/utils/AutoRefresher'
|
||||
import {getTimeRangeVars} from 'src/variables/utils/getTimeRangeVars'
|
||||
import {getVariableAssignments} from 'src/variables/selectors'
|
||||
|
||||
// Types
|
||||
import {TimeRange} from 'src/types'
|
||||
import {VariableAssignment} from 'src/types/ast'
|
||||
import {AppState} from 'src/types/v2'
|
||||
import {DashboardQuery} from 'src/types/v2/dashboards'
|
||||
import {QueryViewProperties, ViewType} from 'src/types/v2/dashboards'
|
||||
|
||||
interface Props {
|
||||
interface OwnProps {
|
||||
timeRange: TimeRange
|
||||
viewID: string
|
||||
inView: boolean
|
||||
manualRefresh: number
|
||||
onZoom: (range: TimeRange) => void
|
||||
properties: QueryViewProperties
|
||||
dashboardID: string
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
variableAssignments: VariableAssignment[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
submitToken: number
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps
|
||||
|
||||
class RefreshingView extends PureComponent<Props, State> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
inView: true,
|
||||
|
@ -66,7 +77,7 @@ class RefreshingView extends PureComponent<Props, State> {
|
|||
submitToken={submitToken}
|
||||
queries={this.queries}
|
||||
key={manualRefresh}
|
||||
variables={getTimeRangeVars(timeRange)}
|
||||
variables={this.variableAssignments}
|
||||
>
|
||||
{({tables, loading, error, isInitialFetch}) => {
|
||||
return (
|
||||
|
@ -108,6 +119,12 @@ class RefreshingView extends PureComponent<Props, State> {
|
|||
return queries
|
||||
}
|
||||
|
||||
private get variableAssignments(): VariableAssignment[] {
|
||||
const {timeRange, variableAssignments} = this.props
|
||||
|
||||
return [...variableAssignments, ...getTimeRangeVars(timeRange)]
|
||||
}
|
||||
|
||||
private get fallbackNote(): string {
|
||||
const {note, showNoteWhenEmpty} = this.props.properties
|
||||
|
||||
|
@ -119,4 +136,13 @@ class RefreshingView extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default RefreshingView
|
||||
const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
|
||||
const variableAssignments = getVariableAssignments(
|
||||
state,
|
||||
ownProps.dashboardID
|
||||
)
|
||||
|
||||
return {variableAssignments}
|
||||
}
|
||||
|
||||
export default connect<StateProps, {}, OwnProps>(mstp)(RefreshingView)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Libraries
|
||||
import React, {Component, ComponentClass} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {get} from 'lodash'
|
||||
|
||||
|
@ -8,13 +8,12 @@ import CellHeader from 'src/shared/components/cells/CellHeader'
|
|||
import CellContext from 'src/shared/components/cells/CellContext'
|
||||
import ViewComponent from 'src/shared/components/cells/View'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import Conditional from 'src/shared/components/Conditional'
|
||||
|
||||
// Actions
|
||||
import {getView} from 'src/dashboards/actions/v2/views'
|
||||
// Utils
|
||||
import {getView} from 'src/dashboards/selectors'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState, TimeRange} from 'src/types'
|
||||
import {TimeRange} from 'src/types'
|
||||
import {AppState, ViewType, View, Cell} from 'src/types/v2'
|
||||
|
||||
// Styles
|
||||
|
@ -22,14 +21,9 @@ import './Cell.scss'
|
|||
|
||||
interface StateProps {
|
||||
view: View
|
||||
viewStatus: RemoteDataState
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onGetView: typeof getView
|
||||
}
|
||||
|
||||
interface PassedProps {
|
||||
interface OwnProps {
|
||||
cell: Cell
|
||||
timeRange: TimeRange
|
||||
autoRefresh: number
|
||||
|
@ -40,19 +34,10 @@ interface PassedProps {
|
|||
onZoom: (range: TimeRange) => void
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps & PassedProps
|
||||
type Props = StateProps & OwnProps
|
||||
|
||||
@ErrorHandling
|
||||
class CellComponent extends Component<Props> {
|
||||
public async componentDidMount() {
|
||||
const {viewStatus, cell, onGetView} = this.props
|
||||
|
||||
if (viewStatus === RemoteDataState.NotStarted) {
|
||||
const dashboardID = cell.dashboardID
|
||||
onGetView(dashboardID, cell.id)
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {onEditCell, onDeleteCell, onCloneCell, cell, view} = this.props
|
||||
|
||||
|
@ -110,21 +95,18 @@ class CellComponent extends Component<Props> {
|
|||
manualRefresh,
|
||||
onZoom,
|
||||
view,
|
||||
viewStatus,
|
||||
onEditCell,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Conditional isRendered={viewStatus === RemoteDataState.Done}>
|
||||
<ViewComponent
|
||||
view={view}
|
||||
onZoom={onZoom}
|
||||
timeRange={timeRange}
|
||||
autoRefresh={autoRefresh}
|
||||
manualRefresh={manualRefresh}
|
||||
onEditCell={onEditCell}
|
||||
/>
|
||||
</Conditional>
|
||||
<ViewComponent
|
||||
view={view}
|
||||
onZoom={onZoom}
|
||||
timeRange={timeRange}
|
||||
autoRefresh={autoRefresh}
|
||||
manualRefresh={manualRefresh}
|
||||
onEditCell={onEditCell}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -133,21 +115,11 @@ class CellComponent extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
const mstp = (state: AppState, ownProps: PassedProps): StateProps => {
|
||||
const entry = state.views[ownProps.cell.id]
|
||||
const mstp = (state: AppState, ownProps: OwnProps): StateProps => ({
|
||||
view: getView(state, ownProps.cell.id),
|
||||
})
|
||||
|
||||
if (entry) {
|
||||
return {view: entry.view, viewStatus: entry.status}
|
||||
}
|
||||
|
||||
return {view: null, viewStatus: RemoteDataState.NotStarted}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
onGetView: getView,
|
||||
}
|
||||
|
||||
export default connect(
|
||||
export default connect<StateProps, {}, OwnProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(CellComponent) as ComponentClass<PassedProps>
|
||||
null
|
||||
)(CellComponent)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import React, {Component} from 'react'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
|
||||
// Components
|
||||
import Markdown from 'src/shared/components/views/Markdown'
|
||||
|
@ -11,7 +12,7 @@ import {ViewType, ViewShape, View} from 'src/types/v2'
|
|||
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
interface OwnProps {
|
||||
view: View
|
||||
timeRange: TimeRange
|
||||
autoRefresh: number
|
||||
|
@ -20,6 +21,8 @@ interface Props {
|
|||
onEditCell: () => void
|
||||
}
|
||||
|
||||
type Props = OwnProps & WithRouterProps
|
||||
|
||||
@ErrorHandling
|
||||
class ViewComponent extends Component<Props> {
|
||||
public state = {
|
||||
|
@ -28,6 +31,7 @@ class ViewComponent extends Component<Props> {
|
|||
|
||||
public render() {
|
||||
const {view, onZoom, timeRange, manualRefresh} = this.props
|
||||
const {dashboardID} = this.props.params
|
||||
|
||||
switch (view.properties.type) {
|
||||
case ViewShape.Empty:
|
||||
|
@ -42,6 +46,8 @@ class ViewComponent extends Component<Props> {
|
|||
timeRange={timeRange}
|
||||
properties={view.properties}
|
||||
manualRefresh={manualRefresh}
|
||||
inView={true}
|
||||
dashboardID={dashboardID}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -61,4 +67,4 @@ class ViewComponent extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default ViewComponent
|
||||
export default withRouter<OwnProps>(ViewComponent)
|
||||
|
|
|
@ -472,10 +472,10 @@ export const tempVarAlreadyExists = (tempVarName: string): Notification => ({
|
|||
message: `Variable '${tempVarName}' already exists. Please enter a new value.`,
|
||||
})
|
||||
|
||||
export const dashboardNotFound = (dashboardID: string): Notification => ({
|
||||
export const dashboardGetFailed = (dashboardID: string): Notification => ({
|
||||
...defaultErrorNotification,
|
||||
icon: 'dash-h',
|
||||
message: `Dashboard ${dashboardID} could not be found`,
|
||||
message: `Failed to load dashboard with id "${dashboardID}"`,
|
||||
})
|
||||
|
||||
export const dashboardUpdateFailed = (): Notification => ({
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Constants
|
||||
import {OPTION_NAME} from 'src/variables/constants'
|
||||
|
||||
// Types
|
||||
import {QueryView} from 'src/types/v2/dashboards'
|
||||
import {Variable, View} from '@influxdata/influx'
|
||||
|
||||
/*
|
||||
Given a collection variables and a collection of views, return only the
|
||||
variables that are used in at least one of the view queries.
|
||||
*/
|
||||
export const filterUnusedVars = (variables: Variable[], views: View[]) => {
|
||||
const queryViews: QueryView[] = views.filter(
|
||||
view => !!view.properties.queries
|
||||
)
|
||||
|
||||
const queryTexts = queryViews.reduce(
|
||||
(acc, view) => [
|
||||
...acc,
|
||||
...view.properties.queries.map(query => query.text),
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const varsInUse = variables.filter(variable =>
|
||||
queryTexts.some(text => text.includes(`${OPTION_NAME}.${variable.name}`))
|
||||
)
|
||||
|
||||
return varsInUse
|
||||
}
|
|
@ -185,6 +185,7 @@ export const refreshVariableValues = (
|
|||
|
||||
dispatch(setValues(contextID, RemoteDataState.Done, values))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
dispatch(setValues(contextID, RemoteDataState.Error))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,18 @@
|
|||
import memoizeOne from 'memoize-one'
|
||||
import {get} from 'lodash'
|
||||
|
||||
// Utils
|
||||
import {getVarAssignment} from 'src/variables/utils/getVarAssignment'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
import {VariableAssignment} from 'src/types/ast'
|
||||
import {AppState} from 'src/types/v2'
|
||||
import {VariableValues, ValueSelections} from 'src/variables/types'
|
||||
import {Variable} from '@influxdata/influx'
|
||||
|
||||
type VariablesState = AppState['variables']['variables']
|
||||
type ValuesState = AppState['variables']['values']['contextID']
|
||||
|
||||
const getVariablesForOrgMemoized = memoizeOne(
|
||||
(variablesState: VariablesState, orgID: string) => {
|
||||
|
@ -19,7 +25,10 @@ const getVariablesForOrgMemoized = memoizeOne(
|
|||
}
|
||||
)
|
||||
|
||||
export const getVariablesForOrg = (state: AppState, orgID: string) => {
|
||||
export const getVariablesForOrg = (
|
||||
state: AppState,
|
||||
orgID: string
|
||||
): Variable[] => {
|
||||
return getVariablesForOrgMemoized(state.variables.variables, orgID)
|
||||
}
|
||||
|
||||
|
@ -27,11 +36,8 @@ export const getValueSelections = (
|
|||
state: AppState,
|
||||
contextID: string
|
||||
): ValueSelections => {
|
||||
const contextValues: VariableValues = get(
|
||||
state,
|
||||
`variables.values.${contextID}.values`,
|
||||
{}
|
||||
)
|
||||
const contextValues: VariableValues =
|
||||
get(state, `variables.values.${contextID}.values`) || {}
|
||||
|
||||
const selections: ValueSelections = Object.keys(contextValues).reduce(
|
||||
(acc, k) => {
|
||||
|
@ -48,3 +54,35 @@ export const getValueSelections = (
|
|||
|
||||
return selections
|
||||
}
|
||||
|
||||
const getVariableAssignmentsMemoized = memoizeOne(
|
||||
(
|
||||
valuesState: ValuesState,
|
||||
variablesState: VariablesState
|
||||
): VariableAssignment[] => {
|
||||
if (!valuesState) {
|
||||
return []
|
||||
}
|
||||
|
||||
const result = []
|
||||
|
||||
for (const [variableID, values] of Object.entries(valuesState.values)) {
|
||||
const variableName = get(variablesState, [variableID, 'variable', 'name'])
|
||||
|
||||
if (variableName) {
|
||||
result.push(getVarAssignment(variableName, values))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
)
|
||||
|
||||
export const getVariableAssignments = (
|
||||
state: AppState,
|
||||
contextID: string
|
||||
): VariableAssignment[] =>
|
||||
getVariableAssignmentsMemoized(
|
||||
state.variables.values[contextID],
|
||||
state.variables.variables
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue