Handle deletion of a tempVar used in nested tempVar

Catch error and send notification of error
pull/10616/head
Iris Scholten 2018-07-09 15:17:35 -07:00
parent 234023dd5e
commit 4156c42684
5 changed files with 98 additions and 36 deletions

View File

@ -1,6 +1,7 @@
import {replace, RouterAction} from 'react-router-redux'
import _ from 'lodash'
import qs from 'qs'
import {Dispatch} from 'redux'
import {
getDashboards as getDashboardsAJAX,
@ -38,6 +39,7 @@ import {
notifyDashboardNotFound,
notifyInvalidZoomedTimeRangeValueInURLQuery,
notifyInvalidTimeRangeValueInURLQuery,
notifyInvalidTempVarValueInMetaQuery,
} from 'src/shared/copy/notifications'
import {getDeep} from 'src/utils/wrappers'
@ -396,7 +398,7 @@ const getDashboard = (state, dashboardId: number): Dashboard => {
// Thunkers
export const getDashboardsAsync = () => async (
dispatch
dispatch: Dispatch<Action>
): Promise<Dashboard[]> => {
try {
const {
@ -442,7 +444,7 @@ const removeUnselectedTemplateValues = (dashboard: Dashboard): Template[] => {
}
export const putDashboard = (dashboard: Dashboard) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
try {
// save only selected template values to server
@ -469,7 +471,7 @@ export const putDashboard = (dashboard: Dashboard) => async (
}
export const putDashboardByID = (dashboardID: number) => async (
dispatch,
dispatch: Dispatch<Action>,
getState
): Promise<void> => {
try {
@ -483,7 +485,7 @@ export const putDashboardByID = (dashboardID: number) => async (
}
export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
try {
const {data} = await updateDashboardCellAJAX(cell)
@ -495,7 +497,7 @@ export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
}
export const deleteDashboardAsync = (dashboard: Dashboard) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
dispatch(deleteDashboard(dashboard))
try {
@ -515,7 +517,7 @@ export const deleteDashboardAsync = (dashboard: Dashboard) => async (
export const addDashboardCellAsync = (
dashboard: Dashboard,
cellType?: CellType
) => async (dispatch): Promise<void> => {
) => async (dispatch: Dispatch<Action>): Promise<void> => {
try {
const {data} = await addDashboardCellAJAX(
dashboard,
@ -532,7 +534,7 @@ export const addDashboardCellAsync = (
export const cloneDashboardCellAsync = (
dashboard: Dashboard,
cell: Cell
) => async (dispatch): Promise<void> => {
) => async (dispatch: Dispatch<Action>): Promise<void> => {
try {
const clonedCell = getClonedDashboardCell(dashboard, cell)
const {data} = await addDashboardCellAJAX(dashboard, clonedCell)
@ -547,7 +549,7 @@ export const cloneDashboardCellAsync = (
export const deleteDashboardCellAsync = (
dashboard: Dashboard,
cell: Cell
) => async (dispatch): Promise<void> => {
) => async (dispatch: Dispatch<Action>): Promise<void> => {
try {
await deleteDashboardCellAJAX(cell)
dispatch(deleteDashboardCell(dashboard, cell))
@ -559,7 +561,7 @@ export const deleteDashboardCellAsync = (
}
export const importDashboardAsync = (dashboard: Dashboard) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
try {
// save only selected template values to server
@ -603,7 +605,7 @@ export const importDashboardAsync = (dashboard: Dashboard) => async (
}
const updateTimeRangeFromQueryParams = (dashboardID: number) => (
dispatch,
dispatch: Dispatch<Action>,
getState
): void => {
const {dashTimeV1} = getState()
@ -660,6 +662,27 @@ const updateTimeRangeFromQueryParams = (dashboardID: number) => (
dispatch(updateQueryParams(updatedQueryParams))
}
const hydrateTemplates = (
templates: Template[],
nonNestedTemplates: Template[],
proxyLink: string,
dispatch: Dispatch<Action>
) => {
return templates.map(async t => {
try {
return await hydrateTemplate(proxyLink, t, nonNestedTemplates)
} catch (error) {
const errorMessage = getDeep(error, 'data.message', '')
.replace(/.*(err):/g, '')
.trim()
dispatch(
notify(notifyInvalidTempVarValueInMetaQuery(t.tempVar, errorMessage))
)
return t
}
})
}
export const getDashboardWithTemplatesAsync = (
dashboardId: number,
source: Source
@ -679,17 +702,23 @@ export const getDashboardWithTemplatesAsync = (
const templateSelections = templateSelectionsFromQueryParams()
const proxyLink = source.links.proxy
const nonNestedTemplates = await Promise.all(
dashboard.templates
.filter(t => !isTemplateNested(t))
.map(t => hydrateTemplate(proxyLink, t, []))
hydrateTemplates(
dashboard.templates.filter(t => !isTemplateNested(t)),
[],
proxyLink,
dispatch
)
)
applyLocalSelections(nonNestedTemplates, templateSelections)
const nestedTemplates = await Promise.all(
dashboard.templates
.filter(t => isTemplateNested(t))
.map(t => hydrateTemplate(proxyLink, t, nonNestedTemplates))
hydrateTemplates(
dashboard.templates.filter(t => isTemplateNested(t)),
nonNestedTemplates,
proxyLink,
dispatch
)
)
applyLocalSelections(nestedTemplates, templateSelections)
@ -722,7 +751,7 @@ export const rehydrateNestedTemplatesAsync = (
}
export const updateTemplateQueryParams = (dashboardId: number) => (
dispatch,
dispatch: Dispatch<Action>,
getState
): void => {
const templates = getDashboard(getState(), dashboardId).templates

View File

@ -168,7 +168,20 @@ class DashboardPage extends Component<Props, State> {
const prevPath = getDeep(prevProps.location, 'pathname', null)
const thisPath = getDeep(this.props.location, 'pathname', null)
if (prevPath && thisPath && prevPath !== thisPath) {
const templates = getDeep<TempVarsModels.Template[]>(
this.props.dashboard,
'templates',
[]
).map(t => t.tempVar)
const prevTemplates = getDeep<TempVarsModels.Template[]>(
prevProps.dashboard,
'templates',
[]
).map(t => t.tempVar)
const isTemplateDeleted: boolean =
_.intersection(templates, prevTemplates).length !== prevTemplates.length
if ((prevPath && thisPath && prevPath !== thisPath) || isTemplateDeleted) {
this.getDashboard()
}
}

View File

@ -526,6 +526,16 @@ export const notifyBuilderDisabled = (): Notification => ({
// Template Variables & URL Queries
// ----------------------------------------------------------------------------
export const notifyInvalidTempVarValueInMetaQuery = (
tempVar: string,
errorMessage: string
): Notification => ({
...defaultErrorNotification,
icon: 'cube',
duration: 7500,
message: `Invalid query supplied for template variable ${tempVar}: ${errorMessage}`,
})
export const notifyInvalidTempVarValueInURLQuery = ({
key,
value,

View File

@ -16,28 +16,35 @@ export const hydrateTemplate = async (
return template
}
const query = templateReplace(makeQueryForTemplate(template.query), templates)
const response = await proxy({source: proxyLink, query})
const values = parseMetaQuery(query, response.data)
const type = TEMPLATE_VARIABLE_TYPES[template.type]
const selectedValue = getSelectedValue(template)
const selectedLocalValue = getLocalSelectedValue(template)
try {
const query = templateReplace(
makeQueryForTemplate(template.query),
templates
)
const response = await proxy({source: proxyLink, query})
const values = parseMetaQuery(query, response.data)
const type = TEMPLATE_VARIABLE_TYPES[template.type]
const selectedValue = getSelectedValue(template)
const selectedLocalValue = getLocalSelectedValue(template)
const templateValues = values.map(value => {
return {
type,
value,
selected: value === selectedValue,
localSelected: value === selectedLocalValue,
const templateValues = values.map(value => {
return {
type,
value,
selected: value === selectedValue,
localSelected: value === selectedLocalValue,
}
})
if (templateValues.length && !templateValues.find(v => v.selected)) {
// Handle stale selected value
templateValues[0].selected = true
}
})
if (templateValues.length && !templateValues.find(v => v.selected)) {
// Handle stale selected value
templateValues[0].selected = true
return {...template, values: templateValues}
} catch (error) {
throw error
}
return {...template, values: templateValues}
}
export const isTemplateNested = (template: Template): boolean => {

View File

@ -1,6 +1,8 @@
import React, {Component} from 'react'
import classnames from 'classnames'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplateControlDropdown from 'src/tempVars/components/TemplateControlDropdown'
import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology'
import TemplateVariableEditor from 'src/tempVars/components/TemplateVariableEditor'
@ -22,6 +24,7 @@ interface State {
isAdding: boolean
}
@ErrorHandling
class TemplateControlBar extends Component<Props, State> {
constructor(props) {
super(props)