replies filtering for blocked domains

This commit is contained in:
Alexander Strizhakov 2020-06-02 08:50:24 +03:00
parent 7e6ec778d9
commit 19f468c5bc
No known key found for this signature in database
GPG key ID: 022896A53AEF1381
5 changed files with 15 additions and 95 deletions

View file

@ -228,24 +228,16 @@ defp fetch_public_timeline(user, :only_media) do
fetch_public_timeline(opts, "public timeline only media") fetch_public_timeline(opts, "public timeline only media")
end end
# TODO: remove using `:method` after benchmarks
defp fetch_public_timeline(user, :with_blocks) do defp fetch_public_timeline(user, :with_blocks) do
opts = opts_for_public_timeline(user) opts = opts_for_public_timeline(user)
remote_non_friends = Agent.get(:non_friends_remote, & &1) remote_non_friends = Agent.get(:non_friends_remote, & &1)
Benchee.run( Benchee.run(%{
%{ "public timeline without blocks" => fn ->
"public timeline without blocks" => fn opts ->
ActivityPub.fetch_public_activities(opts) ActivityPub.fetch_public_activities(opts)
end end
}, })
inputs: %{
"old filtering" => Map.delete(opts, :method),
"with psql fun" => Map.put(opts, :method, :fun),
"with unnest" => Map.put(opts, :method, :unnest)
}
)
Enum.each(remote_non_friends, fn non_friend -> Enum.each(remote_non_friends, fn non_friend ->
{:ok, _} = User.block(user, non_friend) {:ok, _} = User.block(user, non_friend)
@ -257,15 +249,10 @@ defp fetch_public_timeline(user, :with_blocks) do
Benchee.run( Benchee.run(
%{ %{
"public timeline with user block" => fn opts -> "public timeline with user block" => fn ->
ActivityPub.fetch_public_activities(opts) ActivityPub.fetch_public_activities(opts)
end end
}, },
inputs: %{
"old filtering" => Map.delete(opts, :method),
"with psql fun" => Map.put(opts, :method, :fun),
"with unnest" => Map.put(opts, :method, :unnest)
}
) )
domains = domains =
@ -289,11 +276,6 @@ defp fetch_public_timeline(user, :with_blocks) do
"public timeline with domain block" => fn opts -> "public timeline with domain block" => fn opts ->
ActivityPub.fetch_public_activities(opts) ActivityPub.fetch_public_activities(opts)
end end
},
inputs: %{
"old filtering" => Map.delete(opts, :method),
"with psql fun" => Map.put(opts, :method, :fun),
"with unnest" => Map.put(opts, :method, :unnest)
} }
) )
end end

View file

@ -932,37 +932,16 @@ defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
query = query =
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query) if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
# TODO: update after benchmarks
query =
case opts[:method] do
:fun ->
from(a in query,
where:
fragment(
"recipients_contain_blocked_domains(?, ?) = false",
a.recipients,
^domain_blocks
)
)
:unnest ->
from(a in query,
where:
fragment(
"NOT ? && (SELECT ARRAY(SELECT split_part(UNNEST(?), '/', 3)))",
^domain_blocks,
a.recipients
)
)
_ ->
query
end
from( from(
[activity, object: o] in query, [activity, object: o] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids), where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids), where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
where:
fragment(
"recipients_contain_blocked_domains(?, ?) = false",
activity.recipients,
^domain_blocks
),
where: where:
fragment( fragment(
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",

View file

@ -62,13 +62,6 @@ def public_operation do
only_media_param(), only_media_param(),
with_muted_param(), with_muted_param(),
exclude_visibilities_param(), exclude_visibilities_param(),
# TODO: remove after benchmarks
Operation.parameter(
:method,
:query,
%Schema{type: :string},
"Temp parameter"
),
reply_visibility_param() | pagination_params() reply_visibility_param() | pagination_params()
], ],
operationId: "TimelineController.public", operationId: "TimelineController.public",

View file

@ -109,23 +109,14 @@ def public(%{assigns: %{user: user}} = conn, params) do
if restrict? and is_nil(user) do if restrict? and is_nil(user) do
render_error(conn, :unauthorized, "authorization required for timeline view") render_error(conn, :unauthorized, "authorization required for timeline view")
else else
# TODO: return back after benchmarks activities =
params =
params params
|> Map.put("type", ["Create", "Announce"]) |> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", local_only) |> Map.put("local_only", local_only)
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("muting_user", user) |> Map.put("muting_user", user)
|> Map.put("reply_filtering_user", user) |> Map.put("reply_filtering_user", user)
|> ActivityPub.fetch_public_activities()
params =
if params["method"] do
Map.put(params, :method, String.to_existing_atom(params["method"]))
else
params
end
activities = ActivityPub.fetch_public_activities(params)
conn conn
|> add_link_headers(activities, %{"local" => local_only}) |> add_link_headers(activities, %{"local" => local_only})

View file

@ -111,7 +111,6 @@ test "doesn't return replies if follower is posting with blocked user" do
[%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200) [%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200)
end end
# TODO: update after benchmarks
test "doesn't return replies if follow is posting with users from blocked domain" do test "doesn't return replies if follow is posting with users from blocked domain" do
%{conn: conn, user: blocker} = oauth_access(["read:statuses"]) %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
friend = insert(:user) friend = insert(:user)
@ -129,31 +128,7 @@ test "doesn't return replies if follow is posting with users from blocked domain
{:ok, _reply_from_friend} = {:ok, _reply_from_friend} =
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
res_conn = get(conn, "/api/v1/timelines/public?method=fun") res_conn = get(conn, "/api/v1/timelines/public")
activities = json_response_and_validate_schema(res_conn, 200)
[%{"id" => ^activity_id}] = activities
end
# TODO: update after benchmarks
test "doesn't return replies if follow is posting with users from blocked domain with unnest param" do
%{conn: conn, user: blocker} = oauth_access(["read:statuses"])
friend = insert(:user)
blockee = insert(:user, ap_id: "https://example.com/users/blocked")
{:ok, blocker} = User.follow(blocker, friend)
{:ok, blocker} = User.block_domain(blocker, "example.com")
conn = assign(conn, :user, blocker)
{:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
{:ok, reply_from_blockee} =
CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
{:ok, _reply_from_friend} =
CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
res_conn = get(conn, "/api/v1/timelines/public?method=unnest")
activities = json_response_and_validate_schema(res_conn, 200) activities = json_response_and_validate_schema(res_conn, 200)
[%{"id" => ^activity_id}] = activities [%{"id" => ^activity_id}] = activities