Merge branch 'feature/delete-category' into 'master'

Add ability to delete a group

See merge request framasoft/mobilizon!50
This commit is contained in:
Thomas Citharel 2019-01-25 16:14:00 +01:00
commit b6f905313a
8 changed files with 145 additions and 2 deletions

View File

@ -242,6 +242,13 @@ defmodule Mobilizon.Actors do
|> Repo.insert() |> Repo.insert()
end end
@doc """
Delete a group
"""
def delete_group!(%Actor{type: :Group} = group) do
Repo.delete!(group)
end
alias Mobilizon.Actors.User alias Mobilizon.Actors.User
@doc """ @doc """
@ -551,6 +558,16 @@ defmodule Mobilizon.Actors do
end end
end end
@doc """
Find a group by its actor id
"""
def find_group_by_actor_id(actor_id) do
case Repo.get_by(Actor, id: actor_id, type: :Group) do
nil -> {:error, :group_not_found}
actor -> {:ok, actor}
end
end
@doc """ @doc """
Authenticate user Authenticate user
""" """

View File

@ -6,6 +6,7 @@ defmodule Mobilizon.Actors.Member do
import Ecto.Changeset import Ecto.Changeset
alias Mobilizon.Actors.Member alias Mobilizon.Actors.Member
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Repo
schema "members" do schema "members" do
field(:approved, :boolean, default: true) field(:approved, :boolean, default: true)
@ -24,4 +25,22 @@ defmodule Mobilizon.Actors.Member do
|> validate_required([:parent_id, :actor_id]) |> validate_required([:parent_id, :actor_id])
|> unique_constraint(:parent_id, name: :members_actor_parent_unique_index) |> unique_constraint(:parent_id, name: :members_actor_parent_unique_index)
end end
@doc """
Gets a single member of an actor (for example a group)
"""
def get_member(actor_id, parent_id) do
case Repo.get_by(Member, actor_id: actor_id, parent_id: parent_id) do
nil -> {:error, :member_not_found}
member -> {:ok, member}
end
end
def is_administrator(%Member{role: 2} = member) do
{:is_admin, true}
end
def is_administrator(%Member{}) do
{:is_admin, false}
end
end end

View File

@ -144,4 +144,16 @@ defmodule Mobilizon.Actors.User do
def is_confirmed(%User{} = user) do def is_confirmed(%User{} = user) do
{:ok, user} {:ok, user}
end end
def owns_actor(%User{default_actor_id: default_actor_id} = user, %Actor{id: actor_id})
when default_actor_id == actor_id do
{:is_owned, true}
end
def owns_actor(%User{actors: actors} = user, actor_id) do
case Enum.any?(actors, fn a -> a.id == actor_id end) do
true -> {:is_owned, true}
_ -> {:is_owned, false}
end
end
end end

View File

@ -3,7 +3,7 @@ defmodule MobilizonWeb.Resolvers.Group do
Handles the group-related GraphQL calls Handles the group-related GraphQL calls
""" """
alias Mobilizon.Actors alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor} alias Mobilizon.Actors.{Actor, User, Member}
alias Mobilizon.Service.ActivityPub alias Mobilizon.Service.ActivityPub
alias Mobilizon.Activity alias Mobilizon.Activity
require Logger require Logger
@ -68,4 +68,41 @@ defmodule MobilizonWeb.Resolvers.Group do
def create_group(_parent, _args, _resolution) do def create_group(_parent, _args, _resolution) do
{:error, "You need to be logged-in to create a group"} {:error, "You need to be logged-in to create a group"}
end end
@doc """
Delete an existing group
"""
def delete_group(
_parent,
%{group_id: group_id, actor_id: actor_id},
%{
context: %{
current_user: user
}
}
) do
with {:ok, %Actor{} = group} <- Actors.find_group_by_actor_id(group_id),
{:is_owned, true} <- User.owns_actor(user, actor_id),
{:ok, %Member{} = member} <- Member.get_member(actor_id, group.id),
{:is_admin, true} <- Member.is_administrator(member),
group <- Actors.delete_group!(group) do
{:ok, %{id: group.id}}
else
{:error, :group_not_found} ->
{:error, "Group with preferred username not found"}
{:is_owned, false} ->
{:error, "Actor id is not owned by authenticated user"}
{:error, :member_not_found} ->
{:error, "Actor id is not a member of this group"}
{:is_admin, false} ->
{:error, "User is not an administrator of the selected group"}
end
end
def delete_group(_parent, _args, _resolution) do
{:error, "You need to be logged-in to delete a group"}
end
end end

View File

@ -20,6 +20,11 @@ defmodule MobilizonWeb.Schema do
alias MobilizonWeb.Resolvers alias MobilizonWeb.Resolvers
@desc "A struct containing the id of the deleted object"
object :deleted_object do
field(:id, :integer)
end
@desc "A JWT and the associated user ID" @desc "A JWT and the associated user ID"
object :login do object :login do
field(:token, non_null(:string), description: "A JWT Token for this session") field(:token, non_null(:string), description: "A JWT Token for this session")
@ -301,6 +306,14 @@ defmodule MobilizonWeb.Schema do
resolve(&Resolvers.Group.create_group/3) resolve(&Resolvers.Group.create_group/3)
end end
@desc "Delete a group"
field :delete_group, :deleted_object do
arg(:group_id, non_null(:integer))
arg(:actor_id, non_null(:integer))
resolve(&Resolvers.Group.delete_group/3)
end
# @desc "Upload a picture" # @desc "Upload a picture"
# field :upload_picture, :picture do # field :upload_picture, :picture do
# arg(:file, non_null(:upload)) # arg(:file, non_null(:upload))

View File

@ -0,0 +1,13 @@
defmodule Mobilizon.Repo.Migrations.GroupDeleteMembersCascade do
use Ecto.Migration
def change do
drop constraint(:members, "members_account_id_fkey")
drop constraint(:members, "members_parent_id_fkey")
alter table(:members) do
modify :actor_id, references(:actors, on_delete: :delete_all)
modify :parent_id, references(:actors, on_delete: :delete_all)
end
end
end

View File

@ -114,5 +114,36 @@ defmodule MobilizonWeb.Resolvers.GroupResolverTest do
assert hd(json_response(res, 200)["errors"])["message"] == assert hd(json_response(res, 200)["errors"])["message"] ==
"Group with name #{@non_existent_username} not found" "Group with name #{@non_existent_username} not found"
end end
test "delete_group/3 deletes a group", %{conn: conn, user: user, actor: actor} do
group = insert(:group)
insert(:member, parent: group, actor: actor, role: 2)
mutation = """
mutation {
deleteGroup(
actor_id: #{actor.id},
group_id: #{group.id}
) {
id
}
}
"""
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert json_response(res, 200)["errors"] == nil
assert json_response(res, 200)["data"]["deleteGroup"]["id"] == group.id
res =
conn
|> auth_conn(user)
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
assert hd(json_response(res, 200)["errors"])["message"] =~ "not found"
end
end end
end end

View File

@ -148,7 +148,8 @@ defmodule Mobilizon.Factory do
def member_factory do def member_factory do
%Mobilizon.Actors.Member{ %Mobilizon.Actors.Member{
parent: build(:actor), parent: build(:actor),
actor: build(:actor) actor: build(:actor),
role: 0
} }
end end
end end