From 0b54f87a31c23544ca5917bf772ce9c64a61562c Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 11 May 2023 12:12:30 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- app/assets/javascripts/notes/constants.js | 45 ++++---- .../container_expiration_policy_form.vue | 6 - .../settings/project/constants.js | 2 +- .../queries/approvals.subscription.graphql | 17 +++ .../vue_merge_request_widget/mixins/approvals.js | 39 ++++++- .../work_items/components/work_item_detail.vue | 6 +- .../components/work_item_detail_modal.vue | 66 ----------- .../work_item_links/work_item_link_child.vue | 10 +- .../components/work_item_links/work_item_links.vue | 27 ++--- .../work_item_links/work_item_tree_children.vue | 2 +- .../delete_task_from_work_item.mutation.graphql | 9 -- app/controllers/projects/grafana_api_controller.rb | 2 + app/controllers/projects/jobs_controller.rb | 4 +- .../projects/merge_requests_controller.rb | 1 + .../metrics/dashboards/builder_controller.rb | 2 + .../dashboards_controller.rb | 4 + app/finders/pending_todos_finder.rb | 28 ++++- app/helpers/ci/jobs_helper.rb | 18 --- app/models/application_setting.rb | 7 +- app/models/group.rb | 2 - app/models/issue.rb | 4 +- app/models/key.rb | 2 +- app/models/packages/package.rb | 7 +- app/models/project_feature.rb | 2 +- app/models/snippet.rb | 2 +- app/models/todo.rb | 2 + app/models/user.rb | 121 +++++++++++---------- app/models/user_preference.rb | 4 +- app/models/users/group_callout.rb | 6 +- app/models/users/phone_number_validation.rb | 33 +++--- app/models/users/project_callout.rb | 6 +- app/models/users/user_follow_user.rb | 10 +- app/models/web_ide_terminal.rb | 14 ++- app/models/webauthn_registration.rb | 2 +- app/models/wiki_page.rb | 10 +- app/models/work_item.rb | 6 +- app/services/keys/last_used_service.rb | 24 ++-- .../members/approve_access_request_service.rb | 2 +- app/services/members/base_service.rb | 4 +- app/services/members/destroy_service.rb | 2 +- app/services/projects/open_issues_count_service.rb | 2 +- app/services/todo_service.rb | 32 ++++-- app/views/projects/snippets/index.html.haml | 2 +- app/workers/all_queues.yml | 9 ++ app/workers/ssh_keys/update_last_used_at_worker.rb | 21 ++++ 45 files changed, 328 insertions(+), 298 deletions(-) create mode 100644 app/assets/javascripts/vue_merge_request_widget/components/approvals/queries/approvals.subscription.graphql delete mode 100644 app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql create mode 100644 app/workers/ssh_keys/update_last_used_at_worker.rb (limited to 'app') diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js index e7c3385ae5c..419b427682e 100644 --- a/app/assets/javascripts/notes/constants.js +++ b/app/assets/javascripts/notes/constants.js @@ -61,17 +61,7 @@ export const MR_FILTER_OPTIONS = [ { text: __('Approvals'), value: 'approval', - systemNoteIcons: ['approval', 'unapproval'], - }, - { - text: __('Commits & branches'), - value: 'commit_branches', - systemNoteIcons: ['commit', 'fork'], - }, - { - text: __('Merge request status'), - value: 'status', - systemNoteIcons: ['git-merge', 'issue-close', 'issues'], + systemNoteIcons: ['approval', 'unapproval', 'check'], }, { text: __('Assignees & reviewers'), @@ -83,6 +73,18 @@ export const MR_FILTER_OPTIONS = [ s__('IssuableEvents|unassigned'), ], }, + { + text: __('Comments'), + value: 'comments', + noteType: ['DiscussionNote', 'DiffNote'], + individualNote: true, + noteText: [s__('IssuableEvents|resolved all threads')], + }, + { + text: __('Commits & branches'), + value: 'commit_branches', + systemNoteIcons: ['commit', 'fork'], + }, { text: __('Edits'), value: 'edits', @@ -93,26 +95,25 @@ export const MR_FILTER_OPTIONS = [ value: 'labels', systemNoteIcons: ['label'], }, + { + text: __('Lock status'), + value: 'lock_status', + systemNoteIcons: ['lock', 'lock-open'], + }, { text: __('Mentions'), value: 'mentions', systemNoteIcons: ['comment-dots'], }, + { + text: __('Merge request status'), + value: 'status', + systemNoteIcons: ['git-merge', 'issue-close', 'issues', 'merge-request-close'], + }, { text: __('Tracking'), value: 'tracking', noteType: ['MilestoneNote'], systemNoteIcons: ['timer'], }, - { - text: __('Comments'), - value: 'comments', - noteType: ['DiscussionNote', 'DiffNote'], - individualNote: true, - }, - { - text: __('Lock status'), - value: 'lock_status', - systemNoteIcons: ['lock', 'lock-open'], - }, ]; diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue index dd22d29d9a7..c13d49b5379 100644 --- a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue +++ b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue @@ -225,9 +225,6 @@ export default { -

{{ content }} -

{ - if (errors?.length) { - throw new Error(errors[0]); - } - - this.$emit('workItemDeleted', descriptionHtml); - this.hide(); - }, - ) - .catch((error) => { - this.setErrorMessage(error.message); - }); - }, - deleteWorkItemWithoutTaskData() { this.$apollo .mutate({ mutation: deleteWorkItemMutation, @@ -191,7 +126,6 @@ export default { diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue index 46c109f2d57..5728e33880e 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue @@ -185,29 +185,26 @@ export default { variables: { id: this.issuableGid, workItem }, }); }, - async updateWorkItem(workItem, childId, parentId) { - const response = await this.$apollo.mutate({ + async undoChildRemoval(workItem, childId) { + const { data } = await this.$apollo.mutate({ mutation: updateWorkItemMutation, - variables: { input: { id: childId, hierarchyWidget: { parentId } } }, + variables: { input: { id: childId, hierarchyWidget: { parentId: this.issuableGid } } }, }); - if (parentId === null) { - await this.removeHierarchyChild(workItem); - } else { - await this.addHierarchyChild(workItem); - } - - return response; - }, - async undoChildRemoval(workItem, childId) { - const { data } = await this.updateWorkItem(workItem, childId, this.issuableGid); + await this.addHierarchyChild(workItem); if (data.workItemUpdate.errors.length === 0) { this.activeToast?.hide(); } }, - async removeChild(childId) { - const { data } = await this.updateWorkItem({ id: childId }, childId, null); + async removeChild(workItem) { + const childId = workItem.id; + const { data } = await this.$apollo.mutate({ + mutation: updateWorkItemMutation, + variables: { input: { id: childId, hierarchyWidget: { parentId: null } } }, + }); + + await this.removeHierarchyChild(workItem); if (data.workItemUpdate.errors.length === 0) { this.activeToast = this.$toast.show(s__('WorkItem|Child removed'), { diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue index 121c987da71..2cabf489bc6 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree_children.vue @@ -36,7 +36,7 @@ export default { :issuable-gid="workItemId" :child-item="child" :work-item-type="workItemType" - @removeChild="$emit('removeChild', child.id)" + @removeChild="$emit('removeChild', $event)" @click="$emit('click', Object.assign($event, { childItem: child }))" /> diff --git a/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql deleted file mode 100644 index 32c07ed48c7..00000000000 --- a/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql +++ /dev/null @@ -1,9 +0,0 @@ -mutation workItemDeleteTask($input: WorkItemDeleteTaskInput!) { - workItemDeleteTask(input: $input) { - workItem { - id - descriptionHtml - } - errors - } -} diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb index 9cd511f6a11..2cc6c6c35ba 100644 --- a/app/controllers/projects/grafana_api_controller.rb +++ b/app/controllers/projects/grafana_api_controller.rb @@ -10,6 +10,8 @@ class Projects::GrafanaApiController < Projects::ApplicationController urgency :low def proxy + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Grafana::ProxyService.new( project, params[:datasource_id], diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 36fa1fab68f..79ddcbf732d 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -8,8 +8,8 @@ class Projects::JobsController < Projects::ApplicationController urgency :low, [:index, :show, :trace, :retry, :play, :cancel, :unschedule, :erase, :raw] - before_action :find_job_as_build, except: [:index, :play, :show, :retry] - before_action :find_job_as_processable, only: [:play, :show, :retry] + before_action :find_job_as_build, except: [:index, :play, :retry] + before_action :find_job_as_processable, only: [:play, :retry] before_action :authorize_read_build_trace!, only: [:trace, :raw] before_action :authorize_read_build! before_action :authorize_update_build!, diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index dbcbd2467b6..f72f0ccb593 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -47,6 +47,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:single_file_file_by_file, project) push_frontend_feature_flag(:mr_experience_survey, project) push_frontend_feature_flag(:realtime_mr_status_change, project) + push_frontend_feature_flag(:realtime_approvals, project) push_frontend_feature_flag(:saved_replies, current_user) push_frontend_feature_flag(:code_quality_inline_drawer, project) push_frontend_feature_flag(:hide_create_issue_resolve_all, project) diff --git a/app/controllers/projects/metrics/dashboards/builder_controller.rb b/app/controllers/projects/metrics/dashboards/builder_controller.rb index a6b57798923..02e3afcdc80 100644 --- a/app/controllers/projects/metrics/dashboards/builder_controller.rb +++ b/app/controllers/projects/metrics/dashboards/builder_controller.rb @@ -10,6 +10,8 @@ module Projects urgency :low def panel_preview + return not_found if Feature.enabled?(:remove_monitor_metrics) + respond_to do |format| format.json do if rendered_panel.success? diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb index d043f8d0b9f..1255ec1dde2 100644 --- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb +++ b/app/controllers/projects/performance_monitoring/dashboards_controller.rb @@ -16,6 +16,8 @@ module Projects urgency :low def create + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Metrics::Dashboard::CloneDashboardService.new(project, current_user, dashboard_params).execute if result[:status] == :success @@ -26,6 +28,8 @@ module Projects end def update + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Metrics::Dashboard::UpdateDashboardService.new(project, current_user, dashboard_params.merge(file_content_params)).execute if result[:status] == :success diff --git a/app/finders/pending_todos_finder.rb b/app/finders/pending_todos_finder.rb index babff65cc37..a1a72840236 100644 --- a/app/finders/pending_todos_finder.rb +++ b/app/finders/pending_todos_finder.rb @@ -13,24 +13,36 @@ class PendingTodosFinder attr_reader :users, :params - # users - The list of users to retrieve the todos for. + # users - The list of users to retrieve the todos for. If nil is passed, it won't filter todos based on users # params - A Hash containing columns and values to use for filtering todos. - def initialize(users, params = {}) - @users = users + def initialize(params = {}) @params = params + + # To prevent N+1 queries when fetching the users of the PendingTodos. + @preload_user_association = params.fetch(:preload_user_association, false) end def execute - todos = Todo.for_user(users) - todos = todos.pending + todos = Todo.pending + todos = by_users(todos) todos = by_project(todos) todos = by_target_id(todos) todos = by_target_type(todos) + todos = by_author_id(todos) todos = by_discussion(todos) todos = by_commit_id(todos) + + todos = todos.with_preloaded_user if @preload_user_association + by_action(todos) end + def by_users(todos) + return todos unless params[:users].present? + + todos.for_user(params[:users]) + end + def by_project(todos) if (id = params[:project_id]) todos.for_project(id) @@ -55,6 +67,12 @@ class PendingTodosFinder end end + def by_author_id(todos) + return todos unless params[:author_id] + + todos.for_author(params[:author_id]) + end + def by_commit_id(todos) if (id = params[:commit_id]) todos.for_commit(id) diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb index 424e5920fed..a7e1de173bd 100644 --- a/app/helpers/ci/jobs_helper.rb +++ b/app/helpers/ci/jobs_helper.rb @@ -18,24 +18,6 @@ module Ci } end - def bridge_data(build, project) - { - "build_id" => build.id, - "empty-state-illustration-path" => image_path('illustrations/job-trigger-md.svg'), - "pipeline_iid" => build.pipeline.iid, - "project_full_path" => project.full_path - } - end - - def job_counts - { - "all" => limited_counter_with_delimiter(@all_builds), - "pending" => limited_counter_with_delimiter(@all_builds.pending), - "running" => limited_counter_with_delimiter(@all_builds.running), - "finished" => limited_counter_with_delimiter(@all_builds.finished) - } - end - def job_statuses statuses = Ci::HasStatus::AVAILABLE_STATUSES diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index fca6c76434a..d2ca88aae0e 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -14,6 +14,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord ignore_column :send_user_confirmation_email, remove_with: '15.8', remove_after: '2022-12-18' ignore_column :web_ide_clientside_preview_enabled, remove_with: '15.11', remove_after: '2023-04-22' ignore_column :clickhouse_connection_string, remove_with: '16.1', remove_after: '2023-05-22' + ignore_columns %i[instance_administration_project_id instance_administrators_group_id], remove_with: '16.2', remove_after: '2023-06-22' INSTANCE_REVIEW_MIN_USERS = 50 GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ @@ -33,17 +34,13 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord enum whats_new_variant: { all_tiers: 0, current_tier: 1, disabled: 2 }, _prefix: true enum email_confirmation_setting: { off: 0, soft: 1, hard: 2 }, _prefix: true - add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required } + add_authentication_token_field :runners_registration_token, encrypted: :required add_authentication_token_field :health_check_access_token add_authentication_token_field :static_objects_external_storage_auth_token, encrypted: :required add_authentication_token_field :error_tracking_access_token, encrypted: :required belongs_to :push_rule - belongs_to :instance_group, class_name: "Group", foreign_key: :instance_administrators_group_id, - inverse_of: :application_setting - alias_attribute :instance_group_id, :instance_administrators_group_id - alias_attribute :instance_administrators_group, :instance_group alias_attribute :housekeeping_optimize_repository_period, :housekeeping_incremental_repack_period sanitizes! :default_branch_name diff --git a/app/models/group.rb b/app/models/group.rb index f13ce2ddca1..ab8e0101684 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -108,8 +108,6 @@ class Group < Namespace has_one :import_state, class_name: 'GroupImportState', inverse_of: :group - has_many :application_setting, foreign_key: :instance_administrators_group_id, inverse_of: :instance_group - has_many :bulk_import_exports, class_name: 'BulkImports::Export', inverse_of: :group has_many :bulk_import_entities, class_name: 'BulkImports::Entity', foreign_key: :namespace_id, inverse_of: :group diff --git a/app/models/issue.rb b/app/models/issue.rb index b0535a28640..0d33c6a71aa 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -684,13 +684,13 @@ class Issue < ApplicationRecord elsif project.personal? && project.team.owner?(user) true elsif confidential? && !assignee_or_author?(user) - project.team.member?(user, Gitlab::Access::REPORTER) + project.member?(user, Gitlab::Access::REPORTER) elsif hidden? false elsif project.public? || (project.internal? && !user.external?) project.feature_available?(:issues, user) else - project.team.member?(user) + project.member?(user) end end diff --git a/app/models/key.rb b/app/models/key.rb index 596186276bb..2ea71bfcd6d 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -92,7 +92,7 @@ class Key < ApplicationRecord # rubocop: disable CodeReuse/ServiceClass def update_last_used_at - Keys::LastUsedService.new(self).execute + Keys::LastUsedService.new(self).execute_async end # rubocop: enable CodeReuse/ServiceClass diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index a3946724fd3..c58ad92d7a6 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -297,9 +297,14 @@ class Packages::Package < ApplicationRecord end # Technical debt: to be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/281937 + # TODO: rename the method https://gitlab.com/gitlab-org/gitlab/-/issues/410352 def original_build_info strong_memoize(:original_build_info) do - build_infos.first + if Feature.enabled?(:packages_display_last_pipeline, project) + build_infos.last + else + build_infos.first + end end end diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 52e623db7b0..772a82fa173 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -206,7 +206,7 @@ class ProjectFeature < ApplicationRecord override :resource_member? def resource_member?(user, feature) - project.team.member?(user, ProjectFeature.required_minimum_access_level(feature)) + project.member?(user, ProjectFeature.required_minimum_access_level(feature)) end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 181130d45bd..3c40f4beedc 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -157,7 +157,7 @@ class Snippet < ApplicationRecord def for_project_with_user(project, user = nil) return none unless project.snippets_visible?(user) - if project.team.member?(user) + if project.member?(user) project.snippets else project.snippets.public_to_user(user) diff --git a/app/models/todo.rb b/app/models/todo.rb index ac41b5d0b2c..e1b5076e3d8 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -13,6 +13,7 @@ class Todo < ApplicationRecord # and giving it back again. WAIT_FOR_DELETE = 1.hour + # Actions ASSIGNED = 1 MENTIONED = 2 BUILD_FAILED = 3 @@ -80,6 +81,7 @@ class Todo < ApplicationRecord end scope :joins_issue_and_assignees, -> { left_joins(issue: :assignees) } scope :for_internal_notes, -> { joins(:note).where(note: { confidential: true }) } + scope :with_preloaded_user, -> { preload(:user) } enum resolved_by_action: { system_done: 0, api_all_done: 1, api_done: 2, mark_all_done: 3, mark_done: 4 }, _prefix: :resolved_by diff --git a/app/models/user.rb b/app/models/user.rb index 71c5afb1c79..daee2687d2f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -80,14 +80,14 @@ class User < ApplicationRecord algorithm: 'aes-256-cbc' devise :two_factor_authenticatable, - otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base + otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base devise :two_factor_backupable, otp_number_of_backup_codes: 10 devise :two_factor_backupable_pbkdf2 serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, - :validatable, :omniauthable, :confirmable, :registerable + :validatable, :omniauthable, :confirmable, :registerable # Must be included after `devise` include EncryptedUserPassword @@ -132,11 +132,11 @@ class User < ApplicationRecord # Namespace for personal projects has_one :namespace, - -> { where(type: Namespaces::UserNamespace.sti_name) }, - dependent: :destroy, # rubocop:disable Cop/ActiveRecordDependent - foreign_key: :owner_id, - inverse_of: :owner, - autosave: true # rubocop:disable Cop/ActiveRecordDependent + -> { where(type: Namespaces::UserNamespace.sti_name) }, + dependent: :destroy, # rubocop:disable Cop/ActiveRecordDependent + foreign_key: :owner_id, + inverse_of: :owner, + autosave: true # rubocop:disable Cop/ActiveRecordDependent # Profile has_many :keys, -> { regular_keys }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -171,18 +171,18 @@ class User < ApplicationRecord has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group has_many :developer_groups, -> { where(members: { access_level: ::Gitlab::Access::DEVELOPER }) }, through: :group_members, source: :group has_many :owned_or_maintainers_groups, - -> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, - through: :group_members, - source: :group + -> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, + through: :group_members, + source: :group alias_attribute :masters_groups, :maintainers_groups has_many :developer_maintainer_owned_groups, - -> { where(members: { access_level: [Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, - through: :group_members, - source: :group + -> { where(members: { access_level: [Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, + through: :group_members, + source: :group has_many :reporter_developer_maintainer_owned_groups, - -> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, - through: :group_members, - source: :group + -> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) }, + through: :group_members, + source: :group has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, class_name: 'GroupMember' has_many :minimal_access_groups, through: :minimal_access_group_members, source: :group @@ -349,27 +349,27 @@ class User < ApplicationRecord enum role: { software_developer: 0, development_team_lead: 1, devops_engineer: 2, systems_administrator: 3, security_analyst: 4, data_analyst: 5, product_manager: 6, product_designer: 7, other: 8 }, _suffix: true delegate :notes_filter_for, - :set_notes_filter, - :first_day_of_week, :first_day_of_week=, - :timezone, :timezone=, - :time_display_relative, :time_display_relative=, - :show_whitespace_in_diffs, :show_whitespace_in_diffs=, - :view_diffs_file_by_file, :view_diffs_file_by_file=, - :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=, - :tab_width, :tab_width=, - :sourcegraph_enabled, :sourcegraph_enabled=, - :gitpod_enabled, :gitpod_enabled=, - :setup_for_company, :setup_for_company=, - :render_whitespace_in_code, :render_whitespace_in_code=, - :markdown_surround_selection, :markdown_surround_selection=, - :markdown_automatic_lists, :markdown_automatic_lists=, - :diffs_deletion_color, :diffs_deletion_color=, - :diffs_addition_color, :diffs_addition_color=, - :use_new_navigation, :use_new_navigation=, - :pinned_nav_items, :pinned_nav_items=, - :achievements_enabled, :achievements_enabled=, - :enabled_following, :enabled_following=, - to: :user_preference + :set_notes_filter, + :first_day_of_week, :first_day_of_week=, + :timezone, :timezone=, + :time_display_relative, :time_display_relative=, + :show_whitespace_in_diffs, :show_whitespace_in_diffs=, + :view_diffs_file_by_file, :view_diffs_file_by_file=, + :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=, + :tab_width, :tab_width=, + :sourcegraph_enabled, :sourcegraph_enabled=, + :gitpod_enabled, :gitpod_enabled=, + :setup_for_company, :setup_for_company=, + :render_whitespace_in_code, :render_whitespace_in_code=, + :markdown_surround_selection, :markdown_surround_selection=, + :markdown_automatic_lists, :markdown_automatic_lists=, + :diffs_deletion_color, :diffs_deletion_color=, + :diffs_addition_color, :diffs_addition_color=, + :use_new_navigation, :use_new_navigation=, + :pinned_nav_items, :pinned_nav_items=, + :achievements_enabled, :achievements_enabled=, + :enabled_following, :enabled_following=, + to: :user_preference delegate :path, to: :namespace, allow_nil: true, prefix: true delegate :job_title, :job_title=, to: :user_detail, allow_nil: true @@ -517,28 +517,27 @@ class User < ApplicationRecord scope :with_dashboard, -> (dashboard) { where(dashboard: dashboard) } scope :with_public_profile, -> { where(private_profile: false) } scope :with_expiring_and_not_notified_personal_access_tokens, ->(at) do - where('EXISTS (?)', - ::PersonalAccessToken - .where('personal_access_tokens.user_id = users.id') - .without_impersonation - .expiring_and_not_notified(at).select(1)) + where('EXISTS (?)', ::PersonalAccessToken + .where('personal_access_tokens.user_id = users.id') + .without_impersonation + .expiring_and_not_notified(at).select(1) + ) end scope :with_personal_access_tokens_expired_today, -> do - where('EXISTS (?)', - ::PersonalAccessToken - .select(1) - .where('personal_access_tokens.user_id = users.id') - .without_impersonation - .expired_today_and_not_notified) + where('EXISTS (?)', ::PersonalAccessToken + .select(1) + .where('personal_access_tokens.user_id = users.id') + .without_impersonation + .expired_today_and_not_notified + ) end scope :with_ssh_key_expiring_soon, -> do includes(:expiring_soon_and_unnotified_keys) - .where('EXISTS (?)', - ::Key - .select(1) - .where('keys.user_id = users.id') - .expiring_soon_and_not_notified) + .where('EXISTS (?)', ::Key + .select(1) + .where('keys.user_id = users.id') + .expiring_soon_and_not_notified) end scope :order_recent_sign_in, -> { reorder(arel_table[:current_sign_in_at].desc.nulls_last) } scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) } @@ -2057,9 +2056,11 @@ class User < ApplicationRecord # # Returns a Hash mapping project ID -> maximum access level. def max_member_access_for_project_ids(project_ids) - Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(Project), - resource_ids: project_ids, - default_value: Gitlab::Access::NO_ACCESS) do |project_ids| + Gitlab::SafeRequestLoader.execute( + resource_key: max_member_access_for_resource_key(Project), + resource_ids: project_ids, + default_value: Gitlab::Access::NO_ACCESS + ) do |project_ids| project_authorizations.where(project: project_ids) .group(:project_id) .maximum(:access_level) @@ -2074,9 +2075,11 @@ class User < ApplicationRecord # # Returns a Hash mapping project ID -> maximum access level. def max_member_access_for_group_ids(group_ids) - Gitlab::SafeRequestLoader.execute(resource_key: max_member_access_for_resource_key(Group), - resource_ids: group_ids, - default_value: Gitlab::Access::NO_ACCESS) do |group_ids| + Gitlab::SafeRequestLoader.execute( + resource_key: max_member_access_for_resource_key(Group), + resource_ids: group_ids, + default_value: Gitlab::Access::NO_ACCESS + ) do |group_ids| group_members.where(source: group_ids).group(:source_id).maximum(:access_level) end end diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 3a1fce9c0b4..9c40376bb13 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -20,8 +20,8 @@ class UserPreference < ApplicationRecord less_than_or_equal_to: Gitlab::TabWidth::MAX } validates :diffs_deletion_color, :diffs_addition_color, - format: { with: ColorsHelper::HEX_COLOR_PATTERN }, - allow_blank: true + format: { with: ColorsHelper::HEX_COLOR_PATTERN }, + allow_blank: true validates :pass_user_identities_to_ci_jwt, allow_nil: false, inclusion: { in: [true, false] } diff --git a/app/models/users/group_callout.rb b/app/models/users/group_callout.rb index 601622b8c4c..1cc9f1f50ad 100644 --- a/app/models/users/group_callout.rb +++ b/app/models/users/group_callout.rb @@ -30,9 +30,9 @@ module Users validates :group, presence: true validates :feature_name, - presence: true, - uniqueness: { scope: [:user_id, :group_id] }, - inclusion: { in: GroupCallout.feature_names.keys } + presence: true, + uniqueness: { scope: [:user_id, :group_id] }, + inclusion: { in: GroupCallout.feature_names.keys } def source_feature_name "#{feature_name}_#{group_id}" diff --git a/app/models/users/phone_number_validation.rb b/app/models/users/phone_number_validation.rb index b9e4e908ddd..52f16a7861f 100644 --- a/app/models/users/phone_number_validation.rb +++ b/app/models/users/phone_number_validation.rb @@ -8,28 +8,25 @@ module Users belongs_to :user, foreign_key: :user_id belongs_to :banned_user, class_name: '::Users::BannedUser', foreign_key: :user_id - validates :country, - presence: true, - length: { maximum: 3 } + validates :country, presence: true, length: { maximum: 3 } validates :international_dial_code, - presence: true, - numericality: { - only_integer: true, - greater_than_or_equal_to: 1, - less_than_or_equal_to: 999 - } + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 1, + less_than_or_equal_to: 999 + } validates :phone_number, - presence: true, - format: { - with: /\A\d+\Z/, - message: -> (object, data) { _('can contain only digits') } - }, - length: { maximum: 12 } - - validates :telesign_reference_xid, - length: { maximum: 255 } + presence: true, + format: { + with: /\A\d+\Z/, + message: -> (object, data) { _('can contain only digits') } + }, + length: { maximum: 12 } + + validates :telesign_reference_xid, length: { maximum: 255 } scope :for_user, -> (user_id) { where(user_id: user_id) } diff --git a/app/models/users/project_callout.rb b/app/models/users/project_callout.rb index 6199c3697f7..3964f202be6 100644 --- a/app/models/users/project_callout.rb +++ b/app/models/users/project_callout.rb @@ -20,8 +20,8 @@ module Users validates :project, presence: true validates :feature_name, - presence: true, - uniqueness: { scope: [:user_id, :project_id] }, - inclusion: { in: ProjectCallout.feature_names.keys } + presence: true, + uniqueness: { scope: [:user_id, :project_id] }, + inclusion: { in: ProjectCallout.feature_names.keys } end end diff --git a/app/models/users/user_follow_user.rb b/app/models/users/user_follow_user.rb index 5a82a81364a..c9d4bee496c 100644 --- a/app/models/users/user_follow_user.rb +++ b/app/models/users/user_follow_user.rb @@ -14,9 +14,13 @@ module Users followee_count = self.class.where(follower_id: follower_id).limit(MAX_FOLLOWEE_LIMIT).count return if followee_count < MAX_FOLLOWEE_LIMIT - errors.add(:base, format( - _("You can't follow more than %{limit} users. To follow more users, unfollow some others."), - limit: MAX_FOLLOWEE_LIMIT)) + errors.add( + :base, + format( + _("You can't follow more than %{limit} users. To follow more users, unfollow some others."), + limit: MAX_FOLLOWEE_LIMIT + ) + ) end end end diff --git a/app/models/web_ide_terminal.rb b/app/models/web_ide_terminal.rb index ef70df2405f..fe69ca80c32 100644 --- a/app/models/web_ide_terminal.rb +++ b/app/models/web_ide_terminal.rb @@ -39,12 +39,14 @@ class WebIdeTerminal private def web_ide_terminal_route_generator(action, options = {}) - options.reverse_merge!(action: action, - controller: 'projects/web_ide_terminals', - namespace_id: project.namespace.to_param, - project_id: project.to_param, - id: build.id, - only_path: true) + options.reverse_merge!( + action: action, + controller: 'projects/web_ide_terminals', + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: build.id, + only_path: true + ) url_for(options) end diff --git a/app/models/webauthn_registration.rb b/app/models/webauthn_registration.rb index 86e7e2dedc3..c8b2513e702 100644 --- a/app/models/webauthn_registration.rb +++ b/app/models/webauthn_registration.rb @@ -12,5 +12,5 @@ class WebauthnRegistration < ApplicationRecord validates :credential_xid, :public_key, :counter, presence: true validates :name, length: { minimum: 0, allow_nil: false } validates :counter, - numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 2**32 - 1 } + numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 2**32 - 1 } end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index b04aa196883..e1468872f52 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -145,10 +145,12 @@ class WikiPage default_per_page = Kaminari.config.default_per_page offset = [options[:page].to_i - 1, 0].max * options.fetch(:per_page, default_per_page) - wiki.repository.commits(wiki.default_branch, - path: page.path, - limit: options.fetch(:limit, default_per_page), - offset: offset) + wiki.repository.commits( + wiki.default_branch, + path: page.path, + limit: options.fetch(:limit, default_per_page), + offset: offset + ) end def count_versions diff --git a/app/models/work_item.rb b/app/models/work_item.rb index 4724d644318..24d1078516e 100644 --- a/app/models/work_item.rb +++ b/app/models/work_item.rb @@ -16,10 +16,10 @@ class WorkItem < Issue has_many :child_links, class_name: '::WorkItems::ParentLink', foreign_key: :work_item_parent_id has_many :work_item_children, through: :child_links, class_name: 'WorkItem', - foreign_key: :work_item_id, source: :work_item + foreign_key: :work_item_id, source: :work_item has_many :work_item_children_by_relative_position, -> { work_item_children_keyset_order }, - through: :child_links, class_name: 'WorkItem', - foreign_key: :work_item_id, source: :work_item + through: :child_links, class_name: 'WorkItem', + foreign_key: :work_item_id, source: :work_item scope :inc_relations_for_permission_check, -> { includes(:author, project: :project_feature) } diff --git a/app/services/keys/last_used_service.rb b/app/services/keys/last_used_service.rb index daef544bac0..3683c03b7a4 100644 --- a/app/services/keys/last_used_service.rb +++ b/app/services/keys/last_used_service.rb @@ -2,7 +2,7 @@ module Keys class LastUsedService - TIMEOUT = 1.day.to_i + TIMEOUT = 1.day attr_reader :key @@ -12,26 +12,24 @@ module Keys end def execute + return unless update? + # We _only_ want to update last_used_at and not also updated_at (which # would be updated when using #touch). - key.update_column(:last_used_at, Time.zone.now) if update? + key.update_column(:last_used_at, Time.zone.now) end - def update? - return false if ::Gitlab::Database.read_only? - - last_used = key.last_used_at + def execute_async + return unless update? - return false if last_used && (Time.zone.now - last_used) <= TIMEOUT - - !!redis_lease.try_obtain + ::SshKeys::UpdateLastUsedAtWorker.perform_async(key.id) end - private + def update? + return false if ::Gitlab::Database.read_only? - def redis_lease - Gitlab::ExclusiveLease - .new("key_update_last_used_at:#{key.id}", timeout: TIMEOUT) + last_used = key.last_used_at + last_used.blank? || last_used <= TIMEOUT.ago end end end diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb index 20f96ac2949..f8c91fbae7d 100644 --- a/app/services/members/approve_access_request_service.rb +++ b/app/services/members/approve_access_request_service.rb @@ -18,7 +18,7 @@ module Members def after_execute(member:, skip_log_audit_event:) super - resolve_access_request_todos(current_user, member) + resolve_access_request_todos(member) end def validate_access!(access_requester) diff --git a/app/services/members/base_service.rb b/app/services/members/base_service.rb index 801f77ae082..80fba33b20e 100644 --- a/app/services/members/base_service.rb +++ b/app/services/members/base_service.rb @@ -53,8 +53,8 @@ module Members end end - def resolve_access_request_todos(current_user, requester) - todo_service.resolve_access_request_todos(current_user, requester) + def resolve_access_request_todos(member) + todo_service.resolve_access_request_todos(member) end def enqueue_delete_todos(member) diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb index 0f195663a61..b77485ce744 100644 --- a/app/services/members/destroy_service.rb +++ b/app/services/members/destroy_service.rb @@ -84,7 +84,7 @@ module Members delete_subresources(member) unless skip_subresources delete_project_invitations_by(member) unless skip_subresources - resolve_access_request_todos(current_user, member) + resolve_access_request_todos(member) after_execute(member: member) end diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb index b373a099020..d31f4596fa5 100644 --- a/app/services/projects/open_issues_count_service.rb +++ b/app/services/projects/open_issues_count_service.rb @@ -26,7 +26,7 @@ module Projects def user_is_at_least_reporter? strong_memoize(:user_is_at_least_reporter) do - @project.team.member?(@user, Gitlab::Access::REPORTER) + @project.member?(@user, Gitlab::Access::REPORTER) end end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 2025d438ae7..c55e1680bfe 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -175,13 +175,26 @@ class TodoService TodosFinder.new(current_user).any_for_target?(issuable, :pending) end - # Resolves all todos related to target + # Resolves all todos related to target for the current_user def resolve_todos_for_target(target, current_user) attributes = attributes_for_target(target) resolve_todos(pending_todos([current_user], attributes), current_user) end + # Resolves all todos related to target for all users + def resolve_todos_with_attributes_for_target(target, attributes, resolution: :done, resolved_by_action: :system_done) + target_attributes = { target_id: target.id, target_type: target.class.polymorphic_name } + attributes.merge!(target_attributes) + attributes[:preload_user_association] = true + + todos = PendingTodosFinder.new(attributes).execute + users = todos.map(&:user) + todos_ids = todos.batch_update(state: resolution, resolved_by_action: resolved_by_action) + users.each(&:update_todos_count_cache) + todos_ids + end + def resolve_todos(todos, current_user, resolution: :done, resolved_by_action: :system_done) todos_ids = todos.batch_update(state: resolution, resolved_by_action: resolved_by_action) @@ -198,21 +211,20 @@ class TodoService current_user.update_todos_count_cache end - def resolve_access_request_todos(current_user, member) - return if current_user.nil? || member.nil? + def resolve_access_request_todos(member) + return if member.nil? + # Group or Project target = member.source - finder_params = { + todos_params = { state: :pending, author_id: member.user_id, - action_id: ::Todo::MEMBER_ACCESS_REQUESTED, - type: target.class.polymorphic_name, - target: target.id + action: ::Todo::MEMBER_ACCESS_REQUESTED, + type: target.class.polymorphic_name } - todos = TodosFinder.new(current_user, finder_params).execute - resolve_todos(todos, current_user) + resolve_todos_with_attributes_for_target(target, todos_params) end def restore_todos(todos, current_user) @@ -419,7 +431,7 @@ class TodoService end def pending_todos(users, criteria = {}) - PendingTodosFinder.new(users, criteria).execute + PendingTodosFinder.new(criteria.merge(users: users)).execute end def track_todo_creation(user, issue_type, namespace, project) diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index f53b2051835..7c936c849d0 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -4,7 +4,7 @@ - if @snippets.exists? - if current_user .top-area - - include_private = @project.team.member?(current_user) || current_user.admin? + - include_private = @project.member?(current_user) || current_user.admin? = render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private, counts: @snippet_counts } - if new_project_snippet_link.present? diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index e9965ff0027..d6f410f4b1d 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -3468,6 +3468,15 @@ :weight: 1 :idempotent: true :tags: [] +- :name: ssh_keys_update_last_used_at + :worker_name: SshKeys::UpdateLastUsedAtWorker + :feature_category: :source_code_management + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: system_hook_push :worker_name: SystemHookPushWorker :feature_category: :source_code_management diff --git a/app/workers/ssh_keys/update_last_used_at_worker.rb b/app/workers/ssh_keys/update_last_used_at_worker.rb new file mode 100644 index 00000000000..80c2132b8d8 --- /dev/null +++ b/app/workers/ssh_keys/update_last_used_at_worker.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module SshKeys + class UpdateLastUsedAtWorker + include ApplicationWorker + + idempotent! + deduplicate :until_executed + data_consistency :sticky + + feature_category :source_code_management + + def perform(key_id) + key = Key.find_by_id(key_id) + + return unless key + + Keys::LastUsedService.new(key).execute + end + end +end -- cgit v1.2.1