Class: Account::Inviter

Inherits:
BaseService show all
Defined in:
app/services/account/inviter.rb

Overview

Service object: inviter.

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Attributes inherited from BaseService

#options

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger

Constructor Details

This class inherits a constructor from BaseService

Instance Attribute Details

#accountObject (readonly)

Returns the value of attribute account.



18
19
20
# File 'app/services/account/inviter.rb', line 18

def 
  @account
end

#partyObject (readonly)

Returns the value of attribute party.



18
19
20
# File 'app/services/account/inviter.rb', line 18

def party
  @party
end

Class Method Details

.determine_login(email) ⇒ String

Picks an unused login derived from the email, suffixing -1, -2, … on collision.

Parameters:

  • email (String)

    email to derive the login from

Returns:

  • (String)

    an available login



74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/services/account/inviter.rb', line 74

def self.(email)
   = email
  if Account.where(login: ).exists?
    name_part, = email.split('@')
     = name_part
    counter = 0
     = 
    while Account.where(login: ).exists?
      counter += 1
       = "#{}-#{counter}"
    end
  end
  
end

Instance Method Details

#destroy_existing_account!(party, existing_account, _email) ⇒ void (protected)

This method returns an undefined value.

Destroys the party's existing account before a forced re-invite.

Parameters:

  • party (Party)

    party being re-invited

  • existing_account (Account)

    account to destroy

  • _email (String)

    unused (new invitation email)



96
97
98
99
100
101
102
103
# File 'app/services/account/inviter.rb', line 96

def destroy_existing_account!(party, , )
  ErrorReporting.warning(
    "[Account::Inviter] destroying existing account #{.id} on party #{party.id} before re-inviting",
    party_id: party.id, account_id: .id, account_email: .email
  )
  .destroy
  party.
end

#existing_account_result(existing_account) ⇒ Result (protected)

Failure result when the party already has an online account.

Parameters:

  • existing_account (Account)

    the party's existing account

Returns:

  • (Result)

    failure result pointing at the existing account



122
123
124
# File 'app/services/account/inviter.rb', line 122

def ()
  Result.new(invited: false, account: , messages: ["This customer already has an online account (#{.}). Use the existing account page to resend the invitation or reset their password."])
end

#login_in_use_result(login) ⇒ Result (protected)

Failure result when the requested login is already taken.

Parameters:

  • login (String)

    the conflicting login

Returns:

  • (Result)

    failure result with an explanatory message



115
116
117
# File 'app/services/account/inviter.rb', line 115

def ()
  Result.new(invited: false, messages: ["The login you requested #{} is already in use."])
end

#missing_email_result(party) ⇒ Result (protected)

Failure result when the party has no usable email.

Parameters:

  • party (Party)

    party that was to be invited

Returns:

  • (Result)

    failure result with an explanatory message



108
109
110
# File 'app/services/account/inviter.rb', line 108

def missing_email_result(party)
  Result.new(invited: false, messages: ["No Email for party #{party.id}"])
end

#process(party:, email: nil, login: nil, skip_invitation_delivery: false, return_path: nil, force_reinvite: false) ⇒ Result

Invites the party to create an online account, publishing an
Events::AccountInvited event for async email delivery.

Parameters:

  • party (Party)

    party to invite

  • email (String, nil) (defaults to: nil)

    invitation email (defaults to the party's email)

  • login (String, nil) (defaults to: nil)

    requested login (derived from email when nil)

  • skip_invitation_delivery (Boolean) (defaults to: false)

    skip publishing the invitation event

  • return_path (String, nil) (defaults to: nil)

    path to return to after accepting the invite

  • force_reinvite (Boolean) (defaults to: false)

    destroy an existing account and re-invite

Returns:

  • (Result)

    invitation outcome



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
# File 'app/services/account/inviter.rb', line 29

def process(party:, email: nil, login: nil, skip_invitation_delivery: false,
            return_path: nil, force_reinvite: false)
  email ||= party.email

  return missing_email_result(party) unless email

  existing = Account.find_by(party_id: party.id)
  if existing
    return (existing) unless force_reinvite

    destroy_existing_account!(party, existing, email)

  end

   ||= self.class.(email)
  return () if Account.where(login: ).exists?

  # `name` intentionally omitted: the account's `name` column was retired
  # (redundant with `party.full_name`, which the invited account reaches via
  # `party_id` below). Passing it here raised UnknownAttributeError once the
  # column became `ignored_columns` — AppSignal #6286.
   = Account.invite!(login: , email: email) do |a|
    a.party_id = party.id
    a.return_path_for_invite = return_path
    a.skip_invitation = true
    a.set_default_marketing_preferences(I18n.locale)
  end
  # Send the invitation email off the request path: announce the invite and
  # let AccountInvitedHandler deliver it async (Sidekiq :mailers). The invite
  # token's digest is already persisted by `invite!` above (skip_invitation),
  # and `deliver_invitation` regenerates the raw token in the handler process,
  # so the emailed link stays valid.
  unless skip_invitation_delivery
    Rails.configuration.event_store.publish(
      Events::AccountInvited.new(data: { account_id: .id }),
      stream_name: "Account-#{.id}"
    )
  end

  Result.new(invited: .persisted?, email: email, account: , messages: .errors.full_messages)
end