defmodule PatreonEx.Impl.Wrapper do
@scopes MapSet.new([
"campaigns",
"campaigns.members",
"campaigns.members[email]",
"campaigns.members.address",
"identity",
"identity.memberships",
"identity[email]"
])
defp base_url(), do: "www.patreon.com"
# defp redirect_uri(), do: Patreon.Config.redirect_uri()
# defp client_id, do: Patreon.Config.client_id()
# defp secret, do: Patreon.Config.secret()
defp http(host, method, path, query, headers, body \\ "") do
{:ok, conn} = Mint.HTTP.connect(:https, host, 443)
query_string =
cond do
query == %{} -> ""
TRUE -> "?" <> URI.encode_query(query)
end
path = path <> query_string
{:ok, conn, ref} =
Mint.HTTP.request(
conn,
method,
path,
headers,
body
)
receive_resp(conn, ref, nil, nil, false)
end
defp receive_resp(conn, ref, status, data, done?) do
receive do
message ->
{:ok, conn, responses} = Mint.HTTP.stream(conn, message)
{new_status, new_data, done?} =
Enum.reduce(responses, {status, data, done?}, fn
{:status, ^ref, new_status}, {_old_status, data, done?} -> {new_status, data, done?}
{:headers, ^ref, _headers}, acc -> acc
{:data, ^ref, binary}, {status, nil, done?} -> {status, binary, done?}
{:data, ^ref, binary}, {status, data, done?} -> {status, data <> binary, done?}
{:done, ^ref}, {status, data, _done?} -> {status, data, true}
end)
cond do
done? and new_status == 200 -> {:ok, new_data}
done? -> {:error, {new_status, new_data}}
!done? -> receive_resp(conn, ref, new_status, new_data, done?)
end
end
end
defp authorize_query(scope, true, redirect_uri, client_id) do
state = random_string()
%{
response_type: "code",
redirect_uri: redirect_uri,
scope: Enum.join(scope, " "),
state: state,
client_id: client_id
}
end
defp authorize_query(_scope, false, redirect_uri, client_id) do
state = random_string()
%{
response_type: "code",
redirect_uri: redirect_uri,
state: state,
client_id: client_id
}
end
def authorize_url(scope, redirect_uri, client_id) when is_list(scope) do
scope = MapSet.new(scope)
cond do
MapSet.subset?(scope, @scopes) ->
{
:ok,
base_url()
<> "/oauth2/authorize?"
<> URI.encode_query(authorize_query(scope, scope !== MapSet.new(), redirect_uri, client_id))
}
true ->
{:error, "Invalid scope. Valid scopes are: #{Enum.join(@scopes, ", ")}"}
end
end
def authorize_url(redirect_uri, client_id) do
base_url()
<> "/oauth2/authorize?"
<> URI.encode_query(authorize_query(@scopes, true, redirect_uri, client_id))
end
defp random_string() do
binary = <<
System.system_time(:nanosecond)::64,
:erlang.phash2({node(), self()})::16,
:erlang.unique_integer()::16
>>
binary
|> Base.url_encode64()
|> String.replace(["/", "+"], "-")
end
defp validate_query(validation_code, redirect_uri, client_id, client_secret) do
%{
code: validation_code,
grant_type: "authorization_code",
client_id: client_id,
client_secret: client_secret,
redirect_uri: redirect_uri,
}
end
def validate_code(validation_code, redirect_uri, client_id, client_secret) do
resp = http(
base_url(),
"POST",
"/api/oauth2/token",
validate_query(validation_code, redirect_uri, client_id, client_secret),
["Content-Type: application/x-www-form-urlencoded"]
)
case resp do
{:ok, info} -> {:ok, Jason.decode!(info)}
{:error, _reason} = err -> err
end
end
def get_user(token, params) do
resp = http(
base_url(),
"GET",
"/api/oauth2/v2/identity",
params, # %{"fields[user]" => Enum.join(fields, ",")},
[{"Authorization", "Bearer #{token}"}]
)
case resp do
{:ok, info} -> {:ok, Jason.decode!(info)}
{:error, _reason} = err -> err
end
end
def get_campaigns(token, params) do
resp = http(
base_url(),
"GET",
"/api/oauth2/v2/campaigns",
params, # %{"fields[campaign]" => Enum.join(fields, ",")},#
[{"Authorization", "Bearer #{token}"}]
)
case resp do
{:ok, info} -> {:ok, Jason.decode!(info)}
{:error, _reason} = err -> err
end
end
end