Merge branch 'fix/2412-filters' into 'develop'
Support for expires_in/expires_at in filters Closes #2412 See merge request pleroma/pleroma!3279
This commit is contained in:
commit
859309e116
|
@ -81,6 +81,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: Fix not being able to add or remove multiple users at once in lists.
|
- Mastodon API: Fix not being able to add or remove multiple users at once in lists.
|
||||||
- Mastodon API: Fixed own_votes being not returned with poll data.
|
- Mastodon API: Fixed own_votes being not returned with poll data.
|
||||||
- Mastodon API: Fixed creation of scheduled posts with polls.
|
- Mastodon API: Fixed creation of scheduled posts with polls.
|
||||||
|
- Mastodon API: Support for expires_in/expires_at in the Filters.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Unreleased (Patch)
|
## Unreleased (Patch)
|
||||||
|
|
|
@ -33,10 +33,11 @@ defp fetch_user(user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_filter(user) do
|
defp create_filter(user) do
|
||||||
Pleroma.Filter.create(%Pleroma.Filter{
|
Pleroma.Filter.create(%{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
phrase: "must be filtered",
|
phrase: "must be filtered",
|
||||||
hide: true
|
hide: true,
|
||||||
|
context: ["home"]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -543,6 +543,7 @@
|
||||||
queues: [
|
queues: [
|
||||||
activity_expiration: 10,
|
activity_expiration: 10,
|
||||||
token_expiration: 5,
|
token_expiration: 5,
|
||||||
|
filter_expiration: 1,
|
||||||
backup: 1,
|
backup: 1,
|
||||||
federator_incoming: 50,
|
federator_incoming: 50,
|
||||||
federator_outgoing: 50,
|
federator_outgoing: 50,
|
||||||
|
|
|
@ -11,6 +11,9 @@ defmodule Pleroma.Filter do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@type t() :: %__MODULE__{}
|
||||||
|
@type format() :: :postgres | :re
|
||||||
|
|
||||||
schema "filters" do
|
schema "filters" do
|
||||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
field(:filter_id, :integer)
|
field(:filter_id, :integer)
|
||||||
|
@ -18,15 +21,16 @@ defmodule Pleroma.Filter do
|
||||||
field(:whole_word, :boolean, default: true)
|
field(:whole_word, :boolean, default: true)
|
||||||
field(:phrase, :string)
|
field(:phrase, :string)
|
||||||
field(:context, {:array, :string})
|
field(:context, {:array, :string})
|
||||||
field(:expires_at, :utc_datetime)
|
field(:expires_at, :naive_datetime)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get(integer() | String.t(), User.t()) :: t() | nil
|
||||||
def get(id, %{id: user_id} = _user) do
|
def get(id, %{id: user_id} = _user) do
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
f in Pleroma.Filter,
|
f in __MODULE__,
|
||||||
where: f.filter_id == ^id,
|
where: f.filter_id == ^id,
|
||||||
where: f.user_id == ^user_id
|
where: f.user_id == ^user_id
|
||||||
)
|
)
|
||||||
|
@ -34,14 +38,17 @@ def get(id, %{id: user_id} = _user) do
|
||||||
Repo.one(query)
|
Repo.one(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_active(Ecto.Query.t() | module()) :: Ecto.Query.t()
|
||||||
def get_active(query) do
|
def get_active(query) do
|
||||||
from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now())
|
from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_irreversible(Ecto.Query.t()) :: Ecto.Query.t()
|
||||||
def get_irreversible(query) do
|
def get_irreversible(query) do
|
||||||
from(f in query, where: f.hide)
|
from(f in query, where: f.hide)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_filters(Ecto.Query.t() | module(), User.t()) :: [t()]
|
||||||
def get_filters(query \\ __MODULE__, %User{id: user_id}) do
|
def get_filters(query \\ __MODULE__, %User{id: user_id}) do
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
|
@ -53,7 +60,32 @@ def get_filters(query \\ __MODULE__, %User{id: user_id}) do
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do
|
@spec create(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def create(attrs \\ %{}) do
|
||||||
|
Repo.transaction(fn -> create_with_expiration(attrs) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_with_expiration(attrs) do
|
||||||
|
with {:ok, filter} <- do_create(attrs),
|
||||||
|
{:ok, _} <- maybe_add_expiration_job(filter) do
|
||||||
|
filter
|
||||||
|
else
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_create(attrs) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> cast(attrs, [:phrase, :context, :hide, :expires_at, :whole_word, :user_id, :filter_id])
|
||||||
|
|> maybe_add_filter_id()
|
||||||
|
|> validate_required([:phrase, :context, :user_id, :filter_id])
|
||||||
|
|> maybe_add_expires_at(attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_filter_id(%{changes: %{filter_id: _}} = changeset), do: changeset
|
||||||
|
|
||||||
|
defp maybe_add_filter_id(%{changes: %{user_id: user_id}} = changeset) do
|
||||||
# If filter_id wasn't given, use the max filter_id for this user plus 1.
|
# If filter_id wasn't given, use the max filter_id for this user plus 1.
|
||||||
# XXX This could result in a race condition if a user tries to add two
|
# XXX This could result in a race condition if a user tries to add two
|
||||||
# different filters for their account from two different clients at the
|
# different filters for their account from two different clients at the
|
||||||
|
@ -61,7 +93,7 @@ def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do
|
||||||
|
|
||||||
max_id_query =
|
max_id_query =
|
||||||
from(
|
from(
|
||||||
f in Pleroma.Filter,
|
f in __MODULE__,
|
||||||
where: f.user_id == ^user_id,
|
where: f.user_id == ^user_id,
|
||||||
select: max(f.filter_id)
|
select: max(f.filter_id)
|
||||||
)
|
)
|
||||||
|
@ -76,34 +108,92 @@ def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do
|
||||||
max_id + 1
|
max_id + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
change(changeset, filter_id: filter_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# don't override expires_at, if passed expires_at and expires_in
|
||||||
|
defp maybe_add_expires_at(%{changes: %{expires_at: %NaiveDateTime{} = _}} = changeset, _) do
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_expires_at(changeset, %{expires_in: expires_in})
|
||||||
|
when is_integer(expires_in) and expires_in > 0 do
|
||||||
|
expires_at =
|
||||||
|
NaiveDateTime.utc_now()
|
||||||
|
|> NaiveDateTime.add(expires_in)
|
||||||
|
|> NaiveDateTime.truncate(:second)
|
||||||
|
|
||||||
|
change(changeset, expires_at: expires_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_expires_at(changeset, %{expires_in: nil}) do
|
||||||
|
change(changeset, expires_at: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_expires_at(changeset, _), do: changeset
|
||||||
|
|
||||||
|
defp maybe_add_expiration_job(%{expires_at: %NaiveDateTime{} = expires_at} = filter) do
|
||||||
|
Pleroma.Workers.PurgeExpiredFilter.enqueue(%{
|
||||||
|
filter_id: filter.id,
|
||||||
|
expires_at: DateTime.from_naive!(expires_at, "Etc/UTC")
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_expiration_job(_), do: {:ok, nil}
|
||||||
|
|
||||||
|
@spec delete(t()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def delete(%__MODULE__{} = filter) do
|
||||||
|
Repo.transaction(fn -> delete_with_expiration(filter) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_with_expiration(filter) do
|
||||||
|
with {:ok, _} <- maybe_delete_old_expiration_job(filter, nil),
|
||||||
|
{:ok, filter} <- Repo.delete(filter) do
|
||||||
filter
|
filter
|
||||||
|> Map.put(:filter_id, filter_id)
|
else
|
||||||
|> Repo.insert()
|
{:error, error} -> Repo.rollback(error)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%Pleroma.Filter{} = filter) do
|
@spec update(t(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
Repo.insert(filter)
|
def update(%__MODULE__{} = filter, params) do
|
||||||
|
Repo.transaction(fn -> update_with_expiration(filter, params) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(%Pleroma.Filter{id: filter_key} = filter) when is_number(filter_key) do
|
defp update_with_expiration(filter, params) do
|
||||||
Repo.delete(filter)
|
with {:ok, updated} <- do_update(filter, params),
|
||||||
|
{:ok, _} <- maybe_delete_old_expiration_job(filter, updated),
|
||||||
|
{:ok, _} <-
|
||||||
|
maybe_add_expiration_job(updated) do
|
||||||
|
updated
|
||||||
|
else
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(%Pleroma.Filter{id: filter_key} = filter) when is_nil(filter_key) do
|
defp do_update(filter, params) do
|
||||||
%Pleroma.Filter{id: id} = get(filter.filter_id, %{id: filter.user_id})
|
|
||||||
|
|
||||||
filter
|
|
||||||
|> Map.put(:id, id)
|
|
||||||
|> Repo.delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
def update(%Pleroma.Filter{} = filter, params) do
|
|
||||||
filter
|
filter
|
||||||
|> cast(params, [:phrase, :context, :hide, :expires_at, :whole_word])
|
|> cast(params, [:phrase, :context, :hide, :expires_at, :whole_word])
|
||||||
|> validate_required([:phrase, :context])
|
|> validate_required([:phrase, :context])
|
||||||
|
|> maybe_add_expires_at(params)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_delete_old_expiration_job(%{expires_at: nil}, _), do: {:ok, nil}
|
||||||
|
|
||||||
|
defp maybe_delete_old_expiration_job(%{expires_at: expires_at}, %{expires_at: expires_at}) do
|
||||||
|
{:ok, nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_delete_old_expiration_job(%{id: id}, _) do
|
||||||
|
with %Oban.Job{} = job <- Pleroma.Workers.PurgeExpiredFilter.get_expiration(id) do
|
||||||
|
Repo.delete(job)
|
||||||
|
else
|
||||||
|
nil -> {:ok, nil}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec compose_regex(User.t() | [t()], format()) :: String.t() | Regex.t() | nil
|
||||||
def compose_regex(user_or_filters, format \\ :postgres)
|
def compose_regex(user_or_filters, format \\ :postgres)
|
||||||
|
|
||||||
def compose_regex(%User{} = user, format) do
|
def compose_regex(%User{} = user, format) do
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.FilterOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
|
@ -20,7 +21,8 @@ def index_operation do
|
||||||
operationId: "FilterController.index",
|
operationId: "FilterController.index",
|
||||||
security: [%{"oAuth" => ["read:filters"]}],
|
security: [%{"oAuth" => ["read:filters"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Filters", "application/json", array_of_filters())
|
200 => Operation.response("Filters", "application/json", array_of_filters()),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -32,7 +34,10 @@ def create_operation do
|
||||||
operationId: "FilterController.create",
|
operationId: "FilterController.create",
|
||||||
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||||
security: [%{"oAuth" => ["write:filters"]}],
|
security: [%{"oAuth" => ["write:filters"]}],
|
||||||
responses: %{200 => Operation.response("Filter", "application/json", filter())}
|
responses: %{
|
||||||
|
200 => Operation.response("Filter", "application/json", filter()),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,7 +49,9 @@ def show_operation do
|
||||||
operationId: "FilterController.show",
|
operationId: "FilterController.show",
|
||||||
security: [%{"oAuth" => ["read:filters"]}],
|
security: [%{"oAuth" => ["read:filters"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Filter", "application/json", filter())
|
200 => Operation.response("Filter", "application/json", filter()),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -58,7 +65,8 @@ def update_operation do
|
||||||
requestBody: Helpers.request_body("Parameters", update_request(), required: true),
|
requestBody: Helpers.request_body("Parameters", update_request(), required: true),
|
||||||
security: [%{"oAuth" => ["write:filters"]}],
|
security: [%{"oAuth" => ["write:filters"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Filter", "application/json", filter())
|
200 => Operation.response("Filter", "application/json", filter()),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -75,7 +83,8 @@ def delete_operation do
|
||||||
Operation.response("Filter", "application/json", %Schema{
|
Operation.response("Filter", "application/json", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
description: "Empty object"
|
description: "Empty object"
|
||||||
})
|
}),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -210,15 +219,13 @@ defp update_request do
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Consider word boundaries?",
|
description: "Consider word boundaries?",
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
expires_in: %Schema{
|
||||||
|
nullable: true,
|
||||||
|
type: :integer,
|
||||||
|
description:
|
||||||
|
"Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire."
|
||||||
}
|
}
|
||||||
# TODO: probably should implement filter expiration
|
|
||||||
# expires_in: %Schema{
|
|
||||||
# type: :string,
|
|
||||||
# format: :"date-time",
|
|
||||||
# description:
|
|
||||||
# "ISO 8601 Datetime for when the filter expires. Otherwise,
|
|
||||||
# null for a filter that doesn't expire."
|
|
||||||
# }
|
|
||||||
},
|
},
|
||||||
required: [:phrase, :context],
|
required: [:phrase, :context],
|
||||||
example: %{
|
example: %{
|
||||||
|
|
|
@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FilterOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FilterOperation
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
@doc "GET /api/v1/filters"
|
@doc "GET /api/v1/filters"
|
||||||
def index(%{assigns: %{user: user}} = conn, _) do
|
def index(%{assigns: %{user: user}} = conn, _) do
|
||||||
filters = Filter.get_filters(user)
|
filters = Filter.get_filters(user)
|
||||||
|
@ -29,25 +31,23 @@ def index(%{assigns: %{user: user}} = conn, _) do
|
||||||
|
|
||||||
@doc "POST /api/v1/filters"
|
@doc "POST /api/v1/filters"
|
||||||
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||||
query = %Filter{
|
with {:ok, response} <-
|
||||||
user_id: user.id,
|
params
|
||||||
phrase: params.phrase,
|
|> Map.put(:user_id, user.id)
|
||||||
context: params.context,
|
|> Map.put(:hide, params[:irreversible])
|
||||||
hide: params.irreversible,
|
|> Map.delete(:irreversible)
|
||||||
whole_word: params.whole_word
|
|> Filter.create() do
|
||||||
# TODO: support `expires_in` parameter (as in Mastodon API)
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, response} = Filter.create(query)
|
|
||||||
|
|
||||||
render(conn, "show.json", filter: response)
|
render(conn, "show.json", filter: response)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/filters/:id"
|
@doc "GET /api/v1/filters/:id"
|
||||||
def show(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
def show(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
||||||
filter = Filter.get(filter_id, user)
|
with %Filter{} = filter <- Filter.get(filter_id, user) do
|
||||||
|
|
||||||
render(conn, "show.json", filter: filter)
|
render(conn, "show.json", filter: filter)
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PUT /api/v1/filters/:id"
|
@doc "PUT /api/v1/filters/:id"
|
||||||
|
@ -56,28 +56,31 @@ def update(
|
||||||
%{id: filter_id}
|
%{id: filter_id}
|
||||||
) do
|
) do
|
||||||
params =
|
params =
|
||||||
|
if is_boolean(params[:irreversible]) do
|
||||||
params
|
params
|
||||||
|> Map.delete(:irreversible)
|
|
||||||
|> Map.put(:hide, params[:irreversible])
|
|> Map.put(:hide, params[:irreversible])
|
||||||
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
|
|> Map.delete(:irreversible)
|
||||||
|> Map.new()
|
else
|
||||||
|
params
|
||||||
# TODO: support `expires_in` parameter (as in Mastodon API)
|
end
|
||||||
|
|
||||||
with %Filter{} = filter <- Filter.get(filter_id, user),
|
with %Filter{} = filter <- Filter.get(filter_id, user),
|
||||||
{:ok, %Filter{} = filter} <- Filter.update(filter, params) do
|
{:ok, %Filter{} = filter} <- Filter.update(filter, params) do
|
||||||
render(conn, "show.json", filter: filter)
|
render(conn, "show.json", filter: filter)
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
error -> error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "DELETE /api/v1/filters/:id"
|
@doc "DELETE /api/v1/filters/:id"
|
||||||
def delete(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
def delete(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
||||||
query = %Filter{
|
with %Filter{} = filter <- Filter.get(filter_id, user),
|
||||||
user_id: user.id,
|
{:ok, _} <- Filter.delete(filter) do
|
||||||
filter_id: filter_id
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, _} = Filter.delete(query)
|
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Workers.PurgeExpiredFilter do
|
||||||
|
@moduledoc """
|
||||||
|
Worker which purges expired filters
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Oban.Worker, queue: :filter_expiration, max_attempts: 1, unique: [period: :infinity]
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Oban.Job
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
@spec enqueue(%{filter_id: integer(), expires_at: DateTime.t()}) ::
|
||||||
|
{:ok, Job.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def enqueue(args) do
|
||||||
|
{scheduled_at, args} = Map.pop(args, :expires_at)
|
||||||
|
|
||||||
|
args
|
||||||
|
|> new(scheduled_at: scheduled_at)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def perform(%Job{args: %{"filter_id" => id}}) do
|
||||||
|
Pleroma.Filter
|
||||||
|
|> Repo.get(id)
|
||||||
|
|> Repo.delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_expiration(pos_integer()) :: Job.t() | nil
|
||||||
|
def get_expiration(id) do
|
||||||
|
from(j in Job,
|
||||||
|
where: j.state == "scheduled",
|
||||||
|
where: j.queue == "filter_expiration",
|
||||||
|
where: fragment("?->'filter_id' = ?", j.args, ^id)
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,81 +7,120 @@ defmodule Pleroma.FilterTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Oban.Job
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
alias Pleroma.Repo
|
|
||||||
|
setup do
|
||||||
|
[user: insert(:user)]
|
||||||
|
end
|
||||||
|
|
||||||
describe "creating filters" do
|
describe "creating filters" do
|
||||||
test "creating one filter" do
|
test "creation validation error", %{user: user} do
|
||||||
user = insert(:user)
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
expires_in: 60
|
||||||
|
}
|
||||||
|
|
||||||
query = %Filter{
|
{:error, _} = Filter.create(attrs)
|
||||||
|
|
||||||
|
assert Repo.all(Job) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "use passed expires_at instead expires_in", %{user: user} do
|
||||||
|
now = NaiveDateTime.utc_now()
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
expires_at: now,
|
||||||
|
phrase: "knights",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Filter{} = filter} = Filter.create(attrs)
|
||||||
|
|
||||||
|
result = Filter.get(filter.filter_id, user)
|
||||||
|
assert result.expires_at == NaiveDateTime.truncate(now, :second)
|
||||||
|
|
||||||
|
[job] = Repo.all(Job)
|
||||||
|
|
||||||
|
assert DateTime.truncate(job.scheduled_at, :second) ==
|
||||||
|
now |> NaiveDateTime.truncate(:second) |> DateTime.from_naive!("Etc/UTC")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creating one filter", %{user: user} do
|
||||||
|
attrs = %{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: 42,
|
filter_id: 42,
|
||||||
phrase: "knights",
|
phrase: "knights",
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, %Filter{} = filter} = Filter.create(query)
|
{:ok, %Filter{} = filter} = Filter.create(attrs)
|
||||||
result = Filter.get(filter.filter_id, user)
|
result = Filter.get(filter.filter_id, user)
|
||||||
assert query.phrase == result.phrase
|
assert attrs.phrase == result.phrase
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creating one filter without a pre-defined filter_id" do
|
test "creating with expired_at", %{user: user} do
|
||||||
user = insert(:user)
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
filter_id: 42,
|
||||||
|
phrase: "knights",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 60
|
||||||
|
}
|
||||||
|
|
||||||
query = %Filter{
|
{:ok, %Filter{} = filter} = Filter.create(attrs)
|
||||||
|
result = Filter.get(filter.filter_id, user)
|
||||||
|
assert attrs.phrase == result.phrase
|
||||||
|
|
||||||
|
assert [_] = Repo.all(Job)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creating one filter without a pre-defined filter_id", %{user: user} do
|
||||||
|
attrs = %{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
phrase: "knights",
|
phrase: "knights",
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, %Filter{} = filter} = Filter.create(query)
|
{:ok, %Filter{} = filter} = Filter.create(attrs)
|
||||||
# Should start at 1
|
# Should start at 1
|
||||||
assert filter.filter_id == 1
|
assert filter.filter_id == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creating additional filters uses previous highest filter_id + 1" do
|
test "creating additional filters uses previous highest filter_id + 1", %{user: user} do
|
||||||
user = insert(:user)
|
filter1 = insert(:filter, user: user)
|
||||||
|
|
||||||
query_one = %Filter{
|
attrs = %{
|
||||||
user_id: user.id,
|
|
||||||
filter_id: 42,
|
|
||||||
phrase: "knights",
|
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, %Filter{} = filter_one} = Filter.create(query_one)
|
|
||||||
|
|
||||||
query_two = %Filter{
|
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
# No filter_id
|
# No filter_id
|
||||||
phrase: "who",
|
phrase: "who",
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, %Filter{} = filter_two} = Filter.create(query_two)
|
{:ok, %Filter{} = filter2} = Filter.create(attrs)
|
||||||
assert filter_two.filter_id == filter_one.filter_id + 1
|
assert filter2.filter_id == filter1.filter_id + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "filter_id is unique per user" do
|
test "filter_id is unique per user", %{user: user_one} do
|
||||||
user_one = insert(:user)
|
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
|
|
||||||
query_one = %Filter{
|
attrs1 = %{
|
||||||
user_id: user_one.id,
|
user_id: user_one.id,
|
||||||
phrase: "knights",
|
phrase: "knights",
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, %Filter{} = filter_one} = Filter.create(query_one)
|
{:ok, %Filter{} = filter_one} = Filter.create(attrs1)
|
||||||
|
|
||||||
query_two = %Filter{
|
attrs2 = %{
|
||||||
user_id: user_two.id,
|
user_id: user_two.id,
|
||||||
phrase: "who",
|
phrase: "who",
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, %Filter{} = filter_two} = Filter.create(query_two)
|
{:ok, %Filter{} = filter_two} = Filter.create(attrs2)
|
||||||
|
|
||||||
assert filter_one.filter_id == 1
|
assert filter_one.filter_id == 1
|
||||||
assert filter_two.filter_id == 1
|
assert filter_two.filter_id == 1
|
||||||
|
@ -94,65 +133,61 @@ test "filter_id is unique per user" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deleting a filter" do
|
test "deleting a filter", %{user: user} do
|
||||||
user = insert(:user)
|
filter = insert(:filter, user: user)
|
||||||
|
|
||||||
query = %Filter{
|
assert Repo.get(Filter, filter.id)
|
||||||
user_id: user.id,
|
{:ok, filter} = Filter.delete(filter)
|
||||||
filter_id: 0,
|
refute Repo.get(Filter, filter.id)
|
||||||
phrase: "knights",
|
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, _filter} = Filter.create(query)
|
|
||||||
{:ok, filter} = Filter.delete(query)
|
|
||||||
assert is_nil(Repo.get(Filter, filter.filter_id))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "getting all filters by an user" do
|
test "deleting a filter with expires_at is removing Oban job too", %{user: user} do
|
||||||
user = insert(:user)
|
attrs = %{
|
||||||
|
|
||||||
query_one = %Filter{
|
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: 1,
|
phrase: "cofe",
|
||||||
phrase: "knights",
|
context: ["home"],
|
||||||
context: ["home"]
|
expires_in: 600
|
||||||
}
|
}
|
||||||
|
|
||||||
query_two = %Filter{
|
{:ok, filter} = Filter.create(attrs)
|
||||||
user_id: user.id,
|
assert %Job{id: job_id} = Pleroma.Workers.PurgeExpiredFilter.get_expiration(filter.id)
|
||||||
filter_id: 2,
|
{:ok, _} = Filter.delete(filter)
|
||||||
phrase: "who",
|
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, filter_one} = Filter.create(query_one)
|
assert Repo.get(Job, job_id) == nil
|
||||||
{:ok, filter_two} = Filter.create(query_two)
|
|
||||||
filters = Filter.get_filters(user)
|
|
||||||
assert filter_one in filters
|
|
||||||
assert filter_two in filters
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updating a filter" do
|
test "getting all filters by an user", %{user: user} do
|
||||||
user = insert(:user)
|
filter1 = insert(:filter, user: user)
|
||||||
|
filter2 = insert(:filter, user: user)
|
||||||
|
|
||||||
query_one = %Filter{
|
filter_ids = user |> Filter.get_filters() |> collect_ids()
|
||||||
user_id: user.id,
|
|
||||||
filter_id: 1,
|
assert filter1.id in filter_ids
|
||||||
phrase: "knights",
|
assert filter2.id in filter_ids
|
||||||
context: ["home"]
|
end
|
||||||
}
|
|
||||||
|
test "updating a filter", %{user: user} do
|
||||||
|
filter = insert(:filter, user: user)
|
||||||
|
|
||||||
changes = %{
|
changes = %{
|
||||||
phrase: "who",
|
phrase: "who",
|
||||||
context: ["home", "timeline"]
|
context: ["home", "timeline"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, filter_one} = Filter.create(query_one)
|
{:ok, updated_filter} = Filter.update(filter, changes)
|
||||||
{:ok, filter_two} = Filter.update(filter_one, changes)
|
|
||||||
|
|
||||||
assert filter_one != filter_two
|
assert filter != updated_filter
|
||||||
assert filter_two.phrase == changes.phrase
|
assert updated_filter.phrase == changes.phrase
|
||||||
assert filter_two.context == changes.context
|
assert updated_filter.context == changes.context
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updating with error", %{user: user} do
|
||||||
|
filter = insert(:filter, user: user)
|
||||||
|
|
||||||
|
changes = %{
|
||||||
|
phrase: nil
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, _} = Filter.update(filter, changes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,149 +4,412 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
import Pleroma.Factory
|
||||||
|
|
||||||
test "creating a filter" do
|
alias Pleroma.Filter
|
||||||
%{conn: conn} = oauth_access(["write:filters"])
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Workers.PurgeExpiredFilter
|
||||||
|
|
||||||
filter = %Pleroma.Filter{
|
test "non authenticated creation request", %{conn: conn} do
|
||||||
phrase: "knights",
|
response =
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
conn
|
||||||
|> put_req_header("content-type", "application/json")
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
|
|> post("/api/v1/filters", %{"phrase" => "knights", context: ["home"]})
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
assert response = json_response_and_validate_schema(conn, 200)
|
assert response["error"] == "Invalid credentials."
|
||||||
assert response["phrase"] == filter.phrase
|
end
|
||||||
assert response["context"] == filter.context
|
|
||||||
assert response["irreversible"] == false
|
describe "creating" do
|
||||||
|
setup do: oauth_access(["write:filters"])
|
||||||
|
|
||||||
|
test "a common filter", %{conn: conn, user: user} do
|
||||||
|
params = %{
|
||||||
|
phrase: "knights",
|
||||||
|
context: ["home"],
|
||||||
|
irreversible: true
|
||||||
|
}
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", params)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["phrase"] == params.phrase
|
||||||
|
assert response["context"] == params.context
|
||||||
|
assert response["irreversible"] == true
|
||||||
assert response["id"] != nil
|
assert response["id"] != nil
|
||||||
assert response["id"] != ""
|
assert response["id"] != ""
|
||||||
|
assert response["expires_at"] == nil
|
||||||
|
|
||||||
|
filter = Filter.get(response["id"], user)
|
||||||
|
assert filter.hide == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a filter with expires_in", %{conn: conn, user: user} do
|
||||||
|
in_seconds = 600
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{
|
||||||
|
"phrase" => "knights",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: in_seconds
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["irreversible"] == false
|
||||||
|
|
||||||
|
expires_at =
|
||||||
|
NaiveDateTime.utc_now()
|
||||||
|
|> NaiveDateTime.add(in_seconds)
|
||||||
|
|> Pleroma.Web.CommonAPI.Utils.to_masto_date()
|
||||||
|
|
||||||
|
assert response["expires_at"] == expires_at
|
||||||
|
|
||||||
|
filter = Filter.get(response["id"], user)
|
||||||
|
|
||||||
|
id = filter.id
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, %{id: ^id}} =
|
||||||
|
perform_job(PurgeExpiredFilter, %{
|
||||||
|
filter_id: filter.id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Repo.aggregate(Filter, :count, :id) == 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "fetching a list of filters" do
|
test "fetching a list of filters" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:filters"])
|
%{user: user, conn: conn} = oauth_access(["read:filters"])
|
||||||
|
|
||||||
query_one = %Pleroma.Filter{
|
%{filter_id: id1} = insert(:filter, user: user)
|
||||||
user_id: user.id,
|
%{filter_id: id2} = insert(:filter, user: user)
|
||||||
filter_id: 1,
|
|
||||||
phrase: "knights",
|
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
query_two = %Pleroma.Filter{
|
id1 = to_string(id1)
|
||||||
user_id: user.id,
|
id2 = to_string(id2)
|
||||||
filter_id: 2,
|
|
||||||
phrase: "who",
|
|
||||||
context: ["home"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, filter_one} = Pleroma.Filter.create(query_one)
|
assert [%{"id" => ^id2}, %{"id" => ^id1}] =
|
||||||
{:ok, filter_two} = Pleroma.Filter.create(query_two)
|
conn
|
||||||
|
|> get("/api/v1/filters")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fetching a list of filters without token", %{conn: conn} do
|
||||||
|
insert(:filter)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/filters")
|
|> get("/api/v1/filters")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response(403)
|
||||||
|
|
||||||
assert response ==
|
assert response["error"] == "Invalid credentials."
|
||||||
render_json(
|
|
||||||
FilterView,
|
|
||||||
"index.json",
|
|
||||||
filters: [filter_two, filter_one]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "get a filter" do
|
test "get a filter" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:filters"])
|
%{user: user, conn: conn} = oauth_access(["read:filters"])
|
||||||
|
|
||||||
# check whole_word false
|
# check whole_word false
|
||||||
query = %Pleroma.Filter{
|
filter = insert(:filter, user: user, whole_word: false)
|
||||||
user_id: user.id,
|
|
||||||
filter_id: 2,
|
|
||||||
phrase: "knight",
|
|
||||||
context: ["home"],
|
|
||||||
whole_word: false
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, filter} = Pleroma.Filter.create(query)
|
resp1 =
|
||||||
|
conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
|
assert resp1["whole_word"] == false
|
||||||
|
|
||||||
assert response = json_response_and_validate_schema(conn, 200)
|
|
||||||
assert response["whole_word"] == false
|
|
||||||
|
|
||||||
# check whole_word true
|
# check whole_word true
|
||||||
%{user: user, conn: conn} = oauth_access(["read:filters"])
|
filter = insert(:filter, user: user, whole_word: true)
|
||||||
|
|
||||||
query = %Pleroma.Filter{
|
resp2 =
|
||||||
user_id: user.id,
|
conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(200)
|
||||||
filter_id: 3,
|
|
||||||
phrase: "knight",
|
|
||||||
context: ["home"],
|
|
||||||
whole_word: true
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, filter} = Pleroma.Filter.create(query)
|
assert resp2["whole_word"] == true
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
|
|
||||||
|
|
||||||
assert response = json_response_and_validate_schema(conn, 200)
|
|
||||||
assert response["whole_word"] == true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update a filter" do
|
test "get a filter not_found error" do
|
||||||
%{user: user, conn: conn} = oauth_access(["write:filters"])
|
filter = insert(:filter)
|
||||||
|
%{conn: conn} = oauth_access(["read:filters"])
|
||||||
|
|
||||||
query = %Pleroma.Filter{
|
response =
|
||||||
user_id: user.id,
|
conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(404)
|
||||||
filter_id: 2,
|
|
||||||
phrase: "knight",
|
assert response["error"] == "Record not found"
|
||||||
context: ["home"],
|
end
|
||||||
|
|
||||||
|
describe "updating a filter" do
|
||||||
|
setup do: oauth_access(["write:filters"])
|
||||||
|
|
||||||
|
test "common" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write:filters"])
|
||||||
|
|
||||||
|
filter =
|
||||||
|
insert(:filter,
|
||||||
|
user: user,
|
||||||
hide: true,
|
hide: true,
|
||||||
whole_word: true
|
whole_word: true
|
||||||
}
|
)
|
||||||
|
|
||||||
{:ok, _filter} = Pleroma.Filter.create(query)
|
params = %{
|
||||||
|
|
||||||
new = %Pleroma.Filter{
|
|
||||||
phrase: "nii",
|
phrase: "nii",
|
||||||
context: ["home"]
|
context: ["public"],
|
||||||
|
irreversible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
conn =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put_req_header("content-type", "application/json")
|
|> put_req_header("content-type", "application/json")
|
||||||
|> put("/api/v1/filters/#{query.filter_id}", %{
|
|> put("/api/v1/filters/#{filter.filter_id}", params)
|
||||||
phrase: new.phrase,
|
|> json_response_and_validate_schema(200)
|
||||||
context: new.context
|
|
||||||
})
|
|
||||||
|
|
||||||
assert response = json_response_and_validate_schema(conn, 200)
|
assert response["phrase"] == params.phrase
|
||||||
assert response["phrase"] == new.phrase
|
assert response["context"] == params.context
|
||||||
assert response["context"] == new.context
|
assert response["irreversible"] == false
|
||||||
assert response["irreversible"] == true
|
|
||||||
assert response["whole_word"] == true
|
assert response["whole_word"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete a filter" do
|
test "with adding expires_at", %{conn: conn, user: user} do
|
||||||
%{user: user, conn: conn} = oauth_access(["write:filters"])
|
filter = insert(:filter, user: user)
|
||||||
|
in_seconds = 600
|
||||||
|
|
||||||
query = %Pleroma.Filter{
|
response =
|
||||||
user_id: user.id,
|
conn
|
||||||
filter_id: 2,
|
|> put_req_header("content-type", "application/json")
|
||||||
phrase: "knight",
|
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||||
context: ["home"]
|
phrase: "nii",
|
||||||
}
|
context: ["public"],
|
||||||
|
expires_in: in_seconds,
|
||||||
|
irreversible: true
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
{:ok, filter} = Pleroma.Filter.create(query)
|
assert response["irreversible"] == true
|
||||||
|
|
||||||
conn = delete(conn, "/api/v1/filters/#{filter.filter_id}")
|
assert response["expires_at"] ==
|
||||||
|
NaiveDateTime.utc_now()
|
||||||
|
|> NaiveDateTime.add(in_seconds)
|
||||||
|
|> Pleroma.Web.CommonAPI.Utils.to_masto_date()
|
||||||
|
|
||||||
assert json_response_and_validate_schema(conn, 200) == %{}
|
filter = Filter.get(response["id"], user)
|
||||||
|
|
||||||
|
id = filter.id
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, %{id: ^id}} =
|
||||||
|
perform_job(PurgeExpiredFilter, %{
|
||||||
|
filter_id: id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Repo.aggregate(Filter, :count, :id) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with removing expires_at", %{conn: conn, user: user} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{
|
||||||
|
phrase: "cofe",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
filter = Filter.get(response["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||||
|
phrase: "nii",
|
||||||
|
context: ["public"],
|
||||||
|
expires_in: nil,
|
||||||
|
whole_word: true
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
refute_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response["irreversible"] == false
|
||||||
|
assert response["whole_word"] == true
|
||||||
|
assert response["expires_at"] == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "expires_at is the same in create and update so job is in db", %{conn: conn, user: user} do
|
||||||
|
resp1 =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{
|
||||||
|
phrase: "cofe",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
filter = Filter.get(resp1["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
job = PurgeExpiredFilter.get_expiration(filter.id)
|
||||||
|
|
||||||
|
resp2 =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||||
|
phrase: "nii",
|
||||||
|
context: ["public"]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
updated_filter = Filter.get(resp2["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: updated_filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
after_update = PurgeExpiredFilter.get_expiration(updated_filter.id)
|
||||||
|
|
||||||
|
assert resp1["expires_at"] == resp2["expires_at"]
|
||||||
|
|
||||||
|
assert job.scheduled_at == after_update.scheduled_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updating expires_at updates oban job too", %{conn: conn, user: user} do
|
||||||
|
resp1 =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{
|
||||||
|
phrase: "cofe",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
filter = Filter.get(resp1["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
job = PurgeExpiredFilter.get_expiration(filter.id)
|
||||||
|
|
||||||
|
resp2 =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||||
|
phrase: "nii",
|
||||||
|
context: ["public"],
|
||||||
|
expires_in: 300
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
updated_filter = Filter.get(resp2["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: updated_filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
after_update = PurgeExpiredFilter.get_expiration(updated_filter.id)
|
||||||
|
|
||||||
|
refute resp1["expires_at"] == resp2["expires_at"]
|
||||||
|
|
||||||
|
refute job.scheduled_at == after_update.scheduled_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update filter without token", %{conn: conn} do
|
||||||
|
filter = insert(:filter)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||||
|
phrase: "nii",
|
||||||
|
context: ["public"]
|
||||||
|
})
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
assert response["error"] == "Invalid credentials."
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "delete a filter" do
|
||||||
|
setup do: oauth_access(["write:filters"])
|
||||||
|
|
||||||
|
test "common", %{conn: conn, user: user} do
|
||||||
|
filter = insert(:filter, user: user)
|
||||||
|
|
||||||
|
assert conn
|
||||||
|
|> delete("/api/v1/filters/#{filter.filter_id}")
|
||||||
|
|> json_response_and_validate_schema(200) == %{}
|
||||||
|
|
||||||
|
assert Repo.all(Filter) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with expires_at", %{conn: conn, user: user} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{
|
||||||
|
phrase: "cofe",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
filter = Filter.get(response["id"], user)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert conn
|
||||||
|
|> delete("/api/v1/filters/#{filter.filter_id}")
|
||||||
|
|> json_response_and_validate_schema(200) == %{}
|
||||||
|
|
||||||
|
refute_enqueued(
|
||||||
|
worker: PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: filter.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Repo.all(Filter) == []
|
||||||
|
assert Repo.all(Oban.Job) == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete a filter without token", %{conn: conn} do
|
||||||
|
filter = insert(:filter)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/filters/#{filter.filter_id}")
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
assert response["error"] == "Invalid credentials."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
defmodule Pleroma.Workers.PurgeExpiredFilterTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
use Oban.Testing, repo: Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "purges expired filter" do
|
||||||
|
%{id: user_id} = insert(:user)
|
||||||
|
|
||||||
|
{:ok, %{id: id}} =
|
||||||
|
Pleroma.Filter.create(%{
|
||||||
|
user_id: user_id,
|
||||||
|
phrase: "cofe",
|
||||||
|
context: ["home"],
|
||||||
|
expires_in: 600
|
||||||
|
})
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: Pleroma.Workers.PurgeExpiredFilter,
|
||||||
|
args: %{filter_id: id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert {:ok, %{id: ^id}} =
|
||||||
|
perform_job(Pleroma.Workers.PurgeExpiredFilter, %{
|
||||||
|
filter_id: id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Repo.aggregate(Pleroma.Filter, :count, :id) == 0
|
||||||
|
end
|
||||||
|
end
|
|
@ -486,7 +486,8 @@ def filter_factory do
|
||||||
%Pleroma.Filter{
|
%Pleroma.Filter{
|
||||||
user: build(:user),
|
user: build(:user),
|
||||||
filter_id: sequence(:filter_id, & &1),
|
filter_id: sequence(:filter_id, & &1),
|
||||||
phrase: "cofe"
|
phrase: "cofe",
|
||||||
|
context: ["home"]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue