meta/lib/recycledcloud/odoo.ex
Timothée Floure 1133025016
Make Odoo GenServer more resilient
i.e. do not crash if Odoo is not available or configuration not set
2021-01-20 09:56:12 +01:00

110 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