From 95d309bfb847dbafbfb3784d1933a3eb269dde24 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 27 May 2021 21:10:59 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../components/issuable_item.vue | 16 +- .../components/issuable_list_root.vue | 3 +- .../components/issue_card_time_info.vue | 7 +- .../components/issues_list_app.vue | 155 ++++++----- .../javascripts/issues_list/constants.js | 223 +++++++++++++--- app/assets/javascripts/issues_list/index.js | 11 +- .../queries/get_issues.query.graphql | 45 ---- .../queries/issue_info.fragment.graphql | 51 ---- app/assets/javascripts/issues_list/utils.js | 50 +--- .../components/app_index_apollo_client.vue | 37 +++ .../releases_pagination_apollo_client.vue | 37 +++ .../tokens/iteration_token.vue | 6 +- .../concerns/integrations_actions.rb | 1 - .../projects/branches_controller.rb | 18 +- .../projects/services_controller.rb | 9 +- .../settings/integrations_controller.rb | 2 +- app/helpers/issues_helper.rb | 1 + .../admin/serverless/domains/_form.html.haml | 2 +- app/views/admin/users/_modals.html.haml | 2 +- .../layouts/header/_help_dropdown.html.haml | 2 +- app/views/profiles/show.html.haml | 2 +- .../_bitbucket_import_modal.html.haml | 2 +- .../projects/_gitlab_import_modal.html.haml | 2 +- .../projects/_visibility_modal.html.haml | 2 +- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/blob/_remove.html.haml | 2 +- app/views/projects/blob/_upload.html.haml | 2 +- .../_delete_protected_modal.html.haml | 2 +- .../settings/integrations/show.html.haml | 2 +- .../shared/_confirm_fork_modal.html.haml | 2 +- app/views/shared/_confirm_modal.html.haml | 2 +- .../_apply_template_warning.html.haml | 2 +- .../gitpod/_enable_gitpod_modal.html.haml | 2 +- doc/administration/database_load_balancing.md | 28 +- doc/administration/troubleshooting/debug.md | 2 +- doc/administration/troubleshooting/defcon.md | 20 +- .../troubleshooting/diagnostics_tools.md | 2 +- .../gitlab_rails_cheat_sheet.md | 20 ++ doc/administration/troubleshooting/index.md | 2 +- .../troubleshooting/kubernetes_cheat_sheet.md | 2 +- .../troubleshooting/linux_cheat_sheet.md | 2 +- .../troubleshooting/log_parsing.md | 2 +- .../navigating_gitlab_via_rails_console.md | 2 +- doc/administration/troubleshooting/sidekiq.md | 2 +- doc/administration/troubleshooting/ssl.md | 2 +- .../troubleshooting/test_environments.md | 2 +- .../troubleshooting/tracing_correlation_id.md | 2 +- doc/development/sidekiq_style_guide.md | 68 +++++ doc/subscriptions/img/license-file.png | Bin 0 -> 72190 bytes doc/subscriptions/img/license-overview.png | Bin 0 -> 38018 bytes doc/subscriptions/img/publicly-visible.png | Bin 0 -> 58946 bytes doc/subscriptions/img/support-diagram.png | Bin 0 -> 49941 bytes doc/subscriptions/index.md | 100 ++++++- doc/topics/autodevops/requirements.md | 19 ++ .../img/rebase_reset.png | Bin 21836 -> 33399 bytes .../img/revert.png | Bin 13243 -> 21473 bytes .../index.md | 163 +++++------- lib/gitlab/ci/variables/collection.rb | 4 + locale/gitlab.pot | 6 - .../usage_data/histogram_with_large_table.rb | 58 ++++ rubocop/rubocop-usage-data.yml | 58 ++++ .../projects/branches_controller_spec.rb | 15 -- .../components/issue_card_time_info_spec.js | 10 +- .../components/issues_list_app_spec.js | 249 ++++++++++-------- spec/frontend/issues_list/mock_data.js | 122 ++------- spec/frontend/issues_list/utils_spec.js | 26 +- .../app_index_apollo_client_spec.js | 65 ++++- .../releases_pagination_apollo_client_spec.js | 126 +++++++++ spec/helpers/issues_helper_spec.rb | 1 + .../gitlab/ci/variables/collection_spec.rb | 24 ++ .../histogram_with_large_table_spec.rb | 108 ++++++++ 71 files changed, 1309 insertions(+), 707 deletions(-) delete mode 100644 app/assets/javascripts/issues_list/queries/get_issues.query.graphql delete mode 100644 app/assets/javascripts/issues_list/queries/issue_info.fragment.graphql create mode 100644 app/assets/javascripts/releases/components/releases_pagination_apollo_client.vue create mode 100644 doc/subscriptions/img/license-file.png create mode 100644 doc/subscriptions/img/license-overview.png create mode 100644 doc/subscriptions/img/publicly-visible.png create mode 100644 doc/subscriptions/img/support-diagram.png create mode 100644 rubocop/cop/usage_data/histogram_with_large_table.rb create mode 100644 spec/frontend/releases/components/releases_pagination_apollo_client_spec.js create mode 100644 spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb diff --git a/app/assets/javascripts/issuable_list/components/issuable_item.vue b/app/assets/javascripts/issuable_list/components/issuable_item.vue index f9414076260..7635536c54f 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_item.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_item.vue @@ -50,9 +50,6 @@ export default { }, }, computed: { - issuableId() { - return getIdFromGraphQLId(this.issuable.id); - }, createdInPastDay() { const createdSecondsAgo = differenceInSeconds(new Date(this.issuable.createdAt), new Date()); return createdSecondsAgo < SECONDS_IN_DAY; @@ -64,7 +61,7 @@ export default { return this.issuable.gitlabWebUrl || this.issuable.webUrl; }, authorId() { - return getIdFromGraphQLId(this.author.id); + return getIdFromGraphQLId(`${this.author.id}`); }, isIssuableUrlExternal() { return isExternal(this.webUrl); @@ -73,10 +70,10 @@ export default { return this.issuable.labels?.nodes || this.issuable.labels || []; }, labelIdsString() { - return JSON.stringify(this.labels.map((label) => getIdFromGraphQLId(label.id))); + return JSON.stringify(this.labels.map((label) => label.id)); }, assignees() { - return this.issuable.assignees?.nodes || this.issuable.assignees || []; + return this.issuable.assignees || []; }, createdAt() { return sprintf(__('created %{timeAgo}'), { @@ -84,9 +81,6 @@ export default { }); }, updatedAt() { - if (!this.issuable.updatedAt) { - return ''; - } return sprintf(__('updated %{timeAgo}'), { timeAgo: getTimeago().format(this.issuable.updatedAt), }); @@ -163,7 +157,7 @@ export default { diff --git a/app/assets/javascripts/issues_list/constants.js b/app/assets/javascripts/issues_list/constants.js index 414e7b756ef..06e140d6420 100644 --- a/app/assets/javascripts/issues_list/constants.js +++ b/app/assets/javascripts/issues_list/constants.js @@ -101,6 +101,7 @@ export const i18n = { export const JIRA_IMPORT_SUCCESS_ALERT_HIDE_MAP_KEY = 'jira-import-success-alert-hide-map'; export const PARAM_DUE_DATE = 'due_date'; +export const PARAM_PAGE = 'page'; export const PARAM_SORT = 'sort'; export const PARAM_STATE = 'state'; @@ -124,21 +125,21 @@ export const CREATED_ASC = 'CREATED_ASC'; export const CREATED_DESC = 'CREATED_DESC'; export const DUE_DATE_ASC = 'DUE_DATE_ASC'; export const DUE_DATE_DESC = 'DUE_DATE_DESC'; -export const LABEL_PRIORITY_ASC = 'LABEL_PRIORITY_ASC'; export const LABEL_PRIORITY_DESC = 'LABEL_PRIORITY_DESC'; export const MILESTONE_DUE_ASC = 'MILESTONE_DUE_ASC'; export const MILESTONE_DUE_DESC = 'MILESTONE_DUE_DESC'; export const POPULARITY_ASC = 'POPULARITY_ASC'; export const POPULARITY_DESC = 'POPULARITY_DESC'; -export const PRIORITY_ASC = 'PRIORITY_ASC'; export const PRIORITY_DESC = 'PRIORITY_DESC'; -export const RELATIVE_POSITION_ASC = 'RELATIVE_POSITION_ASC'; +export const RELATIVE_POSITION_DESC = 'RELATIVE_POSITION_DESC'; export const UPDATED_ASC = 'UPDATED_ASC'; export const UPDATED_DESC = 'UPDATED_DESC'; export const WEIGHT_ASC = 'WEIGHT_ASC'; export const WEIGHT_DESC = 'WEIGHT_DESC'; -const PRIORITY_ASC_SORT = 'priority_asc'; +const SORT_ASC = 'asc'; +const SORT_DESC = 'desc'; + const CREATED_DATE_SORT = 'created_date'; const CREATED_ASC_SORT = 'created_asc'; const UPDATED_DESC_SORT = 'updated_desc'; @@ -146,30 +147,129 @@ const UPDATED_ASC_SORT = 'updated_asc'; const MILESTONE_SORT = 'milestone'; const MILESTONE_DUE_DESC_SORT = 'milestone_due_desc'; const DUE_DATE_DESC_SORT = 'due_date_desc'; -const LABEL_PRIORITY_ASC_SORT = 'label_priority_asc'; const POPULARITY_ASC_SORT = 'popularity_asc'; const WEIGHT_DESC_SORT = 'weight_desc'; const BLOCKING_ISSUES_DESC_SORT = 'blocking_issues_desc'; +const BLOCKING_ISSUES = 'blocking_issues'; + +export const apiSortParams = { + [PRIORITY_DESC]: { + order_by: PRIORITY, + sort: SORT_DESC, + }, + [CREATED_ASC]: { + order_by: CREATED_AT, + sort: SORT_ASC, + }, + [CREATED_DESC]: { + order_by: CREATED_AT, + sort: SORT_DESC, + }, + [UPDATED_ASC]: { + order_by: UPDATED_AT, + sort: SORT_ASC, + }, + [UPDATED_DESC]: { + order_by: UPDATED_AT, + sort: SORT_DESC, + }, + [MILESTONE_DUE_ASC]: { + order_by: MILESTONE_DUE, + sort: SORT_ASC, + }, + [MILESTONE_DUE_DESC]: { + order_by: MILESTONE_DUE, + sort: SORT_DESC, + }, + [DUE_DATE_ASC]: { + order_by: DUE_DATE, + sort: SORT_ASC, + }, + [DUE_DATE_DESC]: { + order_by: DUE_DATE, + sort: SORT_DESC, + }, + [POPULARITY_ASC]: { + order_by: POPULARITY, + sort: SORT_ASC, + }, + [POPULARITY_DESC]: { + order_by: POPULARITY, + sort: SORT_DESC, + }, + [LABEL_PRIORITY_DESC]: { + order_by: LABEL_PRIORITY, + sort: SORT_DESC, + }, + [RELATIVE_POSITION_DESC]: { + order_by: RELATIVE_POSITION, + per_page: 100, + sort: SORT_ASC, + }, + [WEIGHT_ASC]: { + order_by: WEIGHT, + sort: SORT_ASC, + }, + [WEIGHT_DESC]: { + order_by: WEIGHT, + sort: SORT_DESC, + }, + [BLOCKING_ISSUES_DESC]: { + order_by: BLOCKING_ISSUES, + sort: SORT_DESC, + }, +}; export const urlSortParams = { - [PRIORITY_ASC]: PRIORITY_ASC_SORT, - [PRIORITY_DESC]: PRIORITY, - [CREATED_ASC]: CREATED_ASC_SORT, - [CREATED_DESC]: CREATED_DATE_SORT, - [UPDATED_ASC]: UPDATED_ASC_SORT, - [UPDATED_DESC]: UPDATED_DESC_SORT, - [MILESTONE_DUE_ASC]: MILESTONE_SORT, - [MILESTONE_DUE_DESC]: MILESTONE_DUE_DESC_SORT, - [DUE_DATE_ASC]: DUE_DATE, - [DUE_DATE_DESC]: DUE_DATE_DESC_SORT, - [POPULARITY_ASC]: POPULARITY_ASC_SORT, - [POPULARITY_DESC]: POPULARITY, - [LABEL_PRIORITY_ASC]: LABEL_PRIORITY_ASC_SORT, - [LABEL_PRIORITY_DESC]: LABEL_PRIORITY, - [RELATIVE_POSITION_ASC]: RELATIVE_POSITION, - [WEIGHT_ASC]: WEIGHT, - [WEIGHT_DESC]: WEIGHT_DESC_SORT, - [BLOCKING_ISSUES_DESC]: BLOCKING_ISSUES_DESC_SORT, + [PRIORITY_DESC]: { + sort: PRIORITY, + }, + [CREATED_ASC]: { + sort: CREATED_ASC_SORT, + }, + [CREATED_DESC]: { + sort: CREATED_DATE_SORT, + }, + [UPDATED_ASC]: { + sort: UPDATED_ASC_SORT, + }, + [UPDATED_DESC]: { + sort: UPDATED_DESC_SORT, + }, + [MILESTONE_DUE_ASC]: { + sort: MILESTONE_SORT, + }, + [MILESTONE_DUE_DESC]: { + sort: MILESTONE_DUE_DESC_SORT, + }, + [DUE_DATE_ASC]: { + sort: DUE_DATE, + }, + [DUE_DATE_DESC]: { + sort: DUE_DATE_DESC_SORT, + }, + [POPULARITY_ASC]: { + sort: POPULARITY_ASC_SORT, + }, + [POPULARITY_DESC]: { + sort: POPULARITY, + }, + [LABEL_PRIORITY_DESC]: { + sort: LABEL_PRIORITY, + }, + [RELATIVE_POSITION_DESC]: { + sort: RELATIVE_POSITION, + per_page: 100, + }, + [WEIGHT_ASC]: { + sort: WEIGHT, + }, + [WEIGHT_DESC]: { + sort: WEIGHT_DESC_SORT, + }, + [BLOCKING_ISSUES_DESC]: { + sort: BLOCKING_ISSUES_DESC_SORT, + }, }; export const MAX_LIST_SIZE = 10; @@ -194,7 +294,12 @@ export const TOKEN_TYPE_WEIGHT = 'weight'; export const filters = { [TOKEN_TYPE_AUTHOR]: { [API_PARAM]: { - [NORMAL_FILTER]: 'authorUsername', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'author_username', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[author_username]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -207,8 +312,13 @@ export const filters = { }, [TOKEN_TYPE_ASSIGNEE]: { [API_PARAM]: { - [NORMAL_FILTER]: 'assigneeUsernames', - [SPECIAL_FILTER]: 'assigneeId', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'assignee_username', + [SPECIAL_FILTER]: 'assignee_id', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[assignee_username]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -223,7 +333,12 @@ export const filters = { }, [TOKEN_TYPE_MILESTONE]: { [API_PARAM]: { - [NORMAL_FILTER]: 'milestoneTitle', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'milestone', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[milestone]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -236,13 +351,16 @@ export const filters = { }, [TOKEN_TYPE_LABEL]: { [API_PARAM]: { - [NORMAL_FILTER]: 'labelName', - [SPECIAL_FILTER]: 'labelName', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'labels', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[labels]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { [NORMAL_FILTER]: 'label_name[]', - [SPECIAL_FILTER]: 'label_name[]', }, [OPERATOR_IS_NOT]: { [NORMAL_FILTER]: 'not[label_name][]', @@ -251,8 +369,10 @@ export const filters = { }, [TOKEN_TYPE_MY_REACTION]: { [API_PARAM]: { - [NORMAL_FILTER]: 'myReactionEmoji', - [SPECIAL_FILTER]: 'myReactionEmoji', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'my_reaction_emoji', + [SPECIAL_FILTER]: 'my_reaction_emoji', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -263,7 +383,9 @@ export const filters = { }, [TOKEN_TYPE_CONFIDENTIAL]: { [API_PARAM]: { - [NORMAL_FILTER]: 'confidential', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'confidential', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -273,23 +395,33 @@ export const filters = { }, [TOKEN_TYPE_ITERATION]: { [API_PARAM]: { - [NORMAL_FILTER]: 'iterationId', - [SPECIAL_FILTER]: 'iterationWildcardId', - }, - [URL_PARAM]: { [OPERATOR_IS]: { - [NORMAL_FILTER]: 'iteration_id', + [NORMAL_FILTER]: 'iteration_title', [SPECIAL_FILTER]: 'iteration_id', }, [OPERATOR_IS_NOT]: { - [NORMAL_FILTER]: 'not[iteration_id]', + [NORMAL_FILTER]: 'not[iteration_title]', + }, + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'iteration_title', + [SPECIAL_FILTER]: 'iteration_id', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[iteration_title]', }, }, }, [TOKEN_TYPE_EPIC]: { [API_PARAM]: { - [NORMAL_FILTER]: 'epicId', - [SPECIAL_FILTER]: 'epicId', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'epic_id', + [SPECIAL_FILTER]: 'epic_id', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[epic_id]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { @@ -303,8 +435,13 @@ export const filters = { }, [TOKEN_TYPE_WEIGHT]: { [API_PARAM]: { - [NORMAL_FILTER]: 'weight', - [SPECIAL_FILTER]: 'weight', + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'weight', + [SPECIAL_FILTER]: 'weight', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[weight]', + }, }, [URL_PARAM]: { [OPERATOR_IS]: { diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index 7d800caa33d..d0c9462a3d7 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -73,13 +73,6 @@ export function mountIssuesListApp() { return false; } - Vue.use(VueApollo); - - const defaultClient = createDefaultClient({}, { assumeImmutableResults: true }); - const apolloProvider = new VueApollo({ - defaultClient, - }); - const { autocompleteAwardEmojisPath, autocompleteUsersPath, @@ -90,6 +83,7 @@ export function mountIssuesListApp() { email, emailsHelpPagePath, emptyStateSvgPath, + endpoint, exportCsvPath, groupEpicsPath, hasBlockedIssuesFeature, @@ -121,13 +115,14 @@ export function mountIssuesListApp() { el, // Currently does not use Vue Apollo, but need to provide {} for now until the // issue is fixed upstream in https://github.com/vuejs/vue-apollo/pull/1153 - apolloProvider, + apolloProvider: {}, provide: { autocompleteAwardEmojisPath, autocompleteUsersPath, calendarPath, canBulkUpdate: parseBoolean(canBulkUpdate), emptyStateSvgPath, + endpoint, groupEpicsPath, hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature), hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature), diff --git a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues.query.graphql deleted file mode 100644 index ded70d2d7ba..00000000000 --- a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql +++ /dev/null @@ -1,45 +0,0 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -#import "./issue_info.fragment.graphql" - -query getProjectIssues( - $projectPath: ID! - $search: String - $sort: IssueSort - $state: IssuableState - $assigneeId: String - $authorUsername: String - $assigneeUsernames: [String!] - $milestoneTitle: [String] - $labelName: [String] - $not: NegatedIssueFilterInput - $beforeCursor: String - $afterCursor: String - $firstPageSize: Int - $lastPageSize: Int -) { - project(fullPath: $projectPath) { - issues( - search: $search - sort: $sort - state: $state - assigneeId: $assigneeId - authorUsername: $authorUsername - assigneeUsernames: $assigneeUsernames - milestoneTitle: $milestoneTitle - labelName: $labelName - not: $not - before: $beforeCursor - after: $afterCursor - first: $firstPageSize - last: $lastPageSize - ) { - count - pageInfo { - ...PageInfo - } - nodes { - ...IssueInfo - } - } - } -} diff --git a/app/assets/javascripts/issues_list/queries/issue_info.fragment.graphql b/app/assets/javascripts/issues_list/queries/issue_info.fragment.graphql deleted file mode 100644 index c219dc8e35b..00000000000 --- a/app/assets/javascripts/issues_list/queries/issue_info.fragment.graphql +++ /dev/null @@ -1,51 +0,0 @@ -fragment IssueInfo on Issue { - id - iid - closedAt - confidential - createdAt - downvotes - dueDate - humanTimeEstimate - moved - title - updatedAt - upvotes - userDiscussionsCount - webUrl - assignees { - nodes { - id - avatarUrl - name - username - webUrl - } - } - author { - id - avatarUrl - name - username - webUrl - } - labels { - nodes { - id - color - title - description - } - } - milestone { - id - dueDate - startDate - webPath - title - } - taskCompletionStatus { - completedCount - count - } -} diff --git a/app/assets/javascripts/issues_list/utils.js b/app/assets/javascripts/issues_list/utils.js index 23e5b70b353..b5ec44198da 100644 --- a/app/assets/javascripts/issues_list/utils.js +++ b/app/assets/javascripts/issues_list/utils.js @@ -1,5 +1,4 @@ import { - API_PARAM, BLOCKING_ISSUES_DESC, CREATED_ASC, CREATED_DESC, @@ -7,36 +6,29 @@ import { DUE_DATE_DESC, DUE_DATE_VALUES, filters, - LABEL_PRIORITY_ASC, LABEL_PRIORITY_DESC, MILESTONE_DUE_ASC, MILESTONE_DUE_DESC, NORMAL_FILTER, POPULARITY_ASC, POPULARITY_DESC, - PRIORITY_ASC, PRIORITY_DESC, - RELATIVE_POSITION_ASC, + RELATIVE_POSITION_DESC, SPECIAL_FILTER, SPECIAL_FILTER_VALUES, TOKEN_TYPE_ASSIGNEE, - TOKEN_TYPE_ITERATION, UPDATED_ASC, UPDATED_DESC, - URL_PARAM, urlSortParams, WEIGHT_ASC, WEIGHT_DESC, } from '~/issues_list/constants'; import { isPositiveInteger } from '~/lib/utils/number_utils'; import { __ } from '~/locale'; -import { - FILTERED_SEARCH_TERM, - OPERATOR_IS_NOT, -} from '~/vue_shared/components/filtered_search_bar/constants'; +import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants'; export const getSortKey = (sort) => - Object.keys(urlSortParams).find((key) => urlSortParams[key] === sort); + Object.keys(urlSortParams).find((key) => urlSortParams[key].sort === sort); export const getDueDateValue = (value) => (DUE_DATE_VALUES.includes(value) ? value : undefined); @@ -46,7 +38,7 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) id: 1, title: __('Priority'), sortDirection: { - ascending: PRIORITY_ASC, + ascending: PRIORITY_DESC, descending: PRIORITY_DESC, }, }, @@ -94,7 +86,7 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) id: 7, title: __('Label priority'), sortDirection: { - ascending: LABEL_PRIORITY_ASC, + ascending: LABEL_PRIORITY_DESC, descending: LABEL_PRIORITY_DESC, }, }, @@ -102,8 +94,8 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) id: 8, title: __('Manual'), sortDirection: { - ascending: RELATIVE_POSITION_ASC, - descending: RELATIVE_POSITION_ASC, + ascending: RELATIVE_POSITION_DESC, + descending: RELATIVE_POSITION_DESC, }, }, ]; @@ -186,36 +178,12 @@ const getFilterType = (data, tokenType = '') => ? SPECIAL_FILTER : NORMAL_FILTER; -const isIterationSpecialValue = (tokenType, value) => - tokenType === TOKEN_TYPE_ITERATION && SPECIAL_FILTER_VALUES.includes(value); - -export const convertToApiParams = (filterTokens) => { - const params = {}; - const not = {}; - - filterTokens - .filter((token) => token.type !== FILTERED_SEARCH_TERM) - .forEach((token) => { - const filterType = getFilterType(token.value.data, token.type); - const field = filters[token.type][API_PARAM][filterType]; - const obj = token.value.operator === OPERATOR_IS_NOT ? not : params; - const data = isIterationSpecialValue(token.type, token.value.data) - ? token.value.data.toUpperCase() - : token.value.data; - Object.assign(obj, { - [field]: obj[field] ? [obj[field], data].flat() : data, - }); - }); - - return Object.keys(not).length ? Object.assign(params, { not }) : params; -}; - -export const convertToUrlParams = (filterTokens) => +export const convertToParams = (filterTokens, paramType) => filterTokens .filter((token) => token.type !== FILTERED_SEARCH_TERM) .reduce((acc, token) => { const filterType = getFilterType(token.value.data, token.type); - const param = filters[token.type][URL_PARAM][token.value.operator]?.[filterType]; + const param = filters[token.type][paramType][token.value.operator]?.[filterType]; return Object.assign(acc, { [param]: acc[param] ? [acc[param], token.value.data].flat() : token.value.data, }); diff --git a/app/assets/javascripts/releases/components/app_index_apollo_client.vue b/app/assets/javascripts/releases/components/app_index_apollo_client.vue index be4d7d8543c..b915ec9c98f 100644 --- a/app/assets/javascripts/releases/components/app_index_apollo_client.vue +++ b/app/assets/javascripts/releases/components/app_index_apollo_client.vue @@ -2,6 +2,7 @@ import { GlButton } from '@gitlab/ui'; import createFlash from '~/flash'; import { getParameterByName } from '~/lib/utils/common_utils'; +import { scrollUp } from '~/lib/utils/scroll_utils'; import { __ } from '~/locale'; import { PAGE_SIZE } from '~/releases/constants'; import allReleasesQuery from '~/releases/graphql/queries/all_releases.query.graphql'; @@ -9,6 +10,7 @@ import { convertAllReleasesGraphQLResponse } from '~/releases/util'; import ReleaseBlock from './release_block.vue'; import ReleaseSkeletonLoader from './release_skeleton_loader.vue'; import ReleasesEmptyState from './releases_empty_state.vue'; +import ReleasesPaginationApolloClient from './releases_pagination_apollo_client.vue'; export default { name: 'ReleasesIndexApolloClientApp', @@ -17,6 +19,7 @@ export default { ReleaseBlock, ReleaseSkeletonLoader, ReleasesEmptyState, + ReleasesPaginationApolloClient, }, inject: { projectPath: { @@ -85,6 +88,16 @@ export default { return convertAllReleasesGraphQLResponse(this.graphqlResponse).data; }, + pageInfo() { + if (!this.graphqlResponse || this.hasError) { + return { + hasPreviousPage: false, + hasNextPage: false, + }; + } + + return this.graphqlResponse.data.project.releases.pageInfo; + }, shouldRenderEmptyState() { return !this.releases.length && !this.hasError && !this.isLoading; }, @@ -94,6 +107,13 @@ export default { shouldRenderLoadingIndicator() { return this.isLoading && !this.hasError; }, + shouldRenderPagination() { + return ( + !this.isLoading && + !this.hasError && + (this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage) + ); + }, }, created() { this.updateQueryParamsFromUrl(); @@ -108,6 +128,16 @@ export default { this.cursors.before = getParameterByName('before'); this.cursors.after = getParameterByName('after'); }, + onPaginationButtonPress() { + this.updateQueryParamsFromUrl(); + + // In some cases, Apollo Client is able to pull its results from the cache instead of making + // a new network request. In these cases, the page's content gets swapped out immediately without + // changing the page's scroll, leaving the user looking at the bottom of the new page. + // To make the experience consistent, regardless of how the data is sourced, we manually + // scroll to the top of the page every time a pagination button is pressed. + scrollUp(); + }, }, i18n: { newRelease: __('New release'), @@ -140,6 +170,13 @@ export default { :class="{ 'linked-card': releases.length > 1 && index !== releases.length - 1 }" /> + +