Code Walhalla ... Avatar

Sencha App with Salesforce API using a Sinatra Proxy

In my last article, I have presented Sencha Framework and some advantages this product got over other Mobile solutions. Convinced that I had a wonderful tool into my hands I have decided to create a simple app. The aim is to have two tabs. First one with an intro page, second one with a list of Leads retrieved through the Salesforce REST API. The list must be sorted and provide basic interaction by displaying additional details of a selected Lead.

As soon as you start playing with mobile application and a remote API you have to face a common javascript limitation: the Same Origin Policy. I invite you to read the Wikipedia article if you want to know more about that rule. Briefly, “The policy permits scripts running on pages originating from the same site to access each other’s methods and properties with no specific restrictions, but prevents access to most methods and properties across pages on different sites”. Said differently, you’re cute javascript mobile application hosted under xyz.com domain _can not_ request any data from salesforce.com domain. Such a shame but so imperative to keep the web safer.

If you browse the web you’ll find a couple of way to load nevertheless remote Data. Here is a quick options list to introduce you to possible solutions or to refresh your mind :

  • Proxy your request: a common pattern to mimic same origin requests is to proxy calls from your javascript application using a service hosted on your initial domain. Pat Patterson released a modified version of Simple proxy for Force.com. Really useful but so PHP for me :)
  • Host your Sencha app on Force.com: right before Dreamforce 12, the Sencha Team published a really detailled 2-parts blog post explaining how to create a Sencha App direclty on Force.com. As a direct consequence, your application is generated from Force.com, is hosted on a Force.com site and can provide Salesforce data straight into the Sencha App. 
  • Implement CORS: CORS (Cross-Origin Resource Sharing) is a recent solution to allow cross domain requests. That solution is robust and standardized but unfortunately requires you got the ability to white list you originating domain on the target server. That’s not the case with Salesforce. 
  • Implement JSONP: JsonP is a way to perform requests on a remote server. That option is limited to GET and implies that you implement a simple “service” (a js function wrapping your call) on the remote server.

You understood that in a Salesforce context you can either use a Proxy or implement your Sencha app on a Force.com site. No other choice. Not really :)

As a Ruby developer, I finally came with an Hybrid solution: build an endpoints service with Sinatra. Sinatra is a perfect companion to build simple web services. Mixed with databasedotcom gem to easily access Salesforce REST API, you can write in less than 50 lines a service able to proxy your requests. 

The main advantage of that solution is that, before sending results back to your Sencha App, you can

  • preprocess data in Ruby: computed fields, grouping, sorting data
  • perform caching: include a redis cache or a standard caching solution
  • mix data from various sources
  • reduce payload size reducing json to its strict minimum

Furthemore, the Sencha App is served by Sinatra App which embed it. To clarify that here is the structure of the App folder:

image

You can see that the Sencha app is under the asset/ folder of the sinatra app. The Sinatra app 3 main actions are:

  • serve an index page for Salesforce Oauth session opening and manage sessions
  • after a successful authentication, redirect the user to the sencha app
  • serve data from Salesforce REST API through the /leads.json URL 
Bellow you can see the Sinatra app code. It’s divided into 5 sections:
  1. Load libs and gems
  2. Config the app. We use Rack session cookies to store the token and connection to Salesforce ORG , path to assets (where the sencha app code stands) is defined and we setup the configuration for oauth based on a salesforce.yml file
  3. Basic routing to respond to / and /home. /home view load the Sencha app
  4. OAuth callback Management
  5. Json Data Store, the homemade proxy collecting Leads from Salesforce REST API and formatting the output json
#!/usr/bin/env ruby -I ../lib -I lib
  # SECTION 1 : load libs and gems
  require 'sinatra'
  require "sinatra/content_for"
  require 'sinatra/json'
  require 'haml'
  require 'databasedotcom'
  require 'yaml'
  require 'omniauth'
  require 'omniauth-salesforce'

  # SECTION 2: config the app
  use Rack::Session::Cookie
  set :public_folder, File.dirname(__FILE__) + '/assets'
  config = YAML.load_file("config/salesforce.yml") rescue {}
  use OmniAuth::Builder do
    provider :salesforce, config["client_id"], config["client_secret"]
  end

  # SECTION 3: basic routing for / and /home 
  get '/' do
    haml :intro
  end
  get '/home' do
    haml :home
  end

  # SECTION 4: OAuth Callback management
  get '/auth/salesforce/callback' do
     session[:token] = request.env['omniauth.auth']['credentials']['token']
    config = YAML.load_file("config/salesforce.yml") rescue {}
    dbdc = Databasedotcom::Client.new(:client_id => config["client_id"], :client_secret => config["client_secret"])
   dbdc.authenticate :token => session[:token], :instance_url => "http://na14.salesforce.com"
    session['client'] = dbdc
    redirect '/home'
  end

  # SECTION 5: Json Data store for the Sencha app
  get '/leads.json' do
    content_type :json
    session['client'].materialize('Lead')
    leads = Lead.all
    leads.collect! { |obj| {
                        :id    => obj.Id,
                        :name  => obj.LastName,
                        :email => obj.Email}
                      }.to_json
  end

You can access that sample application on Github: https://github.com/vzmind/sencha-salesforce

Next article part of that Sencha serie will be about the Sencha code itself and how to generate a standalone iphone app with offline capabilities out of that example.

Replies

Likes

  1. abseo-blog reblogged this from vzmind-blog
  2. socialcloudtech reblogged this from vzmind-blog and added:
    #sinatra #salesforce #ruby
  3. vzmind-blog posted this

 

Reblogs