module Killbill::Zendesk class UserUpdater def initialize(client, kb_apis, logger) @client = client @kb_apis = kb_apis @logger = logger end def update(lookup_key) kb_account = lookup_kb_account(lookup_key) user = find_by_kb_account(kb_account) user = create_user(kb_account) if user.nil? if user.nil? @logger.warn "Failed to create user in Zendesk for account #{lookup_key}" return end save_kb_zd_mapping(kb_account, user) user.name = kb_account.name user.external_id = kb_account.external_key || kb_account.id user.locale = kb_account.locale user.timezone = kb_account.time_zone user.email = kb_account.email user.phone = kb_account.phone user.details = build_details_field(kb_account) if user.save @logger.info "Successfully updated #{user.name} in Zendesk: #{user.url}" user.url else @logger.warn "Unable to update #{user.name} in Zendesk: #{user.url}" nil end end # Find the Kill Bill account associated with that lookup_key (account id or external key) def lookup_kb_account(lookup_key) if lookup_key =~ /[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}/ @kb_apis.account_user_api.get_account_by_id(lookup_key, @kb_apis.create_context) else @kb_apis.account_user_api.get_account_by_key(lookup_key, @kb_apis.create_context) end end # Build the Zendesk details field (address information) def build_details_field(kb_account) details = [kb_account.address1, kb_account.address2, kb_account.city, kb_account.state_or_province, kb_account.postal_code, kb_account.country] (details.reject { |detail| detail.blank? }).join(', ') end # Create a user in Zendesk def create_user(kb_account) @client.users.create(:name => kb_account.name) end def save_kb_zd_mapping(kb_account, user) # Save the mapping locally - this is required due to the indexing lag on the Zendesk side, # see https://support.zendesk.com/entries/20239737: # When you add new data to your Zendesk, it typically takes about 2 to 3 minutes before it's indexed and can be searched. # This is unacceptable for us: if an account creation event is quickly followed by a account update event, # we wouldn't be able to retrieve the user, potentially causing duplicates and/or triggering validation errors, e.g. # Email 1370587241-test@tester.com is already being used by another user ZendeskUser.where(:kb_account_id => kb_account.id, :zd_user_id => user.id).first_or_create! end # Find the Zendesk user associated with that Kill Bill account def find_by_kb_account(kb_account) # Do we have already a mapping for that user? zd_account = find_by_id(kb_account.id) return zd_account if zd_account # TODO In the search results below, should we worry about potential dups? # First search by external_id, which is the safest method. # The external_id is either the account external key... zd_account = find_by_external_id(kb_account.external_key) if kb_account.external_key return zd_account if zd_account # ...or the Kill Bill account id zd_account = find_by_external_id(kb_account.id) return zd_account if zd_account # At this point, we haven't matched this user yet. To reconcile it, use the email address which is guaranteed # to exist on the Zendesk side zd_account = find_by_email(kb_account.email) if kb_account.email return zd_account if zd_account # We couldn't find a match - the account will be created nil end def find_by_id(kb_account_id) zd_user = ZendeskUser.find_by_kb_account_id(kb_account_id) zd_user ? @client.users.find(:id => zd_user.zd_user_id) : nil end def find_by_external_id(external_id) find_all_by_external_id(external_id).first end def find_all_by_external_id(external_id) @client.search(:query => "type:user external_id:#{external_id}", :reload => true) end def find_by_email(email) find_all_by_email(email).first end def find_all_by_email(email) @client.search(:query => "type:user email:#{email}", :reload => true) end end end