Commit 564baa23 authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Illya Klymov

Refactor issues analytics filter handling

Migrate issues analytics filters from being stored as a
string to an object.

Required in order to better handle transformation of params
for different endpoint requirements.
parent 83c43fe0
...@@ -6,8 +6,9 @@ import { GlColumnChart, GlChartLegend } from '@gitlab/ui/dist/charts'; ...@@ -6,8 +6,9 @@ import { GlColumnChart, GlChartLegend } from '@gitlab/ui/dist/charts';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { getMonthNames } from '~/lib/utils/datetime_utility'; import { getMonthNames } from '~/lib/utils/datetime_utility';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import IssuesAnalyticsTable from './issues_analytics_table.vue'; import IssuesAnalyticsTable from './issues_analytics_table.vue';
import { transformIssuesApiEndpoint } from '../utils'; import { transformFilters } from '../utils';
export default { export default {
components: { components: {
...@@ -111,11 +112,16 @@ export default { ...@@ -111,11 +112,16 @@ export default {
return engineeringNotation(sum(...this.series)); return engineeringNotation(sum(...this.series));
}, },
issuesTableEndpoints() { issuesTableEndpoints() {
const publicApiFilters = transformFilters(this.appliedFilters);
return { return {
api: transformIssuesApiEndpoint(`${this.issuesApiEndpoint}${this.appliedFilters}`), api: mergeUrlParams(publicApiFilters, this.issuesApiEndpoint),
issuesPage: this.issuesPageEndpoint, issuesPage: this.issuesPageEndpoint,
}; };
}, },
filterString() {
return JSON.stringify(this.appliedFilters);
},
}, },
watch: { watch: {
appliedFilters() { appliedFilters() {
...@@ -183,7 +189,7 @@ export default { ...@@ -183,7 +189,7 @@ export default {
</div> </div>
</div> </div>
<issues-analytics-table :key="appliedFilters" class="mt-8" :endpoints="issuesTableEndpoints" /> <issues-analytics-table :key="filterString" class="mt-8" :endpoints="issuesTableEndpoints" />
<gl-empty-state <gl-empty-state
v-if="showFiltersEmptyState" v-if="showFiltersEmptyState"
......
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager'; import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
import { historyPushState } from '~/lib/utils/common_utils'; import { historyPushState, urlParamsToObject } from '~/lib/utils/common_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import issueAnalyticsStore from './stores'; import issueAnalyticsStore from './stores';
...@@ -33,6 +33,8 @@ export default class FilteredSearchIssueAnalytics extends FilteredSearchManager ...@@ -33,6 +33,8 @@ export default class FilteredSearchIssueAnalytics extends FilteredSearchManager
*/ */
updateObject = path => { updateObject = path => {
historyPushState(path); historyPushState(path);
issueAnalyticsStore.dispatch('issueAnalytics/setFilters', path);
const filters = urlParamsToObject(path);
issueAnalyticsStore.dispatch('issueAnalytics/setFilters', filters);
}; };
} }
...@@ -2,6 +2,7 @@ import Vue from 'vue'; ...@@ -2,6 +2,7 @@ import Vue from 'vue';
import IssuesAnalytics from './components/issues_analytics.vue'; import IssuesAnalytics from './components/issues_analytics.vue';
import store from './stores'; import store from './stores';
import FilteredSearchIssueAnalytics from './filtered_search_issues_analytics'; import FilteredSearchIssueAnalytics from './filtered_search_issues_analytics';
import { urlParamsToObject } from '~/lib/utils/common_utils';
export default () => { export default () => {
const el = document.querySelector('#js-issues-analytics'); const el = document.querySelector('#js-issues-analytics');
...@@ -18,7 +19,8 @@ export default () => { ...@@ -18,7 +19,8 @@ export default () => {
} = el.dataset; } = el.dataset;
// Set default filters from URL // Set default filters from URL
store.dispatch('issueAnalytics/setFilters', window.location.search); const filters = urlParamsToObject(window.location.search);
store.dispatch('issueAnalytics/setFilters', filters);
return new Vue({ return new Vue({
el, el,
......
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
export default { export default {
fetchChartData(endpoint, filters) { fetchChartData(endpoint, filters) {
return axios.get(`${endpoint}${filters}`); return axios.get(mergeUrlParams(filters, endpoint));
}, },
}; };
export default () => ({ export default () => ({
loading: false, loading: false,
filters: '', filters: {},
chartData: null, chartData: null,
}); });
import { mergeUrlParams, getParameterValues, removeParams } from '~/lib/utils/url_utility';
const LABEL_FILTER_NAME = 'label_name[]';
const MILESTONE_FILTER_NAME = 'milestone_title';
/** /**
* This util method takes the issues api endpoint with global page filters * This util method takes the global page filters and transforms parameters which
* and transforms parameters which are not standardized between the internal * are not standardized between the internal issues analytics api and the public
* issues analytics api and the public issues api. * issues api.
* *
* @param {String} endpoint the api endpoint with global filters used to fetch issues data * @param {Object} filters the global filters used to fetch issues data
* *
* @returns {String} The endpoint formatted for the public api * @returns {Object} the transformed filters for the public api
*/ */
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export const transformIssuesApiEndpoint = endpoint => { export const transformFilters = ({
const cleanEndpoint = removeParams([LABEL_FILTER_NAME, MILESTONE_FILTER_NAME], endpoint, true); label_name: labels = null,
const labels = getParameterValues(LABEL_FILTER_NAME, endpoint); milestone_title: milestone = null,
const milestone = getParameterValues(MILESTONE_FILTER_NAME, endpoint); ...restOfFilters
}) => ({
return mergeUrlParams({ labels, milestone }, cleanEndpoint); ...restOfFilters,
}; labels,
milestone,
});
...@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; ...@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import * as actions from 'ee/issues_analytics/stores/modules/issue_analytics/actions'; import * as actions from 'ee/issues_analytics/stores/modules/issue_analytics/actions';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { TEST_HOST } from 'helpers/test_constants';
describe('Issue analytics store actions', () => { describe('Issue analytics store actions', () => {
describe('setFilters', () => { describe('setFilters', () => {
...@@ -41,14 +42,14 @@ describe('Issue analytics store actions', () => { ...@@ -41,14 +42,14 @@ describe('Issue analytics store actions', () => {
}); });
it('commits SET_CHART_DATA with chart data', () => { it('commits SET_CHART_DATA with chart data', () => {
const getters = { appliedFilters: '?hello=world' }; const getters = { appliedFilters: { hello: 'world' } };
const context = { const context = {
dispatch, dispatch,
getters, getters,
commit, commit,
}; };
return actions.fetchChartData(context, gl.TEST_HOST).then(() => { return actions.fetchChartData(context, TEST_HOST).then(() => {
expect(dispatch.mock.calls[0]).toEqual(['setLoadingState', true]); expect(dispatch.mock.calls[0]).toEqual(['setLoadingState', true]);
expect(commit).toHaveBeenCalledWith('SET_CHART_DATA', chartData); expect(commit).toHaveBeenCalledWith('SET_CHART_DATA', chartData);
expect(dispatch.mock.calls[1]).toEqual(['setLoadingState', false]); expect(dispatch.mock.calls[1]).toEqual(['setLoadingState', false]);
......
import { transformIssuesApiEndpoint } from 'ee/issues_analytics/utils'; import { transformFilters } from 'ee/issues_analytics/utils';
import { TEST_HOST } from 'helpers/test_constants';
const dirtyEndpoint = `${TEST_HOST}/issues?label_name[]=cool&label_name[]=beans&milestone_title=v4.0`; const originalFilters = {
const cleanEndpoint = `${TEST_HOST}/issues?labels=cool%2Cbeans&milestone=v4.0`; label_name: ['one', 'two'],
milestone_title: 'title',
author_username: 'root',
};
const tranformedFilters = { labels: ['one', 'two'], milestone: 'title', author_username: 'root' };
describe('issues analytics utils', () => { describe('issues analytics utils', () => {
describe('transformIssuesApiEndpoint', () => { describe('transformFilters', () => {
it('replaces the params as expected', () => { it('transforms the object keys as expected', () => {
const endpoint = transformIssuesApiEndpoint(dirtyEndpoint); const filters = transformFilters(originalFilters);
expect(endpoint).toBe(cleanEndpoint); expect(filters).toStrictEqual(tranformedFilters);
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment