@@ -586,12 +586,23 @@ defmodule Pleroma.User do | |||
@spec get_followers_query(User.t()) :: Ecto.Query.t() | |||
def get_followers_query(user), do: get_followers_query(user, nil) | |||
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())} | |||
def get_followers(user, page \\ nil) do | |||
q = get_followers_query(user, page) | |||
{:ok, Repo.all(q)} | |||
end | |||
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())} | |||
def get_external_followers(user, page \\ nil) do | |||
q = | |||
user | |||
|> get_followers_query(page) | |||
|> User.Query.build(%{external: true}) | |||
{:ok, Repo.all(q)} | |||
end | |||
def get_followers_ids(user, page \\ nil) do | |||
q = get_followers_query(user, page) | |||
@@ -97,13 +97,13 @@ defmodule Pleroma.Web.ActivityPub.Publisher do | |||
end | |||
end | |||
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | [] | |||
defp recipients(actor, activity) do | |||
followers = | |||
{:ok, followers} = | |||
if actor.follower_address in activity.recipients do | |||
{:ok, followers} = User.get_followers(actor) | |||
Enum.filter(followers, &(!&1.local)) | |||
User.get_external_followers(actor) | |||
else | |||
[] | |||
{:ok, []} | |||
end | |||
Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers | |||
@@ -8,14 +8,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
@public "https://www.w3.org/ns/activitystreams#Public" | |||
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean() | |||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false | |||
def is_public?(%Object{data: data}), do: is_public?(data) | |||
def is_public?(%Activity{data: data}), do: is_public?(data) | |||
def is_public?(%{"directMessage" => true}), do: false | |||
def is_public?(data) do | |||
"https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || [])) | |||
end | |||
def is_public?(data), do: @public in (data["to"] ++ (data["cc"] || [])) | |||
def is_private?(activity) do | |||
with false <- is_public?(activity), | |||
@@ -69,15 +69,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do | |||
end | |||
def get_visibility(object) do | |||
public = "https://www.w3.org/ns/activitystreams#Public" | |||
to = object.data["to"] || [] | |||
cc = object.data["cc"] || [] | |||
cond do | |||
public in to -> | |||
@public in to -> | |||
"public" | |||
public in cc -> | |||
@public in cc -> | |||
"unlisted" | |||
# this should use the sql for the object's activity | |||
@@ -118,17 +118,20 @@ defmodule Pleroma.Factory do | |||
def note_activity_factory(attrs \\ %{}) do | |||
user = attrs[:user] || insert(:user) | |||
note = attrs[:note] || insert(:note, user: user) | |||
attrs = Map.drop(attrs, [:user, :note]) | |||
data = %{ | |||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), | |||
"type" => "Create", | |||
"actor" => note.data["actor"], | |||
"to" => note.data["to"], | |||
"object" => note.data["id"], | |||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(), | |||
"context" => note.data["context"] | |||
} | |||
data_attrs = attrs[:data_attrs] || %{} | |||
attrs = Map.drop(attrs, [:user, :note, :data_attrs]) | |||
data = | |||
%{ | |||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), | |||
"type" => "Create", | |||
"actor" => note.data["actor"], | |||
"to" => note.data["to"], | |||
"object" => note.data["id"], | |||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(), | |||
"context" => note.data["context"] | |||
} | |||
|> Map.merge(data_attrs) | |||
%Pleroma.Activity{ | |||
data: data, | |||
@@ -6,11 +6,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Activity | |||
alias Pleroma.Builders.ActivityBuilder | |||
alias Pleroma.Instances | |||
alias Pleroma.Object | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.ActivityPub.Publisher | |||
alias Pleroma.Web.ActivityPub.Utils | |||
alias Pleroma.Web.CommonAPI | |||
@@ -1083,113 +1081,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||
} = activity | |||
end | |||
describe "publish_one/1" do | |||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: NaiveDateTime.utc_now() | |||
}) | |||
assert called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: nil | |||
}) | |||
refute called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://404.site/users/nick1/inbox" | |||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://connrefused.site/users/nick1/inbox" | |||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
refute called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://connrefused.site/users/nick1/inbox" | |||
assert {:error, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: NaiveDateTime.utc_now() | |||
}) | |||
refute called(Instances.set_unreachable(inbox)) | |||
end | |||
end | |||
test "fetch_activities/2 returns activities addressed to a list " do | |||
user = insert(:user) | |||
member = insert(:user) | |||
@@ -6,12 +6,20 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do | |||
use Pleroma.DataCase | |||
import Pleroma.Factory | |||
import Tesla.Mock | |||
import Mock | |||
alias Pleroma.Activity | |||
alias Pleroma.Instances | |||
alias Pleroma.Web.ActivityPub.Publisher | |||
@as_public "https://www.w3.org/ns/activitystreams#Public" | |||
setup do | |||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end) | |||
:ok | |||
end | |||
describe "determine_inbox/2" do | |||
test "it returns sharedInbox for messages involving as:Public in to" do | |||
user = | |||
@@ -109,4 +117,150 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do | |||
assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox" | |||
end | |||
end | |||
describe "publish_one/1" do | |||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: NaiveDateTime.utc_now() | |||
}) | |||
assert called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: nil | |||
}) | |||
refute called(Instances.set_reachable(inbox)) | |||
end | |||
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://404.site/users/nick1/inbox" | |||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://connrefused.site/users/nick1/inbox" | |||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
assert called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://200.site/users/nick1/inbox" | |||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) | |||
refute called(Instances.set_unreachable(inbox)) | |||
end | |||
test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`", | |||
Instances, | |||
[:passthrough], | |||
[] do | |||
actor = insert(:user) | |||
inbox = "http://connrefused.site/users/nick1/inbox" | |||
assert {:error, _} = | |||
Publisher.publish_one(%{ | |||
inbox: inbox, | |||
json: "{}", | |||
actor: actor, | |||
id: 1, | |||
unreachable_since: NaiveDateTime.utc_now() | |||
}) | |||
refute called(Instances.set_unreachable(inbox)) | |||
end | |||
end | |||
describe "publish/2" do | |||
test_with_mock "publishes an activity with BCC to all relevant peers.", | |||
Pleroma.Web.Federator.Publisher, | |||
[:passthrough], | |||
[] do | |||
follower = | |||
insert(:user, | |||
local: false, | |||
info: %{ | |||
ap_enabled: true, | |||
source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"} | |||
} | |||
) | |||
actor = insert(:user, follower_address: follower.ap_id) | |||
user = insert(:user) | |||
{:ok, _follower_one} = Pleroma.User.follow(follower, actor) | |||
actor = refresh_record(actor) | |||
note_activity = | |||
insert(:note_activity, | |||
recipients: [follower.ap_id], | |||
data_attrs: %{"bcc" => [user.ap_id]} | |||
) | |||
res = Publisher.publish(actor, note_activity) | |||
assert res == :ok | |||
assert called( | |||
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ | |||
inbox: "https://domain.com/users/nick1/inbox", | |||
actor: actor, | |||
id: note_activity.data["id"] | |||
}) | |||
) | |||
end | |||
end | |||
end |
@@ -22,6 +22,15 @@ defmodule Pleroma.Web.FederatorTest do | |||
:ok | |||
end | |||
describe "Publisher.perform" do | |||
test "call `perform` with unknown task" do | |||
assert { | |||
:error, | |||
"Don't know what to do with this" | |||
} = Pleroma.Web.Federator.Publisher.perform("test", :ok, :ok) | |||
end | |||
end | |||
describe "Publish an activity" do | |||
setup do | |||
user = insert(:user) | |||