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';
import { s__ } from '~/locale';
import { getMonthNames } from '~/lib/utils/datetime_utility';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import IssuesAnalyticsTable from './issues_analytics_table.vue';
import { transformIssuesApiEndpoint } from '../utils';
import { transformFilters } from '../utils';
export default {
components: {
......@@ -111,11 +112,16 @@ export default {
return engineeringNotation(sum(...this.series));
},
issuesTableEndpoints() {
const publicApiFilters = transformFilters(this.appliedFilters);
return {
api: transformIssuesApiEndpoint(`${this.issuesApiEndpoint}${this.appliedFilters}`),
api: mergeUrlParams(publicApiFilters, this.issuesApiEndpoint),
issuesPage: this.issuesPageEndpoint,
};
},
filterString() {
return JSON.stringify(this.appliedFilters);
},
},
watch: {
appliedFilters() {
......@@ -183,7 +189,7 @@ export default {
</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
v-if="showFiltersEmptyState"
......
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_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 { historyPushState } from '~/lib/utils/common_utils';
import { historyPushState, urlParamsToObject } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import issueAnalyticsStore from './stores';
......@@ -33,6 +33,8 @@ export default class FilteredSearchIssueAnalytics extends FilteredSearchManager
*/
updateObject = 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';
import IssuesAnalytics from './components/issues_analytics.vue';
import store from './stores';
import FilteredSearchIssueAnalytics from './filtered_search_issues_analytics';
import { urlParamsToObject } from '~/lib/utils/common_utils';
export default () => {
const el = document.querySelector('#js-issues-analytics');
......@@ -18,7 +19,8 @@ export default () => {
} = el.dataset;
// 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({
el,
......
import axios from '~/lib/utils/axios_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
export default {
fetchChartData(endpoint, filters) {
return axios.get(`${endpoint}${filters}`);
return axios.get(mergeUrlParams(filters, endpoint));
},
};
export default () => ({
loading: false,
filters: '',
filters: {},
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
* and transforms parameters which are not standardized between the internal
* issues analytics api and the public issues api.
* This util method takes the global page filters and transforms parameters which
* are not standardized between the internal issues analytics api and the public
* 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
export const transformIssuesApiEndpoint = endpoint => {
const cleanEndpoint = removeParams([LABEL_FILTER_NAME, MILESTONE_FILTER_NAME], endpoint, true);
const labels = getParameterValues(LABEL_FILTER_NAME, endpoint);
const milestone = getParameterValues(MILESTONE_FILTER_NAME, endpoint);
return mergeUrlParams({ labels, milestone }, cleanEndpoint);
};
export const transformFilters = ({
label_name: labels = null,
milestone_title: milestone = null,
...restOfFilters
}) => ({
...restOfFilters,
labels,
milestone,
});
......@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from 'ee/issues_analytics/stores/modules/issue_analytics/actions';
import axios from '~/lib/utils/axios_utils';
import { TEST_HOST } from 'helpers/test_constants';
describe('Issue analytics store actions', () => {
describe('setFilters', () => {
......@@ -41,14 +42,14 @@ describe('Issue analytics store actions', () => {
});
it('commits SET_CHART_DATA with chart data', () => {
const getters = { appliedFilters: '?hello=world' };
const getters = { appliedFilters: { hello: 'world' } };
const context = {
dispatch,
getters,
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(commit).toHaveBeenCalledWith('SET_CHART_DATA', chartData);
expect(dispatch.mock.calls[1]).toEqual(['setLoadingState', false]);
......
import { transformIssuesApiEndpoint } from 'ee/issues_analytics/utils';
import { TEST_HOST } from 'helpers/test_constants';
import { transformFilters } from 'ee/issues_analytics/utils';
const dirtyEndpoint = `${TEST_HOST}/issues?label_name[]=cool&label_name[]=beans&milestone_title=v4.0`;
const cleanEndpoint = `${TEST_HOST}/issues?labels=cool%2Cbeans&milestone=v4.0`;
const originalFilters = {
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('transformIssuesApiEndpoint', () => {
it('replaces the params as expected', () => {
const endpoint = transformIssuesApiEndpoint(dirtyEndpoint);
describe('transformFilters', () => {
it('transforms the object keys as expected', () => {
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