# Forcex

[![Build Status](](
[![ Version](](
[![Coverage Status](](

Elixir library for interacting with the REST API.

## Usage

Add Forcex to your dependency list
  defp deps do
    [ {:forcex, "~> 0.4"}

At compile time Forcex will query the 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

client = Forcex.Bulk.Client.login
|> sobject -> {sobject, ["select Id, Name from #{sobject}"]} end)
|> {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: "",
  password: "my_super_secret_password",
  security_token: "EMAILED_FROM_SALESFORCE",

or these environment variables:


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 [ REST API section on Connected Apps](

Currently, to be fully functional, the `Forcex.Client` must both `login` and

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.login(%Forcex.Client{endpoint: "", 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 --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/v41.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"}
    |> expect(:raw_request, fn(:get, ^expected_url, _, ^auth_header, _) -> response end)

## Current State


 - [x] List API versions available
 - [x] Login (Username/Password/Client Key/Client Secret)
 - [ ] Login (Web Server OAuth)
 - [ ] Login (User-Agent OAuth)
 - [ ] OAuth Refresh Token
 - [x] Resources by Version
 - [x] Limits
 - [x] Describe Global
 - [x] SObject Basic Information
 - [x] SObject Describe
 - [x] SObject Get Deleted
 - [x] SObject Get Updated
 - [ ] SObject Named Layouts
 - [x] SObject Rows
 - [x] SObject Rows by External ID
 - [ ] SObject ApprovalLayouts
 - [ ] SObject CompactLayouts
 - [ ] SObject Layouts
 - [x] SObject Blob Retrieve
 - [ ] SObject Quick Actions
 - [ ] SObject Suggested Articles for Case
 - [ ] SObject User Password
 - [ ] AppMenu
 - [ ] Compact Layouts
 - [ ] FlexiPage
 - [ ] Process Approvals
 - [ ] Process Rules
 - [x] Query
 - [x] QueryAll
 - [x] Quick Actions
 - [ ] Search
 - [ ] Search Scope and Order
 - [ ] Search Result Layouts
 - [x] Recently Viewed Items
 - [ ] Search Suggested Article Title Matches
 - [x] Tabs
 - [x] Themes

# License

MIT License, see LICENSE