parent
4948117fcf
commit
d6b0fce6e9
@ -0,0 +1,60 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.Synchronization do
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.User
|
||||
|
||||
@spec call([User.t()], map(), keyword()) :: {User.t(), map()}
|
||||
def call(users, errors, opts \\ []) do
|
||||
do_call(users, errors, opts)
|
||||
end
|
||||
|
||||
defp do_call([user | []], errors, opts) do
|
||||
updated = fetch_counters(user, errors, opts)
|
||||
{user, updated}
|
||||
end
|
||||
|
||||
defp do_call([user | others], errors, opts) do
|
||||
updated = fetch_counters(user, errors, opts)
|
||||
do_call(others, updated, opts)
|
||||
end
|
||||
|
||||
defp fetch_counters(user, errors, opts) do
|
||||
%{host: host} = URI.parse(user.ap_id)
|
||||
|
||||
info = %{}
|
||||
{following, errors} = fetch_counter(user.ap_id <> "/following", host, errors, opts)
|
||||
info = if following, do: Map.put(info, :following_count, following), else: info
|
||||
|
||||
{followers, errors} = fetch_counter(user.ap_id <> "/followers", host, errors, opts)
|
||||
info = if followers, do: Map.put(info, :follower_count, followers), else: info
|
||||
|
||||
User.set_info_cache(user, info)
|
||||
errors
|
||||
end
|
||||
|
||||
defp available_domain?(domain, errors, opts) do
|
||||
max_retries = Keyword.get(opts, :max_retries, 3)
|
||||
not (Map.has_key?(errors, domain) && errors[domain] >= max_retries)
|
||||
end
|
||||
|
||||
defp fetch_counter(url, host, errors, opts) do
|
||||
with true <- available_domain?(host, errors, opts),
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
HTTP.get(
|
||||
url,
|
||||
[{:Accept, "application/activity+json"}]
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
{data["totalItems"], errors}
|
||||
else
|
||||
false ->
|
||||
{nil, errors}
|
||||
|
||||
_ ->
|
||||
{nil, Map.update(errors, host, 1, &(&1 + 1))}
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,32 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-onl
|
||||
|
||||
defmodule Pleroma.User.SynchronizationWorker do
|
||||
use GenServer
|
||||
|
||||
def start_link do
|
||||
config = Pleroma.Config.get([:instance, :external_user_synchronization])
|
||||
|
||||
if config[:enabled] do
|
||||
GenServer.start_link(__MODULE__, interval: config[:interval])
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def init(opts) do
|
||||
schedule_next(opts)
|
||||
{:ok, opts}
|
||||
end
|
||||
|
||||
def handle_info(:sync_follow_counters, opts) do
|
||||
Pleroma.User.sync_follow_counter()
|
||||
schedule_next(opts)
|
||||
{:noreply, opts}
|
||||
end
|
||||
|
||||
defp schedule_next(opts) do
|
||||
Process.send_after(self(), :sync_follow_counters, opts[:interval])
|
||||
end
|
||||
end
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://localhost:4001/users/masto_closed/followers",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 437,
|
||||
"first": "http://localhost:4001/users/masto_closed/followers?page=1"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://localhost:4001/users/masto_closed/following",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 152,
|
||||
"first": "http://localhost:4001/users/masto_closed/following?page=1"
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 527,
|
||||
"id": "http://localhost:4001/users/fuser2/followers",
|
||||
"first": {
|
||||
"type": "OrderedCollectionPage",
|
||||
"totalItems": 527,
|
||||
"partOf": "http://localhost:4001/users/fuser2/followers",
|
||||
"orderedItems": [],
|
||||
"next": "http://localhost:4001/users/fuser2/followers?page=2",
|
||||
"id": "http://localhost:4001/users/fuser2/followers?page=1"
|
||||
},
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
{
|
||||
"@language": "und"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 267,
|
||||
"id": "http://localhost:4001/users/fuser2/following",
|
||||
"first": {
|
||||
"type": "OrderedCollectionPage",
|
||||
"totalItems": 267,
|
||||
"partOf": "http://localhost:4001/users/fuser2/following",
|
||||
"orderedItems": [],
|
||||
"next": "http://localhost:4001/users/fuser2/following?page=2",
|
||||
"id": "http://localhost:4001/users/fuser2/following?page=1"
|
||||
},
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
{
|
||||
"@language": "und"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.SynchronizationTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.User
|
||||
alias Pleroma.User.Synchronization
|
||||
|
||||
setup do
|
||||
Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "update following/followers counters" do
|
||||
user1 =
|
||||
insert(:user,
|
||||
local: false,
|
||||
ap_id: "http://localhost:4001/users/masto_closed"
|
||||
)
|
||||
|
||||
user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
|
||||
|
||||
users = User.external_users()
|
||||
assert length(users) == 2
|
||||
{user, %{}} = Synchronization.call(users, %{})
|
||||
assert user == List.last(users)
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
|
||||
assert followers == 437
|
||||
assert following == 152
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
|
||||
|
||||
assert followers == 527
|
||||
assert following == 267
|
||||
end
|
||||
|
||||
test "don't check host if errors exist" do
|
||||
user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
|
||||
|
||||
user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
|
||||
|
||||
users = User.external_users()
|
||||
assert length(users) == 2
|
||||
|
||||
{user, %{"domain-with-errors" => 2}} =
|
||||
Synchronization.call(users, %{"domain-with-errors" => 2}, max_retries: 2)
|
||||
|
||||
assert user == List.last(users)
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
|
||||
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
end
|
||||
|
||||
test "don't check host if errors appeared" do
|
||||
user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
|
||||
|
||||
user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
|
||||
|
||||
users = User.external_users()
|
||||
assert length(users) == 2
|
||||
|
||||
{user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
|
||||
|
||||
assert user == List.last(users)
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
|
||||
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
end
|
||||
|
||||
test "other users after error appeared" do
|
||||
user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
|
||||
user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
|
||||
|
||||
users = User.external_users()
|
||||
assert length(users) == 2
|
||||
|
||||
{user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
|
||||
assert user == List.last(users)
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
|
||||
assert followers == 0
|
||||
assert following == 0
|
||||
|
||||
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
|
||||
|
||||
assert followers == 527
|
||||
assert following == 267
|
||||
end
|
||||
end
|
@ -0,0 +1,49 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.SynchronizationWorkerTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
|
||||
setup do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
||||
config = Pleroma.Config.get([:instance, :external_user_synchronization])
|
||||
|
||||
for_update = [enabled: true, interval: 1000]
|
||||
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], for_update)
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], config)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "sync follow counters" do
|
||||
user1 =
|
||||
insert(:user,
|
||||
local: false,
|
||||
ap_id: "http://localhost:4001/users/masto_closed"
|
||||
)
|
||||
|
||||
user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
|
||||
|
||||
{:ok, _} = Pleroma.User.SynchronizationWorker.start_link()
|
||||
:timer.sleep(1500)
|
||||
|
||||
%{follower_count: followers, following_count: following} =
|
||||
Pleroma.User.get_cached_user_info(user1)
|
||||
|
||||
assert followers == 437
|
||||
assert following == 152
|
||||
|
||||
%{follower_count: followers, following_count: following} =
|
||||
Pleroma.User.get_cached_user_info(user2)
|
||||
|
||||
assert followers == 527
|
||||
assert following == 267
|
||||
end
|
||||
end
|
Loading…
Reference in new issue