Load all users at once in timelines.
authorlain <lain@soykaf.club>
Mon, 2 Apr 2018 14:27:36 +0000 (16:27 +0200)
committerlain <lain@soykaf.club>
Mon, 2 Apr 2018 14:35:41 +0000 (16:35 +0200)
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/twitter_api/views/activity_view.ex
test/web/activity_pub/activity_pub_test.exs
test/web/twitter_api/views/activity_view_test.exs

index ac3a57e035414e3b3502bd2cfa118db52e2409bf..7a0762e9f17fc7bb0bc1d49634105dcb1905faea 100644 (file)
@@ -50,10 +50,13 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     changeset = Object.context_mapping(context)
 
     case Repo.insert(changeset) do
-      {:ok, object} -> object
+      {:ok, object} ->
+        object
+
       # This should be solved by an upsert, but it seems ecto
       # has problems accessing the constraint inside the jsonb.
-      {:error, _} -> Object.get_cached_by_ap_id(context)
+      {:error, _} ->
+        Object.get_cached_by_ap_id(context)
     end
   end
 
index dc92e30c5cec024ebe1ba138e6a97b5dc9557ab7..64928fda5bed5aa1a3306caa45c48af67d23930a 100644 (file)
@@ -513,10 +513,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       )
 
     statuses = Repo.all(q) ++ fetched
-    tags = String.split(query)
-    |> Enum.uniq()
-    |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
-    |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+
+    tags =
+      String.split(query)
+      |> Enum.uniq()
+      |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
+      |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
 
     res = %{
       "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
index 7608402397eefc5e53a4d286223b3893ba86a80f..4e4433aed5653e1186314473d3b7736dc2ec4422 100644 (file)
@@ -8,46 +8,99 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
   alias Pleroma.Activity
   alias Pleroma.Object
+  alias Pleroma.User
   alias Pleroma.Repo
   alias Pleroma.Formatter
 
   import Ecto.Query
 
   defp query_context_ids([]), do: []
+
   defp query_context_ids(contexts) do
-    query = from o in Object,
-      where: fragment("(?)->>'id' = ANY(?)", o.data, ^contexts)
+    query = from(o in Object, where: fragment("(?)->>'id' = ANY(?)", o.data, ^contexts))
+
+    Repo.all(query)
+  end
+
+  defp query_users([]), do: []
+
+  defp query_users(user_ids) do
+    query = from(user in User, where: user.ap_id in ^user_ids)
 
     Repo.all(query)
   end
 
   defp collect_context_ids(activities) do
-    contexts = activities
-    |> Enum.reject(&(&1.data["context_id"]))
-    |> Enum.map(fn(%{data: data}) ->
-      data["context"]
+    contexts =
+      activities
+      |> Enum.reject(& &1.data["context_id"])
+      |> Enum.map(fn %{data: data} ->
+        data["context"]
+      end)
+      |> Enum.filter(& &1)
+      |> query_context_ids()
+      |> Enum.reduce(%{}, fn %{data: %{"id" => ap_id}, id: id}, acc ->
+        Map.put(acc, ap_id, id)
+      end)
+  end
+
+  defp collect_users(activities) do
+    activities
+    |> Enum.map(fn activity ->
+      case activity.data do
+        data = %{"type" => "Follow"} ->
+          [data["actor"], data["object"]]
+
+        data ->
+          [data["actor"]]
+      end ++ activity.recipients
     end)
-    |> Enum.filter(&(&1))
-    |> query_context_ids()
-    |> Enum.reduce(%{}, fn(%{data: %{"id" => ap_id}, id: id}, acc) ->
-      Map.put(acc, ap_id, id)
+    |> List.flatten()
+    |> Enum.uniq()
+    |> query_users()
+    |> Enum.reduce(%{}, fn user, acc ->
+      Map.put(acc, user.ap_id, user)
     end)
   end
 
-  defp get_context_id(%{data: %{"context_id" => context_id}}, _) when not is_nil(context_id), do: context_id
+  defp get_context_id(%{data: %{"context_id" => context_id}}, _) when not is_nil(context_id),
+    do: context_id
+
   defp get_context_id(%{data: %{"context" => nil}}, _), do: nil
+
   defp get_context_id(%{data: %{"context" => context}}, options) do
     cond do
       id = options[:context_ids][context] -> id
       true -> TwitterAPI.context_to_conversation_id(context)
     end
   end
+
   defp get_context_id(_, _), do: nil
 
+  defp get_user(ap_id, opts) do
+    cond do
+      user = opts[:users][ap_id] ->
+        user
+
+      String.ends_with?(ap_id, "/followers") ->
+        nil
+
+      ap_id == "https://www.w3.org/ns/activitystreams#Public" ->
+        nil
+
+      true ->
+        User.get_cached_by_ap_id(ap_id)
+    end
+  end
+
   def render("index.json", opts) do
     context_ids = collect_context_ids(opts.activities)
-    opts = opts
-    |> Map.put(:context_ids, context_ids)
+    users = collect_users(opts.activities)
+
+    opts =
+      opts
+      |> Map.put(:context_ids, context_ids)
+      |> Map.put(:users, users)
 
     render_many(
       opts.activities,
@@ -58,7 +111,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   end
 
   def render("activity.json", %{activity: %{data: %{"type" => "Delete"}} = activity} = opts) do
-    user = User.get_cached_by_ap_id(activity.data["actor"])
+    user = get_user(activity.data["actor"], opts)
     created_at = activity.data["published"] |> Utils.date_to_asctime()
 
     %{
@@ -78,11 +131,11 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   end
 
   def render("activity.json", %{activity: %{data: %{"type" => "Follow"}} = activity} = opts) do
-    user = User.get_cached_by_ap_id(activity.data["actor"])
+    user = get_user(activity.data["actor"], opts)
     created_at = activity.data["published"] || DateTime.to_iso8601(activity.inserted_at)
     created_at = created_at |> Utils.date_to_asctime()
 
-    followed = User.get_cached_by_ap_id(activity.data["object"])
+    followed = get_user(activity.data["object"], opts)
     text = "#{user.nickname} started following #{followed.nickname}"
 
     %{
@@ -101,7 +154,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   end
 
   def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do
-    user = User.get_by_ap_id(activity.data["actor"])
+    user = get_user(activity.data["actor"], opts)
     created_at = activity.data["published"] |> Utils.date_to_asctime()
     announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
 
@@ -126,7 +179,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   end
 
   def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do
-    user = User.get_cached_by_ap_id(activity.data["actor"])
+    user = get_user(activity.data["actor"], opts)
     liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
 
     created_at =
@@ -154,8 +207,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
         "activity.json",
         %{activity: %{data: %{"type" => "Create", "object" => object}} = activity} = opts
       ) do
-    actor = get_in(activity.data, ["actor"])
-    user = User.get_cached_by_ap_id(actor)
+    user = get_user(activity.data["actor"], opts)
 
     created_at = object["published"] |> Utils.date_to_asctime()
     like_count = object["like_count"] || 0
@@ -165,7 +217,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
 
     attentions =
       activity.recipients
-      |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
+      |> Enum.map(fn ap_id -> get_user(ap_id, opts) end)
       |> Enum.filter(& &1)
       |> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end)
 
index c6434f789126b67b776b3ee798087c7c51801985..657d75a554c08f3c6825f227b71e15b66830fdd2 100644 (file)
@@ -56,7 +56,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
         "id" => "some_id",
         "object" => %{
           "id" => "object_id"
-         }
+        }
       }
 
       {:ok, %Activity{} = activity} = ActivityPub.insert(data)
index 46ffdef4482b0cd912a752fa1eca7bd311891942..7f2017d3cf6fed72652c23dfc73ca49ede55463f 100644 (file)
@@ -64,7 +64,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
       {
         TwitterAPI,
         [],
-        [context_to_conversation_id: fn(_) -> false end]
+        [context_to_conversation_id: fn _ -> false end]
+      },
+      {
+        User,
+        [:passthrough],
+        [get_cached_by_ap_id: fn _ -> nil end]
       }
     ]
 
@@ -73,7 +78,9 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
 
       assert result["statusnet_conversation_id"] == convo_id
       assert result["user"]
-      refute called TwitterAPI.context_to_conversation_id(:_)
+      refute called(TwitterAPI.context_to_conversation_id(:_))
+      refute called(User.get_cached_by_ap_id(user.ap_id))
+      refute called(User.get_cached_by_ap_id(other_user.ap_id))
     end
   end