logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma
commit: 349b9d86dc25ae9514781ef2ac56953fa357a031
parent: 8444e7ee9651f3b1836954a3891b5ae26ee79f99
Author: lain <lain@soykaf.club>
Date:   Mon,  6 Apr 2020 11:59:01 +0000

Merge branch 'remake-remodel-2' into 'develop'

Ingestion Pipeline Revamp

See merge request pleroma/pleroma!2315

Diffstat:

MCOPYING4++--
Mlib/pleroma/object/containment.ex12++++++++++++
Mlib/pleroma/web/activity_pub/activity_pub.ex15+++++++++++++++
Alib/pleroma/web/activity_pub/builder.ex43+++++++++++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validator.ex37+++++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/common_validations.ex32++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/create_validator.ex30++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/like_validator.ex57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/note_validator.ex63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/types/date_time.ex34++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/object_validators/types/object_id.ex33+++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/pipeline.ex42++++++++++++++++++++++++++++++++++++++++++
Alib/pleroma/web/activity_pub/side_effects.ex28++++++++++++++++++++++++++++
Mlib/pleroma/web/activity_pub/transmogrifier.ex65++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mlib/pleroma/web/common_api/common_api.ex58+++++++++++++++++++++++++++++++++++++++++++++++-----------
Mlib/pleroma/web/mastodon_api/controllers/status_controller.ex6+++---
Mtest/notification_test.exs8++++----
Mtest/object_test.exs3++-
Mtest/stat_test.exs2+-
Mtest/tasks/database_test.exs2+-
Mtest/user_test.exs4++--
Mtest/web/activity_pub/activity_pub_test.exs16++++++++--------
Atest/web/activity_pub/object_validator_test.exs83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/web/activity_pub/object_validators/note_validator_test.exs35+++++++++++++++++++++++++++++++++++
Atest/web/activity_pub/object_validators/types/date_time_test.exs32++++++++++++++++++++++++++++++++
Atest/web/activity_pub/object_validators/types/object_id_test.exs37+++++++++++++++++++++++++++++++++++++
Atest/web/activity_pub/pipeline_test.exs87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/web/activity_pub/side_effects_test.exs34++++++++++++++++++++++++++++++++++
Mtest/web/activity_pub/transmogrifier_test.exs4+++-
Mtest/web/activity_pub/views/object_view_test.exs2+-
Mtest/web/common_api/common_api_test.exs13++++++++-----
Mtest/web/mastodon_api/controllers/notification_controller_test.exs12++++++------
Mtest/web/mastodon_api/controllers/status_controller_test.exs24++++++++++++++----------
Mtest/web/mastodon_api/views/notification_view_test.exs2+-
Mtest/web/ostatus/ostatus_controller_test.exs2+-
Mtest/web/pleroma_api/controllers/account_controller_test.exs16++++++++--------
Mtest/web/push/impl_test.exs2+-
Mtest/web/streamer/streamer_test.exs24++++++++++++------------
38 files changed, 915 insertions(+), 88 deletions(-)

diff --git a/COPYING b/COPYING @@ -1,4 +1,4 @@ -Unless otherwise stated this repository is copyright © 2017-2019 +Unless otherwise stated this repository is copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>, and is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as AGPL-3. @@ -23,7 +23,7 @@ priv/static/images/pleroma-fox-tan-shy.png --- -The following files are copyright © 2017-2019 Pleroma Authors +The following files are copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>, and are distributed under the Creative Commons Attribution-ShareAlike 4.0 International license, you should have received a copy of the license file as CC-BY-SA-4.0. diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex @@ -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. diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -125,6 +125,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def increase_poll_votes_if_vote(_create_data), do: :noop + @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()} + def persist(object, meta) do + with local <- Keyword.fetch!(meta, :local), + {recipients, _, _} <- get_recipients(object), + {:ok, activity} <- + Repo.insert(%Activity{ + data: object, + local: local, + recipients: recipients, + actor: object["actor"] + }) do + {:ok, activity, meta} + end + end + @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()} def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do with nil <- Activity.normalize(map), diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex @@ -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 @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 + |> Map.new(fn {key, val} -> {to_string(key), val} end) + 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 @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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, object -> + if Object.get_cached_by_ap_id(object) do + [] + else + [{field_name, "can't find object"}] + 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 @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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.NoteValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.Types + + import Ecto.Changeset + + @primary_key false + + embedded_schema do + field(:id, Types.ObjectID, 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 + cast(%__MODULE__{}, 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 @@ -0,0 +1,57 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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, Types.ObjectID, 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 @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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, Types.ObjectID, 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 @@ -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_id.ex b/lib/pleroma/web/activity_pub/object_validators/types/object_id.ex @@ -0,0 +1,33 @@ +defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do + use Ecto.Type + + def type, do: :string + + def cast(object) when is_binary(object) do + # Host has to be present and scheme has to be an http scheme (for now) + case URI.parse(object) do + %URI{host: nil} -> + :error + + %URI{scheme: scheme} when scheme in ["https", "http"] -> + {:ok, object} + + _ -> + :error + end + end + + def cast(%{"id" => object}), do: cast(object) + + 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 @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 + {:mrf_object, {:reject, _}} -> {:ok, nil, meta} + 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, :badarg} + end + end +end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex @@ -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 diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -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 @@ -609,17 +612,20 @@ 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 = ObjectValidator.stringify_keys(Map.from_struct(cast_data_sym)), + :ok <- ObjectValidator.fetch_actor_and_object(cast_data), + {_, {:ok, cast_data}} <- {:ensure_context_presence, ensure_context_presence(cast_data)}, + {_, {:ok, cast_data}} <- + {:ensure_recipients_presence, ensure_recipients_presence(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 @@ -1243,4 +1249,45 @@ 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 ensure_context_presence(%{"context" => context} = data) when is_binary(context), + do: {:ok, data} + + defp ensure_context_presence(%{"object" => object} = data) when is_binary(object) do + with %{data: %{"context" => context}} when is_binary(context) <- Object.normalize(object) do + {:ok, Map.put(data, "context", context)} + else + _ -> + {:error, :no_context} + end + end + + defp ensure_context_presence(_) do + {:error, :no_context} + end + + defp ensure_recipients_presence(%{"to" => [_ | _], "cc" => [_ | _]} = data), + do: {:ok, data} + + defp ensure_recipients_presence(%{"object" => object} = data) do + case Object.normalize(object) do + %{data: %{"actor" => actor}} -> + data = + data + |> Map.put("to", [actor]) + |> Map.put("cc", data["cc"] || []) + + {:ok, data} + + nil -> + {:error, :no_object} + + _ -> + {:error, :no_actor} + end + end + + defp ensure_recipients_presence(_) do + {:error, :no_object} + end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex @@ -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,51 @@ 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() | :already_liked} | {:error, any()} + def favorite(%User{} = user, id) do + case favorite_helper(user, id) do + {:ok, _} = res -> + res + + {:error, :not_found} = res -> + res + + {:error, e} -> + Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}") + {:error, dgettext("errors", "Could not favorite")} + end + end + + def favorite_helper(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 + {:error, e} + end + + e -> + {:error, e} end end diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -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 diff --git a/test/notification_test.exs b/test/notification_test.exs @@ -537,7 +537,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) {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity_two) @@ -620,7 +620,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 @@ -637,7 +637,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 @@ -692,7 +692,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 diff --git a/test/object_test.exs b/test/object_test.exs @@ -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 diff --git a/test/stat_test.exs b/test/stat_test.exs @@ -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} = diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs @@ -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" => diff --git a/test/user_test.exs b/test/user_test.exs @@ -1141,8 +1141,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) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs @@ -1900,14 +1900,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 @@ -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 @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 @@ -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/object_validators/types/object_id_test.exs b/test/web/activity_pub/object_validators/types/object_id_test.exs @@ -0,0 +1,37 @@ +defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do + alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID + use Pleroma.DataCase + + @uris [ + "http://lain.com/users/lain", + "http://lain.com", + "https://lain.com/object/1" + ] + + @non_uris [ + "https://", + "rin", + 1, + :x, + %{"1" => 2} + ] + + test "it accepts http uris" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(uri) + end) + end + + test "it accepts an object with a nested uri id" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(%{"id" => uri}) + end) + end + + test "it rejects non-uri strings" do + Enum.each(@non_uris, fn non_uri -> + assert :error == ObjectID.cast(non_uri) + assert :error == ObjectID.cast(%{"id" => non_uri}) + end) + end +end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs @@ -0,0 +1,87 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 @@ -0,0 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs @@ -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" diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs @@ -59,7 +59,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}) diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs @@ -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 diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -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) @@ -310,7 +310,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) diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -775,7 +775,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) @@ -850,11 +850,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 @@ -866,7 +870,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") @@ -1176,7 +1180,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 @@ -1207,7 +1211,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 @@ -1219,7 +1223,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() @@ -1239,7 +1243,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" @@ -1399,7 +1403,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") @@ -1416,7 +1420,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"] diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs @@ -54,7 +54,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) diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs @@ -136,7 +136,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" diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -138,7 +138,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 @@ -155,7 +155,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") @@ -172,7 +172,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 = @@ -202,7 +202,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do "visibility" => "direct" }) - CommonAPI.favorite(direct.id, user) + CommonAPI.favorite(user, direct.id) response = conn @@ -219,7 +219,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) @@ -245,7 +245,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 = @@ -277,7 +277,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") @@ -287,7 +287,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") diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs @@ -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" diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs @@ -64,9 +64,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( @@ -74,6 +71,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 @@ -83,10 +83,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( @@ -94,6 +90,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 @@ -103,10 +103,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( @@ -114,6 +110,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 @@ -476,7 +476,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 ->