Merge pull request #5747 from influxdata/5738/query_statuses
fix(ui): use multiple query statusespull/5748/head
commit
d4f90987c2
|
@ -35,6 +35,7 @@
|
|||
1. [#5716](https://github.com/influxdata/chronograf/pull/5716): Do not fetch tag values when no measurement tags are available.
|
||||
1. [#5722](https://github.com/influxdata/chronograf/pull/5722): Filter out roles with unknown organization reference.
|
||||
1. [#5724](https://github.com/influxdata/chronograf/pull/5724): Detect actual flux support in flux proxy.
|
||||
1. [#5747](https://github.com/influxdata/chronograf/pull/5747): Manage individual execution status per query.
|
||||
|
||||
### Other
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ interface PassedProps {
|
|||
onSave: (cell: Cell | NewDefaultCell) => void
|
||||
source: SourcesModels.Source
|
||||
dashboardID: string
|
||||
queryStatus: QueriesModels.QueryStatus
|
||||
queryStatuses: QueriesModels.QueryStatuses
|
||||
dashboardTemplates: Template[]
|
||||
cell: Cell | NewDefaultCell
|
||||
dashboardTimeRange: TimeRange
|
||||
|
@ -134,7 +134,7 @@ class CellEditorOverlay extends Component<Props, State> {
|
|||
notify,
|
||||
source,
|
||||
sources,
|
||||
queryStatus,
|
||||
queryStatuses,
|
||||
dashboardRefresh,
|
||||
} = this.props
|
||||
|
||||
|
@ -158,7 +158,7 @@ class CellEditorOverlay extends Component<Props, State> {
|
|||
onResetFocus={this.handleResetFocus}
|
||||
onToggleStaticLegend={this.handleToggleStaticLegend}
|
||||
isStaticLegend={isStaticLegend}
|
||||
queryStatus={queryStatus}
|
||||
queryStatuses={queryStatuses}
|
||||
onUpdateScriptStatus={this.handleUpdateScriptStatus}
|
||||
refresh={dashboardRefresh}
|
||||
>
|
||||
|
|
|
@ -86,7 +86,7 @@ interface Props extends ManualRefreshProps, WithRouterProps {
|
|||
showTemplateVariableControlBar: boolean
|
||||
toggleTemplateVariableControlBar: typeof appActions.toggleTemplateVariableControlBar
|
||||
handleClickPresentationButton: AppActions.DelayEnablePresentationModeDispatcher
|
||||
cellQueryStatus: QueriesModels.QueryStatus
|
||||
cellQueryStatuses: QueriesModels.QueryStatuses
|
||||
errorThrown: ErrorsActions.ErrorThrownActionCreator
|
||||
meRole: string
|
||||
isUsingAuth: boolean
|
||||
|
@ -260,7 +260,7 @@ class DashboardPage extends Component<Props, State> {
|
|||
refreshRate,
|
||||
manualRefresh,
|
||||
onManualRefresh,
|
||||
cellQueryStatus,
|
||||
cellQueryStatuses,
|
||||
inPresentationMode,
|
||||
showTemplateVariableControlBar,
|
||||
handleClickPresentationButton,
|
||||
|
@ -301,7 +301,7 @@ class DashboardPage extends Component<Props, State> {
|
|||
fluxLinks={fluxLinks}
|
||||
cell={selectedCell}
|
||||
dashboardID={dashboardID}
|
||||
queryStatus={cellQueryStatus}
|
||||
queryStatuses={cellQueryStatuses}
|
||||
onSave={this.handleSaveEditedCell}
|
||||
onCancel={this.handleHideCellEditorOverlay}
|
||||
dashboardTemplates={_.get(dashboard, 'templates', [])}
|
||||
|
@ -593,7 +593,7 @@ const mstp = (state, {params: {dashboardID}}) => {
|
|||
},
|
||||
links,
|
||||
annotations: {displaySetting},
|
||||
dashboardUI: {dashboards, cellQueryStatus, zoomedTimeRange},
|
||||
dashboardUI: {dashboards, cellQueryStatuses, zoomedTimeRange},
|
||||
sources,
|
||||
auth: {me, isUsingAuth},
|
||||
cellEditorOverlay: {cell, timeRange: editorTimeRange},
|
||||
|
@ -620,7 +620,7 @@ const mstp = (state, {params: {dashboardID}}) => {
|
|||
zoomedTimeRange,
|
||||
autoRefresh,
|
||||
isUsingAuth,
|
||||
cellQueryStatus,
|
||||
cellQueryStatuses,
|
||||
inPresentationMode,
|
||||
selectedCell,
|
||||
editorTimeRange,
|
||||
|
|
|
@ -13,7 +13,7 @@ export const initialState: DashboardUIState = {
|
|||
timeRange: {lower, upper},
|
||||
zoomedTimeRange: {lower: null, upper: null},
|
||||
isEditMode: false,
|
||||
cellQueryStatus: {queryID: null, status: null},
|
||||
cellQueryStatuses: {},
|
||||
hoverTime: NULL_HOVER_TIME,
|
||||
activeCellID: '',
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ export default (
|
|||
dashboards,
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
return {...state, ...newState, cellQueryStatuses: {}}
|
||||
}
|
||||
|
||||
case ActionType.LoadDashboard: {
|
||||
const {dashboard} = action.payload
|
||||
const newDashboards = _.unionBy([dashboard], state.dashboards, 'id')
|
||||
|
||||
return {...state, dashboards: newDashboards}
|
||||
return {...state, dashboards: newDashboards, cellQueryStatuses: {}}
|
||||
}
|
||||
|
||||
case ActionType.SetDashboardTimeRange: {
|
||||
|
@ -140,8 +140,12 @@ export default (
|
|||
|
||||
case ActionType.EditCellQueryStatus: {
|
||||
const {queryID, status} = action.payload
|
||||
const {cellQueryStatuses} = state
|
||||
|
||||
return {...state, cellQueryStatus: {queryID, status}}
|
||||
return {
|
||||
...state,
|
||||
cellQueryStatuses: {...cellQueryStatuses, [queryID]: status},
|
||||
}
|
||||
}
|
||||
|
||||
case ActionType.TemplateVariableLocalSelected: {
|
||||
|
|
|
@ -8,10 +8,14 @@ export interface State {
|
|||
|
||||
export enum ActionType {
|
||||
UpdateSourceLink = 'DE_UPDATE_SOURCE_LINK',
|
||||
EditQueryStatus = 'EDIT_QUERY_STATUS',
|
||||
EditQueryStatus = 'DE_EDIT_QUERY_STATUS',
|
||||
ResetQueryStatuses = 'DE_RESET_STATUSES',
|
||||
}
|
||||
|
||||
export type Action = UpdateSourceLinkAction | EditQueryStatusAction
|
||||
export type Action =
|
||||
| UpdateSourceLinkAction
|
||||
| EditQueryStatusAction
|
||||
| ResetQueryStatusesAction
|
||||
|
||||
export interface UpdateSourceLinkAction {
|
||||
type: ActionType.UpdateSourceLink
|
||||
|
@ -44,3 +48,11 @@ export const editQueryStatus = (
|
|||
type: ActionType.EditQueryStatus,
|
||||
payload: {queryID, status},
|
||||
})
|
||||
|
||||
interface ResetQueryStatusesAction {
|
||||
type: ActionType.ResetQueryStatuses
|
||||
}
|
||||
|
||||
export const resetQueryStatuses = (): ResetQueryStatusesAction => ({
|
||||
type: ActionType.ResetQueryStatuses,
|
||||
})
|
||||
|
|
|
@ -5,7 +5,6 @@ import {getQueryConfigAndStatus} from 'src/shared/apis'
|
|||
import {errorThrown} from 'src/shared/actions/errors'
|
||||
import {
|
||||
QueryConfig,
|
||||
Status,
|
||||
Field,
|
||||
GroupBy,
|
||||
Tag,
|
||||
|
@ -35,7 +34,6 @@ export type Action =
|
|||
| ActionToggleTagAcceptance
|
||||
| ActionToggleField
|
||||
| ActionGroupByTag
|
||||
| ActionEditQueryStatus
|
||||
| ActionAddInitialField
|
||||
|
||||
export interface ActionAddQuery {
|
||||
|
@ -348,25 +346,6 @@ export const addInitialField = (
|
|||
},
|
||||
})
|
||||
|
||||
interface ActionEditQueryStatus {
|
||||
type: 'DE_EDIT_QUERY_STATUS'
|
||||
payload: {
|
||||
queryID: string
|
||||
status: Status
|
||||
}
|
||||
}
|
||||
|
||||
export const editQueryStatus = (
|
||||
queryID: string,
|
||||
status: Status
|
||||
): ActionEditQueryStatus => ({
|
||||
type: 'DE_EDIT_QUERY_STATUS',
|
||||
payload: {
|
||||
queryID,
|
||||
status,
|
||||
},
|
||||
})
|
||||
|
||||
interface ActionTimeShift {
|
||||
type: 'DE_TIME_SHIFT'
|
||||
payload: {
|
||||
|
|
|
@ -35,7 +35,10 @@ import {
|
|||
} from 'src/dashboards/actions'
|
||||
import {writeLineProtocolAsync} from 'src/data_explorer/actions/view/write'
|
||||
import {updateSourceLink as updateSourceLinkAction} from 'src/data_explorer/actions/queries'
|
||||
import {editQueryStatus as editQueryStatusAction} from 'src/data_explorer/actions/queries'
|
||||
import {
|
||||
editQueryStatus as editQueryStatusAction,
|
||||
resetQueryStatuses as resetQueryStatusesAction,
|
||||
} from 'src/data_explorer/actions/queries'
|
||||
import {setTimeZone as setTimeZoneAction} from 'src/shared/actions/app'
|
||||
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
@ -52,7 +55,7 @@ import {
|
|||
Source,
|
||||
Dashboard,
|
||||
QueryConfig,
|
||||
QueryStatus,
|
||||
QueryStatuses,
|
||||
Template,
|
||||
TemplateType,
|
||||
TemplateValueType,
|
||||
|
@ -86,7 +89,8 @@ interface PassedProps {
|
|||
newCell: Partial<Cell>
|
||||
) => Promise<{success: boolean; dashboard: Dashboard}>
|
||||
editQueryStatus: typeof editQueryStatusAction
|
||||
queryStatus: QueryStatus
|
||||
resetQueryStatuses: typeof resetQueryStatusesAction
|
||||
queryStatuses: QueryStatuses
|
||||
fluxLinks: Links
|
||||
notify: (message: Notification) => void
|
||||
sourceLink: string
|
||||
|
@ -128,6 +132,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
|||
isComponentMounted: false,
|
||||
activeQueryIndex: 0,
|
||||
}
|
||||
props.resetQueryStatuses()
|
||||
|
||||
props.onResetTimeMachine()
|
||||
}
|
||||
|
@ -169,7 +174,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
|||
timeZone,
|
||||
timeRange,
|
||||
fluxLinks,
|
||||
queryStatus,
|
||||
queryStatuses,
|
||||
editQueryStatus,
|
||||
updateSourceLink,
|
||||
onSetTimeZone,
|
||||
|
@ -194,7 +199,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
|||
sources={sources}
|
||||
fluxLinks={fluxLinks}
|
||||
templates={this.templates}
|
||||
queryStatus={queryStatus}
|
||||
queryStatuses={queryStatuses}
|
||||
isStaticLegend={isStaticLegend}
|
||||
editQueryStatus={editQueryStatus}
|
||||
updateSourceLink={updateSourceLink}
|
||||
|
@ -456,7 +461,7 @@ const mstp = state => {
|
|||
app: {
|
||||
persisted: {autoRefresh, timeZone},
|
||||
},
|
||||
dataExplorer: {timeRange, queryStatus, sourceLink},
|
||||
dataExplorer: {timeRange, queryStatuses, sourceLink},
|
||||
dashboardUI: {dashboards},
|
||||
sources,
|
||||
links,
|
||||
|
@ -469,7 +474,7 @@ const mstp = state => {
|
|||
timeRange,
|
||||
dashboards,
|
||||
sources,
|
||||
queryStatus,
|
||||
queryStatuses,
|
||||
sourceLink,
|
||||
}
|
||||
}
|
||||
|
@ -481,6 +486,7 @@ const mdtp = {
|
|||
handleGetDashboards: getDashboardsAsync,
|
||||
sendDashboardCell: sendDashboardCellAsync,
|
||||
editQueryStatus: editQueryStatusAction,
|
||||
resetQueryStatuses: resetQueryStatusesAction,
|
||||
notify: notifyAction,
|
||||
updateSourceLink: updateSourceLinkAction,
|
||||
onSetTimeZone: setTimeZoneAction,
|
||||
|
|
|
@ -6,7 +6,7 @@ import {DEState} from 'src/types/dataExplorer'
|
|||
|
||||
export const initialState: DEState = {
|
||||
sourceLink: '',
|
||||
queryStatus: {queryID: null, status: null},
|
||||
queryStatuses: {},
|
||||
}
|
||||
|
||||
export default (state = initialState, action: Action): DEState => {
|
||||
|
@ -19,8 +19,17 @@ export default (state = initialState, action: Action): DEState => {
|
|||
|
||||
case ActionType.EditQueryStatus: {
|
||||
const {queryID, status} = action.payload
|
||||
|
||||
return {...state, queryStatus: {queryID, status}}
|
||||
const {queryStatuses} = state
|
||||
return {
|
||||
...state,
|
||||
queryStatuses: {...queryStatuses, [queryID]: status},
|
||||
}
|
||||
}
|
||||
case ActionType.ResetQueryStatuses: {
|
||||
return {
|
||||
...state,
|
||||
queryStatuses: {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ import {
|
|||
Source,
|
||||
CellQuery,
|
||||
NotificationAction,
|
||||
QueryStatus,
|
||||
QueryStatuses,
|
||||
Status,
|
||||
Query,
|
||||
QueryType,
|
||||
|
@ -91,7 +91,7 @@ interface PassedProps {
|
|||
activeEditorTab: CEOTabs,
|
||||
onSetActiveEditorTab: (activeEditorTab: CEOTabs) => void
|
||||
) => JSX.Element
|
||||
queryStatus: QueryStatus
|
||||
queryStatuses: QueryStatuses
|
||||
onUpdateScriptStatus?: (status: ScriptStatus) => void
|
||||
onActiveQueryIndexChange?: (activeQueryIndex: number) => void
|
||||
refresh: RefreshRate
|
||||
|
@ -290,26 +290,17 @@ class TimeMachine extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private get queriesWorkingDraft(): QueryConfig[] {
|
||||
const {queryDrafts, queryStatus} = this.props
|
||||
const {queryDrafts, queryStatuses} = this.props
|
||||
|
||||
if (!queryDrafts || !queryDrafts.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
return queryDrafts.map(q => {
|
||||
if (queryStatus.queryID === q.id) {
|
||||
return {
|
||||
...q.queryConfig,
|
||||
source: this.source,
|
||||
status: queryStatus.status,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...q.queryConfig,
|
||||
source: this.source,
|
||||
}
|
||||
})
|
||||
return queryDrafts.map(q => ({
|
||||
...q.queryConfig,
|
||||
source: this.source,
|
||||
status: queryStatuses[String(q.id)],
|
||||
}))
|
||||
}
|
||||
|
||||
private get formattedSources(): SourceOption[] {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Template, TimeRange, QueryConfig, Status} from 'src/types'
|
||||
import {Template, TimeRange, QueryConfig, QueryStatuses} from 'src/types'
|
||||
import {ColorString} from 'src/types/colors'
|
||||
|
||||
export interface Axis {
|
||||
|
@ -150,10 +150,7 @@ export interface DashboardUIState {
|
|||
timeRange: TimeRange
|
||||
zoomedTimeRange: TimeRange
|
||||
isEditMode: boolean
|
||||
cellQueryStatus: {
|
||||
queryID: string | null
|
||||
status: Status
|
||||
}
|
||||
cellQueryStatuses: QueryStatuses
|
||||
hoverTime: string
|
||||
activeCellID: string
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {ColorNumber, ColorString} from 'src/types/colors'
|
||||
import {CellType, Axes, Status} from 'src/types'
|
||||
import {CellType, Axes, QueryStatuses} from 'src/types'
|
||||
import {
|
||||
DecimalPlaces,
|
||||
FieldOption,
|
||||
|
@ -16,10 +16,7 @@ export enum WriteDataMode {
|
|||
|
||||
export interface DEState {
|
||||
sourceLink: string
|
||||
queryStatus: {
|
||||
queryID: string | null
|
||||
status: Status
|
||||
}
|
||||
queryStatuses: QueryStatuses
|
||||
}
|
||||
|
||||
export interface VisualizationOptions {
|
||||
|
|
|
@ -38,6 +38,7 @@ import {
|
|||
FuncArg,
|
||||
Namespace,
|
||||
QueryStatus,
|
||||
QueryStatuses,
|
||||
Tag,
|
||||
Tags,
|
||||
TagValues,
|
||||
|
@ -134,6 +135,7 @@ export {
|
|||
TemplateBuilderProps,
|
||||
WriteDataMode,
|
||||
QueryStatus,
|
||||
QueryStatuses,
|
||||
Host,
|
||||
Layout,
|
||||
QueryType,
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface QueryStatus {
|
|||
queryID: string
|
||||
status: Status
|
||||
}
|
||||
export type QueryStatuses = Record<string, Status>
|
||||
|
||||
export interface Field {
|
||||
value: string
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
templateVariableLocalSelected,
|
||||
setActiveCell,
|
||||
updateTemplates,
|
||||
editCellQueryStatus,
|
||||
loadDashboard,
|
||||
} from 'src/dashboards/actions'
|
||||
|
||||
let state
|
||||
|
@ -212,4 +214,36 @@ describe('DataExplorer.Reducers.UI', () => {
|
|||
)
|
||||
})
|
||||
})
|
||||
describe('EDIT_CELL_QUERY_STATUS', () => {
|
||||
it('can edit cell query status', () => {
|
||||
let actual = reducer(
|
||||
initialState,
|
||||
editCellQueryStatus('query-id1', {success: 'yes'})
|
||||
)
|
||||
expect(actual.cellQueryStatuses).toEqual({'query-id1': {success: 'yes'}})
|
||||
actual = reducer(actual, editCellQueryStatus('query-id2', {error: 'no'}))
|
||||
expect(actual.cellQueryStatuses).toEqual({
|
||||
'query-id1': {success: 'yes'},
|
||||
'query-id2': {error: 'no'},
|
||||
})
|
||||
})
|
||||
it('resets query status on dashboards load', () => {
|
||||
let actual = reducer(
|
||||
initialState,
|
||||
editCellQueryStatus('query-id1', {success: 'yes'})
|
||||
)
|
||||
expect(actual.cellQueryStatuses).toEqual({'query-id1': {success: 'yes'}})
|
||||
actual = reducer(actual, loadDashboards(dashboards, d1.id))
|
||||
expect(actual.cellQueryStatuses).toEqual({})
|
||||
})
|
||||
it('resets query status on dashboard load', () => {
|
||||
let actual = reducer(
|
||||
initialState,
|
||||
editCellQueryStatus('query-id1', {success: 'yes'})
|
||||
)
|
||||
expect(actual.cellQueryStatuses).toEqual({'query-id1': {success: 'yes'}})
|
||||
actual = reducer(actual, loadDashboard(d1))
|
||||
expect(actual.cellQueryStatuses).toEqual({})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import reducer from 'src/data_explorer/reducers/queries'
|
||||
|
||||
import {
|
||||
editQueryStatus,
|
||||
resetQueryStatuses,
|
||||
updateSourceLink,
|
||||
} from 'src/data_explorer/actions/queries'
|
||||
|
||||
describe('DataExplorer.Reducers.queries', () => {
|
||||
it('it sets the default query statuses', () => {
|
||||
const state = reducer(undefined, {type: 'NOOP'})
|
||||
|
||||
expect(state.queryStatuses).toEqual({})
|
||||
})
|
||||
|
||||
it('updates query status', () => {
|
||||
const state = reducer(undefined, editQueryStatus('q1', {success: 'ok'}))
|
||||
|
||||
expect(state.queryStatuses).toEqual({q1: {success: 'ok'}})
|
||||
})
|
||||
it('updates multiple query statuses', () => {
|
||||
let state = reducer(undefined, editQueryStatus('q1', {success: 'ok'}))
|
||||
state = reducer(state, editQueryStatus('q1', {warn: 'ok'}))
|
||||
state = reducer(state, editQueryStatus('q2', {error: 'no'}))
|
||||
|
||||
expect(state.queryStatuses).toEqual({q1: {warn: 'ok'}, q2: {error: 'no'}})
|
||||
})
|
||||
it('resets query statuses', () => {
|
||||
let state = reducer(undefined, editQueryStatus('q1', {success: 'ok'}))
|
||||
state = reducer(state, resetQueryStatuses())
|
||||
|
||||
expect(state.queryStatuses).toEqual({})
|
||||
})
|
||||
it('updates source link', () => {
|
||||
let state = reducer(undefined, {type: 'NOOP'})
|
||||
expect(state.sourceLink).toEqual('')
|
||||
state = reducer(state, updateSourceLink('newLink'))
|
||||
|
||||
expect(state.sourceLink).toEqual('newLink')
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue