diff --git a/services/app/apps/codebattle/lib/codebattle/auth/discord.ex b/services/app/apps/codebattle/lib/codebattle/auth/discord.ex index d7a9b9a17..09284ee24 100644 --- a/services/app/apps/codebattle/lib/codebattle/auth/discord.ex +++ b/services/app/apps/codebattle/lib/codebattle/auth/discord.ex @@ -6,13 +6,7 @@ defmodule Codebattle.Auth.Discord do @discord_auth_url "https://discord.com/oauth2/authorize" @discord_token_url "https://discord.com/api/v10/oauth2/token" - - @http_client Application.compile_env(:codebattle, :discord_oauth_client) - - @doc """ - `http_client/0` injects a TestDouble of Req in Test - """ - def http_client, do: @http_client + @discord_user_url "https://discord.com/api/users/@me" @doc """ `client_id/0` returns a `String` of the `DISCORD_CLIENT_ID` @@ -52,23 +46,23 @@ defmodule Codebattle.Auth.Discord do Bad authentication codes will return a tuple with `:error` and an error map. """ def discord_auth(code, redirect_uri) do - query = - URI.encode_query(%{ - client_id: client_id(), - client_secret: client_secret(), - grant_type: "authorization_code", - code: code, - redirect_uri: redirect_uri - }) - - http_client().post!(@discord_token_url <> query, - headers: [ - "content-type": "application/x-www-form-urlencoded" - ] - ) + opts = + Keyword.merge( + Application.get_env(:codebattle, :auth_req_options, []), + form: %{ + client_id: client_id(), + client_secret: client_secret(), + grant_type: "authorization_code", + code: code, + redirect_uri: redirect_uri + } + ) + + @discord_token_url + |> Req.post!(opts) |> Map.get(:body) |> URI.decode_query() - |> check_authenticated + |> check_authenticated() end defp check_authenticated(%{"access_token" => access_token}) do @@ -78,21 +72,24 @@ defmodule Codebattle.Auth.Discord do defp check_authenticated(error), do: {:error, error} defp get_user_details(access_token) do - http_client().get!("https://discord.com/api/users/@me", - headers: [ - "user-agent": "Codebattle", - authorization: "Bearer #{access_token}" - ] - ) + opts = + Keyword.merge( + Application.get_env(:codebattle, :auth_req_options, []), + headers: [ + "user-agent": "Codebattle", + authorization: "Bearer #{access_token}" + ] + ) + + @discord_user_url + |> Req.get!(opts) |> Map.get(:body) |> set_user_details() end - defp set_user_details({:ok, profile}) do + defp set_user_details(profile) do atom_key_map = for {key, val} <- profile, into: %{}, do: {String.to_atom(key), val} {:ok, atom_key_map} end - - defp set_user_details({:error, reason}), do: {:error, reason} end diff --git a/services/app/apps/codebattle/lib/codebattle/auth/discord_mock.ex b/services/app/apps/codebattle/lib/codebattle/auth/discord_mock.ex index ab0e1f42f..004a61478 100644 --- a/services/app/apps/codebattle/lib/codebattle/auth/discord_mock.ex +++ b/services/app/apps/codebattle/lib/codebattle/auth/discord_mock.ex @@ -67,11 +67,11 @@ defmodule Codebattle.Auth.DiscordMock do ], _options ) do - %{body: Jason.encode!(@body_email_nil)} + %{body: @body_email_nil} end def get!("https://discord.com/api/users/@me", _headers, _options) do - %{body: Jason.encode!(@valid_body)} + %{body: @valid_body} end @doc """ @@ -85,10 +85,10 @@ defmodule Codebattle.Auth.DiscordMock do _headers, _options ) do - %{body: Jason.encode!(%{error: "error"})} + %{body: %{error: "error"}} end def post!(_url, _body, _headers, _options) do - %{body: Jason.encode!(%{access_token: "asfd"})} + %{body: %{access_token: "asfd"}} end end diff --git a/services/app/apps/codebattle/lib/codebattle/auth/github.ex b/services/app/apps/codebattle/lib/codebattle/auth/github.ex index 08b713f36..60846acc3 100644 --- a/services/app/apps/codebattle/lib/codebattle/auth/github.ex +++ b/services/app/apps/codebattle/lib/codebattle/auth/github.ex @@ -5,14 +5,8 @@ defmodule Codebattle.Auth.Github do """ @github_url "https://github.com/login/oauth/" - @github_auth_url @github_url <> "access_token?" - - @http_client Application.compile_env(:codebattle, :github_oauth_client) - - @doc """ - `http_client/0` injects a TestDouble of Req in Test - """ - def http_client, do: @http_client + @github_auth_url "https://github.com/login/oauth/access_token?" + @github_user_url "https://api.github.com/user" @doc """ `client_id/0` returns a `String` of the `GITHUB_CLIENT_ID` @@ -51,15 +45,21 @@ defmodule Codebattle.Auth.Github do """ def github_auth(code) do query = - URI.encode_query(%{ - "client_id" => client_id(), - "client_secret" => client_secret(), - "code" => code - }) - - http_client().post!(@github_auth_url <> query, - headers: ["content-type": "application/x-www-form-urlencoded"] - ) + @github_auth_url <> + URI.encode_query(%{ + "client_id" => client_id(), + "client_secret" => client_secret(), + "code" => code + }) + + opts = + Keyword.merge( + Application.get_env(:codebattle, :auth_req_options, []), + headers: ["content-type": "application/x-www-form-urlencoded"] + ) + + query + |> Req.post!(opts) |> Map.get(:body) |> URI.decode_query() |> check_authenticated @@ -73,28 +73,29 @@ defmodule Codebattle.Auth.Github do defp check_authenticated(error), do: {:error, error} defp get_user_details(access_token) do - http_client().get!("https://api.github.com/user", - #  https://developer.github.com/v3/#user-agent-required - headers: [ - "user-agent": "Codebattle", - authorization: "token #{access_token}" - ] - ) + opts = + Keyword.merge( + Application.get_env(:codebattle, :auth_req_options, []), + headers: [ + "user-agent": "Codebattle", + authorization: "token #{access_token}" + ] + ) + + @github_user_url + |> Req.get!(opts) |> Map.get(:body) - # |> Jason.decode!() |> set_user_details(access_token) end defp get_primary_email(access_token) do - http_client().get!("https://api.github.com/user/emails", - #  https://developer.github.com/v3/#user-agent-required + Req.get!("https://api.github.com/user/emails", headers: [ "user-agent": "Codebattle", authorization: "token #{access_token}" ] ) |> Map.get(:body) - # |> Jason.decode!() |> Enum.find_value(&if &1["primary"], do: &1["email"]) end diff --git a/services/app/apps/codebattle/lib/codebattle/auth/github_mock.ex b/services/app/apps/codebattle/lib/codebattle/auth/github_mock.ex index 54c154506..a8b326800 100644 --- a/services/app/apps/codebattle/lib/codebattle/auth/github_mock.ex +++ b/services/app/apps/codebattle/lib/codebattle/auth/github_mock.ex @@ -11,21 +11,21 @@ defmodule Codebattle.Auth.GithubMock do `get/3` stubs the Req get! function when parameters match test vars. """ @valid_body %{ - access_token: "12345", - login: "test_user", - name: "Testy McTestface", - email: "test@gmail.com", - avatar_url: "https://avatars3.githubusercontent.com/u/10835816", - id: "19" + "access_token" => "12345", + "login" => "test_user", + "name" => "Testy McTestface", + "email" => "test@gmail.com", + "avatar_url" => "https://avatars3.githubusercontent.com/u/10835816", + "id" => "19" } @body_email_nil %{ - access_token: "12345", - login: "test_user", - name: "Testy McTestface", - email: nil, - avatar_url: "https://avatars3.githubusercontent.com/u/10835816", - id: "28" + "access_token" => "12345", + "login" => "test_user", + "name" => "Testy McTestface", + "email" => nil, + "avatar_url" => "https://avatars3.githubusercontent.com/u/10835816", + "id" => "28" } @emails [ @@ -64,7 +64,7 @@ defmodule Codebattle.Auth.GithubMock do ], _options ) do - %{body: Jason.encode!(@body_email_nil)} + %{body: @body_email_nil} end # user emails @@ -76,11 +76,11 @@ defmodule Codebattle.Auth.GithubMock do ], _options ) do - %{body: Jason.encode!(@emails)} + %{body: @emails} end def get!(_url, _headers, _options) do - %{body: Jason.encode!(@valid_body)} + %{body: @valid_body} end @doc """ @@ -88,34 +88,6 @@ defmodule Codebattle.Auth.GithubMock do """ def post!(url, body, headers \\ [], options \\ []) - def post!( - "https://github.com/login/oauth/access_token?client_id=TEST_ID&client_secret=TEST_SECRET&code=1234", - _body, - _headers, - _options - ) do - %{body: "error=error"} - end - - def post!( - "https://github.com/login/oauth/access_token?client_id=TEST_ID&client_secret=TEST_SECRET&code=123", - _body, - _headers, - _options - ) do - %{body: "access_token=123"} - end - - def post!( - "https://github.com/login/oauth/access_token?client_id=TEST_ID&client_secret=TEST_SECRET&code=42", - _body, - _headers, - _options - ) do - %{body: "access_token=42"} - end - - # for some reason GitHub's Post returns a URI encoded string def post!(_url, _body, _headers, _options) do %{body: URI.encode_query(@valid_body)} end diff --git a/services/app/apps/codebattle/lib/codebattle/tasks_importer.ex b/services/app/apps/codebattle/lib/codebattle/tasks_importer.ex index 91488eeb2..3c01f36eb 100644 --- a/services/app/apps/codebattle/lib/codebattle/tasks_importer.ex +++ b/services/app/apps/codebattle/lib/codebattle/tasks_importer.ex @@ -45,7 +45,7 @@ defmodule Codebattle.TasksImporter do File.mkdir_p!(@tmp_basedir) dir_path = Temp.mkdir!(%{basedir: @tmp_basedir, prefix: to_string(:rand.uniform(10_000_000))}) - response = Req.get!(@issues_link) |> dbg() + response = Req.get!(@issues_link) file_name = Path.join(dir_path, "issues.tar.gz") File.write!(file_name, response.body) diff --git a/services/app/apps/codebattle/lib/codebattle_web/controllers/auth_controller.ex b/services/app/apps/codebattle/lib/codebattle_web/controllers/auth_controller.ex index 9285e5e24..4b233201a 100644 --- a/services/app/apps/codebattle/lib/codebattle_web/controllers/auth_controller.ex +++ b/services/app/apps/codebattle/lib/codebattle_web/controllers/auth_controller.ex @@ -73,12 +73,10 @@ defmodule CodebattleWeb.AuthController do case provider_name do "github" -> - # TODO: user with {:ok, profile} = Codebattle.Auth.Github.github_auth(code) Codebattle.Auth.User.GithubUser.find_or_create(profile) "discord" -> - # TODO: user with redirect_uri = Routes.auth_url(conn, :callback, provider_name) {:ok, profile} = Codebattle.Auth.Discord.discord_auth(code, redirect_uri) Codebattle.Auth.User.DiscordUser.find_or_create(profile) diff --git a/services/app/apps/codebattle/test/codebattle_web/controllers/auth_bind_controller_test.exs b/services/app/apps/codebattle/test/codebattle_web/controllers/auth_bind_controller_test.exs index 81a8ecf06..80d9a9229 100644 --- a/services/app/apps/codebattle/test/codebattle_web/controllers/auth_bind_controller_test.exs +++ b/services/app/apps/codebattle/test/codebattle_web/controllers/auth_bind_controller_test.exs @@ -1,103 +1,106 @@ -# defmodule CodebattleWeb.AuthBindControllerTest do -# use CodebattleWeb.ConnCase, async: true -# -# alias Codebattle.Repo -# alias Codebattle.User -# -# describe "request" do -# test "GET /auth/github/bind", %{conn: conn} do -# conn = get(conn, "/auth/github/bind") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) =~ "https://github.com/login/oauth/authorize?" -# end -# -# test "GET /auth/discord/bind", %{conn: conn} do -# conn = get(conn, "/auth/discord/bind") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) =~ "https://discord.com/oauth2/authorize?" -# end -# -# test "GET /auth/lol/bind", %{conn: conn} do -# conn = get(conn, "/auth/lol/bind") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) == "/" -# end -# end -# -# describe "callback" do -# test "GET /auth/github/callback/bind", %{conn: conn} do -# user = insert(:user, github_id: 1, discord_id: 1, name: "lol-kek") -# -# conn = -# conn -# |> put_session(:user_id, user.id) -# |> get("/auth/github/callback/bind", %{"code" => "asfd"}) -# -# user = Repo.reload(user) -# -# assert %User{ -# discord_id: 1, -# name: "lol-kek", -# email: "test@gmail.com", -# github_name: "test_user", -# github_id: 19, -# avatar_url: "https://avatars3.githubusercontent.com/u/10835816" -# } = user -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/settings" -# end -# -# test "GET /auth/discord/callback/bind", %{conn: conn} do -# user = insert(:user, github_id: 1, discord_id: 1, name: "lol-kek") -# -# conn = -# conn -# |> put_session(:user_id, user.id) -# |> get("/auth/discord/callback/bind", %{"code" => "asfd"}) -# -# user = Repo.reload(user) -# -# assert %User{ -# avatar_url: "https://cdn.discordapp.com/avatars/1234567/12345.jpg", -# discord_avatar: "12345", -# discord_id: 1_234_567, -# discord_name: "test_name", -# email: "lol@kek.com", -# github_id: 1, -# name: "lol-kek" -# } = user -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/settings" -# end -# end -# -# describe "DELETE /auth/:provider/" do -# test "unbinds discord", %{conn: conn} do -# user = insert(:user) -# conn = conn |> put_session(:user_id, user.id) -# delete(conn, "/auth/discord") -# -# user = Repo.reload!(user) -# -# assert user.discord_id == nil -# assert user.discord_name == nil -# assert user.discord_avatar == nil -# end -# -# test "unbinds github", %{conn: conn} do -# user = insert(:user) -# conn = conn |> put_session(:user_id, user.id) -# delete(conn, "/auth/github") -# -# user = Repo.reload!(user) -# -# assert user.github_id == nil -# assert user.github_name == nil -# end -# end -# end +defmodule CodebattleWeb.AuthBindControllerTest do + use CodebattleWeb.ConnCase, async: true + + alias Codebattle.Repo + alias Codebattle.User + + describe "request" do + test "GET /auth/github/bind", %{conn: conn} do + conn = get(conn, "/auth/github/bind") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) =~ "https://github.com/login/oauth/authorize?" + end + + test "GET /auth/discord/bind", %{conn: conn} do + conn = get(conn, "/auth/discord/bind") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) =~ "https://discord.com/oauth2/authorize?" + end + + test "GET /auth/lol/bind", %{conn: conn} do + conn = get(conn, "/auth/lol/bind") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) == "/" + end + end + + describe "callback" do + test "GET /auth/github/callback/bind", %{conn: conn} do + stub_github_oauth_requests() + + user = insert(:user, github_id: 1, discord_id: 1, name: "lol-kek") + + conn = + conn + |> put_session(:user_id, user.id) + |> get("/auth/github/callback/bind", %{"code" => "asfd"}) + + user = Repo.reload(user) + + assert %User{ + discord_id: 1, + name: "lol-kek", + email: "test@gmail.com", + github_name: "test_user", + github_id: 19, + avatar_url: "https://avatars3.githubusercontent.com/u/10835816" + } = user + + assert conn.state == :sent + assert redirected_to(conn) == "/settings" + end + + test "GET /auth/discord/callback/bind", %{conn: conn} do + stub_discord_oauth_requests() + user = insert(:user, github_id: 1, discord_id: 1, name: "lol-kek") + + conn = + conn + |> put_session(:user_id, user.id) + |> get("/auth/discord/callback/bind", %{"code" => "asfd"}) + + user = Repo.reload(user) + + assert %User{ + avatar_url: "https://cdn.discordapp.com/avatars/1234567/12345.jpg", + discord_avatar: "12345", + discord_id: 1_234_567, + discord_name: "test_name", + email: "lol@kek.com", + github_id: 1, + name: "lol-kek" + } = user + + assert conn.state == :sent + assert redirected_to(conn) == "/settings" + end + end + + describe "DELETE /auth/:provider/" do + test "unbinds discord", %{conn: conn} do + user = insert(:user) + conn = conn |> put_session(:user_id, user.id) + delete(conn, "/auth/discord") + + user = Repo.reload!(user) + + assert user.discord_id == nil + assert user.discord_name == nil + assert user.discord_avatar == nil + end + + test "unbinds github", %{conn: conn} do + user = insert(:user) + conn = conn |> put_session(:user_id, user.id) + delete(conn, "/auth/github") + + user = Repo.reload!(user) + + assert user.github_id == nil + assert user.github_name == nil + end + end +end diff --git a/services/app/apps/codebattle/test/codebattle_web/controllers/auth_controller_test.exs b/services/app/apps/codebattle/test/codebattle_web/controllers/auth_controller_test.exs index 48debd0ec..f33022758 100644 --- a/services/app/apps/codebattle/test/codebattle_web/controllers/auth_controller_test.exs +++ b/services/app/apps/codebattle/test/codebattle_web/controllers/auth_controller_test.exs @@ -1,118 +1,126 @@ -# defmodule CodebattleWeb.AuthControllerTest do -# use CodebattleWeb.ConnCase, async: true -# -# alias Codebattle.Repo -# alias Codebattle.User -# -# describe "request" do -# test "GET /auth/github", %{conn: conn} do -# conn = get(conn, "/auth/github") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) =~ "https://github.com/login/oauth/authorize?" -# end -# -# test "GET /auth/discord", %{conn: conn} do -# conn = get(conn, "/auth/discord") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) =~ "https://discord.com/oauth2/authorize?" -# end -# -# test "GET /auth/lol", %{conn: conn} do -# conn = get(conn, "/auth/lol") -# assert conn.state == :sent -# assert conn.status == 302 -# assert redirected_to(conn) == "/" -# end -# end -# -# describe "callback" do -# # TODO: add failure tests -# test "/auth/github/callback creates user", %{conn: conn} do -# conn = get(conn, "/auth/github/callback", %{"code" => "asfd", "next" => "/next_path"}) -# user = Repo.get_by(User, name: "test_user") -# -# assert %User{ -# achievements: [], -# avatar_url: "https://avatars3.githubusercontent.com/u/10835816", -# discord_avatar: nil, -# discord_id: nil, -# discord_name: nil, -# email: "test@gmail.com", -# github_id: 19, -# github_name: "test_user", -# is_bot: false, -# is_guest: false, -# name: "test_user", -# rank: 5432, -# rating: 1200 -# } = user -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/next_path" -# end -# -# test "/auth/github/callback creates uniq name for user", %{conn: conn} do -# insert(:user, name: "test_user", github_id: 1111) -# conn = get(conn, "/auth/github/callback", %{"code" => "asfd", "next" => "/next_path"}) -# user = Repo.get_by(User, github_id: 19) -# -# assert %User{github_id: 19, github_name: "test_user"} = user -# "test_user_" <> code = user.name -# assert String.length(code) == 4 -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/next_path" -# end -# -# test "/auth/discord/callback creates user", %{conn: conn} do -# conn = get(conn, "/auth/discord/callback", %{"code" => "asfd", "next" => "/next_path"}) -# user = Repo.get_by(User, name: "test_name") -# -# assert %User{ -# achievements: [], -# avatar_url: "https://cdn.discordapp.com/avatars/1234567/12345.jpg", -# discord_avatar: "12345", -# discord_id: 1_234_567, -# discord_name: "test_name", -# editor_mode: nil, -# editor_theme: nil, -# email: "lol@kek.com", -# firebase_uid: nil, -# games_played: nil, -# github_id: nil, -# github_name: nil, -# is_bot: false, -# is_guest: false, -# lang: "js", -# name: "test_name", -# rank: 5432, -# rating: 1200 -# } = user -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/next_path" -# end -# -# test "/auth/discord/callback creates uniq name for user", %{conn: conn} do -# insert(:user, name: "test_name", discord_id: 123) -# conn = get(conn, "/auth/discord/callback", %{"code" => "asfd", "next" => "/next_path"}) -# user = Repo.get_by(User, discord_id: 1_234_567) -# -# assert %User{discord_id: 1_234_567, discord_name: "test_name"} = user -# "test_name_" <> code = user.name -# assert String.length(code) == 4 -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/next_path" -# end -# -# test "/auth/github/lol", %{conn: conn} do -# conn = get(conn, "/auth/lol/callback") -# -# assert conn.state == :sent -# assert redirected_to(conn) == "/" -# end -# end -# end +defmodule CodebattleWeb.AuthControllerTest do + use CodebattleWeb.ConnCase, async: true + + alias Codebattle.Repo + alias Codebattle.User + + describe "request" do + test "GET /auth/github", %{conn: conn} do + conn = get(conn, "/auth/github") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) =~ "https://github.com/login/oauth/authorize?" + end + + test "GET /auth/discord", %{conn: conn} do + conn = get(conn, "/auth/discord") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) =~ "https://discord.com/oauth2/authorize?" + end + + test "GET /auth/lol", %{conn: conn} do + conn = get(conn, "/auth/lol") + assert conn.state == :sent + assert conn.status == 302 + assert redirected_to(conn) == "/" + end + end + + describe "callback" do + test "/auth/github/callback creates user", %{conn: conn} do + stub_github_oauth_requests() + + conn = get(conn, "/auth/github/callback", %{"code" => "asfd", "next" => "/next_path"}) + user = Repo.get_by(User, name: "test_user") + + assert %User{ + achievements: [], + avatar_url: "https://avatars3.githubusercontent.com/u/10835816", + discord_avatar: nil, + discord_id: nil, + discord_name: nil, + email: "test@gmail.com", + github_id: 19, + github_name: "test_user", + is_bot: false, + is_guest: false, + name: "test_user", + rank: 5432, + rating: 1200 + } = user + + assert conn.state == :sent + assert redirected_to(conn) == "/next_path" + end + + test "/auth/github/callback creates uniq name for user", %{conn: conn} do + stub_github_oauth_requests() + + insert(:user, name: "test_user", github_id: 1111) + conn = get(conn, "/auth/github/callback", %{"code" => "asfd", "next" => "/next_path"}) + user = Repo.get_by(User, github_id: 19) + + assert %User{github_id: 19, github_name: "test_user"} = user + "test_user_" <> code = user.name + assert String.length(code) == 4 + + assert conn.state == :sent + assert redirected_to(conn) == "/next_path" + end + + test "/auth/discord/callback creates user", %{conn: conn} do + stub_discord_oauth_requests() + + conn = get(conn, "/auth/discord/callback", %{"code" => "asfd", "next" => "/next_path"}) + user = Repo.get_by(User, name: "test_name") + + assert %User{ + achievements: [], + avatar_url: "https://cdn.discordapp.com/avatars/1234567/12345.jpg", + discord_avatar: "12345", + discord_id: 1_234_567, + discord_name: "test_name", + editor_mode: nil, + editor_theme: nil, + email: "lol@kek.com", + firebase_uid: nil, + games_played: nil, + github_id: nil, + github_name: nil, + is_bot: false, + is_guest: false, + lang: "js", + name: "test_name", + rank: 5432, + rating: 1200 + } = user + + assert conn.state == :sent + assert redirected_to(conn) == "/next_path" + end + + test "/auth/discord/callback creates uniq name for user", %{conn: conn} do + insert(:user, name: "test_name", discord_id: 123) + + stub_discord_oauth_requests() + + conn = get(conn, "/auth/discord/callback", %{"code" => "asfd", "next" => "/next_path"}) + user = Repo.get_by(User, discord_id: 1_234_567) + + assert %User{discord_id: 1_234_567, discord_name: "test_name"} = user + "test_name_" <> code = user.name + assert String.length(code) == 4 + + assert conn.state == :sent + assert redirected_to(conn) == "/next_path" + end + + test "/auth/github/lol", %{conn: conn} do + conn = get(conn, "/auth/lol/callback") + + assert conn.state == :sent + assert redirected_to(conn) == "/" + end + end +end diff --git a/services/app/apps/codebattle/test/support/conn_case.ex b/services/app/apps/codebattle/test/support/conn_case.ex index ba6fa778b..459712b4c 100644 --- a/services/app/apps/codebattle/test/support/conn_case.ex +++ b/services/app/apps/codebattle/test/support/conn_case.ex @@ -29,6 +29,7 @@ defmodule CodebattleWeb.ConnCase do import Phoenix.ConnTest import Phoenix.LiveViewTest import CodebattleWeb.Factory + import Codebattle.OauthTestHelpers alias Codebattle.{Repo, User, Game, UserGame} alias Codebattle.Game.{Player} diff --git a/services/app/apps/codebattle/test/support/oauth_helpers.ex b/services/app/apps/codebattle/test/support/oauth_helpers.ex new file mode 100644 index 000000000..62093c96e --- /dev/null +++ b/services/app/apps/codebattle/test/support/oauth_helpers.ex @@ -0,0 +1,52 @@ +defmodule Codebattle.OauthTestHelpers do + @valid_github_body %{ + "access_token" => "12345", + "login" => "test_user", + "name" => "Testy McTestface", + "email" => "test@gmail.com", + "avatar_url" => "https://avatars3.githubusercontent.com/u/10835816", + "id" => "19" + } + + @valid_discord_body %{ + "accent_color" => nil, + "avatar" => "12345", + "avatar_decoration" => nil, + "banner" => nil, + "banner_color" => nil, + "discriminator" => "0123", + "display_name" => nil, + "email" => "lol@kek.com", + "flags" => 0, + "id" => "1234567", + "locale" => "ab", + "premium_type" => 0, + "public_flags" => 0, + "username" => "test_name", + "verified" => true + } + + def stub_github_oauth_requests do + Req.Test.stub(Codebattle.Auth, fn req -> + case req do + %{request_path: "/login/oauth/access_token", method: "POST", host: "github.com"} -> + Req.Test.text(req, URI.encode_query(@valid_github_body)) + + %{request_path: "/user", method: "GET", host: "api.github.com"} -> + Req.Test.json(req, @valid_github_body) + end + end) + end + + def stub_discord_oauth_requests do + Req.Test.stub(Codebattle.Auth, fn req -> + case req do + %{request_path: "/api/v10/oauth2/token", method: "POST", host: "discord.com"} -> + Req.Test.text(req, URI.encode_query(%{access_token: "asfd"})) + + %{request_path: "/api/users/@me", method: "GET", host: "discord.com"} -> + Req.Test.json(req, @valid_discord_body) + end + end) + end +end diff --git a/services/app/config/config.exs b/services/app/config/config.exs index c2d669802..451c6c374 100644 --- a/services/app/config/config.exs +++ b/services/app/config/config.exs @@ -47,9 +47,6 @@ config :logger, :console, config :phoenix, :json_library, Jason -config :codebattle, github_oauth_client: Req -config :codebattle, discord_oauth_client: Req - config :codebattle, :oauth, github_client_id: System.get_env("GITHUB_CLIENT_ID", "ASFD"), github_client_secret: System.get_env("GITHUB_CLIENT_SECRET", "ASFD"), diff --git a/services/app/config/test.exs b/services/app/config/test.exs index 46f55373c..0b8b39284 100644 --- a/services/app/config/test.exs +++ b/services/app/config/test.exs @@ -55,8 +55,16 @@ config :codebattle, code_check_timeout: 35_000 config :codebattle, tournament_match_timeout: 1 config :codebattle, max_alive_tournaments: 700 -config :codebattle, github_oauth_client: Codebattle.Auth.GithubMock -config :codebattle, discord_oauth_client: Codebattle.Auth.DiscordMock +config :codebattle, + auth_req_options: [ + plug: {Req.Test, Codebattle.Auth} + ] + +config :codebattle, :oauth, + github_client_id: "GITHUB_CLIENT_ID", + github_client_secret: "GITHUB_CLIENT_SECRET", + discord_client_id: "DISCORD_CLIENT_ID", + discord_client_secret: "DISCORD_CLIENT_SECRET" config :codebattle, Codebattle.Invite, timeout: :timer.seconds(1000), diff --git a/services/app/mix.lock b/services/app/mix.lock index 616a12117..df4c20604 100644 --- a/services/app/mix.lock +++ b/services/app/mix.lock @@ -1,31 +1,31 @@ %{ "bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, + "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, - "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, + "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "delta": {:git, "https://github.com/slab/delta-elixir.git", "361f409074c5d9021cb8088b5d73f9709ac41844", []}, - "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, "diff_match_patch": {:git, "https://github.com/vtm9/diff_match_patch.git", "633417590d1ee3af5c3e9aab8b6af1811d1c67d2", []}, "diffy": {:hex, :diffy, "1.1.2", "7060e34fd512f210056b7e66e8d315cb90d06b97a6656d92fc299eb91692e36e", [:rebar3], [{:proper, "~> 1.2.0", [hex: :proper, repo: "hexpm", optional: false]}, {:zotonic_stdlib, "1.2.3", [hex: :zotonic_stdlib, repo: "hexpm", optional: false]}], "hexpm", "9a59bbdcafb6b115b5099711f7f114edb00be11658cef08ef26e04f8040c7149"}, "earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"}, - "ecto": {:hex, :ecto, "3.12.2", "bae2094f038e9664ce5f089e5f3b6132a535d8b018bd280a485c2f33df5c0ce1", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "492e67c70f3a71c6afe80d946d3ced52ecc57c53c9829791bfff1830ff5a1f0c"}, + "ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"}, "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.8.1", "cdfee5cb21d51af37d14233e4aa3ef38189bf42c499d6a8160a943c990cc74e2", [:mix], [{:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "> 0.16.0 and < 0.20.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1 or ~> 4.0.0", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "c0a1df8181c87a0419afa881452329a9dfd2c1fb2a737bf147f862dfa44a1af0"}, "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"}, "envy": {:hex, :envy, "1.1.1", "0bc9bd654dec24fcdf203f7c5aa1b8f30620f12cfb28c589d5e9c38fe1b07475", [:mix], [], "hexpm", "7061eb1a47415fd757145d8dec10dc0b1e48344960265cb108f194c4252c3a89"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"}, - "excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"}, + "excoveralls": {:hex, :excoveralls, "0.18.3", "bca47a24d69a3179951f51f1db6d3ed63bca9017f476fe520eb78602d45f7756", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "746f404fcd09d5029f1b211739afb8fb8575d775b21f6a3908e7ce3e640724c6"}, "exfake": {:hex, :exfake, "1.0.1", "bef8c6fa2af4f3709507d5f171294143f060f5e55bd1ca8fb0f3d1d98566cf28", [:mix], [], "hexpm", "e75fcc35a8b84708428ff6953350bcab2922f15157749cf936c3174fb84443fe"}, - "expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"}, + "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, - "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "fun_with_flags": {:hex, :fun_with_flags, "1.12.0", "8036ad262ae4321d160e36c4db13c8cadf1890121d74c4f5852ab0de0ac3ad2b", [:mix], [{:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: true]}, {:redix, "~> 1.0", [hex: :redix, repo: "hexpm", optional: true]}], "hexpm", "9ed303bee60687f7a07dde2c036d3e8905771001ebd77790543ba5655a5f9066"}, "fun_with_flags_ui": {:hex, :fun_with_flags_ui, "1.0.0", "d764a4d1cc1233bdbb18dfb416a6ef96d0ecf4a5dc5a0201f7aa0b13cf2e7802", [:mix], [{:cowboy, ">= 2.0.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:fun_with_flags, "~> 1.11", [hex: :fun_with_flags, repo: "hexpm", optional: false]}, {:plug, "~> 1.12", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 2.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "b0c145894c00d65d5dc20ee5b1f18457985d1fd0b87866f0b41894d5979e55e0"}, @@ -33,7 +33,6 @@ "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "html_to_image": {:git, "https://github.com/vtm9/html_to_image.git", "63573446aad76c666a6674d3f0fa97ba97f443d8", []}, - "httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, @@ -61,16 +60,16 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.2", "fdadb973799ae691bf9ecad99125b16625b1c6039999da5fe544d99218e662e4", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "245d8a11ee2306094840c000e8816f0cbed69a23fc0ac2bcf8d7835ae019bb2f"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "porcelain": {:hex, :porcelain, "2.0.3", "2d77b17d1f21fed875b8c5ecba72a01533db2013bd2e5e62c6d286c029150fdc", [:mix], [], "hexpm", "dc996ab8fadbc09912c787c7ab8673065e50ea1a6245177b0c24569013d23620"}, "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"}, "proper": {:hex, :proper, "1.2.0", "1466492385959412a02871505434e72e92765958c60dba144b43863554b505a4", [:make, :mix, :rebar3], [], "hexpm", "cbc3766c08337806741343d330bf4bcb826155d2141be8514c4b02858aa19fd3"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "recase": {:hex, :recase, "0.8.1", "ab98cd35857a86fa5ca99036f575241d71d77d9c2ab0c39aacf1c9b61f6f7d1d", [:mix], [], "hexpm", "9fd8d63e7e43bd9ea385b12364e305778b2bbd92537e95c4b2e26fc507d5e4c2"}, - "recon": {:hex, :recon, "2.5.5", "c108a4c406fa301a529151a3bb53158cadc4064ec0c5f99b03ddb8c0e4281bdf", [:mix, :rebar3], [], "hexpm", "632a6f447df7ccc1a4a10bdcfce71514412b16660fe59deca0fcf0aa3c054404"}, + "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "req": {:hex, :req, "0.5.6", "8fe1eead4a085510fe3d51ad854ca8f20a622aae46e97b302f499dfb84f726ac", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cfaa8e720945d46654853de39d368f40362c2641c4b2153c886418914b372185"}, - "sentry": {:hex, :sentry, "10.7.0", "22af74d002229238b027d051d2bdd26bcd9789db5fea7335170aa41de7a658ce", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "ca732b35480b8023db2d6dd36418a603c54bdcb9111dbcc246f9d7219669b531"}, + "sentry": {:hex, :sentry, "10.7.1", "33392222d80ccff99c503f972998d2858b4c1e5aca2219a34269b68dacba8e7d", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "56291312397bf2b6afab6cf4f7aa1f27413b0eb2ceeb63b8aab2d7658aaea882"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistics": {:hex, :statistics, "0.6.3", "7fb182e7c1cab2980e392c7efef7ce326539f081f9defda4099550e9c2c7cb0f", [:mix], [], "hexpm", "a43d87726d240205e9ef47f29650a6e3132b4e4061e05512f32fa8120784a8e0"}, "table_rex": {:hex, :table_rex, "4.0.0", "3c613a68ebdc6d4d1e731bc973c233500974ec3993c99fcdabb210407b90959b", [:mix], [], "hexpm", "c35c4d5612ca49ebb0344ea10387da4d2afe278387d4019e4d8111e815df8f55"}, @@ -81,7 +80,7 @@ "thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, - "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, + "tzdata": {:hex, :tzdata, "1.1.2", "45e5f1fcf8729525ec27c65e163be5b3d247ab1702581a94674e008413eef50b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cec7b286e608371602318c414f344941d5eb0375e14cfdab605cca2fe66cba8b"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},