diff --git a/ui/spec/chronograf/reducers/queryConfigSpec.js b/ui/spec/chronograf/reducers/queryConfigSpec.js index 825b9f50bf..04f5ee66cf 100644 --- a/ui/spec/chronograf/reducers/queryConfigSpec.js +++ b/ui/spec/chronograf/reducers/queryConfigSpec.js @@ -175,6 +175,7 @@ describe('Chronograf.Reducers.queryConfig', () => { const nextState = reducer(initialState, action); + console.log('im nextstate :)', nextState) expect(nextState[queryId].tags).to.eql({ k1: ['v0', 'v1'], k2: ['foo'], diff --git a/ui/src/chronograf/reducers/queryConfigs.js b/ui/src/chronograf/reducers/queryConfigs.js index e64ecb3edb..64cb8bc204 100644 --- a/ui/src/chronograf/reducers/queryConfigs.js +++ b/ui/src/chronograf/reducers/queryConfigs.js @@ -1,7 +1,16 @@ import defaultQueryConfig from 'src/utils/defaultQueryConfig'; -import {chooseNamespace, chooseMeasurement, toggleField} from 'src/utils/queryTransitions'; +import { + applyFuncsToField, + chooseMeasurement, + chooseNamespace, + chooseTag, + groupByTag, + groupByTime, + toggleField, + toggleTagAcceptance, + updateRawQuery, +} from 'src/utils/queryTransitions'; import update from 'react-addons-update'; -import u from 'updeep'; export default function queryConfigs(state = {}, action) { switch (action.type) { @@ -12,19 +21,19 @@ export default function queryConfigs(state = {}, action) { case 'CHOOSE_NAMESPACE': { const {queryId, database, retentionPolicy} = action.payload; const nextQueryConfig = chooseNamespace(defaultQueryConfig(queryId), {database, retentionPolicy}); - const nextState = update(state, { - [queryId]: {$set: nextQueryConfig}, + + return Object.assign({}, state, { + [queryId]: nextQueryConfig, }); - return nextState; } case 'CHOOSE_MEASUREMENT': { const {queryId, measurement} = action.payload; const nextQueryConfig = chooseMeasurement(state[queryId], measurement); - const nextState = update(state, { - [queryId]: {$set: nextQueryConfig}, + + return Object.assign({}, state, { + [queryId]: nextQueryConfig, }); - return nextState; } case 'CREATE_PANEL': @@ -48,26 +57,20 @@ export default function queryConfigs(state = {}, action) { case 'GROUP_BY_TIME': { const {queryId, time} = action.payload; - const nextState = update(state, { - [queryId]: { - groupBy: { - time: {$set: time}, - }, - }, - }); + const nextQueryConfig = groupByTime(state[queryId], time); - return nextState; + return Object.assign({}, state, { + [queryId]: nextQueryConfig, + }); } case 'TOGGLE_TAG_ACCEPTANCE': { const {queryId} = action.payload; - const nextState = update(state, { - [queryId]: { - areTagsAccepted: {$set: !state[queryId].areTagsAccepted}, - }, - }); + const nextQueryConfig = toggleTagAcceptance(state[queryId]); - return nextState; + return Object.assign({}, state, { + [queryId]: nextQueryConfig, + }); } case 'DELETE_QUERY': { @@ -91,115 +94,36 @@ export default function queryConfigs(state = {}, action) { case 'APPLY_FUNCS_TO_FIELD': { const {queryId, fieldFunc} = action.payload; - const {field, funcs} = fieldFunc; - const shouldRemoveFuncs = funcs.length === 0; + const nextQueryConfig = applyFuncsToField(state[queryId], fieldFunc); - const nextState = update(state, { - [queryId]: {$apply: (queryConfig) => { - const nextFields = queryConfig.fields.map((f) => { - // If one field has no funcs, all fields must have no funcs - if (shouldRemoveFuncs) { - return update(f, {funcs: {$set: []}}); - } - - if (f.field === field || !f.funcs || !f.funcs.length) { - return update(f, {funcs: {$set: funcs}}); - } - - return f; - }); - - // If there are no functions, then there should be no GROUP BY time - if (shouldRemoveFuncs) { - const nextGroupBy = update(state[queryId].groupBy, {time: {$set: null}}); - return update(queryConfig, {fields: {$set: nextFields}, groupBy: {$set: nextGroupBy}}); - } - - return update(queryConfig, {fields: {$set: nextFields}}); - }}, + return Object.assign({}, state, { + [queryId]: nextQueryConfig, }); - - return nextState; } case 'CHOOSE_TAG': { const {queryId, tag} = action.payload; + const nextQueryConfig = chooseTag(state[queryId], tag); - const tagValues = state[queryId].tags[tag.key]; - const shouldRemoveTag = tagValues && tagValues.length === 1 && tagValues[0] === tag.value; - if (shouldRemoveTag) { - const nextState = update(state, { - [queryId]: { - tags: {$apply: (tags) => { - const tagsCopy = Object.assign({}, tags); - delete tagsCopy[tag.key]; - return tagsCopy; - }}, - }, - }); - - return nextState; - } - - const nextState = update(state, { - [queryId]: { - tags: { - [tag.key]: {$apply: (vals) => { - if (!vals) { - return [tag.value]; - } - - // If the tag value is already selected, deselect it by removing it from the list - const valsCopy = vals.slice(); - const i = valsCopy.indexOf(tag.value); - if (i > -1) { - valsCopy.splice(i, 1); - return valsCopy; - } - - return update(valsCopy, {$push: [tag.value]}); - }}, - }, - }, + return Object.assign({}, state, { + [queryId]: nextQueryConfig, }); - - return nextState; } case 'GROUP_BY_TAG': { const {queryId, tagKey} = action.payload; - - const nextState = update(state, { - [queryId]: { - groupBy: { - tags: {$apply: (groupByTags) => { - // If the tag value is already selected, deselect it by removing it from the list - const groupByTagsCopy = groupByTags.slice(); - const i = groupByTagsCopy.indexOf(tagKey); - if (i > -1) { - groupByTagsCopy.splice(i, 1); - return groupByTagsCopy; - } - - return update(groupByTagsCopy, {$push: [tagKey]}); - }}, - }, - }, + const nextQueryConfig = groupByTag(state[queryId], tagKey); + return Object.assign({}, state, { + [queryId]: nextQueryConfig, }); - - return nextState; } case 'UPDATE_RAW_QUERY': { const {queryID, text} = action.payload; - - const updateQuery = { - [queryID]: { - rawText: u.constant(text), - }, - }; - - return u(updateQuery, state); + const nextQueryConfig = updateRawQuery(state[queryID], text); + return Object.assign({}, state, { + [queryID]: nextQueryConfig, + }); } } return state; diff --git a/ui/src/utils/queryTransitions.js b/ui/src/utils/queryTransitions.js index 86ba24b10f..e593e74abe 100644 --- a/ui/src/utils/queryTransitions.js +++ b/ui/src/utils/queryTransitions.js @@ -33,3 +33,102 @@ export function toggleField(query, {field, funcs}) { fields: query.fields.concat({field, funcs}), }); } + +export function groupByTime(query, time) { + return Object.assign({}, query, { + groupBy: Object.assign({}, query.groupBy, { + time, + }), + }); +} + +export function toggleTagAcceptance(query) { + return Object.assign({}, query, { + areTagsAccepted: !query.areTagsAccepted, + }); +} + +export function applyFuncsToField(query, {field, funcs}) { + const shouldRemoveFuncs = funcs.length === 0; + const nextFields = query.fields.map((f) => { + // If one field has no funcs, all fields must have no funcs + if (shouldRemoveFuncs) { + return Object.assign({}, f, {funcs: []}); + } + + // If there is a func applied to only one field, add it to the other fields + if (f.field === field || !f.funcs || !f.funcs.length) { + return Object.assign({}, f, {funcs}); + } + + return f; + }); + + // If there are no functions, then there should be no GROUP BY time + if (shouldRemoveFuncs) { + const nextGroupBy = Object.assign({}, query.groupBy, {time: null}); + return Object.assign({}, query, { + fields: nextFields, + groupBy: nextGroupBy, + }); + } + + return Object.assign({}, query, {fields: nextFields}); +} + +export function updateRawQuery(query, rawText) { + return Object.assign({}, query, { + rawText, + }); +} + +export function groupByTag(query, tagKey) { + const oldTags = query.groupBy.tags; + let newTags; + + // Toggle the presence of the tagKey + if (oldTags.includes(tagKey)) { + const i = oldTags.indexOf(tagKey); + newTags = oldTags.slice(); + newTags.splice(i, 1); + } else { + newTags = oldTags.concat(tagKey); + } + + return Object.assign({}, query, { + groupBy: Object.assign({}, query.groupBy, {tags: newTags}), + }); +} + +export function chooseTag(query, tag) { + const tagValues = query.tags[tag.key]; + const shouldRemoveTag = tagValues && tagValues.length === 1 && tagValues[0] === tag.value; + if (shouldRemoveTag) { + const newTags = Object.assign({}, query.tags); + delete newTags[tag.key]; + return Object.assign({}, query, {tags: newTags}); + } + + const oldTagValues = query.tags[tag.key]; + if (!oldTagValues) { + return updateTagValues([tag.value]); + } + + // If the tag value is already selected, deselect it by removing it from the list + const tagValuesCopy = oldTagValues.slice(); + const i = tagValuesCopy.indexOf(tag.value); + if (i > -1) { + tagValuesCopy.splice(i, 1); + return updateTagValues(tagValuesCopy); + } + + return updateTagValues(query.tags[tag.key].concat(tag.value)); + + function updateTagValues(newTagValues) { + return Object.assign({}, query, { + tags: Object.assign({}, query.tags, { + [tag.key]: newTagValues, + }), + }); + } +}