diff --git a/app/assets/javascripts/commons/nav/user_merge_requests.js b/app/assets/javascripts/commons/nav/user_merge_requests.js index 1e2cf756059..eab2b5a31d7 100644 --- a/app/assets/javascripts/commons/nav/user_merge_requests.js +++ b/app/assets/javascripts/commons/nav/user_merge_requests.js @@ -11,7 +11,17 @@ function broadcastCount(newCount) { } function updateUserMergeRequestCounts(newCount) { - const mergeRequestsCountEl = document.querySelector('.merge-requests-count'); + const mergeRequestsCountEl = document.querySelector('.js-assigned-mr-count'); + mergeRequestsCountEl.textContent = newCount.toLocaleString(); +} + +function updateReviewerMergeRequestCounts(newCount) { + const mergeRequestsCountEl = document.querySelector('.js-reviewer-mr-count'); + mergeRequestsCountEl.textContent = newCount.toLocaleString(); +} + +function updateMergeRequestCounts(newCount) { + const mergeRequestsCountEl = document.querySelector('.js-merge-requests-count'); mergeRequestsCountEl.textContent = newCount.toLocaleString(); mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0); } @@ -22,10 +32,14 @@ function updateUserMergeRequestCounts(newCount) { export function refreshUserMergeRequestCounts() { return Api.userCounts() .then(({ data }) => { - const count = data.merge_requests; + const assignedMergeRequests = data.assigned_merge_requests; + const reviewerMergeRequests = data.review_requested_merge_requests; + const fullCount = assignedMergeRequests + reviewerMergeRequests; - updateUserMergeRequestCounts(count); - broadcastCount(count); + updateUserMergeRequestCounts(assignedMergeRequests); + updateReviewerMergeRequestCounts(reviewerMergeRequests); + updateMergeRequestCounts(fullCount); + broadcastCount(fullCount); }) .catch((ex) => { console.error(ex); // eslint-disable-line no-console @@ -60,7 +74,7 @@ export function openUserCountsBroadcast() { if (currentUserId) { channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`); channel.onmessage = (ev) => { - updateUserMergeRequestCounts(ev.data); + updateMergeRequestCounts(ev.data); }; } } diff --git a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue index aee94a55134..d9228450371 100644 --- a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue +++ b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue @@ -2,6 +2,7 @@ // NOTE! For the first iteration, we are simply copying the implementation of Assignees // It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736 import { deprecatedCreateFlash as Flash } from '~/flash'; +import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import eventHub from '~/sidebar/event_hub'; import Store from '~/sidebar/stores/sidebar_store'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -80,8 +81,7 @@ export default { .saveReviewers(this.field) .then(() => { this.loading = false; - // Uncomment once this issue has been addressed > https://gitlab.com/gitlab-org/gitlab/-/issues/237922 - // refreshUserMergeRequestCounts(); + refreshUserMergeRequestCounts(); }) .catch(() => { this.loading = false; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index e2335c184b0..c5ffcb48814 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -796,6 +796,14 @@ .navbar-gitlab { li.dropdown { position: static; + + &.user-counter { + margin-left: 8px !important; + + > a { + padding: 0 4px !important; + } + } } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index a6a01c7b090..730e10114c3 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -172,7 +172,7 @@ } li { - .badge.badge-pill { + .badge.badge-pill:not(.merge-request-badge) { box-shadow: none; font-weight: $gl-font-weight-bold; } @@ -438,7 +438,7 @@ .title-container, .navbar-nav { - .badge.badge-pill { + .badge.badge-pill:not(.merge-request-badge) { position: inherit; font-weight: $gl-font-weight-normal; margin-left: -6px; diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 195b3162039..08f357916b5 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -11,6 +11,10 @@ module DashboardHelper merge_requests_dashboard_path(assignee_username: current_user.username) end + def reviewer_mrs_dashboard_path + merge_requests_dashboard_path(reviewer_username: current_user.username) + end + def dashboard_nav_links @dashboard_nav_links ||= get_dashboard_nav_links end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 37e701c1c2b..bbfd06ca06d 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -159,6 +159,32 @@ module MergeRequestsHelper issuable_path(issuable, { merge_request: { wip_event: wip_event } }) end + + def user_merge_requests_counts + @user_merge_requests_counts ||= begin + assigned_count = assigned_issuables_count(:merge_requests) + review_requested_count = review_requested_merge_requests_count + total_count = assigned_count + review_requested_count + + { + assigned: assigned_count, + review_requested: review_requested_count, + total: total_count + } + end + end + + def merge_request_reviewers_enabled? + Feature.enabled?(:merge_request_reviewers, default_enabled: :yaml) + end + + private + + def review_requested_merge_requests_count + return 0 unless merge_request_reviewers_enabled? + + current_user.review_requested_open_merge_requests_count + end end MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper') diff --git a/app/models/user.rb b/app/models/user.rb index c735f20b92c..284182797c0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1564,6 +1564,12 @@ class User < ApplicationRecord end end + def review_requested_open_merge_requests_count(force: false) + Rails.cache.fetch(['users', id, 'review_requested_open_merge_requests_count'], force: force, expires_in: 20.minutes) do + MergeRequestsFinder.new(self, reviewer_id: id, state: 'opened', non_archived: true).execute.count + end + end + def assigned_open_issues_count(force: false) Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count @@ -1607,6 +1613,7 @@ class User < ApplicationRecord def invalidate_merge_request_cache_counts Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count']) + Rails.cache.delete(['users', id, 'review_requested_open_merge_requests_count']) end def invalidate_todos_done_count diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 403fb34803e..3b8c59c6bf8 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -135,10 +135,6 @@ class ProjectPolicy < BasePolicy ::Feature.enabled?(:build_service_proxy, @subject) end - condition(:project_bot_is_member) do - user.project_bot? & team_member? - end - with_scope :subject condition(:packages_disabled) { !@subject.packages_enabled } @@ -619,8 +615,6 @@ class ProjectPolicy < BasePolicy enable :admin_resource_access_tokens end - rule { project_bot_is_member & ~blocked }.enable :bot_log_in - private def user_is_user? diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 1d0ab403f12..d2e5a2a1619 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -112,9 +112,11 @@ module MergeRequests end def handle_reviewers_change(merge_request, old_reviewers) + affected_reviewers = (old_reviewers + merge_request.reviewers) - (old_reviewers & merge_request.reviewers) create_reviewer_note(merge_request, old_reviewers) notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers) todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers) + invalidate_cache_counts(merge_request, users: affected_reviewers.compact) end def create_branch_change_note(issuable, branch_type, old_branch, new_branch) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 70ab0a56581..1394f15cd15 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -47,17 +47,36 @@ %span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) } = number_with_delimiter(issues_count) - if header_link?(:merge_requests) - = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do - = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _('Merge requests') }, - data: { qa_selector: 'merge_requests_shortcut_button', toggle: 'tooltip', placement: 'bottom', + - reviewers_enabled = merge_request_reviewers_enabled? + = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter #{reviewers_enabled ? 'dropdown' : ''}" }) do + = link_to assigned_mrs_dashboard_path, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') }, + data: { qa_selector: 'merge_requests_shortcut_button', + toggle: reviewers_enabled ? "dropdown" : "tooltip", + placement: 'bottom', track_label: 'main_navigation', track_event: 'click_merge_link', track_property: 'navigation', container: 'body' } do = sprite_icon('git-merge') - - merge_requests_count = assigned_issuables_count(:merge_requests) - %span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count == 0) } - = number_with_delimiter(merge_requests_count) + %span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) } + = number_with_delimiter(user_merge_requests_counts[:total]) + - if reviewers_enabled + = sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!') + - if reviewers_enabled + .dropdown-menu.dropdown-menu-right + %ul + %li.dropdown-header + = _('Merge requests') + %li + = link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do + = _('Assigned to you') + %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" } + = user_merge_requests_counts[:assigned] + %li + = link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do + = _('Review requests for you') + %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" } + = user_merge_requests_counts[:review_requested] - if header_link?(:todos) = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do = link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos', diff --git a/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml b/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml new file mode 100644 index 00000000000..c280ccb21ec --- /dev/null +++ b/changelogs/unreleased/296563-follow-up-from-fix-project-access-token-regression.yml @@ -0,0 +1,5 @@ +--- +title: Fix project access token regression +merge_request: 50800 +author: +type: fixed diff --git a/changelogs/unreleased/eliminate-tz-sensitivity-ci-analytics-charts.yml b/changelogs/unreleased/eliminate-tz-sensitivity-ci-analytics-charts.yml deleted file mode 100644 index 23b00c80186..00000000000 --- a/changelogs/unreleased/eliminate-tz-sensitivity-ci-analytics-charts.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix empty pipeline analytics charts when time_zone is non-UTC -merge_request: 50760 -author: -type: fixed diff --git a/doc/development/internal_api.md b/doc/development/internal_api.md index 9a1ec0285c0..43655c37048 100644 --- a/doc/development/internal_api.md +++ b/doc/development/internal_api.md @@ -459,11 +459,11 @@ Cluster. | `alert` | Hash | yes | Alerts detail. Currently same format as [3rd party alert](../operations/incident_management/alert_integrations.md#customize-the-alert-payload-outside-of-gitlab). | ```plaintext -POST internal/kubernetes/modules/cilium/network_alert +POST internal/kubernetes/modules/cilium_alert ``` Example Request: ```shell -curl --request POST --header "Gitlab-Kas-Api-Request: " --header "Authorization: Bearer " --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium/network_alert" +curl --request POST --header "Gitlab-Kas-Api-Request: " --header "Authorization: Bearer " --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium_alert" ``` diff --git a/doc/user/admin_area/img/license_admin_area.png b/doc/user/admin_area/img/license_admin_area.png deleted file mode 100644 index b5662b81c5e..00000000000 Binary files a/doc/user/admin_area/img/license_admin_area.png and /dev/null differ diff --git a/doc/user/admin_area/img/license_upload.png b/doc/user/admin_area/img/license_upload.png deleted file mode 100644 index 29d55175a2d..00000000000 Binary files a/doc/user/admin_area/img/license_upload.png and /dev/null differ diff --git a/doc/user/admin_area/img/license_upload_v13_8.png b/doc/user/admin_area/img/license_upload_v13_8.png new file mode 100644 index 00000000000..c15bc2bfa02 Binary files /dev/null and b/doc/user/admin_area/img/license_upload_v13_8.png differ diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md index 9838dc61c2d..dab98982e26 100644 --- a/doc/user/admin_area/license.md +++ b/doc/user/admin_area/license.md @@ -42,18 +42,21 @@ Otherwise, you can: 1. Navigate to the **License** tab, and click **Upload New License**. - ![License Admin Area](img/license_admin_area.png) + - *If you've received a `.gitlab-license` file:* + 1. Download the license file to your local machine. + 1. Select **Upload `.gitlab-license` file**. + 1. Select **Choose File** and select the license file. + In this example the license file is named `GitLab.gitlab-license`. + 1. Check the **Subscription Agreement** checkbox. + 1. Select **Upload License**. - - *If you've received a `.gitlab-license` file,* you should have already downloaded - it in your local machine. You can then upload it directly by choosing the - license file and clicking the **Upload license** button. In the image below, - the selected license file is named `GitLab.gitlab-license`. + ![Upload license](img/license_upload_v13_8.png) - ![Upload license](img/license_upload.png) - - - *If you've received your license as plain text,* select the - **Enter license key** option, copy the license, paste it into the **License key** - field, and click **Upload license**. + - *If you've received your license as plain text:* + 1. Select **Enter license key**. + 1. Copy the license and paste it into the **License key** field. + 1. Check the **Subscription Agreement** checkbox. + 1. Select **Upload License**. ## Add your license at install time diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md index 213c8c4116a..781241ffd20 100644 --- a/doc/user/admin_area/settings/sign_up_restrictions.md +++ b/doc/user/admin_area/settings/sign_up_restrictions.md @@ -47,6 +47,9 @@ To enforce confirmation of the email address used for new sign ups: 1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**. 1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**. +In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/273258), if an administrator disables this setting, the users in pending approval state are +automatically approved in a background job. + ## User cap **(CORE ONLY)** > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6. diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md index c411fd556df..38f829c94e8 100644 --- a/doc/user/packages/npm_registry/index.md +++ b/doc/user/packages/npm_registry/index.md @@ -102,7 +102,7 @@ To authenticate to the Package Registry, you must use one of the following: - It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow). Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers. - A [CI job token](#authenticate-with-a-ci-job-token). -- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case. +- Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case. ### Authenticate with a personal access token or deploy token @@ -201,7 +201,7 @@ Then, you can run `npm publish` either locally or by using GitLab CI/CD. ## Package naming convention -Your NPM package name must be in the format of `@scope:package-name`. +Your NPM package name must be in the format of `@scope/package-name`. - The `@scope` is the root namespace of the GitLab project. It must match exactly, including the case. - The `package-name` can be whatever you want. @@ -241,7 +241,7 @@ Prerequisites: - [Authenticate](#authenticate-to-the-package-registry) to the Package Registry. - Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages). -- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case. +- Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case. To upload an NPM package to your project, run this command: @@ -461,7 +461,7 @@ If you get this error, ensure that: ### `npm publish` returns `npm ERR! 400 Bad Request` If you get this error, your package name may not meet the -[@scope:package-name package naming convention](#package-naming-convention). +[@scope/package-name package naming convention](#package-naming-convention). Ensure the name meets the convention exactly, including the case. Then try to publish again. diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md index 954ea91ba28..187d964c422 100644 --- a/doc/user/packages/pypi_repository/index.md +++ b/doc/user/packages/pypi_repository/index.md @@ -235,9 +235,12 @@ password = ${env.CI_JOB_TOKEN} When publishing packages, note that: -- The maximum allowed size is 50 MB. +- You must [authenticate with the Package Registry](#authenticate-with-the-package-registry). +- Your [version string must be valid](#ensure-your-version-string-is-valid). +- The maximum allowed package size is 5 GB. - You can't upload the same version of a package multiple times. If you try, you receive the error `Validation failed: File name has already been taken`. +- You cannot publish PyPI packages to a group, only to a project. ### Ensure your version string is valid diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb index 3071f08e1de..31c923a219a 100644 --- a/lib/api/user_counts.rb +++ b/lib/api/user_counts.rb @@ -12,7 +12,9 @@ module API unauthorized! unless current_user { - merge_requests: current_user.assigned_open_merge_requests_count + merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated + assigned_merge_requests: current_user.assigned_open_merge_requests_count, + review_requested_merge_requests: current_user.review_requested_open_merge_requests_count } end end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 1aabb05f19e..1f5cce249d8 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -198,7 +198,9 @@ module Gitlab return unless valid_scoped_token?(token, all_available_scopes) - if token.user.can?(:log_in) || token.user.can?(:bot_log_in, project) + return if project && token.user.project_bot? && !project.bots.include?(token.user) + + if token.user.can?(:log_in) || token.user.project_bot? Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes)) end end @@ -283,7 +285,7 @@ module Gitlab return unless build.project.builds_enabled? if build.user - return unless build.user.can?(:log_in) || build.user.can?(:bot_log_in, build.project) + return unless build.user.can?(:log_in) || (build.user.project_bot? && build.project.bots&.include?(build.user)) # If user is assigned to build, use restricted credentials of user Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities) diff --git a/lib/gitlab/ci/charts.rb b/lib/gitlab/ci/charts.rb index 797193a6be5..25fb9c0ca97 100644 --- a/lib/gitlab/ci/charts.rb +++ b/lib/gitlab/ci/charts.rb @@ -31,10 +31,9 @@ module Gitlab current = @from while current <= @to - label = current.strftime(@format) - @labels << label - @total << (totals_count[label] || 0) - @success << (success_count[label] || 0) + @labels << current.strftime(@format) + @total << (totals_count[current] || 0) + @success << (success_count[current] || 0) current += interval_step end @@ -46,7 +45,6 @@ module Gitlab query .group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)") .count(:created_at) - .transform_keys { |date| date.strftime(@format) } end # rubocop: enable CodeReuse/ActiveRecord diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b27d977225a..9e8b50f39d7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3943,6 +3943,9 @@ msgstr "" msgid "Assigned to me" msgstr "" +msgid "Assigned to you" +msgstr "" + msgid "Assignee" msgid_plural "%d Assignees" msgstr[0] "" @@ -24032,6 +24035,9 @@ msgstr "" msgid "Review requested from %{name}" msgstr "" +msgid "Review requests for you" +msgstr "" + msgid "Review the changes locally" msgstr "" diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index 0e76b5478a1..26b376be660 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -110,6 +110,12 @@ RSpec.describe 'Dashboard Merge Requests' do visit merge_requests_dashboard_path(assignee_username: current_user.username) end + it 'includes assigned and reviewers in badge' do + expect(find('.merge-requests-count')).to have_content('3') + expect(find('.js-assigned-mr-count')).to have_content('2') + expect(find('.js-reviewer-mr-count')).to have_content('1') + end + it 'shows assigned merge requests' do expect(page).to have_content(assigned_merge_request.title) expect(page).to have_content(assigned_merge_request_from_fork.title) diff --git a/spec/frontend/commons/nav/user_merge_requests_spec.js b/spec/frontend/commons/nav/user_merge_requests_spec.js index 4da6d53557a..5f81067f84f 100644 --- a/spec/frontend/commons/nav/user_merge_requests_spec.js +++ b/spec/frontend/commons/nav/user_merge_requests_spec.js @@ -8,7 +8,7 @@ import Api from '~/api'; jest.mock('~/api'); const TEST_COUNT = 1000; -const MR_COUNT_CLASS = 'merge-requests-count'; +const MR_COUNT_CLASS = 'js-merge-requests-count'; describe('User Merge Requests', () => { let channelMock; @@ -24,7 +24,9 @@ describe('User Merge Requests', () => { newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock); global.BroadcastChannel = newBroadcastChannelMock; - setFixtures(`
0
`); + setFixtures( + `
0
`, + ); }); const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent; @@ -33,7 +35,10 @@ describe('User Merge Requests', () => { beforeEach(() => { Api.userCounts.mockReturnValue( Promise.resolve({ - data: { merge_requests: TEST_COUNT }, + data: { + assigned_merge_requests: TEST_COUNT, + review_requested_merge_requests: TEST_COUNT, + }, }), ); }); @@ -46,7 +51,7 @@ describe('User Merge Requests', () => { }); it('updates the top count of merge requests', () => { - expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString()); + expect(findMRCountText()).toEqual(Number(TEST_COUNT + TEST_COUNT).toLocaleString()); }); it('calls the API', () => { @@ -54,7 +59,7 @@ describe('User Merge Requests', () => { }); it('posts count to BroadcastChannel', () => { - expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT); + expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT + TEST_COUNT); }); }); diff --git a/spec/helpers/dashboard_helper_spec.rb b/spec/helpers/dashboard_helper_spec.rb index 65182dcb729..8a76771be0a 100644 --- a/spec/helpers/dashboard_helper_spec.rb +++ b/spec/helpers/dashboard_helper_spec.rb @@ -89,4 +89,10 @@ RSpec.describe DashboardHelper do it { is_expected.to eq(false) } end + + describe '#reviewer_mrs_dashboard_path' do + subject { helper.reviewer_mrs_dashboard_path } + + it { is_expected.to eq(merge_requests_dashboard_path(reviewer_username: user.username)) } + end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 377e2c43a72..821faaab194 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -67,4 +67,37 @@ RSpec.describe MergeRequestsHelper do end end end + + describe '#user_merge_requests_counts' do + let(:user) do + double( + assigned_open_merge_requests_count: 1, + review_requested_open_merge_requests_count: 2 + ) + end + + subject { helper.user_merge_requests_counts } + + before do + allow(helper).to receive(:current_user).and_return(user) + end + + it "returns assigned, review requested and total merge request counts" do + expect(subject).to eq( + assigned: user.assigned_open_merge_requests_count, + review_requested: user.review_requested_open_merge_requests_count, + total: user.assigned_open_merge_requests_count + user.review_requested_open_merge_requests_count + ) + end + + context 'when merge_request_reviewers is disabled' do + before do + stub_feature_flags(merge_request_reviewers: false) + end + + it 'returns review_requested as 0' do + expect(subject[:review_requested]).to eq(0) + end + end + end end diff --git a/spec/lib/gitlab/ci/charts_spec.rb b/spec/lib/gitlab/ci/charts_spec.rb index c7869383dc4..cfc2019a89b 100644 --- a/spec/lib/gitlab/ci/charts_spec.rb +++ b/spec/lib/gitlab/ci/charts_spec.rb @@ -47,10 +47,6 @@ RSpec.describe Gitlab::Ci::Charts do subject { chart.to } - before do - create(:ci_empty_pipeline, project: project, duration: 120) - end - it 'includes the whole current day' do is_expected.to eq(Date.today.end_of_day) end @@ -62,37 +58,6 @@ RSpec.describe Gitlab::Ci::Charts do it 'uses %d %B as labels format' do expect(chart.labels).to include(chart.from.strftime('%d %B')) end - - it 'returns count of pipelines run each day in the current week' do - expect(chart.total).to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1) - end - end - - context 'weekchart_non_utc' do - today = Date.today - end_of_today = Time.use_zone(Time.find_zone('Asia/Dubai')) { today.end_of_day } - - let(:project) { create(:project) } - let(:chart) do - allow(Date).to receive(:today).and_return(today) - allow(today).to receive(:end_of_day).and_return(end_of_today) - Gitlab::Ci::Charts::WeekChart.new(project) - end - - subject { chart.total } - - before do - create(:ci_empty_pipeline, project: project, duration: 120) - end - - it 'uses a non-utc time zone for range times' do - expect(chart.to.zone).to eq(end_of_today.zone) - expect(chart.from.zone).to eq(end_of_today.zone) - end - - it 'returns count of pipelines run each day in the current week' do - is_expected.to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1) - end end context 'pipeline_times' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index fb05c9e8052..3c5bc125011 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4084,6 +4084,7 @@ RSpec.describe User do cache_mock = double expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count']) + expect(cache_mock).to receive(:delete).with(['users', user.id, 'review_requested_open_merge_requests_count']) allow(Rails).to receive(:cache).and_return(cache_mock) @@ -4163,6 +4164,20 @@ RSpec.describe User do end end + describe '#review_requested_open_merge_requests_count' do + it 'returns number of open merge requests from non-archived projects' do + user = create(:user) + project = create(:project, :public) + archived_project = create(:project, :public, :archived) + + create(:merge_request, source_project: project, author: user, reviewers: [user]) + create(:merge_request, :closed, source_project: project, author: user, reviewers: [user]) + create(:merge_request, source_project: archived_project, author: user, reviewers: [user]) + + expect(user.review_requested_open_merge_requests_count(force: true)).to eq 1 + end + end + describe '#assigned_open_issues_count' do it 'returns number of open issues from non-archived projects' do user = create(:user) diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 7f6c47d675b..c21d3b0939f 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -401,40 +401,6 @@ RSpec.describe ProjectPolicy do end end - describe 'bot_log_in' do - let(:bot_user) { create(:user, :project_bot) } - let(:project) { private_project } - - context 'when bot is in project and is not blocked' do - before do - project.add_maintainer(bot_user) - end - - it 'is a valid project bot' do - expect(bot_user.can?(:bot_log_in, project)).to be_truthy - end - end - - context 'when project bot is invalid' do - context 'when bot is not in project' do - it 'is not a valid project bot' do - expect(bot_user.can?(:bot_log_in, project)).to be_falsy - end - end - - context 'when bot user is blocked' do - before do - project.add_maintainer(bot_user) - bot_user.block! - end - - it 'is not a valid project bot' do - expect(bot_user.can?(:bot_log_in, project)).to be_falsy - end - end - end - end - context 'support bot' do let(:current_user) { User.support_bot } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index be802c430ff..fdda1515cce 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -521,6 +521,19 @@ RSpec.describe MergeRequests::UpdateService, :mailer do should_email(user2) should_email(user3) end + + it 'updates open merge request counter for reviewers', :use_clean_rails_memory_store_caching do + merge_request.reviewers = [user3] + + # Cache them to ensure the cache gets invalidated on update + expect(user2.review_requested_open_merge_requests_count).to eq(0) + expect(user3.review_requested_open_merge_requests_count).to eq(1) + + update_merge_request(reviewer_ids: [user2.id]) + + expect(user2.review_requested_open_merge_requests_count).to eq(1) + expect(user3.review_requested_open_merge_requests_count).to eq(0) + end end context 'when the milestone is removed' do