Commit 03eeb688 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch 'ek-add-extract-filter-params-util' into 'master'

Extract helpers for filter and pagination params

See merge request gitlab-org/gitlab!71917
parents 58ee9026 378790eb
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { dateFormats } from './constants'; import { dateFormats } from './constants';
export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'name') => { export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'name') => {
...@@ -7,3 +8,45 @@ export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'na ...@@ -7,3 +8,45 @@ export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'na
}; };
export const toYmd = (date) => dateFormat(date, dateFormats.isoDate); export const toYmd = (date) => dateFormat(date, dateFormats.isoDate);
/**
* Takes a url and extracts query parameters used for the shared
* filter bar
*
* @param {string} url The URL to extract query parameters from
* @returns {Object}
*/
export const extractFilterQueryParameters = (url = '') => {
const {
source_branch_name = null,
target_branch_name = null,
author_username = null,
milestone_title = null,
assignee_username = [],
label_name = [],
} = urlQueryToFilter(url);
return {
selectedSourceBranch: source_branch_name,
selectedTargetBranch: target_branch_name,
selectedAuthor: author_username,
selectedMilestone: milestone_title,
selectedAssigneeList: assignee_username,
selectedLabelList: label_name,
};
};
/**
* Takes a url and extracts sorting and pagination query parameters into an object
*
* @param {string} url The URL to extract query parameters from
* @returns {Object}
*/
export const extractPaginationQueryParameters = (url = '') => {
const { sort, direction, page } = urlQueryToFilter(url);
return {
sort: sort?.value || null,
direction: direction?.value || null,
page: page?.value || null,
};
};
import Vue from 'vue'; import Vue from 'vue';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; import { extractFilterQueryParameters } from '~/analytics/shared/utils';
import CodeAnalyticsApp from './components/app.vue'; import CodeAnalyticsApp from './components/app.vue';
import store from './store'; import store from './store';
...@@ -20,11 +20,11 @@ export default () => { ...@@ -20,11 +20,11 @@ export default () => {
labelsEndpoint: labelsPath, labelsEndpoint: labelsPath,
projectEndpoint: projectPath, projectEndpoint: projectPath,
}); });
const { milestone_title = null, label_name = [] } = urlQueryToFilter(window.location.search);
store.dispatch('filters/initialize', { const { selectedMilestone, selectedLabelList } = extractFilterQueryParameters(
selectedMilestone: milestone_title, window.location.search,
selectedLabelList: label_name, );
}); store.dispatch('filters/initialize', { selectedMilestone, selectedLabelList });
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
......
...@@ -2,7 +2,10 @@ import { GlToast } from '@gitlab/ui'; ...@@ -2,7 +2,10 @@ import { GlToast } from '@gitlab/ui';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; import {
extractFilterQueryParameters,
extractPaginationQueryParameters,
} from '~/analytics/shared/utils';
import { buildCycleAnalyticsInitialData } from '../shared/utils'; import { buildCycleAnalyticsInitialData } from '../shared/utils';
import CycleAnalytics from './components/base.vue'; import CycleAnalytics from './components/base.vue';
import createStore from './store'; import createStore from './store';
...@@ -20,27 +23,21 @@ export default () => { ...@@ -20,27 +23,21 @@ export default () => {
const initialData = buildCycleAnalyticsInitialData(el.dataset); const initialData = buildCycleAnalyticsInitialData(el.dataset);
const store = createStore(); const store = createStore();
const pagination = extractPaginationQueryParameters(window.location.search);
const { const {
author_username = null, selectedAuthor,
milestone_title = null, selectedMilestone,
assignee_username = [], selectedAssigneeList,
label_name = [], selectedLabelList,
sort, } = extractFilterQueryParameters(window.location.search);
direction,
page,
} = urlQueryToFilter(window.location.search);
store.dispatch('initializeCycleAnalytics', { store.dispatch('initializeCycleAnalytics', {
...initialData, ...initialData,
selectedAuthor: author_username, selectedAuthor,
selectedMilestone: milestone_title, selectedMilestone,
selectedAssigneeList: assignee_username, selectedAssigneeList,
selectedLabelList: label_name, selectedLabelList,
pagination: { pagination,
sort: sort?.value || null,
direction: direction?.value || null,
page: page?.value || null,
},
}); });
return new Vue({ return new Vue({
......
...@@ -3,7 +3,7 @@ import VueApollo from 'vue-apollo'; ...@@ -3,7 +3,7 @@ import VueApollo from 'vue-apollo';
import { ITEM_TYPE } from '~/groups/constants'; import { ITEM_TYPE } from '~/groups/constants';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { getParameterValues } from '~/lib/utils/url_utility'; import { getParameterValues } from '~/lib/utils/url_utility';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; import { extractFilterQueryParameters } from '~/analytics/shared/utils';
import MergeRequestAnalyticsApp from './components/app.vue'; import MergeRequestAnalyticsApp from './components/app.vue';
import createStore from './store'; import createStore from './store';
import { parseAndValidateDates } from './utils'; import { parseAndValidateDates } from './utils';
...@@ -28,21 +28,23 @@ export default () => { ...@@ -28,21 +28,23 @@ export default () => {
groupEndpoint: type === ITEM_TYPE.GROUP ? fullPath : null, groupEndpoint: type === ITEM_TYPE.GROUP ? fullPath : null,
projectEndpoint: type === ITEM_TYPE.PROJECT ? fullPath : null, projectEndpoint: type === ITEM_TYPE.PROJECT ? fullPath : null,
}); });
const { const {
source_branch_name = null, selectedSourceBranch,
target_branch_name = null, selectedTargetBranch,
assignee_username = null, selectedAssignee,
author_username = null, selectedAuthor,
milestone_title = null, selectedMilestone,
label_name = [], selectedLabelList,
} = urlQueryToFilter(window.location.search); } = extractFilterQueryParameters(window.location.search);
store.dispatch('filters/initialize', { store.dispatch('filters/initialize', {
selectedSourceBranch: source_branch_name, selectedSourceBranch,
selectedTargetBranch: target_branch_name, selectedTargetBranch,
selectedAssignee: assignee_username, selectedAssignee,
selectedAuthor: author_username, selectedAuthor,
selectedMilestone: milestone_title, selectedMilestone,
selectedLabelList: label_name, selectedLabelList,
}); });
const { startDate, endDate } = parseAndValidateDates( const { startDate, endDate } = parseAndValidateDates(
......
import { filterBySearchTerm } from '~/analytics/shared/utils'; import {
filterBySearchTerm,
extractFilterQueryParameters,
extractPaginationQueryParameters,
} from '~/analytics/shared/utils';
import { objectToQuery } from '~/lib/utils/url_utility';
describe('filterBySearchTerm', () => { describe('filterBySearchTerm', () => {
const data = [ const data = [
...@@ -22,3 +27,102 @@ describe('filterBySearchTerm', () => { ...@@ -22,3 +27,102 @@ describe('filterBySearchTerm', () => {
expect(filterBySearchTerm(data, 'ne', 'title')).toEqual([data[0]]); expect(filterBySearchTerm(data, 'ne', 'title')).toEqual([data[0]]);
}); });
}); });
describe('extractFilterQueryParameters', () => {
const selectedAuthor = 'Author 1';
const selectedMilestone = 'Milestone 1.0';
const selectedSourceBranch = 'main';
const selectedTargetBranch = 'feature-1';
const selectedAssigneeList = ['Alice', 'Bob'];
const selectedLabelList = ['Label 1', 'Label 2'];
const queryParamsString = objectToQuery({
source_branch_name: selectedSourceBranch,
target_branch_name: selectedTargetBranch,
author_username: selectedAuthor,
milestone_title: selectedMilestone,
assignee_username: selectedAssigneeList,
label_name: selectedLabelList,
});
it('extracts the correct filter parameters from a url', () => {
const result = extractFilterQueryParameters(queryParamsString);
const operator = '=';
const expectedFilters = {
selectedAssigneeList: { operator, value: selectedAssigneeList.join(',') },
selectedLabelList: { operator, value: selectedLabelList.join(',') },
selectedAuthor: { operator, value: selectedAuthor },
selectedMilestone: { operator, value: selectedMilestone },
selectedSourceBranch: { operator, value: selectedSourceBranch },
selectedTargetBranch: { operator, value: selectedTargetBranch },
};
expect(result).toMatchObject(expectedFilters);
});
it('returns null for missing parameters', () => {
const result = extractFilterQueryParameters('');
const expectedFilters = {
selectedAuthor: null,
selectedMilestone: null,
selectedSourceBranch: null,
selectedTargetBranch: null,
};
expect(result).toMatchObject(expectedFilters);
});
it('only returns the parameters we expect', () => {
const result = extractFilterQueryParameters('foo="one"&bar="two"');
const resultKeys = Object.keys(result);
['foo', 'bar'].forEach((key) => {
expect(resultKeys).not.toContain(key);
});
[
'selectedAuthor',
'selectedMilestone',
'selectedSourceBranch',
'selectedTargetBranch',
'selectedAssigneeList',
'selectedLabelList',
].forEach((key) => {
expect(resultKeys).toContain(key);
});
});
it('returns an empty array for missing list parameters', () => {
const result = extractFilterQueryParameters('');
const expectedFilters = { selectedAssigneeList: [], selectedLabelList: [] };
expect(result).toMatchObject(expectedFilters);
});
});
describe('extractPaginationQueryParameters', () => {
const sort = 'title';
const direction = 'asc';
const page = '1';
const queryParamsString = objectToQuery({ sort, direction, page });
it('extracts the correct filter parameters from a url', () => {
const result = extractPaginationQueryParameters(queryParamsString);
const expectedFilters = { sort, page, direction };
expect(result).toMatchObject(expectedFilters);
});
it('returns null for missing parameters', () => {
const result = extractPaginationQueryParameters('');
const expectedFilters = { sort: null, direction: null, page: null };
expect(result).toMatchObject(expectedFilters);
});
it('only returns the parameters we expect', () => {
const result = extractPaginationQueryParameters('foo="one"&bar="two"&qux="three"');
const resultKeys = Object.keys(result);
['foo', 'bar', 'qux'].forEach((key) => {
expect(resultKeys).not.toContain(key);
});
['sort', 'page', 'direction'].forEach((key) => {
expect(resultKeys).toContain(key);
});
});
});
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