151 lines
4.0 KiB
Elixir
151 lines
4.0 KiB
Elixir
defmodule MicrosoftTranslator.Client do
|
|
@empty_state %{data: [], done: false}
|
|
@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({:ok, %{data: body}}, api_method) do
|
|
Jason.decode!(body, keys: :atoms)
|
|
|> transform_response(api_method)
|
|
end
|
|
|
|
defp transform_response(response, :detect) do
|
|
result =
|
|
response
|
|
|> List.first()
|
|
|> Map.fetch(:language)
|
|
|
|
case result do
|
|
{:ok, language} -> %{languageCode: language}
|
|
end
|
|
end
|
|
|
|
defp transform_response(response, :translate) do
|
|
result =
|
|
response
|
|
|> List.first()
|
|
|> Map.take([:translations])
|
|
end
|
|
|
|
defp transform_response(response, :languages) do
|
|
%{dictionary: dictionary} = response
|
|
|
|
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) do
|
|
method = @api_methods[api_method].method
|
|
path = @base_path <> @api_methods[api_method].path <> params
|
|
|
|
with {:ok, conn} <- Mint.HTTP.connect(:https, @base_host, 443),
|
|
{:ok, conn, _ref} <- Mint.HTTP.request(conn, method, path, headers, body) do
|
|
handle_response(conn, @empty_state)
|
|
end
|
|
end
|
|
|
|
defp handle_response(conn, state) do
|
|
receive do
|
|
message ->
|
|
case Mint.HTTP.stream(conn, message) do
|
|
{:ok, conn, responses} ->
|
|
case Enum.reduce(responses, state, &handle_res/2) do
|
|
# Loop ends here
|
|
%{done: true} = state -> {:ok, state}
|
|
%{done: false} = state -> handle_response(conn, state)
|
|
end
|
|
|
|
{:error, _, reason, _} ->
|
|
{:error, reason}
|
|
|
|
:unknown ->
|
|
exit({:unexpected, message})
|
|
end
|
|
end
|
|
end
|
|
|
|
defp handle_res({:status, _, status}, state),
|
|
do: Map.put(state, :status, status)
|
|
|
|
defp handle_res({:headers, _, headers}, state),
|
|
do: Map.put(state, :headers, headers)
|
|
|
|
defp handle_res({:data, _, data}, state),
|
|
do: Map.update!(state, :data, fn acc -> [data | acc] end)
|
|
|
|
defp handle_res({:done, _}, state) do
|
|
Map.update!(state, :data, fn acc ->
|
|
acc
|
|
|> Enum.reverse()
|
|
|> Enum.join("")
|
|
end)
|
|
|> Map.put(:done, true)
|
|
end
|
|
end
|