feat: Add group() to Query Builder
parent
5569193116
commit
dd41af3d7a
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -3,11 +3,11 @@
|
|||
### Features
|
||||
|
||||
1. [16234](https://github.com/influxdata/influxdb/pull/16234): Add support for notification endpoints to influx templates/pkgs.
|
||||
2. [16242](https://github.com/influxdata/influxdb/pull/16242): Drop id prefix for secret key requirement for notification endpoints
|
||||
3. [16259](https://github.com/influxdata/influxdb/pull/16259): Add support for check resource to pkger parser
|
||||
4. [16262](https://github.com/influxdata/influxdb/pull/16262): Add support for check resource pkger dry run functionality
|
||||
5. [16275](https://github.com/influxdata/influxdb/pull/16275): Add support for check resource pkger apply functionality
|
||||
6. [16283](https://github.com/influxdata/influxdb/pull/16283): Add support for check resource pkger export functionality
|
||||
1. [16242](https://github.com/influxdata/influxdb/pull/16242): Drop id prefix for secret key requirement for notification endpoints
|
||||
1. [16259](https://github.com/influxdata/influxdb/pull/16259): Add support for check resource to pkger parser
|
||||
1. [16262](https://github.com/influxdata/influxdb/pull/16262): Add support for check resource pkger dry run functionality
|
||||
1. [16275](https://github.com/influxdata/influxdb/pull/16275): Add support for check resource pkger apply functionality
|
||||
1. [16283](https://github.com/influxdata/influxdb/pull/16283): Add support for check resource pkger export functionality
|
||||
1. [16212](https://github.com/influxdata/influxdb/pull/16212): Add new kv.ForwardCursor interface
|
||||
1. [16297](https://github.com/influxdata/influxdb/pull/16297): Add support for notification rule to pkger parser
|
||||
1. [16298](https://github.com/influxdata/influxdb/pull/16298): Add support for notification rule pkger dry run functionality
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
1. [16322](https://github.com/influxdata/influxdb/pull/16322): Add support for tasks to pkger dry run functionality
|
||||
1. [16323](https://github.com/influxdata/influxdb/pull/16323): Add support for tasks to pkger apply functionality
|
||||
1. [16324](https://github.com/influxdata/influxdb/pull/16324): Add support for tasks to pkger export functionality
|
||||
1. [16226](https://github.com/influxdata/influxdb/pull/16226): Add group() to Query Builder
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -349,7 +349,8 @@ describe('DataExplorer', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('raw script editing', () => {
|
||||
// todo: investigate flakiness of this test: https://github.com/influxdata/influxdb/issues/16330
|
||||
describe.skip('raw script editing', () => {
|
||||
beforeEach(() => {
|
||||
cy.getByTestID('switch-to-script-editor').click()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ logfile = ""
|
|||
hostname = ""
|
||||
## If set to true, do no set the "host" tag in the telegraf agent.
|
||||
omit_hostname = false
|
||||
`
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: '__default__',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export const OSS_FLAGS = {
|
|||
monacoEditor: false,
|
||||
downloadCellCSV: false,
|
||||
telegrafEditor: false,
|
||||
queryBuilderGrouping: false,
|
||||
}
|
||||
|
||||
export const CLOUD_FLAGS = {
|
||||
|
|
@ -16,6 +17,7 @@ export const CLOUD_FLAGS = {
|
|||
cloudBilling: CLOUD_BILLING_VISIBLE, // should be visible in dev and acceptance, but not in cloud
|
||||
downloadCellCSV: false,
|
||||
telegrafEditor: false,
|
||||
queryBuilderGrouping: false,
|
||||
}
|
||||
|
||||
export const isFlagEnabled = (flagName: string, equals?: string | boolean) => {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ import {
|
|||
|
||||
// Types
|
||||
import {Dispatch} from 'redux-thunk'
|
||||
import {GetState, RemoteDataState} from 'src/types'
|
||||
import {BuilderAggregateFunctionType} from 'src/client/generatedRoutes'
|
||||
import {
|
||||
BuilderAggregateFunctionType,
|
||||
GetState,
|
||||
RemoteDataState,
|
||||
} from 'src/types'
|
||||
import {BuilderFunctionsType} from '@influxdata/influx'
|
||||
|
||||
export type Action =
|
||||
|
|
@ -285,7 +288,8 @@ export const selectTagValue = (index: number, value: string) => (
|
|||
timeMachines: {activeTimeMachineID},
|
||||
} = state
|
||||
const tags = getActiveQuery(state).builderConfig.tags
|
||||
const values = tags[index].values
|
||||
const currentTag = tags[index]
|
||||
const values = currentTag.values
|
||||
|
||||
let newValues: string[]
|
||||
|
||||
|
|
@ -293,7 +297,7 @@ export const selectTagValue = (index: number, value: string) => (
|
|||
newValues = values.filter(v => v !== value)
|
||||
} else if (
|
||||
activeTimeMachineID === 'alerting' &&
|
||||
tags[index].key === '_field'
|
||||
currentTag.key === '_field'
|
||||
) {
|
||||
newValues = [value]
|
||||
} else {
|
||||
|
|
@ -302,6 +306,11 @@ export const selectTagValue = (index: number, value: string) => (
|
|||
|
||||
dispatch(setBuilderTagValuesSelection(index, newValues))
|
||||
|
||||
// don't add a new tag filter if we're grouping
|
||||
if (currentTag.aggregateFunctionType === 'group') {
|
||||
return
|
||||
}
|
||||
|
||||
if (index === tags.length - 1 && newValues.length) {
|
||||
dispatch(addTagSelector())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -31,17 +31,22 @@ import {
|
|||
} from 'src/timeMachine/actions/queryBuilder'
|
||||
|
||||
// Utils
|
||||
import {toComponentStatus} from 'src/shared/utils/toComponentStatus'
|
||||
import DefaultDebouncer from 'src/shared/utils/debouncer'
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
import {toComponentStatus} from 'src/shared/utils/toComponentStatus'
|
||||
import {
|
||||
getActiveQuery,
|
||||
getActiveTagValues,
|
||||
getActiveTimeMachine,
|
||||
getIsInCheckOverlay,
|
||||
} from 'src/timeMachine/selectors'
|
||||
|
||||
// Types
|
||||
import {AppState, RemoteDataState} from 'src/types'
|
||||
import {BuilderAggregateFunctionType} from 'src/client'
|
||||
import {
|
||||
AppState,
|
||||
BuilderAggregateFunctionType,
|
||||
RemoteDataState,
|
||||
} from 'src/types'
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 500
|
||||
|
||||
|
|
@ -80,8 +85,6 @@ type Props = StateProps & DispatchProps & OwnProps
|
|||
class TagSelector extends PureComponent<Props> {
|
||||
private debouncer = new DefaultDebouncer()
|
||||
|
||||
// bucky: this will currently always be 'Filter'
|
||||
// updates to this are imminent
|
||||
private renderAggregateFunctionType(
|
||||
aggregateFunctionType: BuilderAggregateFunctionType
|
||||
) {
|
||||
|
|
@ -92,19 +95,32 @@ class TagSelector extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {aggregateFunctionType, index} = this.props
|
||||
|
||||
return (
|
||||
<BuilderCard>
|
||||
<BuilderCard.Header
|
||||
title={this.renderAggregateFunctionType(aggregateFunctionType)}
|
||||
onDelete={index !== 0 && this.handleRemoveTagSelector}
|
||||
/>
|
||||
{this.header}
|
||||
{this.body}
|
||||
</BuilderCard>
|
||||
)
|
||||
}
|
||||
|
||||
private get header() {
|
||||
const {aggregateFunctionType, index} = this.props
|
||||
|
||||
return isFlagEnabled('queryBuilderGrouping') ? (
|
||||
<BuilderCard.DropdownHeader
|
||||
options={['filter', 'group']}
|
||||
selectedOption={this.renderAggregateFunctionType(aggregateFunctionType)}
|
||||
onDelete={index !== 0 && this.handleRemoveTagSelector}
|
||||
onSelect={this.handleAggregateFunctionSelect}
|
||||
/>
|
||||
) : (
|
||||
<BuilderCard.Header
|
||||
title={this.renderAggregateFunctionType(aggregateFunctionType)}
|
||||
onDelete={index !== 0 && this.handleRemoveTagSelector}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private get body() {
|
||||
const {
|
||||
index,
|
||||
|
|
@ -265,33 +281,51 @@ class TagSelector extends PureComponent<Props> {
|
|||
|
||||
onSearchValues(index)
|
||||
}
|
||||
|
||||
private handleAggregateFunctionSelect = (
|
||||
option: BuilderAggregateFunctionType
|
||||
) => {
|
||||
const {index, onSetBuilderAggregateFunctionType} = this.props
|
||||
onSetBuilderAggregateFunctionType(option, index)
|
||||
}
|
||||
}
|
||||
|
||||
const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
|
||||
const activeQueryBuilder = getActiveTimeMachine(state).queryBuilder
|
||||
|
||||
const {
|
||||
keys,
|
||||
keysSearchTerm,
|
||||
keysStatus,
|
||||
values,
|
||||
valuesSearchTerm,
|
||||
valuesStatus,
|
||||
} = getActiveTimeMachine(state).queryBuilder.tags[ownProps.index]
|
||||
} = activeQueryBuilder.tags[ownProps.index]
|
||||
|
||||
const tags = getActiveQuery(state).builderConfig.tags
|
||||
|
||||
let emptyText: string
|
||||
const previousTagSelector = tags[ownProps.index - 1]
|
||||
if (
|
||||
ownProps.index === 0 ||
|
||||
!previousTagSelector ||
|
||||
!previousTagSelector.key
|
||||
) {
|
||||
emptyText = ''
|
||||
} else {
|
||||
emptyText = `Select a ${tags[ownProps.index - 1].key} value first`
|
||||
}
|
||||
|
||||
const {
|
||||
key: selectedKey,
|
||||
values: selectedValues,
|
||||
aggregateFunctionType,
|
||||
} = tags[ownProps.index]
|
||||
|
||||
let emptyText: string
|
||||
|
||||
if (ownProps.index === 0 || !tags[ownProps.index - 1].key) {
|
||||
emptyText = ''
|
||||
} else {
|
||||
emptyText = `Select a ${tags[ownProps.index - 1].key} value first`
|
||||
}
|
||||
|
||||
const values = getActiveTagValues(
|
||||
activeQueryBuilder.tags,
|
||||
aggregateFunctionType,
|
||||
ownProps.index
|
||||
)
|
||||
const isInCheckOverlay = getIsInCheckOverlay(state)
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import classnames from 'classnames'
|
|||
|
||||
// Components
|
||||
import BuilderCardHeader from 'src/timeMachine/components/builderCard/BuilderCardHeader'
|
||||
import BuilderCardDropdownHeader from 'src/timeMachine/components/builderCard/BuilderCardDropdownHeader'
|
||||
import BuilderCardMenu from 'src/timeMachine/components/builderCard/BuilderCardMenu'
|
||||
import BuilderCardBody from 'src/timeMachine/components/builderCard/BuilderCardBody'
|
||||
import BuilderCardEmpty from 'src/timeMachine/components/builderCard/BuilderCardEmpty'
|
||||
|
|
@ -16,6 +17,7 @@ interface Props {
|
|||
|
||||
export default class BuilderCard extends PureComponent<Props> {
|
||||
public static Header = BuilderCardHeader
|
||||
public static DropdownHeader = BuilderCardDropdownHeader
|
||||
public static Menu = BuilderCardMenu
|
||||
public static Body = BuilderCardBody
|
||||
public static Empty = BuilderCardEmpty
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
import {SelectDropdown} from '@influxdata/clockface'
|
||||
import {BuilderAggregateFunctionType} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
options: string[]
|
||||
selectedOption: string
|
||||
testID: string
|
||||
onSelect?: (option: BuilderAggregateFunctionType) => void
|
||||
onDelete?: () => void
|
||||
}
|
||||
|
||||
const emptyFunction = () => {}
|
||||
|
||||
export default class BuilderCardDropdownHeader extends PureComponent<Props> {
|
||||
public static defaultProps = {
|
||||
testID: 'builder-card--header',
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {children, options, onSelect, selectedOption, testID} = this.props
|
||||
|
||||
return (
|
||||
<div className="builder-card--header" data-testid={testID}>
|
||||
<SelectDropdown
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
testID="select-option-dropdown"
|
||||
onSelect={onSelect ? onSelect : emptyFunction}
|
||||
/>
|
||||
|
||||
{children}
|
||||
{this.deleteButton}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get deleteButton(): JSX.Element | undefined {
|
||||
const {onDelete} = this.props
|
||||
|
||||
if (onDelete) {
|
||||
return <div className="builder-card--delete" onClick={onDelete} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -671,11 +671,16 @@ export const timeMachineReducer = (
|
|||
const {index, builderAggregateFunctionType} = action.payload
|
||||
const draftQuery = draftState.draftQueries[draftState.activeQueryIndex]
|
||||
|
||||
buildActiveQuery(draftState)
|
||||
if (
|
||||
draftQuery &&
|
||||
draftQuery.builderConfig &&
|
||||
draftQuery.builderConfig.tags[index]
|
||||
) {
|
||||
// When switching between filtering and grouping
|
||||
// we want to clear out any previously selected values
|
||||
draftQuery.builderConfig.tags[index].values = []
|
||||
|
||||
draftQuery.builderConfig.tags[
|
||||
index
|
||||
].aggregateFunctionType = builderAggregateFunctionType
|
||||
|
|
@ -761,6 +766,8 @@ export const timeMachineReducer = (
|
|||
|
||||
draftState.queryBuilder.tags[index].values = values
|
||||
draftState.queryBuilder.tags[index].valuesStatus = RemoteDataState.Done
|
||||
|
||||
buildActiveQuery(draftState)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -829,10 +836,13 @@ export const timeMachineReducer = (
|
|||
const {index} = action.payload
|
||||
const draftQuery = draftState.draftQueries[draftState.activeQueryIndex]
|
||||
|
||||
const selectedValues = draftQuery.builderConfig.tags[index].values
|
||||
let selectedValues = []
|
||||
if (draftQuery) {
|
||||
selectedValues = draftQuery.builderConfig.tags[index].values
|
||||
|
||||
draftQuery.builderConfig.tags.splice(index, 1)
|
||||
draftState.queryBuilder.tags.splice(index, 1)
|
||||
draftQuery.builderConfig.tags.splice(index, 1)
|
||||
draftState.queryBuilder.tags.splice(index, 1)
|
||||
}
|
||||
|
||||
if (selectedValues.length) {
|
||||
buildActiveQuery(draftState)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
// Funcs
|
||||
import {getStartTime, getEndTime} from 'src/timeMachine/selectors/index'
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
import {mocked} from 'ts-jest/utils'
|
||||
jest.mock('src/shared/utils/featureFlag')
|
||||
|
||||
import {
|
||||
getActiveTagValues,
|
||||
getStartTime,
|
||||
getEndTime,
|
||||
} from 'src/timeMachine/selectors/index'
|
||||
import moment from 'moment'
|
||||
|
||||
import {
|
||||
|
|
@ -59,3 +67,107 @@ describe('TimeMachine.Selectors.Index', () => {
|
|||
expect(getEndTime(pastThirtyDaysTimeRange)).toBeGreaterThanOrEqual(now)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getting active tag values', () => {
|
||||
const activeQueryTags = [
|
||||
{
|
||||
keys: [
|
||||
'_field',
|
||||
'_measurement',
|
||||
'cpu',
|
||||
'device',
|
||||
'fstype',
|
||||
'host',
|
||||
'interface',
|
||||
'mode',
|
||||
'name',
|
||||
'path',
|
||||
],
|
||||
values: [
|
||||
'cpu',
|
||||
'disk',
|
||||
'diskio',
|
||||
'mem',
|
||||
'net',
|
||||
'processes',
|
||||
'swap',
|
||||
'system',
|
||||
],
|
||||
},
|
||||
{
|
||||
keys: ['_field', 'host'],
|
||||
values: [
|
||||
'active',
|
||||
'available',
|
||||
'available_percent',
|
||||
'buffered',
|
||||
'cached',
|
||||
'commit_limit',
|
||||
'committed_as',
|
||||
'dirty',
|
||||
'free',
|
||||
'high_free',
|
||||
'high_total',
|
||||
'huge_page_size',
|
||||
'huge_pages_free',
|
||||
'huge_pages_total',
|
||||
'inactive',
|
||||
'low_free',
|
||||
'low_total',
|
||||
'mapped',
|
||||
'page_tables',
|
||||
'shared',
|
||||
'slab',
|
||||
'swap_cached',
|
||||
'swap_free',
|
||||
'swap_total',
|
||||
'total',
|
||||
'used',
|
||||
'used_percent',
|
||||
'vmalloc_chunk',
|
||||
'vmalloc_total',
|
||||
'vmalloc_used',
|
||||
'wired',
|
||||
'write_back',
|
||||
'write_back_tmp',
|
||||
],
|
||||
},
|
||||
{
|
||||
keys: ['host'],
|
||||
values: ['foo_computer'],
|
||||
},
|
||||
]
|
||||
beforeEach(() => {
|
||||
mocked(isFlagEnabled).mockReset()
|
||||
})
|
||||
|
||||
it("returns the active query tag values when the isFlagEnabled('queryBuilderGrouping') is toggled off", () => {
|
||||
mocked(isFlagEnabled).mockImplementation(() => {
|
||||
return false
|
||||
})
|
||||
|
||||
const actualTags = getActiveTagValues(activeQueryTags, 'filter', 2)
|
||||
expect(actualTags).toEqual(activeQueryTags[2].values)
|
||||
})
|
||||
|
||||
it("returns the active query tag values when the isFlagEnabled('queryBuilderGrouping') is toggled on, but the function is filter", () => {
|
||||
mocked(isFlagEnabled).mockImplementation(() => {
|
||||
return true
|
||||
})
|
||||
|
||||
const actualTags = getActiveTagValues(activeQueryTags, 'filter', 2)
|
||||
expect(actualTags).toEqual(activeQueryTags[2].values)
|
||||
})
|
||||
|
||||
it("returns all previous tag values when the isFlagEnabled('queryBuilderGrouping') is toggled on and the function is group", () => {
|
||||
mocked(isFlagEnabled).mockImplementation(() => {
|
||||
return true
|
||||
})
|
||||
|
||||
const actualTags = getActiveTagValues(activeQueryTags, 'group', 2)
|
||||
expect(actualTags).toEqual([
|
||||
...activeQueryTags[0].values,
|
||||
...activeQueryTags[1].values,
|
||||
])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -22,8 +22,12 @@ import {
|
|||
durationToMilliseconds,
|
||||
} from 'src/shared/utils/duration'
|
||||
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Types
|
||||
import {
|
||||
BuilderAggregateFunctionType,
|
||||
BuilderTagsType,
|
||||
DashboardQuery,
|
||||
FluxTable,
|
||||
QueryView,
|
||||
|
|
@ -309,3 +313,29 @@ export const getActiveTimeRange = (
|
|||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const getActiveTagValues = (
|
||||
activeQueryBuilderTags: BuilderTagsType[],
|
||||
aggregateFunctionType: BuilderAggregateFunctionType,
|
||||
index: number
|
||||
): string[] => {
|
||||
// if we're grouping, we want to be able to group on all previous tags
|
||||
if (
|
||||
isFlagEnabled('queryBuilderGrouping') &&
|
||||
aggregateFunctionType === 'group'
|
||||
) {
|
||||
const values = []
|
||||
activeQueryBuilderTags.forEach((tag, i) => {
|
||||
// if we don't skip the current set of tags, we'll double render them at the bottom of the selector list
|
||||
if (i === index) {
|
||||
return
|
||||
}
|
||||
tag.values.forEach(value => {
|
||||
values.push(value)
|
||||
})
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
return activeQueryBuilderTags[index].values
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import {get, isEmpty} from 'lodash'
|
||||
import {BuilderConfig, DashboardDraftQuery, Check} from 'src/types'
|
||||
import {
|
||||
BuilderConfig,
|
||||
BuilderTagsType,
|
||||
DashboardDraftQuery,
|
||||
Check,
|
||||
} from 'src/types'
|
||||
import {FUNCTIONS} from 'src/timeMachine/constants/queryBuilder'
|
||||
import {
|
||||
TIME_RANGE_START,
|
||||
|
|
@ -8,7 +13,7 @@ import {
|
|||
WINDOW_PERIOD,
|
||||
} from 'src/variables/constants'
|
||||
import {AGG_WINDOW_AUTO} from 'src/timeMachine/constants/queryBuilder'
|
||||
import {BuilderTagsType} from '@influxdata/influx'
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
|
||||
export function isConfigValid(builderConfig: BuilderConfig): boolean {
|
||||
const {buckets, tags} = builderConfig
|
||||
|
|
@ -85,11 +90,14 @@ export function buildQuery(builderConfig: BuilderConfig): string {
|
|||
const {functions} = builderConfig
|
||||
|
||||
let query: string
|
||||
const helper = isFlagEnabled('queryBuilderGrouping')
|
||||
? buildQueryHelperButWithGrouping
|
||||
: buildQueryHelper
|
||||
|
||||
if (functions.length) {
|
||||
query = functions.map(f => buildQueryHelper(builderConfig, f)).join('\n\n')
|
||||
query = functions.map(f => helper(builderConfig, f)).join('\n\n')
|
||||
} else {
|
||||
query = buildQueryHelper(builderConfig)
|
||||
query = helper(builderConfig, null)
|
||||
}
|
||||
|
||||
return query
|
||||
|
|
@ -110,6 +118,30 @@ function buildQueryHelper(
|
|||
return query
|
||||
}
|
||||
|
||||
function buildQueryHelperButWithGrouping(
|
||||
builderConfig: BuilderConfig,
|
||||
fn?: BuilderConfig['functions'][0]
|
||||
): string {
|
||||
const [bucket] = builderConfig.buckets
|
||||
|
||||
const tags = Array.from(builderConfig.tags)
|
||||
|
||||
// todo: (bucky) - check to see if we can combine filter calls
|
||||
// https://github.com/influxdata/influxdb/issues/16076
|
||||
let tagsFunctionCalls = ''
|
||||
tags.forEach(tag => {
|
||||
tagsFunctionCalls += convertTagsToFluxFunctionString(tag)
|
||||
})
|
||||
|
||||
const {aggregateWindow} = builderConfig
|
||||
const fnCall = fn ? formatFunctionCall(fn, aggregateWindow.period) : ''
|
||||
|
||||
const query = `from(bucket: "${bucket}")
|
||||
|> range(start: ${OPTION_NAME}.${TIME_RANGE_START}, stop: ${OPTION_NAME}.${TIME_RANGE_STOP})${tagsFunctionCalls}${fnCall}`
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
export function formatFunctionCall(
|
||||
fn: BuilderConfig['functions'][0],
|
||||
period: string
|
||||
|
|
@ -125,6 +157,37 @@ export function formatFunctionCall(
|
|||
return `\n ${fnSpec.flux(formattedPeriod)}\n |> yield(name: "${fn.name}")`
|
||||
}
|
||||
|
||||
const convertTagsToFluxFunctionString = function convertTagsToFluxFunctionString(
|
||||
tag: BuilderTagsType
|
||||
) {
|
||||
if (!tag.key) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (tag.aggregateFunctionType === 'filter') {
|
||||
if (!tag.values.length) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const fnBody = tag.values
|
||||
.map(value => `r.${tag.key} == "${value}"`)
|
||||
.join(' or ')
|
||||
return `\n |> filter(fn: (r) => ${fnBody})`
|
||||
}
|
||||
|
||||
if (tag.aggregateFunctionType === 'group') {
|
||||
const quotedValues = tag.values.map(value => `"${value}"`) // wrap the value in double quotes
|
||||
|
||||
if (quotedValues.length) {
|
||||
return `\n |> group(columns: [${quotedValues.join(', ')}])` // join with a comma (e.g. "foo","bar","baz")
|
||||
}
|
||||
|
||||
return '\n |> group()'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
const formatPeriod = (period: string): string => {
|
||||
if (period === AGG_WINDOW_AUTO || !period) {
|
||||
return `${OPTION_NAME}.${WINDOW_PERIOD}`
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ export type TableOptions = TableViewProperties['tableOptions']
|
|||
|
||||
export {
|
||||
DashboardQuery,
|
||||
BuilderAggregateFunctionType,
|
||||
BuilderTagsType,
|
||||
BuilderConfig,
|
||||
ViewProperties,
|
||||
QueryEditMode,
|
||||
|
|
|
|||
Loading…
Reference in New Issue