Move to GraphQL

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2018-11-06 10:30:27 +01:00
parent 7e137d1a1c
commit b54dae7e15
149 changed files with 5605 additions and 4665 deletions

View File

@@ -11,6 +11,14 @@ defmodule Mobilizon.Actors do
alias Mobilizon.Service.ActivityPub
def data() do
Dataloader.Ecto.new(Repo, query: &query/2)
end
def query(queryable, _params) do
queryable
end
@doc """
Returns the list of actors.
@@ -42,6 +50,28 @@ defmodule Mobilizon.Actors do
Repo.get!(Actor, id)
end
@doc """
Returns the associated actor for an user, either the default set one or the first found
"""
@spec get_actor_for_user(%Mobilizon.Actors.User{}) :: %Mobilizon.Actors.Actor{}
def get_actor_for_user(%Mobilizon.Actors.User{} = user) do
case user.default_actor_id do
nil -> get_first_actor_for_user(user)
actor_id -> get_actor!(actor_id)
end
end
@doc """
Returns the first actor found for an user
Useful when the user has not defined default actor
Raises `Ecto.NoResultsError` if no Actor is found for this ID
"""
defp get_first_actor_for_user(%Mobilizon.Actors.User{id: id} = _user) do
Repo.one!(from(a in Actor, where: a.user_id == ^id))
end
def get_actor_with_everything!(id) do
actor = Repo.get!(Actor, id)
Repo.preload(actor, :organized_events)
@@ -162,10 +192,13 @@ defmodule Mobilizon.Actors do
Repo.all(User)
end
def list_users_with_actors do
users = Repo.all(User)
Repo.preload(users, :actors)
end
@doc """
List users with their associated actors. No reason for that, so removed
"""
# def list_users_with_actors do
# users = Repo.all(User)
# Repo.preload(users, :actors)
# end
defp blank?(""), do: nil
defp blank?(n), do: n
@@ -226,6 +259,14 @@ defmodule Mobilizon.Actors do
Repo.preload(user, :actors)
end
@spec get_user_with_actor(integer()) :: %User{}
def get_user_with_actor(id) do
case Repo.get(User, id) do
nil -> {:error, "User with ID #{id} not found"}
user -> {:ok, Repo.preload(user, :actors)}
end
end
def get_actor_by_url(url) do
Repo.get_by(Actor, url: url)
end
@@ -297,10 +338,17 @@ defmodule Mobilizon.Actors do
@doc """
Find actors by their name or displayed name
"""
def find_actors_by_username_or_name(username) do
def find_actors_by_username_or_name(username, page \\ 1, limit \\ 10)
def find_actors_by_username_or_name("", page, limit), do: []
def find_actors_by_username_or_name(username, page, limit) do
start = (page - 1) * limit
Repo.all(
from(
a in Actor,
limit: ^limit,
offset: ^start,
where:
ilike(a.preferred_username, ^like_sanitize(username)) or
ilike(a.name, ^like_sanitize(username))
@@ -340,19 +388,6 @@ defmodule Mobilizon.Actors do
end
end
@doc """
Get an user by email
"""
def find_by_email(email) do
case Repo.preload(Repo.get_by(User, email: email), :actors) do
nil ->
{:error, nil}
user ->
{:ok, user}
end
end
@doc """
Authenticate user
"""
@@ -390,31 +425,37 @@ defmodule Mobilizon.Actors do
nil
end
actor =
Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{
preferred_username: username,
domain: nil,
keys: pem,
avatar_url: avatar
})
user =
Mobilizon.Actors.User.registration_changeset(%Mobilizon.Actors.User{}, %{
email: email,
password: password
})
actor_with_user = Ecto.Changeset.put_assoc(actor, :user, user)
try do
Mobilizon.Repo.insert!(actor_with_user)
find_by_email(email)
rescue
e in Ecto.InvalidChangesetError ->
{:error, e.changeset}
with actor_changeset <-
Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{
preferred_username: username,
domain: nil,
keys: pem,
avatar_url: avatar
}),
{:ok, %Mobilizon.Actors.Actor{id: id} = actor} <- Mobilizon.Repo.insert(actor_changeset),
user_changeset <-
Mobilizon.Actors.User.registration_changeset(%Mobilizon.Actors.User{}, %{
email: email,
password: password,
default_actor_id: id
}),
{:ok, %Mobilizon.Actors.User{} = user} <- Mobilizon.Repo.insert(user_changeset) do
{:ok, Map.put(actor, :user, user)}
else
{:error, %Ecto.Changeset{} = changeset} ->
handle_actor_user_changeset(changeset)
end
end
defp handle_actor_user_changeset(changeset) do
changeset =
Ecto.Changeset.traverse_errors(changeset, fn
{msg, opts} -> msg
msg -> msg
end)
{:error, hd(Map.get(changeset, :email))}
end
def register_bot_account(%{name: name, summary: summary}) do
key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
@@ -466,9 +507,16 @@ defmodule Mobilizon.Actors do
iex> get_user_by_email(user, wrong_email)
{:error, nil}
"""
def get_user_by_email(email) do
case Repo.get_by(User, email: email) do
nil -> {:error, nil}
def get_user_by_email(email, activated \\ nil) do
query =
case activated do
nil -> from(u in User, where: u.email == ^email)
true -> from(u in User, where: u.email == ^email and not is_nil(u.confirmed_at))
false -> from(u in User, where: u.email == ^email and is_nil(u.confirmed_at))
end
case Repo.one(query) do
nil -> {:error, :user_not_found}
user -> {:ok, user}
end
end

View File

@@ -3,6 +3,7 @@ defmodule Mobilizon.Actors.Service.Activation do
alias Mobilizon.{Mailer, Repo, Actors.User, Actors}
alias Mobilizon.Email.User, as: UserEmail
alias Mobilizon.Actors.Service.Tools
require Logger
@@ -15,7 +16,8 @@ defmodule Mobilizon.Actors.Service.Activation do
"confirmation_sent_at" => nil,
"confirmation_token" => nil
}) do
{:ok, Repo.preload(user, :actors)}
Logger.info("User #{user.email} has been confirmed")
{:ok, user}
else
_err ->
{:error, "Invalid token"}
@@ -23,8 +25,12 @@ defmodule Mobilizon.Actors.Service.Activation do
end
def resend_confirmation_email(%User{} = user, locale \\ "en") do
{:ok, user} = Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()})
send_confirmation_email(user, locale)
with :ok <- Tools.we_can_send_email(user, :confirmation_sent_at),
{:ok, user} <- Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()}) do
send_confirmation_email(user, locale)
Logger.info("Sent confirmation email again to #{user.email}")
{:ok, user.email}
end
end
def send_confirmation_email(%User{} = user, locale \\ "en") do

View File

@@ -5,6 +5,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
alias Mobilizon.{Mailer, Repo, Actors.User}
alias Mobilizon.Email.User, as: UserEmail
alias Mobilizon.Actors.Service.Tools
@doc """
Check that the provided token is correct and update provided password
@@ -20,7 +21,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
"reset_password_token" => nil
})
) do
{:ok, Repo.preload(user, :actors)}
{:ok, user}
else
err ->
{:error, :invalid_token}
@@ -32,11 +33,11 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
"""
@spec send_password_reset_email(User.t(), String.t()) :: tuple
def send_password_reset_email(%User{} = user, locale \\ "en") do
with :ok <- we_can_send_email(user),
with :ok <- Tools.we_can_send_email(user, :reset_password_sent_at),
{:ok, %User{} = user_updated} <-
Repo.update(
User.send_password_reset_changeset(user, %{
"reset_password_token" => random_string(30),
"reset_password_token" => Tools.random_string(30),
"reset_password_sent_at" => DateTime.utc_now()
})
) do
@@ -50,28 +51,4 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
{:error, reason} -> {:error, reason}
end
end
@spec random_string(integer) :: String.t()
defp random_string(length) do
length
|> :crypto.strong_rand_bytes()
|> Base.url_encode64()
end
@spec we_can_send_email(User.t()) :: boolean
defp we_can_send_email(%User{} = user) do
case user.reset_password_sent_at do
nil ->
:ok
_ ->
case Timex.before?(Timex.shift(user.reset_password_sent_at, hours: 1), DateTime.utc_now()) do
true ->
:ok
false ->
{:error, :email_too_soon}
end
end
end
end

View File

@@ -0,0 +1,27 @@
defmodule Mobilizon.Actors.Service.Tools do
alias Mobilizon.Actors.User
@spec we_can_send_email(User.t()) :: boolean
def we_can_send_email(%User{} = user, key \\ :reset_password_sent_at) do
case Map.get(user, key) do
nil ->
:ok
_ ->
case Timex.before?(Timex.shift(Map.get(user, key), hours: 1), DateTime.utc_now()) do
true ->
:ok
false ->
{:error, :email_too_soon}
end
end
end
@spec random_string(integer) :: String.t()
def random_string(length) do
length
|> :crypto.strong_rand_bytes()
|> Base.url_encode64()
end
end

View File

@@ -12,6 +12,7 @@ defmodule Mobilizon.Actors.User do
field(:password, :string, virtual: true)
field(:role, :integer, default: 0)
has_many(:actors, Actor)
field(:default_actor_id, :integer)
field(:confirmed_at, :utc_datetime)
field(:confirmation_sent_at, :utc_datetime)
field(:confirmation_token, :string)
@@ -27,6 +28,7 @@ defmodule Mobilizon.Actors.User do
|> cast(attrs, [
:email,
:role,
:default_actor_id,
:password_hash,
:confirmed_at,
:confirmation_sent_at,
@@ -49,7 +51,8 @@ defmodule Mobilizon.Actors.User do
struct
|> changeset(params)
|> cast(params, ~w(password)a, [])
|> validate_required([:email, :password])
|> validate_required([:email, :password, :default_actor_id])
|> validate_email()
|> validate_length(
:password,
min: 6,
@@ -92,6 +95,19 @@ defmodule Mobilizon.Actors.User do
end
end
defp validate_email(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{email: email}} ->
case EmailChecker.valid?(email) do
false -> add_error(changeset, :email, "Email doesn't fit required format")
_ -> changeset
end
_ ->
changeset
end
end
defp random_string(length) do
length
|> :crypto.strong_rand_bytes()

View File

@@ -16,7 +16,7 @@ defmodule Mobilizon.Email.User do
base_email()
|> to(user.email)
|> subject(
gettext("Peakweaver: Confirmation instructions for %{instance}", instance: instance_url)
gettext("Mobilizon: Confirmation instructions for %{instance}", instance: instance_url)
)
|> put_header("Reply-To", get_config(:reply_to))
|> assign(:token, user.confirmation_token)
@@ -32,7 +32,7 @@ defmodule Mobilizon.Email.User do
|> to(user.email)
|> subject(
gettext(
"Peakweaver: Reset your password on %{instance} instructions",
"Mobilizon: Reset your password on %{instance} instructions",
instance: instance_url
)
)

View File

@@ -5,10 +5,11 @@ defmodule Mobilizon.Events.Category do
use Ecto.Schema
import Ecto.Changeset
alias Mobilizon.Events.Category
use Arc.Ecto.Schema
schema "categories" do
field(:description, :string)
field(:picture, :string)
field(:picture, MobilizonWeb.Uploaders.Category.Type)
field(:title, :string, null: false)
timestamps()
@@ -17,7 +18,8 @@ defmodule Mobilizon.Events.Category do
@doc false
def changeset(%Category{} = category, attrs) do
category
|> cast(attrs, [:title, :description, :picture])
|> cast(attrs, [:title, :description])
|> cast_attachments(attrs, [:picture])
|> validate_required([:title])
|> unique_constraint(:title)
end

View File

@@ -86,7 +86,6 @@ defmodule Mobilizon.Events.Event do
|> validate_required([
:title,
:begins_on,
:ends_on,
:organizer_actor_id,
:category_id,
:url,

View File

@@ -6,23 +6,16 @@ defmodule Mobilizon.Events do
import Ecto.Query, warn: false
alias Mobilizon.Repo
alias Mobilizon.Events.Event
alias Mobilizon.Events.Comment
alias Mobilizon.Events.{Event, Comment, Participant}
alias Mobilizon.Actors.Actor
alias Mobilizon.Addresses.Address
@doc """
Returns the list of events.
def data() do
Dataloader.Ecto.new(Mobilizon.Repo, query: &query/2)
end
## Examples
iex> list_events()
[%Event{}, ...]
"""
def list_events do
events = Repo.all(Event)
Repo.preload(events, [:organizer_actor])
def query(queryable, _params) do
queryable
end
def get_events_for_actor(%Actor{id: actor_id} = _actor, page \\ 1, limit \\ 10) do
@@ -179,15 +172,47 @@ defmodule Mobilizon.Events do
])
end
@doc """
Returns the list of events.
## Examples
iex> list_events()
[%Event{}, ...]
"""
def list_events(page \\ 1, limit \\ 10) do
start = (page - 1) * limit
query =
from(e in Event,
limit: ^limit,
offset: ^start,
preload: [:organizer_actor]
)
Repo.all(query)
end
@doc """
Find events by name
"""
def find_events_by_name(name) when name == "", do: []
def find_events_by_name(name, page \\ 1, limit \\ 10)
def find_events_by_name("", page, limit), do: list_events(page, limit)
def find_events_by_name(name) do
def find_events_by_name(name, page, limit) do
name = String.trim(name)
events = Repo.all(from(a in Event, where: ilike(a.title, ^like_sanitize(name))))
Repo.preload(events, [:organizer_actor])
start = (page - 1) * limit
query =
from(e in Event,
limit: ^limit,
offset: ^start,
where: ilike(e.title, ^like_sanitize(name)),
preload: [:organizer_actor]
)
Repo.all(query)
end
@doc """
@@ -210,9 +235,16 @@ defmodule Mobilizon.Events do
"""
def create_event(attrs \\ %{}) do
case %Event{} |> Event.changeset(attrs) |> Repo.insert() do
{:ok, %Event{} = event} -> {:ok, Repo.preload(event, [:organizer_actor])}
err -> err
with {:ok, %Event{} = event} <- %Event{} |> Event.changeset(attrs) |> Repo.insert(),
{:ok, %Participant{} = _participant} <-
%Participant{}
|> Participant.changeset(%{
actor_id: attrs.organizer_actor_id,
role: 4,
event_id: event.id
})
|> Repo.insert() do
{:ok, Repo.preload(event, [:organizer_actor])}
end
end
@@ -475,6 +507,27 @@ defmodule Mobilizon.Events do
Repo.all(Participant)
end
@doc """
Returns the list of participants for an event.
## Examples
iex> list_participants_for_event(someuuid)
[%Participant{}, ...]
"""
def list_participants_for_event(uuid) do
Repo.all(
from(
p in Participant,
join: e in Event,
on: p.event_id == e.id,
where: e.uuid == ^uuid,
preload: [:actor]
)
)
end
@doc """
Gets a single participant.