Feature/761 add users filtration Closes #761 See merge request pleroma/pleroma!1117tags/v1.1.4
@@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Pleroma API: Healthcheck endpoint | |||
- Admin API: Endpoints for listing/revoking invite tokens | |||
- Admin API: Endpoints for making users follow/unfollow each other | |||
- Admin API: added filters (role, tags, email, name) for users endpoint | |||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/) | |||
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension) | |||
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension) | |||
@@ -8,15 +8,20 @@ Authentication is required and the user must be an admin. | |||
- Method `GET` | |||
- Query Params: | |||
- *optional* `query`: **string** search term | |||
- *optional* `query`: **string** search term (e.g. nickname, domain, nickname@domain) | |||
- *optional* `filters`: **string** comma-separated string of filters: | |||
- `local`: only local users | |||
- `external`: only external users | |||
- `active`: only active users | |||
- `deactivated`: only deactivated users | |||
- `is_admin`: users with admin role | |||
- `is_moderator`: users with moderator role | |||
- *optional* `page`: **integer** page number | |||
- *optional* `page_size`: **integer** number of users per page (default is `50`) | |||
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10` | |||
- *optional* `tags`: **[string]** tags list | |||
- *optional* `name`: **string** user display name | |||
- *optional* `email`: **string** user email | |||
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com` | |||
- Response: | |||
```JSON | |||
@@ -287,6 +287,29 @@ defmodule Pleroma.Activity do | |||
|> Repo.all() | |||
end | |||
def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do | |||
from( | |||
a in Activity, | |||
where: | |||
fragment( | |||
"? ->> 'type' = 'Follow'", | |||
a.data | |||
), | |||
where: | |||
fragment( | |||
"? ->> 'state' = 'pending'", | |||
a.data | |||
), | |||
where: | |||
fragment( | |||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?", | |||
a.data, | |||
a.data, | |||
^ap_id | |||
) | |||
) | |||
end | |||
@spec query_by_actor(actor()) :: Ecto.Query.t() | |||
def query_by_actor(actor) do | |||
from(a in Activity, where: a.actor == ^actor) | |||
@@ -34,7 +34,7 @@ defmodule Pleroma.Stats do | |||
def update_stats do | |||
peers = | |||
from( | |||
u in Pleroma.User, | |||
u in User, | |||
select: fragment("distinct split_part(?, '@', 2)", u.nickname), | |||
where: u.local != ^true | |||
) | |||
@@ -44,10 +44,13 @@ defmodule Pleroma.Stats do | |||
domain_count = Enum.count(peers) | |||
status_query = | |||
from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info)) | |||
from(u in User.Query.build(%{local: true}), | |||
select: fragment("sum((?->>'note_count')::int)", u.info) | |||
) | |||
status_count = Repo.one(status_query) | |||
user_count = Repo.aggregate(User.active_local_user_query(), :count, :id) | |||
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) | |||
Agent.update(__MODULE__, fn _ -> | |||
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} | |||
@@ -254,10 +254,7 @@ defmodule Pleroma.User do | |||
candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames]) | |||
autofollowed_users = | |||
from(u in User, | |||
where: u.local == true, | |||
where: u.nickname in ^candidates | |||
) | |||
User.Query.build(%{nickname: candidates, local: true}) | |||
|> Repo.all() | |||
follow_all(user, autofollowed_users) | |||
@@ -576,19 +573,17 @@ defmodule Pleroma.User do | |||
) | |||
end | |||
def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do | |||
from( | |||
u in User, | |||
where: fragment("? <@ ?", ^[follower_address], u.following), | |||
where: u.id != ^id | |||
) | |||
@spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() | |||
def get_followers_query(%User{} = user, nil) do | |||
User.Query.build(%{followers: user}) | |||
end | |||
def get_followers_query(user, page) do | |||
from(u in get_followers_query(user, nil)) | |||
|> paginate(page, 20) | |||
|> User.Query.paginate(page, 20) | |||
end | |||
@spec get_followers_query(User.t()) :: Ecto.Query.t() | |||
def get_followers_query(user), do: get_followers_query(user, nil) | |||
def get_followers(user, page \\ nil) do | |||
@@ -603,19 +598,17 @@ defmodule Pleroma.User do | |||
Repo.all(from(u in q, select: u.id)) | |||
end | |||
def get_friends_query(%User{id: id, following: following}, nil) do | |||
from( | |||
u in User, | |||
where: u.follower_address in ^following, | |||
where: u.id != ^id | |||
) | |||
@spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() | |||
def get_friends_query(%User{} = user, nil) do | |||
User.Query.build(%{friends: user}) | |||
end | |||
def get_friends_query(user, page) do | |||
from(u in get_friends_query(user, nil)) | |||
|> paginate(page, 20) | |||
|> User.Query.paginate(page, 20) | |||
end | |||
@spec get_friends_query(User.t()) :: Ecto.Query.t() | |||
def get_friends_query(user), do: get_friends_query(user, nil) | |||
def get_friends(user, page \\ nil) do | |||
@@ -630,33 +623,10 @@ defmodule Pleroma.User do | |||
Repo.all(from(u in q, select: u.id)) | |||
end | |||
def get_follow_requests_query(%User{} = user) do | |||
from( | |||
a in Activity, | |||
where: | |||
fragment( | |||
"? ->> 'type' = 'Follow'", | |||
a.data | |||
), | |||
where: | |||
fragment( | |||
"? ->> 'state' = 'pending'", | |||
a.data | |||
), | |||
where: | |||
fragment( | |||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?", | |||
a.data, | |||
a.data, | |||
^user.ap_id | |||
) | |||
) | |||
end | |||
@spec get_follow_requests(User.t()) :: {:ok, [User.t()]} | |||
def get_follow_requests(%User{} = user) do | |||
users = | |||
user | |||
|> User.get_follow_requests_query() | |||
Activity.follow_requests_for_actor(user) | |||
|> join(:inner, [a], u in User, on: a.actor == u.ap_id) | |||
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) | |||
|> group_by([a, u], u.id) | |||
@@ -729,10 +699,7 @@ defmodule Pleroma.User do | |||
def update_follower_count(%User{} = user) do | |||
follower_count_query = | |||
User | |||
|> where([u], ^user.follower_address in u.following) | |||
|> where([u], u.id != ^user.id) | |||
|> select([u], %{count: count(u.id)}) | |||
User.Query.build(%{followers: user}) |> select([u], %{count: count(u.id)}) | |||
User | |||
|> where(id: ^user.id) | |||
@@ -755,38 +722,19 @@ defmodule Pleroma.User do | |||
end | |||
end | |||
def get_users_from_set_query(ap_ids, false) do | |||
from( | |||
u in User, | |||
where: u.ap_id in ^ap_ids | |||
) | |||
end | |||
def get_users_from_set_query(ap_ids, true) do | |||
query = get_users_from_set_query(ap_ids, false) | |||
from( | |||
u in query, | |||
where: u.local == true | |||
) | |||
end | |||
@spec get_users_from_set([String.t()], boolean()) :: [User.t()] | |||
def get_users_from_set(ap_ids, local_only \\ true) do | |||
get_users_from_set_query(ap_ids, local_only) | |||
criteria = %{ap_id: ap_ids} | |||
criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria | |||
User.Query.build(criteria) | |||
|> Repo.all() | |||
end | |||
@spec get_recipients_from_activity(Activity.t()) :: [User.t()] | |||
def get_recipients_from_activity(%Activity{recipients: to}) do | |||
query = | |||
from( | |||
u in User, | |||
where: u.ap_id in ^to, | |||
or_where: fragment("? && ?", u.following, ^to) | |||
) | |||
query = from(u in query, where: u.local == true) | |||
Repo.all(query) | |||
User.Query.build(%{recipients_from_activity: to, local: true}) | |||
|> Repo.all() | |||
end | |||
def search(query, resolve \\ false, for_user \\ nil) do | |||
@@ -1048,14 +996,23 @@ defmodule Pleroma.User do | |||
end | |||
end | |||
def muted_users(user), | |||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes)) | |||
@spec muted_users(User.t()) :: [User.t()] | |||
def muted_users(user) do | |||
User.Query.build(%{ap_id: user.info.mutes}) | |||
|> Repo.all() | |||
end | |||
def blocked_users(user), | |||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks)) | |||
@spec blocked_users(User.t()) :: [User.t()] | |||
def blocked_users(user) do | |||
User.Query.build(%{ap_id: user.info.blocks}) | |||
|> Repo.all() | |||
end | |||
def subscribers(user), | |||
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers)) | |||
@spec subscribers(User.t()) :: [User.t()] | |||
def subscribers(user) do | |||
User.Query.build(%{ap_id: user.info.subscribers}) | |||
|> Repo.all() | |||
end | |||
def block_domain(user, domain) do | |||
info_cng = | |||
@@ -1081,69 +1038,6 @@ defmodule Pleroma.User do | |||
update_and_set_cache(cng) | |||
end | |||
def maybe_local_user_query(query, local) do | |||
if local, do: local_user_query(query), else: query | |||
end | |||
def local_user_query(query \\ User) do | |||
from( | |||
u in query, | |||
where: u.local == true, | |||
where: not is_nil(u.nickname) | |||
) | |||
end | |||
def maybe_external_user_query(query, external) do | |||
if external, do: external_user_query(query), else: query | |||
end | |||
def external_user_query(query \\ User) do | |||
from( | |||
u in query, | |||
where: u.local == false, | |||
where: not is_nil(u.nickname) | |||
) | |||
end | |||
def maybe_active_user_query(query, active) do | |||
if active, do: active_user_query(query), else: query | |||
end | |||
def active_user_query(query \\ User) do | |||
from( | |||
u in query, | |||
where: fragment("not (?->'deactivated' @> 'true')", u.info), | |||
where: not is_nil(u.nickname) | |||
) | |||
end | |||
def maybe_deactivated_user_query(query, deactivated) do | |||
if deactivated, do: deactivated_user_query(query), else: query | |||
end | |||
def deactivated_user_query(query \\ User) do | |||
from( | |||
u in query, | |||
where: fragment("(?->'deactivated' @> 'true')", u.info), | |||
where: not is_nil(u.nickname) | |||
) | |||
end | |||
def active_local_user_query do | |||
from( | |||
u in local_user_query(), | |||
where: fragment("not (?->'deactivated' @> 'true')", u.info) | |||
) | |||
end | |||
def moderator_user_query do | |||
from( | |||
u in User, | |||
where: u.local == true, | |||
where: fragment("?->'is_moderator' @> 'true'", u.info) | |||
) | |||
end | |||
def deactivate(%User{} = user, status \\ true) do | |||
info_cng = User.Info.set_activation_status(user.info, status) | |||
@@ -1306,7 +1200,7 @@ defmodule Pleroma.User do | |||
def ap_enabled?(_), do: false | |||
@doc "Gets or fetch a user by uri or nickname." | |||
@spec get_or_fetch(String.t()) :: User.t() | |||
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()} | |||
def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri) | |||
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname) | |||
@@ -1423,22 +1317,12 @@ defmodule Pleroma.User do | |||
} | |||
end | |||
@spec all_superusers() :: [User.t()] | |||
def all_superusers do | |||
from( | |||
u in User, | |||
where: u.local == true, | |||
where: fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info) | |||
) | |||
User.Query.build(%{super_users: true, local: true}) | |||
|> Repo.all() | |||
end | |||
defp paginate(query, page, page_size) do | |||
from(u in query, | |||
limit: ^page_size, | |||
offset: ^((page - 1) * page_size) | |||
) | |||
end | |||
def showing_reblogs?(%User{} = user, %User{} = target) do | |||
target.ap_id not in user.info.muted_reblogs | |||
end | |||
@@ -0,0 +1,150 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.User.Query do | |||
@moduledoc """ | |||
User query builder module. Builds query from new query or another user query. | |||
## Example: | |||
query = Pleroma.User.Query(%{nickname: "nickname"}) | |||
another_query = Pleroma.User.Query.build(query, %{email: "email@example.com"}) | |||
Pleroma.Repo.all(query) | |||
Pleroma.Repo.all(another_query) | |||
Adding new rules: | |||
- *ilike criteria* | |||
- add field to @ilike_criteria list | |||
- pass non empty string | |||
- e.g. Pleroma.User.Query.build(%{nickname: "nickname"}) | |||
- *equal criteria* | |||
- add field to @equal_criteria list | |||
- pass non empty string | |||
- e.g. Pleroma.User.Query.build(%{email: "email@example.com"}) | |||
- *contains criteria* | |||
- add field to @containns_criteria list | |||
- pass values list | |||
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]}) | |||
""" | |||
import Ecto.Query | |||
import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1] | |||
alias Pleroma.User | |||
@type criteria :: | |||
%{ | |||
query: String.t(), | |||
tags: [String.t()], | |||
name: String.t(), | |||
email: String.t(), | |||
local: boolean(), | |||
external: boolean(), | |||
active: boolean(), | |||
deactivated: boolean(), | |||
is_admin: boolean(), | |||
is_moderator: boolean(), | |||
super_users: boolean(), | |||
followers: User.t(), | |||
friends: User.t(), | |||
recipients_from_activity: [String.t()], | |||
nickname: [String.t()], | |||
ap_id: [String.t()] | |||
} | |||
| %{} | |||
@ilike_criteria [:nickname, :name, :query] | |||
@equal_criteria [:email] | |||
@role_criteria [:is_admin, :is_moderator] | |||
@contains_criteria [:ap_id, :nickname] | |||
@spec build(criteria()) :: Query.t() | |||
def build(query \\ base_query(), criteria) do | |||
prepare_query(query, criteria) | |||
end | |||
@spec paginate(Ecto.Query.t(), pos_integer(), pos_integer()) :: Ecto.Query.t() | |||
def paginate(query, page, page_size) do | |||
from(u in query, | |||
limit: ^page_size, | |||
offset: ^((page - 1) * page_size) | |||
) | |||
end | |||
defp base_query do | |||
from(u in User) | |||
end | |||
defp prepare_query(query, criteria) do | |||
Enum.reduce(criteria, query, &compose_query/2) | |||
end | |||
defp compose_query({key, value}, query) | |||
when key in @ilike_criteria and not_empty_string(value) do | |||
# hack for :query key | |||
key = if key == :query, do: :nickname, else: key | |||
where(query, [u], ilike(field(u, ^key), ^"%#{value}%")) | |||
end | |||
defp compose_query({key, value}, query) | |||
when key in @equal_criteria and not_empty_string(value) do | |||
where(query, [u], ^[{key, value}]) | |||
end | |||
defp compose_query({key, values}, query) when key in @contains_criteria and is_list(values) do | |||
where(query, [u], field(u, ^key) in ^values) | |||
end | |||
defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 do | |||
Enum.reduce(tags, query, &prepare_tag_criteria/2) | |||
end | |||
defp compose_query({key, _}, query) when key in @role_criteria do | |||
where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key))) | |||
end | |||
defp compose_query({:super_users, _}, query) do | |||
where( | |||
query, | |||
[u], | |||
fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info) | |||
) | |||
end | |||
defp compose_query({:local, _}, query), do: location_query(query, true) | |||
defp compose_query({:external, _}, query), do: location_query(query, false) | |||
defp compose_query({:active, _}, query) do | |||
where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info)) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
defp compose_query({:deactivated, _}, query) do | |||
where(query, [u], fragment("?->'deactivated' @> 'true'", u.info)) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do | |||
where(query, [u], fragment("? <@ ?", ^[follower_address], u.following)) | |||
|> where([u], u.id != ^id) | |||
end | |||
defp compose_query({:friends, %User{id: id, following: following}}, query) do | |||
where(query, [u], u.follower_address in ^following) | |||
|> where([u], u.id != ^id) | |||
end | |||
defp compose_query({:recipients_from_activity, to}, query) do | |||
where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to)) | |||
end | |||
defp compose_query(_unsupported_param, query), do: query | |||
defp prepare_tag_criteria(tag, query) do | |||
or_where(query, [u], fragment("? = any(?)", ^tag, u.tags)) | |||
end | |||
defp location_query(query, local) do | |||
where(query, [u], u.local == ^local) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
end |
@@ -101,7 +101,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
search_params = %{ | |||
query: params["query"], | |||
page: page, | |||
page_size: page_size | |||
page_size: page_size, | |||
tags: params["tags"], | |||
name: params["name"], | |||
email: params["email"] | |||
} | |||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)), | |||
@@ -116,11 +119,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
) | |||
end | |||
@filters ~w(local external active deactivated) | |||
@filters ~w(local external active deactivated is_admin is_moderator) | |||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} | |||
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} | |||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} | |||
defp maybe_parse_filters(filters) do | |||
filters | |||
|> String.split(",") | |||
@@ -10,45 +10,23 @@ defmodule Pleroma.Web.AdminAPI.Search do | |||
@page_size 50 | |||
def user(%{query: term} = params) when is_nil(term) or term == "" do | |||
query = maybe_filtered_query(params) | |||
defmacro not_empty_string(string) do | |||
quote do | |||
is_binary(unquote(string)) and unquote(string) != "" | |||
end | |||
end | |||
@spec user(map()) :: {:ok, [User.t()], pos_integer()} | |||
def user(params \\ %{}) do | |||
query = User.Query.build(params) |> order_by([u], u.nickname) | |||
paginated_query = | |||
maybe_filtered_query(params) | |||
|> paginate(params[:page] || 1, params[:page_size] || @page_size) | |||
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size) | |||
count = query |> Repo.aggregate(:count, :id) | |||
count = Repo.aggregate(query, :count, :id) | |||
results = Repo.all(paginated_query) | |||
{:ok, results, count} | |||
end | |||
def user(%{query: term} = params) when is_binary(term) do | |||
search_query = from(u in maybe_filtered_query(params), where: ilike(u.nickname, ^"%#{term}%")) | |||
count = search_query |> Repo.aggregate(:count, :id) | |||
results = | |||
search_query | |||
|> paginate(params[:page] || 1, params[:page_size] || @page_size) | |||
|> Repo.all() | |||
{:ok, results, count} | |||
end | |||
defp maybe_filtered_query(params) do | |||
from(u in User, order_by: u.nickname) | |||
|> User.maybe_local_user_query(params[:local]) | |||
|> User.maybe_external_user_query(params[:external]) | |||
|> User.maybe_active_user_query(params[:active]) | |||
|> User.maybe_deactivated_user_query(params[:deactivated]) | |||
end | |||
defp paginate(query, page, page_size) do | |||
from(u in query, | |||
limit: ^page_size, | |||
offset: ^((page - 1) * page_size) | |||
) | |||
end | |||
end |
@@ -419,14 +419,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
end | |||
describe "GET /api/pleroma/admin/users" do | |||
test "renders users array for the first page" do | |||
setup do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
user = insert(:user, local: false, tags: ["foo", "bar"]) | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/users?page=1") | |||
{:ok, conn: conn, admin: admin} | |||
end | |||
test "renders users array for the first page", %{conn: conn, admin: admin} do | |||
user = insert(:user, local: false, tags: ["foo", "bar"]) | |||
conn = get(conn, "/api/pleroma/admin/users?page=1") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 2, | |||
@@ -452,14 +457,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
} | |||
end | |||
test "renders empty array for the second page" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "renders empty array for the second page", %{conn: conn} do | |||
insert(:user) | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/users?page=2") | |||
conn = get(conn, "/api/pleroma/admin/users?page=2") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 2, | |||
@@ -468,14 +469,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
} | |||
end | |||
test "regular search" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "regular search", %{conn: conn} do | |||
user = insert(:user, nickname: "bob") | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/users?query=bo") | |||
conn = get(conn, "/api/pleroma/admin/users?query=bo") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
@@ -493,17 +490,101 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
} | |||
end | |||
test "regular search with page size" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "search by domain", %{conn: conn} do | |||
user = insert(:user, nickname: "nickname@domain.com") | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?query=domain.com") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => user.info.deactivated, | |||
"id" => user.id, | |||
"nickname" => user.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => true, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "search by full nickname", %{conn: conn} do | |||
user = insert(:user, nickname: "nickname@domain.com") | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => user.info.deactivated, | |||
"id" => user.id, | |||
"nickname" => user.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => true, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "search by display name", %{conn: conn} do | |||
user = insert(:user, name: "Display name") | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?name=display") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => user.info.deactivated, | |||
"id" => user.id, | |||
"nickname" => user.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => true, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "search by email", %{conn: conn} do | |||
user = insert(:user, email: "email@example.com") | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => user.info.deactivated, | |||
"id" => user.id, | |||
"nickname" => user.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => true, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "regular search with page size", %{conn: conn} do | |||
user = insert(:user, nickname: "aalice") | |||
user2 = insert(:user, nickname: "alice") | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/users?query=a&page_size=1&page=1") | |||
conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") | |||
assert json_response(conn, 200) == %{ | |||
assert json_response(conn1, 200) == %{ | |||
"count" => 2, | |||
"page_size" => 1, | |||
"users" => [ | |||
@@ -518,12 +599,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
] | |||
} | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/users?query=a&page_size=1&page=2") | |||
conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") | |||
assert json_response(conn, 200) == %{ | |||
assert json_response(conn2, 200) == %{ | |||
"count" => 2, | |||
"page_size" => 1, | |||
"users" => [ | |||
@@ -566,7 +644,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
} | |||
end | |||
test "only local users with no query" do | |||
test "only local users with no query", %{admin: old_admin} do | |||
admin = insert(:user, info: %{is_admin: true}, nickname: "john") | |||
user = insert(:user, nickname: "bob") | |||
@@ -578,7 +656,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
|> get("/api/pleroma/admin/users?filters=local") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 2, | |||
"count" => 3, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
@@ -596,6 +674,100 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
"roles" => %{"admin" => true, "moderator" => false}, | |||
"local" => true, | |||
"tags" => [] | |||
}, | |||
%{ | |||
"deactivated" => false, | |||
"id" => old_admin.id, | |||
"local" => true, | |||
"nickname" => old_admin.nickname, | |||
"roles" => %{"admin" => true, "moderator" => false}, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "load only admins", %{conn: conn, admin: admin} do | |||
second_admin = insert(:user, info: %{is_admin: true}) | |||
insert(:user) | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 2, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => false, | |||
"id" => admin.id, | |||
"nickname" => admin.nickname, | |||
"roles" => %{"admin" => true, "moderator" => false}, | |||
"local" => admin.local, | |||
"tags" => [] | |||
}, | |||
%{ | |||
"deactivated" => false, | |||
"id" => second_admin.id, | |||
"nickname" => second_admin.nickname, | |||
"roles" => %{"admin" => true, "moderator" => false}, | |||
"local" => second_admin.local, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "load only moderators", %{conn: conn} do | |||
moderator = insert(:user, info: %{is_moderator: true}) | |||
insert(:user) | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 1, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => false, | |||
"id" => moderator.id, | |||
"nickname" => moderator.nickname, | |||
"roles" => %{"admin" => false, "moderator" => true}, | |||
"local" => moderator.local, | |||
"tags" => [] | |||
} | |||
] | |||
} | |||
end | |||
test "load users with tags list", %{conn: conn} do | |||
user1 = insert(:user, tags: ["first"]) | |||
user2 = insert(:user, tags: ["second"]) | |||
insert(:user) | |||
insert(:user) | |||
conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") | |||
assert json_response(conn, 200) == %{ | |||
"count" => 2, | |||
"page_size" => 50, | |||
"users" => [ | |||
%{ | |||
"deactivated" => false, | |||
"id" => user1.id, | |||
"nickname" => user1.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => user1.local, | |||
"tags" => ["first"] | |||
}, | |||
%{ | |||
"deactivated" => false, | |||
"id" => user2.id, | |||
"nickname" => user2.nickname, | |||
"roles" => %{"admin" => false, "moderator" => false}, | |||
"local" => user2.local, | |||
"tags" => ["second"] | |||
} | |||
] | |||
} | |||
@@ -651,13 +823,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
end | |||
describe "GET /api/pleroma/admin/invite_token" do | |||
test "without options" do | |||
setup do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invite_token") | |||
{:ok, conn: conn} | |||
end | |||
test "without options", %{conn: conn} do | |||
conn = get(conn, "/api/pleroma/admin/invite_token") | |||
token = json_response(conn, 200) | |||
invite = UserInviteToken.find_by_token!(token) | |||
@@ -667,13 +844,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert invite.invite_type == "one_time" | |||
end | |||
test "with expires_at" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "with expires_at", %{conn: conn} do | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invite_token", %{ | |||
get(conn, "/api/pleroma/admin/invite_token", %{ | |||
"invite" => %{"expires_at" => Date.to_string(Date.utc_today())} | |||
}) | |||
@@ -686,13 +859,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert invite.invite_type == "date_limited" | |||
end | |||
test "with max_use" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "with max_use", %{conn: conn} do | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invite_token", %{ | |||
get(conn, "/api/pleroma/admin/invite_token", %{ | |||
"invite" => %{"max_use" => 150} | |||
}) | |||
@@ -704,13 +873,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert invite.invite_type == "reusable" | |||
end | |||
test "with max use and expires_at" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "with max use and expires_at", %{conn: conn} do | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invite_token", %{ | |||
get(conn, "/api/pleroma/admin/invite_token", %{ | |||
"invite" => %{"max_use" => 150, "expires_at" => Date.to_string(Date.utc_today())} | |||
}) | |||
@@ -724,25 +889,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
end | |||
describe "GET /api/pleroma/admin/invites" do | |||
test "no invites" do | |||
setup do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invites") | |||
{:ok, conn: conn} | |||
end | |||
test "no invites", %{conn: conn} do | |||
conn = get(conn, "/api/pleroma/admin/invites") | |||
assert json_response(conn, 200) == %{"invites" => []} | |||
end | |||
test "with invite" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
test "with invite", %{conn: conn} do | |||
{:ok, invite} = UserInviteToken.create_invite() | |||
conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> get("/api/pleroma/admin/invites") | |||
conn = get(conn, "/api/pleroma/admin/invites") | |||
assert json_response(conn, 200) == %{ | |||
"invites" => [ | |||
@@ -70,11 +70,11 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do | |||
test "it returns specific user" do | |||
insert(:user) | |||
insert(:user) | |||
insert(:user, nickname: "bob", local: true, info: %{deactivated: false}) | |||
user = insert(:user, nickname: "bob", local: true, info: %{deactivated: false}) | |||
{:ok, _results, total_count} = Search.user(%{query: ""}) | |||
{:ok, _results, count} = | |||
{:ok, [^user], count} = | |||
Search.user(%{ | |||
query: "Bo", | |||
active: true, | |||
@@ -84,5 +84,87 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do | |||
assert total_count == 3 | |||
assert count == 1 | |||
end | |||
test "it returns user by domain" do | |||
insert(:user) | |||
insert(:user) | |||
user = insert(:user, nickname: "some@domain.com") | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^user], count} = Search.user(%{query: "domain.com"}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
test "it return user by full nickname" do | |||
insert(:user) | |||
insert(:user) | |||
user = insert(:user, nickname: "some@domain.com") | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^user], count} = Search.user(%{query: "some@domain.com"}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
test "it returns admin user" do | |||
admin = insert(:user, info: %{is_admin: true}) | |||
insert(:user) | |||
insert(:user) | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^admin], count} = Search.user(%{is_admin: true}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
test "it returns moderator user" do | |||
moderator = insert(:user, info: %{is_moderator: true}) | |||
insert(:user) | |||
insert(:user) | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^moderator], count} = Search.user(%{is_moderator: true}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
test "it returns users with tags" do | |||
user1 = insert(:user, tags: ["first"]) | |||
user2 = insert(:user, tags: ["second"]) | |||
insert(:user) | |||
insert(:user) | |||
{:ok, _results, total} = Search.user() | |||
{:ok, users, count} = Search.user(%{tags: ["first", "second"]}) | |||
assert total == 4 | |||
assert count == 2 | |||
assert user1 in users | |||
assert user2 in users | |||
end | |||
test "it returns user by display name" do | |||
user = insert(:user, name: "Display name") | |||
insert(:user) | |||
insert(:user) | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^user], count} = Search.user(%{name: "display"}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
test "it returns user by email" do | |||
user = insert(:user, email: "some@example.com") | |||
insert(:user) | |||
insert(:user) | |||
{:ok, _results, total} = Search.user() | |||
{:ok, [^user], count} = Search.user(%{email: "some@example.com"}) | |||
assert total == 3 | |||
assert count == 1 | |||
end | |||
end | |||
end |