mobilizon/lib/graphql/api/search.ex

140 lines
3.9 KiB
Elixir
Raw Normal View History

2020-01-26 21:11:16 +01:00
defmodule Mobilizon.GraphQL.API.Search do
@moduledoc """
2019-09-09 00:52:49 +02:00
API for search.
"""
2019-09-09 00:52:49 +02:00
alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor, ActorType}
alias Mobilizon.Events
alias Mobilizon.Events.Event
2019-09-09 00:52:49 +02:00
alias Mobilizon.Storage.Page
2020-01-22 02:14:42 +01:00
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
import Mobilizon.GraphQL.Resolvers.Event.Utils
2020-01-22 02:14:42 +01:00
require Logger
@doc """
2019-09-09 00:52:49 +02:00
Searches actors.
"""
@spec search_actors(map(), integer | nil, integer | nil, ActorType.t()) ::
2019-09-09 00:52:49 +02:00
{:ok, Page.t()} | {:error, String.t()}
def search_actors(%{term: term} = args, page \\ 1, limit \\ 10, result_type) do
term = String.trim(term)
cond do
2019-09-09 00:52:49 +02:00
# Some URLs could be domain.tld/@username, so keep this condition above
# the `is_handle` function
is_url(term) ->
2019-09-09 00:52:49 +02:00
# skip, if it's not an actor
case process_from_url(term) do
%Page{total: _total, elements: [%Actor{} = _actor]} = page ->
2019-09-09 00:52:49 +02:00
{:ok, page}
_ ->
{:ok, %{total: 0, elements: []}}
end
is_handle(term) ->
{:ok, process_from_username(term)}
true ->
page =
Actors.search_actors(
term,
[
actor_type: result_type,
radius: Map.get(args, :radius),
location: Map.get(args, :location),
minimum_visibility: Map.get(args, :minimum_visibility, :public),
current_actor_id: Map.get(args, :current_actor_id),
exclude_my_groups: Map.get(args, :exclude_my_groups, false),
exclude_stale_actors: true
],
page,
limit
)
2019-09-09 00:52:49 +02:00
{:ok, page}
end
end
@doc """
Search events
"""
@spec search_events(map(), integer | nil, integer | nil) ::
{:ok, Page.t()}
def search_events(%{term: term} = args, page \\ 1, limit \\ 10) do
term = String.trim(term)
if is_url(term) do
# skip, if it's not an event
case process_from_url(term) do
%Page{total: _total, elements: [%Event{} = event]} = page ->
if Map.get(args, :current_user) != nil || check_event_access?(event) do
{:ok, page}
else
{:ok, %{total: 0, elements: []}}
end
_ ->
{:ok, %{total: 0, elements: []}}
end
else
{:ok, Events.build_events_for_search(Map.put(args, :term, term), page, limit)}
end
end
@spec interact(String.t()) :: {:ok, struct()} | {:error, :not_found}
def interact(uri) do
case ActivityPub.fetch_object_from_url(uri) do
{:ok, object} ->
{:ok, object}
{:error, _err} ->
Logger.debug(fn -> "Unable to find or make object from URI '#{uri}'" end)
{:error, :not_found}
end
end
# If the search string is an username
2019-09-09 00:52:49 +02:00
@spec process_from_username(String.t()) :: Page.t()
defp process_from_username(search) do
case ActivityPubActor.find_or_make_actor_from_nickname(search) do
{:ok, %Actor{type: :Group} = actor} ->
2019-09-09 00:52:49 +02:00
%Page{total: 1, elements: [actor]}
# Don't return anything else than groups
{:ok, %Actor{}} ->
%Page{total: 0, elements: []}
{:error, _err} ->
Logger.debug(fn -> "Unable to find or make actor '#{search}'" end)
2019-09-09 00:52:49 +02:00
%Page{total: 0, elements: []}
end
end
# If the search string is an URL
2019-09-09 00:52:49 +02:00
@spec process_from_url(String.t()) :: Page.t()
defp process_from_url(search) do
case ActivityPub.fetch_object_from_url(search) do
{:ok, object} ->
2019-09-09 00:52:49 +02:00
%Page{total: 1, elements: [object]}
{:error, _err} ->
Logger.debug(fn -> "Unable to find or make object from URL '#{search}'" end)
2019-09-09 00:52:49 +02:00
%Page{total: 0, elements: []}
end
end
2019-09-09 00:52:49 +02:00
@spec is_url(String.t()) :: boolean
defp is_url(search), do: String.starts_with?(search, ["http://", "https://"])
2019-09-09 00:52:49 +02:00
@spec is_handle(String.t()) :: boolean
defp is_handle(search), do: String.match?(search, ~r/@/)
end