Current section

Files

Jump to
aura lib aura users.ex
Raw

lib/aura/users.ex

# SPDX-License-Identifier: Apache-2.0
defmodule Aura.Users do
@moduledoc Aura.Doc.mod_doc("Service module for interacting with Hex users")
import Aura.Common
alias Aura.Common
alias Aura.Model.HexAuditLog
alias Aura.Model.HexUser
alias Aura.Requester
require Logger
@base_path "/users"
@type user_opts :: [
repo_url: Common.repo_url()
]
@tos_warning """
If your service is using hex.pm as a backend, you **must** have users to agree to Hex's [Terms of Service](https://hex.pm/policies/termsofservice)
"""
@doc Aura.Doc.func_doc("Requests a hex user be created",
warning: {"📄 Terms of Service", @tos_warning},
params: [
{:username, {Common, :username}},
{:password, "User's password"},
{:emails, {Common, :email, :list}},
{"opts[:repo_url]", {Common, :repo_url}}
],
success: "{:ok, %HexUser{...}}",
failure: "{:error, (some error)}",
api: %{method: :post, route: @base_path, controller: :User, action: :create},
example: """
iex> Application.delete_env(:aura, :api_key)
iex> username = Faker.Internet.user_name()
iex> password = Faker.Internet.slug()
iex> emails = [Faker.Internet.email()]
iex> alias Aura.Users
iex> opts = [repo_url: "http://localhost:4000/api"]
iex> {:ok, _user} = Users.create_user(username, password, emails, opts)
"""
)
@spec create_user(
username :: Common.username(),
password :: String.t(),
emails :: [Common.email()],
opts :: user_opts()
) :: {:ok, HexUser.t()} | {:error, any()}
def create_user(username, password, emails, opts \\ [])
when is_bitstring(username) and is_bitstring(password) and is_list(emails) do
if Requester.find_repo_url(opts) == Requester.hex_pm_url() do
Logger.warning(
"By using create_user, you are agreeing to Hex's terms of service. See: https://hex.pm/policies/termsofservice"
)
end
emails = Enum.map(emails, fn email -> %{"email" => email} end)
opts = Keyword.merge([json: %{username: username, password: password, emails: emails}], opts)
with {:ok, %{body: body}} <- Requester.post(@base_path, opts) do
{:ok, HexUser.build(body)}
end
end
@doc Aura.Doc.func_doc("Grabs a hex user, given their **username_or_email**",
params: [
{:username_or_email, "`t:Aura.Common.username/0` or `t:Aura.Common.email/0`"},
{"opts[:repo_url]", {Common, :repo_url}}
],
success: "{:ok, %HexUser{...}}",
failure: "{:error, (some error)}",
api: %{route: Path.join(@base_path, ":username_or_email"), controller: :User, action: :show},
example: """
iex> alias Aura.Users
iex> opts = [repo_url: "http://localhost:4000/api"]
iex> {:ok, _user} = Users.get_user("eric@example.com", opts)
"""
)
@spec get_user(
username_or_email :: Common.username() | Common.email(),
opts :: user_opts()
) :: {:ok, HexUser.t()} | {:error, any()}
def get_user(username_or_email, opts \\ []) when is_bitstring(username_or_email) do
path = Path.join(@base_path, username_or_email)
with {:ok, %{body: body}} <- Requester.get(path, opts) do
{:ok, HexUser.build(body)}
end
end
@doc Aura.Doc.func_doc(
"Grabs the hex user representing the currently authenticated user",
params: [
{"opts[:repo_url]", {Common, :repo_url}}
],
success: "{:ok, %HexUser{...}}",
failure: "{:error, (some error)}",
api: %{route: Path.join(@base_path, "me"), controller: :User, action: :me},
example: """
iex> alias Aura.Users
iex> opts = [repo_url: "http://localhost:4000/api"]
iex> {:ok, _user} = Users.get_current_user(opts)
"""
)
@spec get_current_user(opts :: user_opts) :: {:ok, HexUser.t()} | {:error, any()}
def get_current_user(opts \\ []) do
path = Path.join(@base_path, "me")
with {:ok, %{body: body}} <- Requester.get(path, opts) do
{:ok, HexUser.build(body)}
end
end
@doc Aura.Doc.func_doc(
[
"Streams audit logs, scoped to the current authenticated user",
"Note that the page size is fixed by the API to be 100 per page."
],
params: [
{"opts[:repo_url]", {Common, :repo_url}},
{"opts[:page]", {Common, :start_page}}
],
success: "Stream.resource/3",
example: """
iex> alias Aura.Users
iex> opts = [repo_url: "http://localhost:4000/api"]
iex> audit_logs = Users.stream_audit_logs(opts)
iex> Enum.empty?(audit_logs)
false
""",
api: %{route: Path.join(@base_path, "me/audit-logs"), controller: :User, action: :audit_logs}
)
@spec stream_audit_logs(opts :: list()) :: Enumerable.t()
def stream_audit_logs(opts \\ []) do
path = Path.join(@base_path, "me/audit-logs")
stream_paginate(path, &HexAuditLog.build/1, opts)
end
@doc Aura.Doc.func_doc("Resets a specified user's password",
params: [
{:username_or_email, "`t:Aura.Common.username/0` or `t:Aura.Common.email/0`"},
{"opts[:repo_url]", {Common, :repo_url}}
],
success: ":ok",
failure: "{:error, (some error)}",
api: %{route: Path.join(@base_path, ":username_or_email/reset"), controller: :User, action: :reset},
example: """
iex> alias Aura.Users
iex> opts = [repo_url: "http://localhost:4000/api"]
iex> {:ok, user} = Users.get_current_user(opts)
iex> Users.reset_user_password(user.email, opts)
:ok
"""
)
@spec reset_user_password(
username_or_email :: Common.username() | Common.email(),
opts :: list()
) :: :ok | {:error, any()}
def reset_user_password(username_or_email, opts \\ []) do
path = Path.join(@base_path, "#{username_or_email}/reset")
with {:ok, _} <- Requester.post(path, opts) do
:ok
end
end
end