Commit 193188b0 authored by Coung Ngo's avatar Coung Ngo Committed by Jacques Erasmus

Simplify issues counts query in issues page refactor

parent 2ec79fd7
...@@ -9,8 +9,8 @@ import { ...@@ -9,8 +9,8 @@ import {
GlTooltipDirective, GlTooltipDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus'; import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { cloneDeep } from 'lodash';
import getIssuesQuery from 'ee_else_ce/issues_list/queries/get_issues.query.graphql'; import getIssuesQuery from 'ee_else_ce/issues_list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues_list/queries/get_issues_counts.query.graphql';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { TYPE_USER } from '~/graphql_shared/constants'; import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
...@@ -21,7 +21,6 @@ import { IssuableListTabs, IssuableStates } from '~/issuable_list/constants'; ...@@ -21,7 +21,6 @@ import { IssuableListTabs, IssuableStates } from '~/issuable_list/constants';
import { import {
CREATED_DESC, CREATED_DESC,
i18n, i18n,
issuesCountSmartQueryBase,
MAX_LIST_SIZE, MAX_LIST_SIZE,
PAGE_SIZE, PAGE_SIZE,
PARAM_DUE_DATE, PARAM_DUE_DATE,
...@@ -164,18 +163,16 @@ export default { ...@@ -164,18 +163,16 @@ export default {
}, },
}, },
data() { data() {
const filterTokens = getFilterTokens(window.location.search);
const state = getParameterByName(PARAM_STATE); const state = getParameterByName(PARAM_STATE);
const sortKey = getSortKey(getParameterByName(PARAM_SORT)); const sortKey = getSortKey(getParameterByName(PARAM_SORT));
const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC; const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
this.initialFilterTokens = cloneDeep(filterTokens);
return { return {
dueDateFilter: getDueDateValue(getParameterByName(PARAM_DUE_DATE)), dueDateFilter: getDueDateValue(getParameterByName(PARAM_DUE_DATE)),
exportCsvPathWithQuery: this.getExportCsvPathWithQuery(), exportCsvPathWithQuery: this.getExportCsvPathWithQuery(),
filterTokens, filterTokens: getFilterTokens(window.location.search),
issues: [], issues: [],
issuesCounts: {},
pageInfo: {}, pageInfo: {},
pageParams: getInitialPageParams(sortKey), pageParams: getInitialPageParams(sortKey),
showBulkEditSidebar: false, showBulkEditSidebar: false,
...@@ -202,40 +199,21 @@ export default { ...@@ -202,40 +199,21 @@ export default {
}, },
debounce: 200, debounce: 200,
}, },
countOpened: { issuesCounts: {
...issuesCountSmartQueryBase, query: getIssuesCountsQuery,
variables() { variables() {
return { return this.queryVariables;
...this.queryVariables,
state: IssuableStates.Opened,
};
},
skip() {
return !this.hasAnyIssues;
}, },
}, update: ({ project }) => project ?? {},
countClosed: { error(error) {
...issuesCountSmartQueryBase, createFlash({ message: this.$options.i18n.errorFetchingCounts, captureError: true, error });
variables() {
return {
...this.queryVariables,
state: IssuableStates.Closed,
};
}, },
skip() { skip() {
return !this.hasAnyIssues; return !this.hasAnyIssues;
}, },
}, debounce: 200,
countAll: { context: {
...issuesCountSmartQueryBase, isSingleRequest: true,
variables() {
return {
...this.queryVariables,
state: IssuableStates.All,
};
},
skip() {
return !this.hasAnyIssues;
}, },
}, },
}, },
...@@ -263,6 +241,9 @@ export default { ...@@ -263,6 +241,9 @@ export default {
isOpenTab() { isOpenTab() {
return this.state === IssuableStates.Opened; return this.state === IssuableStates.Opened;
}, },
showCsvButtons() {
return this.isSignedIn;
},
apiFilterParams() { apiFilterParams() {
return convertToApiParams(this.filterTokens); return convertToApiParams(this.filterTokens);
}, },
...@@ -405,10 +386,11 @@ export default { ...@@ -405,10 +386,11 @@ export default {
return getSortOptions(this.hasIssueWeightsFeature, this.hasBlockedIssuesFeature); return getSortOptions(this.hasIssueWeightsFeature, this.hasBlockedIssuesFeature);
}, },
tabCounts() { tabCounts() {
const { openedIssues, closedIssues, allIssues } = this.issuesCounts;
return { return {
[IssuableStates.Opened]: this.countOpened, [IssuableStates.Opened]: openedIssues?.count,
[IssuableStates.Closed]: this.countClosed, [IssuableStates.Closed]: closedIssues?.count,
[IssuableStates.All]: this.countAll, [IssuableStates.All]: allIssues?.count,
}; };
}, },
currentTabCount() { currentTabCount() {
...@@ -584,13 +566,13 @@ export default { ...@@ -584,13 +566,13 @@ export default {
}) })
.then(() => { .then(() => {
const serializedVariables = JSON.stringify(this.queryVariables); const serializedVariables = JSON.stringify(this.queryVariables);
this.$apollo.mutate({ return this.$apollo.mutate({
mutation: reorderIssuesMutation, mutation: reorderIssuesMutation,
variables: { oldIndex, newIndex, serializedVariables }, variables: { oldIndex, newIndex, serializedVariables },
}); });
}) })
.catch(() => { .catch((error) => {
createFlash({ message: this.$options.i18n.reorderError }); createFlash({ message: this.$options.i18n.reorderError, captureError: true, error });
}); });
}, },
handleSort(sortKey) { handleSort(sortKey) {
...@@ -613,7 +595,7 @@ export default { ...@@ -613,7 +595,7 @@ export default {
recent-searches-storage-key="issues" recent-searches-storage-key="issues"
:search-input-placeholder="$options.i18n.searchPlaceholder" :search-input-placeholder="$options.i18n.searchPlaceholder"
:search-tokens="searchTokens" :search-tokens="searchTokens"
:initial-filter-value="initialFilterTokens" :initial-filter-value="filterTokens"
:sort-options="sortOptions" :sort-options="sortOptions"
:initial-sort-by="sortKey" :initial-sort-by="sortKey"
:issuables="issues" :issuables="issues"
...@@ -653,7 +635,7 @@ export default { ...@@ -653,7 +635,7 @@ export default {
:aria-label="$options.i18n.calendarLabel" :aria-label="$options.i18n.calendarLabel"
/> />
<csv-import-export-buttons <csv-import-export-buttons
v-if="isSignedIn" v-if="showCsvButtons"
class="gl-md-mr-3" class="gl-md-mr-3"
:export-csv-path="exportCsvPathWithQuery" :export-csv-path="exportCsvPathWithQuery"
:issuable-count="currentTabCount" :issuable-count="currentTabCount"
...@@ -766,6 +748,7 @@ export default { ...@@ -766,6 +748,7 @@ export default {
{{ $options.i18n.newIssueLabel }} {{ $options.i18n.newIssueLabel }}
</gl-button> </gl-button>
<csv-import-export-buttons <csv-import-export-buttons
v-if="showCsvButtons"
class="gl-mr-3" class="gl-mr-3"
:export-csv-path="exportCsvPathWithQuery" :export-csv-path="exportCsvPathWithQuery"
:issuable-count="currentTabCount" :issuable-count="currentTabCount"
......
import getIssuesCountQuery from 'ee_else_ce/issues_list/queries/get_issues_count.query.graphql';
import createFlash from '~/flash';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { import {
FILTER_ANY, FILTER_ANY,
...@@ -351,15 +349,3 @@ export const filters = { ...@@ -351,15 +349,3 @@ export const filters = {
}, },
}, },
}; };
export const issuesCountSmartQueryBase = {
query: getIssuesCountQuery,
context: {
isSingleRequest: true,
},
update: ({ project }) => project?.issues.count,
error(error) {
createFlash({ message: i18n.errorFetchingCounts, captureError: true, error });
},
debounce: 200,
};
query getIssuesCount( query getIssuesCount(
$fullPath: ID! $fullPath: ID!
$search: String $search: String
$state: IssuableState
$assigneeId: String $assigneeId: String
$assigneeUsernames: [String!] $assigneeUsernames: [String!]
$authorUsername: String $authorUsername: String
...@@ -12,9 +11,37 @@ query getIssuesCount( ...@@ -12,9 +11,37 @@ query getIssuesCount(
$not: NegatedIssueFilterInput $not: NegatedIssueFilterInput
) { ) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
issues( openedIssues: issues(
state: opened
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
labelName: $labelName
milestoneTitle: $milestoneTitle
milestoneWildcardId: $milestoneWildcardId
types: $types
not: $not
) {
count
}
closedIssues: issues(
state: closed
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
labelName: $labelName
milestoneTitle: $milestoneTitle
milestoneWildcardId: $milestoneWildcardId
types: $types
not: $not
) {
count
}
allIssues: issues(
state: all
search: $search search: $search
state: $state
assigneeId: $assigneeId assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername authorUsername: $authorUsername
......
query getIssuesCount( query getIssuesCount(
$fullPath: ID! $fullPath: ID!
$search: String $search: String
$state: IssuableState
$assigneeId: String $assigneeId: String
$assigneeUsernames: [String!] $assigneeUsernames: [String!]
$authorUsername: String $authorUsername: String
...@@ -16,9 +15,45 @@ query getIssuesCount( ...@@ -16,9 +15,45 @@ query getIssuesCount(
$not: NegatedIssueFilterInput $not: NegatedIssueFilterInput
) { ) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
issues( openedIssues: issues(
state: opened
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
labelName: $labelName
milestoneTitle: $milestoneTitle
milestoneWildcardId: $milestoneWildcardId
types: $types
epicId: $epicId
iterationId: $iterationId
iterationWildcardId: $iterationWildcardId
weight: $weight
not: $not
) {
count
}
closedIssues: issues(
state: closed
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
labelName: $labelName
milestoneTitle: $milestoneTitle
milestoneWildcardId: $milestoneWildcardId
types: $types
epicId: $epicId
iterationId: $iterationId
iterationWildcardId: $iterationWildcardId
weight: $weight
not: $not
) {
count
}
allIssues: issues(
state: all
search: $search search: $search
state: $state
assigneeId: $assigneeId assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername authorUsername: $authorUsername
......
...@@ -5,17 +5,17 @@ import { cloneDeep } from 'lodash'; ...@@ -5,17 +5,17 @@ import { cloneDeep } from 'lodash';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import getIssuesQuery from 'ee_else_ce/issues_list/queries/get_issues.query.graphql'; import getIssuesQuery from 'ee_else_ce/issues_list/queries/get_issues.query.graphql';
import getIssuesCountQuery from 'ee_else_ce/issues_list/queries/get_issues_count.query.graphql'; import getIssuesCountsQuery from 'ee_else_ce/issues_list/queries/get_issues_counts.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper'; import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { import {
getIssuesCountsQueryResponse,
getIssuesQueryResponse, getIssuesQueryResponse,
filteredTokens, filteredTokens,
locationSearch, locationSearch,
urlParams, urlParams,
getIssuesCountQueryResponse,
} from 'jest/issues_list/mock_data'; } from 'jest/issues_list/mock_data';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
...@@ -97,12 +97,12 @@ describe('IssuesListApp component', () => { ...@@ -97,12 +97,12 @@ describe('IssuesListApp component', () => {
const mountComponent = ({ const mountComponent = ({
provide = {}, provide = {},
issuesQueryResponse = jest.fn().mockResolvedValue(defaultQueryResponse), issuesQueryResponse = jest.fn().mockResolvedValue(defaultQueryResponse),
issuesQueryCountResponse = jest.fn().mockResolvedValue(getIssuesCountQueryResponse), issuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse),
mountFn = shallowMount, mountFn = shallowMount,
} = {}) => { } = {}) => {
const requestHandlers = [ const requestHandlers = [
[getIssuesQuery, issuesQueryResponse], [getIssuesQuery, issuesQueryResponse],
[getIssuesCountQuery, issuesQueryCountResponse], [getIssuesCountsQuery, issuesCountsQueryResponse],
]; ];
const apolloProvider = createMockApollo(requestHandlers); const apolloProvider = createMockApollo(requestHandlers);
...@@ -571,9 +571,9 @@ describe('IssuesListApp component', () => { ...@@ -571,9 +571,9 @@ describe('IssuesListApp component', () => {
describe('errors', () => { describe('errors', () => {
describe.each` describe.each`
error | mountOption | message error | mountOption | message
${'fetching issues'} | ${'issuesQueryResponse'} | ${IssuesListApp.i18n.errorFetchingIssues} ${'fetching issues'} | ${'issuesQueryResponse'} | ${IssuesListApp.i18n.errorFetchingIssues}
${'fetching issue counts'} | ${'issuesQueryCountResponse'} | ${IssuesListApp.i18n.errorFetchingCounts} ${'fetching issue counts'} | ${'issuesCountsQueryResponse'} | ${IssuesListApp.i18n.errorFetchingCounts}
`('when there is an error $error', ({ mountOption, message }) => { `('when there is an error $error', ({ mountOption, message }) => {
beforeEach(() => { beforeEach(() => {
wrapper = mountComponent({ wrapper = mountComponent({
...@@ -696,7 +696,11 @@ describe('IssuesListApp component', () => { ...@@ -696,7 +696,11 @@ describe('IssuesListApp component', () => {
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: IssuesListApp.i18n.reorderError }); expect(createFlash).toHaveBeenCalledWith({
message: IssuesListApp.i18n.reorderError,
captureError: true,
error: new Error('Request failed with status code 500'),
});
}); });
}); });
}); });
......
...@@ -70,10 +70,16 @@ export const getIssuesQueryResponse = { ...@@ -70,10 +70,16 @@ export const getIssuesQueryResponse = {
}, },
}; };
export const getIssuesCountQueryResponse = { export const getIssuesCountsQueryResponse = {
data: { data: {
project: { project: {
issues: { openedIssues: {
count: 1,
},
closedIssues: {
count: 1,
},
allIssues: {
count: 1, count: 1,
}, },
}, },
......
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