Draisine
A bi-directional syncing solution for Salesforce and ActiveRecord.
Gem overall design is heavily inspired by InfoTech's salesforce_ar_sync gem, but with focus on clearer and more modular code.
Dependencies
- Rails 4.2+ (for ActiveJob)
- databasedotcom (for salesforce connections)
Installation and configuration
After you've got the gem installed, you will need to setup the salesforce client. For example:
sf_client = Databasedotcom::Client.new("config/databasedotcom.yml")
sf_client.authenticate :username => <username>, :password => <password>
Draisine.salesforce_client = sf_client
You will also need to have your organization id set up:
Draisine.organization_id = '123456789012345678'
Use this tool to convert your 15-char org id into 18-char.
Usage
Draisine adds a salesforce_syncable
macro to ActiveRecord models, used like this:
class Lead < Salesforce::Model
salesforce_syncable synced_attributes: [:FirstName, :LastName, ...],
mapping: { 'FirstName' => 'first_name', 'LastName' => 'last_name' },
operations: [:outbound_create, :outbound_update, :outbound_delete, :inbound_update, :inbound_delete],
salesforce_object_name: 'Lead',
sync: true
end
Your model class must have salesforce_id
string column for everything to work.
Available options
salesforce_object_name (String)
Self-explanatory. Defaults to the class name.
synced_attributes (Array[Symbol|String], required)
List of all Salesforce attributes that are required to be synced. If your ActiveRecord attributes should have different names, you can remap them later.
mapping (Hash[String => String])
sync (Boolean, default: true)
When set to true, all jobs are launched inline (via #perform_now
), otherwise, they are set to perform as soon as workers get to them (#perform_later
).
operations (Array[Symbol])
List of operations that must be synced with Salesforce.
Available operations: [:outbound_create, :outbound_update, :outbound_delete, :inbound_update, :inbound_delete]
non_audited_attributes (Array[Symbol|String])
Setting up outbound messages
In the left sidebar of salesforce interface, choose Create -> Workflow & Approvals -> Workflow Rules. Then click "New Rule". The rest is more or less self-explanatory. You would want to have all the necessary fields attached to your outbound message.
Assuming you mount draisine engine to /salesforce
, endpoint url would be /salesforce/sf_soap/lead
(for Lead object). Make sure you use full proper URL since salesforce will not follow redirects.
You can check out status for the latest sent messages in the Monitoring -> Outbound Messages section.
Handling special object types, e.g. LeadHistory
Some object types in salesforce are not directly user-editable and can't be set up to send outbound messages. One example of such object is LeadHistory. That means you'll have to poll them yourselves. Easiest way to do so is sort by Id
both on your model (also known as salesforce_id
) and at salesforce and get only the records with Id > max(salesforce_id).
Handling inbound deletes
Salesforce only sends outbound messages for record creates and updates, to sync deletes you'll have to go extra mile. You'll need to create a custom object, called Deleted_Object
that has two fields: Object_Id (text (18))
and Object_Type (text (128))
and a trigger for every observed model that creates an instance of such object after every delete. Then you'll need to setup outbound messaging, like for a normal model, but use /sf_soap/delete
instead of /sf_soap/<modelname>
for endpoint.
See a trigger example and a corresponding test class.
How to create trigger on your production instance
You might notice that unlike your sandbox instance, your production instance doesn't have "new trigger" button. Congratulations and welcome to Salesforce! You can't create new triggers on production instances directly, you'll have to use something called inbound/outbound change sets. In a nutshell, it's a protocol for generic object exchange between salesforce instances. Long story short, you'll need to export your apex trigger with its test class from sandbox to production instance.
To do so, go Deploy -> Outbound Change Set -> New -> etc etc etc.
If you export Deleted Object this way, don't forget to add custom fields to your change set. Also you must add test coverage for your trigger to the changeset or it won't apply. Good luck.
Error handling
You can setup handling most transient errors using Draisine.job_error_handler = proc {|exception, job, arguments| }
setter. It will be called every time a job, such as InboundUpdateJob
fails with any error.
Roadmap
ActiveRecord plugin and hooksActiveRecord -> Salesforce synchronization (outbound creates, updates, deletes)ActiveJob delayed jobsSalesforce -> ActiveRecord inbound updatesSalesforce -> ActiveRecord inbound deletesError handling inside delayed jobsAuditingMigration generatorConflict resolution- Use restforce instead of / alongside databasedotcom
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/chloeandisabel/draisine.
License
The gem is available as open source under the terms of the MIT License.