Skip to content

Scheduled sync with salesforce

Bryan Eli edited this page Oct 14, 2020 · 9 revisions

Accounts does a sync with salesforce on a schedule basis. It pulls information about Contacts and Leads from Salesforce and updates User records in Accounts accordingly.

All the code for this is in one file, here: https://github.com/openstax/accounts/blob/35629624f3569327afa552900490e72d2372ae51/app/routines/update_user_salesforce_info.rb#L1

It does is the following:

  • Goes through all users that have already have a Salesforce Contact ID, pulls their Salesforce information, and saves it in the User record.
  • For users who do not yet have a Contact ID, looks for any leads with the given user's email address. If we find any, we check their lead's Status, and if all their leads have the status as "Converted", then it means that the user has been rejected as faculty.
  • For all other users who we don't have a contact or a lead for in SF, we change their User faculty_status to "no_faculty_info".

Here's the schedule on which it runs: https://github.com/openstax/accounts/blob/660711e8985fa40e0fddc531f0115d7746048eac/config/schedule.rb#L12-L18

every '5,35 * * * *' do
  runner <<-CMD
    OpenStax::RescueFrom.this{
      UpdateUserSalesforceInfo.call(allow_error_email: Time.zone.now.hour == 0 && Time.zone.now.min < 10)
    }
  CMD
end

Gems used

These are the most important models from the openstax_salesforce gem we use

This is the most relevant chunk of code

https://github.com/openstax/accounts/blob/35629624f3569327afa552900490e72d2372ae51/app/routines/update_user_salesforce_info.rb#L54-L84

    # Go through all users that don't yet have a Salesforce Contact ID and populate their
    # salesforce info when they have verified emails that match SF data.

    @contacts_by_email.keys.each_slice(1000) do |emails|
      # eager_load by default produces a LEFT OUTER JOIN
      # But we can use an INNER JOIN here since we have a WHERE condition on contact_infos
      # So we use joins to convert the LEFT OUTER JOIN to an INNER JOIN
      User.activated.joins(:contact_infos)
          .eager_load(:contact_infos)
          .where(salesforce_contact_id: nil)
          .where( ci_table[:value].lower.in(emails) )
          .where( ci_table[:verified].eq(true).or(ci_table[:is_school_issued].eq(true)) )
          .each do |user|

        begin
          # The query above limits us to only verified email addresses
          # eager_load ensures only the verified email addresses are returned

          contacts = user.contact_infos
                         .map{|ci| @contacts_by_email[ci.value.downcase]}
                         .uniq

          if contacts.size > 1
            error!(message: "More than one SF contact (#{contacts.map(&:id).join(', ')}) " \
                            "for user #{user.id}")
          else
            contact = contacts.first
            cache_contact_data_in_user!(contact, user)
          end
        rescue StandardError => ee
          error!(exception: ee, user: user)
        end
      end
    end