Merge pull request #5747 from influxdata/5738/query_statuses

fix(ui): use multiple query statuses
pull/5748/head
Pavel Závora 2021-05-07 19:55:31 +02:00 committed by GitHub
commit d4f90987c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 146 additions and 72 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] {

View File

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

View File

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

View File

@ -38,6 +38,7 @@ import {
FuncArg,
Namespace,
QueryStatus,
QueryStatuses,
Tag,
Tags,
TagValues,
@ -134,6 +135,7 @@ export {
TemplateBuilderProps,
WriteDataMode,
QueryStatus,
QueryStatuses,
Host,
Layout,
QueryType,

View File

@ -33,6 +33,7 @@ export interface QueryStatus {
queryID: string
status: Status
}
export type QueryStatuses = Record<string, Status>
export interface Field {
value: string

View File

@ -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({})
})
})
})

View File

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