Instance rules

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2022-02-05 18:03:53 +01:00
parent 4605efe272
commit bd52e2aec7
9 changed files with 373 additions and 1 deletions

56
lib/pleroma/rule.ex Normal file
View File

@ -0,0 +1,56 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Rule do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.Rule
schema "rules" do
field(:priority, :integer, default: 0)
field(:text, :string)
timestamps()
end
def changeset(%Rule{} = rule, params \\ %{}) do
rule
|> cast(params, [:priority, :text])
|> validate_required([:text])
end
def query do
Rule
|> order_by(asc: :priority)
end
def get(id), do: Repo.get(__MODULE__, id)
def create(params) do
{:ok, rule} =
%Rule{}
|> changeset(params)
|> Repo.insert()
rule
end
def update(params, id) do
{:ok, rule} =
get(id)
|> changeset(params)
|> Repo.update()
rule
end
def delete(id) do
get(id)
|> Repo.delete()
end
end

View File

@ -0,0 +1,64 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.RuleController do
use Pleroma.Web, :controller
alias Pleroma.Repo
alias Pleroma.Rule
alias Pleroma.Web.Plugs.OAuthScopesPlug
import Pleroma.Web.ControllerHelper,
only: [
json_response: 3
]
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(
OAuthScopesPlug,
%{scopes: ["admin:write"]}
when action in [:create, :update, :delete]
)
plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :index)
action_fallback(AdminAPI.FallbackController)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.RuleOperation
def index(conn, _) do
rules =
Rule.query()
|> Repo.all()
render(conn, "index.json", rules: rules)
end
def create(%{body_params: params} = conn, _) do
rule =
params
|> Rule.create()
render(conn, "show.json", rule: rule)
end
def update(%{body_params: params} = conn, %{id: id}) do
rule =
params
|> Rule.update(id)
render(conn, "show.json", rule: rule)
end
def delete(conn, %{id: id}) do
with {:ok, _} <- Rule.delete(id) do
json(conn, %{})
else
_ -> json_response(conn, :bad_request, "")
end
end
end

View File

@ -0,0 +1,21 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.RuleView do
use Pleroma.Web, :view
require Pleroma.Constants
def render("index.json", %{rules: rules} = _opts) do
render_many(rules, __MODULE__, "show.json")
end
def render("show.json", %{rule: rule} = _opts) do
%{
id: rule.id,
priority: rule.priority,
text: rule.text
}
end
end

View File

@ -0,0 +1,113 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.RuleOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.ApiError
import Pleroma.Web.ApiSpec.Helpers
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def index_operation do
%Operation{
tags: ["Instance rule managment"],
summary: "Retrieve a list of instance rules",
operationId: "AdminAPI.RuleController.index",
security: [%{"oAuth" => ["admin:read"]}],
responses: %{
200 =>
Operation.response("Response", "application/json", %Schema{
type: :array,
items: rule()
}),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
def create_operation do
%Operation{
tags: ["Instance rule managment"],
summary: "Create new rule",
operationId: "AdminAPI.RuleController.create",
security: [%{"oAuth" => ["admin:write"]}],
parameters: admin_api_params(),
requestBody: request_body("Parameters", create_request(), required: true),
responses: %{
200 => Operation.response("Response", "application/json", rule()),
400 => Operation.response("Bad Request", "application/json", ApiError),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
def update_operation do
%Operation{
tags: ["Instance rule managment"],
summary: "Modify existing rule",
operationId: "AdminAPI.RuleController.update",
security: [%{"oAuth" => ["admin:write"]}],
parameters: [Operation.parameter(:id, :path, :string, "Rule ID")],
requestBody: request_body("Parameters", update_request(), required: true),
responses: %{
200 => Operation.response("Response", "application/json", rule()),
400 => Operation.response("Bad Request", "application/json", ApiError),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
def delete_operation do
%Operation{
tags: ["Instance rule managment"],
summary: "Delete rule",
operationId: "AdminAPI.RuleController.delete",
parameters: [Operation.parameter(:id, :path, :string, "Rule ID")],
security: [%{"oAuth" => ["admin:write"]}],
responses: %{
200 => empty_object_response(),
404 => Operation.response("Not Found", "application/json", ApiError),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
defp create_request do
%Schema{
type: :object,
required: [:text],
properties: %{
priority: %Schema{type: :integer},
text: %Schema{type: :string}
}
}
end
defp update_request do
%Schema{
type: :object,
properties: %{
priority: %Schema{type: :integer},
text: %Schema{type: :string}
}
}
end
defp rule do
%Schema{
type: :object,
properties: %{
id: %Schema{type: :integer},
priority: %Schema{type: :integer},
text: %Schema{type: :string},
created_at: %Schema{type: :string, format: :"date-time"}
}
}
end
end

View File

@ -40,6 +40,7 @@ def render("show.json", _) do
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
shout_limit: Config.get([:shout, :limit]),
description_limit: Keyword.get(instance, :description_limit),
rules: rules(),
pleroma: %{
metadata: %{
account_activation_required: Keyword.get(instance, :account_activation_required),
@ -137,4 +138,10 @@ def fields_limits do
value_length: Config.get([:instance, :account_field_value_length])
}
end
def rules do
Pleroma.Rule.query()
|> Pleroma.Repo.all()
|> Enum.map(&%{id: &1.id, text: &1.text})
end
end

View File

@ -229,6 +229,11 @@ defmodule Pleroma.Web.Router do
post("/frontends/install", FrontendController, :install)
post("/backups", AdminAPIController, :create_backup)
get("/rules", RuleController, :index)
post("/rules", RuleController, :create)
patch("/rules/:id", RuleController, :update)
delete("/rules/:id", RuleController, :delete)
end
# AdminAPI: admins and mods (staff) can perform these actions (if enabled by config)

View File

@ -0,0 +1,12 @@
defmodule Pleroma.Repo.Migrations.CreateRules do
use Ecto.Migration
def change do
create_if_not_exists table(:rules) do
add(:priority, :integer, default: 0, null: false)
add(:text, :text, null: false)
timestamps()
end
end
end

View File

@ -0,0 +1,78 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.RuleControllerTest do
use Pleroma.Web.ConnCase, async: true
import Pleroma.Factory
alias Pleroma.Rule
setup do
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{admin: admin, token: token, conn: conn}}
end
describe "GET /api/pleroma/admin/rules" do
test "sorts rules by priority", %{conn: conn} do
%{id: id1} = Rule.create(%{text: "Example rule"})
%{id: id2} = Rule.create(%{text: "Second rule", priority: 2})
%{id: id3} = Rule.create(%{text: "Third rule", priority: 1})
response =
conn
|> get("/api/pleroma/admin/rules")
|> json_response_and_validate_schema(:ok)
assert [%{"id" => ^id1}, %{"id" => ^id3}, %{"id" => ^id2}] = response
end
end
describe "POST /api/pleroma/admin/rules" do
test "creates a rule", %{conn: conn} do
%{"id" => id} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/rules", %{text: "Example rule"})
|> json_response_and_validate_schema(:ok)
assert %{text: "Example rule"} = Rule.get(id)
end
end
describe "PATCH /api/pleroma/admin/rules" do
test "edits a rule", %{conn: conn} do
%{id: id} = Rule.create(%{text: "Example rule"})
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/rules/#{id}", %{text: "There are no rules", priority: 2})
|> json_response_and_validate_schema(:ok)
assert %{text: "There are no rules", priority: 2} = Rule.get(id)
end
end
describe "DELETE /api/pleroma/admin/rules" do
test "deletes a rule", %{conn: conn} do
%{id: id} = Rule.create(%{text: "Example rule"})
conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/rules/#{id}")
|> json_response_and_validate_schema(:ok)
assert [] =
Rule.query()
|> Pleroma.Repo.all()
end
end
end

View File

@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
# TODO: Should not need Cachex
use Pleroma.Web.ConnCase
alias Pleroma.Rule
alias Pleroma.User
import Pleroma.Factory
@ -39,7 +40,8 @@ test "get instance information", %{conn: conn} do
"banner_upload_limit" => _,
"background_image" => from_config_background,
"shout_limit" => _,
"description_limit" => _
"description_limit" => _,
"rules" => _
} = result
assert result["pleroma"]["metadata"]["account_activation_required"] != nil
@ -91,4 +93,18 @@ test "get peers", %{conn: conn} do
assert ["peer1.com", "peer2.com"] == Enum.sort(result)
end
test "get instance rules", %{conn: conn} do
Rule.create(%{text: "Example rule"})
Rule.create(%{text: "Second rule"})
Rule.create(%{text: "Third rule"})
conn = get(conn, "/api/v1/instance")
assert result = json_response_and_validate_schema(conn, 200)
rules = result["rules"]
assert length(rules) == 3
end
end