Changes to the actor - user relation

Now the GraphQL API replies mostly with users which have the default_actor
property filled to show profile information

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2018-11-29 17:43:22 +01:00
parent 6f15127cb0
commit 51aa511101
10 changed files with 232 additions and 123 deletions

View File

@@ -59,12 +59,16 @@ defmodule Mobilizon.Actors do
"""
@spec get_actor_for_user(Mobilizon.Actors.User.t()) :: Mobilizon.Actors.Actor.t()
def get_actor_for_user(%Mobilizon.Actors.User{} = user) do
with %User{default_actor: actor} = user when not is_nil(user) and not is_nil(actor) <-
Repo.preload(user, [:default_actor]) do
actor
case Repo.one(from(a in Actor, join: u in User, on: u.default_actor_id == a.id)) do
nil -> get_actors_for_user(user) |> hd
actor -> actor
end
end
def get_actors_for_user(%User{id: user_id}) do
Repo.all(from(a in Actor, where: a.user_id == ^user_id))
end
def get_actor_with_everything!(id) do
actor = Repo.get!(Actor, id)
Repo.preload(actor, [:organized_events, :followers, :followings])
@@ -106,6 +110,13 @@ defmodule Mobilizon.Actors do
|> Repo.update()
end
def update_user_default_actor(user_id, actor_id) do
with from(u in User, where: u.id == ^user_id, update: [set: [default_actor_id: ^actor_id]])
|> Repo.update_all([]) do
Repo.get!(User, user_id) |> Repo.preload([:default_actor])
end
end
@doc """
Deletes a Actor.
@@ -248,14 +259,25 @@ defmodule Mobilizon.Actors do
@spec get_user_with_actors!(integer()) :: User.t()
def get_user_with_actors!(id) do
user = Repo.get!(User, id)
Repo.preload(user, :actors)
Repo.preload(user, [:actors, :default_actor])
end
@doc """
Get user with it's actors by ID
"""
@spec get_user_with_actors(integer()) :: User.t()
def get_user_with_actors(id) do
case Repo.get(User, id) do
nil -> {:error, "User with ID #{id} not found"}
user -> {:ok, Repo.preload(user, :actors)}
nil ->
{:error, "User with ID #{id} not found"}
user ->
user =
user
|> Repo.preload([:actors, :default_actor])
|> Map.put(:actors, get_actors_for_user(user))
{:ok, user}
end
end
@@ -497,22 +519,21 @@ defmodule Mobilizon.Actors do
pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
with avatar <- gravatar(email),
actor_changeset <-
Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{
preferred_username: username,
domain: nil,
keys: pem,
avatar_url: avatar
}),
{:ok, %Mobilizon.Actors.Actor{} = actor} <- Mobilizon.Repo.insert(actor_changeset),
user_changeset <-
Mobilizon.Actors.User.registration_changeset(%Mobilizon.Actors.User{}, %{
User.registration_changeset(%User{}, %{
email: email,
password: password,
default_actor: actor
default_actor: %{
preferred_username: username,
domain: nil,
keys: pem,
avatar_url: avatar
}
}),
{:ok, %Mobilizon.Actors.User{} = user} <- Mobilizon.Repo.insert(user_changeset) do
{:ok, Map.put(actor, :user, user)}
{:ok, %User{default_actor: %Actor{} = actor, id: user_id} = user} <-
Mobilizon.Repo.insert(user_changeset),
{:ok, %Actor{} = _actor} <- update_actor(actor, %{user_id: user_id}) do
{:ok, Repo.preload(user, [:actors])}
else
{:error, %Ecto.Changeset{} = changeset} ->
handle_actor_user_changeset(changeset)
@@ -600,9 +621,20 @@ defmodule Mobilizon.Actors do
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))
nil ->
from(u in User, where: u.email == ^email, preload: :default_actor)
true ->
from(u in User,
where: u.email == ^email and not is_nil(u.confirmed_at),
preload: :default_actor
)
false ->
from(u in User,
where: u.email == ^email and is_nil(u.confirmed_at),
preload: :default_actor
)
end
case Repo.one(query) do
@@ -611,6 +643,9 @@ defmodule Mobilizon.Actors do
end
end
@doc """
Get an user by it's activation token
"""
@spec get_user_by_activation_token(String.t()) :: Actor.t()
def get_user_by_activation_token(token) do
Repo.one(
@@ -621,6 +656,19 @@ defmodule Mobilizon.Actors do
)
end
@doc """
Get an user by it's reset password token
"""
@spec get_user_by_reset_password_token(String.t()) :: Actor.t()
def get_user_by_reset_password_token(token) do
Repo.one(
from(u in User,
where: u.reset_password_token == ^token,
preload: [:default_actor]
)
)
end
@doc """
Updates a user.
@@ -634,9 +682,12 @@ defmodule Mobilizon.Actors do
"""
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
with {:ok, %User{} = user} <-
user
|> User.changeset(attrs)
|> Repo.update() do
{:ok, Repo.preload(user, [:default_actor])}
end
end
@doc """

View File

@@ -3,7 +3,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
require Logger
alias Mobilizon.{Mailer, Repo, Actors.User}
alias Mobilizon.{Mailer, Repo, Actors.User, Actors}
alias Mobilizon.Email.User, as: UserEmail
alias Mobilizon.Actors.Service.Tools
@@ -12,7 +12,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
"""
@spec check_reset_password_token(String.t(), String.t()) :: tuple
def check_reset_password_token(password, token) do
with %User{} = user <- Repo.get_by(User, reset_password_token: token),
with %User{} = user <- Actors.get_user_by_reset_password_token(token),
{:ok, %User{} = user} <-
Repo.update(
User.password_reset_changeset(user, %{

View File

@@ -13,7 +13,7 @@ defmodule Mobilizon.Actors.User do
field(:password, :string, virtual: true)
field(:role, :integer, default: 0)
has_many(:actors, Actor)
has_one(:default_actor, Actor)
belongs_to(:default_actor, Actor)
field(:confirmed_at, :utc_datetime)
field(:confirmation_sent_at, :utc_datetime)
field(:confirmation_token, :string)
@@ -25,34 +25,40 @@ defmodule Mobilizon.Actors.User do
@doc false
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [
:email,
:role,
# :default_actor,
:password_hash,
:confirmed_at,
:confirmation_sent_at,
:confirmation_token,
:reset_password_sent_at,
:reset_password_token
])
|> validate_required([:email])
|> unique_constraint(:email, message: "registration.error.email_already_used")
|> validate_format(:email, ~r/@/)
|> validate_length(
:password,
min: 6,
max: 100,
message: "registration.error.password_too_short"
)
changeset =
user
|> cast(attrs, [
:email,
:role,
:password_hash,
:confirmed_at,
:confirmation_sent_at,
:confirmation_token,
:reset_password_sent_at,
:reset_password_token
])
|> validate_required([:email])
|> unique_constraint(:email, message: "registration.error.email_already_used")
|> validate_format(:email, ~r/@/)
|> validate_length(
:password,
min: 6,
max: 100,
message: "registration.error.password_too_short"
)
if Map.has_key?(attrs, :default_actor) do
put_assoc(changeset, :default_actor, attrs.default_actor)
else
changeset
end
end
def registration_changeset(struct, params) do
struct
|> changeset(params)
|> cast(params, ~w(password)a, [])
|> put_assoc(:default_actor, params.default_actor)
|> cast_assoc(:default_actor)
|> validate_required([:email, :password])
|> validate_email()
|> validate_length(

View File

@@ -26,9 +26,8 @@ defmodule MobilizonWeb.Resolvers.User do
"""
def login_user(_parent, %{email: email, password: password}, _resolution) do
with {:ok, %User{} = user} <- Actors.get_user_by_email(email, true),
{:ok, token, _} <- Actors.authenticate(%{user: user, password: password}),
%Actor{} = actor <- Actors.get_actor_for_user(user) do
{:ok, %{token: token, user: user, person: actor}}
{:ok, token, _} <- Actors.authenticate(%{user: user, password: password}) do
{:ok, %{token: token, user: user}}
else
{:error, :user_not_found} ->
{:error, "User with email not found"}
@@ -47,9 +46,9 @@ defmodule MobilizonWeb.Resolvers.User do
"""
@spec create_user_actor(any(), map(), any()) :: tuple()
def create_user_actor(_parent, args, _resolution) do
with {:ok, %Actor{user: user} = actor} <- Actors.register(args) do
with {:ok, %User{} = user} <- Actors.register(args) do
Mobilizon.Actors.Service.Activation.send_confirmation_email(user)
{:ok, actor}
{:ok, user}
end
end
@@ -114,9 +113,8 @@ defmodule MobilizonWeb.Resolvers.User do
def reset_password(_parent, %{password: password, token: token}, _resolution) do
with {:ok, %User{} = user} <-
Mobilizon.Actors.Service.ResetPassword.check_reset_password_token(password, token),
%Actor{} = actor <- Actors.get_actor_for_user(user),
{:ok, token, _} <- MobilizonWeb.Guardian.encode_and_sign(user) do
{:ok, %{token: token, user: user, actor: actor}}
{:ok, %{token: token, user: user}}
end
end
@@ -124,8 +122,17 @@ defmodule MobilizonWeb.Resolvers.User do
def change_default_actor(_parent, %{preferred_username: username}, %{
context: %{current_user: user}
}) do
with %Actor{} = actor <- Actors.get_local_actor_by_name(username) do
Actors.update_user(user, %{default_actor: actor})
with %Actor{id: actor_id} <- Actors.get_local_actor_by_name(username),
{:user_actor, true} <-
{:user_actor, actor_id in Enum.map(Actors.get_actors_for_user(user), & &1.id)},
%User{} = user <- Actors.update_user_default_actor(user.id, actor_id) do
{:ok, user}
else
{:user_actor, _} ->
{:error, :actor_not_from_user}
_err ->
{:error, :unable_to_change_default_actor}
end
end
end

View File

@@ -202,7 +202,6 @@ defmodule MobilizonWeb.Schema do
object :login do
field(:token, non_null(:string), description: "A JWT Token for this session")
field(:user, non_null(:user), description: "The user associated to this session")
field(:person, non_null(:person), description: "The person associated to this session")
end
@desc "An event"
@@ -480,8 +479,8 @@ defmodule MobilizonWeb.Schema do
resolve(&Resolvers.Category.create_category/3)
end
@desc "Create an user (returns an actor)"
field :create_user, type: :person do
@desc "Create an user"
field :create_user, type: :user do
arg(:email, non_null(:string))
arg(:password, non_null(:string))
arg(:username, non_null(:string))