Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui) More reasonable aggregate window intervals for selected query durations #16536

Merged
merged 6 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
### UI Improvements

1. [16203](https://github.com/influxdata/influxdb/pull/16203): Move cloud navigation to top of page instead of within left side navigation
1. [16536](https://github.com/influxdata/influxdb/pull/16536): Adjust aggregate window periods to be more "reasonable". Use duration input with validation.


## v2.0.0-beta.1 [2020-01-08]

Expand Down
4 changes: 2 additions & 2 deletions ui/src/alerting/components/builder/CheckMetaCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {

// Constants
import {CHECK_OFFSET_OPTIONS} from 'src/alerting/constants'
import {DURATION_STRINGS} from 'src/timeMachine/constants/queryBuilder'
import {DURATIONS} from 'src/timeMachine/constants/queryBuilder'

// Types
import {AppState, CheckTagSet} from 'src/types'
Expand Down Expand Up @@ -66,7 +66,7 @@ const CheckMetaCard: FC<Props> = ({
<Form.Element label="Schedule Every">
<DurationInput
value={every}
suggestions={DURATION_STRINGS}
suggestions={DURATIONS}
onSubmit={onSelectCheckEvery}
/>
</Form.Element>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/alerting/components/notifications/RuleSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import DurationInput from 'src/shared/components/DurationInput'

// Types
import {RuleState} from './RuleOverlay.reducer'
import {DURATION_STRINGS} from 'src/timeMachine/constants/queryBuilder'
import {DURATIONS} from 'src/timeMachine/constants/queryBuilder'
import {NotificationRuleDraft} from 'src/types'
import {CHECK_OFFSET_OPTIONS} from 'src/alerting/constants'

Expand All @@ -26,7 +26,7 @@ const RuleSchedule: FC<Props> = ({rule, onChange}) => {
<DurationInput
value={every || ''}
onSubmit={onChange('every')}
suggestions={DURATION_STRINGS}
suggestions={DURATIONS}
placeholder="1d3h30s"
testID="rule-schedule-every--input"
/>
Expand Down
21 changes: 16 additions & 5 deletions ui/src/shared/components/DurationInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Libraries
import React, {useState, FC} from 'react'
import React, {useState, useEffect, FC} from 'react'
import {
Input,
ComponentStatus,
Expand All @@ -20,6 +20,7 @@ type Props = {
submitInvalid?: boolean
showDivider?: boolean
testID?: string
validFunction?: (input: string) => boolean
}

const DurationInput: FC<Props> = ({
Expand All @@ -30,14 +31,17 @@ const DurationInput: FC<Props> = ({
submitInvalid = true,
showDivider = true,
testID = 'duration-input',
validFunction = _ => false,
}) => {
const [isFocused, setIsFocused] = useState(false)

const [inputValue, setInputValue] = useState(value)

const inputStatus = isDurationParseable(inputValue)
? ComponentStatus.Default
: ComponentStatus.Error
useEffect(() => {
if (value != inputValue) {
setInputValue(value)
}
}, [value])

const handleClickSuggestion = (suggestion: string) => {
setInputValue(suggestion)
Expand All @@ -56,9 +60,16 @@ const DurationInput: FC<Props> = ({
}
}

const isValid = (i: string): boolean =>
isDurationParseable(i) || validFunction(i)

const inputStatus = isValid(inputValue)
? ComponentStatus.Default
: ComponentStatus.Error

const onChange = (i: string) => {
setInputValue(i)
if (submitInvalid || (!submitInvalid && isDurationParseable(i))) {
if (submitInvalid || (!submitInvalid && isValid(i))) {
onSubmit(i)
}
}
Expand Down
9 changes: 9 additions & 0 deletions ui/src/shared/constants/timeRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const pastHourTimeRange: SelectableDurationTimeRange = {
label: 'Past 1h',
duration: '1h',
type: 'selectable-duration',
windowPeriod: 10000, // 10s
}

export const pastThirtyDaysTimeRange: SelectableDurationTimeRange = {
Expand All @@ -20,6 +21,7 @@ export const pastThirtyDaysTimeRange: SelectableDurationTimeRange = {
label: 'Past 30d',
duration: '30d',
type: 'selectable-duration',
windowPeriod: 3600000, // 1h
}

export const pastFifteenMinTimeRange: SelectableDurationTimeRange = {
Expand All @@ -29,6 +31,7 @@ export const pastFifteenMinTimeRange: SelectableDurationTimeRange = {
label: 'Past 15m',
duration: '15m',
type: 'selectable-duration',
windowPeriod: 10000, // 10s
}

export const CUSTOM_TIME_RANGE: {label: string; type: 'custom'} = {
Expand All @@ -44,6 +47,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 5m',
duration: '5m',
type: 'selectable-duration',
windowPeriod: 10000, // 10s
},
pastFifteenMinTimeRange,
pastHourTimeRange,
Expand All @@ -54,6 +58,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 6h',
duration: '6h',
type: 'selectable-duration',
windowPeriod: 60000, // 1m
},
{
seconds: 43200,
Expand All @@ -62,6 +67,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 12h',
duration: '12h',
type: 'selectable-duration',
windowPeriod: 120000, // 2m
},
{
seconds: 86400,
Expand All @@ -70,6 +76,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 24h',
duration: '24h',
type: 'selectable-duration',
windowPeriod: 300000, // 5m
},
{
seconds: 172800,
Expand All @@ -78,6 +85,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 2d',
duration: '2d',
type: 'selectable-duration',
windowPeriod: 600000, // 10m
},
{
seconds: 604800,
Expand All @@ -86,6 +94,7 @@ export const SELECTABLE_TIME_RANGES: SelectableDurationTimeRange[] = [
label: 'Past 7d',
duration: '7d',
type: 'selectable-duration',
windowPeriod: 1800000, // 30 min
},
pastThirtyDaysTimeRange,
]
Expand Down
111 changes: 74 additions & 37 deletions ui/src/timeMachine/components/FunctionSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import React, {PureComponent, ChangeEvent} from 'react'
import {connect} from 'react-redux'

// Components
import {Input} from '@influxdata/clockface'
import {
Input,
FlexBox,
FlexDirection,
ComponentSize,
TextBlock,
InfluxColors,
} from '@influxdata/clockface'
import SelectorList from 'src/timeMachine/components/SelectorList'
import BuilderCard from 'src/timeMachine/components/builderCard/BuilderCard'
import DurationSelector, {
DurationOption,
} from 'src/shared/components/DurationSelector'
import DurationInput from 'src/shared/components/DurationInput'

// Actions
import {
Expand Down Expand Up @@ -59,30 +64,52 @@ class FunctionSelector extends PureComponent<Props, State> {
public state: State = {searchTerm: ''}

public render() {
const {
selectedFunctions,
onSelectAggregateWindow,
isInCheckOverlay,
} = this.props
const {isInCheckOverlay} = this.props

const {searchTerm} = this.state

return (
<BuilderCard className="function-selector" testID="function-selector">
<BuilderCard.Header title="Aggregate Functions" />
<BuilderCard.Menu>
<DurationSelector
onSelectDuration={onSelectAggregateWindow}
selectedDuration={this.duration}
durations={this.durations}
disabled={!selectedFunctions.length}
/>
<Input
className="tag-selector--search"
value={searchTerm}
onChange={this.handleSetSearchTerm}
placeholder="Search functions..."
/>
<FlexBox
direction={FlexDirection.Column}
margin={ComponentSize.Small}
>
<FlexBox
direction={FlexDirection.Row}
margin={ComponentSize.Small}
stretchToFitWidth
>
<FlexBox.Child grow={2} testID="component-spacer--flex-child">
<Input
className="tag-selector--search"
value={searchTerm}
onChange={this.handleSetSearchTerm}
placeholder="Search functions..."
/>
</FlexBox.Child>
</FlexBox>
<FlexBox
direction={FlexDirection.Row}
margin={ComponentSize.Small}
stretchToFitWidth
testID="component-spacer"
>
<TextBlock
textColor={InfluxColors.Sidewalk}
text="Window period:"
/>
<FlexBox.Child grow={2} testID="component-spacer--flex-child">
<DurationInput
onSubmit={this.handleSelectAggregateWindow}
value={this.duration}
suggestions={this.durations}
submitInvalid={false}
validFunction={this.windowInputValid}
/>
</FlexBox.Child>
</FlexBox>
</FlexBox>
</BuilderCard.Menu>
<SelectorList
items={this.functions}
Expand All @@ -94,28 +121,27 @@ class FunctionSelector extends PureComponent<Props, State> {
)
}

private get autoLabel(): string {
const {autoWindowPeriod} = this.props
return autoWindowPeriod
? `${AGG_WINDOW_AUTO} (${millisecondsToDuration(autoWindowPeriod)})`
: AGG_WINDOW_AUTO
}

private get duration(): string {
const {aggregateWindow} = this.props

return aggregateWindow.period || AGG_WINDOW_AUTO
if (!aggregateWindow.period || aggregateWindow.period === AGG_WINDOW_AUTO) {
return this.autoLabel
}

return aggregateWindow.period
}

private get durations(): DurationOption[] {
private get durations(): string[] {
return this.props.isInCheckOverlay
? DURATIONS
: [...this.autoNoneDurations, ...DURATIONS]
}

private get autoNoneDurations(): DurationOption[] {
const {autoWindowPeriod} = this.props
const autoLabel = autoWindowPeriod
? `Auto (${millisecondsToDuration(autoWindowPeriod)})`
: 'Auto'

return [
{duration: AGG_WINDOW_AUTO, displayText: autoLabel},
{duration: AGG_WINDOW_NONE, displayText: 'None'},
]
: [this.autoLabel, AGG_WINDOW_NONE, ...DURATIONS]
}

private get functions(): string[] {
Expand All @@ -140,6 +166,17 @@ class FunctionSelector extends PureComponent<Props, State> {

onSelectFunction(functionName)
}

private handleSelectAggregateWindow = (input: string) => {
if (input.startsWith(AGG_WINDOW_AUTO)) {
this.props.onSelectAggregateWindow(AGG_WINDOW_AUTO)
return
}
this.props.onSelectAggregateWindow(input)
}

private windowInputValid = (input: string): boolean =>
input == 'none' || input == this.autoLabel
}

const mstp = (state: AppState): StateProps => {
Expand Down
15 changes: 0 additions & 15 deletions ui/src/timeMachine/constants/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,6 @@ export const AGG_WINDOW_AUTO = 'auto'
export const AGG_WINDOW_NONE = 'none'

export const DURATIONS = [
{duration: '5s', displayText: 'Every 5 seconds'},
{duration: '15s', displayText: 'Every 15 seconds'},
{duration: '1m', displayText: 'Every minute'},
{duration: '5m', displayText: 'Every 5 minutes'},
{duration: '15m', displayText: 'Every 15 minutes'},
{duration: '1h', displayText: 'Every hour'},
{duration: '6h', displayText: 'Every 6 hours'},
{duration: '12h', displayText: 'Every 12 hours'},
{duration: '24h', displayText: 'Every 24 hours'},
{duration: '2d', displayText: 'Every 2 days'},
{duration: '7d', displayText: 'Every 7 days'},
{duration: '30d', displayText: 'Every 30 days'},
]

export const DURATION_STRINGS = [
'5s',
'15s',
'1m',
Expand Down
1 change: 1 addition & 0 deletions ui/src/types/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface SelectableDurationTimeRange {
label: string
duration: string
type: 'selectable-duration'
windowPeriod: number
}

export interface DurationTimeRange {
Expand Down
11 changes: 10 additions & 1 deletion ui/src/variables/utils/getWindowVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {WINDOW_PERIOD} from 'src/variables/constants'

// Types
import {VariableAssignment, Package} from 'src/types/ast'
import {SELECTABLE_TIME_RANGES} from 'src/shared/constants/timeRanges'

const DESIRED_POINTS_PER_GRAPH = 360
const FALLBACK_WINDOW_PERIOD = 15000
Expand Down Expand Up @@ -63,7 +64,15 @@ export const getWindowPeriod = (
files: [ast, buildVarsOption(variables)],
}

const queryDuration = getMinDurationFromAST(substitutedAST)
const queryDuration = getMinDurationFromAST(substitutedAST) // in ms

const foundDuration = SELECTABLE_TIME_RANGES.find(
tr => tr.seconds * 1000 === queryDuration
)

if (foundDuration) {
return foundDuration.windowPeriod
}

return Math.round(queryDuration / DESIRED_POINTS_PER_GRAPH)
} catch (error) {
Expand Down