diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index 181e61137..62f9cc98a 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -266,8 +266,21 @@ defmodule Mobilizon.Actors do [%Mobilizon.Actors.User{}] """ - def list_users do - Repo.all(User) + def list_users(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do + Repo.all( + User + |> paginate(page, limit) + |> sort(sort, direction) + ) + end + + def count_users() do + Repo.one( + from( + u in User, + select: count(u.id) + ) + ) end def insert_or_update_actor(data, preload \\ false) do @@ -300,15 +313,6 @@ defmodule Mobilizon.Actors do # update_and_set_cache(cs) # end - def count_users() do - Repo.one( - from( - u in User, - select: count(u.id) - ) - ) - end - @doc """ Gets a single user. diff --git a/lib/mobilizon/ecto.ex b/lib/mobilizon/ecto.ex index b5ee0d4f7..66fb2ec72 100644 --- a/lib/mobilizon/ecto.ex +++ b/lib/mobilizon/ecto.ex @@ -19,6 +19,16 @@ defmodule Mobilizon.Ecto do ) end + @doc """ + Add sort to the query + """ + def sort(query, sort, direction) do + from( + query, + order_by: [{^direction, ^sort}] + ) + end + def increment_slug(slug) do case List.pop_at(String.split(slug, "-"), -1) do {nil, _} -> diff --git a/lib/mobilizon_web/resolvers/user.ex b/lib/mobilizon_web/resolvers/user.ex index 12594c6a6..3b3786803 100644 --- a/lib/mobilizon_web/resolvers/user.ex +++ b/lib/mobilizon_web/resolvers/user.ex @@ -24,6 +24,20 @@ defmodule MobilizonWeb.Resolvers.User do {:error, "You need to be logged-in to view current user"} end + @doc """ + List instance users + """ + def list_and_count_users( + _parent, + %{page: page, limit: limit, sort: sort, direction: direction}, + _resolution + ) do + total = Task.async(&Actors.count_users/0) + elements = Task.async(fn -> Actors.list_users(page, limit, sort, direction) end) + + {:ok, %{total: Task.await(total), elements: Task.await(elements)}} + end + @doc """ Login an user. Returns a token and the user """ diff --git a/lib/mobilizon_web/schema/sort.ex b/lib/mobilizon_web/schema/sort.ex new file mode 100644 index 000000000..15584044b --- /dev/null +++ b/lib/mobilizon_web/schema/sort.ex @@ -0,0 +1,9 @@ +defmodule MobilizonWeb.Schema.SortType do + use Absinthe.Schema.Notation + + @desc "Available sort directions" + enum :sort_direction do + value(:asc) + value(:desc) + end +end diff --git a/lib/mobilizon_web/schema/user.ex b/lib/mobilizon_web/schema/user.ex index c0eba4562..ebbc73815 100644 --- a/lib/mobilizon_web/schema/user.ex +++ b/lib/mobilizon_web/schema/user.ex @@ -6,6 +6,8 @@ defmodule MobilizonWeb.Schema.UserType do alias MobilizonWeb.Resolvers.User import MobilizonWeb.Schema.Utils + import_types(MobilizonWeb.Schema.SortType) + @desc "A local user of Mobilizon" object :user do field(:id, non_null(:id), description: "The user's ID") @@ -36,6 +38,17 @@ defmodule MobilizonWeb.Schema.UserType do ) end + @desc "Users list" + object :users do + field(:total, non_null(:integer), description: "Total elements") + field(:elements, non_null(list_of(:user)), description: "User elements") + end + + @desc "The list of possible options for the event's status" + enum :sortable_user_field do + value(:id) + end + object :user_queries do @desc "Get an user" field :user, :user do @@ -47,6 +60,17 @@ defmodule MobilizonWeb.Schema.UserType do field :logged_user, :user do resolve(&User.get_current_user/3) end + + @desc "List instance users" + field :users, :users do + arg(:page, :integer, default_value: 1) + arg(:limit, :integer, default_value: 10) + + arg(:sort, :sortable_user_field, default_value: :id) + arg(:direction, :sort_direction, default_value: :asc) + + resolve(&User.list_and_count_users/3) + end end object :user_mutations do diff --git a/test/mobilizon/actors/actors_test.exs b/test/mobilizon/actors/actors_test.exs index 30948c724..b4c3fabba 100644 --- a/test/mobilizon/actors/actors_test.exs +++ b/test/mobilizon/actors/actors_test.exs @@ -274,7 +274,7 @@ defmodule Mobilizon.ActorsTest do test "list_users/0 returns all users" do user = insert(:user) - users = Actors.list_users() + users = Actors.list_users(nil, nil, :id, :desc) assert [user.id] == users |> Enum.map(& &1.id) end diff --git a/test/mobilizon_web/resolvers/user_resolver_test.exs b/test/mobilizon_web/resolvers/user_resolver_test.exs index e0cbd4525..2fcfd5c87 100644 --- a/test/mobilizon_web/resolvers/user_resolver_test.exs +++ b/test/mobilizon_web/resolvers/user_resolver_test.exs @@ -72,6 +72,114 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do end end + describe "Resolver: List users" do + test "list_users/3 returns a list of users", context do + insert(:user, email: "riri@example.com") + insert(:user, email: "fifi@example.com") + insert(:user, email: "loulou@example.com") + + query = """ + { + users { + total, + elements { + email + } + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "user")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["users"]["total"] == 3 + assert json_response(res, 200)["data"]["users"]["elements"] |> length == 3 + + assert json_response(res, 200)["data"]["users"]["elements"] + |> Enum.map(& &1["email"]) == [ + "riri@example.com", + "fifi@example.com", + "loulou@example.com" + ] + + query = """ + { + users(page: 2, limit: 1) { + total, + elements { + email + } + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "user")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["users"]["total"] == 3 + assert json_response(res, 200)["data"]["users"]["elements"] |> length == 1 + + assert json_response(res, 200)["data"]["users"]["elements"] |> Enum.map(& &1["email"]) == [ + "fifi@example.com" + ] + + query = """ + { + users(page: 3, limit: 1, sort: ID, direction: DESC) { + total, + elements { + email + } + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "user")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["users"]["total"] == 3 + assert json_response(res, 200)["data"]["users"]["elements"] |> length == 1 + + assert json_response(res, 200)["data"]["users"]["elements"] |> Enum.map(& &1["email"]) == [ + "riri@example.com" + ] + end + + test "get_current_user/3 returns the current logged-in user", context do + user = insert(:user) + + query = """ + { + loggedUser { + id + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "logged_user")) + + assert json_response(res, 200)["data"]["loggedUser"] == nil + + assert hd(json_response(res, 200)["errors"])["message"] == + "You need to be logged-in to view current user" + + res = + context.conn + |> auth_conn(user) + |> get("/api", AbsintheHelpers.query_skeleton(query, "logged_user")) + + assert json_response(res, 200)["data"]["loggedUser"]["id"] == to_string(user.id) + end + end + describe "Resolver: Create an user & actor" do @user_creation %{ email: "test@demo.tld", @@ -490,7 +598,7 @@ defmodule MobilizonWeb.Resolvers.UserResolverTest do ) { token, user { - id + id } } }