Populate and substitute variables on dashboard load

pull/12521/head
Christopher Henn 2019-03-11 14:56:16 -07:00 committed by Chris Henn
parent 3796b328eb
commit 7642ae5563
14 changed files with 261 additions and 110 deletions

61
ui/package-lock.json generated
View File

@ -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": {

View File

@ -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",

View File

@ -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
}

View File

@ -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'

View File

@ -340,7 +340,7 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
},
ranges,
dashboards,
views,
views: {views},
} = state
const timeRange =

View File

@ -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},
},
}
}
}

View File

@ -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`)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 => ({

View File

@ -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
}

View File

@ -185,6 +185,7 @@ export const refreshVariableValues = (
dispatch(setValues(contextID, RemoteDataState.Done, values))
} catch (e) {
console.error(e)
dispatch(setValues(contextID, RemoteDataState.Error))
}
}

View File

@ -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
)