2016-05-11

ElixirでforceのAPIを叩いてみた

Elixirのforce.com用ライブラリのforcexを使ってforce.comのAPIを叩いてみました。

設定

mixでプロジェクトを作成します
$ mix new sfdc --module SFDC
$ cd sfdc

configファイルを編集します。

# config/config.exs
use Mix.Config

config :forcex, Forcex.Client,
  username: "{username}",
  password: "{password}",
  security_token: "{security_token}",
  client_id: "{client_id}",
  client_secret: "{client_secret}"

config :forcex, :request_options,
  timeout: 20000,
  recv_timeout: :infinity

dependencyも変更

# mix.exs
defmodule SFDC.Mixfile do
  use Mix.Project

  def project do
    [app: :sfdc,
     version: "0.0.1",
     elixir: "~> 1.2",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  def application do
    [applications: [:logger, :forcex]]
  end

  defp deps do
    [{:forcex, "~> 0.3"}
    ]
  end
end

依存ライブラリを取得

$ mix deps.get

メインスクリプト↓

# {project_root}/main.exs
client = Forcex.Client.login |> Forcex.Client.locate_services
first_page = Forcex.query("select Id, Name from Account order by CreatedDate desc", client)
SFDC.show_record_name(first_page["records"])
if Map.has_key?(first_page, "nextRecordsUrl") do
  SFDC.next_query(client, first_page)
end

モジュール↓

# {project_root}/lib/sfdc.ex
defmodule SFDC do
  def next_query(client, current_page) do
    next_page = current_page |> Map.get("nextRecordsUrl") |> Forcex.get(client)
    show_record_name(next_page["records"])
    if Map.has_key?(next_page, "nextRecordsUrl") do
      next_query(client, next_page)
    end
  end

  def show_record_name([]) do
  end

  def show_record_name([head | tail]) do
    IO.puts head["Name"]
    show_record_name(tail)
  end
end

あとはプロジェクトルートでコンパイル&スクリプト実行すればOK。今回の例だと取引先名が標準出力に表示されます。

$ mix run main.exs

ちなみに、configを書かなくても以下の環境変数を設定して、接続先を決定することも出来ます。

接続環境とAPIバージョンの切り替え

issueを出したら対応していただきました!

Can we change the endpoint for salesforce? · Issue #7 · jeffweiss/forcex

こんな感じで使います。

client = Forcex.Client.default_config |> 
  Forcex.Client.login(%Forcex.Client{endpoint: "https://test.salesforce.com", api_version: "34.0"}) |> 
  Forcex.Client.locate_services

ハマりどころ

forcexというよりElixir及びmixの使い方のところでハマったので以下、備忘録

def application内の依存モジュールを入れないと以下のエラーが発生します。

** (exit) exited in: :gen_server.call(:hackney_manager, {:new_request, #PID<0.47.0>, 
#Reference<0.0.3.1489>, {:client, :undefined, {:metrics_ng, :metrics_dummy}, 
:hackney_ssl_transport, 'login.salesforce.com', 443, "login.salesforce.com", 
[recv_timeout: :infinity, connect_timeout: 20000], nil, nil, nil, true, 
:hackney_pool, :infinity, false, 5, false, 5, nil, nil, nil, :undefined, 
:start, nil, :normal, false, false, false, :undefined, false, nil, 
:waiting, nil, 4096, "", [], :undefined, nil, nil, nil, nil, :undefined, nil}}, :infinity)
    ** (EXIT) no process
    (stdlib) gen_server.erl:212: :gen_server.call/3
    src/hackney_manager.erl:66: :hackney_manager.init_request/1
    src/hackney_manager.erl:56: :hackney_manager.new_request/1
    src/hackney_connect.erl:184: :hackney_connect.socket_from_pool/4
    src/hackney_connect.erl:36: :hackney_connect.connect/5
    src/hackney.erl:328: :hackney.request/5
    lib/httpoison/base.ex:396: HTTPoison.Base.request/9
    lib/forcex.ex:3: Forcex.request!/5

configを適切に設定しないとclient_idが不正だよ、と以下のエラーが発生します

20:57:13.564 [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"}
** (BadMapError) expected a map, got: {400, [%{"errorCode" => "URL_NOT_RESET", 
"message" => "Destination URL not reset. The URL returned from login must be set"}]}
    (stdlib) :maps.find("query", {400, [%{"errorCode" => "URL_NOT_RESET", "message" =>
   "Destination URL not reset. The URL returned from login must be set"}]})
    (elixir) lib/map.ex:131: Map.get/3
    (forcex) lib/forcex.ex:99: Forcex.query/2
    main.exs:2: (file)
    (elixir) lib/code.ex:363: Code.require_file/2
このエントリーをはてなブックマークに追加