Skip to content

Instantly share code, notes, and snippets.

@rilian
Forked from Aupajo/restforce.md
Created May 11, 2019 14:48
Show Gist options
  • Save rilian/0057d1898a9c311e38dddba46a752017 to your computer and use it in GitHub Desktop.
Save rilian/0057d1898a9c311e38dddba46a752017 to your computer and use it in GitHub Desktop.

Revisions

  1. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ You can hook into authentication success by passing an `authentication_callback`
    If you're happy relying on an internal API call, you can make sure your token is fresh by calling:

    ```ruby
    client.authenticate!
    restforce_client.authenticate!
    ```

    ## Walkthrough
  2. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,11 @@

    You can hook into authentication success by passing an `authentication_callback` to the client.

    There's no simple way to issue a straight OAuth token refresh. You always have to make some API request.
    If you're happy relying on an internal API call, you can make sure your token is fresh by calling:

    ```ruby
    client.authenticate!
    ```

    ## Walkthrough

  3. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    You can hook into authentication success by passing an `authentication_callback` to the client.

    There's no simple way to issue a straight OAuth token refresh. It's assumed you're trying to make a RESTful request.
    There's no simple way to issue a straight OAuth token refresh. You always have to make some API request.

    ## Walkthrough

  4. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 22 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions restforce.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,11 @@
    ## Summary

    You can hook into authentication success by passing an `authentication_callback` to the client.

    There's no simple way to issue a straight OAuth token refresh. It's assumed you're trying to make a RESTful request.

    ## Walkthrough

    Typical client instantiation looks like this:

    ```ruby
    @@ -150,3 +158,17 @@ end

    Interestingly, `connection` in this case refers to a separate `Faraday::Connection` than we saw previously.

    The reraising in the `rescue` block is for the verb method back in `Restforce::Concerns::Verbs`:

    ```ruby
    ...
    rescue Restforce::UnauthorizedError
    if retries > 0
    retries -= 1
    connection.url_prefix = options[:instance_url]
    retry
    end
    raise
    end
    ...
    ```
  5. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 22 additions and 1 deletion.
    23 changes: 22 additions & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -128,4 +128,25 @@ end

    It inherits from `Restforce::Middleware::Authentication`, which works like this:

    ```
    ```ruby
    def call(env)
    @app.call(env)
    rescue Restforce::UnauthorizedError
    authenticate!
    raise
    end

    def authenticate!
    response = connection.post '/services/oauth2/token' do |req|
    req.body = encode_www_form(params)
    end
    raise Restforce::AuthenticationError, error_message(response) if response.status != 200
    @options[:instance_url] = response.body['instance_url']
    @options[:oauth_token] = response.body['access_token']
    @options[:authentication_callback].call(response.body) if @options[:authentication_callback]
    response.body
    end
    ```

    Interestingly, `connection` in this case refers to a separate `Faraday::Connection` than we saw previously.

  6. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 51 additions and 3 deletions.
    54 changes: 51 additions & 3 deletions restforce.md
    Original file line number Diff line number Diff line change
    @@ -54,8 +54,6 @@ end
    builder.use authentication_middleware, self, options if authentication_middleware
    # Sets the oauth token in the headers.
    builder.use Restforce::Middleware::Authorization, self, options
    # Ensures the instance url is set.
    builder.use Restforce::Middleware::InstanceURL, self, options
    ...
    # Raises errors for 40x responses.
    builder.use Restforce::Middleware::RaiseError
    @@ -64,9 +62,21 @@ end
    end
    ```

    `authentication_middleware` is a module that's determined at run time:

    ```ruby
    def authentication_middleware
    if username_password?
    Restforce::Middleware::Authentication::Password
    elsif oauth_refresh?
    Restforce::Middleware::Authentication::Token
    end
    end
    ```

    Let's look at the middleware.

    ### [`Restforce::Middleware::Authorization`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/middleware/authorization.rb)
    #### [Restforce::Middleware::Authorization](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/middleware/authorization.rb)

    This simply adds the `OAuth` header to each request:

    @@ -81,3 +91,41 @@ def token
    end
    ```

    #### Restforce::Middleware::RaiseError

    This is a type of `Faraday::Response` middleware (it acts after a response has been made).

    ```ruby
    def on_complete(env)
    @env = env
    case env[:status]
    when 404
    raise Faraday::Error::ResourceNotFound, message
    when 401
    raise Restforce::UnauthorizedError, message
    when 413
    raise Faraday::Error::ClientError.new("HTTP 413 - Request Entity Too Large", env[:response])
    when 400...600
    raise Faraday::Error::ClientError.new(message, env[:response])
    end
    end
    ```

    The key part to note here is that a `Restforce::UnauthorizedError` will be raised if a 401 response is returned.

    #### Restforce::Middleware::Authentication::Token

    This is an almost empty class, that just defines a `params` method:

    ```ruby
    def params
    { :grant_type => 'refresh_token',
    :refresh_token => @options[:refresh_token],
    :client_id => @options[:client_id],
    :client_secret => @options[:client_secret] }
    end
    ```

    It inherits from `Restforce::Middleware::Authentication`, which works like this:

    ```
  7. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 19 additions and 1 deletion.
    20 changes: 19 additions & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,22 @@ end
    ...
    end
    end
    ```
    ```

    Let's look at the middleware.

    ### [`Restforce::Middleware::Authorization`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/middleware/authorization.rb)

    This simply adds the `OAuth` header to each request:

    ```ruby
    def call(env)
    env[:request_headers][AUTH_HEADER] = %(OAuth #{token})
    @app.call(env)
    end

    def token
    @options[:oauth_token]
    end
    ```

  8. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 25 additions and 2 deletions.
    27 changes: 25 additions & 2 deletions restforce.md
    Original file line number Diff line number Diff line change
    @@ -7,9 +7,13 @@ client = Restforce.new :oauth_token => 'oauth token', :instance_url => 'instanc
    Any method to interact with Salesforce (`query`, `find`, `search`, etc.) generally wrap around `api_get`:

    ```ruby
    def query(soql)
    response = api_get 'query', :q => soql
    mashify? ? response.body : response.body['records']
    end
    ```

    defined in [`Restforce::Concerns::Verbs`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/verbs.rb):
    `api_get` is defined in [`Restforce::Concerns::Verbs`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/verbs.rb):

    ```ruby
    def define_api_verb(verb)
    @@ -20,6 +24,8 @@ def define_api_verb(verb)
    end
    ```

    `send` is calling `get`, which is defined in the same module:

    ```ruby
    def define_verb(verb)
    define_method verb do |*args, &block|
    @@ -38,5 +44,22 @@ def define_verb(verb)
    end
    ```

    This simply converts the given request path to a full URL (with the Salesforce)
    `connection` comes from [`Restforce::Concerns::Connection`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/connection.rb), and is essentially just a Faraday connection automatically configured with a bunch of Middleware:

    ```ruby
    def connection
    @connection ||= Faraday.new(options[:instance_url], connection_options) do |builder|
    ...
    # Handles reauthentication for 403 responses.
    builder.use authentication_middleware, self, options if authentication_middleware
    # Sets the oauth token in the headers.
    builder.use Restforce::Middleware::Authorization, self, options
    # Ensures the instance url is set.
    builder.use Restforce::Middleware::InstanceURL, self, options
    ...
    # Raises errors for 40x responses.
    builder.use Restforce::Middleware::RaiseError
    ...
    end
    end
    ```
  9. @Aupajo Aupajo revised this gist Mar 27, 2015. 1 changed file with 35 additions and 1 deletion.
    36 changes: 35 additions & 1 deletion restforce.md
    Original file line number Diff line number Diff line change
    @@ -4,5 +4,39 @@ Typical client instantiation looks like this:
    client = Restforce.new :oauth_token => 'oauth token', :instance_url => 'instance url'
    ```

    Any method to interact with Salesforce (`query`, `find`, `search`, etc.) generally wrap around `api_get`, defined in [`Restforce::Concerns::Verbs`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/verbs.rb).
    Any method to interact with Salesforce (`query`, `find`, `search`, etc.) generally wrap around `api_get`:

    ```ruby
    ```

    defined in [`Restforce::Concerns::Verbs`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/verbs.rb):

    ```ruby
    def define_api_verb(verb)
    define_method :"api_#{verb}" do |*args, &block|
    args[0] = api_path(args[0])
    send(verb, *args, &block)
    end
    end
    ```

    ```ruby
    def define_verb(verb)
    define_method verb do |*args, &block|
    retries = options[:authentication_retries]
    begin
    connection.send(verb, *args, &block)
    rescue Restforce::UnauthorizedError
    if retries > 0
    retries -= 1
    connection.url_prefix = options[:instance_url]
    retry
    end
    raise
    end
    end
    end
    ```

    This simply converts the given request path to a full URL (with the Salesforce)

  10. @Aupajo Aupajo created this gist Mar 27, 2015.
    8 changes: 8 additions & 0 deletions restforce.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    Typical client instantiation looks like this:

    ```ruby
    client = Restforce.new :oauth_token => 'oauth token', :instance_url => 'instance url'
    ```

    Any method to interact with Salesforce (`query`, `find`, `search`, etc.) generally wrap around `api_get`, defined in [`Restforce::Concerns::Verbs`](https://github.com/ejholmes/restforce/blob/92b2a0041984d3b4b058e2b4ee337a375056b598/lib/restforce/concerns/verbs.rb).