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 }}
-
- {{ content }}
-
{{ content }}
-
- {{ 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