Working only through API (IAM tokens disabled), translate (with glossary), detect and list — working
This commit is contained in:
parent
3fd3f2c444
commit
b3402f10d7
@ -1,41 +1,2 @@
|
|||||||
defmodule YandexTranslate do
|
defmodule YandexTranslate do
|
||||||
@iam_token_url "https://iam.api.cloud.yandex.net/iam/v1/tokens"
|
|
||||||
|
|
||||||
def get_key(type, key \\ Application.get_env(:yandex_translate, :private_key))
|
|
||||||
|
|
||||||
def get_key(:public, key),
|
|
||||||
do: get_key(:private, key) |> JOSE.JWK.to_public()
|
|
||||||
|
|
||||||
def get_key(:private, key),
|
|
||||||
do: fetch_key(key)
|
|
||||||
|
|
||||||
defp fetch_key("-----BEGIN" <> _ = key_content), do: JOSE.JWK.from_pem(key_content)
|
|
||||||
|
|
||||||
defp fetch_key(key_file), do: JOSE.JWK.from_pem_file(key_file)
|
|
||||||
|
|
||||||
def get_iam_token(),
|
|
||||||
do: Application.get_all_env(:yandex_translate) |> Map.new() |> get_iam_token()
|
|
||||||
|
|
||||||
def get_iam_token(%{service_account_id: iss, private_key: private_key, authorized_key_id: kid}) do
|
|
||||||
now = DateTime.utc_now() |> DateTime.to_unix()
|
|
||||||
jwk = get_key(:private, private_key) |> JOSE.JWK.merge(%{"kid" => kid})
|
|
||||||
|
|
||||||
# JSON Web Signature (JWS)
|
|
||||||
jws = %{
|
|
||||||
"alg" => "RS256",
|
|
||||||
"typ" => "JWT",
|
|
||||||
"kid" => kid
|
|
||||||
}
|
|
||||||
|
|
||||||
# JSON Web Token (JWT)
|
|
||||||
jwt =
|
|
||||||
JOSE.JWT.from(%{
|
|
||||||
"iss" => iss,
|
|
||||||
"exp" => now + 60 * 60,
|
|
||||||
"iat" => now,
|
|
||||||
"aud" => @iam_token_url
|
|
||||||
})
|
|
||||||
|
|
||||||
_signed = JOSE.JWT.sign(jwk, jws, jwt) |> JOSE.JWS.compact() |> elem(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
7
lib/yandex_translate/auth/api_key.ex
Normal file
7
lib/yandex_translate/auth/api_key.ex
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
defmodule YandexTranslate.Auth.ApiKey do
|
||||||
|
def get_auth_value() do
|
||||||
|
"Api-Key " <> get_api_key()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_api_key(), do: Application.get_env(:yandex_translate, :api_key)
|
||||||
|
end
|
15
lib/yandex_translate/auth/auth.ex
Normal file
15
lib/yandex_translate/auth/auth.ex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
defmodule YandexTranslate.Auth do
|
||||||
|
alias YandexTranslate.Auth.{ApiKey, IAMToken}
|
||||||
|
|
||||||
|
def get_config() do
|
||||||
|
Application.get_all_env(:yandex_translate) |> Map.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorization_value(config \\ get_config())
|
||||||
|
|
||||||
|
def authorization_value(%{private_key: _, authorized_key_id: _, service_account_id: _}),
|
||||||
|
do: IAMToken.get_auth_value()
|
||||||
|
|
||||||
|
def authorization_value(%{api_key: _}),
|
||||||
|
do: ApiKey.get_auth_value()
|
||||||
|
end
|
37
lib/yandex_translate/auth/iam_token.ex
Normal file
37
lib/yandex_translate/auth/iam_token.ex
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
defmodule YandexTranslate.Auth.IAMToken do
|
||||||
|
alias YandexTranslate.Auth.JWTTokenConfig
|
||||||
|
|
||||||
|
def get_auth_value() do
|
||||||
|
"Bearer " <> get_iam_token()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_iam_token() do
|
||||||
|
# Rewrite needed, signing process not worked. Fucking PS256 and hash length optoins on my opinion
|
||||||
|
# Pluged by old IAMToken
|
||||||
|
"CggVAgAAABoBMRKABDnO1uw1a7U-AIlRglhz8yQVQzXaKjXt1saXk5VDXHH41mlPpMpK3V9-GUgthtdposNYva9hj0DaaHE1YDZfBcZkiZU7T_45ilhFtouG5rARRP8E6GqR4-dMutrYo69XQ3Uj4-KPa0IkOa2HrCTJEePhDHGlz-iW_YnN0QXFIlKN7NMIrZQyYRJ1dvQExx29TARcy_OVWOO8234rU_Vi5BRBKXTfp7NFB589bMe6UhEoYTF-1PdOEtxDJ2DLMdqMiYw4BZnyhOwn81u8kAetHpC-R6kizR8P2omcMyZORk4zwMfgdWlDzAhrIGq4ev8aOpcQH2vH8xh8ZMTgqbRZ9rezOflEZ3bDdDVdcQWjMq4ZYABY1n278pzhucbiTZJl6uxxTXg9Ucbg7_9mpA8DOIBn72wx8dTmrJgXWTstSJ6aShpg_1VJE48gUSenO1AjXWnat9hcvhgrBGKdQFlmhqnrRTptnvhgdNctwvD_tjpjC9ZWa5pBPyE9pKdiZzYUkhSEKEpRKFAh7oGv4i0w5gCHDzQrQfrrMFxFUUPJCg8Hx0tUbLqMgF9sYXYS4vhJFWFtBl_x0dd3KMPy33Fg4oDxlbZwsolyrVI9QnT9Es2qXGp1Sw9TLMM3pAI8G8JOD7O8eTRCJalRrrFOgybbK8LjK7AKLBhI0MNtQs0A_QS-GnYKIGIxODQ5YTljMmQ4ZjRiNjQ5YTU1MzhhN2E1OTdkNDU3ELTgzugFGPSx0egFIj4KFGFqZTB1czQyMWdoNG9idmJwcW5iEg5leGNoYW5nZXJzNGRldioUYjFnbmw5N3VkNzltYXA3bmhnM2wwAjAFOAFQAVoA"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_jwt_token() do
|
||||||
|
signer = Joken.Signer.create("PS256", %{"pem" => get_rsa_key(:private)}, get_headers())
|
||||||
|
JWTTokenConfig.generate_and_sign(%{}, signer) |> elem(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_headers(kid \\ Application.get_env(:yandex_translate, :authorized_key_id)),
|
||||||
|
do: %{"kid" => kid}
|
||||||
|
|
||||||
|
defp get_rsa_key(type, key \\ Application.get_env(:yandex_translate, :private_key))
|
||||||
|
|
||||||
|
defp get_rsa_key(:private, key), do: fetch_key(key)
|
||||||
|
|
||||||
|
defp get_rsa_key(:public, key) do
|
||||||
|
get_rsa_key(:private, key)
|
||||||
|
|> JOSE.JWK.from_pem()
|
||||||
|
|> JOSE.JWK.to_public()
|
||||||
|
|> JOSE.JWK.to_pem()
|
||||||
|
|> elem(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_key("-----BEGIN" <> _ = key_content), do: key_content
|
||||||
|
|
||||||
|
defp fetch_key(key_file), do: File.read!(key_file) |> fetch_key()
|
||||||
|
end
|
15
lib/yandex_translate/auth/jwt_token_config.ex
Normal file
15
lib/yandex_translate/auth/jwt_token_config.ex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
defmodule YandexTranslate.Auth.JWTTokenConfig do
|
||||||
|
@iam_token_url "https://iam.api.cloud.yandex.net/iam/v1/tokens"
|
||||||
|
|
||||||
|
use Joken.Config
|
||||||
|
|
||||||
|
@impl Joken.Config
|
||||||
|
def token_config() do
|
||||||
|
default_claims(
|
||||||
|
iss: Application.get_env(:yandex_translate, :service_account_id),
|
||||||
|
default_exp: 60 * 60,
|
||||||
|
skip: [:jti, :nbf],
|
||||||
|
aud: Application.get_env(:yandex_translate, :iam_token_url) || @iam_token_url
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
106
lib/yandex_translate/client.ex
Normal file
106
lib/yandex_translate/client.ex
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
defmodule YandexTranslate.Client do
|
||||||
|
@base_host "translate.api.cloud.yandex.net"
|
||||||
|
@base_path "/translate/v2/"
|
||||||
|
@api_methods %{
|
||||||
|
listLanguages: "languages",
|
||||||
|
detectLanguage: "detect",
|
||||||
|
translate: "translate"
|
||||||
|
}
|
||||||
|
@availaible_api_methods Map.keys(@api_methods)
|
||||||
|
@default_headers [{"content-type", "application/json"}]
|
||||||
|
|
||||||
|
alias YandexTranslate.Auth
|
||||||
|
|
||||||
|
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: [{"authorization", Auth.authorization_value()} | @default_headers ++ headers]
|
||||||
|
|
||||||
|
defp generate_body(method, params) do
|
||||||
|
params_map = transform_body(method, params)
|
||||||
|
filter_params(method, params_map) |> Jason.encode!()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_body(:translate, params) do
|
||||||
|
params
|
||||||
|
|> transform_glossary()
|
||||||
|
|> transform_text()
|
||||||
|
end
|
||||||
|
|
||||||
|
# defp transform_body(:detectLanguage, params), do: params
|
||||||
|
|
||||||
|
defp transform_body(_, params), do: params
|
||||||
|
|
||||||
|
defp transform_glossary(%{glossary: glossary} = params) do
|
||||||
|
Map.put(params, :glossaryConfig, %{
|
||||||
|
glossaryData: %{
|
||||||
|
glossaryPairs: generate_glossary_pairs(glossary)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp transform_glossary(result), do: result
|
||||||
|
|
||||||
|
defp generate_glossary_pairs(glossary) when is_list(glossary),
|
||||||
|
do: glossary |> Enum.map(fn {from, to} -> %{sourceText: from, translatedText: to} end)
|
||||||
|
|
||||||
|
defp transform_text(%{texts: text} = params) when is_binary(text),
|
||||||
|
do: %{params | texts: [text]}
|
||||||
|
|
||||||
|
defp transform_text(%{text: text} = params) when is_binary(text),
|
||||||
|
do: Map.put(params, :texts, [text])
|
||||||
|
|
||||||
|
defp filter_params(method, params), do: params |> Map.take(valid_argument_keys(method))
|
||||||
|
|
||||||
|
defp valid_argument_keys(:detectLanguage), do: [:text, :languageCodeHints]
|
||||||
|
|
||||||
|
defp valid_argument_keys(:translate),
|
||||||
|
do: [:texts, :sourceLanguageCode, :targetLanguageCode, :glossaryConfig]
|
||||||
|
|
||||||
|
defp valid_argument_keys(_), do: []
|
||||||
|
|
||||||
|
def call(method \\ :listLanguages, args \\ %{})
|
||||||
|
when method in @availaible_api_methods and is_map(args) do
|
||||||
|
body = generate_body(method, args)
|
||||||
|
headers = generate_headers()
|
||||||
|
|
||||||
|
method
|
||||||
|
|> fetch(headers, body)
|
||||||
|
|> parse()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse(body), do: Jason.decode!(body)
|
||||||
|
|
||||||
|
defp fetch(method, headers, body) do
|
||||||
|
{:ok, conn} = Mint.HTTP.connect(:https, @base_host, 443)
|
||||||
|
|
||||||
|
{:ok, conn, request_ref} =
|
||||||
|
Mint.HTTP.request(
|
||||||
|
conn,
|
||||||
|
"POST",
|
||||||
|
@base_path <> @api_methods[method],
|
||||||
|
headers,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
|
||||||
|
receive do
|
||||||
|
message ->
|
||||||
|
{:ok, conn, responses} = Mint.HTTP.stream(conn, message)
|
||||||
|
|
||||||
|
body =
|
||||||
|
responses
|
||||||
|
|> Enum.filter(fn
|
||||||
|
{:data, ^request_ref, _} -> true
|
||||||
|
_ -> false
|
||||||
|
end)
|
||||||
|
|> Enum.map(fn {_, _, data} -> data end)
|
||||||
|
|
||||||
|
Mint.HTTP.close(conn)
|
||||||
|
|
||||||
|
body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,15 +0,0 @@
|
|||||||
# defmodule YandexTranslate.Token do
|
|
||||||
# @iam_token_url "https://iam.api.cloud.yandex.net/iam/v1/tokens"
|
|
||||||
|
|
||||||
# use Joken.Config
|
|
||||||
|
|
||||||
# @impl Joken.Config
|
|
||||||
# def token_config() do
|
|
||||||
# default_claims(
|
|
||||||
# iss: Application.get_env(:yandex_translate, :service_account_id),
|
|
||||||
# default_exp: 60 * 60,
|
|
||||||
# skip: [:jti, :nbf],
|
|
||||||
# aud: @iam_token_url
|
|
||||||
# )
|
|
||||||
# end
|
|
||||||
# end
|
|
6
mix.exs
6
mix.exs
@ -28,11 +28,11 @@ defmodule YandexTranslate.Mixfile do
|
|||||||
|
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
# {:joken, "~> 2.1.0"},
|
{:joken, "~> 2.1.0"},
|
||||||
# {:jose, "~> 1.9.0"},
|
# {:jose, "~> 1.9.0"},
|
||||||
{:jose, github: "potatosalad/erlang-jose", branch: "master"},
|
|
||||||
{:jason, "~> 1.1.2"},
|
{:jason, "~> 1.1.2"},
|
||||||
{:mojito, "~> 0.3.0"},
|
{:mint, "~> 0.3.0"},
|
||||||
|
{:castore, "~> 0.1.2"},
|
||||||
{:ex_doc, "~> 0.20.2", only: :dev},
|
{:ex_doc, "~> 0.20.2", only: :dev},
|
||||||
{:remix, "~> 0.0.2", only: :dev}
|
{:remix, "~> 0.0.2", only: :dev}
|
||||||
]
|
]
|
||||||
|
4
mix.lock
4
mix.lock
@ -9,12 +9,12 @@
|
|||||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"joken": {:hex, :joken, "2.1.0", "bf21a73105d82649f617c5e59a7f8919aa47013d2519ebcc39d998d8d12adda9", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"},
|
"joken": {:hex, :joken, "2.1.0", "bf21a73105d82649f617c5e59a7f8919aa47013d2519ebcc39d998d8d12adda9", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"jose": {:git, "https://github.com/potatosalad/erlang-jose.git", "80551fc36abab81b6fac798e112a9c1a644b60f1", [branch: "master"]},
|
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||||
"mint": {:hex, :mint, "0.2.1", "a2ec8729fcad5c8b6460e07dfa64b008b3d9697a9f4604cd5684a87b44677c99", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm"},
|
"mint": {:hex, :mint, "0.3.0", "f00f09363e5cdba1572519914aab3cea1a73b4737c63a29afe74a08362f5f91c", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"mojito": {:hex, :mojito, "0.3.0", "806cd3c1832333a9ee784e7ea2799863fbe4de55ecb4623a8f4ef870c2844cc6", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 0.2.1", [hex: :mint, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
|
"mojito": {:hex, :mojito, "0.3.0", "806cd3c1832333a9ee784e7ea2799863fbe4de55ecb4623a8f4ef870c2844cc6", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 0.2.1", [hex: :mint, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
||||||
"ojson": {:hex, :ojson, "1.0.0", "fd28614eadaec00a15cdb2f53f29d8717a812a508ddb80d202f2f2e2aaeabbcc", [:mix, :rebar3], [], "hexpm"},
|
"ojson": {:hex, :ojson, "1.0.0", "fd28614eadaec00a15cdb2f53f29d8717a812a508ddb80d202f2f2e2aaeabbcc", [:mix, :rebar3], [], "hexpm"},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user