Awesome
Forcex
Elixir library for interacting with the Force.com REST API.
Usage
Add Forcex to your dependency list
defp deps do
[ {:forcex, "~> 0.4"}
]
end
At compile time Forcex will query the Force.com REST API and generate modules for all the SObjects you have configured and permission to see.
If you see a warning like
23:37:02.057 [warn] Cannot log into SFDC API. Please ensure you have Forcex properly configured.
Got error code 400 and message %{"error" => "invalid_client_id", "error_description" => "client identifier invalid"}
You will need to configure Forcex, as noted below, and then explicitly recompile Forcex
$ mix deps.clean forcex
$ mix deps.compile forcex
You can have Forcex generate modules at compile time using the accompanying Mix task.
$ mix compile.forcex
This can also be invoked automatically by adding Forcex to your project's compilers in mix.exs
compilers: [:forcex] ++ Mix.compilers,
Bulk API Usage
Forcex has an example Bulk API query job controller. Here's roughly how that can work.
client = Forcex.Bulk.Client.login
[
"Account",
"Campaign",
"Contact",
"Lead",
"Opportunity",
"OpportunityLineItem",
]
|> Enum.map(fn sobject -> {sobject, ["select Id, Name from #{sobject}"]} end)
|> Enum.map(fn {sobject, queries} ->
Forcex.Bulk.JobController.start_link({:query, sobject, queries, client}) end)
Configuration
The Forcex.Client
is configured to read login information either from
application configuration:
config :forcex, Forcex.Client,
username: "user@example.com",
password: "my_super_secret_password",
security_token: "EMAILED_FROM_SALESFORCE",
client_id: "CONNECTED_APP_OAUTH_CLIENT_ID",
client_secret: "CONNECTED_APP_OAUTH_CLIENT_SECRET"
or these environment variables:
SALESFORCE_USERNAME
SALESFORCE_PASSWORD
SALESFORCE_SECURITY_TOKEN
SALESFORCE_CLIENT_ID
SALESFORCE_CLIENT_SECRET
HTTPoison request-specific options may also be configured:
config :forcex, :request_options,
timeout: 20000,
recv_timeout: :infinity
For steps on how to create a Connected App with OAuth keys and secrets, please see the Force.com REST API section on Connected Apps.
Currently, to be fully functional, the Forcex.Client
must both login
and
locate_services
.
Pagination of results is entirely manual at the moment.
client = Forcex.Client.login |> Forcex.Client.locate_services
first_page = Forcex.query("select Id, Name from Account order by CreatedDate desc", client)
second_page = first_page |> Map.get(:nextRecordsUrl) |> Forcex.get(client)
Further Configuration
Forcex allows additional configuration of API endpoint and API version via the
%Forcex.Client{}
struct. You may also use this mechanism if you have a
grant_type
other than password.
This example shows how to use both an older API version and the SalesForce sandbox API.
Forcex.Client.default_config
|> Forcex.Client.login(%Forcex.Client{endpoint: "https://test.salesforce.com", api_version: "34.0"})
Testing
Make sure dependencies are installed
mix deps.get
Tests can be run with
mix test
Tests can be run automatically when files change with
mix test.watch --stale
Tests mock the api calls to the Salesforce API using Mox to set expectations on
Forcex.Api.MockHttp.raw_request
. To know what to put in a mock response just
run the client in iex
and look for the debug logging response from http.ex.
Make sure to scrub any responses for sensitive data before including them
in a commit.
Example assuming environment variables are in place with login info
% iex -S mix
iex(1)> client = Forcex.Client.login |> Forcex.Client.locate_services
14:40:27.858 file=forcex/lib/forcex/api/http.ex line=19 [debug] Elixir.Forcex.Api.Http.raw_request response=%{access_token: "redacted",...
14:40:28.222 file=forcex/lib/forcex/api/http.ex line=19 [debug] Elixir.Forcex.Api.Http.raw_request response=%{process: "/services/data/v43.0/process", search...
iex(2)> Forcex.query("select Id, Name from Account order by CreatedDate desc", client)
14:43:05.896 file=forcex/lib/forcex/api/http.ex line=19 [debug] Elixir.Forcex.Api.Http.raw_request response=%{done: false, nextRecordsUrl: "/services/data/v4
Just take the data after response=
and throw it in a Mox expectation. See
existing tests for full examples
response = %{access_token: "redacted"}
Forcex.Api.MockHttp
|> expect(:raw_request, fn(:get, ^expected_url, _, ^auth_header, _) -> response end)
Current State
See https://www.salesforce.com/us/developer/docs/api_rest/
- List API versions available
- Login (Username/Password/Client Key/Client Secret)
- Login (Web Server OAuth)
- Login (User-Agent OAuth)
- OAuth Refresh Token
- Resources by Version
- Limits
- Describe Global
- SObject Basic Information
- SObject Describe
- SObject Get Deleted
- SObject Get Updated
- SObject Named Layouts
- SObject Rows
- SObject Rows by External ID
- SObject ApprovalLayouts
- SObject CompactLayouts
- SObject Layouts
- SObject Blob Retrieve
- SObject Quick Actions
- SObject Suggested Articles for Case
- SObject User Password
- AppMenu
- Compact Layouts
- FlexiPage
- Process Approvals
- Process Rules
- Query
- QueryAll
- Quick Actions
- Search
- Search Scope and Order
- Search Result Layouts
- Recently Viewed Items
- Search Suggested Article Title Matches
- Tabs
- Themes
License
MIT License, see LICENSE