Skip to content
Merged
2 changes: 1 addition & 1 deletion app/controllers/concerns/unconfirmed_user_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def email_confirmation_token_validator_result

def email_confirmation_token_validator
@email_confirmation_token_validator ||= begin
EmailConfirmationTokenValidator.new(@email_address, current_user)
EmailConfirmationTokenValidator.new(email_address: @email_address, current_user:)
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/sign_up/cancellations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def find_user

confirmation_token = session[:user_confirmation_token]
email_address = EmailAddress.find_with_confirmation_token(confirmation_token)
@token_validator = EmailConfirmationTokenValidator.new(email_address, current_user)
@token_validator = EmailConfirmationTokenValidator.new(email_address:, current_user:)
result = @token_validator.submit

if result.success?
Expand Down
16 changes: 8 additions & 8 deletions app/controllers/users/email_confirmations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
module Users
class EmailConfirmationsController < ApplicationController
def create
store_from_select_email_flow_in_session
result = email_confirmation_token_validator.submit
analytics.add_email_confirmation(**result)
analytics.add_email_confirmation(**result, from_select_email_flow: from_select_email_flow?)
if result.success?
process_successful_confirmation(email_address)
else
Expand All @@ -28,12 +29,8 @@ def email_address
end

def email_confirmation_token_validator
@email_confirmation_token_validator ||= begin
EmailConfirmationTokenValidator.new(
email_address,
current_user,
)
end
@email_confirmation_token_validator ||=
EmailConfirmationTokenValidator.new(email_address:, current_user:)
end

def email_address_already_confirmed?
Expand All @@ -42,7 +39,6 @@ def email_address_already_confirmed?

def process_successful_confirmation(email_address)
confirm_and_notify(email_address)
store_from_select_email_flow_in_session
if current_user
flash[:success] = t('devise.confirmations.confirmed')
if params[:request_id]
Expand Down Expand Up @@ -107,5 +103,9 @@ def confirmation_params
def store_from_select_email_flow_in_session
session[:from_select_email_flow] = params[:from_select_email_flow].to_s == 'true'
end

def from_select_email_flow?
session[:from_select_email_flow] == true
end
end
end
12 changes: 7 additions & 5 deletions app/controllers/users/emails_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ class EmailsController < ApplicationController
before_action :confirm_recently_authenticated_2fa

def show
analytics.add_email_visit
session[:in_select_email_flow] = params[:in_select_email_flow]
session[:in_select_email_flow] = true if params[:in_select_email_flow]
analytics.add_email_visit(in_select_email_flow: in_select_email_flow?)
@add_user_email_form = AddUserEmailForm.new
@pending_completions_consent = pending_completions_consent?
end

def add
@add_user_email_form = AddUserEmailForm.new(
session[:in_select_email_flow],
)
@add_user_email_form = AddUserEmailForm.new(in_select_email_flow: in_select_email_flow?)

result = @add_user_email_form.submit(
current_user, permitted_params.merge(request_id:)
Expand Down Expand Up @@ -83,6 +81,10 @@ def verify

private

def in_select_email_flow?
session[:in_select_email_flow] == true
end

def authorize_user_to_edit_email
return render_not_found if email_address.user != current_user
rescue ActiveRecord::RecordNotFound
Expand Down
6 changes: 4 additions & 2 deletions app/forms/add_user_email_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ class AddUserEmailForm
include ActionView::Helpers::TranslationHelper

attr_reader :email, :in_select_email_flow
alias_method :in_select_email_flow?, :in_select_email_flow

def self.model_name
ActiveModel::Name.new(self, nil, 'User')
end

def initialize(in_select_email_flow = nil)
def initialize(in_select_email_flow: false)
@in_select_email_flow = in_select_email_flow
end

Expand Down Expand Up @@ -52,13 +53,14 @@ def process_successful_submission
@success = true
email_address.save!
SendAddEmailConfirmation.new(user).
call(email_address:, in_select_email_flow:, request_id:)
call(email_address:, in_select_email_flow: in_select_email_flow?, request_id:)
end

def extra_analytics_attributes
{
user_id: existing_user.uuid,
domain_name: email&.split('@')&.last,
in_select_email_flow: in_select_email_flow?,
}
end

Expand Down
13 changes: 11 additions & 2 deletions app/forms/select_email_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@ def initialize(user:, identity: nil)
end

def submit(params)
@selected_email_id = params[:selected_email_id]
@selected_email_id = params[:selected_email_id].try(:to_i) if !params[:selected_email_id].blank?

success = valid?
identity.update(email_address_id: selected_email_id) if success && identity

FormResponse.new(success:, errors:, serialize_error_details_only: true)
FormResponse.new(
success:,
errors:,
extra: extra_analytics_attributes,
serialize_error_details_only: true,
)
end

private

def extra_analytics_attributes
{ selected_email_id: }
end

def validate_owns_selected_email
return if user.confirmed_email_addresses.exists?(id: selected_email_id)
errors.add(:selected_email_id, :not_found, message: t('email_address.not_found'))
Expand Down
41 changes: 33 additions & 8 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,24 @@ def account_visit
# @param [Hash] errors Errors resulting from form validation
# @param [Hash] error_details Details for errors that occurred in unsuccessful submission
# @param [String] user_id User the email is linked to
# @param [Boolean] from_select_email_flow Whether email was added as part of partner email
# selection.
# A user has clicked the confirmation link in an email
def add_email_confirmation(user_id:, success:, errors:, error_details: nil, **extra)
def add_email_confirmation(
user_id:,
success:,
errors:,
from_select_email_flow:,
error_details: nil,
**extra
)
track_event(
'Add Email: Email Confirmation',
user_id:,
success:,
errors:,
error_details:,
from_select_email_flow:,
**extra,
)
end
Expand All @@ -259,21 +269,33 @@ def add_email_confirmation(user_id:, success:, errors:, error_details: nil, **ex
# @param [Hash] errors Errors resulting from form validation
# @param [Hash] error_details Details for errors that occurred in unsuccessful submission
# @param [String] domain_name Domain name of email address submitted
# @param [Boolean] in_select_email_flow Whether email is being added as part of partner email
# selection.
# Tracks request for adding new emails to an account
def add_email_request(success:, errors:, domain_name:, error_details: nil, **extra)
def add_email_request(
success:,
errors:,
domain_name:,
in_select_email_flow:,
error_details: nil,
**extra
)
track_event(
'Add Email Requested',
success:,
errors:,
error_details:,
domain_name:,
in_select_email_flow:,
**extra,
)
end

# When a user views the add email address page
def add_email_visit
track_event('Add Email Address Page Visited')
# @param [Boolean] in_select_email_flow Whether email is being added as part of partner email
# selection.
def add_email_visit(in_select_email_flow:, **extra)
track_event('Add Email Address Page Visited', in_select_email_flow:, **extra)
end

# Tracks When users visit the add phone page
Expand Down Expand Up @@ -6825,10 +6847,12 @@ def sp_revoke_consent_visited(issuer:, **extra)
# User submitted form to change email shared with service provider
# @param [Boolean] success Whether form validation was successful
# @param [Hash] error_details Details for errors that occurred in unsuccessful submission
# @param [Integer] selected_email_id Selected email address record ID
# @param [String, nil] needs_completion_screen_reason Reason for the consent screen being shown,
# if user is changing email in consent flow
def sp_select_email_submitted(
success:,
selected_email_id:,
error_details: nil,
needs_completion_screen_reason: nil,
**extra
Expand All @@ -6838,6 +6862,7 @@ def sp_select_email_submitted(
success:,
error_details:,
needs_completion_screen_reason:,
selected_email_id:,
**extra,
)
end
Expand Down Expand Up @@ -7135,10 +7160,10 @@ def user_registration_email_confirmation(
)
track_event(
'User Registration: Email Confirmation',
success: success,
errors: errors,
error_details: error_details,
user_id: user_id,
success:,
errors:,
error_details:,
user_id:,
**extra,
)
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/email_confirmation_token_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class EmailConfirmationTokenValidator
validate :email_not_already_confirmed, if: :email_address_found_with_token?
validate :token_not_expired, if: :email_address_found_with_token?

def initialize(email_address, current_user = nil)
def initialize(email_address:, current_user: nil)
@current_user = current_user
@email_address = email_address
@user = email_address&.user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@

response

expect(@analytics).to have_logged_event(:sp_select_email_submitted, success: true)
expect(@analytics).to have_logged_event(
:sp_select_email_submitted,
success: true,
selected_email_id: selected_email.id,
)
end

context 'with invalid submission' do
let(:params) { super().merge(select_email_form: { selected_email_id: nil }) }
let(:params) { super().merge(select_email_form: { selected_email_id: '' }) }

it 'redirects to form with flash' do
expect(response).to redirect_to(edit_connected_account_selected_email_path(identity.id))
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/sign_up/passwords_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
end

it 'rejects when confirmation_token is invalid' do
validator = EmailConfirmationTokenValidator.new(user.email_addresses.first)
validator = EmailConfirmationTokenValidator.new(email_address: user.email_addresses.first)
result = validator.submit
expect(result.success?).to eq false

Expand Down
2 changes: 2 additions & 0 deletions spec/controllers/sign_up/select_email_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
:sp_select_email_submitted,
success: true,
needs_completion_screen_reason: :new_attributes,
selected_email_id: selected_email.id,
)
end

Expand All @@ -131,6 +132,7 @@
success: false,
error_details: { selected_email_id: { not_found: true } },
needs_completion_screen_reason: :new_attributes,
selected_email_id: selected_email.id,
)
end
end
Expand Down
19 changes: 19 additions & 0 deletions spec/controllers/users/email_confirmations_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
describe '#create' do
describe 'Valid email confirmation tokens' do
it 'tracks a valid email confirmation token event' do
stub_analytics

user = create(:user)
new_email = Faker::Internet.email

Expand All @@ -23,6 +25,14 @@
email_record = add_email_form.email_address_record(new_email)

get :create, params: { confirmation_token: email_record.reload.confirmation_token }

expect(@analytics).to have_logged_event(
'Add Email: Email Confirmation',
success: true,
errors: {},
from_select_email_flow: false,
user_id: user.uuid,
)
end

context 'when select email feature is disabled' do
Expand Down Expand Up @@ -126,6 +136,7 @@
end

it 'adds an email from the service provider consent flow' do
stub_analytics
new_email = Faker::Internet.email
add_email_form = AddUserEmailForm.new
add_email_form.submit(user, email: new_email, request_id: sp_request_uuid)
Expand All @@ -134,8 +145,16 @@
get :create, params: {
confirmation_token: email_record.reload.confirmation_token,
request_id: sp_request_uuid,
from_select_email_flow: 'true',
}

expect(@analytics).to have_logged_event(
'Add Email: Email Confirmation',
success: true,
errors: {},
from_select_email_flow: true,
user_id: user.uuid,
)
expect(response).to redirect_to(sign_up_select_email_url)
end
end
Expand Down
Loading