From fea36967999fed5399ab3533e806e4cbc990ad05 Mon Sep 17 00:00:00 2001
From: William Pitcock <nenolod@dereferenced.org>
Date: Thu, 21 Mar 2019 23:17:53 +0000
Subject: [PATCH 1/4] common api: move context functions from twitterapi

---
 lib/pleroma/web/common_api/utils.ex           | 29 ++++++++++++++++++
 lib/pleroma/web/twitter_api/twitter_api.ex    | 30 -------------------
 .../web/twitter_api/twitter_api_controller.ex |  3 +-
 .../web/twitter_api/views/activity_view.ex    |  3 +-
 test/web/common_api/common_api_utils_test.exs | 16 ++++++++++
 test/web/twitter_api/twitter_api_test.exs     | 16 ----------
 .../twitter_api/views/activity_view_test.exs  |  9 +++---
 7 files changed, 52 insertions(+), 54 deletions(-)

diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index b7513ef28..fcdfea8e1 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -344,4 +344,33 @@ def get_report_statuses(%User{ap_id: actor}, %{"status_ids" => status_ids}) do
   end
 
   def get_report_statuses(_, _), do: {:ok, nil}
+
+  # DEPRECATED mostly, context objects are now created at insertion time.
+  def context_to_conversation_id(context) do
+    with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
+      id
+    else
+      _e ->
+        changeset = Object.context_mapping(context)
+
+        case Repo.insert(changeset) do
+          {:ok, %{id: id}} ->
+            id
+
+          # 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).id
+        end
+    end
+  end
+
+  def conversation_id_to_context(id) do
+    with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
+      context
+    else
+      _e ->
+        {:error, "No such conversation"}
+    end
+  end
 end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index d57100491..9978c7f64 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -5,7 +5,6 @@
 defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
   alias Pleroma.Activity
   alias Pleroma.Mailer
-  alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.UserEmail
@@ -282,35 +281,6 @@ def search(_user, %{"q" => query} = params) do
     _activities = Repo.all(q)
   end
 
-  # DEPRECATED mostly, context objects are now created at insertion time.
-  def context_to_conversation_id(context) do
-    with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
-      id
-    else
-      _e ->
-        changeset = Object.context_mapping(context)
-
-        case Repo.insert(changeset) do
-          {:ok, %{id: id}} ->
-            id
-
-          # 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).id
-        end
-    end
-  end
-
-  def conversation_id_to_context(id) do
-    with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
-      context
-    else
-      _e ->
-        {:error, "No such conversation"}
-    end
-  end
-
   def get_external_profile(for_user, uri) do
     with %User{} = user <- User.get_or_fetch(uri) do
       {:ok, UserView.render("show.json", %{user: user, for: for_user})}
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 6ea0b110b..62cce18dc 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -16,6 +16,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.CommonAPI
+  alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.TwitterAPI.ActivityView
   alias Pleroma.Web.TwitterAPI.NotificationView
@@ -278,7 +279,7 @@ def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
   end
 
   def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
-    with context when is_binary(context) <- TwitterAPI.conversation_id_to_context(id),
+    with context when is_binary(context) <- Utils.conversation_id_to_context(id),
          activities <-
            ActivityPub.fetch_activities_for_context(context, %{
              "blocking_user" => user,
diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex
index 4926f007e..fe7d49975 100644
--- a/lib/pleroma/web/twitter_api/views/activity_view.ex
+++ b/lib/pleroma/web/twitter_api/views/activity_view.ex
@@ -15,7 +15,6 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.TwitterAPI.ActivityView
   alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
-  alias Pleroma.Web.TwitterAPI.TwitterAPI
   alias Pleroma.Web.TwitterAPI.UserView
 
   import Ecto.Query
@@ -78,7 +77,7 @@ 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)
+      true -> Utils.context_to_conversation_id(context)
     end
   end
 
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index 4c97b0d62..d095762ab 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -136,4 +136,20 @@ test "works for text/markdown with mentions" do
       assert output == expected
     end
   end
+
+  describe "context_to_conversation_id" do
+    test "creates a mapping object" do
+      conversation_id = Utils.context_to_conversation_id("random context")
+      object = Object.get_by_ap_id("random context")
+
+      assert conversation_id == object.id
+    end
+
+    test "returns an existing mapping for an existing object" do
+      {:ok, object} = Object.context_mapping("random context") |> Repo.insert()
+      conversation_id = Utils.context_to_conversation_id("random context")
+
+      assert conversation_id == object.id
+    end
+  end
 end
diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs
index c8dd3fd7a..b823bfd68 100644
--- a/test/web/twitter_api/twitter_api_test.exs
+++ b/test/web/twitter_api/twitter_api_test.exs
@@ -445,22 +445,6 @@ test "it assigns an integer conversation_id" do
     :ok
   end
 
-  describe "context_to_conversation_id" do
-    test "creates a mapping object" do
-      conversation_id = TwitterAPI.context_to_conversation_id("random context")
-      object = Object.get_by_ap_id("random context")
-
-      assert conversation_id == object.id
-    end
-
-    test "returns an existing mapping for an existing object" do
-      {:ok, object} = Object.context_mapping("random context") |> Repo.insert()
-      conversation_id = TwitterAPI.context_to_conversation_id("random context")
-
-      assert conversation_id == object.id
-    end
-  end
-
   describe "fetching a user by uri" do
     test "fetches a user by uri" do
       id = "https://mastodon.social/users/lambadalambda"
diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs
index d9df01c6e..ed18a60a3 100644
--- a/test/web/twitter_api/views/activity_view_test.exs
+++ b/test/web/twitter_api/views/activity_view_test.exs
@@ -12,7 +12,6 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.TwitterAPI.ActivityView
-  alias Pleroma.Web.TwitterAPI.TwitterAPI
   alias Pleroma.Web.TwitterAPI.UserView
 
   import Pleroma.Factory
@@ -129,7 +128,7 @@ test "a create activity with a note" do
 
     result = ActivityView.render("activity.json", activity: activity)
 
-    convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+    convo_id = Utils.context_to_conversation_id(activity.data["object"]["context"])
 
     expected = %{
       "activity_type" => "post",
@@ -177,7 +176,7 @@ test "a list of activities" do
     other_user = insert(:user, %{nickname: "shp"})
     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
 
-    convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+    convo_id = Utils.context_to_conversation_id(activity.data["object"]["context"])
 
     mocks = [
       {
@@ -197,7 +196,7 @@ test "a list of activities" do
 
       assert result["statusnet_conversation_id"] == convo_id
       assert result["user"]
-      refute called(TwitterAPI.context_to_conversation_id(:_))
+      refute called(Utils.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
@@ -280,7 +279,7 @@ test "an announce activity" do
     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
     {:ok, announce, _object} = CommonAPI.repeat(activity.id, other_user)
 
-    convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+    convo_id = Utils.context_to_conversation_id(activity.data["object"]["context"])
 
     activity = Repo.get(Activity, activity.id)
 

From 3cc2554fa3d63ba22dc5f598229a02b928b9fd14 Mon Sep 17 00:00:00 2001
From: William Pitcock <nenolod@dereferenced.org>
Date: Thu, 21 Mar 2019 23:25:41 +0000
Subject: [PATCH 2/4] mastodon api: add conversation_id extension (ref #674)

---
 lib/pleroma/web/mastodon_api/views/status_view.ex | 11 ++++++++++-
 test/web/mastodon_api/status_view_test.exs        |  6 +++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 209119dd5..1ca8338cc 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -46,6 +46,14 @@ defp get_user(ap_id) do
     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" => context}}) when is_binary(context),
+    do: Utils.context_to_conversation_id(context)
+
+  defp get_context_id(_), do: nil
+
   def render("index.json", opts) do
     replied_to_activities = get_replied_to_activities(opts.activities)
 
@@ -186,7 +194,8 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
       language: nil,
       emojis: build_emojis(activity.data["object"]["emoji"]),
       pleroma: %{
-        local: activity.local
+        local: activity.local,
+        conversation_id: get_context_id(activity)
       }
     }
   end
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index ade0ca9f9..e1c9b2c8f 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.CommonAPI
+  alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.OStatus
@@ -72,6 +73,8 @@ test "a note activity" do
     note = insert(:note_activity)
     user = User.get_cached_by_ap_id(note.data["actor"])
 
+    convo_id = Utils.context_to_conversation_id(note.data["object"]["context"])
+
     status = StatusView.render("status.json", %{activity: note})
 
     created_at =
@@ -122,7 +125,8 @@ test "a note activity" do
         }
       ],
       pleroma: %{
-        local: true
+        local: true,
+        conversation_id: convo_id
       }
     }
 

From ae8fa5d0aab3561bf66506766e2beb03a251e499 Mon Sep 17 00:00:00 2001
From: William Pitcock <nenolod@dereferenced.org>
Date: Thu, 21 Mar 2019 23:27:42 +0000
Subject: [PATCH 3/4] docs: document `conversation_id` extension

---
 docs/Differences-in-MastodonAPI-Responses.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/Differences-in-MastodonAPI-Responses.md b/docs/Differences-in-MastodonAPI-Responses.md
index 14b67ca7d..d993d1383 100644
--- a/docs/Differences-in-MastodonAPI-Responses.md
+++ b/docs/Differences-in-MastodonAPI-Responses.md
@@ -19,6 +19,7 @@ Adding the parameter `with_muted=true` to the timeline queries will also return
 Has these additional fields under the `pleroma` object:
 
 - `local`: true if the post was made on the local instance.
+- `conversation_id`: the ID of the conversation the status is associated with (if any)
 
 ## Attachments
 

From a223e65f35da158ef79f05f65316920dcecaa54b Mon Sep 17 00:00:00 2001
From: William Pitcock <nenolod@dereferenced.org>
Date: Thu, 21 Mar 2019 23:37:00 +0000
Subject: [PATCH 4/4] tests: fixup

---
 test/web/common_api/common_api_utils_test.exs     | 1 +
 test/web/twitter_api/views/activity_view_test.exs | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index d095762ab..e04b9f9b5 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Web.CommonAPI.UtilsTest do
   alias Pleroma.Builders.UserBuilder
+  alias Pleroma.Object
   alias Pleroma.Web.CommonAPI.Utils
   alias Pleroma.Web.Endpoint
   use Pleroma.DataCase
diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs
index ed18a60a3..a1776b3e6 100644
--- a/test/web/twitter_api/views/activity_view_test.exs
+++ b/test/web/twitter_api/views/activity_view_test.exs
@@ -180,8 +180,8 @@ test "a list of activities" do
 
     mocks = [
       {
-        TwitterAPI,
-        [],
+        Utils,
+        [:passthrough],
         [context_to_conversation_id: fn _ -> false end]
       },
       {