2020-06-30 07:48:19 +03:00

131 lines
3.7 KiB
Elixir

defmodule MicrosoftTranslator.Client do
@attempt_delay 3_000
@base_host "api.cognitive.microsofttranslator.com"
@base_path "/"
@api_methods %{
languages: %{
method: :get,
path: "languages"
},
detect: %{
method: :post,
path: "detect"
},
translate: %{
method: :post,
path: "translate"
}
}
@api_default_params %{
"api-version" => "3.0"
}
@availaible_api_methods Map.keys(@api_methods)
@default_headers [{"Content-Type", "application/json"}]
alias MicrosoftTranslator.Auth
def call(api_method \\ :languages, args \\ %{})
when api_method in @availaible_api_methods and is_map(args) do
body = generate_body(api_method, args)
headers = generate_headers()
params = generate_params(api_method, args)
api_method
|> fetch(headers, body, params)
|> parse(api_method)
end
defp generate_headers(headers \\ [])
defp generate_headers(header) when is_tuple(header),
do: generate_headers([header])
defp generate_headers(headers) when is_list(headers),
do: @default_headers ++ Auth.authorization_headers() ++ headers
defp generate_params(:translate, params) do
params
|> Map.take([:to, :from])
|> params_to_string()
end
defp generate_params(api_method, _params) when api_method in [:detect, :languages],
do: params_to_string(%{})
defp params_to_string(params_map) do
params_map
|> Map.merge(@api_default_params)
|> Enum.reduce("", fn {key, value}, acc -> acc <> "&" <> "#{key}" <> "=" <> "#{value}" end)
|> String.replace_leading("&", "?")
end
defp generate_body(method, params), do: transform_body(method, params) |> Jason.encode!()
defp transform_body(:languages, _), do: %{}
defp transform_body(method, params) when method in [:detect, :translate],
do: [%{text: Map.fetch!(params, :text)}]
defp transform_body(_, params), do: params
defp parse(%{body: body}, api_method) do
Jason.decode!(body, keys: :atoms)
|> process_response(api_method)
end
defp parse(%Mojito.Error{reason: :timeout}, api_method) do
%{error: %{code: :timeout, message: "timeout"}}
|> process_response(api_method)
end
defp process_response(response, api_method) do
case response do
%{error: %{code: code, message: message}} ->
%{error: %{code: code, message: message}}
list when is_list(list) and api_method in ~w(detect translate)a ->
list
|> List.first()
|> transform_response(api_method)
map when is_map(map) and api_method in ~w(languages)a ->
transform_response(map, api_method)
end
end
defp transform_response(response, :detect) when is_map(response) do
case Map.fetch(response, :language) do
{:ok, language} -> %{languageCode: language}
end
end
defp transform_response(response, :translate) when is_map(response),
do: Map.take(response, ~w(translations)a)
defp transform_response(%{dictionary: dictionary}, :languages) do
dictionary
|> Enum.map(fn {key, %{name: name, nativeName: native, dir: dir}} ->
%{code: "#{key}", name: name, nativeName: native, dir: dir}
end)
end
def fetch(api_method, headers, body, params, attempt \\ 0) do
method = @api_methods[api_method].method
url = "https://#{@base_host}#{@base_path}#{@api_methods[api_method].path}#{params}"
case Mojito.request(method, url, headers, body) do
{:ok, response} ->
response
{:error, %Mojito.Error{reason: :timeout} = response} ->
if attempt < 2 do
:timer.sleep(@attempt_delay)
IO.inspect(url, label: "Attempt #{attempt + 1}")
fetch(api_method, headers, body, params, attempt + 1)
else
response
end
end
end
end