diff --git a/http/swagger.yml b/http/swagger.yml index 636b9b94ee..8632a51174 100644 --- a/http/swagger.yml +++ b/http/swagger.yml @@ -7862,6 +7862,11 @@ components: type: array items: type: string + aggregateFunctionType: + $ref: '#/components/schemas/BuilderAggregateFunctionType' + BuilderAggregateFunctionType: + type: string + enum: ['filter', 'group'] BuilderFunctionsType: type: object properties: diff --git a/ui/jest.config.js b/ui/jest.config.js index edecf67f88..16173e2df6 100644 --- a/ui/jest.config.js +++ b/ui/jest.config.js @@ -23,7 +23,7 @@ module.exports = { 'ts-jest': { tsConfig: 'tsconfig.test.json', diagnostics: { - ignoreCodes: [6133] // ignore `'foo' is declared but its value is never read.` + ignoreCodes: [6133, 6192] // ignore unused variable errors }, }, }, diff --git a/ui/src/timeMachine/actions/queryBuilder.ts b/ui/src/timeMachine/actions/queryBuilder.ts index d48496108c..40b7e46734 100644 --- a/ui/src/timeMachine/actions/queryBuilder.ts +++ b/ui/src/timeMachine/actions/queryBuilder.ts @@ -10,220 +10,113 @@ import { // Types import {Dispatch} from 'redux-thunk' -import {GetState} from 'src/types' -import {RemoteDataState} from 'src/types' +import {GetState, RemoteDataState} from 'src/types' +import {BuilderAggregateFunctionType} from 'src/client/generatedRoutes' import {BuilderFunctionsType} from '@influxdata/influx' export type Action = - | SetBuilderBucketSelectionAction - | SetBuilderBucketsAction - | SetBuilderBucketsStatusAction - | SetBuilderTagKeysAction - | SetBuilderTagKeysStatusAction - | SetBuilderTagValuesAction - | SetBuilderTagValuesStatusAction - | SetBuilderTagKeySelectionAction - | SetBuilderTagValuesSelectionAction - | AddTagSelectorAction - | RemoveTagSelectorAction - | SetFunctionsAction - | SelectAggregateWindowAction - | SetValuesSearchTermAction - | SetKeysSearchTermAction - | SetBuilderTagsStatusAction + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType -interface SetBuilderBucketsStatusAction { - type: 'SET_BUILDER_BUCKETS_STATUS' - payload: {bucketsStatus: RemoteDataState} -} +export const setBuilderAggregateFunctionType = ( + builderAggregateFunctionType: BuilderAggregateFunctionType, + index: number +) => ({ + type: 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE' as 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE', + payload: {builderAggregateFunctionType, index}, +}) -const setBuilderBucketsStatus = ( - bucketsStatus: RemoteDataState -): SetBuilderBucketsStatusAction => ({ - type: 'SET_BUILDER_BUCKETS_STATUS', +const setBuilderBucketsStatus = (bucketsStatus: RemoteDataState) => ({ + type: 'SET_BUILDER_BUCKETS_STATUS' as 'SET_BUILDER_BUCKETS_STATUS', payload: {bucketsStatus}, }) -interface SetBuilderBucketsAction { - type: 'SET_BUILDER_BUCKETS' - payload: {buckets: string[]} -} - -export const setBuilderBuckets = ( - buckets: string[] -): SetBuilderBucketsAction => ({ - type: 'SET_BUILDER_BUCKETS', +export const setBuilderBuckets = (buckets: string[]) => ({ + type: 'SET_BUILDER_BUCKETS' as 'SET_BUILDER_BUCKETS', payload: {buckets}, }) -interface SetBuilderBucketSelectionAction { - type: 'SET_BUILDER_BUCKET_SELECTION' - payload: {bucket: string; resetSelections: boolean} -} - -const setBuilderBucket = ( - bucket: string, - resetSelections: boolean -): SetBuilderBucketSelectionAction => ({ - type: 'SET_BUILDER_BUCKET_SELECTION', +const setBuilderBucket = (bucket: string, resetSelections: boolean) => ({ + type: 'SET_BUILDER_BUCKET_SELECTION' as 'SET_BUILDER_BUCKET_SELECTION', payload: {bucket, resetSelections}, }) -interface SetBuilderTagsStatusAction { - type: 'SET_BUILDER_TAGS_STATUS' - payload: {status: RemoteDataState} -} - -export const setBuilderTagsStatus = ( - status: RemoteDataState -): SetBuilderTagsStatusAction => ({ - type: 'SET_BUILDER_TAGS_STATUS', +export const setBuilderTagsStatus = (status: RemoteDataState) => ({ + type: 'SET_BUILDER_TAGS_STATUS' as 'SET_BUILDER_TAGS_STATUS', payload: {status}, }) -interface SetBuilderTagKeysAction { - type: 'SET_BUILDER_TAG_KEYS' - payload: {index: number; keys: string[]} -} - -const setBuilderTagKeys = ( - index: number, - keys: string[] -): SetBuilderTagKeysAction => ({ - type: 'SET_BUILDER_TAG_KEYS', +const setBuilderTagKeys = (index: number, keys: string[]) => ({ + type: 'SET_BUILDER_TAG_KEYS' as 'SET_BUILDER_TAG_KEYS', payload: {index, keys}, }) -interface SetBuilderTagKeysStatusAction { - type: 'SET_BUILDER_TAG_KEYS_STATUS' - payload: {index: number; status: RemoteDataState} -} - -const setBuilderTagKeysStatus = ( - index: number, - status: RemoteDataState -): SetBuilderTagKeysStatusAction => ({ - type: 'SET_BUILDER_TAG_KEYS_STATUS', +const setBuilderTagKeysStatus = (index: number, status: RemoteDataState) => ({ + type: 'SET_BUILDER_TAG_KEYS_STATUS' as 'SET_BUILDER_TAG_KEYS_STATUS', payload: {index, status}, }) -interface SetBuilderTagValuesAction { - type: 'SET_BUILDER_TAG_VALUES' - payload: {index: number; values: string[]} -} - -const setBuilderTagValues = ( - index: number, - values: string[] -): SetBuilderTagValuesAction => ({ - type: 'SET_BUILDER_TAG_VALUES', +const setBuilderTagValues = (index: number, values: string[]) => ({ + type: 'SET_BUILDER_TAG_VALUES' as 'SET_BUILDER_TAG_VALUES', payload: {index, values}, }) -interface SetBuilderTagValuesStatusAction { - type: 'SET_BUILDER_TAG_VALUES_STATUS' - payload: {index: number; status: RemoteDataState} -} - -const setBuilderTagValuesStatus = ( - index: number, - status: RemoteDataState -): SetBuilderTagValuesStatusAction => ({ - type: 'SET_BUILDER_TAG_VALUES_STATUS', +const setBuilderTagValuesStatus = (index: number, status: RemoteDataState) => ({ + type: 'SET_BUILDER_TAG_VALUES_STATUS' as 'SET_BUILDER_TAG_VALUES_STATUS', payload: {index, status}, }) -interface SetBuilderTagKeySelectionAction { - type: 'SET_BUILDER_TAG_KEY_SELECTION' - payload: {index: number; key: string} -} - -const setBuilderTagKeySelection = ( - index: number, - key: string -): SetBuilderTagKeySelectionAction => ({ - type: 'SET_BUILDER_TAG_KEY_SELECTION', +const setBuilderTagKeySelection = (index: number, key: string) => ({ + type: 'SET_BUILDER_TAG_KEY_SELECTION' as 'SET_BUILDER_TAG_KEY_SELECTION', payload: {index, key}, }) -interface SetBuilderTagValuesSelectionAction { - type: 'SET_BUILDER_TAG_VALUES_SELECTION' - payload: {index: number; values: string[]} -} - -const setBuilderTagValuesSelection = ( - index: number, - values: string[] -): SetBuilderTagValuesSelectionAction => ({ - type: 'SET_BUILDER_TAG_VALUES_SELECTION', +const setBuilderTagValuesSelection = (index: number, values: string[]) => ({ + type: 'SET_BUILDER_TAG_VALUES_SELECTION' as 'SET_BUILDER_TAG_VALUES_SELECTION', payload: {index, values}, }) -interface AddTagSelectorAction { - type: 'ADD_TAG_SELECTOR' -} - -const addTagSelectorSync = (): AddTagSelectorAction => ({ - type: 'ADD_TAG_SELECTOR', +const addTagSelectorSync = () => ({ + type: 'ADD_TAG_SELECTOR' as 'ADD_TAG_SELECTOR', }) -interface RemoveTagSelectorAction { - type: 'REMOVE_TAG_SELECTOR' - payload: {index: number} -} - -const removeTagSelectorSync = (index: number): RemoveTagSelectorAction => ({ - type: 'REMOVE_TAG_SELECTOR', +const removeTagSelectorSync = (index: number) => ({ + type: 'REMOVE_TAG_SELECTOR' as 'REMOVE_TAG_SELECTOR', payload: {index}, }) -interface SetFunctionsAction { - type: 'SELECT_BUILDER_FUNCTION' - payload: {functions: BuilderFunctionsType[]} -} - -export const setFunctions = ( - functions: BuilderFunctionsType[] -): SetFunctionsAction => ({ - type: 'SELECT_BUILDER_FUNCTION', +export const setFunctions = (functions: BuilderFunctionsType[]) => ({ + type: 'SELECT_BUILDER_FUNCTION' as 'SELECT_BUILDER_FUNCTION', payload: {functions}, }) -interface SelectAggregateWindowAction { - type: 'SELECT_AGGREGATE_WINDOW' - payload: {period: string} -} - -export const selectAggregateWindow = ( - period: string -): SelectAggregateWindowAction => ({ - type: 'SELECT_AGGREGATE_WINDOW', +export const selectAggregateWindow = (period: string) => ({ + type: 'SELECT_AGGREGATE_WINDOW' as 'SELECT_AGGREGATE_WINDOW', payload: {period}, }) -interface SetValuesSearchTermAction { - type: 'SET_BUILDER_VALUES_SEARCH_TERM' - payload: {index: number; searchTerm: string} -} - -interface SetKeysSearchTermAction { - type: 'SET_BUILDER_KEYS_SEARCH_TERM' - payload: {index: number; searchTerm: string} -} - -export const setValuesSearchTerm = ( - index: number, - searchTerm: string -): SetValuesSearchTermAction => ({ - type: 'SET_BUILDER_VALUES_SEARCH_TERM', +export const setValuesSearchTerm = (index: number, searchTerm: string) => ({ + type: 'SET_BUILDER_VALUES_SEARCH_TERM' as 'SET_BUILDER_VALUES_SEARCH_TERM', payload: {index, searchTerm}, }) -export const setKeysSearchTerm = ( - index: number, - searchTerm: string -): SetKeysSearchTermAction => ({ - type: 'SET_BUILDER_KEYS_SEARCH_TERM', +export const setKeysSearchTerm = (index: number, searchTerm: string) => ({ + type: 'SET_BUILDER_KEYS_SEARCH_TERM' as 'SET_BUILDER_KEYS_SEARCH_TERM', payload: {index, searchTerm}, }) diff --git a/ui/src/timeMachine/components/TagSelector.tsx b/ui/src/timeMachine/components/TagSelector.tsx index 3fd05be093..4ee03f1b47 100644 --- a/ui/src/timeMachine/components/TagSelector.tsx +++ b/ui/src/timeMachine/components/TagSelector.tsx @@ -20,11 +20,12 @@ import {ErrorHandling} from 'src/shared/decorators/errors' // Actions import { + removeTagSelector, + searchTagKeys, + searchTagValues, selectTagKey, selectTagValue, - searchTagValues, - searchTagKeys, - removeTagSelector, + setBuilderAggregateFunctionType, setKeysSearchTerm, setValuesSearchTerm, } from 'src/timeMachine/actions/queryBuilder' @@ -40,10 +41,12 @@ import { // Types import {AppState, RemoteDataState} from 'src/types' +import {BuilderAggregateFunctionType} from 'src/client' const SEARCH_DEBOUNCE_MS = 500 interface StateProps { + aggregateFunctionType: BuilderAggregateFunctionType emptyText: string keys: string[] keysStatus: RemoteDataState @@ -57,13 +60,14 @@ interface StateProps { } interface DispatchProps { - onSelectValue: typeof selectTagValue - onSelectTag: typeof selectTagKey - onSearchValues: typeof searchTagValues - onSearchKeys: typeof searchTagKeys onRemoveTagSelector: typeof removeTagSelector - onSetValuesSearchTerm: typeof setValuesSearchTerm + onSearchKeys: typeof searchTagKeys + onSearchValues: typeof searchTagValues + onSelectTag: typeof selectTagKey + onSelectValue: typeof selectTagValue + onSetBuilderAggregateFunctionType: typeof setBuilderAggregateFunctionType onSetKeysSearchTerm: typeof setKeysSearchTerm + onSetValuesSearchTerm: typeof setValuesSearchTerm } interface OwnProps { @@ -76,13 +80,24 @@ type Props = StateProps & DispatchProps & OwnProps class TagSelector extends PureComponent { private debouncer = new DefaultDebouncer() + // bucky: this will currently always be 'Filter' + // updates to this are imminent + private renderAggregateFunctionType( + aggregateFunctionType: BuilderAggregateFunctionType + ) { + if (aggregateFunctionType === 'group') { + return 'Group' + } + return 'Filter' + } + public render() { - const {index} = this.props + const {aggregateFunctionType, index} = this.props return ( {this.body} @@ -255,15 +270,19 @@ class TagSelector extends PureComponent { const mstp = (state: AppState, ownProps: OwnProps): StateProps => { const { keys, + keysSearchTerm, keysStatus, values, - valuesStatus, valuesSearchTerm, - keysSearchTerm, + valuesStatus, } = getActiveTimeMachine(state).queryBuilder.tags[ownProps.index] const tags = getActiveQuery(state).builderConfig.tags - const {key: selectedKey, values: selectedValues} = tags[ownProps.index] + const { + key: selectedKey, + values: selectedValues, + aggregateFunctionType, + } = tags[ownProps.index] let emptyText: string @@ -276,6 +295,7 @@ const mstp = (state: AppState, ownProps: OwnProps): StateProps => { const isInCheckOverlay = getIsInCheckOverlay(state) return { + aggregateFunctionType, emptyText, keys, keysStatus, @@ -290,11 +310,12 @@ const mstp = (state: AppState, ownProps: OwnProps): StateProps => { } const mdtp = { - onSelectValue: selectTagValue, - onSelectTag: selectTagKey, - onSearchValues: searchTagValues, - onSearchKeys: searchTagKeys, onRemoveTagSelector: removeTagSelector, + onSearchKeys: searchTagKeys, + onSearchValues: searchTagValues, + onSelectTag: selectTagKey, + onSelectValue: selectTagValue, + onSetBuilderAggregateFunctionType: setBuilderAggregateFunctionType, onSetKeysSearchTerm: setKeysSearchTerm, onSetValuesSearchTerm: setValuesSearchTerm, } diff --git a/ui/src/timeMachine/reducers/index.test.ts b/ui/src/timeMachine/reducers/index.test.ts new file mode 100644 index 0000000000..f4a682b751 --- /dev/null +++ b/ui/src/timeMachine/reducers/index.test.ts @@ -0,0 +1,42 @@ +import {createStore} from 'redux' + +import { + initialState, + initialStateHelper, + timeMachinesReducer, +} from 'src/timeMachine/reducers' + +describe('the Time Machine reducer', () => { + describe('setting the default aggregateFunctionType', () => { + const store = createStore(timeMachinesReducer, initialState()) + const expectedAggregatefunctionType = initialStateHelper().queryBuilder + .tags[0].aggregateFunctionType + + it('is set when setting a builder bucket selection', () => { + store.dispatch({ + type: 'SET_BUILDER_BUCKET_SELECTION', + payload: {bucket: 'foo', resetSelections: true}, + }) + const actualState = store.getState() + const defaultAggregateFunctionType = + actualState.timeMachines.de.draftQueries[0].builderConfig.tags[0] + .aggregateFunctionType + + expect(defaultAggregateFunctionType).toEqual( + expectedAggregatefunctionType + ) + }) + + it('is set when adding a new tag selector', () => { + store.dispatch({type: 'ADD_TAG_SELECTOR'}) + const actualState = store.getState() + const defaultAggregateFunctionType = + actualState.timeMachines.de.draftQueries[0].builderConfig.tags[0] + .aggregateFunctionType + + expect(defaultAggregateFunctionType).toEqual( + expectedAggregatefunctionType + ) + }) + }) +}) diff --git a/ui/src/timeMachine/reducers/index.ts b/ui/src/timeMachine/reducers/index.ts index 69d48c96fc..0d3234e27b 100644 --- a/ui/src/timeMachine/reducers/index.ts +++ b/ui/src/timeMachine/reducers/index.ts @@ -41,6 +41,7 @@ import {Action} from 'src/timeMachine/actions' import {TimeMachineTab} from 'src/types/timeMachine' import {RemoteDataState, TimeMachineID} from 'src/types' import {Color} from 'src/types/colors' +import {BuilderAggregateFunctionType} from 'src/client/generatedRoutes' interface QueryBuilderState { buckets: string[] @@ -48,6 +49,7 @@ interface QueryBuilderState { functions: Array<[{name: string}]> aggregateWindow: BuilderConfigAggregateWindow tags: Array<{ + aggregateFunctionType: BuilderAggregateFunctionType valuesSearchTerm: string keysSearchTerm: string keys: string[] @@ -115,11 +117,12 @@ export const initialStateHelper = (): TimeMachineState => ({ functions: [], tags: [ { - valuesSearchTerm: '', - keysSearchTerm: '', + aggregateFunctionType: 'filter', keys: [], + keysSearchTerm: '', keysStatus: RemoteDataState.NotStarted, values: [], + valuesSearchTerm: '', valuesStatus: RemoteDataState.NotStarted, }, ], @@ -649,6 +652,23 @@ export const timeMachineReducer = ( }) } + case 'SET_BUILDER_AGGREGATE_FUNCTION_TYPE': { + return produce(state, draftState => { + const {index, builderAggregateFunctionType} = action.payload + const draftQuery = draftState.draftQueries[draftState.activeQueryIndex] + + if ( + draftQuery && + draftQuery.builderConfig && + draftQuery.builderConfig.tags[index] + ) { + draftQuery.builderConfig.tags[ + index + ].aggregateFunctionType = builderAggregateFunctionType + } + }) + } + case 'SET_BUILDER_BUCKET_SELECTION': { return produce(state, draftState => { const builderConfig = @@ -657,7 +677,16 @@ export const timeMachineReducer = ( builderConfig.buckets = [action.payload.bucket] if (action.payload.resetSelections) { - builderConfig.tags = [{key: '', values: []}] + const defaultAggregateFunctionType = initialStateHelper().queryBuilder + .tags[0].aggregateFunctionType + + builderConfig.tags = [ + { + key: '', + values: [], + aggregateFunctionType: defaultAggregateFunctionType, + }, + ] buildActiveQuery(draftState) } }) @@ -770,16 +799,14 @@ export const timeMachineReducer = ( case 'ADD_TAG_SELECTOR': { return produce(state, draftState => { const draftQuery = draftState.draftQueries[draftState.activeQueryIndex] + const [initialTags] = initialStateHelper().queryBuilder.tags - draftQuery.builderConfig.tags.push({key: '', values: []}) - draftState.queryBuilder.tags.push({ - valuesSearchTerm: '', - keysSearchTerm: '', - keys: [], - keysStatus: RemoteDataState.NotStarted, + draftQuery.builderConfig.tags.push({ + key: '', values: [], - valuesStatus: RemoteDataState.NotStarted, + aggregateFunctionType: initialTags.aggregateFunctionType, }) + draftState.queryBuilder.tags.push(initialTags) }) } @@ -1066,14 +1093,7 @@ const initialQueryBuilderState = ( bucketsStatus: RemoteDataState.NotStarted, functions: [], aggregateWindow: {period: 'auto'}, - tags: builderConfig.tags.map(_ => ({ - valuesSearchTerm: '', - keysSearchTerm: '', - keys: [], - keysStatus: RemoteDataState.NotStarted, - values: [], - valuesStatus: RemoteDataState.NotStarted, - })), + tags: initialStateHelper().queryBuilder.tags, } }