1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
# frozen_string_literal: true
module Users
class UpdateService < BaseService
include NewUserNotifier
attr_reader :user, :identity_params
ATTRS_REQUIRING_PASSWORD_CHECK = %w[email].freeze
BATCH_SIZE = 100
def initialize(current_user, params = {})
@current_user = current_user
@validation_password = params.delete(:validation_password)
@user = params.delete(:user)
@status_params = params.delete(:status)
@identity_params = params.slice(*identity_attributes)
@params = params.dup
end
def execute(validate: true, check_password: false, &block)
yield(@user) if block
user_exists = @user.persisted?
@user.user_detail # prevent assignment
discard_read_only_attributes
assign_attributes
if check_password && require_password_check? && !@user.valid_password?(@validation_password)
return error(s_("Profiles|Invalid password"))
end
assign_identity
build_canonical_email
reset_unconfirmed_email
if @user.save(validate: validate) && update_status
after_update(user_exists)
else
messages = @user.errors.full_messages + Array(@user.status&.errors&.full_messages)
error(messages.uniq.join('. '))
end
end
def execute!(*args, **kargs, &block)
result = execute(*args, **kargs, &block)
raise ActiveRecord::RecordInvalid, @user unless result[:status] == :success
true
end
private
def require_password_check?
return false unless @user.persisted?
return false if @user.password_automatically_set?
changes = @user.changed
ATTRS_REQUIRING_PASSWORD_CHECK.any? { |param| changes.include?(param) }
end
def build_canonical_email
return unless @user.email_changed?
Users::UpdateCanonicalEmailService.new(user: @user).execute
end
def reset_unconfirmed_email
return unless @user.persisted?
return unless @user.email_changed?
@user.update_column(:unconfirmed_email, nil)
end
def update_status
return true unless @status_params
Users::SetStatusService.new(current_user, @status_params.merge(user: @user)).execute
end
def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists
end
def discard_read_only_attributes
discard_synced_attributes
end
def discard_synced_attributes
if (metadata = @user.user_synced_attributes_metadata)
read_only = metadata.read_only_attributes
params.reject! { |key, _| read_only.include?(key.to_sym) }
end
end
def assign_attributes
@user.assign_attributes(params.except(*identity_attributes)) unless params.empty?
end
def assign_identity
return unless identity_params.present?
identity = user.identities.find_or_create_by(provider_params) # rubocop: disable CodeReuse/ActiveRecord
identity.update(identity_params)
end
def identity_attributes
[:provider, :extern_uid]
end
def provider_attributes
[:provider]
end
def provider_params
identity_params.slice(*provider_attributes)
end
def after_update(user_exists)
notify_success(user_exists)
remove_followers_and_followee! if ::Feature.enabled?(:disable_follow_users, user)
success
end
def remove_followers_and_followee!
return false unless user.user_preference.enabled_following_previously_changed?(from: true, to: false)
# rubocop: disable CodeReuse/ActiveRecord
loop do
inner_query = Users::UserFollowUser
.where(follower_id: user.id).or(Users::UserFollowUser.where(followee_id: user.id))
.select(:follower_id, :followee_id)
.limit(BATCH_SIZE)
deleted_records = Users::UserFollowUser.where('(follower_id, followee_id) IN (?)', inner_query).delete_all
break if deleted_records == 0
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
Users::UpdateService.prepend_mod_with('Users::UpdateService')
|