```json
{
- "totalReports" : 1,
+ "total" : 1,
"reports": [
{
"account": {
- 400 Bad Request `"Invalid parameters"` when `status` is missing
- On success: `204`, empty response
-## `POST /api/pleroma/admin/reports/:report_id/notes/:id`
+## `DELETE /api/pleroma/admin/reports/:report_id/notes/:id`
### Delete report note
def get_reports(params, page, page_size) do
params =
params
+ |> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Flag")
|> Map.put("skip_preload", true)
|> Map.put("preload_report_notes", true)
require Logger
- @users_page_size 50
-
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} when action in [:index, :show])
plug(
action_fallback(AdminAPI.FallbackController)
- def index(conn, params) do
- {page, page_size} = page_params(params)
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
- reports = Utils.get_reports(params, page, page_size)
+ def index(conn, params) do
+ reports = Utils.get_reports(params, params.page, params.page_size)
render(conn, "index.json", reports: reports)
end
- def show(conn, %{"id" => id}) do
+ def show(conn, %{id: id}) do
with %Activity{} = report <- Activity.get_by_id(id) do
render(conn, "show.json", Report.extract_report_info(report))
else
end
end
- def update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
+ def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do
result =
- reports
- |> Enum.map(fn report ->
- with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
- ModerationLog.insert_log(%{
- action: "report_update",
- actor: admin,
- subject: activity
- })
-
- activity
- else
- {:error, message} -> %{id: report["id"], error: message}
+ Enum.map(reports, fn report ->
+ case CommonAPI.update_report_state(report.id, report.state) do
+ {:ok, activity} ->
+ ModerationLog.insert_log(%{
+ action: "report_update",
+ actor: admin,
+ subject: activity
+ })
+
+ activity
+
+ {:error, message} ->
+ %{id: report.id, error: message}
end
end)
- case Enum.any?(result, &Map.has_key?(&1, :error)) do
- true -> json_response(conn, :bad_request, result)
- false -> json_response(conn, :no_content, "")
+ if Enum.any?(result, &Map.has_key?(&1, :error)) do
+ json_response(conn, :bad_request, result)
+ else
+ json_response(conn, :no_content, "")
end
end
- def notes_create(%{assigns: %{user: user}} = conn, %{
- "id" => report_id,
- "content" => content
+ def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
+ id: report_id
}) do
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
ModerationLog.insert_log(%{
end
def notes_delete(%{assigns: %{user: user}} = conn, %{
- "id" => note_id,
- "report_id" => report_id
+ id: note_id,
+ report_id: report_id
}) do
with {:ok, note} <- ReportNote.destroy(note_id) do
ModerationLog.insert_log(%{
_ -> json_response(conn, :bad_request, "")
end
end
-
- defp page_params(params) do
- {get_page(params["page"]), get_page_size(params["page_size"])}
- end
-
- defp get_page(page_string) when is_nil(page_string), do: 1
-
- defp get_page(page_string) do
- case Integer.parse(page_string) do
- {page, _} -> page
- :error -> 1
- end
- end
-
- defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
-
- defp get_page_size(page_size_string) do
- case Integer.parse(page_size_string) do
- {page_size, _} -> page_size
- :error -> @users_page_size
- end
- end
end
--- /dev/null
+# 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.Admin.ReportOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.Account
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+ alias Pleroma.Web.ApiSpec.Schemas.Status
+
+ 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: ["Admin", "Reports"],
+ summary: "Get a list of reports",
+ operationId: "AdminAPI.ReportController.index",
+ security: [%{"oAuth" => ["read:reports"]}],
+ parameters: [
+ Operation.parameter(
+ :state,
+ :query,
+ report_state(),
+ "Filter by report state"
+ ),
+ Operation.parameter(
+ :limit,
+ :query,
+ %Schema{type: :integer},
+ "The number of records to retrieve"
+ ),
+ Operation.parameter(
+ :page,
+ :query,
+ %Schema{type: :integer, default: 1},
+ "Page number"
+ ),
+ Operation.parameter(
+ :page_size,
+ :query,
+ %Schema{type: :integer, default: 50},
+ "Number number of log entries per page"
+ )
+ ],
+ responses: %{
+ 200 =>
+ Operation.response("Response", "application/json", %Schema{
+ type: :object,
+ properties: %{
+ total: %Schema{type: :integer},
+ reports: %Schema{
+ type: :array,
+ items: report()
+ }
+ }
+ }),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def show_operation do
+ %Operation{
+ tags: ["Admin", "Reports"],
+ summary: "Get an individual report",
+ operationId: "AdminAPI.ReportController.show",
+ parameters: [id_param()],
+ security: [%{"oAuth" => ["read:reports"]}],
+ responses: %{
+ 200 => Operation.response("Report", "application/json", report()),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def update_operation do
+ %Operation{
+ tags: ["Admin", "Reports"],
+ summary: "Change the state of one or multiple reports",
+ operationId: "AdminAPI.ReportController.update",
+ security: [%{"oAuth" => ["write:reports"]}],
+ requestBody: request_body("Parameters", update_request(), required: true),
+ responses: %{
+ 204 => no_content_response(),
+ 400 => Operation.response("Bad Request", "application/json", update_400_response()),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def notes_create_operation do
+ %Operation{
+ tags: ["Admin", "Reports"],
+ summary: "Create report note",
+ operationId: "AdminAPI.ReportController.notes_create",
+ parameters: [id_param()],
+ requestBody:
+ request_body("Parameters", %Schema{
+ type: :object,
+ properties: %{
+ content: %Schema{type: :string, description: "The message"}
+ }
+ }),
+ security: [%{"oAuth" => ["write:reports"]}],
+ responses: %{
+ 204 => no_content_response(),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def notes_delete_operation do
+ %Operation{
+ tags: ["Admin", "Reports"],
+ summary: "Delete report note",
+ operationId: "AdminAPI.ReportController.notes_delete",
+ parameters: [
+ Operation.parameter(:report_id, :path, :string, "Report ID"),
+ Operation.parameter(:id, :path, :string, "Note ID")
+ ],
+ security: [%{"oAuth" => ["write:reports"]}],
+ responses: %{
+ 204 => no_content_response(),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp report_state do
+ %Schema{type: :string, enum: ["open", "closed", "resolved"]}
+ end
+
+ defp id_param do
+ Operation.parameter(:id, :path, FlakeID, "Report ID",
+ example: "9umDrYheeY451cQnEe",
+ required: true
+ )
+ end
+
+ defp report do
+ %Schema{
+ type: :object,
+ properties: %{
+ id: FlakeID,
+ state: report_state(),
+ account: account_admin(),
+ actor: account_admin(),
+ content: %Schema{type: :string},
+ created_at: %Schema{type: :string, format: :"date-time"},
+ statuses: %Schema{type: :array, items: Status},
+ notes: %Schema{
+ type: :array,
+ items: %Schema{
+ type: :object,
+ properties: %{
+ id: %Schema{type: :integer},
+ user_id: FlakeID,
+ content: %Schema{type: :string},
+ inserted_at: %Schema{type: :string, format: :"date-time"}
+ }
+ }
+ }
+ }
+ }
+ end
+
+ defp account_admin do
+ %Schema{
+ title: "Account",
+ description: "Account view for admins",
+ type: :object,
+ properties:
+ Map.merge(Account.schema().properties, %{
+ nickname: %Schema{type: :string},
+ deactivated: %Schema{type: :boolean},
+ local: %Schema{type: :boolean},
+ roles: %Schema{
+ type: :object,
+ properties: %{
+ admin: %Schema{type: :boolean},
+ moderator: %Schema{type: :boolean}
+ }
+ },
+ confirmation_pending: %Schema{type: :boolean}
+ })
+ }
+ end
+
+ defp update_request do
+ %Schema{
+ type: :object,
+ required: [:reports],
+ properties: %{
+ reports: %Schema{
+ type: :array,
+ items: %Schema{
+ type: :object,
+ properties: %{
+ id: %Schema{allOf: [FlakeID], description: "Required, report ID"},
+ state: %Schema{
+ type: :string,
+ description:
+ "Required, the new state. Valid values are `open`, `closed` and `resolved`"
+ }
+ }
+ },
+ example: %{
+ "reports" => [
+ %{"id" => "123", "state" => "closed"},
+ %{"id" => "1337", "state" => "resolved"}
+ ]
+ }
+ }
+ }
+ }
+ end
+
+ defp update_400_response do
+ %Schema{
+ type: :array,
+ items: %Schema{
+ type: :object,
+ properties: %{
+ id: %Schema{allOf: [FlakeID], description: "Report ID"},
+ error: %Schema{type: :string, description: "Error message"}
+ }
+ }
+ }
+ end
+end
}
end
- defp admin_account do
+ def admin_account do
%Schema{
type: :object,
properties: %{
response =
conn
|> get("/api/pleroma/admin/reports/#{report_id}")
- |> json_response(:ok)
+ |> json_response_and_validate_schema(:ok)
assert response["id"] == report_id
end
test "returns 404 when report id is invalid", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/reports/test")
- assert json_response(conn, :not_found) == %{"error" => "Not found"}
+ assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end
end
response =
conn
|> assign(:token, read_token)
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [%{"state" => "resolved", "id" => id}]
})
- |> json_response(403)
+ |> json_response_and_validate_schema(403)
assert response == %{
"error" => "Insufficient permissions: admin:write:reports."
conn
|> assign(:token, write_token)
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [%{"state" => "resolved", "id" => id}]
})
- |> json_response(:no_content)
+ |> json_response_and_validate_schema(:no_content)
end
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
conn
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "resolved", "id" => id}
]
})
- |> json_response(:no_content)
+ |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id)
assert activity.data["state"] == "resolved"
test "closes report", %{conn: conn, id: id, admin: admin} do
conn
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "closed", "id" => id}
]
})
- |> json_response(:no_content)
+ |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id)
assert activity.data["state"] == "closed"
test "returns 400 when state is unknown", %{conn: conn, id: id} do
conn =
conn
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "test", "id" => id}
]
})
- assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
+ assert "Unsupported state" =
+ hd(json_response_and_validate_schema(conn, :bad_request))["error"]
end
test "returns 404 when report is not exist", %{conn: conn} do
conn =
conn
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "closed", "id" => "test"}
]
})
- assert hd(json_response(conn, :bad_request))["error"] == "not_found"
+ assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found"
end
test "updates state of multiple reports", %{
second_report_id: second_report_id
} do
conn
+ |> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "resolved", "id" => id},
%{"state" => "closed", "id" => second_report_id}
]
})
- |> json_response(:no_content)
+ |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id)
second_activity = Activity.get_by_id(second_report_id)
response =
conn
|> get("/api/pleroma/admin/reports")
- |> json_response(:ok)
+ |> json_response_and_validate_schema(:ok)
assert Enum.empty?(response["reports"])
assert response["total"] == 0
response =
conn
|> get("/api/pleroma/admin/reports")
- |> json_response(:ok)
+ |> json_response_and_validate_schema(:ok)
[report] = response["reports"]
response =
conn
- |> get("/api/pleroma/admin/reports", %{
- "state" => "open"
- })
- |> json_response(:ok)
+ |> get("/api/pleroma/admin/reports?state=open")
+ |> json_response_and_validate_schema(:ok)
- [open_report] = response["reports"]
+ assert [open_report] = response["reports"]
assert length(response["reports"]) == 1
assert open_report["id"] == first_report_id
response =
conn
- |> get("/api/pleroma/admin/reports", %{
- "state" => "closed"
- })
- |> json_response(:ok)
+ |> get("/api/pleroma/admin/reports?state=closed")
+ |> json_response_and_validate_schema(:ok)
- [closed_report] = response["reports"]
+ assert [closed_report] = response["reports"]
assert length(response["reports"]) == 1
assert closed_report["id"] == second_report_id
assert response["total"] == 1
- response =
- conn
- |> get("/api/pleroma/admin/reports", %{
- "state" => "resolved"
- })
- |> json_response(:ok)
-
- assert Enum.empty?(response["reports"])
- assert response["total"] == 0
+ assert %{"total" => 0, "reports" => []} ==
+ conn
+ |> get("/api/pleroma/admin/reports?state=resolved", %{
+ "" => ""
+ })
+ |> json_response_and_validate_schema(:ok)
end
test "returns 403 when requested by a non-admin" do
test "returns 403 when requested by anonymous" do
conn = get(build_conn(), "/api/pleroma/admin/reports")
- assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
+ assert json_response(conn, :forbidden) == %{
+ "error" => "Invalid credentials."
+ }
end
end
status_ids: [activity.id]
})
- post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
content: "this is disgusting!"
})
- post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
content: "this is disgusting2!"
})
end
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
- [note, _] = Repo.all(ReportNote)
+ assert [note, _] = Repo.all(ReportNote)
assert %{
activity_id: ^report_id,
test "it returns reports with notes", %{conn: conn, admin: admin} do
conn = get(conn, "/api/pleroma/admin/reports")
- response = json_response(conn, 200)
+ response = json_response_and_validate_schema(conn, 200)
notes = hd(response["reports"])["notes"]
[note, _] = notes
test "it deletes the note", %{conn: conn, report_id: report_id} do
assert ReportNote |> Repo.all() |> length() == 2
-
- [note, _] = Repo.all(ReportNote)
+ assert [note, _] = Repo.all(ReportNote)
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")