@@ -10,6 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
### Removed | |||
- **Breaking:** removed `with_move` parameter from notifications timeline. | |||
### Added | |||
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list. | |||
<details> | |||
<summary>API Changes</summary> | |||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`. | |||
</details> | |||
## [2.0.0] - 2019-03-08 | |||
### Security | |||
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request. | |||
@@ -117,6 +117,7 @@ The `type` value is `pleroma:emoji_reaction`. Has these fields: | |||
Accepts additional parameters: | |||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`. | |||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`. | |||
## POST `/api/v1/statuses` | |||
@@ -55,6 +55,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
user | |||
|> Notification.for_user_query(options) | |||
|> restrict(:include_types, options) | |||
|> restrict(:exclude_types, options) | |||
|> restrict(:account_ap_id, options) | |||
|> Pagination.fetch_paginated(params) | |||
@@ -69,6 +70,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
defp cast_params(params) do | |||
param_types = %{ | |||
exclude_types: {:array, :string}, | |||
include_types: {:array, :string}, | |||
exclude_visibilities: {:array, :string}, | |||
reblogs: :boolean, | |||
with_muted: :boolean, | |||
@@ -79,14 +81,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
changeset.changes | |||
end | |||
defp restrict(query, :include_types, %{include_types: mastodon_types = [_ | _]}) do | |||
ap_types = convert_and_filter_mastodon_types(mastodon_types) | |||
where(query, [q, a], fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) | |||
end | |||
defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) do | |||
ap_types = | |||
mastodon_types | |||
|> Enum.map(&Activity.from_mastodon_notification_type/1) | |||
|> Enum.filter(& &1) | |||
ap_types = convert_and_filter_mastodon_types(mastodon_types) | |||
query | |||
|> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) | |||
where(query, [q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) | |||
end | |||
defp restrict(query, :account_ap_id, %{account_ap_id: account_ap_id}) do | |||
@@ -94,4 +98,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
end | |||
defp restrict(query, _, _), do: query | |||
defp convert_and_filter_mastodon_types(types) do | |||
types | |||
|> Enum.map(&Activity.from_mastodon_notification_type/1) | |||
|> Enum.filter(& &1) | |||
end | |||
end |
@@ -60,6 +60,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do | |||
"pleroma_explicit_addressing", | |||
"shareable_emoji_packs", | |||
"multifetch", | |||
"pleroma:api/v1/notifications:include_types_filter", | |||
if Config.get([:media_proxy, :enabled]) do | |||
"media_proxy" | |||
end, | |||
@@ -304,6 +304,51 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do | |||
assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) | |||
end | |||
test "filters notifications using include_types" do | |||
%{user: user, conn: conn} = oauth_access(["read:notifications"]) | |||
other_user = insert(:user) | |||
{:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) | |||
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) | |||
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) | |||
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) | |||
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) | |||
mention_notification_id = get_notification_id_by_activity(mention_activity) | |||
favorite_notification_id = get_notification_id_by_activity(favorite_activity) | |||
reblog_notification_id = get_notification_id_by_activity(reblog_activity) | |||
follow_notification_id = get_notification_id_by_activity(follow_activity) | |||
conn_res = get(conn, "/api/v1/notifications", %{include_types: ["follow"]}) | |||
assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200) | |||
conn_res = get(conn, "/api/v1/notifications", %{include_types: ["mention"]}) | |||
assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200) | |||
conn_res = get(conn, "/api/v1/notifications", %{include_types: ["favourite"]}) | |||
assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200) | |||
conn_res = get(conn, "/api/v1/notifications", %{include_types: ["reblog"]}) | |||
assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) | |||
result = conn |> get("/api/v1/notifications") |> json_response(200) | |||
assert length(result) == 4 | |||
result = | |||
conn | |||
|> get("/api/v1/notifications", %{ | |||
include_types: ["follow", "mention", "favourite", "reblog"] | |||
}) | |||
|> json_response(200) | |||
assert length(result) == 4 | |||
end | |||
test "destroy multiple" do | |||
%{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"]) | |||
other_user = insert(:user) | |||