111 lines
3.2 KiB
Elixir
111 lines
3.2 KiB
Elixir
|
defmodule RecycledCloud.Odoo do
|
||
|
use GenServer
|
||
|
require Logger
|
||
|
|
||
|
@common_endpoint "/xmlrpc/2/common"
|
||
|
@object_endpoint "/xmlrpc/2/object"
|
||
|
|
||
|
# Odoo 14.0 API is documented at https://www.odoo.com/documentation/14.0/webservices/odoo.html
|
||
|
|
||
|
defp get_odoo_config(key) do
|
||
|
Application.get_env(:recycledcloud, :odoo, []) |> Keyword.get(key)
|
||
|
end
|
||
|
|
||
|
defp post!(call, endpoint) do
|
||
|
url = get_odoo_config(:server) <> endpoint
|
||
|
opts = [proxy_auth: {get_odoo_config(:user), get_odoo_config(:secret)}]
|
||
|
headers = []
|
||
|
body = call |> XMLRPC.encode!
|
||
|
|
||
|
response = HTTPoison.post!(url, body, headers, opts).body |> XMLRPC.decode!
|
||
|
case response do
|
||
|
%{fault_code: _, fault_string: err} -> {:error, err}
|
||
|
%{param: result} -> {:ok, result}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
defp authenticate do
|
||
|
auth_params = [
|
||
|
get_odoo_config(:database),
|
||
|
get_odoo_config(:user),
|
||
|
get_odoo_config(:secret),
|
||
|
nil
|
||
|
]
|
||
|
|
||
|
auth_response = %XMLRPC.MethodCall{method_name: "authenticate", params: auth_params}
|
||
|
|> post!(@common_endpoint)
|
||
|
case auth_response do
|
||
|
{:ok, false} -> {:error, "Could not authenticate against Odoo."}
|
||
|
{:ok, uid} -> {:ok, uid}
|
||
|
{:error, err} -> {:error, err}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
defp check_odoo_config(key) do
|
||
|
case get_odoo_config(key) do
|
||
|
nil ->
|
||
|
Logger.warn("Odoo #{key} configuration is missing.")
|
||
|
false
|
||
|
_value -> true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def start_link(_) do
|
||
|
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||
|
end
|
||
|
|
||
|
def init(:ok) do
|
||
|
# Make sure Odoo configuration is defined.
|
||
|
conf_ok? = for key <- [:server, :database, :user, :secret], reduce: true do
|
||
|
acc -> acc && check_odoo_config(key)
|
||
|
end
|
||
|
|
||
|
if conf_ok? do
|
||
|
case authenticate() do
|
||
|
{:ok, uid} ->
|
||
|
# Print Odoo version on console (not quite useful...).
|
||
|
version_call = %XMLRPC.MethodCall{method_name: "version", params: []} |> post!(@common_endpoint)
|
||
|
{:ok, %{"server_version" => version}} = version_call
|
||
|
Logger.info("Successfuly authenticated against Odoo #{version} at #{get_odoo_config(:server)}")
|
||
|
|
||
|
{:ok, uid}
|
||
|
{:error, err} ->
|
||
|
Logger.warning("Failed to authenticate against Odoo: #{inspect(err)}")
|
||
|
|
||
|
# We do not fail here since we do not want to hinder the application's
|
||
|
# main supervisor if Odoo is down.
|
||
|
{:ok, nil}
|
||
|
end
|
||
|
else
|
||
|
Logger.warning("Failed to authenticate against Odoo: missing configuration!")
|
||
|
|
||
|
{:ok, nil}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def handle_call({method, params}, _from, uid) do
|
||
|
case uid do
|
||
|
# uid is nil if start_link's init/1 call failed.
|
||
|
# ... we try to authenticate once again!
|
||
|
nil ->
|
||
|
case init(:ok) do
|
||
|
{:ok, new_uid} -> handle_call({method, params}, nil, new_uid)
|
||
|
err -> {:reply, err, nil}
|
||
|
end
|
||
|
_ ->
|
||
|
call = %XMLRPC.MethodCall{
|
||
|
method_name: method,
|
||
|
params: [get_odoo_config(:database), uid, get_odoo_config(:secret) | params]
|
||
|
}
|
||
|
result = call |> post!(@object_endpoint)
|
||
|
|
||
|
{:reply, result, uid}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def query(params), do: execute_kw(params)
|
||
|
defp execute_kw(params) do
|
||
|
GenServer.call(__MODULE__, {"execute_kw", params})
|
||
|
end
|
||
|
end
|