Pipeline Ingestion: Note

This commit is contained in:
Haelwenn (lanodan) Monnier 2020-09-10 11:11:10 +02:00
parent e2a3365b5c
commit c944932674
No known key found for this signature in database
GPG Key ID: D5B7A8E43C997DEE
19 changed files with 202 additions and 179 deletions

View File

@ -13,19 +13,22 @@ def cast(object) when is_binary(object) do
cast([object]) cast([object])
end end
def cast(object) when is_map(object) do
case ObjectID.cast(object) do
{:ok, data} -> {:ok, data}
_ -> :error
end
end
def cast(data) when is_list(data) do def cast(data) when is_list(data) do
data data
|> Enum.reduce_while({:ok, []}, fn |> Enum.reduce_while({:ok, []}, fn element, {:ok, list} ->
nil, {:ok, list} ->
{:cont, {:ok, list}}
element, {:ok, list} ->
case ObjectID.cast(element) do case ObjectID.cast(element) do
{:ok, id} -> {:ok, id} ->
{:cont, {:ok, [id | list]}} {:cont, {:ok, [id | list]}}
_ -> _ ->
{:halt, {:error, element}} {:cont, {:ok, list}}
end end
end) end)
end end

View File

@ -88,7 +88,7 @@ defp increase_replies_count_if_reply(%{
defp increase_replies_count_if_reply(_create_data), do: :noop defp increase_replies_count_if_reply(_create_data), do: :noop
@object_types ~w[ChatMessage Question Answer Audio Video Event Article] @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note]
@impl true @impl true
def persist(%{"type" => type} = object, meta) when type in @object_types do def persist(%{"type" => type} = object, meta) when type in @object_types do
with {:ok, object} <- Object.create(object) do with {:ok, object} <- Object.create(object) do

View File

@ -101,7 +101,7 @@ def validate(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
meta meta
) )
when objtype in ~w[Question Answer Audio Video Event Article] do when objtype in ~w[Question Answer Audio Video Event Article Note] do
with {:ok, object_data} <- cast_and_apply(object), with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <- {:ok, create_activity} <-
@ -114,7 +114,7 @@ def validate(
end end
def validate(%{"type" => type} = object, meta) def validate(%{"type" => type} = object, meta)
when type in ~w[Event Question Audio Video Article] do when type in ~w[Event Question Audio Video Article Note] do
validator = validator =
case type do case type do
"Event" -> EventValidator "Event" -> EventValidator
@ -122,6 +122,7 @@ def validate(%{"type" => type} = object, meta)
"Audio" -> AudioVideoValidator "Audio" -> AudioVideoValidator
"Video" -> AudioVideoValidator "Video" -> AudioVideoValidator
"Article" -> ArticleNoteValidator "Article" -> ArticleNoteValidator
"Note" -> ArticleNoteValidator
end end
with {:ok, object} <- with {:ok, object} <-
@ -183,7 +184,7 @@ def cast_and_apply(%{"type" => "Event"} = object) do
EventValidator.cast_and_apply(object) EventValidator.cast_and_apply(object)
end end
def cast_and_apply(%{"type" => "Article"} = object) do def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do
ArticleNoteValidator.cast_and_apply(object) ArticleNoteValidator.cast_and_apply(object)
end end

View File

@ -50,6 +50,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do
field(:likes, {:array, ObjectValidators.ObjectID}, default: []) field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
end end
def cast_and_apply(data) do def cast_and_apply(data) do
@ -65,24 +67,39 @@ def cast_and_validate(data) do
end end
def cast_data(data) do def cast_data(data) do
data = fix(data)
%__MODULE__{} %__MODULE__{}
|> changeset(data) |> changeset(data)
end end
defp fix_url(%{"url" => url} = data) when is_map(url) do defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data
Map.put(data, "url", url["href"]) defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"])
end
defp fix_url(data), do: data defp fix_url(data), do: data
defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data
defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
defp fix_tag(data), do: Map.drop(data, ["tag"])
defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data)
when is_list(replies),
do: Map.put(data, "replies", replies)
defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
do: Map.put(data, "replies", replies)
defp fix_replies(%{"replies" => replies} = data) when is_bitstring(replies),
do: Map.drop(data, ["replies"])
defp fix_replies(data), do: data
defp fix(data) do defp fix(data) do
data data
|> CommonFixes.fix_actor() |> CommonFixes.fix_actor()
|> CommonFixes.fix_object_defaults() |> CommonFixes.fix_object_defaults()
|> fix_url() |> fix_url()
|> fix_tag()
|> fix_replies()
|> Transmogrifier.fix_emoji() |> Transmogrifier.fix_emoji()
|> Transmogrifier.fix_content_map()
end end
def changeset(struct, data) do def changeset(struct, data) do

View File

@ -26,14 +26,20 @@ def fix_object_defaults(data) do
|> Transmogrifier.fix_implicit_addressing(follower_collection) |> Transmogrifier.fix_implicit_addressing(follower_collection)
end end
def fix_activity_defaults(data, meta) do defp fix_activity_recipients(activity, field, object) do
{:ok, data} = ObjectValidators.Recipients.cast(activity[field] || object[field])
Map.put(activity, field, data)
end
def fix_activity_defaults(activity, meta) do
object = meta[:object_data] || %{} object = meta[:object_data] || %{}
data activity
|> Map.put_new("to", object["to"] || []) |> fix_activity_recipients("to", object)
|> Map.put_new("cc", object["cc"] || []) |> fix_activity_recipients("cc", object)
|> Map.put_new("bto", object["bto"] || []) |> fix_activity_recipients("bto", object)
|> Map.put_new("bcc", object["bcc"] || []) |> fix_activity_recipients("bcc", object)
end end
def fix_actor(data) do def fix_actor(data) do

View File

@ -14,6 +14,7 @@ def validate_any_presence(cng, fields) do
fields fields
|> Enum.map(fn field -> get_field(cng, field) end) |> Enum.map(fn field -> get_field(cng, field) end)
|> Enum.any?(fn |> Enum.any?(fn
nil -> false
[] -> false [] -> false
_ -> true _ -> true
end) end)

View File

@ -1,29 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
import Ecto.Changeset
@primary_key false
embedded_schema do
field(:id, ObjectValidators.ObjectID, primary_key: true)
field(:actor, ObjectValidators.ObjectID)
field(:type, :string)
field(:to, ObjectValidators.Recipients, default: [])
field(:cc, ObjectValidators.Recipients, default: [])
field(:bto, ObjectValidators.Recipients, default: [])
field(:bcc, ObjectValidators.Recipients, default: [])
embeds_one(:object, NoteValidator)
end
def cast_data(data) do
cast(%__MODULE__{}, data, __schema__(:fields))
end
end

View File

@ -203,6 +203,19 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
Object.increase_replies_count(in_reply_to) Object.increase_replies_count(in_reply_to)
end end
reply_depth = (meta[:depth] || 0) + 1
# FIXME: Force inReplyTo to replies
if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
object.data["replies"] != nil do
for reply_id <- object.data["replies"] do
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
"id" => reply_id,
"depth" => reply_depth
})
end
end
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn -> ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
end) end)
@ -366,7 +379,7 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
end end
def handle_object_creation(%{"type" => objtype} = object, meta) def handle_object_creation(%{"type" => objtype} = object, meta)
when objtype in ~w[Audio Video Question Event Article] do when objtype in ~w[Audio Video Question Event Article Note] do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
{:ok, object, meta} {:ok, object, meta}
end end

View File

@ -404,10 +404,9 @@ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id
# - tags # - tags
# - emoji # - emoji
def handle_incoming( def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data, %{"type" => "Create", "object" => %{"type" => "Page"} = object} = data,
options options
) ) do
when objtype in ~w{Note Page} do
actor = Containment.get_actor(data) actor = Containment.get_actor(data)
with nil <- Activity.get_create_by_object_ap_id(object["id"]), with nil <- Activity.get_create_by_object_ap_id(object["id"]),
@ -499,14 +498,15 @@ def handle_incoming(
def handle_incoming( def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data, %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
_options options
) )
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do
data = Map.put(data, "object", strip_internal_fields(data["object"])) data = Map.put(data, "object", strip_internal_fields(data["object"]))
options = Keyword.put(options, :local, false)
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
nil <- Activity.get_create_by_object_ap_id(obj_id), nil <- Activity.get_create_by_object_ap_id(obj_id),
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity, _} <- Pipeline.common_pipeline(data, options) do
{:ok, activity} {:ok, activity}
else else
%Activity{} = activity -> {:ok, activity} %Activity{} = activity -> {:ok, activity}

View File

@ -96,6 +96,11 @@ def perform(:incoming_ap_doc, params) do
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}") Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
{:error, e} {:error, e}
{:error, {:validate_object, _}} = e ->
Logger.error("Incoming AP doc validation error: #{inspect(e)}")
Logger.debug(Jason.encode!(params, pretty: true))
e
e -> e ->
# Just drop those for now # Just drop those for now
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end) Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)

View File

@ -3,6 +3,7 @@
"type": "Create", "type": "Create",
"object": { "object": {
"type": "Note", "type": "Note",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"content": "It's a note" "content": "It's a note"
}, },
"to": ["https://www.w3.org/ns/activitystreams#Public"] "to": ["https://www.w3.org/ns/activitystreams#Public"]

View File

@ -123,7 +123,8 @@ test "when association is not loaded" do
"type" => "Note", "type" => "Note",
"content" => "find me!", "content" => "find me!",
"id" => "http://mastodon.example.org/users/admin/objects/1", "id" => "http://mastodon.example.org/users/admin/objects/1",
"attributedTo" => "http://mastodon.example.org/users/admin" "attributedTo" => "http://mastodon.example.org/users/admin",
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
}, },
"to" => ["https://www.w3.org/ns/activitystreams#Public"] "to" => ["https://www.w3.org/ns/activitystreams#Public"]
} }
@ -132,6 +133,7 @@ test "when association is not loaded" do
{:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"}) {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"})
{:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params)
{:ok, remote_activity} = ObanHelpers.perform(job) {:ok, remote_activity} = ObanHelpers.perform(job)
remote_activity = Activity.get_by_id_with_object(remote_activity.id)
%{ %{
japanese_activity: japanese_activity, japanese_activity: japanese_activity,

View File

@ -6,10 +6,10 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: true
test "it asserts that all elements of the list are object ids" do test "it only keeps elements that are valid object ids" do
list = ["https://lain.com/users/lain", "invalid"] list = ["https://lain.com/users/lain", "invalid"]
assert {:error, "invalid"} == Recipients.cast(list) assert {:ok, ["https://lain.com/users/lain"]} == Recipients.cast(list)
end end
test "it works with a list" do test "it works with a list" do

View File

@ -624,6 +624,8 @@ test "it sends notifications to mentioned users in new messages" do
"actor" => user.ap_id, "actor" => user.ap_id,
"object" => %{ "object" => %{
"type" => "Note", "type" => "Note",
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"content" => "message with a Mention tag, but no explicit tagging", "content" => "message with a Mention tag, but no explicit tagging",
"tag" => [ "tag" => [
%{ %{
@ -655,6 +657,9 @@ test "it does not send notifications to users who are only cc in new messages" d
"actor" => user.ap_id, "actor" => user.ap_id,
"object" => %{ "object" => %{
"type" => "Note", "type" => "Note",
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [other_user.ap_id],
"content" => "hi everyone", "content" => "hi everyone",
"attributedTo" => user.ap_id "attributedTo" => user.ap_id
} }
@ -951,6 +956,7 @@ test "notifications are deleted if a remote user is deleted" do
"cc" => [], "cc" => [],
"object" => %{ "object" => %{
"type" => "Note", "type" => "Note",
"id" => remote_user.ap_id <> "/objects/test",
"content" => "Hello!", "content" => "Hello!",
"tag" => [ "tag" => [
%{ %{

View File

@ -539,7 +539,7 @@ test "it inserts an incoming activity into the database" <>
File.read!("test/fixtures/mastodon-post-activity.json") File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!() |> Jason.decode!()
|> Map.put("actor", user.ap_id) |> Map.put("actor", user.ap_id)
|> put_in(["object", "attridbutedTo"], user.ap_id) |> put_in(["object", "attributedTo"], user.ap_id)
conn = conn =
conn conn
@ -820,29 +820,34 @@ test "it clears `unreachable` federation status of the sender", %{conn: conn, da
assert Instances.reachable?(sender_host) assert Instances.reachable?(sender_host)
end end
@tag capture_log: true
test "it removes all follower collections but actor's", %{conn: conn} do test "it removes all follower collections but actor's", %{conn: conn} do
[actor, recipient] = insert_pair(:user) [actor, recipient] = insert_pair(:user)
data = to = [
File.read!("test/fixtures/activitypub-client-post-activity.json")
|> Jason.decode!()
object = Map.put(data["object"], "attributedTo", actor.ap_id)
data =
data
|> Map.put("id", Utils.generate_object_id())
|> Map.put("actor", actor.ap_id)
|> Map.put("object", object)
|> Map.put("cc", [
recipient.follower_address,
actor.follower_address
])
|> Map.put("to", [
recipient.ap_id, recipient.ap_id,
recipient.follower_address, recipient.follower_address,
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
]) ]
cc = [recipient.follower_address, actor.follower_address]
data = %{
"@context" => ["https://www.w3.org/ns/activitystreams"],
"type" => "Create",
"id" => Utils.generate_activity_id(),
"to" => to,
"cc" => cc,
"actor" => actor.ap_id,
"object" => %{
"type" => "Note",
"to" => to,
"cc" => cc,
"content" => "It's a note",
"attributedTo" => actor.ap_id,
"id" => Utils.generate_object_id()
}
}
conn conn
|> assign(:valid_signature, true) |> assign(:valid_signature, true)
@ -852,7 +857,7 @@ test "it removes all follower collections but actor's", %{conn: conn} do
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
activity = Activity.get_by_ap_id(data["id"]) assert activity = Activity.get_by_ap_id(data["id"])
assert activity.id assert activity.id
assert actor.follower_address in activity.recipients assert actor.follower_address in activity.recipients

View File

@ -14,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
import Mock import Mock
import Pleroma.Factory import Pleroma.Factory
import ExUnit.CaptureLog
setup_all do setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@ -147,9 +146,7 @@ test "it does not crash if the object in inReplyTo can't be fetched" do
data data
|> Map.put("object", object) |> Map.put("object", object)
assert capture_log(fn -> assert {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
{:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
end end
test "it does not work for deactivated users" do test "it does not work for deactivated users" do
@ -221,8 +218,25 @@ test "it works for incoming notices with hashtags" do
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
object = Object.normalize(data["object"], fetch: false) object = Object.normalize(data["object"], fetch: false)
assert Enum.at(Object.tags(object), 2) == "moo" assert match?(
assert Object.hashtags(object) == ["moo"] %{
"href" => "http://localtesting.pleroma.lol/users/lain",
"name" => "@lain@localtesting.pleroma.lol",
"type" => "Mention"
},
Enum.at(object.data["tag"], 0)
)
assert match?(
%{
"href" => "http://mastodon.example.org/tags/moo",
"name" => "#moo",
"type" => "Hashtag"
},
Enum.at(object.data["tag"], 1)
)
assert "moo" == Enum.at(object.data["tag"], 2)
end end
test "it works for incoming notices with contentMap" do test "it works for incoming notices with contentMap" do
@ -276,13 +290,11 @@ test "it ensures that address fields become lists" do
File.read!("test/fixtures/mastodon-post-activity.json") File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!() |> Jason.decode!()
|> Map.put("actor", user.ap_id) |> Map.put("actor", user.ap_id)
|> Map.put("to", nil)
|> Map.put("cc", nil) |> Map.put("cc", nil)
object = object =
data["object"] data["object"]
|> Map.put("attributedTo", user.ap_id) |> Map.put("attributedTo", user.ap_id)
|> Map.put("to", nil)
|> Map.put("cc", nil) |> Map.put("cc", nil)
|> Map.put("id", user.ap_id <> "/activities/12345678") |> Map.put("id", user.ap_id <> "/activities/12345678")
@ -290,8 +302,7 @@ test "it ensures that address fields become lists" do
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert !is_nil(data["to"]) refute is_nil(data["cc"])
assert !is_nil(data["cc"])
end end
test "it strips internal likes" do test "it strips internal likes" do
@ -330,70 +341,46 @@ test "it strips internal reactions" do
end end
test "it correctly processes messages with non-array to field" do test "it correctly processes messages with non-array to field" do
user = insert(:user) data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("to", "https://www.w3.org/ns/activitystreams#Public")
|> put_in(["object", "to"], "https://www.w3.org/ns/activitystreams#Public")
message = %{ assert {:ok, activity} = Transmogrifier.handle_incoming(data)
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => "https://www.w3.org/ns/activitystreams#Public",
"type" => "Create",
"object" => %{
"content" => "blah blah blah",
"type" => "Note",
"attributedTo" => user.ap_id,
"inReplyTo" => nil
},
"actor" => user.ap_id
}
assert {:ok, activity} = Transmogrifier.handle_incoming(message) assert [
"http://mastodon.example.org/users/admin/followers",
"http://localtesting.pleroma.lol/users/lain"
] == activity.data["cc"]
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
end end
test "it correctly processes messages with non-array cc field" do test "it correctly processes messages with non-array cc field" do
user = insert(:user) data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("cc", "http://mastodon.example.org/users/admin/followers")
|> put_in(["object", "cc"], "http://mastodon.example.org/users/admin/followers")
message = %{ assert {:ok, activity} = Transmogrifier.handle_incoming(data)
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => user.follower_address,
"cc" => "https://www.w3.org/ns/activitystreams#Public",
"type" => "Create",
"object" => %{
"content" => "blah blah blah",
"type" => "Note",
"attributedTo" => user.ap_id,
"inReplyTo" => nil
},
"actor" => user.ap_id
}
assert {:ok, activity} = Transmogrifier.handle_incoming(message) assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
assert [user.follower_address] == activity.data["to"]
end end
test "it correctly processes messages with weirdness in address fields" do test "it correctly processes messages with weirdness in address fields" do
user = insert(:user) data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("cc", ["http://mastodon.example.org/users/admin/followers", ["¿"]])
|> put_in(["object", "cc"], ["http://mastodon.example.org/users/admin/followers", ["¿"]])
message = %{ assert {:ok, activity} = Transmogrifier.handle_incoming(data)
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => [nil, user.follower_address],
"cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
"type" => "Create",
"object" => %{
"content" => "",
"type" => "Note",
"attributedTo" => user.ap_id,
"inReplyTo" => nil
},
"actor" => user.ap_id
}
assert {:ok, activity} = Transmogrifier.handle_incoming(message) assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
assert [user.follower_address] == activity.data["to"]
end end
end end
@ -419,7 +406,11 @@ test "schedules background fetching of `replies` items if max thread depth limit
} do } do
clear_config([:instance, :federation_incoming_replies_max_depth], 10) clear_config([:instance, :federation_incoming_replies_max_depth], 10)
{:ok, _activity} = Transmogrifier.handle_incoming(data) {:ok, activity} = Transmogrifier.handle_incoming(data)
object = Object.normalize(activity.data["object"])
assert object.data["replies"] == items
for id <- items do for id <- items do
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
@ -442,45 +433,41 @@ test "does NOT schedule background fetching of `replies` beyond max thread depth
setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
setup do setup do
user = insert(:user) replies = %{
"type" => "Collection",
"items" => [
Pleroma.Web.ActivityPub.Utils.generate_object_id(),
Pleroma.Web.ActivityPub.Utils.generate_object_id()
]
}
{:ok, activity} = CommonAPI.post(user, %{status: "post1"}) activity =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Kernel.put_in(["object", "replies"], replies)
{:ok, reply1} = %{activity: activity}
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
{:ok, reply2} =
CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
{:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
Repo.delete(activity.object)
Repo.delete(activity)
%{federation_output: federation_output, replies_uris: replies_uris}
end end
test "schedules background fetching of `replies` items if max thread depth limit allows", %{ test "schedules background fetching of `replies` items if max thread depth limit allows", %{
federation_output: federation_output, activity: activity
replies_uris: replies_uris
} do } do
clear_config([:instance, :federation_incoming_replies_max_depth], 1) clear_config([:instance, :federation_incoming_replies_max_depth], 1)
{:ok, _activity} = Transmogrifier.handle_incoming(federation_output) assert {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(activity)
object = Object.normalize(data["object"])
for id <- replies_uris do for id <- object.data["replies"] do
job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
end end
end end
test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
%{federation_output: federation_output} do %{activity: activity} do
clear_config([:instance, :federation_incoming_replies_max_depth], 0) clear_config([:instance, :federation_incoming_replies_max_depth], 0)
{:ok, _activity} = Transmogrifier.handle_incoming(federation_output) {:ok, _activity} = Transmogrifier.handle_incoming(activity)
assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
end end
@ -498,6 +485,7 @@ test "successfully reserializes a message with inReplyTo == nil" do
"object" => %{ "object" => %{
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [], "cc" => [],
"id" => Utils.generate_object_id(),
"type" => "Note", "type" => "Note",
"content" => "Hi", "content" => "Hi",
"inReplyTo" => nil, "inReplyTo" => nil,
@ -522,6 +510,7 @@ test "successfully reserializes a message with AS2 objects in IR" do
"object" => %{ "object" => %{
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [], "cc" => [],
"id" => Utils.generate_object_id(),
"type" => "Note", "type" => "Note",
"content" => "Hi", "content" => "Hi",
"inReplyTo" => nil, "inReplyTo" => nil,

View File

@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
alias Pleroma.Tests.ObanHelpers alias Pleroma.Tests.ObanHelpers
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
@ -159,8 +160,7 @@ test "it adds the json-ld context and the conversation property" do
{:ok, activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert modified["@context"] == assert modified["@context"] == Utils.make_json_ld_header()["@context"]
Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
assert modified["object"]["conversation"] == modified["context"] assert modified["object"]["conversation"] == modified["context"]
end end

View File

@ -123,7 +123,8 @@ test "successfully processes incoming AP docs with correct origin" do
"type" => "Note", "type" => "Note",
"content" => "hi world!", "content" => "hi world!",
"id" => "http://mastodon.example.org/users/admin/objects/1", "id" => "http://mastodon.example.org/users/admin/objects/1",
"attributedTo" => "http://mastodon.example.org/users/admin" "attributedTo" => "http://mastodon.example.org/users/admin",
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
}, },
"to" => ["https://www.w3.org/ns/activitystreams#Public"] "to" => ["https://www.w3.org/ns/activitystreams#Public"]
} }
@ -145,7 +146,8 @@ test "rejects incoming AP docs with incorrect origin" do
"type" => "Note", "type" => "Note",
"content" => "hi world!", "content" => "hi world!",
"id" => "http://mastodon.example.org/users/admin/objects/1", "id" => "http://mastodon.example.org/users/admin/objects/1",
"attributedTo" => "http://mastodon.example.org/users/admin" "attributedTo" => "http://mastodon.example.org/users/admin",
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
}, },
"to" => ["https://www.w3.org/ns/activitystreams#Public"] "to" => ["https://www.w3.org/ns/activitystreams#Public"]
} }

View File

@ -7,6 +7,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
import Pleroma.Factory import Pleroma.Factory
@ -185,16 +186,16 @@ test "404 for private status", %{conn: conn, user: user} do
test "302 for remote cached status", %{conn: conn, user: user} do test "302 for remote cached status", %{conn: conn, user: user} do
message = %{ message = %{
"@context" => "https://www.w3.org/ns/activitystreams", "@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Create",
"actor" => user.ap_id,
"object" => %{
"to" => user.follower_address, "to" => user.follower_address,
"cc" => "https://www.w3.org/ns/activitystreams#Public", "cc" => "https://www.w3.org/ns/activitystreams#Public",
"type" => "Create", "id" => Utils.generate_object_id(),
"object" => %{
"content" => "blah blah blah", "content" => "blah blah blah",
"type" => "Note", "type" => "Note",
"attributedTo" => user.ap_id, "attributedTo" => user.ap_id
"inReplyTo" => nil }
},
"actor" => user.ap_id
} }
assert {:ok, activity} = Transmogrifier.handle_incoming(message) assert {:ok, activity} = Transmogrifier.handle_incoming(message)