defmodule Microwaveprop.BeaconMonitors do @moduledoc """ The BeaconMonitors context: manages the monitor stations a user has registered. Each monitor has a unique random token the remote program uses to authenticate its reports. """ import Ecto.Query alias Microwaveprop.Accounts.User alias Microwaveprop.BeaconMonitors.BeaconMonitor alias Microwaveprop.Repo @token_bytes 32 @doc """ Returns all monitors for the given user, newest first. """ @spec list_monitors_for_user(User.t()) :: [BeaconMonitor.t()] def list_monitors_for_user(%User{id: user_id}) do Repo.all( from m in BeaconMonitor, where: m.user_id == ^user_id, order_by: [desc: m.inserted_at] ) end @doc """ Creates a new monitor for the given user with a freshly generated token. """ @spec create_monitor(User.t(), map()) :: {:ok, BeaconMonitor.t()} | {:error, Ecto.Changeset.t()} def create_monitor(%User{} = user, attrs) do %BeaconMonitor{user_id: user.id, token: generate_token()} |> BeaconMonitor.changeset(attrs) |> Repo.insert() end @doc """ Deletes a monitor owned by the given user. Returns `{:error, :not_found}` if the monitor does not exist or belongs to another user. """ @spec delete_monitor(User.t(), Ecto.UUID.t()) :: {:ok, BeaconMonitor.t()} | {:error, :not_found} | {:error, Ecto.Changeset.t()} def delete_monitor(%User{id: user_id}, monitor_id) do query = from m in BeaconMonitor, where: m.id == ^monitor_id and m.user_id == ^user_id case Repo.one(query) do nil -> {:error, :not_found} monitor -> Repo.delete(monitor) end rescue Ecto.Query.CastError -> {:error, :not_found} end @doc """ Looks up a monitor by its token. Returns nil if not found. """ @spec get_monitor_by_token(String.t()) :: BeaconMonitor.t() | nil def get_monitor_by_token(token) when is_binary(token) do Repo.get_by(BeaconMonitor, token: token) end @doc """ Returns a blank changeset for rendering the new-monitor form. """ @spec change_monitor(map()) :: Ecto.Changeset.t() def change_monitor(attrs \\ %{}) do BeaconMonitor.changeset(%BeaconMonitor{}, attrs) end defp generate_token do @token_bytes |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false) end end