Handle deletion of a tempVar used in nested tempVar
Catch error and send notification of errorpull/10616/head
parent
234023dd5e
commit
4156c42684
|
@ -1,6 +1,7 @@
|
||||||
import {replace, RouterAction} from 'react-router-redux'
|
import {replace, RouterAction} from 'react-router-redux'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
|
import {Dispatch} from 'redux'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDashboards as getDashboardsAJAX,
|
getDashboards as getDashboardsAJAX,
|
||||||
|
@ -38,6 +39,7 @@ import {
|
||||||
notifyDashboardNotFound,
|
notifyDashboardNotFound,
|
||||||
notifyInvalidZoomedTimeRangeValueInURLQuery,
|
notifyInvalidZoomedTimeRangeValueInURLQuery,
|
||||||
notifyInvalidTimeRangeValueInURLQuery,
|
notifyInvalidTimeRangeValueInURLQuery,
|
||||||
|
notifyInvalidTempVarValueInMetaQuery,
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {getDeep} from 'src/utils/wrappers'
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
|
@ -396,7 +398,7 @@ const getDashboard = (state, dashboardId: number): Dashboard => {
|
||||||
// Thunkers
|
// Thunkers
|
||||||
|
|
||||||
export const getDashboardsAsync = () => async (
|
export const getDashboardsAsync = () => async (
|
||||||
dispatch
|
dispatch: Dispatch<Action>
|
||||||
): Promise<Dashboard[]> => {
|
): Promise<Dashboard[]> => {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
|
@ -442,7 +444,7 @@ const removeUnselectedTemplateValues = (dashboard: Dashboard): Template[] => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const putDashboard = (dashboard: Dashboard) => async (
|
export const putDashboard = (dashboard: Dashboard) => async (
|
||||||
dispatch
|
dispatch: Dispatch<Action>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
// save only selected template values to server
|
// save only selected template values to server
|
||||||
|
@ -469,7 +471,7 @@ export const putDashboard = (dashboard: Dashboard) => async (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const putDashboardByID = (dashboardID: number) => async (
|
export const putDashboardByID = (dashboardID: number) => async (
|
||||||
dispatch,
|
dispatch: Dispatch<Action>,
|
||||||
getState
|
getState
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
@ -483,7 +485,7 @@ export const putDashboardByID = (dashboardID: number) => async (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
|
export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
|
||||||
dispatch
|
dispatch: Dispatch<Action>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const {data} = await updateDashboardCellAJAX(cell)
|
const {data} = await updateDashboardCellAJAX(cell)
|
||||||
|
@ -495,7 +497,7 @@ export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteDashboardAsync = (dashboard: Dashboard) => async (
|
export const deleteDashboardAsync = (dashboard: Dashboard) => async (
|
||||||
dispatch
|
dispatch: Dispatch<Action>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
dispatch(deleteDashboard(dashboard))
|
dispatch(deleteDashboard(dashboard))
|
||||||
try {
|
try {
|
||||||
|
@ -515,7 +517,7 @@ export const deleteDashboardAsync = (dashboard: Dashboard) => async (
|
||||||
export const addDashboardCellAsync = (
|
export const addDashboardCellAsync = (
|
||||||
dashboard: Dashboard,
|
dashboard: Dashboard,
|
||||||
cellType?: CellType
|
cellType?: CellType
|
||||||
) => async (dispatch): Promise<void> => {
|
) => async (dispatch: Dispatch<Action>): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const {data} = await addDashboardCellAJAX(
|
const {data} = await addDashboardCellAJAX(
|
||||||
dashboard,
|
dashboard,
|
||||||
|
@ -532,7 +534,7 @@ export const addDashboardCellAsync = (
|
||||||
export const cloneDashboardCellAsync = (
|
export const cloneDashboardCellAsync = (
|
||||||
dashboard: Dashboard,
|
dashboard: Dashboard,
|
||||||
cell: Cell
|
cell: Cell
|
||||||
) => async (dispatch): Promise<void> => {
|
) => async (dispatch: Dispatch<Action>): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const clonedCell = getClonedDashboardCell(dashboard, cell)
|
const clonedCell = getClonedDashboardCell(dashboard, cell)
|
||||||
const {data} = await addDashboardCellAJAX(dashboard, clonedCell)
|
const {data} = await addDashboardCellAJAX(dashboard, clonedCell)
|
||||||
|
@ -547,7 +549,7 @@ export const cloneDashboardCellAsync = (
|
||||||
export const deleteDashboardCellAsync = (
|
export const deleteDashboardCellAsync = (
|
||||||
dashboard: Dashboard,
|
dashboard: Dashboard,
|
||||||
cell: Cell
|
cell: Cell
|
||||||
) => async (dispatch): Promise<void> => {
|
) => async (dispatch: Dispatch<Action>): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await deleteDashboardCellAJAX(cell)
|
await deleteDashboardCellAJAX(cell)
|
||||||
dispatch(deleteDashboardCell(dashboard, cell))
|
dispatch(deleteDashboardCell(dashboard, cell))
|
||||||
|
@ -559,7 +561,7 @@ export const deleteDashboardCellAsync = (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const importDashboardAsync = (dashboard: Dashboard) => async (
|
export const importDashboardAsync = (dashboard: Dashboard) => async (
|
||||||
dispatch
|
dispatch: Dispatch<Action>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
// save only selected template values to server
|
// save only selected template values to server
|
||||||
|
@ -603,7 +605,7 @@ export const importDashboardAsync = (dashboard: Dashboard) => async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateTimeRangeFromQueryParams = (dashboardID: number) => (
|
const updateTimeRangeFromQueryParams = (dashboardID: number) => (
|
||||||
dispatch,
|
dispatch: Dispatch<Action>,
|
||||||
getState
|
getState
|
||||||
): void => {
|
): void => {
|
||||||
const {dashTimeV1} = getState()
|
const {dashTimeV1} = getState()
|
||||||
|
@ -660,6 +662,27 @@ const updateTimeRangeFromQueryParams = (dashboardID: number) => (
|
||||||
dispatch(updateQueryParams(updatedQueryParams))
|
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 = (
|
export const getDashboardWithTemplatesAsync = (
|
||||||
dashboardId: number,
|
dashboardId: number,
|
||||||
source: Source
|
source: Source
|
||||||
|
@ -679,17 +702,23 @@ export const getDashboardWithTemplatesAsync = (
|
||||||
const templateSelections = templateSelectionsFromQueryParams()
|
const templateSelections = templateSelectionsFromQueryParams()
|
||||||
const proxyLink = source.links.proxy
|
const proxyLink = source.links.proxy
|
||||||
const nonNestedTemplates = await Promise.all(
|
const nonNestedTemplates = await Promise.all(
|
||||||
dashboard.templates
|
hydrateTemplates(
|
||||||
.filter(t => !isTemplateNested(t))
|
dashboard.templates.filter(t => !isTemplateNested(t)),
|
||||||
.map(t => hydrateTemplate(proxyLink, t, []))
|
[],
|
||||||
|
proxyLink,
|
||||||
|
dispatch
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
applyLocalSelections(nonNestedTemplates, templateSelections)
|
applyLocalSelections(nonNestedTemplates, templateSelections)
|
||||||
|
|
||||||
const nestedTemplates = await Promise.all(
|
const nestedTemplates = await Promise.all(
|
||||||
dashboard.templates
|
hydrateTemplates(
|
||||||
.filter(t => isTemplateNested(t))
|
dashboard.templates.filter(t => isTemplateNested(t)),
|
||||||
.map(t => hydrateTemplate(proxyLink, t, nonNestedTemplates))
|
nonNestedTemplates,
|
||||||
|
proxyLink,
|
||||||
|
dispatch
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
applyLocalSelections(nestedTemplates, templateSelections)
|
applyLocalSelections(nestedTemplates, templateSelections)
|
||||||
|
@ -722,7 +751,7 @@ export const rehydrateNestedTemplatesAsync = (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateTemplateQueryParams = (dashboardId: number) => (
|
export const updateTemplateQueryParams = (dashboardId: number) => (
|
||||||
dispatch,
|
dispatch: Dispatch<Action>,
|
||||||
getState
|
getState
|
||||||
): void => {
|
): void => {
|
||||||
const templates = getDashboard(getState(), dashboardId).templates
|
const templates = getDashboard(getState(), dashboardId).templates
|
||||||
|
|
|
@ -168,7 +168,20 @@ class DashboardPage extends Component<Props, State> {
|
||||||
const prevPath = getDeep(prevProps.location, 'pathname', null)
|
const prevPath = getDeep(prevProps.location, 'pathname', null)
|
||||||
const thisPath = getDeep(this.props.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()
|
this.getDashboard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -526,6 +526,16 @@ export const notifyBuilderDisabled = (): Notification => ({
|
||||||
|
|
||||||
// Template Variables & URL Queries
|
// 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 = ({
|
export const notifyInvalidTempVarValueInURLQuery = ({
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -16,28 +16,35 @@ export const hydrateTemplate = async (
|
||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = templateReplace(makeQueryForTemplate(template.query), templates)
|
try {
|
||||||
const response = await proxy({source: proxyLink, query})
|
const query = templateReplace(
|
||||||
const values = parseMetaQuery(query, response.data)
|
makeQueryForTemplate(template.query),
|
||||||
const type = TEMPLATE_VARIABLE_TYPES[template.type]
|
templates
|
||||||
const selectedValue = getSelectedValue(template)
|
)
|
||||||
const selectedLocalValue = getLocalSelectedValue(template)
|
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 => {
|
const templateValues = values.map(value => {
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
selected: value === selectedValue,
|
selected: value === selectedValue,
|
||||||
localSelected: value === selectedLocalValue,
|
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)) {
|
return {...template, values: templateValues}
|
||||||
// Handle stale selected value
|
} catch (error) {
|
||||||
templateValues[0].selected = true
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
return {...template, values: templateValues}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isTemplateNested = (template: Template): boolean => {
|
export const isTemplateNested = (template: Template): boolean => {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import React, {Component} from 'react'
|
import React, {Component} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
import TemplateControlDropdown from 'src/tempVars/components/TemplateControlDropdown'
|
import TemplateControlDropdown from 'src/tempVars/components/TemplateControlDropdown'
|
||||||
import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology'
|
import OverlayTechnology from 'src/reusable_ui/components/overlays/OverlayTechnology'
|
||||||
import TemplateVariableEditor from 'src/tempVars/components/TemplateVariableEditor'
|
import TemplateVariableEditor from 'src/tempVars/components/TemplateVariableEditor'
|
||||||
|
@ -22,6 +24,7 @@ interface State {
|
||||||
isAdding: boolean
|
isAdding: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
class TemplateControlBar extends Component<Props, State> {
|
class TemplateControlBar extends Component<Props, State> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
Loading…
Reference in New Issue