Merge branch 'remake-remodel' into develop
authorlain <lain@soykaf.club>
Thu, 19 Mar 2020 17:00:55 +0000 (18:00 +0100)
committerlain <lain@soykaf.club>
Thu, 19 Mar 2020 17:00:55 +0000 (18:00 +0100)
36 files changed:
lib/pleroma/object/containment.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/builder.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validator.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/common_validations.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/create_validator.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/like_validator.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/note_validator.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/types/date_time.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/object_validators/types/object.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/pipeline.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/side_effects.ex [new file with mode: 0644]
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/controllers/status_controller.ex
test/notification_test.exs
test/object_test.exs
test/stat_test.exs
test/tasks/database_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/object_validator_test.exs [new file with mode: 0644]
test/web/activity_pub/object_validators/note_validator_test.exs [new file with mode: 0644]
test/web/activity_pub/object_validators/types/date_time_test.exs [new file with mode: 0644]
test/web/activity_pub/pipeline_test.exs [new file with mode: 0644]
test/web/activity_pub/side_effects_test.exs [new file with mode: 0644]
test/web/activity_pub/transmogrifier_test.exs
test/web/activity_pub/views/object_view_test.exs
test/web/common_api/common_api_test.exs
test/web/mastodon_api/controllers/notification_controller_test.exs
test/web/mastodon_api/controllers/status_controller_test.exs
test/web/mastodon_api/views/notification_view_test.exs
test/web/ostatus/ostatus_controller_test.exs
test/web/pleroma_api/controllers/account_controller_test.exs
test/web/push/impl_test.exs
test/web/streamer/streamer_test.exs

index 9ae6a5600ca1cfc09253244a6a6bb1378c0f33d5..99608b8a5540c68e367158e89590bc0c123ffc1b 100644 (file)
@@ -32,6 +32,18 @@ defmodule Pleroma.Object.Containment do
     get_actor(%{"actor" => actor})
   end
 
+  def get_object(%{"object" => id}) when is_binary(id) do
+    id
+  end
+
+  def get_object(%{"object" => %{"id" => id}}) when is_binary(id) do
+    id
+  end
+
+  def get_object(_) do
+    nil
+  end
+
   # TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
   # objects being present in the test suite environment.  Once these objects are
   # removed, please also remove this.
index d9f74b6a4928004f074025369bd26183aa517253..d9f30e629607ae6a90664ac798908ecf7d30db0f 100644 (file)
@@ -126,6 +126,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   def increase_poll_votes_if_vote(_create_data), do: :noop
 
   @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
+  # TODO rewrite in with style
+  @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+  def persist(object, meta) do
+    local = Keyword.fetch!(meta, :local)
+    {recipients, _, _} = get_recipients(object)
+
+    {:ok, activity} =
+      Repo.insert(%Activity{
+        data: object,
+        local: local,
+        recipients: recipients,
+        actor: object["actor"]
+      })
+
+    {:ok, activity, meta}
+  end
+
   def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
     with nil <- Activity.normalize(map),
          map <- lazy_put_activity_defaults(map, fake),
@@ -133,6 +150,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {_, true} <- {:remote_limit_error, check_remote_limit(map)},
          {:ok, map} <- MRF.filter(map),
          {recipients, _, _} = get_recipients(map),
+         # ???
          {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
          {:containment, :ok} <- {:containment, Containment.contain_child(map)},
          {:ok, map, object} <- insert_full_object(map) do
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
new file mode 100644 (file)
index 0000000..429a510
--- /dev/null
@@ -0,0 +1,43 @@
+defmodule Pleroma.Web.ActivityPub.Builder do
+  @moduledoc """
+  This module builds the objects. Meant to be used for creating local objects.
+
+  This module encodes our addressing policies and general shape of our objects.
+  """
+
+  alias Pleroma.Object
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Utils
+  alias Pleroma.Web.ActivityPub.Visibility
+
+  @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
+  def like(actor, object) do
+    object_actor = User.get_cached_by_ap_id(object.data["actor"])
+
+    # Address the actor of the object, and our actor's follower collection if the post is public.
+    to =
+      if Visibility.is_public?(object) do
+        [actor.follower_address, object.data["actor"]]
+      else
+        [object.data["actor"]]
+      end
+
+    # CC everyone who's been addressed in the object, except ourself and the object actor's
+    # follower collection
+    cc =
+      (object.data["to"] ++ (object.data["cc"] || []))
+      |> List.delete(actor.ap_id)
+      |> List.delete(object_actor.follower_address)
+
+    {:ok,
+     %{
+       "id" => Utils.generate_activity_id(),
+       "actor" => actor.ap_id,
+       "type" => "Like",
+       "object" => object.data["id"],
+       "to" => to,
+       "cc" => cc,
+       "context" => object.data["context"]
+     }, []}
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
new file mode 100644 (file)
index 0000000..cff9240
--- /dev/null
@@ -0,0 +1,38 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidator do
+  @moduledoc """
+  This module is responsible for validating an object (which can be an activity)
+  and checking if it is both well formed and also compatible with our view of
+  the system.
+  """
+
+  alias Pleroma.Object
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+
+  @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+  def validate(object, meta)
+
+  def validate(%{"type" => "Like"} = object, meta) do
+    with {:ok, object} <-
+           object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do
+      object = stringify_keys(object |> Map.from_struct())
+      {:ok, object, meta}
+    end
+  end
+
+  def stringify_keys(object) do
+    object
+    |> Enum.map(fn {key, val} -> {to_string(key), val} end)
+    |> Enum.into(%{})
+  end
+
+  def fetch_actor_and_object(object) do
+    User.get_or_fetch_by_ap_id(object["actor"])
+    Object.normalize(object["object"])
+    :ok
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
new file mode 100644 (file)
index 0000000..db0e207
--- /dev/null
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
+  import Ecto.Changeset
+
+  alias Pleroma.Object
+  alias Pleroma.User
+
+  def validate_actor_presence(cng, field_name \\ :actor) do
+    cng
+    |> validate_change(field_name, fn field_name, actor ->
+      if User.get_cached_by_ap_id(actor) do
+        []
+      else
+        [{field_name, "can't find user"}]
+      end
+    end)
+  end
+
+  def validate_object_presence(cng, field_name \\ :object) do
+    cng
+    |> validate_change(field_name, fn field_name, actor ->
+      if Object.get_cached_by_ap_id(actor) do
+        []
+      else
+        [{field_name, "can't find user"}]
+      end
+    end)
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_validator.ex
new file mode 100644 (file)
index 0000000..bd90f72
--- /dev/null
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
+  use Ecto.Schema
+
+  alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+  alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
+
+  import Ecto.Changeset
+
+  @primary_key false
+
+  embedded_schema do
+    field(:id, :string, primary_key: true)
+    field(:actor, Types.ObjectID)
+    field(:type, :string)
+    field(:to, {:array, :string})
+    field(:cc, {:array, :string})
+    field(:bto, {:array, :string}, default: [])
+    field(:bcc, {:array, :string}, default: [])
+
+    embeds_one(:object, NoteValidator)
+  end
+
+  def cast_data(data) do
+    %__MODULE__{}
+    |> cast(data, __schema__(:fields))
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex
new file mode 100644 (file)
index 0000000..ccbc7d0
--- /dev/null
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
+  use Ecto.Schema
+
+  alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+  alias Pleroma.Web.ActivityPub.Utils
+
+  import Ecto.Changeset
+  import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+
+  @primary_key false
+
+  embedded_schema do
+    field(:id, :string, primary_key: true)
+    field(:type, :string)
+    field(:object, Types.ObjectID)
+    field(:actor, Types.ObjectID)
+    field(:context, :string)
+    field(:to, {:array, :string})
+    field(:cc, {:array, :string})
+  end
+
+  def cast_and_validate(data) do
+    data
+    |> cast_data()
+    |> validate_data()
+  end
+
+  def cast_data(data) do
+    %__MODULE__{}
+    |> cast(data, [:id, :type, :object, :actor, :context, :to, :cc])
+  end
+
+  def validate_data(data_cng) do
+    data_cng
+    |> validate_inclusion(:type, ["Like"])
+    |> validate_required([:id, :type, :object, :actor, :context, :to, :cc])
+    |> validate_actor_presence()
+    |> validate_object_presence()
+    |> validate_existing_like()
+  end
+
+  def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
+    if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
+      cng
+      |> add_error(:actor, "already liked this object")
+      |> add_error(:object, "already liked by this actor")
+    else
+      cng
+    end
+  end
+
+  def validate_existing_like(cng), do: cng
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex
new file mode 100644 (file)
index 0000000..eea15ce
--- /dev/null
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
+  use Ecto.Schema
+
+  alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+
+  import Ecto.Changeset
+
+  @primary_key false
+
+  embedded_schema do
+    field(:id, :string, primary_key: true)
+    field(:to, {:array, :string}, default: [])
+    field(:cc, {:array, :string}, default: [])
+    field(:bto, {:array, :string}, default: [])
+    field(:bcc, {:array, :string}, default: [])
+    # TODO: Write type
+    field(:tag, {:array, :map}, default: [])
+    field(:type, :string)
+    field(:content, :string)
+    field(:context, :string)
+    field(:actor, Types.ObjectID)
+    field(:attributedTo, Types.ObjectID)
+    field(:summary, :string)
+    field(:published, Types.DateTime)
+    # TODO: Write type
+    field(:emoji, :map, default: %{})
+    field(:sensitive, :boolean, default: false)
+    # TODO: Write type
+    field(:attachment, {:array, :map}, default: [])
+    field(:replies_count, :integer, default: 0)
+    field(:like_count, :integer, default: 0)
+    field(:announcement_count, :integer, default: 0)
+    field(:inRepyTo, :string)
+
+    field(:likes, {:array, :string}, default: [])
+    field(:announcements, {:array, :string}, default: [])
+
+    # see if needed
+    field(:conversation, :string)
+    field(:context_id, :string)
+  end
+
+  def cast_and_validate(data) do
+    data
+    |> cast_data()
+    |> validate_data()
+  end
+
+  def cast_data(data) do
+    %__MODULE__{}
+    |> cast(data, __schema__(:fields))
+  end
+
+  def validate_data(data_cng) do
+    data_cng
+    |> validate_inclusion(:type, ["Note"])
+    |> validate_required([:id, :actor, :to, :cc, :type, :content, :context])
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/types/date_time.ex b/lib/pleroma/web/activity_pub/object_validators/types/date_time.ex
new file mode 100644 (file)
index 0000000..4f412fc
--- /dev/null
@@ -0,0 +1,34 @@
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime do
+  @moduledoc """
+  The AP standard defines the date fields in AP as xsd:DateTime. Elixir's
+  DateTime can't parse this, but it can parse the related iso8601. This
+  module punches the date until it looks like iso8601 and normalizes to
+  it.
+
+  DateTimes without a timezone offset are treated as UTC.
+
+  Reference: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published
+  """
+  use Ecto.Type
+
+  def type, do: :string
+
+  def cast(datetime) when is_binary(datetime) do
+    with {:ok, datetime, _} <- DateTime.from_iso8601(datetime) do
+      {:ok, DateTime.to_iso8601(datetime)}
+    else
+      {:error, :missing_offset} -> cast("#{datetime}Z")
+      _e -> :error
+    end
+  end
+
+  def cast(_), do: :error
+
+  def dump(data) do
+    {:ok, data}
+  end
+
+  def load(data) do
+    {:ok, data}
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/types/object.ex b/lib/pleroma/web/activity_pub/object_validators/types/object.ex
new file mode 100644 (file)
index 0000000..92fc13b
--- /dev/null
@@ -0,0 +1,25 @@
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do
+  use Ecto.Type
+
+  def type, do: :string
+
+  def cast(object) when is_binary(object) do
+    {:ok, object}
+  end
+
+  def cast(%{"id" => object}) when is_binary(object) do
+    {:ok, object}
+  end
+
+  def cast(_) do
+    :error
+  end
+
+  def dump(data) do
+    {:ok, data}
+  end
+
+  def load(data) do
+    {:ok, data}
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
new file mode 100644 (file)
index 0000000..cb35719
--- /dev/null
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.Pipeline do
+  alias Pleroma.Activity
+  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.MRF
+  alias Pleroma.Web.ActivityPub.ObjectValidator
+  alias Pleroma.Web.ActivityPub.SideEffects
+  alias Pleroma.Web.Federator
+
+  @spec common_pipeline(map(), keyword()) :: {:ok, Activity.t(), keyword()} | {:error, any()}
+  def common_pipeline(object, meta) do
+    with {_, {:ok, validated_object, meta}} <-
+           {:validate_object, ObjectValidator.validate(object, meta)},
+         {_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
+         {_, {:ok, %Activity{} = activity, meta}} <-
+           {:persist_object, ActivityPub.persist(mrfd_object, meta)},
+         {_, {:ok, %Activity{} = activity, meta}} <-
+           {:execute_side_effects, SideEffects.handle(activity, meta)},
+         {_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
+      {:ok, activity, meta}
+    else
+      e -> {:error, e}
+    end
+  end
+
+  defp maybe_federate(activity, meta) do
+    with {:ok, local} <- Keyword.fetch(meta, :local) do
+      if local do
+        Federator.publish(activity)
+        {:ok, :federated}
+      else
+        {:ok, :not_federated}
+      end
+    else
+      _e -> {:error, "local not set in meta"}
+    end
+  end
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
new file mode 100644 (file)
index 0000000..666a4e3
--- /dev/null
@@ -0,0 +1,28 @@
+defmodule Pleroma.Web.ActivityPub.SideEffects do
+  @moduledoc """
+  This module looks at an inserted object and executes the side effects that it
+  implies. For example, a `Like` activity will increase the like count on the
+  liked object, a `Follow` activity will add the user to the follower
+  collection, and so on.
+  """
+  alias Pleroma.Notification
+  alias Pleroma.Object
+  alias Pleroma.Web.ActivityPub.Utils
+
+  def handle(object, meta \\ [])
+
+  # Tasks this handles:
+  # - Add like to object
+  # - Set up notification
+  def handle(%{data: %{"type" => "Like"}} = object, meta) do
+    liked_object = Object.get_by_ap_id(object.data["object"])
+    Utils.add_like_to_object(object, liked_object)
+    Notification.create_notifications(object)
+    {:ok, object, meta}
+  end
+
+  # Nothing to do
+  def handle(object, meta) do
+    {:ok, object, meta}
+  end
+end
index 9cd3de7053ee9adc49459a8ee4ec073cd6c6f736..dbb14e9aa2be8e0f76a3e7283cf777affb085265 100644 (file)
@@ -13,6 +13,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.ObjectValidator
+  alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+  alias Pleroma.Web.ActivityPub.Pipeline
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Federator
@@ -608,17 +611,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> handle_incoming(options)
   end
 
-  def handle_incoming(
-        %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
-        _options
-      ) do
-    with actor <- Containment.get_actor(data),
-         {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
-         {:ok, object} <- get_obj_helper(object_id),
-         {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
+  def handle_incoming(%{"type" => "Like"} = data, _options) do
+    with {_, {:ok, cast_data_sym}} <-
+           {:casting_data,
+            data |> LikeValidator.cast_data() |> Ecto.Changeset.apply_action(:insert)},
+         {_, cast_data} <-
+           {:stringify_keys, ObjectValidator.stringify_keys(cast_data_sym |> Map.from_struct())},
+         :ok <- ObjectValidator.fetch_actor_and_object(cast_data),
+         {_, {:ok, cast_data}} <- {:maybe_add_context, maybe_add_context_from_object(cast_data)},
+         {_, {:ok, cast_data}} <-
+           {:maybe_add_recipients, maybe_add_recipients_from_object(cast_data)},
+         {_, {:ok, activity, _meta}} <-
+           {:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
       {:ok, activity}
     else
-      _e -> :error
+      e -> {:error, e}
     end
   end
 
@@ -1244,4 +1251,47 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   def maybe_fix_user_url(data), do: data
 
   def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
+
+  defp maybe_add_context_from_object(%{"context" => context} = data) when is_binary(context),
+    do: {:ok, data}
+
+  defp maybe_add_context_from_object(%{"object" => object} = data) when is_binary(object) do
+    if object = Object.normalize(object) do
+      data =
+        data
+        |> Map.put("context", object.data["context"])
+
+      {:ok, data}
+    else
+      {:error, "No context on referenced object"}
+    end
+  end
+
+  defp maybe_add_context_from_object(_) do
+    {:error, "No referenced object"}
+  end
+
+  defp maybe_add_recipients_from_object(%{"object" => object} = data) do
+    to = data["to"] || []
+    cc = data["cc"] || []
+
+    if to == [] && cc == [] do
+      if object = Object.normalize(object) do
+        data =
+          data
+          |> Map.put("to", [object.data["actor"]])
+          |> Map.put("cc", cc)
+
+        {:ok, data}
+      else
+        {:error, "No actor on referenced object"}
+      end
+    else
+      {:ok, data}
+    end
+  end
+
+  defp maybe_add_recipients_from_object(_) do
+    {:error, "No referenced object"}
+  end
 end
index 091011c6b1082abf268221c2b80c2a4169e66cf8..f882f9fcb790fac49ebd88ac81562cdccd44c9dc 100644 (file)
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.CommonAPI do
   alias Pleroma.User
   alias Pleroma.UserRelationship
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Builder
+  alias Pleroma.Web.ActivityPub.Pipeline
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
 
@@ -19,6 +21,7 @@ defmodule Pleroma.Web.CommonAPI do
   import Pleroma.Web.CommonAPI.Utils
 
   require Pleroma.Constants
+  require Logger
 
   def follow(follower, followed) do
     timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@@ -109,18 +112,39 @@ defmodule Pleroma.Web.CommonAPI do
     end
   end
 
-  def favorite(id_or_ap_id, user) do
-    with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)},
-         object <- Object.normalize(activity),
-         like_activity <- Utils.get_existing_like(user.ap_id, object) do
-      if like_activity do
-        {:ok, like_activity, object}
-      else
-        ActivityPub.like(user, object)
-      end
+  @spec favorite(User.t(), binary()) :: {:ok, Activity.t()} | {:error, any()}
+  def favorite(%User{} = user, id) do
+    with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
+         {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
+         {_, {:ok, %Activity{} = activity, _meta}} <-
+           {:common_pipeline,
+            Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
+      {:ok, activity}
     else
-      {:find_activity, _} -> {:error, :not_found}
-      _ -> {:error, dgettext("errors", "Could not favorite")}
+      {:find_object, _} ->
+        {:error, :not_found}
+
+      {:common_pipeline,
+       {
+         :error,
+         {
+           :validate_object,
+           {
+             :error,
+             changeset
+           }
+         }
+       }} = e ->
+        if {:object, {"already liked by this actor", []}} in changeset.errors do
+          {:ok, :already_liked}
+        else
+          Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
+          {:error, dgettext("errors", "Could not favorite"), e}
+        end
+
+      e ->
+        Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
+        {:error, dgettext("errors", "Could not favorite"), e}
     end
   end
 
index 5c90065f6f9298dbb207cab596786d65175baae7..83cd2c768f4db6c2894f491c8e816fd0efc20b03 100644 (file)
@@ -207,9 +207,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
   end
 
   @doc "POST /api/v1/statuses/:id/favourite"
-  def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
-    with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
-         %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
+  def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
+    with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),
+         %Activity{} = activity <- Activity.get_by_id(activity_id) do
       try_render(conn, "show.json", activity: activity, for: user, as: :activity)
     end
   end
index 56a5818103ecf7a0ecc615f1e95d9db50f580386..81eb0e2f7a0e05dfdfc87eb91a7694a5fee0ffcc 100644 (file)
@@ -455,7 +455,7 @@ defmodule Pleroma.NotificationTest do
           "status" => "hey @#{other_user.nickname}!"
         })
 
-      {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user)
+      {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
 
       assert other_user not in Notification.get_notified_from_activity(activity_two)
     end
@@ -485,7 +485,7 @@ defmodule Pleroma.NotificationTest do
 
       assert Enum.empty?(Notification.for_user(user))
 
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       assert length(Notification.for_user(user)) == 1
 
@@ -502,7 +502,7 @@ defmodule Pleroma.NotificationTest do
 
       assert Enum.empty?(Notification.for_user(user))
 
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       assert length(Notification.for_user(user)) == 1
 
@@ -557,7 +557,7 @@ defmodule Pleroma.NotificationTest do
 
       assert Enum.empty?(Notification.for_user(user))
 
-      {:error, _} = CommonAPI.favorite(activity.id, other_user)
+      {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
 
       assert Enum.empty?(Notification.for_user(user))
     end
index 85b2a3f6d2738706eac37068a49476b4bdc514d9..703fd6e0f381967201d388fbd643e6330c5a1277 100644 (file)
@@ -380,7 +380,8 @@ defmodule Pleroma.ObjectTest do
 
       user = insert(:user)
       activity = Activity.get_create_by_object_ap_id(object.data["id"])
-      {:ok, _activity, object} = CommonAPI.favorite(activity.id, user)
+      {:ok, activity} = CommonAPI.favorite(user, activity.id)
+      object = Object.get_by_ap_id(activity.data["object"])
 
       assert object.data["like_count"] == 1
 
index 33b77e7e72559454f89dbf019f1121aa64a48486..bccc1c8d07a435bb5d2df6c07d4507a2deb80f5c 100644 (file)
@@ -60,7 +60,7 @@ defmodule Pleroma.StateTest do
       other_user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
       _ = CommonAPI.follow(user, other_user)
-      CommonAPI.favorite(activity.id, other_user)
+      CommonAPI.favorite(other_user, activity.id)
       CommonAPI.repeat(activity.id, other_user)
 
       assert %{direct: 0, private: 0, public: 1, unlisted: 0} =
index ed1c31d9c47b9678463c8532624c8ee47d084ca3..7b05993d318ff19be76206ef89263b60e778cdf2 100644 (file)
@@ -102,7 +102,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
       {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"})
       {:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"})
 
-      CommonAPI.favorite(id, user2)
+      CommonAPI.favorite(user2, id)
 
       likes = %{
         "first" =>
index b07fed42bac84b2eb9b0830887e0ad4f74893f4c..8dcac676d0cc8636e0da6bb3d6c54a5f1c0e32f5 100644 (file)
@@ -1146,8 +1146,8 @@ defmodule Pleroma.UserTest do
       object_two = insert(:note, user: follower)
       activity_two = insert(:note_activity, user: follower, note: object_two)
 
-      {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
-      {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
+      {:ok, like} = CommonAPI.favorite(user, activity_two.id)
+      {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
       {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
 
       {:ok, job} = User.delete(user)
index 3dd3dd04dcd878959cbba77e38b9398d5cc3ce12..d5dd44cc31cd067816ff4d4cbf8814300cdd7b86 100644 (file)
@@ -1894,14 +1894,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
       {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
 
-      {:ok, _, _} = CommonAPI.favorite(a4.id, user)
-      {:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
-      {:ok, _, _} = CommonAPI.favorite(a3.id, user)
-      {:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
-      {:ok, _, _} = CommonAPI.favorite(a5.id, user)
-      {:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
-      {:ok, _, _} = CommonAPI.favorite(a1.id, user)
-      {:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
+      {:ok, _} = CommonAPI.favorite(user, a4.id)
+      {:ok, _} = CommonAPI.favorite(other_user, a3.id)
+      {:ok, _} = CommonAPI.favorite(user, a3.id)
+      {:ok, _} = CommonAPI.favorite(other_user, a5.id)
+      {:ok, _} = CommonAPI.favorite(user, a5.id)
+      {:ok, _} = CommonAPI.favorite(other_user, a4.id)
+      {:ok, _} = CommonAPI.favorite(user, a1.id)
+      {:ok, _} = CommonAPI.favorite(other_user, a1.id)
       result = ActivityPub.fetch_favourites(user)
 
       assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs
new file mode 100644 (file)
index 0000000..3c5c369
--- /dev/null
@@ -0,0 +1,83 @@
+defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Web.ActivityPub.ObjectValidator
+  alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+  alias Pleroma.Web.ActivityPub.Utils
+  alias Pleroma.Web.CommonAPI
+
+  import Pleroma.Factory
+
+  describe "likes" do
+    setup do
+      user = insert(:user)
+      {:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"})
+
+      valid_like = %{
+        "to" => [user.ap_id],
+        "cc" => [],
+        "type" => "Like",
+        "id" => Utils.generate_activity_id(),
+        "object" => post_activity.data["object"],
+        "actor" => user.ap_id,
+        "context" => "a context"
+      }
+
+      %{valid_like: valid_like, user: user, post_activity: post_activity}
+    end
+
+    test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do
+      {:ok, object, _meta} = ObjectValidator.validate(valid_like, [])
+
+      assert "id" in Map.keys(object)
+    end
+
+    test "is valid for a valid object", %{valid_like: valid_like} do
+      assert LikeValidator.cast_and_validate(valid_like).valid?
+    end
+
+    test "it errors when the actor is missing or not known", %{valid_like: valid_like} do
+      without_actor = Map.delete(valid_like, "actor")
+
+      refute LikeValidator.cast_and_validate(without_actor).valid?
+
+      with_invalid_actor = Map.put(valid_like, "actor", "invalidactor")
+
+      refute LikeValidator.cast_and_validate(with_invalid_actor).valid?
+    end
+
+    test "it errors when the object is missing or not known", %{valid_like: valid_like} do
+      without_object = Map.delete(valid_like, "object")
+
+      refute LikeValidator.cast_and_validate(without_object).valid?
+
+      with_invalid_object = Map.put(valid_like, "object", "invalidobject")
+
+      refute LikeValidator.cast_and_validate(with_invalid_object).valid?
+    end
+
+    test "it errors when the actor has already like the object", %{
+      valid_like: valid_like,
+      user: user,
+      post_activity: post_activity
+    } do
+      _like = CommonAPI.favorite(user, post_activity.id)
+
+      refute LikeValidator.cast_and_validate(valid_like).valid?
+    end
+
+    test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do
+      wrapped_like =
+        valid_like
+        |> Map.put("actor", %{"id" => valid_like["actor"]})
+        |> Map.put("object", %{"id" => valid_like["object"]})
+
+      validated = LikeValidator.cast_and_validate(wrapped_like)
+
+      assert validated.valid?
+
+      assert {:actor, valid_like["actor"]} in validated.changes
+      assert {:object, valid_like["object"]} in validated.changes
+    end
+  end
+end
diff --git a/test/web/activity_pub/object_validators/note_validator_test.exs b/test/web/activity_pub/object_validators/note_validator_test.exs
new file mode 100644 (file)
index 0000000..2bcd75e
--- /dev/null
@@ -0,0 +1,35 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
+  alias Pleroma.Web.ActivityPub.Utils
+
+  import Pleroma.Factory
+
+  describe "Notes" do
+    setup do
+      user = insert(:user)
+
+      note = %{
+        "id" => Utils.generate_activity_id(),
+        "type" => "Note",
+        "actor" => user.ap_id,
+        "to" => [user.follower_address],
+        "cc" => [],
+        "content" => "Hellow this is content.",
+        "context" => "xxx",
+        "summary" => "a post"
+      }
+
+      %{user: user, note: note}
+    end
+
+    test "a basic note validates", %{note: note} do
+      %{valid?: true} = NoteValidator.cast_and_validate(note)
+    end
+  end
+end
diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs
new file mode 100644 (file)
index 0000000..3e17a94
--- /dev/null
@@ -0,0 +1,32 @@
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do
+  alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime
+  use Pleroma.DataCase
+
+  test "it validates an xsd:Datetime" do
+    valid_strings = [
+      "2004-04-12T13:20:00",
+      "2004-04-12T13:20:15.5",
+      "2004-04-12T13:20:00-05:00",
+      "2004-04-12T13:20:00Z"
+    ]
+
+    invalid_strings = [
+      "2004-04-12T13:00",
+      "2004-04-1213:20:00",
+      "99-04-12T13:00",
+      "2004-04-12"
+    ]
+
+    assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z")
+
+    Enum.each(valid_strings, fn date_time ->
+      result = DateTime.cast(date_time)
+      assert {:ok, _} = result
+    end)
+
+    Enum.each(invalid_strings, fn date_time ->
+      result = DateTime.cast(date_time)
+      assert :error == result
+    end)
+  end
+end
diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs
new file mode 100644 (file)
index 0000000..318d306
--- /dev/null
@@ -0,0 +1,87 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.PipelineTest do
+  use Pleroma.DataCase
+
+  import Mock
+  import Pleroma.Factory
+
+  describe "common_pipeline/2" do
+    test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
+      activity = insert(:note_activity)
+      meta = [local: true]
+
+      with_mocks([
+        {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
+        {
+          Pleroma.Web.ActivityPub.MRF,
+          [],
+          [filter: fn o -> {:ok, o} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.ActivityPub,
+          [],
+          [persist: fn o, m -> {:ok, o, m} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.SideEffects,
+          [],
+          [handle: fn o, m -> {:ok, o, m} end]
+        },
+        {
+          Pleroma.Web.Federator,
+          [],
+          [publish: fn _o -> :ok end]
+        }
+      ]) do
+        assert {:ok, ^activity, ^meta} =
+                 Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
+
+        assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
+        assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
+        assert_called(Pleroma.Web.Federator.publish(activity))
+      end
+    end
+
+    test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do
+      activity = insert(:note_activity)
+      meta = [local: false]
+
+      with_mocks([
+        {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
+        {
+          Pleroma.Web.ActivityPub.MRF,
+          [],
+          [filter: fn o -> {:ok, o} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.ActivityPub,
+          [],
+          [persist: fn o, m -> {:ok, o, m} end]
+        },
+        {
+          Pleroma.Web.ActivityPub.SideEffects,
+          [],
+          [handle: fn o, m -> {:ok, o, m} end]
+        },
+        {
+          Pleroma.Web.Federator,
+          [],
+          []
+        }
+      ]) do
+        assert {:ok, ^activity, ^meta} =
+                 Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
+
+        assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
+        assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
+        assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
+      end
+    end
+  end
+end
diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs
new file mode 100644 (file)
index 0000000..ef91954
--- /dev/null
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Object
+  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Builder
+  alias Pleroma.Web.ActivityPub.SideEffects
+  alias Pleroma.Web.CommonAPI
+
+  import Pleroma.Factory
+
+  describe "like objects" do
+    setup do
+      user = insert(:user)
+      {:ok, post} = CommonAPI.post(user, %{"status" => "hey"})
+
+      {:ok, like_data, _meta} = Builder.like(user, post.object)
+      {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
+
+      %{like: like, user: user}
+    end
+
+    test "add the like to the original object", %{like: like, user: user} do
+      {:ok, like, _} = SideEffects.handle(like)
+      object = Object.get_by_ap_id(like.data["object"])
+      assert object.data["like_count"] == 1
+      assert user.ap_id in object.data["likes"]
+    end
+  end
+end
index efbca82f620b177bd9691ef19d612afd8369bac7..83372ec7ef1fe6678961cd34dd1bb0de1054f255 100644 (file)
@@ -334,7 +334,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
         |> Poison.decode!()
         |> Map.put("object", activity.data["object"])
 
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+      {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
+
+      refute Enum.empty?(activity.recipients)
 
       assert data["actor"] == "http://mastodon.example.org/users/admin"
       assert data["type"] == "Like"
index 09866e99be201dd9161d7ae522b828970d3a0e08..f6796ad4ae41332a0ff7e627e6c58574f7f67395 100644 (file)
@@ -61,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
     object = Object.normalize(note)
     user = insert(:user)
 
-    {:ok, like_activity, _} = CommonAPI.favorite(note.id, user)
+    {:ok, like_activity} = CommonAPI.favorite(user, note.id)
 
     result = ObjectView.render("object.json", %{object: like_activity})
 
index b80523160eca66bdf4f85d05c3cf44874a332396..c2ed1c7897db16aaa0edfb18f0bec9bdd1f4ae57 100644 (file)
@@ -284,9 +284,12 @@ defmodule Pleroma.Web.CommonAPITest do
       user = insert(:user)
       other_user = insert(:user)
 
-      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
+      {:ok, post_activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
 
-      {:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
+      {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
+      assert data["type"] == "Like"
+      assert data["actor"] == user.ap_id
+      assert data["object"] == post_activity.data["object"]
     end
 
     test "retweeting a status twice returns the status" do
@@ -298,13 +301,13 @@ defmodule Pleroma.Web.CommonAPITest do
       {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
     end
 
-    test "favoriting a status twice returns the status" do
+    test "favoriting a status twice returns ok, but without the like activity" do
       user = insert(:user)
       other_user = insert(:user)
 
       {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
-      {:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user)
-      {:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user)
+      {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
+      assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
     end
   end
 
index d452ddbdd5d45d4479ed261dedb20bef6d4c54a5..3bc9aff16e7131bf4c7e9cd99d4b1d0ee8552524 100644 (file)
@@ -194,10 +194,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
       {:ok, private_activity} =
         CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
 
-      {:ok, _, _} = CommonAPI.favorite(public_activity.id, user)
-      {:ok, _, _} = CommonAPI.favorite(direct_activity.id, user)
-      {:ok, _, _} = CommonAPI.favorite(unlisted_activity.id, user)
-      {:ok, _, _} = CommonAPI.favorite(private_activity.id, user)
+      {:ok, _} = CommonAPI.favorite(user, public_activity.id)
+      {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
+      {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
+      {:ok, _} = CommonAPI.favorite(user, private_activity.id)
 
       activity_ids =
         conn
@@ -274,7 +274,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
 
     {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
     {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
-    {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
+    {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
     {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
     {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
 
index fbf63f608be841266a7889ca7d8891b759dff27e..f36552041af93f2ab955277319d9448858da68b5 100644 (file)
@@ -622,7 +622,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       user1 = insert(:user)
       user2 = insert(:user)
       user3 = insert(:user)
-      CommonAPI.favorite(activity.id, user2)
+      {:ok, _} = CommonAPI.favorite(user2, activity.id)
       {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
       {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
       {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
@@ -697,11 +697,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       activity = insert(:note_activity)
 
       post(conn, "/api/v1/statuses/#{activity.id}/favourite")
-      assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200)
+
+      assert post(conn, "/api/v1/statuses/#{activity.id}/favourite")
+             |> json_response(200)
     end
 
     test "returns 404 error for a wrong id", %{conn: conn} do
-      conn = post(conn, "/api/v1/statuses/1/favourite")
+      conn =
+        conn
+        |> post("/api/v1/statuses/1/favourite")
 
       assert json_response(conn, 404) == %{"error" => "Record not found"}
     end
@@ -713,7 +717,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     test "unfavorites a status and returns it", %{user: user, conn: conn} do
       activity = insert(:note_activity)
 
-      {:ok, _, _} = CommonAPI.favorite(activity.id, user)
+      {:ok, _} = CommonAPI.favorite(user, activity.id)
 
       conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite")
 
@@ -1025,7 +1029,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
     test "returns users who have favorited the status", %{conn: conn, activity: activity} do
       other_user = insert(:user)
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       response =
         conn
@@ -1056,7 +1060,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       other_user = insert(:user)
       {:ok, _user_relationship} = User.block(user, other_user)
 
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       response =
         conn
@@ -1068,7 +1072,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
     test "does not fail on an unauthenticated request", %{activity: activity} do
       other_user = insert(:user)
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       response =
         build_conn()
@@ -1088,7 +1092,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
           "visibility" => "direct"
         })
 
-      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
 
       favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"
 
@@ -1248,7 +1252,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
     {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
 
-    {:ok, _, _} = CommonAPI.favorite(activity.id, user)
+    {:ok, _} = CommonAPI.favorite(user, activity.id)
 
     first_conn = get(conn, "/api/v1/favourites")
 
@@ -1265,7 +1269,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
           "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
       })
 
-    {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
+    {:ok, _} = CommonAPI.favorite(user, second_activity.id)
 
     last_like = status["id"]
 
index 4df9c3c036c61bc752edf11d8b4422f083d533f0..779126556ad2ff89032abe87e5d7420ecb6fd2fb 100644 (file)
@@ -42,7 +42,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
     user = insert(:user)
     another_user = insert(:user)
     {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
-    {:ok, favorite_activity, _object} = CommonAPI.favorite(create_activity.id, another_user)
+    {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id)
     {:ok, [notification]} = Notification.create_notifications(favorite_activity)
     create_activity = Activity.get_by_id(create_activity.id)
 
index 3b84358e4a66009f15bc8df5aecf1e7674790367..ae99e37fee2c5d88c99a768394d813baae8b346a 100644 (file)
@@ -138,7 +138,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
       user = insert(:user)
 
-      {:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
+      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
 
       assert like_activity.data["type"] == "Like"
 
index 245cc15796125cea78acfced7c3148e1bad505ad..3853a9bbb8bc97c173a10230167625a067cabb7b 100644 (file)
@@ -140,7 +140,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       user: user
     } do
       [activity | _] = insert_pair(:note_activity)
-      CommonAPI.favorite(activity.id, user)
+      CommonAPI.favorite(user, activity.id)
 
       response =
         conn
@@ -157,7 +157,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       user: user
     } do
       activity = insert(:note_activity)
-      CommonAPI.favorite(activity.id, user)
+      CommonAPI.favorite(user, activity.id)
 
       build_conn()
       |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
@@ -174,7 +174,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
           "visibility" => "direct"
         })
 
-      CommonAPI.favorite(direct.id, user)
+      CommonAPI.favorite(user, direct.id)
 
       for u <- [user, current_user] do
         response =
@@ -204,7 +204,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
           "visibility" => "direct"
         })
 
-      CommonAPI.favorite(direct.id, user)
+      CommonAPI.favorite(user, direct.id)
 
       response =
         conn
@@ -221,7 +221,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       activities = insert_list(10, :note_activity)
 
       Enum.each(activities, fn activity ->
-        CommonAPI.favorite(activity.id, user)
+        CommonAPI.favorite(user, activity.id)
       end)
 
       third_activity = Enum.at(activities, 2)
@@ -247,7 +247,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
       7
       |> insert_list(:note_activity)
       |> Enum.each(fn activity ->
-        CommonAPI.favorite(activity.id, user)
+        CommonAPI.favorite(user, activity.id)
       end)
 
       response =
@@ -279,7 +279,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     test "returns 403 error when user has hidden own favorites", %{conn: conn} do
       user = insert(:user, hide_favorites: true)
       activity = insert(:note_activity)
-      CommonAPI.favorite(activity.id, user)
+      CommonAPI.favorite(user, activity.id)
 
       conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
 
@@ -289,7 +289,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
     test "hides favorites for new users by default", %{conn: conn} do
       user = insert(:user)
       activity = insert(:note_activity)
-      CommonAPI.favorite(activity.id, user)
+      CommonAPI.favorite(user, activity.id)
 
       assert user.hide_favorites
       conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
index 089d55577c9771956125326c854adf85d7e03e02..b90e31f94fbed95a998e8a4defca620a761adcf9 100644 (file)
@@ -170,7 +170,7 @@ defmodule Pleroma.Web.Push.ImplTest do
           "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
       })
 
-    {:ok, activity, _} = CommonAPI.favorite(activity.id, user)
+    {:ok, activity} = CommonAPI.favorite(user, activity.id)
     object = Object.normalize(activity)
 
     assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"
index 339f99bbf8b39f2dc2b13fc8971bac19995e6a77..f0bafc0931da46f672eb5ca4f4fa351031ea2672 100644 (file)
@@ -65,9 +65,6 @@ defmodule Pleroma.Web.StreamerTest do
       blocked = insert(:user)
       {:ok, _user_relationship} = User.block(user, blocked)
 
-      {:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
-      {:ok, notif, _} = CommonAPI.favorite(activity.id, blocked)
-
       task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
 
       Streamer.add_socket(
@@ -75,6 +72,9 @@ defmodule Pleroma.Web.StreamerTest do
         %{transport_pid: task.pid, assigns: %{user: user}}
       )
 
+      {:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
+      {:ok, notif} = CommonAPI.favorite(blocked, activity.id)
+
       Streamer.stream("user:notification", notif)
       Task.await(task)
     end
@@ -84,10 +84,6 @@ defmodule Pleroma.Web.StreamerTest do
     } do
       user2 = insert(:user)
 
-      {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
-      {:ok, activity} = CommonAPI.add_mute(user, activity)
-      {:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
-
       task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
 
       Streamer.add_socket(
@@ -95,6 +91,10 @@ defmodule Pleroma.Web.StreamerTest do
         %{transport_pid: task.pid, assigns: %{user: user}}
       )
 
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+      {:ok, activity} = CommonAPI.add_mute(user, activity)
+      {:ok, notif} = CommonAPI.favorite(user2, activity.id)
+
       Streamer.stream("user:notification", notif)
       Task.await(task)
     end
@@ -104,10 +104,6 @@ defmodule Pleroma.Web.StreamerTest do
     } do
       user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
 
-      {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
-      {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
-      {:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
-
       task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
 
       Streamer.add_socket(
@@ -115,6 +111,10 @@ defmodule Pleroma.Web.StreamerTest do
         %{transport_pid: task.pid, assigns: %{user: user}}
       )
 
+      {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+      {:ok, notif} = CommonAPI.favorite(user2, activity.id)
+
       Streamer.stream("user:notification", notif)
       Task.await(task)
     end
@@ -465,7 +465,7 @@ defmodule Pleroma.Web.StreamerTest do
     CommonAPI.hide_reblogs(user1, user2)
 
     {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
-    {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2)
+    {:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
 
     task =
       Task.async(fn ->