Merge branch 'issue/2099' into 'develop'
[#2099] Import/export mutes [BE] See merge request pleroma/pleroma!2965
This commit is contained in:
commit
b36b6259d8
@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Media preview proxy (requires media proxy be enabled; see `:media_preview_proxy` config for more details).
|
- Media preview proxy (requires media proxy be enabled; see `:media_preview_proxy` config for more details).
|
||||||
|
- Pleroma API: Importing the mutes users from CSV files.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Removed `:managed_config` option. In practice, it was accidentally removed with 2.0.0 release when frontends were
|
- Removed `:managed_config` option. In practice, it was accidentally removed with 2.0.0 release when frontends were
|
||||||
switched to a new configuration mechanism, however it was not officially removed until now.
|
switched to a new configuration mechanism, however it was not officially removed until now.
|
||||||
|
|
||||||
|
|
||||||
## [2.1.2] - 2020-09-17
|
## [2.1.2] - 2020-09-17
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
@ -44,6 +44,22 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
|
|||||||
* Response: HTTP 200 on success, 500 on error
|
* Response: HTTP 200 on success, 500 on error
|
||||||
* Note: Users that can't be followed are silently skipped.
|
* Note: Users that can't be followed are silently skipped.
|
||||||
|
|
||||||
|
## `/api/pleroma/blocks_import`
|
||||||
|
### Imports your blocks.
|
||||||
|
* Method: `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `list`: STRING or FILE containing a whitespace-separated list of accounts to block
|
||||||
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
|
||||||
|
## `/api/pleroma/mutes_import`
|
||||||
|
### Imports your mutes.
|
||||||
|
* Method: `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `list`: STRING or FILE containing a whitespace-separated list of accounts to mute
|
||||||
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
|
||||||
## `/api/pleroma/captcha`
|
## `/api/pleroma/captcha`
|
||||||
### Get a new captcha
|
### Get a new captcha
|
||||||
* Method: `GET`
|
* Method: `GET`
|
||||||
|
@ -1685,42 +1685,6 @@ def perform(:delete, %User{} = user) do
|
|||||||
|
|
||||||
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
||||||
|
|
||||||
@spec perform(atom(), User.t(), list()) :: list() | {:error, any()}
|
|
||||||
def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
|
|
||||||
when is_list(blocked_identifiers) do
|
|
||||||
Enum.map(
|
|
||||||
blocked_identifiers,
|
|
||||||
fn blocked_identifier ->
|
|
||||||
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier),
|
|
||||||
{:ok, _block} <- CommonAPI.block(blocker, blocked) do
|
|
||||||
blocked
|
|
||||||
else
|
|
||||||
err ->
|
|
||||||
Logger.debug("blocks_import failed for #{blocked_identifier} with: #{inspect(err)}")
|
|
||||||
err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform(:follow_import, %User{} = follower, followed_identifiers)
|
|
||||||
when is_list(followed_identifiers) do
|
|
||||||
Enum.map(
|
|
||||||
followed_identifiers,
|
|
||||||
fn followed_identifier ->
|
|
||||||
with {:ok, %User{} = followed} <- get_or_fetch(followed_identifier),
|
|
||||||
{:ok, follower} <- maybe_direct_follow(follower, followed),
|
|
||||||
{:ok, _, _, _} <- CommonAPI.follow(follower, followed) do
|
|
||||||
followed
|
|
||||||
else
|
|
||||||
err ->
|
|
||||||
Logger.debug("follow_import failed for #{followed_identifier} with: #{inspect(err)}")
|
|
||||||
err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec external_users_query() :: Ecto.Query.t()
|
@spec external_users_query() :: Ecto.Query.t()
|
||||||
def external_users_query do
|
def external_users_query do
|
||||||
User.Query.build(%{
|
User.Query.build(%{
|
||||||
@ -1749,21 +1713,6 @@ def external_users(opts \\ []) do
|
|||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
|
|
||||||
BackgroundWorker.enqueue("blocks_import", %{
|
|
||||||
"blocker_id" => blocker.id,
|
|
||||||
"blocked_identifiers" => blocked_identifiers
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_import(%User{} = follower, followed_identifiers)
|
|
||||||
when is_list(followed_identifiers) do
|
|
||||||
BackgroundWorker.enqueue("follow_import", %{
|
|
||||||
"follower_id" => follower.id,
|
|
||||||
"followed_identifiers" => followed_identifiers
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_notifications_from_user_activities(%User{ap_id: ap_id}) do
|
def delete_notifications_from_user_activities(%User{ap_id: ap_id}) do
|
||||||
Notification
|
Notification
|
||||||
|> join(:inner, [n], activity in assoc(n, :activity))
|
|> join(:inner, [n], activity in assoc(n, :activity))
|
||||||
|
85
lib/pleroma/user/import.ex
Normal file
85
lib/pleroma/user/import.ex
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.Import do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@spec perform(atom(), User.t(), list()) :: :ok | list() | {:error, any()}
|
||||||
|
def perform(:mutes_import, %User{} = user, [_ | _] = identifiers) do
|
||||||
|
Enum.map(
|
||||||
|
identifiers,
|
||||||
|
fn identifier ->
|
||||||
|
with {:ok, %User{} = muted_user} <- User.get_or_fetch(identifier),
|
||||||
|
{:ok, _} <- User.mute(user, muted_user) do
|
||||||
|
muted_user
|
||||||
|
else
|
||||||
|
error -> handle_error(:mutes_import, identifier, error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(:blocks_import, %User{} = blocker, [_ | _] = identifiers) do
|
||||||
|
Enum.map(
|
||||||
|
identifiers,
|
||||||
|
fn identifier ->
|
||||||
|
with {:ok, %User{} = blocked} <- User.get_or_fetch(identifier),
|
||||||
|
{:ok, _block} <- CommonAPI.block(blocker, blocked) do
|
||||||
|
blocked
|
||||||
|
else
|
||||||
|
error -> handle_error(:blocks_import, identifier, error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(:follow_import, %User{} = follower, [_ | _] = identifiers) do
|
||||||
|
Enum.map(
|
||||||
|
identifiers,
|
||||||
|
fn identifier ->
|
||||||
|
with {:ok, %User{} = followed} <- User.get_or_fetch(identifier),
|
||||||
|
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||||
|
{:ok, _, _, _} <- CommonAPI.follow(follower, followed) do
|
||||||
|
followed
|
||||||
|
else
|
||||||
|
error -> handle_error(:follow_import, identifier, error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(_, _, _), do: :ok
|
||||||
|
|
||||||
|
defp handle_error(op, user_id, error) do
|
||||||
|
Logger.debug("#{op} failed for #{user_id} with: #{inspect(error)}")
|
||||||
|
error
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks_import(%User{} = blocker, [_ | _] = identifiers) do
|
||||||
|
BackgroundWorker.enqueue(
|
||||||
|
"blocks_import",
|
||||||
|
%{"user_id" => blocker.id, "identifiers" => identifiers}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_import(%User{} = follower, [_ | _] = identifiers) do
|
||||||
|
BackgroundWorker.enqueue(
|
||||||
|
"follow_import",
|
||||||
|
%{"user_id" => follower.id, "identifiers" => identifiers}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutes_import(%User{} = user, [_ | _] = identifiers) do
|
||||||
|
BackgroundWorker.enqueue(
|
||||||
|
"mutes_import",
|
||||||
|
%{"user_id" => user.id, "identifiers" => identifiers}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
80
lib/pleroma/web/api_spec/operations/user_import_operation.ex
Normal file
80
lib/pleroma/web/api_spec/operations/user_import_operation.ex
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.UserImportOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
@spec open_api_operation(atom) :: Operation.t()
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["follow_import"],
|
||||||
|
summary: "Imports your follows.",
|
||||||
|
operationId: "UserImportController.follow",
|
||||||
|
requestBody: request_body("Parameters", import_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
},
|
||||||
|
security: [%{"oAuth" => ["write:follow"]}]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["blocks_import"],
|
||||||
|
summary: "Imports your blocks.",
|
||||||
|
operationId: "UserImportController.blocks",
|
||||||
|
requestBody: request_body("Parameters", import_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
},
|
||||||
|
security: [%{"oAuth" => ["write:blocks"]}]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutes_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["mutes_import"],
|
||||||
|
summary: "Imports your mutes.",
|
||||||
|
operationId: "UserImportController.mutes",
|
||||||
|
requestBody: request_body("Parameters", import_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
},
|
||||||
|
security: [%{"oAuth" => ["write:mutes"]}]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp import_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:list],
|
||||||
|
properties: %{
|
||||||
|
list: %Schema{
|
||||||
|
description:
|
||||||
|
"STRING or FILE containing a whitespace-separated list of accounts to import.",
|
||||||
|
anyOf: [
|
||||||
|
%Schema{type: :string, format: :binary},
|
||||||
|
%Schema{type: :string}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ok_response do
|
||||||
|
Operation.response("Ok", "application/json", %Schema{type: :string, example: "ok"})
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,61 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.UserImportController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ApiSpec
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:follows"]} when action == :follow)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action == :mutes)
|
||||||
|
|
||||||
|
plug(OpenApiSpex.Plug.CastAndValidate)
|
||||||
|
defdelegate open_api_operation(action), to: ApiSpec.UserImportOperation
|
||||||
|
|
||||||
|
def follow(%{body_params: %{list: %Plug.Upload{path: path}}} = conn, _) do
|
||||||
|
follow(%Plug.Conn{conn | body_params: %{list: File.read!(path)}}, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow(%{assigns: %{user: follower}, body_params: %{list: list}} = conn, _) do
|
||||||
|
identifiers =
|
||||||
|
list
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.map(&(&1 |> String.split(",") |> List.first()))
|
||||||
|
|> List.delete("Account address")
|
||||||
|
|> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@")))
|
||||||
|
|> Enum.reject(&(&1 == ""))
|
||||||
|
|
||||||
|
User.Import.follow_import(follower, identifiers)
|
||||||
|
json(conn, "job started")
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks(%{body_params: %{list: %Plug.Upload{path: path}}} = conn, _) do
|
||||||
|
blocks(%Plug.Conn{conn | body_params: %{list: File.read!(path)}}, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks(%{assigns: %{user: blocker}, body_params: %{list: list}} = conn, _) do
|
||||||
|
User.Import.blocks_import(blocker, prepare_user_identifiers(list))
|
||||||
|
json(conn, "job started")
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutes(%{body_params: %{list: %Plug.Upload{path: path}}} = conn, _) do
|
||||||
|
mutes(%Plug.Conn{conn | body_params: %{list: File.read!(path)}}, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutes(%{assigns: %{user: user}, body_params: %{list: list}} = conn, _) do
|
||||||
|
User.Import.mutes_import(user, prepare_user_identifiers(list))
|
||||||
|
json(conn, "job started")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_user_identifiers(list) do
|
||||||
|
list
|
||||||
|
|> String.split()
|
||||||
|
|> Enum.map(&String.trim_leading(&1, "@"))
|
||||||
|
end
|
||||||
|
end
|
@ -269,14 +269,15 @@ defmodule Pleroma.Web.Router do
|
|||||||
post("/delete_account", UtilController, :delete_account)
|
post("/delete_account", UtilController, :delete_account)
|
||||||
put("/notification_settings", UtilController, :update_notificaton_settings)
|
put("/notification_settings", UtilController, :update_notificaton_settings)
|
||||||
post("/disable_account", UtilController, :disable_account)
|
post("/disable_account", UtilController, :disable_account)
|
||||||
|
|
||||||
post("/blocks_import", UtilController, :blocks_import)
|
|
||||||
post("/follow_import", UtilController, :follow_import)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
post("/mutes_import", UserImportController, :mutes)
|
||||||
|
post("/blocks_import", UserImportController, :blocks)
|
||||||
|
post("/follow_import", UserImportController, :follow)
|
||||||
|
|
||||||
get("/accounts/mfa", TwoFactorAuthenticationController, :settings)
|
get("/accounts/mfa", TwoFactorAuthenticationController, :settings)
|
||||||
get("/accounts/mfa/backup_codes", TwoFactorAuthenticationController, :backup_codes)
|
get("/accounts/mfa/backup_codes", TwoFactorAuthenticationController, :backup_codes)
|
||||||
get("/accounts/mfa/setup/:method", TwoFactorAuthenticationController, :setup)
|
get("/accounts/mfa/setup/:method", TwoFactorAuthenticationController, :setup)
|
||||||
|
@ -18,14 +18,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["follow", "write:follows"]}
|
|
||||||
when action == :follow_import
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:accounts"]}
|
%{scopes: ["write:accounts"]}
|
||||||
@ -104,33 +96,6 @@ def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
|
||||||
follow_import(conn, %{"list" => File.read!(listfile.path)})
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
|
|
||||||
followed_identifiers =
|
|
||||||
list
|
|
||||||
|> String.split("\n")
|
|
||||||
|> Enum.map(&(&1 |> String.split(",") |> List.first()))
|
|
||||||
|> List.delete("Account address")
|
|
||||||
|> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@")))
|
|
||||||
|> Enum.reject(&(&1 == ""))
|
|
||||||
|
|
||||||
User.follow_import(follower, followed_identifiers)
|
|
||||||
json(conn, "job started")
|
|
||||||
end
|
|
||||||
|
|
||||||
def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
|
|
||||||
blocks_import(conn, %{"list" => File.read!(listfile.path)})
|
|
||||||
end
|
|
||||||
|
|
||||||
def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
|
|
||||||
blocked_identifiers = list |> String.split() |> Enum.map(&String.trim_leading(&1, "@"))
|
|
||||||
User.blocks_import(blocker, blocked_identifiers)
|
|
||||||
json(conn, "job started")
|
|
||||||
end
|
|
||||||
|
|
||||||
def change_password(%{assigns: %{user: user}} = conn, params) do
|
def change_password(%{assigns: %{user: user}} = conn, params) do
|
||||||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
|
@ -26,26 +26,10 @@ def perform(%Job{args: %{"op" => "force_password_reset", "user_id" => user_id}})
|
|||||||
User.perform(:force_password_reset, user)
|
User.perform(:force_password_reset, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Job{
|
def perform(%Job{args: %{"op" => op, "user_id" => user_id, "identifiers" => identifiers}})
|
||||||
args: %{
|
when op in ["blocks_import", "follow_import", "mutes_import"] do
|
||||||
"op" => "blocks_import",
|
user = User.get_cached_by_id(user_id)
|
||||||
"blocker_id" => blocker_id,
|
{:ok, User.Import.perform(String.to_atom(op), user, identifiers)}
|
||||||
"blocked_identifiers" => blocked_identifiers
|
|
||||||
}
|
|
||||||
}) do
|
|
||||||
blocker = User.get_cached_by_id(blocker_id)
|
|
||||||
{:ok, User.perform(:blocks_import, blocker, blocked_identifiers)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def perform(%Job{
|
|
||||||
args: %{
|
|
||||||
"op" => "follow_import",
|
|
||||||
"follower_id" => follower_id,
|
|
||||||
"followed_identifiers" => followed_identifiers
|
|
||||||
}
|
|
||||||
}) do
|
|
||||||
follower = User.get_cached_by_id(follower_id)
|
|
||||||
{:ok, User.perform(:follow_import, follower, followed_identifiers)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%Job{args: %{"op" => "media_proxy_preload", "message" => message}}) do
|
def perform(%Job{args: %{"op" => "media_proxy_preload", "message" => message}}) do
|
||||||
|
76
test/user/import_test.exs
Normal file
76
test/user/import_test.exs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.ImportTest do
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
use Pleroma.DataCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "follow_import" do
|
||||||
|
test "it imports user followings from list" do
|
||||||
|
[user1, user2, user3] = insert_list(3, :user)
|
||||||
|
|
||||||
|
identifiers = [
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, job} = User.Import.follow_import(user1, identifiers)
|
||||||
|
|
||||||
|
assert {:ok, result} = ObanHelpers.perform(job)
|
||||||
|
assert is_list(result)
|
||||||
|
assert result == [user2, user3]
|
||||||
|
assert User.following?(user1, user2)
|
||||||
|
assert User.following?(user1, user3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "blocks_import" do
|
||||||
|
test "it imports user blocks from list" do
|
||||||
|
[user1, user2, user3] = insert_list(3, :user)
|
||||||
|
|
||||||
|
identifiers = [
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, job} = User.Import.blocks_import(user1, identifiers)
|
||||||
|
|
||||||
|
assert {:ok, result} = ObanHelpers.perform(job)
|
||||||
|
assert is_list(result)
|
||||||
|
assert result == [user2, user3]
|
||||||
|
assert User.blocks?(user1, user2)
|
||||||
|
assert User.blocks?(user1, user3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "mutes_import" do
|
||||||
|
test "it imports user mutes from list" do
|
||||||
|
[user1, user2, user3] = insert_list(3, :user)
|
||||||
|
|
||||||
|
identifiers = [
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, job} = User.Import.mutes_import(user1, identifiers)
|
||||||
|
|
||||||
|
assert {:ok, result} = ObanHelpers.perform(job)
|
||||||
|
assert is_list(result)
|
||||||
|
assert result == [user2, user3]
|
||||||
|
assert User.mutes?(user1, user2)
|
||||||
|
assert User.mutes?(user1, user3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -971,23 +971,6 @@ test "it sets the follower_count property" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "follow_import" do
|
|
||||||
test "it imports user followings from list" do
|
|
||||||
[user1, user2, user3] = insert_list(3, :user)
|
|
||||||
|
|
||||||
identifiers = [
|
|
||||||
user2.ap_id,
|
|
||||||
user3.nickname
|
|
||||||
]
|
|
||||||
|
|
||||||
{:ok, job} = User.follow_import(user1, identifiers)
|
|
||||||
|
|
||||||
assert {:ok, result} = ObanHelpers.perform(job)
|
|
||||||
assert is_list(result)
|
|
||||||
assert result == [user2, user3]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "mutes" do
|
describe "mutes" do
|
||||||
test "it mutes people" do
|
test "it mutes people" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
@ -1194,23 +1177,6 @@ test "follows take precedence over domain blocks" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "blocks_import" do
|
|
||||||
test "it imports user blocks from list" do
|
|
||||||
[user1, user2, user3] = insert_list(3, :user)
|
|
||||||
|
|
||||||
identifiers = [
|
|
||||||
user2.ap_id,
|
|
||||||
user3.nickname
|
|
||||||
]
|
|
||||||
|
|
||||||
{:ok, job} = User.blocks_import(user1, identifiers)
|
|
||||||
|
|
||||||
assert {:ok, result} = ObanHelpers.perform(job)
|
|
||||||
assert is_list(result)
|
|
||||||
assert result == [user2, user3]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "get_recipients_from_activity" do
|
describe "get_recipients_from_activity" do
|
||||||
test "works for announces" do
|
test "works for announces" do
|
||||||
actor = insert(:user)
|
actor = insert(:user)
|
||||||
|
235
test/web/pleroma_api/controllers/user_import_controller_test.exs
Normal file
235
test/web/pleroma_api/controllers/user_import_controller_test.exs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
import Mock
|
||||||
|
|
||||||
|
setup do
|
||||||
|
Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/follow_import" do
|
||||||
|
setup do: oauth_access(["follow"])
|
||||||
|
|
||||||
|
test "it returns HTTP 200", %{conn: conn} do
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports follow lists from file", %{conn: conn} do
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
with_mocks([
|
||||||
|
{File, [],
|
||||||
|
read!: fn "follow_list.txt" ->
|
||||||
|
"Account address,Show boosts\n#{user2.ap_id},true"
|
||||||
|
end}
|
||||||
|
]) do
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/follow_import", %{
|
||||||
|
"list" => %Plug.Upload{path: "follow_list.txt"}
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == [user2]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports new-style mastodon follow lists", %{conn: conn} do
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/follow_import", %{
|
||||||
|
"list" => "Account address,Show boosts\n#{user2.ap_id},true"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == "job started"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires 'follow' or 'write:follows' permissions" do
|
||||||
|
token1 = insert(:oauth_token, scopes: ["read", "write"])
|
||||||
|
token2 = insert(:oauth_token, scopes: ["follow"])
|
||||||
|
token3 = insert(:oauth_token, scopes: ["something"])
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
for token <- [token1, token2, token3] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"})
|
||||||
|
|
||||||
|
if token == token3 do
|
||||||
|
assert %{"error" => "Insufficient permissions: follow | write:follows."} ==
|
||||||
|
json_response(conn, 403)
|
||||||
|
else
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports follows with different nickname variations", %{conn: conn} do
|
||||||
|
users = [user2, user3, user4, user5, user6] = insert_list(5, :user)
|
||||||
|
|
||||||
|
identifiers =
|
||||||
|
[
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname,
|
||||||
|
" ",
|
||||||
|
"@" <> user4.nickname,
|
||||||
|
user5.nickname <> "@localhost",
|
||||||
|
"@" <> user6.nickname <> "@localhost"
|
||||||
|
]
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/follow_import", %{"list" => identifiers})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == users
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/blocks_import" do
|
||||||
|
# Note: "follow" or "write:blocks" permission is required
|
||||||
|
setup do: oauth_access(["write:blocks"])
|
||||||
|
|
||||||
|
test "it returns HTTP 200", %{conn: conn} do
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports blocks users from file", %{conn: conn} do
|
||||||
|
users = [user2, user3] = insert_list(2, :user)
|
||||||
|
|
||||||
|
with_mocks([
|
||||||
|
{File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end}
|
||||||
|
]) do
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/blocks_import", %{
|
||||||
|
"list" => %Plug.Upload{path: "blocks_list.txt"}
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == users
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports blocks with different nickname variations", %{conn: conn} do
|
||||||
|
users = [user2, user3, user4, user5, user6] = insert_list(5, :user)
|
||||||
|
|
||||||
|
identifiers =
|
||||||
|
[
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname,
|
||||||
|
"@" <> user4.nickname,
|
||||||
|
user5.nickname <> "@localhost",
|
||||||
|
"@" <> user6.nickname <> "@localhost"
|
||||||
|
]
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/blocks_import", %{"list" => identifiers})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == users
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/mutes_import" do
|
||||||
|
# Note: "follow" or "write:mutes" permission is required
|
||||||
|
setup do: oauth_access(["write:mutes"])
|
||||||
|
|
||||||
|
test "it returns HTTP 200", %{user: user, conn: conn} do
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == [user2]
|
||||||
|
assert Pleroma.User.mutes?(user, user2)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports mutes users from file", %{user: user, conn: conn} do
|
||||||
|
users = [user2, user3] = insert_list(2, :user)
|
||||||
|
|
||||||
|
with_mocks([
|
||||||
|
{File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end}
|
||||||
|
]) do
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/mutes_import", %{
|
||||||
|
"list" => %Plug.Upload{path: "mutes_list.txt"}
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == users
|
||||||
|
assert Enum.all?(users, &Pleroma.User.mutes?(user, &1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it imports mutes with different nickname variations", %{user: user, conn: conn} do
|
||||||
|
users = [user2, user3, user4, user5, user6] = insert_list(5, :user)
|
||||||
|
|
||||||
|
identifiers =
|
||||||
|
[
|
||||||
|
user2.ap_id,
|
||||||
|
user3.nickname,
|
||||||
|
"@" <> user4.nickname,
|
||||||
|
user5.nickname <> "@localhost",
|
||||||
|
"@" <> user6.nickname <> "@localhost"
|
||||||
|
]
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
|
assert "job started" ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/mutes_import", %{"list" => identifiers})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
||||||
|
assert job_result == users
|
||||||
|
assert Enum.all?(users, &Pleroma.User.mutes?(user, &1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -21,170 +21,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
|||||||
setup do: clear_config([:instance])
|
setup do: clear_config([:instance])
|
||||||
setup do: clear_config([:frontend_configurations, :pleroma_fe])
|
setup do: clear_config([:frontend_configurations, :pleroma_fe])
|
||||||
|
|
||||||
describe "POST /api/pleroma/follow_import" do
|
|
||||||
setup do: oauth_access(["follow"])
|
|
||||||
|
|
||||||
test "it returns HTTP 200", %{conn: conn} do
|
|
||||||
user2 = insert(:user)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it imports follow lists from file", %{user: user1, conn: conn} do
|
|
||||||
user2 = insert(:user)
|
|
||||||
|
|
||||||
with_mocks([
|
|
||||||
{File, [],
|
|
||||||
read!: fn "follow_list.txt" ->
|
|
||||||
"Account address,Show boosts\n#{user2.ap_id},true"
|
|
||||||
end}
|
|
||||||
]) do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
|
|
||||||
assert ObanHelpers.member?(
|
|
||||||
%{
|
|
||||||
"op" => "follow_import",
|
|
||||||
"follower_id" => user1.id,
|
|
||||||
"followed_identifiers" => [user2.ap_id]
|
|
||||||
},
|
|
||||||
all_enqueued(worker: Pleroma.Workers.BackgroundWorker)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it imports new-style mastodon follow lists", %{conn: conn} do
|
|
||||||
user2 = insert(:user)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/follow_import", %{
|
|
||||||
"list" => "Account address,Show boosts\n#{user2.ap_id},true"
|
|
||||||
})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "requires 'follow' or 'write:follows' permissions" do
|
|
||||||
token1 = insert(:oauth_token, scopes: ["read", "write"])
|
|
||||||
token2 = insert(:oauth_token, scopes: ["follow"])
|
|
||||||
token3 = insert(:oauth_token, scopes: ["something"])
|
|
||||||
another_user = insert(:user)
|
|
||||||
|
|
||||||
for token <- [token1, token2, token3] do
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("authorization", "Bearer #{token.token}")
|
|
||||||
|> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"})
|
|
||||||
|
|
||||||
if token == token3 do
|
|
||||||
assert %{"error" => "Insufficient permissions: follow | write:follows."} ==
|
|
||||||
json_response(conn, 403)
|
|
||||||
else
|
|
||||||
assert json_response(conn, 200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it imports follows with different nickname variations", %{conn: conn} do
|
|
||||||
[user2, user3, user4, user5, user6] = insert_list(5, :user)
|
|
||||||
|
|
||||||
identifiers =
|
|
||||||
[
|
|
||||||
user2.ap_id,
|
|
||||||
user3.nickname,
|
|
||||||
" ",
|
|
||||||
"@" <> user4.nickname,
|
|
||||||
user5.nickname <> "@localhost",
|
|
||||||
"@" <> user6.nickname <> "@localhost"
|
|
||||||
]
|
|
||||||
|> Enum.join("\n")
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/follow_import", %{"list" => identifiers})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
|
||||||
assert job_result == [user2, user3, user4, user5, user6]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /api/pleroma/blocks_import" do
|
|
||||||
# Note: "follow" or "write:blocks" permission is required
|
|
||||||
setup do: oauth_access(["write:blocks"])
|
|
||||||
|
|
||||||
test "it returns HTTP 200", %{conn: conn} do
|
|
||||||
user2 = insert(:user)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it imports blocks users from file", %{user: user1, conn: conn} do
|
|
||||||
user2 = insert(:user)
|
|
||||||
user3 = insert(:user)
|
|
||||||
|
|
||||||
with_mocks([
|
|
||||||
{File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end}
|
|
||||||
]) do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
|
|
||||||
assert ObanHelpers.member?(
|
|
||||||
%{
|
|
||||||
"op" => "blocks_import",
|
|
||||||
"blocker_id" => user1.id,
|
|
||||||
"blocked_identifiers" => [user2.ap_id, user3.ap_id]
|
|
||||||
},
|
|
||||||
all_enqueued(worker: Pleroma.Workers.BackgroundWorker)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it imports blocks with different nickname variations", %{conn: conn} do
|
|
||||||
[user2, user3, user4, user5, user6] = insert_list(5, :user)
|
|
||||||
|
|
||||||
identifiers =
|
|
||||||
[
|
|
||||||
user2.ap_id,
|
|
||||||
user3.nickname,
|
|
||||||
"@" <> user4.nickname,
|
|
||||||
user5.nickname <> "@localhost",
|
|
||||||
"@" <> user6.nickname <> "@localhost"
|
|
||||||
]
|
|
||||||
|> Enum.join(" ")
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> post("/api/pleroma/blocks_import", %{"list" => identifiers})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response == "job started"
|
|
||||||
assert [{:ok, job_result}] = ObanHelpers.perform_all()
|
|
||||||
assert job_result == [user2, user3, user4, user5, user6]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PUT /api/pleroma/notification_settings" do
|
describe "PUT /api/pleroma/notification_settings" do
|
||||||
setup do: oauth_access(["write:accounts"])
|
setup do: oauth_access(["write:accounts"])
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user