Add ability to search on Group, Person or Event
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
20a4f7244c
commit
d66bbc5414
@ -498,12 +498,17 @@ defmodule Mobilizon.Actors do
|
|||||||
@doc """
|
@doc """
|
||||||
Find actors by their name or displayed name
|
Find actors by their name or displayed name
|
||||||
"""
|
"""
|
||||||
@spec find_actors_by_username_or_name(String.t(), integer(), integer()) :: list(Actor.t())
|
@spec find_and_count_actors_by_username_or_name(
|
||||||
def find_actors_by_username_or_name(username, page \\ nil, limit \\ nil)
|
String.t(),
|
||||||
def find_actors_by_username_or_name("", _page, _limit), do: []
|
[ActorTypeEnum.t()],
|
||||||
|
integer() | nil,
|
||||||
|
integer() | nil
|
||||||
|
) ::
|
||||||
|
%{total: integer(), elements: list(Actor.t())}
|
||||||
|
def find_and_count_actors_by_username_or_name(username, _types, page \\ nil, limit \\ nil)
|
||||||
|
|
||||||
def find_actors_by_username_or_name(username, page, limit) do
|
def find_and_count_actors_by_username_or_name(username, types, page, limit) do
|
||||||
Repo.all(
|
query =
|
||||||
from(
|
from(
|
||||||
a in Actor,
|
a in Actor,
|
||||||
where:
|
where:
|
||||||
@ -515,6 +520,7 @@ defmodule Mobilizon.Actors do
|
|||||||
a.name,
|
a.name,
|
||||||
^username
|
^username
|
||||||
),
|
),
|
||||||
|
where: a.type in ^types,
|
||||||
order_by:
|
order_by:
|
||||||
fragment(
|
fragment(
|
||||||
"word_similarity(?, ?) + word_similarity(coalesce(?, ''), ?) desc",
|
"word_similarity(?, ?) + word_similarity(coalesce(?, ''), ?) desc",
|
||||||
@ -525,7 +531,11 @@ defmodule Mobilizon.Actors do
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> paginate(page, limit)
|
|> paginate(page, limit)
|
||||||
)
|
|
||||||
|
total = Task.async(fn -> Repo.aggregate(query, :count, :id) end)
|
||||||
|
elements = Task.async(fn -> Repo.all(query) end)
|
||||||
|
|
||||||
|
%{total: Task.await(total), elements: Task.await(elements)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -246,10 +246,9 @@ defmodule Mobilizon.Events do
|
|||||||
@doc """
|
@doc """
|
||||||
Find events by name
|
Find events by name
|
||||||
"""
|
"""
|
||||||
def find_events_by_name(name, page \\ nil, limit \\ nil)
|
def find_and_count_events_by_name(name, page \\ nil, limit \\ nil)
|
||||||
def find_events_by_name("", page, limit), do: list_events(page, limit)
|
|
||||||
|
|
||||||
def find_events_by_name(name, page, limit) do
|
def find_and_count_events_by_name(name, page, limit) do
|
||||||
name = String.trim(name)
|
name = String.trim(name)
|
||||||
|
|
||||||
query =
|
query =
|
||||||
@ -271,7 +270,10 @@ defmodule Mobilizon.Events do
|
|||||||
)
|
)
|
||||||
|> paginate(page, limit)
|
|> paginate(page, limit)
|
||||||
|
|
||||||
Repo.all(query)
|
total = Task.async(fn -> Repo.aggregate(query, :count, :id) end)
|
||||||
|
elements = Task.async(fn -> Repo.all(query) end)
|
||||||
|
|
||||||
|
%{total: Task.await(total), elements: Task.await(elements)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -11,92 +11,98 @@ defmodule MobilizonWeb.API.Search do
|
|||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Search
|
Search actors
|
||||||
"""
|
"""
|
||||||
@spec search(String.t(), integer(), integer()) ::
|
@spec search_actors(String.t(), integer(), integer(), String.t()) ::
|
||||||
{:ok, list(Actor.t())} | {:ok, []} | {:error, any()}
|
{:ok, %{total: integer(), elements: list(Actor.t())}} | {:error, any()}
|
||||||
def search(search, page \\ 1, limit \\ 10) do
|
def search_actors(search, page \\ 1, limit \\ 10, result_type) do
|
||||||
do_search(search, page, limit, %{events: true, actors: true})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Not used at the moment
|
|
||||||
"""
|
|
||||||
# TODO: Use me
|
|
||||||
@spec search_actors(String.t(), integer(), integer()) ::
|
|
||||||
{:ok, list(Actor.t())} | {:ok, []} | {:error, any()}
|
|
||||||
def search_actors(search, page \\ 1, limit \\ 10) do
|
|
||||||
do_search(search, page, limit, %{actors: true})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Not used at the moment
|
|
||||||
"""
|
|
||||||
# TODO: Use me
|
|
||||||
@spec search_events(String.t(), integer(), integer()) ::
|
|
||||||
{:ok, list(Event.t())} | {:ok, []} | {:error, any()}
|
|
||||||
def search_events(search, page \\ 1, limit \\ 10) do
|
|
||||||
do_search(search, page, limit, %{events: true})
|
|
||||||
end
|
|
||||||
|
|
||||||
# Do the actual search
|
|
||||||
@spec do_search(String.t(), integer(), integer(), map()) :: {:ok, list(any())}
|
|
||||||
defp do_search(search, page, limit, opts) do
|
|
||||||
search = String.trim(search)
|
search = String.trim(search)
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
search == "" ->
|
search == "" ->
|
||||||
{:error, "Search can't be empty"}
|
{:error, "Search can't be empty"}
|
||||||
|
|
||||||
String.match?(search, ~r/@/) ->
|
# Some URLs could be domain.tld/@username, so keep this condition above handle_search? function
|
||||||
|
url_search?(search) ->
|
||||||
|
# If this is not an actor, skip
|
||||||
|
with %{:total => total, :elements => [%Actor{}] = elements} <- process_from_url(search) do
|
||||||
|
{:ok, %{total: total, elements: elements}}
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
{:ok, %{total: 0, elements: []}}
|
||||||
|
end
|
||||||
|
|
||||||
|
handle_search?(search) ->
|
||||||
{:ok, process_from_username(search)}
|
{:ok, process_from_username(search)}
|
||||||
|
|
||||||
String.starts_with?(search, "https://") ->
|
true ->
|
||||||
{:ok, process_from_url(search)}
|
{:ok,
|
||||||
|
Actors.find_and_count_actors_by_username_or_name(search, [result_type], page, limit)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
String.starts_with?(search, "http://") ->
|
@doc """
|
||||||
{:ok, process_from_url(search)}
|
Search events
|
||||||
|
"""
|
||||||
|
@spec search_events(String.t(), integer(), integer()) ::
|
||||||
|
{:ok, %{total: integer(), elements: list(Event.t())}} | {:error, any()}
|
||||||
|
def search_events(search, page \\ 1, limit \\ 10) do
|
||||||
|
search = String.trim(search)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
search == "" ->
|
||||||
|
{:error, "Search can't be empty"}
|
||||||
|
|
||||||
|
url_search?(search) ->
|
||||||
|
# If this is not an event, skip
|
||||||
|
with {total = total, [%Event{} = elements]} <- process_from_url(search) do
|
||||||
|
{:ok, %{total: total, elements: elements}}
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
{:ok, %{total: 0, elements: []}}
|
||||||
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
events =
|
{:ok, Events.find_and_count_events_by_name(search, page, limit)}
|
||||||
Task.async(fn ->
|
|
||||||
if Map.get(opts, :events, false),
|
|
||||||
do: Events.find_events_by_name(search, page, limit),
|
|
||||||
else: []
|
|
||||||
end)
|
|
||||||
|
|
||||||
actors =
|
|
||||||
Task.async(fn ->
|
|
||||||
if Map.get(opts, :actors, false),
|
|
||||||
do: Actors.find_actors_by_username_or_name(search, page, limit),
|
|
||||||
else: []
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:ok, Task.await(events) ++ Task.await(actors)}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the search string is an username
|
# If the search string is an username
|
||||||
@spec process_from_username(String.t()) :: Actor.t() | nil
|
@spec process_from_username(String.t()) :: %{total: integer(), elements: [Actor.t()]}
|
||||||
defp process_from_username(search) do
|
defp process_from_username(search) do
|
||||||
with {:ok, actor} <- ActivityPub.find_or_make_actor_from_nickname(search) do
|
with {:ok, actor} <- ActivityPub.find_or_make_actor_from_nickname(search) do
|
||||||
actor
|
%{total: 1, elements: [actor]}
|
||||||
else
|
else
|
||||||
{:error, _err} ->
|
{:error, _err} ->
|
||||||
Logger.debug(fn -> "Unable to find or make actor '#{search}'" end)
|
Logger.debug(fn -> "Unable to find or make actor '#{search}'" end)
|
||||||
nil
|
%{total: 0, elements: []}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the search string is an URL
|
# If the search string is an URL
|
||||||
@spec process_from_url(String.t()) :: Actor.t() | Event.t() | Comment.t() | nil
|
@spec process_from_url(String.t()) :: %{
|
||||||
|
total: integer(),
|
||||||
|
elements: [Actor.t() | Event.t() | Comment.t()]
|
||||||
|
}
|
||||||
defp process_from_url(search) do
|
defp process_from_url(search) do
|
||||||
with {:ok, object} <- ActivityPub.fetch_object_from_url(search) do
|
with {:ok, object} <- ActivityPub.fetch_object_from_url(search) do
|
||||||
object
|
%{total: 1, elements: [object]}
|
||||||
else
|
else
|
||||||
{:error, _err} ->
|
{:error, _err} ->
|
||||||
Logger.debug(fn -> "Unable to find or make object from URL '#{search}'" end)
|
Logger.debug(fn -> "Unable to find or make object from URL '#{search}'" end)
|
||||||
nil
|
%{total: 0, elements: []}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Is the search an URL search?
|
||||||
|
@spec url_search?(String.t()) :: boolean
|
||||||
|
defp url_search?(search) do
|
||||||
|
String.starts_with?(search, "https://") or String.starts_with?(search, "http://")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is the search an handle search?
|
||||||
|
@spec handle_search?(String.t()) :: boolean
|
||||||
|
defp handle_search?(search) do
|
||||||
|
String.match?(search, ~r/@/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,9 +5,23 @@ defmodule MobilizonWeb.Resolvers.Search do
|
|||||||
alias MobilizonWeb.API.Search
|
alias MobilizonWeb.API.Search
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Search events and actors by title
|
Search persons
|
||||||
"""
|
"""
|
||||||
def search_events_and_actors(_parent, %{search: search, page: page, limit: limit}, _resolution) do
|
def search_persons(_parent, %{search: search, page: page, limit: limit}, _resolution) do
|
||||||
Search.search(search, page, limit)
|
Search.search_actors(search, page, limit, :Person)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Search groups
|
||||||
|
"""
|
||||||
|
def search_groups(_parent, %{search: search, page: page, limit: limit}, _resolution) do
|
||||||
|
Search.search_actors(search, page, limit, :Group)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Search events
|
||||||
|
"""
|
||||||
|
def search_events(_parent, %{search: search, page: page, limit: limit}, _resolution) do
|
||||||
|
Search.search_events(search, page, limit)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,10 +18,9 @@ defmodule MobilizonWeb.Schema do
|
|||||||
import_types(MobilizonWeb.Schema.Actors.PersonType)
|
import_types(MobilizonWeb.Schema.Actors.PersonType)
|
||||||
import_types(MobilizonWeb.Schema.Actors.GroupType)
|
import_types(MobilizonWeb.Schema.Actors.GroupType)
|
||||||
import_types(MobilizonWeb.Schema.CommentType)
|
import_types(MobilizonWeb.Schema.CommentType)
|
||||||
|
import_types(MobilizonWeb.Schema.SearchType)
|
||||||
import_types(MobilizonWeb.Schema.ConfigType)
|
import_types(MobilizonWeb.Schema.ConfigType)
|
||||||
|
|
||||||
alias MobilizonWeb.Resolvers
|
|
||||||
|
|
||||||
@desc "A struct containing the id of the deleted object"
|
@desc "A struct containing the id of the deleted object"
|
||||||
object :deleted_object do
|
object :deleted_object do
|
||||||
field(:id, :integer)
|
field(:id, :integer)
|
||||||
@ -85,22 +84,6 @@ defmodule MobilizonWeb.Schema do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "A search result"
|
|
||||||
union :search_result do
|
|
||||||
types([:event, :person, :group])
|
|
||||||
|
|
||||||
resolve_type(fn
|
|
||||||
%Actor{type: :Person}, _ ->
|
|
||||||
:person
|
|
||||||
|
|
||||||
%Actor{type: :Group}, _ ->
|
|
||||||
:group
|
|
||||||
|
|
||||||
%Event{}, _ ->
|
|
||||||
:event
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def context(ctx) do
|
def context(ctx) do
|
||||||
loader =
|
loader =
|
||||||
Dataloader.new()
|
Dataloader.new()
|
||||||
@ -120,14 +103,7 @@ defmodule MobilizonWeb.Schema do
|
|||||||
Root Query
|
Root Query
|
||||||
"""
|
"""
|
||||||
query do
|
query do
|
||||||
@desc "Search through events, persons and groups"
|
import_fields(:search_queries)
|
||||||
field :search, list_of(:search_result) do
|
|
||||||
arg(:search, non_null(:string))
|
|
||||||
arg(:page, :integer, default_value: 1)
|
|
||||||
arg(:limit, :integer, default_value: 10)
|
|
||||||
resolve(&Resolvers.Search.search_events_and_actors/3)
|
|
||||||
end
|
|
||||||
|
|
||||||
import_fields(:user_queries)
|
import_fields(:user_queries)
|
||||||
import_fields(:person_queries)
|
import_fields(:person_queries)
|
||||||
import_fields(:group_queries)
|
import_fields(:group_queries)
|
||||||
|
55
lib/mobilizon_web/schema/search.ex
Normal file
55
lib/mobilizon_web/schema/search.ex
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
defmodule MobilizonWeb.Schema.SearchType do
|
||||||
|
@moduledoc """
|
||||||
|
Schema representation for Search
|
||||||
|
"""
|
||||||
|
use Absinthe.Schema.Notation
|
||||||
|
|
||||||
|
alias MobilizonWeb.Resolvers
|
||||||
|
|
||||||
|
@desc "Search persons result"
|
||||||
|
object :persons do
|
||||||
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
|
field(:elements, non_null(list_of(:person)), description: "Person elements")
|
||||||
|
end
|
||||||
|
|
||||||
|
@desc "Search groups result"
|
||||||
|
object :groups do
|
||||||
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
|
field(:elements, non_null(list_of(:group)), description: "Group elements")
|
||||||
|
end
|
||||||
|
|
||||||
|
@desc "Search events result"
|
||||||
|
object :events do
|
||||||
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
|
field(:elements, non_null(list_of(:event)), description: "Event elements")
|
||||||
|
end
|
||||||
|
|
||||||
|
object :search_queries do
|
||||||
|
@desc "Search persons"
|
||||||
|
field :search_persons, :persons do
|
||||||
|
arg(:search, non_null(:string))
|
||||||
|
arg(:page, :integer, default_value: 1)
|
||||||
|
arg(:limit, :integer, default_value: 10)
|
||||||
|
|
||||||
|
resolve(&Resolvers.Search.search_persons/3)
|
||||||
|
end
|
||||||
|
|
||||||
|
@desc "Search groups"
|
||||||
|
field :search_groups, :groups do
|
||||||
|
arg(:search, non_null(:string))
|
||||||
|
arg(:page, :integer, default_value: 1)
|
||||||
|
arg(:limit, :integer, default_value: 10)
|
||||||
|
|
||||||
|
resolve(&Resolvers.Search.search_groups/3)
|
||||||
|
end
|
||||||
|
|
||||||
|
@desc "Search events"
|
||||||
|
field :search_events, :events do
|
||||||
|
arg(:search, non_null(:string))
|
||||||
|
arg(:page, :integer, default_value: 1)
|
||||||
|
arg(:limit, :integer, default_value: 10)
|
||||||
|
|
||||||
|
resolve(&Resolvers.Search.search_events/3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -184,19 +184,26 @@ defmodule Mobilizon.ActorsTest do
|
|||||||
assert MapSet.new([actor_found_id, actor2_found_id]) == MapSet.new([actor.id, actor2.id])
|
assert MapSet.new([actor_found_id, actor2_found_id]) == MapSet.new([actor.id, actor2.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test find_actors_by_username_or_name/1 returns actors with similar usernames", %{
|
test "test find_and_count_actors_by_username_or_name/4 returns actors with similar usernames",
|
||||||
actor: %Actor{id: actor_id}
|
%{
|
||||||
} do
|
actor: %Actor{id: actor_id}
|
||||||
|
} do
|
||||||
use_cassette "actors/remote_actor_mastodon_tcit" do
|
use_cassette "actors/remote_actor_mastodon_tcit" do
|
||||||
with {:ok, %Actor{id: actor2_id}} <- Actors.get_or_fetch_by_url(@remote_account_url) do
|
with {:ok, %Actor{id: actor2_id}} <- Actors.get_or_fetch_by_url(@remote_account_url) do
|
||||||
actors_ids = Actors.find_actors_by_username_or_name("tcit") |> Enum.map(& &1.id)
|
%{total: 2, elements: actors} =
|
||||||
|
Actors.find_and_count_actors_by_username_or_name("tcit", [:Person])
|
||||||
|
|
||||||
|
actors_ids = actors |> Enum.map(& &1.id)
|
||||||
|
|
||||||
assert MapSet.new(actors_ids) == MapSet.new([actor2_id, actor_id])
|
assert MapSet.new(actors_ids) == MapSet.new([actor2_id, actor_id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test find_actors_by_username_or_name/1 returns actors with similar names" do
|
test "test find_and_count_actors_by_username_or_name/4 returns actors with similar names" do
|
||||||
actors = Actors.find_actors_by_username_or_name("ohno")
|
%{total: 0, elements: actors} =
|
||||||
|
Actors.find_and_count_actors_by_username_or_name("ohno", [:Person])
|
||||||
|
|
||||||
assert actors == []
|
assert actors == []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,18 +54,22 @@ defmodule Mobilizon.EventsTest do
|
|||||||
assert Events.get_event_full!(event.id).participants == []
|
assert Events.get_event_full!(event.id).participants == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find_events_by_name/1 returns events for a given name", %{
|
test "find_and_count_events_by_name/1 returns events for a given name", %{
|
||||||
event: %Event{title: title} = event
|
event: %Event{title: title} = event
|
||||||
} do
|
} do
|
||||||
assert title == hd(Events.find_events_by_name(event.title)).title
|
assert title == hd(Events.find_and_count_events_by_name(event.title).elements).title
|
||||||
|
|
||||||
%Event{title: title2} = event2 = insert(:event, title: "Special event")
|
%Event{} = event2 = insert(:event, title: "Special event")
|
||||||
assert event2.title == Events.find_events_by_name("Special") |> hd() |> Map.get(:title)
|
|
||||||
|
|
||||||
assert event2.title == Events.find_events_by_name(" Special ") |> hd() |> Map.get(:title)
|
assert event2.title ==
|
||||||
|
Events.find_and_count_events_by_name("Special").elements |> hd() |> Map.get(:title)
|
||||||
|
|
||||||
assert title == Events.find_events_by_name("") |> hd() |> Map.get(:title)
|
assert event2.title ==
|
||||||
assert title2 == Events.find_events_by_name("") |> tl |> hd() |> Map.get(:title)
|
Events.find_and_count_events_by_name(" Special ").elements
|
||||||
|
|> hd()
|
||||||
|
|> Map.get(:title)
|
||||||
|
|
||||||
|
assert %{elements: [], total: 0} == Events.find_and_count_events_by_name("")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "find_close_events/3 returns events in the area" do
|
test "find_close_events/3 returns events in the area" do
|
||||||
|
@ -12,44 +12,45 @@ defmodule MobilizonWeb.API.SearchTest do
|
|||||||
|
|
||||||
test "search an user by username" do
|
test "search an user by username" do
|
||||||
with_mock ActivityPub,
|
with_mock ActivityPub,
|
||||||
find_or_make_actor_from_nickname: fn "toto@domain.tld" -> {:ok, %Actor{id: 1}} end do
|
find_or_make_actor_from_nickname: fn "toto@domain.tld" -> {:ok, %Actor{id: 42}} end do
|
||||||
assert {:ok, %Actor{id: 1}} == Search.search("toto@domain.tld")
|
assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} ==
|
||||||
|
Search.search_actors("toto@domain.tld", 1, 10, :Person)
|
||||||
|
|
||||||
assert_called(ActivityPub.find_or_make_actor_from_nickname("toto@domain.tld"))
|
assert_called(ActivityPub.find_or_make_actor_from_nickname("toto@domain.tld"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search something by URL" do
|
test "search something by URL" do
|
||||||
with_mock ActivityPub,
|
with_mock ActivityPub,
|
||||||
fetch_object_from_url: fn "https://social.tcit.fr/users/tcit" -> {:ok, %Actor{id: 1}} end do
|
fetch_object_from_url: fn "https://social.tcit.fr/users/tcit" -> {:ok, %Actor{id: 42}} end do
|
||||||
assert {:ok, %Actor{id: 1}} == Search.search("https://social.tcit.fr/users/tcit")
|
assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} ==
|
||||||
assert_called(ActivityPub.fetch_object_from_url("https://social.tcit.fr/users/tcit"))
|
Search.search_actors("https://social.tcit.fr/users/tcit", 1, 10, :Person)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search everything" do
|
assert_called(ActivityPub.fetch_object_from_url("https://social.tcit.fr/users/tcit"))
|
||||||
with_mocks([
|
|
||||||
{Actors, [], [find_actors_by_username_or_name: fn "toto", 1, 10 -> [%Actor{}] end]},
|
|
||||||
{Events, [], [find_events_by_name: fn "toto", 1, 10 -> [%Event{}] end]}
|
|
||||||
]) do
|
|
||||||
assert {:ok, [%Event{}, %Actor{}]} = Search.search("toto")
|
|
||||||
assert_called(Actors.find_actors_by_username_or_name("toto", 1, 10))
|
|
||||||
assert_called(Events.find_events_by_name("toto", 1, 10))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search actors" do
|
test "search actors" do
|
||||||
with_mock Actors,
|
with_mock Actors,
|
||||||
find_actors_by_username_or_name: fn "toto", 1, 10 -> [%Actor{}] end do
|
find_and_count_actors_by_username_or_name: fn "toto", _type, 1, 10 ->
|
||||||
assert {:ok, [%Actor{}]} = Search.search_actors("toto")
|
%{total: 1, elements: [%Actor{id: 42}]}
|
||||||
assert_called(Actors.find_actors_by_username_or_name("toto", 1, 10))
|
end do
|
||||||
|
assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} =
|
||||||
|
Search.search_actors("toto", 1, 10, :Person)
|
||||||
|
|
||||||
|
assert_called(Actors.find_and_count_actors_by_username_or_name("toto", [:Person], 1, 10))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search events" do
|
test "search events" do
|
||||||
with_mock Events,
|
with_mock Events,
|
||||||
find_events_by_name: fn "toto", 1, 10 -> [%Event{}] end do
|
find_and_count_events_by_name: fn "toto", 1, 10 ->
|
||||||
assert {:ok, [%Event{}]} = Search.search_events("toto")
|
%{total: 1, elements: [%Event{title: "super toto event"}]}
|
||||||
assert_called(Events.find_events_by_name("toto", 1, 10))
|
end do
|
||||||
|
assert {:ok, %{total: 1, elements: [%Event{title: "super toto event"}]}} =
|
||||||
|
Search.search_events("toto", 1, 10)
|
||||||
|
|
||||||
|
assert_called(Events.find_and_count_events_by_name("toto", 1, 10))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,26 +9,24 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do
|
|||||||
{:ok, conn: conn, user: user}
|
{:ok, conn: conn, user: user}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search_events_and_actors/3 finds events and actors with basic search", %{
|
test "search_events/3 finds events with basic search", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
actor = insert(:actor, user: user, preferred_username: "test")
|
insert(:actor, user: user, preferred_username: "test_person")
|
||||||
event = insert(:event, title: "test")
|
insert(:actor, type: :Group, preferred_username: "test_group")
|
||||||
|
event = insert(:event, title: "test_event")
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
search(search: "test") {
|
search_events(search: "test") {
|
||||||
...on Event {
|
total,
|
||||||
|
elements {
|
||||||
title,
|
title,
|
||||||
uuid,
|
uuid,
|
||||||
__typename
|
__typename
|
||||||
},
|
|
||||||
...on Actor {
|
|
||||||
preferredUsername,
|
|
||||||
__typename
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -36,33 +34,172 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do
|
|||||||
conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid)
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["total"] == 1
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["elements"] |> length == 1
|
||||||
|
|
||||||
assert hd(tl(json_response(res, 200)["data"]["search"]))["preferredUsername"] ==
|
assert hd(json_response(res, 200)["data"]["search_events"]["elements"])["uuid"] ==
|
||||||
actor.preferred_username
|
to_string(event.uuid)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search_events_and_actors/3 finds events and actors with word search", %{
|
test "search_persons/3 finds persons with basic search", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
actor = insert(:actor, user: user, preferred_username: "toto", name: "I like pineapples")
|
actor = insert(:actor, user: user, preferred_username: "test_person")
|
||||||
event = insert(:event, title: "Pineapple fashion week")
|
insert(:actor, type: :Group, preferred_username: "test_group")
|
||||||
|
insert(:event, title: "test_event")
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
search_persons(search: "test") {
|
||||||
|
total,
|
||||||
|
elements {
|
||||||
|
preferredUsername,
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_persons"]["total"] == 1
|
||||||
|
assert json_response(res, 200)["data"]["search_persons"]["elements"] |> length == 1
|
||||||
|
|
||||||
|
assert hd(json_response(res, 200)["data"]["search_persons"]["elements"])["preferredUsername"] ==
|
||||||
|
actor.preferred_username
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search_groups/3 finds persons with basic search", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
insert(:actor, user: user, preferred_username: "test_person")
|
||||||
|
group = insert(:actor, type: :Group, preferred_username: "test_group")
|
||||||
|
insert(:event, title: "test_event")
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
search_groups(search: "test") {
|
||||||
|
total,
|
||||||
|
elements {
|
||||||
|
preferredUsername,
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_groups"]["total"] == 1
|
||||||
|
assert json_response(res, 200)["data"]["search_groups"]["elements"] |> length == 1
|
||||||
|
|
||||||
|
assert hd(json_response(res, 200)["data"]["search_groups"]["elements"])["preferredUsername"] ==
|
||||||
|
group.preferred_username
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search_events/3 finds events and actors with word search", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
insert(:actor, user: user, preferred_username: "person", name: "I like pineapples")
|
||||||
|
insert(:event, title: "Pineapple fashion week")
|
||||||
|
insert(:event, title: "I love pineAPPLE")
|
||||||
|
insert(:event, title: "Hello")
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
search_events(search: "pineapple") {
|
||||||
|
total,
|
||||||
|
elements {
|
||||||
|
title,
|
||||||
|
uuid,
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["total"] == 2
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["elements"]
|
||||||
|
|> length == 2
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["elements"]
|
||||||
|
|> Enum.map(& &1["title"]) == [
|
||||||
|
"I love pineAPPLE",
|
||||||
|
"Pineapple fashion week"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search_persons/3 finds persons with word search", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
actor = insert(:actor, user: user, preferred_username: "person", name: "I like pineapples")
|
||||||
|
insert(:actor, preferred_username: "group", type: :Group, name: "pineapple group")
|
||||||
|
insert(:event, title: "Pineapple fashion week")
|
||||||
|
insert(:event, title: "I love pineAPPLE")
|
||||||
|
insert(:event, title: "Hello")
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
search_persons(search: "pineapple") {
|
||||||
|
total,
|
||||||
|
elements {
|
||||||
|
preferredUsername,
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_persons"]["total"] == 1
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["search_persons"]["elements"]
|
||||||
|
|> length == 1
|
||||||
|
|
||||||
|
assert hd(json_response(res, 200)["data"]["search_persons"]["elements"])["preferredUsername"] ==
|
||||||
|
actor.preferred_username
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search_events/3 finds events with accented search", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
insert(:actor, user: user, preferred_username: "person", name: "Torréfaction du Kafé")
|
||||||
|
insert(:actor, type: :Group, preferred_username: "group", name: "Kafé group")
|
||||||
|
event = insert(:event, title: "Tour du monde des Kafés")
|
||||||
|
|
||||||
# Elaborate query
|
# Elaborate query
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
search(search: "pineapple") {
|
search_events(search: "Kafé") {
|
||||||
...on Event {
|
total,
|
||||||
title,
|
elements {
|
||||||
uuid,
|
title,
|
||||||
__typename
|
uuid,
|
||||||
},
|
__typename
|
||||||
...on Actor {
|
|
||||||
preferredUsername,
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -70,33 +207,29 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do
|
|||||||
conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid)
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_events"]["total"] == 1
|
||||||
assert hd(tl(json_response(res, 200)["data"]["search"]))["preferredUsername"] ==
|
assert hd(json_response(res, 200)["data"]["search_events"]["elements"])["uuid"] == event.uuid
|
||||||
actor.preferred_username
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search_events_and_actors/3 finds events and actors with accented search", %{
|
test "search_groups/3 finds groups with accented search", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
insert(:actor, user: user, preferred_username: "toto", name: "Torréfaction")
|
insert(:actor, user: user, preferred_username: "person", name: "Torréfaction du Kafé")
|
||||||
event = insert(:event, title: "Tour du monde des cafés")
|
group = insert(:actor, type: :Group, preferred_username: "group", name: "Kafé group")
|
||||||
|
insert(:event, title: "Tour du monde des Kafés")
|
||||||
|
|
||||||
# Elaborate query
|
# Elaborate query
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
search(search: "café") {
|
search_groups(search: "Kafé") {
|
||||||
...on Event {
|
total,
|
||||||
title,
|
elements {
|
||||||
uuid,
|
preferredUsername,
|
||||||
__typename
|
__typename
|
||||||
},
|
|
||||||
...on Actor {
|
|
||||||
preferredUsername,
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -104,6 +237,10 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do
|
|||||||
conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "search"))
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid)
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
assert json_response(res, 200)["data"]["search_groups"]["total"] == 1
|
||||||
|
|
||||||
|
assert hd(json_response(res, 200)["data"]["search_groups"]["elements"])["preferredUsername"] ==
|
||||||
|
group.preferred_username
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user