forked from AkkomaGang/akkoma
Merge remote-tracking branch 'remotes/upstream/develop' into 1304-user-info-deprecation
# Conflicts: # lib/pleroma/notification.ex
This commit is contained in:
commit
8cc809e44e
18 changed files with 552 additions and 37 deletions
|
@ -51,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
|
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
|
||||||
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
||||||
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
||||||
|
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
||||||
|
|
74
lib/pleroma/marker.ex
Normal file
74
lib/pleroma/marker.ex
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Marker do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Ecto.Multi
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@timelines ["notifications"]
|
||||||
|
|
||||||
|
schema "markers" do
|
||||||
|
field(:last_read_id, :string, default: "")
|
||||||
|
field(:timeline, :string, default: "")
|
||||||
|
field(:lock_version, :integer, default: 0)
|
||||||
|
|
||||||
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_markers(user, timelines \\ []) do
|
||||||
|
Repo.all(get_query(user, timelines))
|
||||||
|
end
|
||||||
|
|
||||||
|
def upsert(%User{} = user, attrs) do
|
||||||
|
attrs
|
||||||
|
|> Map.take(@timelines)
|
||||||
|
|> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
|
||||||
|
marker =
|
||||||
|
user
|
||||||
|
|> get_marker(timeline)
|
||||||
|
|> changeset(timeline_attrs)
|
||||||
|
|
||||||
|
Multi.insert(multi, timeline, marker,
|
||||||
|
returning: true,
|
||||||
|
on_conflict: {:replace, [:last_read_id]},
|
||||||
|
conflict_target: [:user_id, :timeline]
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|> Repo.transaction()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_marker(user, timeline) do
|
||||||
|
case Repo.find_resource(get_query(user, timeline)) do
|
||||||
|
{:ok, marker} -> %__MODULE__{marker | user: user}
|
||||||
|
_ -> %__MODULE__{timeline: timeline, user_id: user.id}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
defp changeset(marker, attrs) do
|
||||||
|
marker
|
||||||
|
|> cast(attrs, [:last_read_id])
|
||||||
|
|> validate_required([:user_id, :timeline, :last_read_id])
|
||||||
|
|> validate_inclusion(:timeline, @timelines)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp by_timeline(query, timeline) do
|
||||||
|
from(m in query, where: m.timeline in ^List.wrap(timeline))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)
|
||||||
|
|
||||||
|
defp get_query(user, timelines) do
|
||||||
|
__MODULE__
|
||||||
|
|> by_user_id(user.id)
|
||||||
|
|> by_timeline(timelines)
|
||||||
|
end
|
||||||
|
end
|
|
@ -55,9 +55,19 @@ def for_user_query(user, opts \\ []) do
|
||||||
)
|
)
|
||||||
|> preload([n, a, o], activity: {a, object: o})
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
|> exclude_muted(user, opts)
|
|> exclude_muted(user, opts)
|
||||||
|
|> exclude_blocked(user)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp exclude_blocked(query, user) do
|
||||||
|
query
|
||||||
|
|> where([n, a], a.actor not in ^user.info.blocks)
|
||||||
|
|> where(
|
||||||
|
[n, a],
|
||||||
|
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp exclude_muted(query, _, %{with_muted: true}) do
|
defp exclude_muted(query, _, %{with_muted: true}) do
|
||||||
query
|
query
|
||||||
end
|
end
|
||||||
|
@ -65,11 +75,6 @@ defp exclude_muted(query, _, %{with_muted: true}) do
|
||||||
defp exclude_muted(query, user, _opts) do
|
defp exclude_muted(query, user, _opts) do
|
||||||
query
|
query
|
||||||
|> where([n, a], a.actor not in ^user.muted_notifications)
|
|> where([n, a], a.actor not in ^user.muted_notifications)
|
||||||
|> where([n, a], a.actor not in ^user.blocks)
|
|
||||||
|> where(
|
|
||||||
[n, a],
|
|
||||||
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks
|
|
||||||
)
|
|
||||||
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
||||||
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
||||||
)
|
)
|
||||||
|
|
|
@ -49,26 +49,28 @@ def determine_explicit_mentions(%{"tag" => tag} = object) when is_map(tag) do
|
||||||
|
|
||||||
def determine_explicit_mentions(_), do: []
|
def determine_explicit_mentions(_), do: []
|
||||||
|
|
||||||
@spec recipient_in_collection(any(), any()) :: boolean()
|
@spec label_in_collection?(any(), any()) :: boolean()
|
||||||
defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
|
defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll
|
||||||
defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
|
defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll
|
||||||
defp recipient_in_collection(_, _), do: false
|
defp label_in_collection?(_, _), do: false
|
||||||
|
|
||||||
|
@spec label_in_message?(String.t(), map()) :: boolean()
|
||||||
|
def label_in_message?(label, params),
|
||||||
|
do:
|
||||||
|
[params["to"], params["cc"], params["bto"], params["bcc"]]
|
||||||
|
|> Enum.any?(&label_in_collection?(label, &1))
|
||||||
|
|
||||||
|
@spec unaddressed_message?(map()) :: boolean()
|
||||||
|
def unaddressed_message?(params),
|
||||||
|
do:
|
||||||
|
[params["to"], params["cc"], params["bto"], params["bcc"]]
|
||||||
|
|> Enum.all?(&is_nil(&1))
|
||||||
|
|
||||||
@spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
|
@spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
|
||||||
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
|
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
|
||||||
addresses = [params["to"], params["cc"], params["bto"], params["bcc"]]
|
do:
|
||||||
|
label_in_message?(ap_id, params) || unaddressed_message?(params) ||
|
||||||
cond do
|
User.following?(recipient, actor)
|
||||||
Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true
|
|
||||||
# if the message is unaddressed at all, then assume it is directly addressed
|
|
||||||
# to the recipient
|
|
||||||
Enum.all?(addresses, &is_nil(&1)) -> true
|
|
||||||
# if the message is sent from somebody the user is following, then assume it
|
|
||||||
# is addressed to the recipient
|
|
||||||
User.following?(recipient, actor) -> true
|
|
||||||
true -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extract_list(target) when is_binary(target), do: [target]
|
defp extract_list(target) when is_binary(target), do: [target]
|
||||||
defp extract_list(lst) when is_list(lst), do: lst
|
defp extract_list(lst) when is_list(lst), do: lst
|
||||||
|
@ -76,8 +78,8 @@ defp extract_list(_), do: []
|
||||||
|
|
||||||
def maybe_splice_recipient(ap_id, params) do
|
def maybe_splice_recipient(ap_id, params) do
|
||||||
need_splice? =
|
need_splice? =
|
||||||
!recipient_in_collection(ap_id, params["to"]) &&
|
!label_in_collection?(ap_id, params["to"]) &&
|
||||||
!recipient_in_collection(ap_id, params["cc"])
|
!label_in_collection?(ap_id, params["cc"])
|
||||||
|
|
||||||
if need_splice? do
|
if need_splice? do
|
||||||
cc_list = extract_list(params["cc"])
|
cc_list = extract_list(params["cc"])
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
def is_public?(%Object{data: data}), do: is_public?(data)
|
||||||
def is_public?(%Activity{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?(%{"directMessage" => true}), do: false
|
||||||
def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))
|
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||||
|
|
||||||
def is_private?(activity) do
|
def is_private?(activity) do
|
||||||
with false <- is_public?(activity),
|
with false <- is_public?(activity),
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MarkerController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:statuses"]}
|
||||||
|
when action == :index
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
|
||||||
|
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
# GET /api/v1/markers
|
||||||
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
markers = Pleroma.Marker.get_markers(user, params["timeline"])
|
||||||
|
render(conn, "markers.json", %{markers: markers})
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /api/v1/markers
|
||||||
|
def upsert(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
with {:ok, result} <- Pleroma.Marker.upsert(user, params),
|
||||||
|
markers <- Map.values(result) do
|
||||||
|
render(conn, "markers.json", %{markers: markers})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
lib/pleroma/web/mastodon_api/views/marker_view.ex
Normal file
17
lib/pleroma/web/mastodon_api/views/marker_view.ex
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MarkerView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("markers.json", %{markers: markers}) do
|
||||||
|
Enum.reduce(markers, %{}, fn m, acc ->
|
||||||
|
Map.put_new(acc, m.timeline, %{
|
||||||
|
last_read_id: m.last_read_id,
|
||||||
|
version: m.lock_version,
|
||||||
|
updated_at: NaiveDateTime.to_iso8601(m.updated_at)
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -125,6 +125,10 @@ def format_body(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def format_title(%{activity: %{data: %{"directMessage" => true}}}) do
|
||||||
|
"New Direct Message"
|
||||||
|
end
|
||||||
|
|
||||||
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
def format_title(%{activity: %{data: %{"type" => type}}}) do
|
||||||
case type do
|
case type do
|
||||||
"Create" -> "New Mention"
|
"Create" -> "New Mention"
|
||||||
|
|
|
@ -405,6 +405,9 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/push/subscription", SubscriptionController, :get)
|
get("/push/subscription", SubscriptionController, :get)
|
||||||
put("/push/subscription", SubscriptionController, :update)
|
put("/push/subscription", SubscriptionController, :update)
|
||||||
delete("/push/subscription", SubscriptionController, :delete)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
|
|
||||||
|
get("/markers", MarkerController, :index)
|
||||||
|
post("/markers", MarkerController, :upsert)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/web", Pleroma.Web do
|
scope "/api/web", Pleroma.Web do
|
||||||
|
|
15
priv/repo/migrations/20191014181019_create_markers.exs
Normal file
15
priv/repo/migrations/20191014181019_create_markers.exs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateMarkers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create_if_not_exists table(:markers) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:timeline, :string, default: "", null: false)
|
||||||
|
add(:last_read_id, :string, default: "", null: false)
|
||||||
|
add(:lock_version, :integer, default: 0, null: false)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(unique_index(:markers, [:user_id, :timeline]))
|
||||||
|
end
|
||||||
|
end
|
51
test/marker_test.exs
Normal file
51
test/marker_test.exs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.MarkerTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Marker
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "get_markers/2" do
|
||||||
|
test "returns user markers" do
|
||||||
|
user = insert(:user)
|
||||||
|
marker = insert(:marker, user: user)
|
||||||
|
insert(:marker, timeline: "home", user: user)
|
||||||
|
assert Marker.get_markers(user, ["notifications"]) == [refresh_record(marker)]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "upsert/2" do
|
||||||
|
test "creates a marker" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, %{"notifications" => %Marker{} = marker}} =
|
||||||
|
Marker.upsert(
|
||||||
|
user,
|
||||||
|
%{"notifications" => %{"last_read_id" => "34"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert marker.timeline == "notifications"
|
||||||
|
assert marker.last_read_id == "34"
|
||||||
|
assert marker.lock_version == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates exist marker" do
|
||||||
|
user = insert(:user)
|
||||||
|
marker = insert(:marker, user: user, last_read_id: "8909")
|
||||||
|
|
||||||
|
{:ok, %{"notifications" => %Marker{}}} =
|
||||||
|
Marker.upsert(
|
||||||
|
user,
|
||||||
|
%{"notifications" => %{"last_read_id" => "9909"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
marker = refresh_record(marker)
|
||||||
|
assert marker.timeline == "notifications"
|
||||||
|
assert marker.last_read_id == "9909"
|
||||||
|
assert marker.lock_version == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -683,7 +683,7 @@ test "it doesn't return notifications for muted thread" do
|
||||||
assert Notification.for_user(user) == []
|
assert Notification.for_user(user) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns notifications for muted user with notifications and with_muted parameter" do
|
test "it returns notifications from a muted user when with_muted is set" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
muted = insert(:user)
|
muted = insert(:user)
|
||||||
{:ok, user} = User.mute(user, muted)
|
{:ok, user} = User.mute(user, muted)
|
||||||
|
@ -693,27 +693,27 @@ test "it returns notifications for muted user with notifications and with_muted
|
||||||
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns notifications for blocked user and with_muted parameter" do
|
test "it doesn't return notifications from a blocked user when with_muted is set" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
blocked = insert(:user)
|
blocked = insert(:user)
|
||||||
{:ok, user} = User.block(user, blocked)
|
{:ok, user} = User.block(user, blocked)
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
assert length(Notification.for_user(user, %{with_muted: true})) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns notificatitons for blocked domain and with_muted parameter" do
|
test "it doesn't return notifications from a domain-blocked user when with_muted is set" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
blocked = insert(:user, ap_id: "http://some-domain.com")
|
blocked = insert(:user, ap_id: "http://some-domain.com")
|
||||||
{:ok, user} = User.block_domain(user, "some-domain.com")
|
{:ok, user} = User.block_domain(user, "some-domain.com")
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||||
|
|
||||||
assert length(Notification.for_user(user, %{with_muted: true})) == 1
|
assert length(Notification.for_user(user, %{with_muted: true})) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns notifications for muted thread with_muted parameter" do
|
test "it returns notifications from muted threads when with_muted is set" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
another_user = insert(:user)
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -377,4 +377,13 @@ def config_factory do
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def marker_factory do
|
||||||
|
%Pleroma.Marker{
|
||||||
|
user: build(:user),
|
||||||
|
timeline: "notifications",
|
||||||
|
lock_version: 0,
|
||||||
|
last_read_id: "1"
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -354,6 +354,87 @@ test "it inserts an incoming activity into the database", %{conn: conn, data: da
|
||||||
assert Activity.get_by_ap_id(data["id"])
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data =
|
||||||
|
Map.put(data, "to", user.ap_id)
|
||||||
|
|> Map.delete("cc")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, true)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data =
|
||||||
|
Map.put(data, "cc", user.ap_id)
|
||||||
|
|> Map.delete("to")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, true)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
%Activity{} = activity = Activity.get_by_ap_id(data["id"])
|
||||||
|
assert user.ap_id in activity.recipients
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data =
|
||||||
|
Map.put(data, "bcc", user.ap_id)
|
||||||
|
|> Map.delete("to")
|
||||||
|
|> Map.delete("cc")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, true)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
assert Activity.get_by_ap_id(data["id"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts announces with to as string instead of array", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor" => "http://mastodon.example.org/users/admin",
|
||||||
|
"id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
|
||||||
|
"object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
|
||||||
|
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"cc" => [user.ap_id],
|
||||||
|
"type" => "Announce"
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:valid_signature, true)
|
||||||
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|
|> post("/users/#{user.nickname}/inbox", data)
|
||||||
|
|
||||||
|
assert "ok" == json_response(conn, 200)
|
||||||
|
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||||
|
%Activity{} = activity = Activity.get_by_ap_id(data["id"])
|
||||||
|
assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
|
||||||
|
end
|
||||||
|
|
||||||
test "it accepts messages from actors that are followed by the user", %{
|
test "it accepts messages from actors that are followed by the user", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
data: data
|
data: data
|
||||||
|
|
|
@ -1106,6 +1106,50 @@ test "it accepts Flag activities" do
|
||||||
assert activity.data["actor"] == other_user.ap_id
|
assert activity.data["actor"] == other_user.ap_id
|
||||||
assert activity.data["cc"] == [user.ap_id]
|
assert activity.data["cc"] == [user.ap_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it correctly processes messages with non-array to field" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@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 ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it correctly processes messages with non-array cc field" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@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 ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
||||||
|
assert [user.follower_address] == activity.data["to"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare outgoing" do
|
describe "prepare outgoing" do
|
||||||
|
|
124
test/web/mastodon_api/controllers/marker_controller_test.exs
Normal file
124
test/web/mastodon_api/controllers/marker_controller_test.exs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "GET /api/v1/markers" do
|
||||||
|
test "gets markers with correct scopes", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
|
||||||
|
|
||||||
|
{:ok, %{"notifications" => marker}} =
|
||||||
|
Pleroma.Marker.upsert(
|
||||||
|
user,
|
||||||
|
%{"notifications" => %{"last_read_id" => "69420"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"notifications" => %{
|
||||||
|
"last_read_id" => "69420",
|
||||||
|
"updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
|
||||||
|
"version" => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gets markers with missed scopes", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: [])
|
||||||
|
|
||||||
|
Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
assert response == %{"error" => "Insufficient permissions: read:statuses."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/markers" do
|
||||||
|
test "creates a marker with correct scopes", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> post("/api/v1/markers", %{
|
||||||
|
home: %{last_read_id: "777"},
|
||||||
|
notifications: %{"last_read_id" => "69420"}
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"notifications" => %{
|
||||||
|
"last_read_id" => "69420",
|
||||||
|
"updated_at" => _,
|
||||||
|
"version" => 0
|
||||||
|
}
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates exist marker", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
|
||||||
|
|
||||||
|
{:ok, %{"notifications" => marker}} =
|
||||||
|
Pleroma.Marker.upsert(
|
||||||
|
user,
|
||||||
|
%{"notifications" => %{"last_read_id" => "69477"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> post("/api/v1/markers", %{
|
||||||
|
home: %{last_read_id: "777"},
|
||||||
|
notifications: %{"last_read_id" => "69888"}
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"notifications" => %{
|
||||||
|
"last_read_id" => "69888",
|
||||||
|
"updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
|
||||||
|
"version" => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates a marker with missed scopes", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
token = insert(:oauth_token, user: user, scopes: [])
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> post("/api/v1/markers", %{
|
||||||
|
home: %{last_read_id: "777"},
|
||||||
|
notifications: %{"last_read_id" => "69420"}
|
||||||
|
})
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
assert response == %{"error" => "Insufficient permissions: write:statuses."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
test/web/mastodon_api/views/marker_view_test.exs
Normal file
27
test/web/mastodon_api/views/marker_view_test.exs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Web.MastodonAPI.MarkerView
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "returns markers" do
|
||||||
|
marker1 = insert(:marker, timeline: "notifications", last_read_id: "17")
|
||||||
|
marker2 = insert(:marker, timeline: "home", last_read_id: "42")
|
||||||
|
|
||||||
|
assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{
|
||||||
|
"home" => %{
|
||||||
|
last_read_id: "42",
|
||||||
|
updated_at: NaiveDateTime.to_iso8601(marker2.updated_at),
|
||||||
|
version: 0
|
||||||
|
},
|
||||||
|
"notifications" => %{
|
||||||
|
last_read_id: "17",
|
||||||
|
updated_at: NaiveDateTime.to_iso8601(marker1.updated_at),
|
||||||
|
version: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -84,7 +84,7 @@ test "fail message sending" do
|
||||||
) == :error
|
) == :error
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete subsciption if restult send message between 400..500" do
|
test "delete subscription if result send message between 400..500" do
|
||||||
subscription = insert(:push_subscription)
|
subscription = insert(:push_subscription)
|
||||||
|
|
||||||
assert Impl.push_message(
|
assert Impl.push_message(
|
||||||
|
@ -97,7 +97,7 @@ test "delete subsciption if restult send message between 400..500" do
|
||||||
refute Pleroma.Repo.get(Subscription, subscription.id)
|
refute Pleroma.Repo.get(Subscription, subscription.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders body for create activity" do
|
test "renders title and body for create activity" do
|
||||||
user = insert(:user, nickname: "Bob")
|
user = insert(:user, nickname: "Bob")
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
@ -116,18 +116,24 @@ test "renders body for create activity" do
|
||||||
object
|
object
|
||||||
) ==
|
) ==
|
||||||
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
||||||
|
|
||||||
|
assert Impl.format_title(%{activity: activity}) ==
|
||||||
|
"New Mention"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders body for follow activity" do
|
test "renders title and body for follow activity" do
|
||||||
user = insert(:user, nickname: "Bob")
|
user = insert(:user, nickname: "Bob")
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, _, _, activity} = CommonAPI.follow(user, other_user)
|
{:ok, _, _, activity} = CommonAPI.follow(user, other_user)
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you"
|
assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you"
|
||||||
|
|
||||||
|
assert Impl.format_title(%{activity: activity}) ==
|
||||||
|
"New Follower"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders body for announce activity" do
|
test "renders title and body for announce activity" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
@ -141,9 +147,12 @@ test "renders body for announce activity" do
|
||||||
|
|
||||||
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
|
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
|
||||||
"@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
"@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
|
||||||
|
|
||||||
|
assert Impl.format_title(%{activity: announce_activity}) ==
|
||||||
|
"New Repeat"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders body for like activity" do
|
test "renders title and body for like activity" do
|
||||||
user = insert(:user, nickname: "Bob")
|
user = insert(:user, nickname: "Bob")
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
@ -156,5 +165,21 @@ test "renders body for like activity" do
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"
|
assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"
|
||||||
|
|
||||||
|
assert Impl.format_title(%{activity: activity}) ==
|
||||||
|
"New Favorite"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renders title for create activity with direct visibility" do
|
||||||
|
user = insert(:user, nickname: "Bob")
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"visibility" => "direct",
|
||||||
|
"status" => "This is just between you and me, pal"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Impl.format_title(%{activity: activity}) ==
|
||||||
|
"New Direct Message"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue