diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index 1e9b4b06b..a6baa24a5 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -1,3 +1,6 @@ +import {push} from 'react-router-redux' +import queryString from 'query-string' + import { getDashboards as getDashboardsAJAX, updateDashboard as updateDashboardAJAX, @@ -174,14 +177,14 @@ export const templateVariablesSelectedByName = (dashboardID, query) => ({ }, }) -export const editTemplateVariableOverrides = ( +export const updateTemplateVariableOverride = ( dashboardID, - tempVarOverrides + updatedTempVarOverride ) => ({ - type: 'EDIT_TEMPLATE_VARIABLE_OVERRIDES', + type: 'UPDATE_TEMPLATE_VARIABLE_OVERRIDE', payload: { dashboardID, - tempVarOverrides, + updatedTempVarOverride, }, }) @@ -362,10 +365,26 @@ export const updateTempVarValues = (source, dashboard) => async dispatch => { const {type, query, id} = tempsWithQueries[i] const parsed = parsers[type](data, query.tagKey || query.measurement) const vals = parsed[type] - dispatch(editTemplateVariableValues(dashboard.id, id, vals)) + dispatch(editTemplateVariableValues(+dashboard.id, id, vals)) }) } catch (error) { console.error(error) dispatch(errorThrown(error)) } } + +export const updateURLQueryValue = ( + location, + updatedQueryParam +) => dispatch => { + const updatedLocationQuery = {...location.query, ...updatedQueryParam} + const updatedSearchString = queryString.stringify(updatedLocationQuery) + const updatedSearch = {search: updatedSearchString} + const updatedLocation = { + ...location, + query: updatedLocationQuery, + ...updatedSearch, + } + + dispatch(push(updatedLocation)) +} diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 6190b4368..f0cb62d42 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -29,6 +29,11 @@ import { } from 'src/dashboards/actions/cellEditorOverlay' import {showOverlay} from 'src/shared/actions/overlayTechnology' +import { + applyDashboardTempVarOverrides, + stripTempVar, +} from 'src/dashboards/utils/templateVariableQueryGenerator' + import {dismissEditingAnnotation} from 'src/shared/actions/annotations' import { @@ -255,15 +260,31 @@ class DashboardPage extends Component { dashboardActions.deleteDashboardCellAsync(dashboard, cell) } - handleSelectTemplate = templateID => values => { + handleSelectTemplate = templateID => value => { const { dashboardActions, dashboard, params: {dashboardID}, + location, } = this.props - dashboardActions.templateVariableSelected(dashboard.id, templateID, [ - values, - ]) + // TODO: block viewer from doing this + const currentTempVar = dashboard.templates.find( + tempVar => tempVar.id === templateID + ) + const strippedTempVar = stripTempVar(currentTempVar.tempVar) + const isTempVarInURLQuery = !!location.query[strippedTempVar] + + if (isTempVarInURLQuery) { + const updatedQueryParam = { + [strippedTempVar]: value.value, + } + dashboardActions.updateURLQueryValue(location, updatedQueryParam) + dashboardActions.updateTemplateVariableOverride( + dashboardID, + updatedQueryParam + ) + } + dashboardActions.templateVariableSelected(dashboard.id, templateID, [value]) dashboardActions.putDashboardByID(dashboardID) } @@ -545,6 +566,7 @@ DashboardPage.propTypes = { gaugeColors: colorsNumberSchema.isRequired, lineColors: colorsStringSchema.isRequired, handleShowOverlay: func.isRequired, + tempVarOverrides: shape({}), } const mapStateToProps = (state, {params: {dashboardID}}) => { @@ -553,7 +575,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => { ephemeral: {inPresentationMode}, persisted: {autoRefresh, showTemplateControlBar}, }, - dashboardUI: {dashboards, cellQueryStatus}, + dashboardUI: {dashboards, cellQueryStatus, tempVarOverrides}, sources, dashTimeV1, auth: {me, isUsingAuth}, @@ -573,10 +595,17 @@ const mapStateToProps = (state, {params: {dashboardID}}) => { r => r.dashboardID === idNormalizer(TYPE_ID, dashboardID) ) || defaultTimeRange - const dashboard = dashboards.find( + let dashboard = dashboards.find( d => d.id === idNormalizer(TYPE_ID, dashboardID) ) + if (dashboard) { + dashboard = applyDashboardTempVarOverrides( + dashboard, + tempVarOverrides[dashboard.id] + ) + } + const selectedCell = cell return { diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index 72eb46b5d..2d104582f 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -2,6 +2,8 @@ import _ from 'lodash' import {timeRanges} from 'shared/data/timeRanges' import {NULL_HOVER_TIME} from 'src/shared/constants/tableGraph' +import {applyDashboardTempVarOverrides} from 'src/dashboards/utils/templateVariableQueryGenerator' + const {lower, upper} = timeRanges.find(tr => tr.lower === 'now() - 1h') const initialState = { @@ -235,54 +237,28 @@ export default function ui(state = initialState, action) { case 'TEMPLATE_VARIABLES_SELECTED_BY_NAME': { const {dashboardID, query} = action.payload - const selecteds = Object.keys(query).map(k => ({ - tempVar: `:${k}:`, - selectedValue: query[k], - })) - - const makeNewValue = (value, selected) => ({...value, selected}) - - const makeNewValues = template => ({ - ...template, - values: template.values.map( - value => - selecteds.find(({selectedValue}) => selectedValue === value.value) - ? makeNewValue(value, true) - : makeNewValue(value, false) - ), - }) - - const makeNewTemplates = templates => - templates.map( - template => - selecteds.find(({tempVar}) => tempVar === template.tempVar) - ? makeNewValues(template) - : template - ) - - const makeNewDashboard = dashboard => ({ - ...dashboard, - templates: makeNewTemplates(dashboard.templates), - }) - const newDashboards = state.dashboards.map( oldDashboard => oldDashboard.id === dashboardID - ? makeNewDashboard(oldDashboard) + ? applyDashboardTempVarOverrides(oldDashboard, query) : oldDashboard ) return {...state, dashboards: newDashboards} } - case 'EDIT_TEMPLATE_VARIABLE_OVERRIDES': { - const {dashboardID, tempVarOverrides} = action.payload + case 'UPDATE_TEMPLATE_VARIABLE_OVERRIDE': { + const {dashboardID, updatedTempVarOverride} = action.payload + const updatedTempVarOverrides = { + ...state.tempVarOverrides[dashboardID], + ...updatedTempVarOverride, + } return { ...state, tempVarOverrides: { ...state.tempVarOverrides, - [dashboardID]: tempVarOverrides, + [dashboardID]: updatedTempVarOverrides, }, } } diff --git a/ui/src/dashboards/utils/templateVariableQueryGenerator.js b/ui/src/dashboards/utils/templateVariableQueryGenerator.js index d15a43be7..9d54efe51 100644 --- a/ui/src/dashboards/utils/templateVariableQueryGenerator.js +++ b/ui/src/dashboards/utils/templateVariableQueryGenerator.js @@ -59,4 +59,53 @@ export const makeQueryForTemplate = ({influxql, db, measurement, tagKey}) => .replace(':measurement:', `"${measurement}"`) .replace(':tagKey:', `"${tagKey}"`) +export const stripTempVar = tempVarName => + tempVarName.substr(1, tempVarName.length - 2) + +const reconcileTempVarsWithOverrides = (currentTempVars, tempVarOverrides) => { + if (!tempVarOverrides) { + return currentTempVars + } + const reconciledTempVars = currentTempVars.map(tempVar => { + const {tempVar: name, values} = tempVar + const strippedTempVar = stripTempVar(name) + const overrideValue = tempVarOverrides[strippedTempVar] + + if (overrideValue) { + const isValidTempVarOverride = !!values.find( + ({value}) => value === overrideValue + ) + + if (isValidTempVarOverride) { + const overriddenValues = values.map(tempVarValue => { + const {value} = tempVarValue + if (value === overrideValue) { + return {...tempVarValue, selected: true} + } + return {...tempVarValue, selected: false} + }) + return {...tempVar, values: overriddenValues} + } + + // TODO: generate error notification ? + return tempVar + } + + return tempVar + }) + + return reconciledTempVars +} + +export const applyDashboardTempVarOverrides = ( + dashboard, + tempVarOverrides +) => ({ + ...dashboard, + templates: reconcileTempVarsWithOverrides( + dashboard.templates, + tempVarOverrides + ), +}) + export default generateTemplateVariableQuery diff --git a/ui/src/shared/middleware/queryStringConfig.js b/ui/src/shared/middleware/queryStringConfig.js index 5a9779383..53ff6dc3f 100644 --- a/ui/src/shared/middleware/queryStringConfig.js +++ b/ui/src/shared/middleware/queryStringConfig.js @@ -1,20 +1,21 @@ +import _ from 'lodash' // Middleware generally used for actions needing parsed queryStrings import queryString from 'query-string' import {enablePresentationMode} from 'src/shared/actions/app' import { templateVariablesSelectedByName, - editTemplateVariableOverrides, + updateTemplateVariableOverride, } from 'src/dashboards/actions' export const queryStringConfig = () => { let prevPath return next => action => { next(action) - const qs = queryString.parse(window.location.search) + const queries = queryString.parse(window.location.search) // Presentation Mode - if (qs.present === 'true') { + if (queries.present === 'true') { next(enablePresentationMode()) } @@ -24,8 +25,11 @@ export const queryStringConfig = () => { const currentPath = window.location.pathname const dashboardID = currentPath.match(dashboardRegex)[2] if (currentPath !== prevPath) { - next(templateVariablesSelectedByName(+dashboardID, qs)) - next(editTemplateVariableOverrides(dashboardID, qs)) + next(templateVariablesSelectedByName(+dashboardID, queries)) + _.each(queries, (v, k) => { + const query = {[k]: v} + next(updateTemplateVariableOverride(+dashboardID, query)) + }) } prevPath = currentPath