Merge pull request #15068 from influxdata/fix/disable-check-save

fix(alerting): disable save button if query is invalid
pull/15075/head
Andrew Watkins 2019-09-09 13:04:27 -07:00 committed by GitHub
commit cc22607bd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 50 deletions

View File

@ -0,0 +1,34 @@
const measurement = 'my_meas'
const field = 'my_field'
describe('Checks', () => {
beforeEach(() => {
cy.flush()
cy.signin().then(({body}) => {
const {
org: {id},
} = body
cy.writeData([`${measurement} ${field}=0`, `${measurement} ${field}=1`])
cy.wrap(body.org).as('org')
// visit the alerting index
cy.fixture('routes').then(({orgs, alerting}) => {
cy.visit(`${orgs}/${id}${alerting}`)
})
})
})
it('can validate a check', () => {
cy.getByTestID('create-check').click()
cy.getByTestID(`selector-list ${measurement}`).click()
cy.getByTestID('save-cell--button').should('be.disabled')
cy.getByTestID('checkeo--header alerting-tab').should('be.disabled')
cy.getByTestID(`selector-list ${field}`).click()
cy.getByTestID('save-cell--button').should('be.enabled')
cy.getByTestID('checkeo--header alerting-tab').should('be.enabled')
})
})

View File

@ -305,25 +305,14 @@ export const flush = () => {
}
export const writeData = (
lines: string[],
chunkSize: number = 100
lines: string[]
): Cypress.Chainable<Cypress.Response> => {
return cy.fixture('user').then(({org, bucket}) => {
let chunk: string[]
let chunkCt: number = 0
while (chunkCt < lines.length) {
chunk =
chunkCt + chunkSize <= lines.length
? lines.slice(chunkCt, chunkCt + chunkSize - 1)
: lines.slice(chunkCt, chunkCt + (chunkSize % lines.length))
cy.request({
method: 'POST',
url: '/api/v2/write?org=' + org + '&bucket=' + bucket,
body: chunk.join('\n'),
})
chunkCt += chunkSize
chunk = []
}
cy.request({
method: 'POST',
url: '/api/v2/write?org=' + org + '&bucket=' + bucket,
body: lines.join('\n'),
})
})
}

View File

@ -1,6 +1,5 @@
// Libraries
import React, {FunctionComponent} from 'react'
import {connect} from 'react-redux'
// Components
import {
@ -16,26 +15,20 @@ import {
} from '@influxdata/clockface'
// Utils
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
import {isDraftQueryAlertable} from 'src/timeMachine/utils/queryBuilder'
// Actions
import {setActiveTab} from 'src/timeMachine/actions'
// Types
import {AppState, TimeMachineTab, DashboardDraftQuery} from 'src/types'
import {TimeMachineTab, DashboardDraftQuery} from 'src/types'
interface DispatchProps {
interface Props {
setActiveTab: typeof setActiveTab
}
interface StateProps {
activeTab: TimeMachineTab
draftQueries: DashboardDraftQuery[]
}
type Props = DispatchProps & StateProps
const CheckAlertingButton: FunctionComponent<Props> = ({
setActiveTab,
draftQueries,
@ -101,6 +94,7 @@ const CheckAlertingButton: FunctionComponent<Props> = ({
<Radio.Button
key="alerting"
id="alerting"
testID="checkeo--header alerting-tab"
titleText="alerting"
value="alerting"
active={activeTab === 'alerting'}
@ -114,20 +108,7 @@ const CheckAlertingButton: FunctionComponent<Props> = ({
)
}
const mstp = (state: AppState): StateProps => {
const {activeTab, draftQueries} = getActiveTimeMachine(state)
return {activeTab, draftQueries}
}
const mdtp: DispatchProps = {
setActiveTab: setActiveTab,
}
export default connect<StateProps, DispatchProps, {}>(
mstp,
mdtp
)(CheckAlertingButton)
export default CheckAlertingButton
interface ChecklistItemProps {
selected: boolean

View File

@ -1,5 +1,6 @@
// Libraries
import React, {useState, FC, MouseEvent} from 'react'
import {connect} from 'react-redux'
// Components
import RenamablePageTitle from 'src/pageLayout/components/RenamablePageTitle'
@ -13,22 +14,53 @@ import {
} from '@influxdata/clockface'
import CheckAlertingButton from 'src/alerting/components/CheckAlertingButton'
// Utils
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
import {isCheckSaveable} from 'src/timeMachine/utils/queryBuilder'
// Actions
import {setActiveTab} from 'src/timeMachine/actions'
// Constants
import {DEFAULT_CHECK_NAME, CHECK_NAME_MAX_LENGTH} from 'src/alerting/constants'
// Types
import {RemoteDataState} from 'src/types'
import {
TimeMachineTab,
RemoteDataState,
AppState,
DashboardDraftQuery,
} from 'src/types'
interface Props {
interface OwnProps {
name: string
onSetName: (name: string) => void
onCancel: () => void
onSave: () => Promise<void>
}
interface StateProps {
activeTab: TimeMachineTab
draftQueries: DashboardDraftQuery[]
}
interface DispatchProps {
setActiveTab: typeof setActiveTab
}
type Props = OwnProps & StateProps & DispatchProps
const saveButtonClass = 'veo-header--save-cell-button'
const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
const CheckEOHeader: FC<Props> = ({
name,
onSetName,
onCancel,
onSave,
setActiveTab,
activeTab,
draftQueries,
}) => {
const [saveStatus, setSaveStatus] = useState(RemoteDataState.NotStarted)
const handleSave = async () => {
@ -47,10 +79,17 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
}
}
const saveButtonStatus =
saveStatus === RemoteDataState.Loading
? ComponentStatus.Loading
: ComponentStatus.Default
const saveButtonStatus = () => {
if (!isCheckSaveable(draftQueries)) {
return ComponentStatus.Disabled
}
if (saveStatus == RemoteDataState.Loading) {
return ComponentStatus.Loading
}
return ComponentStatus.Default
}
return (
<Page.Header fullWidth={true}>
@ -64,7 +103,11 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
/>
</Page.Header.Left>
<Page.Header.Center widthPixels={300}>
<CheckAlertingButton />
<CheckAlertingButton
activeTab={activeTab}
draftQueries={draftQueries}
setActiveTab={setActiveTab}
/>
</Page.Header.Center>
<Page.Header.Right>
<SquareButton
@ -77,7 +120,7 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
icon={IconFont.Checkmark}
color={ComponentColor.Success}
size={ComponentSize.Small}
status={saveButtonStatus}
status={saveButtonStatus()}
onClick={handleSave}
testID="save-cell--button"
/>
@ -86,4 +129,17 @@ const CheckEOHeader: FC<Props> = ({name, onSetName, onCancel, onSave}) => {
)
}
export default CheckEOHeader
const mstp = (state: AppState): StateProps => {
const {activeTab, draftQueries} = getActiveTimeMachine(state)
return {activeTab, draftQueries}
}
const mdtp: DispatchProps = {
setActiveTab: setActiveTab,
}
export default connect<StateProps, DispatchProps>(
mstp,
mdtp
)(CheckEOHeader)

View File

@ -46,6 +46,18 @@ export const isDraftQueryAlertable = (
}
}
export const isCheckSaveable = (
draftQueries: DashboardDraftQuery[]
): boolean => {
const {
oneQuery,
builderMode,
singleAggregateFunc,
singleField,
} = isDraftQueryAlertable(draftQueries)
return oneQuery && builderMode && singleAggregateFunc && singleField
}
export function buildQuery(builderConfig: BuilderConfig): string {
const {functions} = builderConfig