Skip to content

Commit

Permalink
Simplify WCA API module
Browse files Browse the repository at this point in the history
  • Loading branch information
jonatanklosko committed Mar 30, 2024
1 parent 0dd2e7b commit d552128
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 184 deletions.
4 changes: 1 addition & 3 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,4 @@ config :wca_live, WcaLive.Wca.OAuth,
authorize_url: "https://staging.worldcubeassociation.org/oauth/authorize",
token_url: "https://staging.worldcubeassociation.org/oauth/token"

# Use a real version of the WCA API, talking to the staging server.
config :wca_live, :wca_api, WcaLive.Wca.Api.Http
config :wca_live, WcaLive.Wca.Api.Http, api_url: "https://staging.worldcubeassociation.org/api/v0"
config :wca_live, :wca_api, url: "https://staging.worldcubeassociation.org/api/v0"
4 changes: 1 addition & 3 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ if config_env() == :prod do
client_id: wca_oauth_client_id,
client_secret: wca_oauth_client_secret

# Use a real version of the WCA API, talking to an actual server.
config :wca_live, :wca_api, WcaLive.Wca.Api.Http
config :wca_live, WcaLive.Wca.Api.Http, api_url: "https://#{wca_host}/api/v0"
config :wca_live, :wca_api, url: "https://#{wca_host}/api/v0"

if dns_query = System.get_env("CLUSTER_DNS_QUERY") do
config :libcluster,
Expand Down
7 changes: 5 additions & 2 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ config :wca_live, WcaLiveWeb.Endpoint,
# Print only warnings and errors during test
config :logger, level: :warn

# Use a mock version of the WCA API.
config :wca_live, :wca_api, WcaLive.Wca.Api.InMemory
config :wca_live, :wca_api,
url: "https://staging.worldcubeassociation.org/api/v0",
# In tests, we use a global stub for the records request, because
# the RecordsStore fetches records in the background
records_req_options: [plug: WcaLive.RecordsStub]
8 changes: 4 additions & 4 deletions lib/wca_live/synchronization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule WcaLive.Synchronization do
@spec import_competition(String.t(), %User{}) :: {:ok, %Competition{}} | {:error, any()}
def import_competition(wca_id, user) do
with {:ok, access_token} <- Accounts.get_valid_access_token(user),
{:ok, wcif} <- Wca.Api.impl().get_wcif(wca_id, access_token.access_token) do
{:ok, wcif} <- Wca.Api.get_wcif(wca_id, access_token.access_token) do
%Competition{}
|> Changeset.change()
|> Changeset.put_assoc(:imported_by, user)
Expand All @@ -52,11 +52,11 @@ defmodule WcaLive.Synchronization do
{:ok, %Competition{}} | {:error, any()}
def synchronize_competition(competition, user) do
with {:ok, access_token} <- Accounts.get_valid_access_token(user),
{:ok, wcif} <- Wca.Api.impl().get_wcif(competition.wca_id, access_token.access_token),
{:ok, wcif} <- Wca.Api.get_wcif(competition.wca_id, access_token.access_token),
{:ok, updated_competition} <-
Synchronization.Import.import_competition(competition, wcif),
wcif <- Synchronization.Export.export_competition(updated_competition),
{:ok, _} <- Wca.Api.impl().patch_wcif(wcif, access_token.access_token) do
{:ok, _} <- Wca.Api.patch_wcif(wcif, access_token.access_token) do
{:ok, updated_competition}
end
end
Expand All @@ -70,7 +70,7 @@ defmodule WcaLive.Synchronization do
def get_importable_competition_briefs(user) do
with {:ok, access_token} <- Accounts.get_valid_access_token(user),
{:ok, data} <-
Wca.Api.impl().get_upcoming_manageable_competitions(access_token.access_token) do
Wca.Api.get_upcoming_manageable_competitions(access_token.access_token) do
competition_briefs =
data
|> Enum.filter(fn data -> data["announced_at"] != nil end)
Expand Down
97 changes: 82 additions & 15 deletions lib/wca_live/wca/api.ex
Original file line number Diff line number Diff line change
@@ -1,47 +1,114 @@
defmodule WcaLive.Wca.Api do
@moduledoc """
A behaviour module for implementing interaction with the WCA API.
Requests to the WCA API.
"""

@doc """
Fetches the user related to the given access token.
"""
@callback get_me(String.t()) :: {:ok, any()} | {:error, any()}
@spec get_me(String.t()) :: {:ok, term()} | {:error, String.t()}
def get_me(access_token) do
build_req()
|> with_user_token(access_token)
|> request(url: "/me")
end

@doc """
Fetches active team roles for the given WCA user.
"""
@callback get_active_team_roles(String.t(), String.t()) :: {:ok, any()} | {:error, any()}
@spec get_active_team_roles(String.t(), String.t()) :: {:ok, term()} | {:error, String.t()}
def get_active_team_roles(wca_user_id, access_token) do
params = %{
"isActive" => true,
"groupType" => "teams_committees"
}

build_req()
|> with_user_token(access_token)
|> request(url: "/user_roles/user/#{wca_user_id}", params: params)
end

@doc """
Fetches WCIF for the given competition id.
"""
@callback get_wcif(String.t(), String.t()) :: {:ok, any()} | {:error, any()}
@spec get_wcif(String.t(), String.t()) :: {:ok, any()} | {:error, String.t()}
def get_wcif(competition_wca_id, access_token) do
build_req()
|> with_user_token(access_token)
|> with_long_timeout()
|> request(url: "/competitions/#{competition_wca_id}/wcif")
end

@doc """
Saves the given WCIF.
"""
@callback patch_wcif(any(), String.t()) :: {:ok, any()} | {:error, any()}
@spec patch_wcif(any(), String.t()) :: {:ok, any()} | {:error, String.t()}
def patch_wcif(wcif, access_token) do
competition_wca_id = wcif["id"]

build_req()
|> with_user_token(access_token)
|> with_long_timeout()
|> request(url: "/competitions/#{competition_wca_id}/wcif", method: :patch, json: wcif)
end

@doc """
Fetches upcoming competitions manageable by the authorized user.
"""
@callback get_upcoming_manageable_competitions(String.t()) :: {:ok, any()} | {:error, any()}
@spec get_upcoming_manageable_competitions(String.t()) :: {:ok, any()} | {:error, String.t()}
def get_upcoming_manageable_competitions(access_token) do
two_days_ago = Date.utc_today() |> Date.add(-2)

params = %{
"managed_by_me" => true,
"start" => two_days_ago
}

build_req()
|> with_user_token(access_token)
|> request(url: "/competitions", params: params)
end

@doc """
Fetches official regional records.
"""
@callback get_records() :: {:ok, any()} | {:error, any()}
@spec get_records() :: {:ok, any()} | {:error, String.t()}
def get_records() do
build_req()
|> Req.merge(Keyword.get(config(), :records_req_options, []))
|> request(url: "/records")
end

@doc """
Returns a module implementing this behaviour
as configured for the current environment.
defp build_req() do
base_url = Keyword.fetch!(config(), :url)
Req.new(base_url: base_url)
end

Make sure to specify the module in configuration like this:
defp with_user_token(req, access_token) do
Req.merge(req, auth: {:bearer, access_token})
end

config :wca_live, :wca_api, WcaLive.Wca.Api.Http
"""
def impl() do
Application.fetch_env!(:wca_live, :wca_api)
defp with_long_timeout(req) do
Req.merge(req, receive_timeout: 60_000)
end

defp request(req, opts) do
case Req.request(req, opts) do
{:ok, %{status: status, body: body}} when status in 200..299 ->
{:ok, body}

{:ok, %{status: status, body: %{"error" => error}}} ->
{:error, "the WCA server returned an error, status: #{status}, message: #{error}"}

{:ok, %{status: status}} ->
{:error, "the WCA server returned an error, status: #{status}"}

{:error, exception} ->
{:error, "request to the WCA server failed, reason: #{Exception.message(exception)}"}
end
end

defp config() do
Application.get_env(:wca_live, :wca_api, [])
end
end
101 changes: 0 additions & 101 deletions lib/wca_live/wca/api/http.ex

This file was deleted.

53 changes: 0 additions & 53 deletions lib/wca_live/wca/api/in_memory.ex

This file was deleted.

2 changes: 1 addition & 1 deletion lib/wca_live/wca/records.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule WcaLive.Wca.Records do
"""
@spec get_regional_records() :: {:ok, list(record())} | {:error, any()}
def get_regional_records() do
with {:ok, data} <- Wca.Api.impl().get_records() do
with {:ok, data} <- Wca.Api.get_records() do
{:ok, wca_json_to_records(data)}
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/wca_live_web/controllers/auth_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ defmodule WcaLiveWeb.AuthController do

def callback(conn, %{"code" => code}) do
{:ok, token_attrs} = Wca.OAuth.get_token(code)
{:ok, data} = Wca.Api.impl().get_me(token_attrs.access_token)
{:ok, data} = Wca.Api.get_me(token_attrs.access_token)

wca_user_id = data["me"]["id"]

{:ok, roles_data} =
Wca.Api.impl().get_active_team_roles(wca_user_id, token_attrs.access_token)
Wca.Api.get_active_team_roles(wca_user_id, token_attrs.access_token)

user_attrs = wca_json_to_user_attrs(data["me"], roles_data)
{:ok, user} = Accounts.import_user(user_attrs, token_attrs)
Expand Down
Loading

0 comments on commit d552128

Please sign in to comment.